--- /dev/null
+/*
+ * ccOneShot.c - Ensure that one-shot CommonDigest routines behave correctly.
+ *
+ * Written 3/31/06 by Doug Mitchell.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include "common.h"
+#include <string.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <openssl/hmac.h>
+
+/*
+ * Defaults.
+ */
+#define LOOPS_DEF 200
+#define MIN_DATA_SIZE 1
+#define MAX_DATA_SIZE 10000 /* bytes */
+#define LOOP_NOTIFY 20
+
+/*
+ * Enumerate algs our own way to allow iteration.
+ */
+typedef enum {
+ ALG_MD2 = 1,
+ ALG_MD4,
+ ALG_MD5,
+ ALG_SHA1,
+ ALG_SHA224,
+ ALG_SHA256,
+ ALG_SHA384,
+ ALG_SHA512
+} HashAlg;
+#define ALG_FIRST ALG_MD2
+#define ALG_LAST ALG_SHA512
+
+static void usage(char **argv)
+{
+ printf("usage: %s [options]\n", argv[0]);
+ printf(" Options:\n");
+ printf(" l=loops (default %d)\n", LOOPS_DEF);
+ printf(" q(uiet)\n");
+ printf(" h(elp)\n");
+ exit(1);
+}
+
+/* the context pointers are void * here for polymorphism later on */
+typedef int (*initFcn)(void *ctx);
+typedef int (*updateFcn)(void *ctx, const void *data, CC_LONG len);
+typedef int (*finalFcn)(unsigned char *md, void *ctx);
+typedef unsigned char (*oneShotFcn)(const void *data, CC_LONG len, unsigned char *md);
+
+typedef struct {
+ HashAlg alg;
+ const char *algName;
+ size_t digestSize;
+ initFcn init;
+ updateFcn update;
+ finalFcn final;
+ oneShotFcn oneShot;
+} CommonDigestInfo;
+
+/* casts are necessary to cover the void* context args */
+static const CommonDigestInfo digests[] =
+{
+ { ALG_MD2, "MD2", CC_MD2_DIGEST_LENGTH,
+ (initFcn)CC_MD2_Init, (updateFcn)CC_MD2_Update,
+ (finalFcn)CC_MD2_Final, (oneShotFcn)CC_MD2
+ },
+ { ALG_MD4, "MD4", CC_MD4_DIGEST_LENGTH,
+ (initFcn)CC_MD4_Init, (updateFcn)CC_MD4_Update,
+ (finalFcn)CC_MD4_Final, (oneShotFcn)CC_MD4
+ },
+ { ALG_MD5, "MD5", CC_MD5_DIGEST_LENGTH,
+ (initFcn)CC_MD5_Init, (updateFcn)CC_MD5_Update,
+ (finalFcn)CC_MD5_Final, (oneShotFcn)CC_MD5
+ },
+ { ALG_SHA1, "SHA1", CC_SHA1_DIGEST_LENGTH,
+ (initFcn)CC_SHA1_Init, (updateFcn)CC_SHA1_Update,
+ (finalFcn)CC_SHA1_Final, (oneShotFcn)CC_SHA1
+ },
+ { ALG_SHA224, "SHA224", CC_SHA224_DIGEST_LENGTH,
+ (initFcn)CC_SHA224_Init, (updateFcn)CC_SHA224_Update,
+ (finalFcn)CC_SHA224_Final, (oneShotFcn)CC_SHA224
+ },
+ { ALG_SHA256, "SHA256", CC_SHA256_DIGEST_LENGTH,
+ (initFcn)CC_SHA256_Init, (updateFcn)CC_SHA256_Update,
+ (finalFcn)CC_SHA256_Final, (oneShotFcn)CC_SHA256
+ },
+ { ALG_SHA384, "SHA384", CC_SHA384_DIGEST_LENGTH,
+ (initFcn)CC_SHA384_Init, (updateFcn)CC_SHA384_Update,
+ (finalFcn)CC_SHA384_Final, (oneShotFcn)CC_SHA384
+ },
+ { ALG_SHA512, "SHA512", CC_SHA512_DIGEST_LENGTH,
+ (initFcn)CC_SHA512_Init, (updateFcn)CC_SHA512_Update,
+ (finalFcn)CC_SHA512_Final, (oneShotFcn)CC_SHA512
+ },
+};
+#define NUM_DIGESTS (sizeof(digests) / sizeof(digests[0]))
+
+static const CommonDigestInfo *findDigestInfo(unsigned alg)
+{
+ unsigned dex;
+ for(dex=0; dex<NUM_DIGESTS; dex++) {
+ if((unsigned)(digests[dex].alg) == alg) {
+ return &digests[dex];
+ }
+ }
+ return NULL;
+}
+
+
+/*
+ * These consts let us allocate context and digest buffers for
+ * any arbitrary algorithm.
+ */
+#define MAX_DIGEST_SIZE 64
+#define MAX_CONTEXT_SIZE sizeof(CC_SHA512_CTX)
+
+/* staged digest with random updates */
+static void doStaged(
+ const CommonDigestInfo *digestInfo,
+ const unsigned char *ptext,
+ unsigned ptextLen,
+ unsigned char *md)
+{
+ char ctx[MAX_CONTEXT_SIZE];
+ unsigned thisMove;
+
+ digestInfo->init(ctx);
+ while(ptextLen) {
+ thisMove = genRand(1, ptextLen);
+ digestInfo->update(ctx, ptext, thisMove);
+ ptext += thisMove;
+ ptextLen -= thisMove;
+ }
+ digestInfo->final(md, ctx);
+}
+
+static int doTest(
+ const CommonDigestInfo *digestInfo,
+ const unsigned char *ptext,
+ unsigned ptextLen,
+ bool quiet)
+{
+ unsigned char mdStaged[MAX_DIGEST_SIZE];
+ unsigned char mdOneShot[MAX_DIGEST_SIZE];
+
+ digestInfo->oneShot(ptext, ptextLen, mdOneShot);
+ doStaged(digestInfo, ptext, ptextLen, mdStaged);
+ if(memcmp(mdStaged, mdOneShot, digestInfo->digestSize)) {
+ printf("***Digest miscompare for %s\n", digestInfo->algName);
+ if(testError(quiet)) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int arg;
+ char *argp;
+ unsigned loop;
+ uint8 *ptext;
+ size_t ptextLen;
+ unsigned currAlg;
+ const CommonDigestInfo *digestInfo;
+ int rtn = 0;
+ int i;
+
+ /*
+ * User-spec'd params
+ */
+ unsigned loops = LOOPS_DEF;
+ bool quiet = false;
+
+ for(arg=1; arg<argc; arg++) {
+ argp = argv[arg];
+ switch(argp[0]) {
+ case 'l':
+ loops = atoi(&argp[2]);
+ break;
+ case 'q':
+ quiet = true;
+ break;
+ case 'h':
+ default:
+ usage(argv);
+ }
+ }
+ ptext = (uint8 *)malloc(MAX_DATA_SIZE);
+ if(ptext == NULL) {
+ printf("Insufficient heap space\n");
+ exit(1);
+ }
+ /* ptext length set in test loop */
+
+ printf("Starting ccOneShot; args: ");
+ for(i=1; i<argc; i++) {
+ printf("%s ", argv[i]);
+ }
+ printf("\n");
+
+ for(currAlg=ALG_FIRST; currAlg<=ALG_LAST; currAlg++) {
+ digestInfo = findDigestInfo(currAlg);
+ if(!quiet) {
+ printf("Testing alg %s\n", digestInfo->algName);
+ }
+ for(loop=1; ; loop++) {
+ ptextLen = genRand(MIN_DATA_SIZE, MAX_DATA_SIZE);
+ appGetRandomBytes(ptext, ptextLen);
+ if(!quiet) {
+ if((loop % LOOP_NOTIFY) == 0) {
+ printf("..loop %d ptextLen %lu\n",
+ loop, (unsigned long)ptextLen);
+ }
+ }
+
+ if(doTest(digestInfo, ptext, ptextLen, quiet)) {
+ rtn = 1;
+ break;
+ }
+ if(loops && (loop == loops)) {
+ break;
+ }
+ } /* main loop */
+ if(rtn) {
+ break;
+ }
+
+ } /* for algs */
+
+ if((rtn == 0) && !quiet) {
+ printf("%s test complete\n", argv[0]);
+ }
+ free(ptext);
+ return rtn;
+}
+
+