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