| RSA BSAFE Crypto-C |
Cryptographic Components for C |
| Search |
/* $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 */