RSA BSAFE Crypto-C

Cryptographic Components for C

Search

parsepkcs5.c

/* $Id: parsepkcs5.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 is a Password-Based Encryption program.  This sample demonstrates
 *  how to parse the OID for PKCS 5v2.  This sample first encryts using
 *  PKCS5v2 and then obtains the BER encoded algorithm info.  There may
 *  be times when a program will not want to set the algorithm info using
 *  the BER encoding - as this can make the code size bigger.  (The reason
 *  for this is that PKCS 5v2 can contain many different encryption algorithms.
 *  All the possible algorithms need to be loaded up in case the algorithm OID
 *  points to that algorithm.)  If you know which algorithm was used then you
 *  can limit the number of algorithms that are loaded.
 *
 *  This sample makes the assumptions that the encoding
 *  of the data does not have any indefinate lengths.  This will always
 *  be true if the encodind is obtained from Crypto-C.  This program also
 *  assumes the format will be correct.  It does some error checking but
 *  a user might want to add more.
 */

#include "bsafe.h"
#include "demoutil.h"  /* in samples/common/include */
#include "bsfutil.h"   /* in samples/common/include */

#define SALT_LEN 8
#define MAX_PW_LEN 20
#define BLOCK_SIZE 8
#define KEY_BITS 24*8

B_ALGORITHM_METHOD *PBE_CHOOSER[] = {
  &AM_SHA,
  &AM_RC2_CBC_ENCRYPT,
  &AM_RC2_CBC_DECRYPT,
  &AM_DES_EDE3_CBC_ENCRYPT,
  (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
};

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

int MAIN(int argc, char *argv[])
{
  B_ALGORITHM_OBJ pbEncrypter = (B_ALGORITHM_OBJ)NULL_PTR;
  B_ALGORITHM_OBJ rc2pbeEncrypter = (B_ALGORITHM_OBJ)NULL_PTR;
  B_ALGORITHM_OBJ tdespbeEncrypter = (B_ALGORITHM_OBJ)NULL_PTR;
  B_ALGORITHM_OBJ randomAlgorithm = (B_ALGORITHM_OBJ)NULL_PTR;

  B_KEY_OBJ pbeKey = (B_KEY_OBJ)NULL_PTR;

  B_PKCS5_V2_PBE_PARAMS pbeParams;

  B_PKCS5_V2_PBE_PARAMS rc2pbeParams;
  B_PKCS5_V2_PBE_PARAMS tdespbeParams;

  B_DIGEST_SPECIFIER prfParams;

  A_RC2_CBC_PARAMS rc2Params;

  unsigned char saltData[SALT_LEN];

  unsigned char iv[8] = {
    0x17, 0x7D, 0x65, 0xB6, 0x70, 0xF9, 0xE2, 0xEE
  };

  unsigned char enteredPassword[MAX_PW_LEN];
  ITEM keyItem;

  unsigned char dataToEncrypt[] = "Encrypt this sentence.";
  unsigned int dataToEncryptLen;
  unsigned char *encryptedData = NULL_PTR;
  unsigned int outputLenUpdate, outputLenFinal;
  unsigned int encryptedDataLen;

  ITEM *rc2AlgorithmInfo;
  ITEM *tdesAlgorithmInfo;
  ITEM pbeAlgInfo;

  /*  The OID's that should be in the PKCS #5v2 message */
  unsigned char pkcs5OID[11] = {
    0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0d
  };

  unsigned char kdfOID[11] = {
    0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c
  };

  unsigned char rc2OID[10] = {
    0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x02
  };

  unsigned char tdesOID[10] = {
    0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x07
  };


  unsigned char *newSalt;
  unsigned int saltLen;
  unsigned int iterationCount;
  unsigned int iterationCountLen;
  unsigned int keylength;
  unsigned int keylengthLen;

  A_RC2_CBC_PARAMS possibleRC2Params;
  unsigned char *possibleIV;
  unsigned int possibleIVLen;

  int index = 0;

  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 ("Obtaining the algorithm info for PKCS #5v2 with RC2\n");
    RSA_PrintMessage ("===================================================\n");

    /*  Initialize a random algorithm object using a procedure described in
        samples/common/include/bsfutil.h  */
    if ((status = RSA_CreateRandomAlgorithmObject (&randomAlgorithm)) != 0)
      break;

    /*  Generate the bytes to get an 8-byte random "salt" */
    if ((status = B_GenerateRandomBytes (randomAlgorithm, saltData, SALT_LEN,
                                         (A_SURRENDER_CTX *)NULL_PTR)) != 0)
      break;

    /*  Create an algorithm object. */
    if ((status = B_CreateAlgorithmObject (&rc2pbeEncrypter)) != 0)
      break;

    /*  See the pkcs5v2pbe sample for an explanation of the parameters */
    rc2Params.effectiveKeyBits = 80;
    rc2Params.iv = iv;

    prfParams.digestInfoType = AI_SHA1;
    prfParams.digestInfoParams = NULL_PTR;

    rc2pbeParams.salt.data = saltData;
    rc2pbeParams.salt.len = 8;
    rc2pbeParams.iterationCount = 1000;
    rc2pbeParams.keyLenOctets = KEY_BITS/8;

    rc2pbeParams.pseudoRandomFunction = AI_HMAC;
    rc2pbeParams.prfParams = (POINTER)&prfParams;
    rc2pbeParams.prfBER = AI_HMAC_BER;

    rc2pbeParams.encryptionAlgorithm = AI_RC2_CBCPad;
    rc2pbeParams.encAlgParams = (POINTER)&rc2Params;
    rc2pbeParams.encAlgBER = AI_RC2_CBCPadBER;


    RSA_PrintBuf ("Salt", saltData, BLOCK_SIZE);

    if ((status = B_SetAlgorithmInfo (rc2pbeEncrypter, AI_PKCS5_V2_PBE,
                                      (POINTER)&rc2pbeParams)) != 0)
       break;

    if ((status = B_GetAlgorithmInfo((POINTER *)&rc2AlgorithmInfo, rc2pbeEncrypter,
                                      AI_PKCS5_V2_PBE_BER)) != 0)
       break;

    RSA_PrintBuf ("\nAlgorithm OID for PKCS#5v2 with RC2",
                   rc2AlgorithmInfo ->data, rc2AlgorithmInfo ->len);

    RSA_PrintMessage ("\nObtaining the algorithm info for PKCS #5v2 with triple DES\n");
    RSA_PrintMessage ("==========================================================\n");

    /*  Create an algorithm object. */
    if ((status = B_CreateAlgorithmObject (&tdespbeEncrypter)) != 0)
      break;

    prfParams.digestInfoType = AI_SHA1;
    prfParams.digestInfoParams = NULL_PTR;

    tdespbeParams.salt.data = saltData;
    tdespbeParams.salt.len = 8;
    tdespbeParams.iterationCount = 1000;
    tdespbeParams.keyLenOctets = KEY_BITS/8;

    tdespbeParams.pseudoRandomFunction = AI_HMAC;
    tdespbeParams.prfParams = (POINTER)&prfParams;
    tdespbeParams.prfBER = AI_HMAC_BER;

    tdespbeParams.encryptionAlgorithm = AI_DES_EDE3_CBCPadIV8;
    tdespbeParams.encAlgParams = (POINTER)&iv;
    tdespbeParams.encAlgBER = AI_DES_EDE3_CBCPadBER;

    if ((status = B_SetAlgorithmInfo (tdespbeEncrypter, AI_PKCS5_V2_PBE,
                                      (POINTER)&tdespbeParams)) != 0)
       break;

    if ((status = B_GetAlgorithmInfo((POINTER *)&tdesAlgorithmInfo, tdespbeEncrypter,
                                      AI_PKCS5_V2_PBE_BER)) != 0)
       break;

    RSA_PrintBuf ("\nAlgorithm OID for PKCS#5v2 with Triple DES",
                   tdesAlgorithmInfo ->data, tdesAlgorithmInfo ->len);

    /*  Pick one of the previous algorithm informations to parse */
    pbeAlgInfo.len = tdesAlgorithmInfo ->len;
    pbeAlgInfo.data = T_malloc(pbeAlgInfo.len);
    T_memcpy(pbeAlgInfo.data, tdesAlgorithmInfo->data, pbeAlgInfo.len);

    RSA_PrintMessage("\nParsing the PKCS #5v2 algorithm OID\n");
    RSA_PrintMessage("===================================\n");

    if ((status = B_CreateAlgorithmObject(&pbEncrypter)) != 0)
      break;

    /*  First check to make sure that the PKCS #5 algorithm id is correct */
    /*  The sequence of the OID should be as follows:
        SEQUENCE {
          OID
          params
        }

        where the params are

       SEQUENCE {
         AlgID  -- Key Derivation Function
         AlgID  -- encryption algorithm
       }
     */

    index = 0;

    /*  The information should start with a sequence identifier */
    if (pbeAlgInfo.data[index] != 0x30)
    {
      RSA_PrintMessage("The OID is not formatted correctly \n");
      break;
    }
    index++;

    /*  Check the length of the data */
    if (pbeAlgInfo.data[index] == 0x81)
    {
      index = index + 2;
    }else{
      if (pbeAlgInfo.data[index] == 0x82)
        index = index + 3;
      else{
      index = index + 1;
      }
    }

    RSA_PrintBuf("\nPKCS  #5v2 OID data ", pbeAlgInfo.data + index, 11);

    if ( T_memcmp(pbeAlgInfo.data + index, pkcs5OID, 11) == 1)
    {
      RSA_PrintMessage("The OID for PKCS #5v2 is incorrect. \n");
      break;
    }

    index = index + 11;

    /*  The next section should be the parameter section */

    if (pbeAlgInfo.data[index] != 0x30)
    {
      RSA_PrintMessage("The parameter information is not formatted correctly \n");
      break;
    }
    index++;

    if (pbeAlgInfo.data[index] == 0x81)
    {
      index = index + 2;
    }else{
      if (pbeAlgInfo.data[index] == 0x82)
        index = index + 3;
      else{
      index = index + 1;
      }
    }

    /*  The first parameter should be the OID for the key derivation
     *  function.  In this case we only support one function for this
     *  so the OID should match.
    */
    if (pbeAlgInfo.data[index] != 0x30)
    {
      RSA_PrintMessage("The parameter OID is not formatted correctly \n");
      break;
    }
    index++;

    if (pbeAlgInfo.data[index] == 0x81)
    {
      index = index + 2;
    }else{
      if (pbeAlgInfo.data[index] == 0x82)
        index = index + 3;
      else{
      index = index + 1;
      }
    }

    RSA_PrintBuf("\nKey Derivation Function OID data ", pbeAlgInfo.data + index, 11);

    if ( T_memcmp(pbeAlgInfo.data + index, kdfOID, 11) == 1)
    {
      RSA_PrintMessage("The OID for the key derivation function is incorrect. \n");
      break;
    }

    index = index + 11;

    /*  Now that we know the Key Derivation Function we can set this into the
     *  parameters.  In this case since the pseudoRandomFunction is standard
     *  then we can use the same parameters as we used above.
    */
    pbeParams.pseudoRandomFunction = AI_HMAC;
    pbeParams.prfParams = (POINTER)&prfParams;
    pbeParams.prfBER = AI_HMAC_BER;

    /*  Next we have the parameters for the key derivation function.  These need
     *  to be obtained and set into a pkcs #5 parameter structure */

    if (pbeAlgInfo.data[index] != 0x30)
    {
      RSA_PrintMessage("The OID is not formatted correctly \n");
      break;
    }
    index++;

    if (pbeAlgInfo.data[index] == 0x81)
    {
      index = index + 2;
    }else{
      if (pbeAlgInfo.data[index] == 0x82)
        index = index + 3;
      else{
      index = index + 1;
      }
    }
    /*  The first parameter is the salt */
    if (pbeAlgInfo.data[index] != 0x04)
    {
      RSA_PrintMessage("The salt value is not formatted correctly \n");
      break;
    }
    index++;

    if (pbeAlgInfo.data[index] == 0x81)
    {
      saltLen = pbeAlgInfo.data[index + 1];

      index = index + 2;
    }else{
      if (pbeAlgInfo.data[index] == 0x82)
      {
        saltLen = pbeAlgInfo.data[index + 2] + pbeAlgInfo.data[index + 1] * 256;
        index = index + 3;
      }else{
       saltLen = pbeAlgInfo.data[index];
       index = index + 1;
      }
    }
    newSalt = T_malloc(saltLen);
    T_memcpy(newSalt, pbeAlgInfo.data + index, saltLen);
    index = index + saltLen;

    RSA_PrintBuf("Salt value is ", newSalt, saltLen);

    /*  Set the salt into the parameter information */
    pbeParams.salt.data = saltData;
    pbeParams.salt.len = 8;

    /*  Next we should have the iteration Count */
    if (pbeAlgInfo.data[index] != 0x02)
    {
      RSA_PrintMessage("Error when finding the iteration count\n");
      break;
    }
    index++;

    /* Assume the length of the iteration count only takes one byte */
    iterationCountLen = pbeAlgInfo.data[index];
    iterationCount = 0;
    if (iterationCountLen == 1)
    {
      iterationCount = pbeAlgInfo.data[index + 1];
      pbeParams.iterationCount = iterationCount;
      index = index + 2;
    }else{
      if(iterationCountLen == 2)
      {
        iterationCount = pbeAlgInfo.data[index + 2] + 256 * pbeAlgInfo.data[index + 1];
        pbeParams.iterationCount = iterationCount;
        index = index + 3;
      }else{
        if(iterationCountLen == 3)
        {
          iterationCount = pbeAlgInfo.data[index + 3] + 256 * pbeAlgInfo.data[index + 2]
                           + 256 * 16 * pbeAlgInfo.data[index + 1];
          pbeParams.iterationCount = iterationCount;
          index = index + 4;
        }else{
          RSA_PrintMessage("Iteration count is too large \n");
          break;
        }
      }
    }

    RSA_PrintMessage("The iteration count is %d\n",iterationCount);

    /*  Now get the key Length */
    if (pbeAlgInfo.data[index] != 0x02)
    {
      RSA_PrintMessage("Error when finding the Key Length\n");
      break;
    }
    index++;

    /* Assume the length of the keylength only takes one byte */
    keylengthLen = pbeAlgInfo.data[index];
    keylength = 0;
    if (keylengthLen == 1)
    {
      keylength = pbeAlgInfo.data[index + 1];
      pbeParams.keyLenOctets = keylength;
      index = index + 2;
    }else{
      if(keylengthLen == 2)
      {
        keylength = pbeAlgInfo.data[index + 2] + 256 * pbeAlgInfo.data[index + 1];
        pbeParams.keyLenOctets = keylength;
        index = index + 3;
      }else{
        if(keylengthLen == 3)
        {
          keylength = pbeAlgInfo.data[index + 3] + 256 * pbeAlgInfo.data[index + 2]
                           + 256 * 16 * pbeAlgInfo.data[index + 1];
          pbeParams.keyLenOctets = keylength;
          index = index + 4;
        }else{
          RSA_PrintMessage("Key length is too large \n");
          break;
        }
      }
    }

    RSA_PrintMessage("The Key Length is %d\n",keylength);

    /*  Now we have to figure out what Encryption Algorithm was used.
     *  For this we should have specific types of algorithm that could
     *  have been used.  We can compare the OID's to the OID for the
     *  algorithms we are prepared to test against and then from there
     *  obtain the correct parameters.
    */

    /*  First make sure we have a sequence */

     if (pbeAlgInfo.data[index] != 0x30)
    {
      RSA_PrintMessage("The Encryption Algorithm OID is not formatted correctly \n");
      break;
    }
    index++;

    if (pbeAlgInfo.data[index] == 0x81)
    {
      index = index + 2;
    }else{
      if (pbeAlgInfo.data[index] == 0x82)
        index = index + 3;
      else{
      index = index + 1;
      }
    }

    if ( T_memcmp(pbeAlgInfo.data + index, rc2OID, 10) == 0)
    {
      index = index + 10;
      RSA_PrintMessage("\nRC2 is the encryption algorithm. \n");

      /*  Now we need to parse the parameters for RC2*/
      /*  The first parameter is the effective key bits */
      if (pbeAlgInfo.data[index] != 0x30){
        RSA_PrintMessage("The parameters for RC2 are formatted incorrectly \n");
        break;
      }
      index++;
      if (pbeAlgInfo.data[index] == 0x81)
      {
        index = index + 2;
      }else{
        if (pbeAlgInfo.data[index] == 0x82)
          index = index + 3;
        else{
          index = index + 1;
        }
      }

      if (pbeAlgInfo.data[index] != 0x02)
      {
        RSA_PrintMessage("The effective Key Bits for RC2 is not formatted correctly \n");
        break;
      }
      /*  Since the key size can only be from 1 to 128 we will assume that the length bit
       *  is only one byte.  We will not do a check here */
      index = index + 2;
      possibleRC2Params.effectiveKeyBits = pbeAlgInfo.data[index];

      RSA_PrintMessage("The effective Key Bits are %d\n", possibleRC2Params.effectiveKeyBits);

      index++;

      /*  The other parameters is the iv */
      if (pbeAlgInfo.data[index] != 0x04)
      {
        RSA_PrintMessage("RC2 iv value is not formatted correctly \n");
        break;
      }
      index++;
      possibleIVLen = pbeAlgInfo.data[index];
      possibleRC2Params.iv = T_malloc(possibleIVLen);
      T_memcpy(possibleRC2Params.iv, pbeAlgInfo.data + index + 1, possibleIVLen);

      RSA_PrintBuf("The iv is ", possibleRC2Params.iv, possibleIVLen);
      RSA_PrintMessage("\n");

      pbeParams.encryptionAlgorithm = AI_RC2_CBCPad;
      pbeParams.encAlgParams = (POINTER)&possibleRC2Params;
      pbeParams.encAlgBER = AI_RC2_CBCPadBER;

    }else{
      if ( T_memcmp(pbeAlgInfo.data + index, tdesOID, 10) == 0)
      {
        index = index + 10;
        RSA_PrintMessage("\nTriple DES is the encryption algorithm\n");

        /*  Triple DES just has one parameter and that is an iv */
        if (pbeAlgInfo.data[index] != 0x04)
        {
          RSA_PrintMessage("Triple DES iv value is not formatted correctly \n");
          break;
        }
        index++;
        possibleIVLen = pbeAlgInfo.data[index];
        possibleIV = T_malloc(possibleIVLen);
        T_memcpy(possibleIV, pbeAlgInfo.data + index + 1, possibleIVLen);

        RSA_PrintBuf("The iv is ", possibleIV, possibleIVLen);
        RSA_PrintMessage("\n");

        pbeParams.encryptionAlgorithm = AI_DES_EDE3_CBCPadIV8;
        pbeParams.encAlgParams = (POINTER)&possibleIV;
        pbeParams.encAlgBER = AI_DES_EDE3_CBCPadBER;

      }else{
        RSA_PrintMessage("This is an unrecognized encryption algorithm\n");
        break;
      }
    }

    /*  Now that we have the information let's use it to encrypt */
    if ((status = B_SetAlgorithmInfo (pbEncrypter, AI_PKCS5_V2_PBE,
                                      (POINTER)&pbeParams)) != 0)
      break;

    /*  Create a key object. */
    if ((status = B_CreateKeyObject (&pbeKey)) != 0)
      break;

    /*  Set the key object -- The data portion of the struct
        is the password.  An insecure method for entering the
        password would be the following.  This is simply for
        illustrative purposes, not for duplication.  (A real
        application would not allow the password to be echoed
        to the screen)  */
    if ((status = RSA_GetCommand ((char *)enteredPassword,
                                  sizeof (enteredPassword),
                                  "Enter a password for encryption")) != 0)
      break;

    keyItem.data = enteredPassword;
    keyItem.len = T_strlen ((char *)enteredPassword);

    if ((status = B_SetKeyInfo (pbeKey, KI_Item, (POINTER)&keyItem)) != 0)
      break;

    /*  Zeroize the memory and free it up immediately after
        setting the key for security reasons.  */
    T_memset (enteredPassword, 0, sizeof (enteredPassword));

    dataToEncryptLen = T_strlen ((char *)dataToEncrypt) + 1;

    RSA_PrintBuf ("Data To Encrypt", dataToEncrypt, dataToEncryptLen);

    /*  Init -- A key is needed before the algorithm object can
                be initialized for encryption.  In PBE, the password is
                the key.  */
    if ((status = B_EncryptInit (pbEncrypter, pbeKey, PBE_CHOOSER,
                                 (A_SURRENDER_CTX *)NULL_PTR)) != 0)
      break;

    /*  Update  */
    encryptedDataLen = dataToEncryptLen + BLOCK_SIZE;
    encryptedData = T_malloc (encryptedDataLen);
    if (encryptedData == NULL_PTR) {
      status = RSA_DEMO_E_ALLOC;
      break;
    }

    if ((status = B_EncryptUpdate (pbEncrypter, encryptedData,
                                   &outputLenUpdate, encryptedDataLen,
                                   dataToEncrypt, dataToEncryptLen,
                                   (B_ALGORITHM_OBJ)NULL_PTR,
                                   (A_SURRENDER_CTX *)NULL_PTR)) != 0)
      break;

    /*  Final  */
    if ((status = B_EncryptFinal (pbEncrypter, encryptedData + outputLenUpdate,
                                  &outputLenFinal,
                                  encryptedDataLen - outputLenUpdate,
                                  (B_ALGORITHM_OBJ)NULL_PTR,
                                  (A_SURRENDER_CTX *)NULL_PTR)) != 0)
      break;

    encryptedDataLen = outputLenUpdate + outputLenFinal;

    RSA_PrintBuf ("Encrypted Data", encryptedData, encryptedDataLen);

  } while (0);

  if (status != 0)
    RSA_PrintError ("parsepkcs5.c", status);

  /*  Destroy the key and algorithm objects  */
  B_DestroyKeyObject (&pbeKey);
  B_DestroyAlgorithmObject (&pbEncrypter);
  B_DestroyAlgorithmObject (&randomAlgorithm);
  B_DestroyAlgorithmObject (&tdespbeEncrypter);
  B_DestroyAlgorithmObject (&rc2pbeEncrypter);

  /*  Free up any memory allocated, save it to a file or print it out first
      if you need to save it.  */
  if (encryptedData != NULL_PTR) {
    T_free (encryptedData);
    encryptedData = NULL_PTR;
  }

  return (status);
} /*  end main  */

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