aboutsummaryrefslogblamecommitdiff
path: root/pbkdf2.c
blob: 1ed83324bd9cbf31ee73d822da0a11a1a9fd446c (plain) (tree)























































































                                                                                                            





                                                                          






                                                               

                                                        




                                                                                         


                              












                                                                      
                              

      

                                                                     


                                                                                
                                                           

                 

                                               


                                                                      




                                                                       
                                                       



                                                         
                        







                                                                    
                                                         



                                                      
                                                  









                        
/*
 * pbkdf2.c
 * --------
 * PBKDF2 (RFC 2898) on top of HAL interface to Cryptech hash cores.
 *
 * Authors: Rob Austein
 * Copyright (c) 2015, SUNET
 *
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>

#include "cryptech.h"

/*
 * Utility to encapsulate the HMAC operations.  May need refactoring
 * if and when we get clever about reusing HMAC state for speed.
 */

static hal_error_t do_hmac(const hal_hash_descriptor_t * const d,
                           const uint8_t * const pw,   const size_t pw_len,
                           const uint8_t * const data, const size_t data_len,
                           const uint32_t  block,
                                 uint8_t * mac,        const size_t mac_len)
{
  assert(d != NULL && pw != NULL && data != NULL && mac != NULL);

  uint8_t sb[d->hmac_state_length];
  hal_hmac_state_t s;
  hal_error_t err;

  if ((err = hal_hmac_initialize(d, &s, sb, sizeof(sb), pw, pw_len)) != HAL_OK)
    return err;

  if ((err = hal_hmac_update(s, data, data_len)) != HAL_OK)
    return err;

  if (block > 0) {
    uint8_t b[4] = { (block >> 24) & 0xFF, (block >> 16) & 0xFF, (block >> 8) & 0xFF, (block >> 0) & 0xFF };
    if ((err = hal_hmac_update(s, b, sizeof(b))) != HAL_OK)
      return err;
  }

  return hal_hmac_finalize(s, mac, mac_len);
}

/*
 * Derive a key from a passphrase using the PBKDF2 algorithm.
 */

hal_error_t hal_pbkdf2(const hal_hash_descriptor_t * const descriptor,
                       const uint8_t * const password, const size_t password_length,
                       const uint8_t * const salt,     const size_t salt_length,
                       uint8_t       * derived_key,          size_t derived_key_length,
                       unsigned iterations_desired)
{
  uint8_t ac[HAL_MAX_HASH_DIGEST_LENGTH], mac[HAL_MAX_HASH_DIGEST_LENGTH];
  uint8_t statebuf[1024];
  unsigned iteration;
  hal_error_t err;
  uint32_t block;
  int i;

  if (descriptor == NULL || password == NULL || salt == NULL ||
      derived_key == NULL || derived_key_length == 0 ||
      iterations_desired == 0)
    return HAL_ERROR_BAD_ARGUMENTS;

  assert(sizeof(statebuf) >= descriptor->hmac_state_length);
  assert(sizeof(ac)       >= descriptor->digest_length);
  assert(sizeof(mac)      >= descriptor->digest_length);

  /* Output length check per RFC 2989 5.2. */
  if ((uint64_t) derived_key_length > ((uint64_t) 0xFFFFFFFF) * descriptor->block_length)
    return HAL_ERROR_UNSUPPORTED_KEY;

  memset(ac,  0, sizeof(ac));
  memset(mac, 0, sizeof(mac));

  /*
   * We probably should check here to see whether the password is
   * longer than the HMAC block size, and, if so, we should hash the
   * password here to avoid having recomputing that every time through
   * the loops below.  There are other optimizations we'd like to
   * make, but this one doesn't require being able to save and restore
   * the hash state.
   */

  /*
   * Generate output blocks until we reach the requested length.
   */

  for (block = 1; ; block++) {

    /*
     * Initial HMAC is of the salt concatenated with the block count.
     * This seeds the accumulator, and constitutes iteration one.
     */

    if ((err = do_hmac(descriptor, password, password_length, salt, salt_length,
                       block, mac, sizeof(mac))) != HAL_OK)
      return err;

    memcpy(ac, mac, descriptor->digest_length);

    /*
     * Now iterate however many times the caller requested, XORing the
     * result back into the accumulator on each iteration.
     */

    for (iteration = 2; iteration <= iterations_desired; iteration++) {

      if ((err = do_hmac(descriptor, password, password_length,
                         ac, descriptor->digest_length,
                         0, mac, sizeof(mac))) != HAL_OK)
        return err;

      for (i = 0; i < descriptor->digest_length; i++)
        ac[i] ^= mac[i];
    }

    /*
     * Accumulator holds the generated block.  Save it, then exit or
     * loop for another block.
     */

    if (derived_key_length > descriptor->digest_length) {
      memcpy(derived_key, ac, descriptor->digest_length);
      derived_key        += descriptor->digest_length;
      derived_key_length -= descriptor->digest_length;
    }
    else {
      memcpy(derived_key, ac, derived_key_length);
      return HAL_OK;
    }
  }
}

/*
 * Local variables:
 * indent-tabs-mode: nil
 * End:
 */