| RSA BSAFE Crypto-C |
Cryptographic Components for C |
| Search |
/* $Id: pkcs12pbe.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 how to generate a key and iv from a * password, salt and iteration count using SHA1 and the PKCS12 * standard. */ #include "bsafe.h" #include "demoutil.h" /* In samples/common/include */ /* The DIGESTLEN is 20 since the digest being used is SHA1. The * KEYLEN is the size of the key desired and the ITERATIONCOUNT * is the number of iterations for the digest. A minimum of * 1024 is recommended for the ITERATIONCOUNT */ #define DIGESTLEN 20 #define KEYLEN 128 #define IVLEN 8 #define ITERATIONCOUNT 1024 /* This function creates a key or an iv depending on the ID passed. For a * key used for encryption the ID should be 1, for a MAC key the ID should * be 3 and for iv data the ID should be 2. The keyLength field is how many * bytes are needed of data. The resulting data is stored in the info field. * This field should be cleared by the user when it is no longer needed as the * data is often highly sensitive. */ int CreatePKCS12Info( unsigned char *password, unsigned int passwordLen, unsigned char *salt, unsigned int saltLen, unsigned int keyLength, unsigned int iterations, unsigned int ID, ITEM *info ); /* This function copies inputData as many times as needed into output * Data until outputData is full. This function assumes that outputData * has already been initialized and that outputData.len holds the length * of the desired buffer. As a warning, the memory for outputData should * be cleared by the user of this function */ int CopyAll( unsigned char *inputData, unsigned int inputDataLen, ITEM *outputData ); /* This function digests the information in firstInput and secondInput an * iteration number of times. The digest algorithm used is SHA1 and after * the function is called the outputString contains the digested data. This * data should be cleared by the user when it is no longer needed. */ int DigestIterations( ITEM firstInput, ITEM secondInput, unsigned int iterations, ITEM *outputString ); /* This function is used to update the oldInput string as specified in the * PKCS 12 standard. This is done by thinking of the oldInput strings as * strings I_0, I_1, ... , I_n where each I_i is the same length as inputSize. * Each I_i is then updated as follows I_i = I_i + addedInput + 1. oldInput * is then the concatenation of these new I_i's. The data stored in oldInput * should be cleared by the user when it is no longer needed. */ int CreateInputString( ITEM *oldInput, ITEM addedInput, unsigned int inputSize ); #ifdef CRYPTOC_APP #define MAIN pkcs12pbeMain #else #define MAIN main #endif int MAIN(int argc, char **argv) { /* In this case the password and the salt are hardcoded. If the key or iv * that is being created is for encryption then the salt can be generated * by a random number generator. If the key or iv that is being created is * for decryption then these fields must be set with the same password and * salt that were used for encryption. */ unsigned char password[] = "password"; unsigned int passwordLen = sizeof(password) - 1; unsigned char salt[] = {0x12, 0x34, 0x56, 0x78, 0x91, 0x23, 0x45, 0x67}; unsigned int saltLen = 8; unsigned char *pass; unsigned int passLen; /* These are used to store the resulting data */ ITEM keyData = {NULL, 0}; ITEM ivData = {NULL, 0}; /* These ID numbers are as specified in the pkcs 12 standard. The keyId * is 1 since the key being created is an encryption key and the ivId is * 2 since this is for iv Data */ int keyID = 1; int ivID = 2; /* This is used to check for errors */ int status; /* This is used as a place holder variable */ unsigned int i; do{ /* The RSA_* demo code utilities are described in comon/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(" PKCS 12 Key Generation Example \n"); RSA_PrintMessage("=======================================\n\n"); /* The password must be represented using two bytes instead * of one. This is done by placing a 0 byte in front of * each byte. The PKCS 12 standard also states that the * last two bytes of the password should be zero. * Here pass is getting the correct representation of the * password */ passLen = 2*passwordLen + 2; pass = T_malloc( passLen ); if ((status = (pass == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } for( i = 0; i<passwordLen; ++i ) { pass[2*i] = '\0'; pass[2 * i + 1] = password[i]; } pass[2*passwordLen] = '\0'; pass[2*passwordLen + 1] = '\0'; /* Allocating memory for the key and then creating the key * using the new password, the salt, and the iteration count */ keyData.len = KEYLEN; keyData.data = T_malloc( keyData.len ); if ((status = (keyData.data == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } if (( status = CreatePKCS12Info( pass, passLen, salt, saltLen, keyData.len, ITERATIONCOUNT, keyID, &keyData )) !=0) break; /* Printing the key data out. At this point the key data can * be used for encryption or decryption by setting this key data * into a symmetric key using KI_ITEM */ RSA_PrintBuf( "The Key Data is: ", keyData.data, keyData.len ); /* Allocating memory for the iv and then creating the iv data * using the new password, the salt, and the iteration count */ ivData.len = IVLEN; ivData.data = T_malloc( ivData.len ); if ((status = (ivData.data == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } if (( status = CreatePKCS12Info ( pass, passLen, salt, saltLen, ivData.len, ITERATIONCOUNT, ivID, &ivData )) !=0) break; /* Printing out the iv data. At this point the iv can be set into * an algorithm object by passing it in as one of the parameters in * the B_SetAlgorithmObject info parameters */ RSA_PrintBuf( "The iv Data is: ", ivData.data, ivData.len ); }while(0); /* If an error occurred print this error out */ if ( status !=0 ) { RSA_PrintError ( "pkcs12", status ); } /* Free the allocated memory */ if (keyData.data != NULL_PTR) { T_memset ( keyData.data, 0, keyData.len ); T_free ( keyData.data ); keyData.data = NULL_PTR; keyData.len = 0; } if (ivData.data != NULL_PTR) { T_memset ( ivData.data, 0, ivData.len ); T_free ( ivData.data ); ivData.data = NULL_PTR; ivData.len = 0; } return ( status ); } /* end main */ /* This function creates a key or an iv depending on the ID passed. For an * encryption key the ID should be 1, for a MAC key the ID should * be 3 and for iv data the ID should be 2. The keyLength field is how many * bytes are needed of data. The resulting data is stored in the info field. * This field should be cleared by the user when it is no longer needed as * the data is often highly sensitive. */ int CreatePKCS12Info(unsigned char *password, unsigned int passwordLen, unsigned char *salt, unsigned int saltLen, unsigned int keyLength, unsigned int iterations, unsigned int ID, ITEM *info) { /* This is used in order to check for errors */ int status = 0; /* InputBytes is defined in the PKCS12 standard, for SHA1 it is defined to be 64 (512 bits) */ int inputBytes = 64; /* This is used to find out how many blocks have to be generated */ int blocks; int temp; int hold; /* This is used to keep track of how many bytes have been generated */ int offset; /* This is used as a place holder variable */ int i; /* idString, saltString, passwordString, and inputString are all defined * in the PKCS 12 standard. outputString contains the output of the * digest function, holdString contains the outputString repeated several * times as defined in the standard, and dataString contains the bytes * generated */ ITEM idString = {NULL, 0}; ITEM saltString = {NULL, 0}; ITEM passwordString = {NULL, 0}; ITEM inputString = {NULL, 0}; ITEM outputString = {NULL, 0}; ITEM holdString = {NULL, 0}; ITEM dataString = {NULL, 0}; do{ /* idString is the ID repeated inputBytes number of times */ idString.len = inputBytes; idString.data = T_malloc ( idString.len ); if ((status = (idString.data == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } T_memset ( idString.data, ID, idString.len ); /* saltString is the salt repeated a number of times */ temp = saltLen/inputBytes; hold = saltLen % inputBytes; if(hold !=0) { temp = temp + 1; } saltString.len = inputBytes * temp; saltString.data = T_malloc ( saltString.len ); if ((status = (saltString.data == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } if ((status = (CopyAll ( salt, saltLen, &saltString ))) !=0) break; /* passwordString is the password reapeated a number of times */ temp = passwordLen/inputBytes; hold = passwordLen % inputBytes; if(hold !=0) { temp = temp+1; } passwordString.len = inputBytes * temp; passwordString.data = T_malloc ( passwordString.len ); if ((status = (passwordString.data == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } if ((status = CopyAll ( password, passwordLen, &passwordString )) !=0) break; /* inputString is the saltString concatenated with passwordString */ inputString.len = saltString.len + passwordString.len; inputString.data = T_malloc ( inputString.len ); if ((status = (inputString.data == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } T_memcpy ( inputString.data, saltString.data, saltString.len ); T_memcpy ( inputString.data + saltString.len, passwordString.data, passwordString.len); /* blocks contains the number of blocks that must be created */ temp = keyLength/DIGESTLEN; hold = keyLength % DIGESTLEN; if (hold !=0) { temp = temp + 1; } blocks = temp; /* outputString must have enough space to contain the output * of the digest */ outputString.len = DIGESTLEN; outputString.data = T_malloc ( outputString.len ); if ((status = (outputString.data == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } /* holdString must have the size of inputBytes */ holdString.len = inputBytes; holdString.data = T_malloc ( holdString.len ); if ((status = (holdString.data == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } /* dataString must have enough space to contain all of the * bytes generated */ dataString.len = blocks * DIGESTLEN; dataString.data = T_malloc ( dataString.len ); if ((status = (dataString.data == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } /* In the beginning no bytes have been created */ offset = 0; for(i = 0; i < blocks; ++i) { /* Digest the idString with the inputString an iteration * number of times */ if ((status = DigestIterations ( idString, inputString, iterations, &outputString )) !=0) break; /* Copy the output of this digest into dataString and set the offset * to say that more bytes have been generated. */ T_memcpy ( dataString.data + offset, outputString.data, outputString.len ); offset = offset + outputString.len; /* If more blocks need to be created update the inputString */ if (i < (blocks - 1)) { /* The first part is to copy the outputString into the holdString * repeating the output String until holdString is full. */ if ((status = CopyAll ( outputString.data, outputString.len, &holdString )) !=0) break; /* Use this holdString and the old inputString to create the new * inputString */ if ((status = CreateInputString ( &inputString, holdString, holdString.len )) !=0) break; } } /* Copy the number of info bytes needed from the dataString into * the info string */ T_memcpy ( info -> data, dataString.data, info -> len ); }while(0); if (status !=0) { RSA_PrintError( "CreatePKCS12Info", status ); } /* Free up the memory allocated in this function */ if (idString.data != NULL_PTR) { T_memset ( idString.data, 0, idString.len ); T_free ( idString.data ); idString.data = NULL_PTR; idString.len = 0; } if (saltString.data != NULL_PTR) { T_memset ( saltString.data, 0, saltString.len ); T_free ( saltString.data ); saltString.data = NULL_PTR; saltString.len = 0; } if (passwordString.data != NULL_PTR) { T_memset ( passwordString.data, 0, passwordString.len ); T_free ( passwordString.data ); passwordString.data = NULL_PTR; passwordString.len = 0; } if (inputString.data != NULL_PTR) { T_memset ( inputString.data, 0, inputString.len ); T_free ( inputString.data ); inputString.data = NULL_PTR; inputString.len = 0; } if (outputString.data != NULL_PTR) { T_memset ( outputString.data, 0, outputString.len ); T_free ( outputString.data ); outputString.data = NULL_PTR; outputString.len = 0; } if (holdString.data != NULL_PTR) { T_memset ( holdString.data, 0, holdString.len ); T_free ( holdString.data ); holdString.data = NULL_PTR; holdString.len = 0; } if (dataString.data != NULL_PTR) { T_memset ( dataString.data, 0, dataString.len ); T_free ( dataString.data ); dataString.data = NULL_PTR; dataString.len = 0; } return ( status ); } /* End CreatePKCS12Info */ /* This function copies inputData as many times as needed into output * Data until outputData is full. This function assumes that outputData * has already been initialized and that outputData.len holds the length * of the desired buffer. As a warning, the memory for outputData should * be cleared by the user of this function */ int CopyAll(unsigned char *inputData, unsigned int inputDataLen, ITEM *outputData) { /* Used to check for errors */ int status = 0; /* Used to find how many times the inputData needs to be copied */ int passes; int remainder; /* Used as a place holder variable */ int i; /* Used to say how many Bytes have been copied */ int offset; do{ passes = (int)(outputData->len/inputDataLen); remainder = outputData->len - (passes * inputDataLen); offset = 0; /* Copy the inputData into the output Data */ for(i = 0; i < passes; ++i) { T_memcpy ( outputData ->data + offset, inputData, inputDataLen ); offset = offset + inputDataLen; } /* If more bytes are needed copy only those number of bytes */ if(remainder != 0) { T_memcpy ( outputData -> data + offset, inputData, remainder ); offset = offset + remainder; } }while(0); if(status !=0) { RSA_PrintError ( "CopyAll", status ); } return ( status ); } /* end CopyAll */ /* This function digests the information in firstInput and secondInput an * iteration number of times. The digest algorithm used is SHA1 and after * the function is called the outputString contains the digested data. This * data should be cleared by the user when it is no longer needed. */ int DigestIterations(ITEM firstInput, ITEM secondInput, unsigned int iterations, ITEM *outputString) { /* Used to check for errors */ int status = 0; /* Used as a place holder */ unsigned int i; /* Used to store the digest information */ B_ALGORITHM_OBJ digester = (B_ALGORITHM_OBJ)NULL_PTR; B_ALGORITHM_METHOD *DIGEST_CHOOSER[] = { &AM_SHA, (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 }; do{ /* Create and initialize the digest with SHA1 */ if ((status = B_CreateAlgorithmObject ( &digester )) !=0) break; if ((status = B_SetAlgorithmInfo ( digester, AI_SHA1, NULL_PTR )) !=0) break; if ((status = B_DigestInit ( digester, (B_KEY_OBJ)NULL_PTR, DIGEST_CHOOSER, (A_SURRENDER_CTX *)NULL_PTR )) !=0) break; /* Digest the first input */ if ((status = B_DigestUpdate ( digester, firstInput.data, firstInput.len, (A_SURRENDER_CTX *)NULL_PTR )) !=0) break; /* Digest the second input */ if ((status = B_DigestUpdate ( digester, secondInput.data, secondInput.len, (A_SURRENDER_CTX *)NULL_PTR )) !=0) break; /* Store the output of the digest in outputString */ if ((status = B_DigestFinal ( digester, outputString-> data, &outputString ->len, DIGESTLEN, (A_SURRENDER_CTX *)NULL_PTR)) !=0) break; /* If iteration is one then the function is done */ if (iterations > 1) { /* Otherwise digest the output the remainder times */ for(i = 1; i < iterations; i++) { if ((status = B_DigestUpdate ( digester, outputString -> data, outputString -> len, (A_SURRENDER_CTX *)NULL_PTR )) !=0) break; if ((status = B_DigestFinal ( digester, outputString-> data, &outputString ->len, DIGESTLEN, (A_SURRENDER_CTX *)NULL_PTR )) !=0) break; } } }while(0); if(status !=0) { RSA_PrintError ( "DigestIterations", status ); } /* Free the allocated memory */ B_DestroyAlgorithmObject ( &digester ); return ( status ); } /* End DigestIterations */ /* This function is used to update the oldInput string as specified in the * PKCS 12 standard. This is done by thinking of the oldInput strings as * strings I_0, I_1, ... , I_n where each I_i is the same length as inputSize. * Each I_i is then updated as follows I_i = I_i + addedInput + 1. oldInput * is then the concatenation of these new I_i's. The data stored in oldInput * should be cleared by the user when it is no longer needed. */ int CreateInputString(ITEM *oldInput, ITEM addedInput, unsigned int inputSize) { /* Used to check for errors */ int status=0; /* oldInputPart is part of the oldInput (an I_i) * addedInputPart is the addedInput * addedPart is I_i + addedInputPart + 1 * afterAdding is the I_i's concatenated together */ unsigned char *oldInputPart = NULL; unsigned char *addedInputPart = NULL; unsigned char *addedPart = NULL; unsigned char *afterAdding = NULL; /* carry and check are used to help with the addition */ unsigned char carry; unsigned int check; /* This is used to keep track of how many parts we have */ unsigned int passes; /* This keeps track of how many bytes have been generated */ unsigned int offset; /* These variables are used as placeholders */ unsigned int i, j, k; /* passes holds the number of parts there are. One thing to note * is that in this division there should be no remainder. (This * follows directly from the PKCS 12 standare */ passes = (int)((double)oldInput->len/(double)inputSize); /* No bytes have been generated */ offset = 0; do{ /* Each part should have the size inputSize */ oldInputPart = T_malloc ( inputSize ); if ((status = (oldInputPart == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } /* addedInputPart is just the addedInput */ addedInputPart = T_malloc ( inputSize ); if ((status = (addedInputPart == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } T_memcpy ( addedInputPart, addedInput.data, addedInput.len ); addedPart = T_malloc ( inputSize ); if ((status = (addedPart == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } /* The total length generated is the same as before */ afterAdding = T_malloc ( oldInput->len ); if ((status = (afterAdding == NULL_PTR)) != 0) { status = RSA_DEMO_E_ALLOC; break; } for(j = 0; j<passes; j++) { /* Each time copy the next part of the oldInput */ T_memcpy ( oldInputPart, oldInput -> data + offset, inputSize ); /* carry starts out as 1 */ carry = 1; /* adding the data byte by byte */ for(k = inputSize; k > 0; --k) { i = k - 1; addedPart[i] = (unsigned char)(oldInputPart[i] + addedInputPart[i] + carry); /* Find what the next carry bit should be */ check = oldInputPart[i] & addedInputPart[i]; if (( check >= 128) || (oldInputPart[i] > addedPart[i]) || (addedInputPart[i] > addedPart[i])) { carry = 1; } else { carry = 0; } } /* Copy the newly addedPart and updating offset */ T_memcpy( afterAdding + offset, addedPart, inputSize ); offset = offset + inputSize; } /* After all the additions are done, copy the new data * into the oldInput */ T_memcpy( oldInput -> data, afterAdding, offset ); }while(0); if (status !=0) { RSA_PrintError ( "CreateInputString", status ); } /* Free up allocated memory */ if (oldInputPart != NULL_PTR) { T_memset ( oldInputPart, 0, inputSize ); T_free ( oldInputPart ); oldInputPart = NULL_PTR; } if (addedInputPart != NULL_PTR) { T_memset ( addedInputPart, 0, inputSize ); T_free ( addedInputPart ); addedInputPart = NULL_PTR; } if (addedPart != NULL_PTR) { T_memset ( addedPart, 0, inputSize ); T_free ( addedPart ); addedPart = NULL_PTR; } if (afterAdding != NULL_PTR) { T_memset ( afterAdding, 0, oldInput -> len ); T_free ( afterAdding ); afterAdding = NULL_PTR; } return ( status ); } /* End CreateInputString */