RSA BSAFE Crypto-C

Cryptographic Components for C

Search

pkcs12pbe.c

/* $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 */

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