RSA BSAFE Crypto-C

Cryptographic Components for C

Search

rc4sv.c

/* $Id: rc4sv.c,v 1.6 2004/12/03 02:08:42 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 program encrypts using AI_RC4, demonstrating how to serialize
 *  an algorithm object by encrypting 10 bytes at a time.  This sample
 *  then demonstrates that the encrypted data is correct by collecting
 *  the ciphertext and decrypting it all at once.  The intended use for
 *  this feature is a situation where it is necessary to save and restore
 *  the algorithm object.   If it is possible to keep a persistent
 *  algorithm object then the data can be encrypted using multiple
 *  updates.  See multencr.c and multdecr.c for and example which uses
 *  this feature.
 */

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


#define KEY_SIZE 24    /* key size in bytes */
#define INPUT_SIZE 100 /* read in 100 bytes at a time from the file */

/*  This routine uses the information in stateInfo to restore an RC4
 *  encryption algorithm object in order to encrypt the dataToEncrypt
 *  and to produce the encryptedData.  Note that the stateInfo->data is
 *  an input/output argument.  The encryptedData->data buffer must be
 *  freed when it is no longer needed.
 */
int RC4Encrypt (ITEM *stateInfo, ITEM *dataToEncrypt, ITEM *encryptedData);

/*  This routine takes an RC4 key and decrypts the given dataToDecrypt.
 *  The decryptedData->data field must be freed when it is no longer needed.
 */
int RC4DecryptAll (B_KEY_OBJ rc4Key, ITEM *dataToDecrypt,
                   ITEM *decryptedData);

/*  Reads up to outputMaxLen bytes from the inputFile.  Note that this
 *  is a destructive operation on the inputFile pointer.  After each call,
 *  inputFile is updated so that this function can be called again to
 *  continue reading data from the file.  When the end of the file is
 *  reached, endFlag will be set to 1 and the inputFile will be closed.
 */
int GetDataFromFile (FILE *inputFile, unsigned int outputMaxLen,
                     unsigned char *output, unsigned int *outputLen,
                     int *endFlag);

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

int MAIN(int argc, char *argv[])
{
  B_KEY_OBJ rc4Key = NULL;
  B_ALGORITHM_OBJ encryptionObj = NULL, randomAlgorithm = NULL;

  B_ALGORITHM_METHOD *encryptionChooser[] = {
    &AM_RC4_ENCRYPT,
    NULL
    /* This will fix a problem that the IA64 compiler finds when *
                 * seeing a short chooser list */
                #ifdef IA64_FORCE_LARGE
                                IA64_FORCE_LARGE
                #endif

  };

  FILE *inputFile = NULL;
  char userInput[RSA_DEMO_MAX_LINE_LEN];
  unsigned char dataToEncrypt[INPUT_SIZE];
  unsigned int dataToEncryptLen = sizeof (dataToEncrypt);

  ITEM initialState = {NULL, 0}, stateInfo = {NULL, 0};
  ITEM rc4KeyItem = {NULL, 0}, fileContents = {NULL, 0};
  ITEM encryptedData = {NULL, 0}, decryptedData = {NULL, 0};
  ITEM blockToEncrypt = {NULL, 0}, encryptedBlock = {NULL, 0};

  int status = 0, endFlag = 0;

  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 ("RC4 algorithm:  Encryption phase \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;

    /*  Create a random RC4 key which we will use for encryption and
        decryption  */
    if ((status = B_CreateKeyObject (&rc4Key)) != 0)
      break;

    rc4KeyItem.len = KEY_SIZE;
    rc4KeyItem.data = T_malloc (rc4KeyItem.len);
    if (rc4KeyItem.data == NULL_PTR) {
      status = RSA_DEMO_E_ALLOC;
      break;
    }

    if ((status = B_GenerateRandomBytes
                    (randomAlgorithm, rc4KeyItem.data, rc4KeyItem.len,
                     (A_SURRENDER_CTX *)NULL_PTR)) != 0)
      break;

    RSA_PrintBuf ("RC4 Key", rc4KeyItem.data, rc4KeyItem.len);

    if ((status = B_SetKeyInfo (rc4Key, KI_Item, (POINTER)&rc4KeyItem)) != 0)
      break;

    /*  Let's zeroize the memory and free it up immediately after
        setting the key for security reasons.  */
    T_memset (rc4KeyItem.data, 0, rc4KeyItem.len);
    T_free (rc4KeyItem.data);
    rc4KeyItem.data = NULL_PTR;
    rc4KeyItem.len = 0;

    /*  We need to obtain an initial state to pass to RC4Encrypt, which
        includes the key info.  */
    if ((status = B_CreateAlgorithmObject (&encryptionObj)) != 0)
      break;

    if ((status = B_SetAlgorithmInfo (encryptionObj, AI_RC4, NULL)) != 0)
      break;

    if ((status = B_EncryptInit (encryptionObj, rc4Key, encryptionChooser,
                                 NULL)) != 0)
      break;

    if ((status = B_GetAlgorithmState (&initialState, encryptionObj)) != 0)
      break;

    /*  Since the buffer in initialState belongs to Crypto-C, we need to make
        our own local copy, since subsequent calls to Crypto-C can change
        the data pointed to by initialState.  */
    stateInfo.len = initialState.len;
    stateInfo.data = T_malloc (stateInfo.len);
    if (stateInfo.data == NULL) {
      status = RSA_DEMO_E_ALLOC;
      break;
    }

    T_memcpy (stateInfo.data, initialState.data, stateInfo.len);

    if ((status = RSA_GetCommand
                    (userInput, sizeof (userInput),
                     "Enter name of file to use for encryption test")) != 0)
      break;

    if (userInput[0] == '\0') {
      status = RSA_DEMO_E_FILE_IO;
      break;
    }

    inputFile = fopen (userInput, "rb");
    if (inputFile == NULL) {
      status = RSA_DEMO_E_FILE_IO;
      break;
    }

    /* Encrypt each "block" of data and keep track of the original data
       to use in verifying the decrypted data later. */
    endFlag = 0;
    while (endFlag == 0) {
      if ((status = GetDataFromFile (inputFile, sizeof (dataToEncrypt),
                                     dataToEncrypt, &dataToEncryptLen,
                                     &endFlag)) != 0)
        break;

      fileContents.data =
        T_realloc (fileContents.data,
                   fileContents.len + dataToEncryptLen);
      if (fileContents.data == NULL) {
        status = RSA_DEMO_E_ALLOC;
        break;
      }

      T_memcpy (fileContents.data + fileContents.len, dataToEncrypt,
                dataToEncryptLen);
      fileContents.len += dataToEncryptLen;

      RSA_PrintBuf ("Plaintext block", dataToEncrypt, dataToEncryptLen);

      blockToEncrypt.len = dataToEncryptLen;
      blockToEncrypt.data = dataToEncrypt;
      if ((status = RC4Encrypt (&stateInfo, &blockToEncrypt,
                                &encryptedBlock)) != 0)
        break;

      RSA_PrintBuf ("Ciphertext block", encryptedBlock.data,
                    encryptedBlock.len);

      /* Collect the ciphertext */
      encryptedData.data =
        T_realloc (encryptedData.data,
                   encryptedData.len + encryptedBlock.len);
      if (encryptedData.data == NULL) {
        status = RSA_DEMO_E_ALLOC;
        break;
      }

      T_memcpy (encryptedData.data + encryptedData.len, encryptedBlock.data,
                encryptedBlock.len);
      encryptedData.len += encryptedBlock.len;

      T_free (encryptedBlock.data);
      encryptedBlock.data = NULL;
    }

    if (status != 0)
      break;

    /* Now check to see if the ciphertext we accumulated was correct */
    if ((status = RC4DecryptAll (rc4Key, &encryptedData, &decryptedData)) != 0)
      break;

    RSA_PrintBuf ("Decrypted data", decryptedData.data, decryptedData.len);

    if ((decryptedData.len == fileContents.len) &&
        (T_memcmp (fileContents.data, decryptedData.data,
                   fileContents.len)) == 0) {
      RSA_PrintMessage ("Success!  ");
      RSA_PrintMessage ("The decrypted data matches the original data.\n");
    } else {
      RSA_PrintMessage ("The decrypted data does not match the original data.");
      status = RSA_DEMO_E_INFO_DOES_NOT_VERIFY;
    }
  } while (0);

  if (status != 0)
    RSA_PrintError ("rc4 saved state example", status);

  /*  Destroy the key and algorithm objects and free any memory used  */
  B_DestroyKeyObject (&rc4Key);
  B_DestroyAlgorithmObject (&encryptionObj);
  B_DestroyAlgorithmObject (&randomAlgorithm);

  T_free (encryptedData.data);
  T_free (encryptedBlock.data);

  T_memset (decryptedData.data, 0, decryptedData.len);
  T_free (decryptedData.data);

  T_memset (fileContents.data, 0, fileContents.len);
  T_free (fileContents.data);

  T_memset (rc4KeyItem.data, 0, rc4KeyItem.len);
  T_free (rc4KeyItem.data);

  T_memset (stateInfo.data, 0, stateInfo.len);
  T_free (stateInfo.data);

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

/*  This routine uses the information in stateInfo to restore an RC4
 *  encryption algorithm object in order to encrypt the dataToEncrypt
 *  and to produce the encryptedData.  Note that the stateInfo->data is
 *  an input/output argument.  The encryptedData->data buffer must be
 *  freed when it is no longer needed.
 */
int RC4Encrypt(ITEM *stateInfo, ITEM *dataToEncrypt, ITEM *encryptedData)
{
  int status = 0;
  B_ALGORITHM_OBJ encryptionObj = NULL;

  B_ALGORITHM_METHOD *encryptionChooser[] = {
    &AM_RC4_ENCRYPT,
    NULL
    /* This will fix a problem that the IA64 compiler finds when *
                 * seeing a short chooser list */
                #ifdef IA64_FORCE_LARGE
                                IA64_FORCE_LARGE
                #endif

  };

  ITEM newStateInfo = {NULL, 0}, bsfStateInfo = {NULL, 0};

  encryptedData->data = NULL;
  encryptedData->len = 0;

  do {
    if ((status = B_CreateAlgorithmObject (&encryptionObj)) != 0)
      break;

    if ((status = B_SetAlgorithmState (encryptionObj, AI_RC4, stateInfo,
                                       encryptionChooser)) != 0)
      break;

    /* The length of the ciphertext should be the same as the data to
       encrypt since RC4 is a stream cipher. */
    encryptedData->len = dataToEncrypt->len;
    encryptedData->data = T_malloc (encryptedData->len);
    if (encryptedData->data == NULL) {
      status = RSA_DEMO_E_ALLOC;
      break;
    }

    if ((status = B_EncryptUpdate (encryptionObj, encryptedData->data,
                                   &encryptedData->len, encryptedData->len,
                                   dataToEncrypt->data, dataToEncrypt->len,
                                   NULL, NULL)) != 0)
      break;

    if ((status = B_GetAlgorithmState (&bsfStateInfo, encryptionObj)) != 0)
      break;

    /* Make a copy of the information pointed to by bsfStateInfo for local
       use, since the info pointed to by bsfStateInfo could be changed or
       reclaimed by the Crypto-C library during subsequent calls. */
    newStateInfo.len = bsfStateInfo.len;
    newStateInfo.data = T_malloc (newStateInfo.len);
    if (newStateInfo.data == NULL) {
      status = RSA_DEMO_E_ALLOC;
      break;
    }

    T_memcpy (newStateInfo.data, bsfStateInfo.data, newStateInfo.len);
  } while (0);

  if (status != 0) {
    T_free (encryptedData->data);
    encryptedData->data = NULL;
    encryptedData->len = 0;

    RSA_PrintError ("RC4Encrypt", status);
  } else {
    /* Update stateInfo so the caller can have an updated algorithm object */
    T_memset (stateInfo->data, 0, stateInfo->len);
    T_free (stateInfo->data);
    stateInfo->data = newStateInfo.data;
    stateInfo->len = newStateInfo.len;
  }

  B_DestroyAlgorithmObject (&encryptionObj);

  return status;
}  /* end RC4Encrypt */

/*  This routine takes an RC4 key and decrypts the given dataToDecrypt.
 *  The decryptedData->data field must be freed when it is no longer needed.
 */
int RC4DecryptAll(B_KEY_OBJ rc4Key, ITEM *dataToDecrypt,
  ITEM *decryptedData)
{
  int status = 0;
  B_ALGORITHM_OBJ decryptionObj = NULL;

  B_ALGORITHM_METHOD *decryptionChooser[] = {
    &AM_RC4_DECRYPT,
    NULL
    /* This will fix a problem that the IA64 compiler finds when *
                 * seeing a short chooser list */
                #ifdef IA64_FORCE_LARGE
                                IA64_FORCE_LARGE
                #endif

  };

  unsigned int outputLenUpdate = 0, outputLenFinal = 0;

  decryptedData->data = NULL;
  decryptedData->len = 0;

  do {
    if ((status = B_CreateAlgorithmObject (&decryptionObj)) != 0)
      break;

    if ((status = B_SetAlgorithmInfo (decryptionObj, AI_RC4, NULL)) != 0)
      break;

    if ((status = B_DecryptInit (decryptionObj, rc4Key, decryptionChooser,
                                 NULL)) != 0)
      break;

    /* The length of the decrypted data should be the same as the ciphertext
       since RC4 is a stream cipher. */
    decryptedData->len = dataToDecrypt->len;
    decryptedData->data = T_malloc (decryptedData->len);
    if (decryptedData == NULL) {
      status = RSA_DEMO_E_ALLOC;
      break;
    }

    if ((status = B_DecryptUpdate (decryptionObj, decryptedData->data,
                                   &outputLenUpdate, decryptedData->len,
                                   dataToDecrypt->data, dataToDecrypt->len,
                                   NULL, NULL)) != 0)
      break;

    if ((status = B_DecryptFinal (decryptionObj,
                                  decryptedData->data + outputLenUpdate,
                                  &outputLenFinal,
                                  decryptedData->len - outputLenUpdate,
                                  NULL, NULL)) != 0)
      break;

    decryptedData->len = outputLenUpdate + outputLenFinal;
  } while (0);

  if (status != 0) {
    T_free (decryptedData->data);
    decryptedData->data = NULL;
    decryptedData->len = 0;

    RSA_PrintError ("RC4DecryptAll", status);
  }

  B_DestroyAlgorithmObject (&decryptionObj);

  return status;
}  /* end RC4DecryptAll */

/*  Reads up to outputMaxLen bytes from the inputFile.  Note that this
 *  is a destructive operation on the inputFile pointer.  After each call,
 *  inputFile is updated so that this function can be called again to
 *  continue reading data from the file.  When the end of the file is
 *  reached, endFlag will be set to 1 and the inputFile will be closed.
 */
int GetDataFromFile(FILE *inputFile, unsigned int outputMaxLen,
  unsigned char *output, unsigned int *outputLen, int *endFlag)
{
  unsigned char dummy;

  do {
    *outputLen = fread (output, 1, outputMaxLen, inputFile);
    if (*outputLen == outputMaxLen)
    /* Temporarily read one more character so that the end-of-file indicator
       will be set if there were exactly outputMaxLen more bytes in the file.
     */
      fread (&dummy, 1, 1, inputFile);

    if (feof (inputFile)) {
      *endFlag = 1;
      fclose (inputFile);
    }
    else {
      *endFlag = 0;
      fseek (inputFile, -1, SEEK_CUR);
    }
  } while (0);

  return (0);
}  /*  end GetDataFromFile  */

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