RSA BSAFE Crypto-C

Cryptographic Components for C

Search

pbkdf2.c

/* $Id: pbkdf2.c,v 1.6 2004/12/03 02:08:41 sparki Exp $ */
/*
 * Copyright (C) 1998-2004 RSA Security Inc.
 *
 * This file shall only be used to demonstrate how to interface to an
 * RSA Security Inc. licensed development product.
 *
 * You have a royalty-free right to use, reproduce and distribute this
 * demonstration file, provided that you agree that RSA Security Inc.
 * has no warranty, implied or otherwise, or liability for this
 * demonstration file (including any modified version).  This software
 * is provided "as is" without warranties or representations of any
 * kind. RSA Security disclaims all conditions and warranties, statutory
 * and otherwise, both express and implied, with respect to the software,
 * its quality and performance, including but not limited to, all
 * implied warranties of merchantability, fitness for a particular
 * purpose, title and noninfringement of third party rights. Without
 * limiting the foregoing, RSA Security does not warrant that the
 * software is error-free or that errors in the product will be
 * corrected. You agree that RSA Security shall not be liable for any
 * direct, indirect, incidental, special, consequential, punitive or
 * other damages whatsoever resulting from your use of this software
 * or any modified version.
 *
 *
 */

/*  This file demonstrates creating a key using the PKCS5v2 standard.
**  In order to generate a key the password, salt, and iteration
**  count must be known. For this example these variables are hard
**  coded.
*/


#include "bsafe.h"
#include "demoutil.h"  /*  In samples/common/include */

/*  DIGESTLEN is the length of the output of the digest
 *    algorithm used - for SHA1 this is 20, whereas for
 *    MD5 or MD2 this should be 16.
 *  KEYLEN is the length of the desired output key
 *  ITERATIONLEN is the number of iterations the algorithm
 *    should run.  A minimum number of 1000 is recommended.
*/
#define DIGESTLEN 20
#define KEYLEN 16
#define ITERATIONLEN 1024


/*  This function appends number to the end of salt and uses
 *  the specified digest to digest this information an
 *  iteration number of times.
 *  Each time after an iteration has taken place the digested
 *  data is XORed with the previous data.  The final
 *  XORed data is stored in the ITEM structure digestedData.
 *  Note this is an input/output function and the digestedData
 *  field must be freed by the user.
 */
int IteratingDigest ( B_ALGORITHM_OBJ digest, unsigned int iteration,
                      unsigned char *salt, unsigned int saltLen,
                      ITEM *digestedData, unsigned int number );


/*  This function uses the specified digest algorithm, the salt, password
 *  and iteration count to produce a key of size keyLen based on the
 *  PKCS5v2.0 standard.  This key is then stored in the desiredKey field.
 *  Again, the user should make sure to free up the desiredKey field when
 *  they are through with it.
 */
int CreatePKCS5V2Key ( B_ALGORITHM_OBJ digest, unsigned int keyLen,
                       unsigned int digestLen, unsigned char *salt,
                       unsigned int saltLen, unsigned int iteration,
                       ITEM *desiredKey );

#ifdef CRYPTOC_APP
#define MAIN pbkdf2Main
#else
#define MAIN main
#endif

int MAIN(int argc, char **argv)
{

  /*  The digest needs to be created and set with
   *  the information for using the hmac
   *  with SHA1 algorithm
  */
  B_ALGORITHM_OBJ digester = (B_ALGORITHM_OBJ)NULL_PTR;

  B_DIGEST_SPECIFIER hmacInfo;

  B_ALGORITHM_METHOD *HMAC_CHOOSER[] = {
    &AM_SHA,
    &AM_SHA_RANDOM,
    (B_ALGORITHM_METHOD *)NULL_PTR
    /* This will fix a problem that the IA64 compiler finds when *
                 * seeing a short chooser list */
                #ifdef IA64_FORCE_LARGE
                                IA64_FORCE_LARGE
                #endif


  };

  /*  The key used in the pkcs standard is initialized
   *  with the password and then used to digest the salt
  */
  B_KEY_OBJ HMACKey = (B_KEY_OBJ)NULL_PTR;
  ITEM HMACKeyInfo = {NULL, 0};

  /*  For this example the password and the salt are hard coded.
   *  If the key generated is being used for decryption then the
   *  password and salt must be the same as used for the key that
   *  encrypted the data.  Otherwise it is recommended to use a
   *  random number generator to produce the salt.
  */
  static unsigned char password[] = "password";
  unsigned int passwordLen = sizeof(password) - 1;
  unsigned char salt[4] = {0x12, 0x34, 0x56, 0x78};
  unsigned int saltLen = 4;

  /* ITEM will hold the data once it's been digested */
  ITEM keyData = {NULL, 0};

  /*  The status value is used to check for errors */
  int status;

  do {

  /* The RSA_* demo code utilities are described in
   * common/include/demoutil.h.  This procedure simply checks the
   * command-line arguments for input or output options. */
        if ((status = RSA_SetOptions( argc, argv )) !=0)
    break;

  RSA_PrintMessage ("PKCS5v2.0:        Key Generation \n");
  RSA_PrintMessage ("================================ \n\n");

  /*  Create an Algorithm Object to digest data
   *  and set it to used HMAC with SHA1
  */
  if ((status = B_CreateAlgorithmObject( &digester )) !=0)
    break;

  hmacInfo.digestInfoType = AI_SHA1;
  hmacInfo.digestInfoParams = NULL_PTR;

  if ((status = B_SetAlgorithmInfo ( digester, AI_HMAC,
                                     (POINTER)&hmacInfo )) !=0)
    break;

  /*  Create a Key object to be passed to the digester
   *  in order to digest the data, and set this key
   *  with the password
  */
  if ((status = B_CreateKeyObject( &HMACKey )) !=0)
    break;


  HMACKeyInfo.len = passwordLen;
  HMACKeyInfo.data = T_malloc( HMACKeyInfo.len );
  T_memcpy( HMACKeyInfo.data, password, HMACKeyInfo.len );

  if ((status = B_SetKeyInfo ( HMACKey, KI_Item,
                               (POINTER)&HMACKeyInfo )) !=0)
    break;

  /*  Initialize the digest with the Key and the
   *  algorithm method
   */
  if ((status = B_DigestInit ( digester, HMACKey, HMAC_CHOOSER,
                               (A_SURRENDER_CTX *)NULL_PTR )) !=0)
    break;


  /*  Get the information needed to create a key. 
   *  Refer to CreatePKCS5V2Key below main.
   */
  if ((status = CreatePKCS5V2Key ( digester, KEYLEN, DIGESTLEN, salt,
                                   saltLen, ITERATIONLEN, &keyData )) !=0)
    break;

  /*  Print out the key to the standard output
   *  This Key Data can now be set into a key object using KI_ITEM.
   *  This key can be used in any symmetric cipher for either
   *  encryption or decryption
  */

  RSA_PrintBuf("The Key Data Is:  ", keyData.data, keyData.len);


}while(0);

  if(status !=0)
  {
    RSA_PrintError("pbkdf2.c", status);
  }

  B_DestroyKeyObject ( &HMACKey );
  B_DestroyAlgorithmObject ( &digester );

  if (keyData.data != NULL_PTR) {
    T_memset ( keyData.data, 0, KEYLEN );
    T_free ( keyData.data );
    keyData.data = NULL_PTR;
    keyData.len = 0;
    }

  if (HMACKeyInfo.data != NULL_PTR) {
    T_memset ( HMACKeyInfo.data, 0, HMACKeyInfo.len );
    T_free( HMACKeyInfo.data );
    HMACKeyInfo.data = NULL_PTR;
    HMACKeyInfo.len = 0;
  }

  return(status);

}/* end main */

/*  This function appends number to the end of salt and uses
 *  the specified digest to digest this information an
 *  iteration number of times.
 *  Each time after an iteration has taken place the digested
 *  data is XORed with the previous data.  The final
 *  XORed data is stored in the ITEM structure digData
 */
int IteratingDigest(B_ALGORITHM_OBJ digest, unsigned int iteration,
  unsigned char *salt, unsigned int saltLen, ITEM *digestedData,
  unsigned int number)
{

  /*  These ITEMs are used to store the itermediate data */
  ITEM holdData = {NULL, 0};
  ITEM holdData2 = {NULL, 0};

  /*  This is used to check that the algorithm is working */
  int status = 0;

  /*  Used as placeholders*/
  unsigned int i, j, k;

  /*  Used to store the salt and the iteration number */
  unsigned char *newsalt = NULL;
  unsigned int inputLen = 0;

  /* Storing the number as four bytes */
  unsigned char num[4];

  do{

    /*  newsalt is 4 bytes longer then saltLen becaue the num[] array
     *  is being appended to the salt */
    newsalt = T_malloc(saltLen + 4);
    if (newsalt == NULL_PTR) {
      status = RSA_DEMO_E_ALLOC;
      break;
    }

    /*  If iteration is less then zero there is an error */
    if(iteration < 0)
    {
      printf("the iteration needs to be greater than zero\n");
      status = RSA_DEMO_E_INVALID_PARAMETER;
      break;
    }

    /*  Copying the salt data into newsalt and appending
     *  number to the end
    */
    i = 0;
    j = 0;

    newsalt[0] = salt[0];

    while(i < saltLen)
    {
      newsalt[i] = salt[j];
      i++;
      j++;
    }

    /*  This lets num[] obtain the number to be appended
    */
    for (i = 0; i <= 3; ++i)
    {

      num[3-i] = (unsigned char)(number>>(8*i));

    }

    T_memcpy(newsalt + saltLen, num, 4);
    inputLen = saltLen + 4;

    /*  making sure holdData has enough space */
    holdData.data = T_malloc(DIGESTLEN);
    if (holdData.data == NULL_PTR) {
      status = RSA_DEMO_E_ALLOC;
      break;
    }

    /*  Digesting the data the first time */

    if ((status = B_DigestUpdate (digest,
                  newsalt, inputLen,
                  (A_SURRENDER_CTX *)NULL_PTR)) !=0)
                  break;

          if ((status = B_DigestFinal (digest,
                  holdData.data, &holdData.len,
                  DIGESTLEN, (A_SURRENDER_CTX *)NULL_PTR)) !=0)
                  break;

    /*  If the iteration number is only one then
     *  the funtion is done
     */
    if (iteration == 1)
    {
           break;
    }

    /*  Once the salt itself has been digested the
     *  PKCS 5v2.0 standard uses the digested data
     *  as the salt for the next digest
     */
     j = 0;
     k = 0;

     inputLen = 20;
     newsalt = T_malloc(inputLen);
     if (newsalt == NULL_PTR) {
      status = RSA_DEMO_E_ALLOC;
      break;
     }

     while(k < inputLen)
     {
       newsalt[k] = holdData.data[j];
       k++;
       j++;
     }


     /*  The data is digested an iteration number of times
      *  Each time storing the XOR of the digested bytes
      *  with the previous bytes and using the most recent
      *  digested bytes as input to be digested
      */

      holdData2.len = DIGESTLEN;
      holdData2.data = T_malloc(DIGESTLEN);
      if (holdData2.data == NULL_PTR) {
        status = RSA_DEMO_E_ALLOC;
        break;
      }

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

        if ((status = B_DigestUpdate (digest,
                      newsalt, inputLen,
                      (A_SURRENDER_CTX *)NULL_PTR)) !=0)
                      break;

            if ((status = B_DigestFinal (digest,
                    holdData2.data, &holdData2.len,
                      DIGESTLEN, (A_SURRENDER_CTX *)NULL_PTR)) !=0)
                      break;

        /*  XORing the newly digested bits with the old
         *  digested bits
         */
        for( j = 0; j < holdData.len; ++j )
        {
                holdData.data[j] ^= holdData2.data[j];
        }

        /*  Copying the Digested Data to be used
         *  as the data for the next digest
         */
        j = 0;
        k = 0;

        while(k < inputLen)
        {
          newsalt[k] = holdData2.data[j];
          k++;
          j++;
        }

    }

  }while(0);

  /*  Store the data into digData */
   digestedData ->len = holdData.len;
   digestedData->data = T_malloc( digestedData->len );
   T_memcpy( digestedData -> data, holdData.data, digestedData ->len );

   /*  If an error occured return this information */
    if( status !=0 )
    {
      RSA_PrintError( "IteratingDigest", status );
      if ( digestedData->data != NULL_PTR ) {
        T_memset ( digestedData->data, 0, digestedData->len );
        T_free ( digestedData->data );
        digestedData->data = NULL_PTR;
        digestedData->len = 0;
      }
    }

    /*  Freeing up any memory allocated in this function
    */
    if ( holdData.data != NULL_PTR ) {
      T_memset ( holdData.data, 0, holdData.len );
      T_free ( holdData.data );
      holdData.data = NULL_PTR;
      holdData.len = 0;
    }
    if ( holdData2.data != NULL_PTR ) {
      T_memset ( holdData2.data, 0, holdData2.len );
      T_free ( holdData2.data );
      holdData2.data = NULL_PTR;
      holdData2.len = 0;
    }
    if ( newsalt != NULL_PTR ) {
      T_memset ( newsalt, 0, inputLen );
      T_free ( newsalt );
      newsalt = NULL_PTR;
      inputLen = 0;
    }

    return (status);

}/* End IteratingDigest */

/*  This function uses the specified digest algorithm, the salt, password
 *  and iteration count to produce a key of size keyLen based on the
 *  PKCS5v2.0 standard.  This key is then stored in the desiredKey field.
 */
int CreatePKCS5V2Key(B_ALGORITHM_OBJ digest, unsigned int keyLen,
  unsigned int digestLen, unsigned char *salt, unsigned int saltLen,
  unsigned int iteration, ITEM *desiredKey)
{

  /*  status is used for error checking*/
  int status = 0;

  /*  Used to see how many blocks are needed
  */
  unsigned int passes;
  unsigned int remainder;
  double div;

  /*  Used for place holding */
  unsigned int i, j, k;

  /*  Used to store intermediate data */
  ITEM digestedData = {NULL, 0};

  /*  Find out how many passes are needed */
  div = ((double) keyLen)/((double) digestLen);

  passes = (int)div;

  remainder = keyLen - ( passes * digestLen );

  do{
    /*  Ensure the key has enough space */
    desiredKey->len = keyLen;
    desiredKey->data = T_malloc( desiredKey->len );
    if ( desiredKey -> data == NULL_PTR ) {
      status = RSA_DEMO_E_ALLOC;
      break;
    }

    /*  Initialize the holding structure */
    digestedData.len = digestLen;
    digestedData.data = T_malloc( digestLen );
    if ( digestedData.data == NULL_PTR ) {
      status = RSA_DEMO_E_ALLOC;
      break;
    }

    j = 0;
    /*  For each block that needs to be created
     *  the IterationDigest function will be called.
     *  The data returned from this will be stored in the
     *  desiredKey.
    */
    for( i = 1; i <= passes; ++i )
    {

      if ((status = IteratingDigest ( digest, iteration, salt, saltLen,
                               &digestedData, i )) !=0)
                               break;
      k = 0;

      while(k < digestLen)
      {
        desiredKey -> data[j] = digestedData.data[k];
        k++;
        j++;
      }
      desiredKey -> len = j;

    }

    /*  If there is a remainder then that number of bits
     *  still needs to be added to the DKey
     */
    if ( remainder != 0 )
    {
      if ((status = IteratingDigest( digest, iteration,
                      salt, saltLen,
                      &digestedData, i )) !=0)
                      break;

      k = 0;

      while( k < remainder )
      {
        desiredKey -> data[j] = digestedData.data[k];
        k++;
        j++;
      }
      desiredKey -> len = j;
    }

  }while(0);

  /*  If there was an error print it out */
  if ( status !=0 )
  {
    RSA_PrintError( "CreatePKCS5v2Key", status );
    if ( desiredKey->data != NULL_PTR ) {
      T_memset ( desiredKey->data, 0, desiredKey->len );
      T_free ( desiredKey->data );
      desiredKey->data = NULL_PTR;
      desiredKey->len = 0;
    }
  }

    if ( digestedData.data != NULL_PTR ) {
      T_memset ( digestedData.data, 0, digestedData.len );
      T_free ( digestedData.data );
      digestedData.data = NULL_PTR;
      digestedData.len = 0;
    }

  return ( status );
}/* End CreatePKCS5V2Key */


Copyright (c) 1999-2005 RSA Security Inc. All rights reserved. 068-001001-6210-001-000 - 6.2.1