| RSA BSAFE Crypto-C |
Cryptographic Components for C |
| Search |
/* $Id: mdigsv.c,v 1.6 2004/12/03 02:08:36 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 will prompt the user for a digest algorithm to use and * a file to digest. It demonstrates multiple ways to digest this data. * First, all of the data may be digested at once. Data may be digested * by using multiple updates, requiring a persistent algorithm object. * Or, finally, data may be digested by serializing an algorithm object * and restoring it as soon as more data to digest is available. */ #include "bsafe.h" #include "demoutil.h" /* in samples/common/include */ #include "menuutil.h" /* in samples/common/include */ #define MAX_DIGEST_LEN 20 #define INPUT_SIZE 100 /* read in 100 bytes at a time from the file */ /* This routine uses the information in stateInfo to restore a digest * algorithm object in order to digest the dataToDigest. When there is * no longer any more data to digest, restore an algorithm object with * the last stateInfo so that B_DigestFinal can be called to obtain the * digest. * * Note that stateInfo->data is an input/output argument. */ int DigestDataSavedState (ITEM *stateInfo, B_INFO_TYPE digestAI, ITEM *dataToDigest); /* This routine takes a filename and digests the data in the file using * multiple updates. Note that digestedData->data must be freed when * it is no longer needed. */ int DigestDataMultipleUpdates (char *filename, B_INFO_TYPE digestAI, ITEM *digest); /* This routine takes a filename and digests the data in the file all at * once. In other words, the contents of the file are read into memory * and is supplied to Crypto-C with one B_DecryptUpdate call. Note that * digestedData->data must be freed when it is no longer needed. */ int DigestDataAll (char *filename, B_INFO_TYPE digestAI, ITEM *digest); /* This routine prompts the user with the possible digest algorithms * and places the appropriate AI in the given digestAI argument. */ int DigestAlgorithmPrompt (B_INFO_TYPE *digestAI); /* 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); B_ALGORITHM_METHOD *DIGEST_CHOOSER[] = { &AM_SHA, &AM_MD5, &AM_MD2, (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 mdigsvMain #else #define MAIN main #endif int MAIN(int argc, char *argv[]) { int status = 0, endFlag = 0; char userInput[RSA_DEMO_MAX_LINE_LEN]; FILE *inputFile = NULL; unsigned char dataBlock[INPUT_SIZE]; unsigned int dataBlockLen = 0; B_INFO_TYPE digestAlg; B_ALGORITHM_OBJ digestObj = NULL; ITEM initialState = {NULL, 0}, stateInfo = {NULL, 0}; ITEM blockToDigest = {NULL, 0}, digest = {NULL, 0}; ITEM digestMultipleUpdates = {NULL, 0}, digestAll = {NULL, 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 ("Message Digest Saved State Example\n"); RSA_PrintMessage ("==================================\n"); if ((status = DigestAlgorithmPrompt (&digestAlg)) != 0) break; if ((status = RSA_GetCommand (userInput, sizeof (userInput), "Enter name of file to digest")) != 0) break; if (userInput[0] == '\0') { status = RSA_DEMO_E_FILE_IO; break; } if ((status = B_CreateAlgorithmObject (&digestObj)) != 0) break; if ((status = B_SetAlgorithmInfo (digestObj, digestAlg, NULL)) != 0) break; if ((status = B_DigestInit (digestObj, NULL, DIGEST_CHOOSER, NULL)) != 0) break; if ((status = B_GetAlgorithmState (&initialState, digestObj)) != 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); inputFile = fopen (userInput, "rb"); if (inputFile == NULL) { status = RSA_DEMO_E_FILE_IO; break; } RSA_PrintMessage ("\nDigesting data, restoring and saving algorithm "); RSA_PrintMessage ("object for each block...\n"); while (endFlag == 0) { if ((status = GetDataFromFile (inputFile, sizeof (dataBlock), dataBlock, &dataBlockLen, &endFlag)) != 0) break; blockToDigest.data = dataBlock; blockToDigest.len = dataBlockLen; if ((status = DigestDataSavedState (&stateInfo, digestAlg, &blockToDigest)) != 0) break; } if (status != 0) break; B_DestroyAlgorithmObject (&digestObj); if ((status = B_CreateAlgorithmObject (&digestObj)) != 0) break; /* Now we have the stateInfo for an algorithm object which has had all of the data to digest fed into it. Restore that algorithm object and finalize the digesting process to get the digest. */ if ((status = B_SetAlgorithmState (digestObj, digestAlg, &stateInfo, DIGEST_CHOOSER)) != 0) break; digest.len = MAX_DIGEST_LEN; digest.data = T_malloc (digest.len); if (digest.data == NULL) { status = RSA_DEMO_E_ALLOC; break; } if ((status = B_DigestFinal (digestObj, digest.data, &digest.len, digest.len, NULL)) != 0) break; RSA_PrintBuf ("Digested Data", digest.data, digest.len); /* Now obtain the digest using alternate methods, as a sanity check that the digest we retrieved above is correct. The above method is useful if an application finds it necessary to have to save and restore a digest algorithm object. If an application can keep an algorithm object around (for example, an application which digests a potentially large file), the best approach would be to use multiple calls to B_DigestUpdate as is done in the following function. */ RSA_PrintMessage ("\nUsing muliple calls to B_DigestUpdate...\n"); if ((status = DigestDataMultipleUpdates (userInput, digestAlg, &digestMultipleUpdates)) != 0) break; RSA_PrintBuf ("Digested Data", digestMultipleUpdates.data, digestMultipleUpdates.len); RSA_PrintMessage ("\nReading entire file into memory and "); RSA_PrintMessage ("digesting all at once...\n"); if ((status = DigestDataAll (userInput, digestAlg, &digestAll)) != 0) break; RSA_PrintBuf ("Digested Data", digestAll.data, digestAll.len); if ((digest.len != digestMultipleUpdates.len) || (digest.len != digestAll.len)) { status = RSA_DEMO_E_INFO_DOES_NOT_VERIFY; break; } if ((T_memcmp (digest.data, digestMultipleUpdates.data, digest.len) != 0) || (T_memcmp (digest.data, digestAll.data, digest.len) != 0)) { status = RSA_DEMO_E_INFO_DOES_NOT_VERIFY; break; } RSA_PrintMessage ("\nCongratulations!! "); RSA_PrintMessage ("All paths lead to the same digest...\n"); } while (0); if (status != 0) RSA_PrintError ("mdigsv", status); B_DestroyAlgorithmObject (&digestObj); if (stateInfo.data != NULL) { T_memset (stateInfo.data, 0, stateInfo.len); T_free (stateInfo.data); } if (digest.data != NULL) T_free (digest.data); if (digestMultipleUpdates.data != NULL) T_free (digestMultipleUpdates.data); if (digestAll.data != NULL) T_free (digestAll.data); return (status); } /* end main */ /* This routine uses the information in stateInfo to restore a digest * algorithm object in order to digest the dataToDigest. When there is * no longer any more data to digest, restore an algorithm object with * the last stateInfo so that B_DigestFinal can be called to obtain the * digest. * * Note that stateInfo->data is an input/output argument. */ int DigestDataSavedState(ITEM *stateInfo, B_INFO_TYPE digestAI, ITEM *dataToDigest) { int status = 0; B_ALGORITHM_OBJ digestObj = NULL; ITEM newStateInfo = {NULL, 0}, bsfStateInfo = {NULL, 0}; do { if ((status = B_CreateAlgorithmObject (&digestObj)) != 0) break; if ((status = B_SetAlgorithmState (digestObj, digestAI, stateInfo, DIGEST_CHOOSER)) != 0) break; if ((status = B_DigestUpdate (digestObj, dataToDigest->data, dataToDigest->len, NULL)) != 0) break; if ((status = B_GetAlgorithmState (&bsfStateInfo, digestObj)) != 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) RSA_PrintError ("DigestDataSavedState", 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 (&digestObj); return status; } /* end DigestDataSavedState */ /* This routine takes a filename and digests the data in the file using * multiple updates. Note that digestedData->data must be freed when * it is no longer needed. */ int DigestDataMultipleUpdates(char *filename, B_INFO_TYPE digestAI, ITEM *digest) { int status = 0, endFlag = 0; unsigned char dataBlock[INPUT_SIZE]; unsigned int dataBlockLen = 0; B_ALGORITHM_OBJ digestObj = NULL; FILE *inputFile = NULL; digest->data = NULL; digest->len = 0; do { if ((status = B_CreateAlgorithmObject (&digestObj)) != 0) break; if ((status = B_SetAlgorithmInfo (digestObj, digestAI, NULL)) != 0) break; if ((status = B_DigestInit (digestObj, NULL, DIGEST_CHOOSER, NULL)) != 0) break; inputFile = fopen (filename, "rb"); if (inputFile == NULL) { status = RSA_DEMO_E_FILE_IO; break; } while (endFlag == 0) { if ((status = GetDataFromFile (inputFile, sizeof (dataBlock), dataBlock, &dataBlockLen, &endFlag)) != 0) break; if ((status = B_DigestUpdate (digestObj, dataBlock, dataBlockLen, NULL)) != 0) break; } digest->len = MAX_DIGEST_LEN; digest->data = T_malloc (digest->len); if (digest->data == NULL) { status = RSA_DEMO_E_ALLOC; break; } if ((status = B_DigestFinal (digestObj, digest->data, &digest->len, digest->len, NULL)) != 0) break; } while (0); if (status != 0) { T_free (digest->data); digest->data = NULL; digest->len = 0; RSA_PrintError ("DigestDataAll", status); } B_DestroyAlgorithmObject (&digestObj); return status; } /* end DigestDataAll */ /* This routine takes a filename and digests the data in the file all at * once. In other words, the contents of the file are read into memory * and is supplied to Crypto-C with one B_DecryptUpdate call. Note that * digestedData->data must be freed when it is no longer needed. */ int DigestDataAll(char *filename, B_INFO_TYPE digestAI, ITEM *digest) { int status = 0, endFlag = 0; unsigned char dataBlock[INPUT_SIZE]; unsigned int dataBlockLen = 0; B_ALGORITHM_OBJ digestObj = NULL; FILE *inputFile = NULL; ITEM fileContents = {NULL, 0}; digest->data = NULL; digest->len = 0; do { inputFile = fopen (filename, "rb"); if (inputFile == NULL) { status = RSA_DEMO_E_FILE_IO; break; } while (endFlag == 0) { if ((status = GetDataFromFile (inputFile, sizeof (dataBlock), dataBlock, &dataBlockLen, &endFlag)) != 0) break; fileContents.data = T_realloc (fileContents.data, fileContents.len + dataBlockLen); if (fileContents.data == NULL) { status = RSA_DEMO_E_ALLOC; break; } T_memcpy (fileContents.data + fileContents.len, dataBlock, dataBlockLen); fileContents.len += dataBlockLen; } if ((status = B_CreateAlgorithmObject (&digestObj)) != 0) break; if ((status = B_SetAlgorithmInfo (digestObj, digestAI, NULL)) != 0) break; if ((status = B_DigestInit (digestObj, NULL, DIGEST_CHOOSER, NULL)) != 0) break; if ((status = B_DigestUpdate (digestObj, fileContents.data, fileContents.len, NULL)) != 0) break; digest->len = MAX_DIGEST_LEN; digest->data = T_malloc (digest->len); if (digest->data == NULL) { status = RSA_DEMO_E_ALLOC; break; } if ((status = B_DigestFinal (digestObj, digest->data, &digest->len, digest->len, NULL)) != 0) break; } while (0); if (status != 0) { T_free (digest->data); digest->data = NULL; digest->len = 0; RSA_PrintError ("DigestDataAll", status); } B_DestroyAlgorithmObject (&digestObj); return status; } /* end DigestDataAll */ /* This routine prompts the user with the possible digest algorithms * and places the appropriate AI in the given digestAI argument. */ int DigestAlgorithmPrompt(B_INFO_TYPE *digestAI) { int status = 0; RSA_DEMO_TABLE_ENTRY digestInfoTable[3]; RSA_DEMO_TABLE_ENTRY *result = NULL; digestInfoTable[0].description = "SHA-1"; digestInfoTable[0].val.ai = AI_SHA1; digestInfoTable[1].description = "MD5"; digestInfoTable[1].val.ai = AI_MD5; digestInfoTable[2].description = "MD2"; digestInfoTable[2].val.ai = AI_MD2; do { RSA_PrintMessage ("Digest Algorithms\n"); status = ChooseTableEntryPrompt (digestInfoTable, sizeof (digestInfoTable) / sizeof (digestInfoTable[0]), &result); if (status != 0) break; if (result != NULL) { *digestAI = result->val.ai; status = 0; } } while (0); if (status != 0) { *digestAI = NULL; RSA_PrintError ("DigestAlgorithmPrompt", status); } return status; } /* end DigestAlgorithmPrompt */ /* 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 */