1/*
2 * ccOneShot.c - Ensure that one-shot CommonDigest routines behave correctly.
3 *
4 * Written 3/31/06 by Doug Mitchell.
5 */
6
7#include <stdlib.h>
8#include <stdio.h>
9#include <time.h>
10#include "common.h"
11#include <string.h>
12#include <CommonCrypto/CommonDigest.h>
13#include <openssl/hmac.h>
14
15/*
16 * Defaults.
17 */
18#define LOOPS_DEF		200
19#define MIN_DATA_SIZE	1
20#define MAX_DATA_SIZE	10000			/* bytes */
21#define LOOP_NOTIFY		20
22
23/*
24 * Enumerate algs our own way to allow iteration.
25 */
26typedef enum {
27	ALG_MD2 = 1,
28	ALG_MD4,
29	ALG_MD5,
30	ALG_SHA1,
31	ALG_SHA224,
32	ALG_SHA256,
33	ALG_SHA384,
34	ALG_SHA512
35} HashAlg;
36#define ALG_FIRST			ALG_MD2
37#define ALG_LAST			ALG_SHA512
38
39static void usage(char **argv)
40{
41	printf("usage: %s [options]\n", argv[0]);
42	printf("   Options:\n");
43	printf("   l=loops (default %d)\n", LOOPS_DEF);
44	printf("   q(uiet)\n");
45	printf("   h(elp)\n");
46	exit(1);
47}
48
49/* the context pointers are void * here for polymorphism later on */
50typedef int (*initFcn)(void *ctx);
51typedef int (*updateFcn)(void *ctx, const void *data, CC_LONG len);
52typedef int (*finalFcn)(unsigned char *md, void *ctx);
53typedef unsigned char (*oneShotFcn)(const void *data, CC_LONG len, unsigned char *md);
54
55typedef struct {
56	HashAlg				alg;
57	const char			*algName;
58	size_t				digestSize;
59	initFcn				init;
60	updateFcn			update;
61	finalFcn			final;
62	oneShotFcn			oneShot;
63} CommonDigestInfo;
64
65/* casts are necessary to cover the void* context args */
66static const CommonDigestInfo digests[] =
67{
68	{	ALG_MD2, "MD2", CC_MD2_DIGEST_LENGTH,
69		(initFcn)CC_MD2_Init, (updateFcn)CC_MD2_Update,
70		(finalFcn)CC_MD2_Final, (oneShotFcn)CC_MD2
71	},
72	{	ALG_MD4, "MD4", CC_MD4_DIGEST_LENGTH,
73		(initFcn)CC_MD4_Init, (updateFcn)CC_MD4_Update,
74		(finalFcn)CC_MD4_Final, (oneShotFcn)CC_MD4
75	},
76	{	ALG_MD5, "MD5", CC_MD5_DIGEST_LENGTH,
77		(initFcn)CC_MD5_Init, (updateFcn)CC_MD5_Update,
78		(finalFcn)CC_MD5_Final, (oneShotFcn)CC_MD5
79	},
80	{	ALG_SHA1, "SHA1", CC_SHA1_DIGEST_LENGTH,
81		(initFcn)CC_SHA1_Init, (updateFcn)CC_SHA1_Update,
82		(finalFcn)CC_SHA1_Final, (oneShotFcn)CC_SHA1
83	},
84	{	ALG_SHA224, "SHA224", CC_SHA224_DIGEST_LENGTH,
85		(initFcn)CC_SHA224_Init, (updateFcn)CC_SHA224_Update,
86		(finalFcn)CC_SHA224_Final, (oneShotFcn)CC_SHA224
87	},
88	{	ALG_SHA256, "SHA256", CC_SHA256_DIGEST_LENGTH,
89		(initFcn)CC_SHA256_Init, (updateFcn)CC_SHA256_Update,
90		(finalFcn)CC_SHA256_Final, (oneShotFcn)CC_SHA256
91	},
92	{	ALG_SHA384, "SHA384", CC_SHA384_DIGEST_LENGTH,
93		(initFcn)CC_SHA384_Init, (updateFcn)CC_SHA384_Update,
94		(finalFcn)CC_SHA384_Final, (oneShotFcn)CC_SHA384
95	},
96	{	ALG_SHA512, "SHA512", CC_SHA512_DIGEST_LENGTH,
97		(initFcn)CC_SHA512_Init, (updateFcn)CC_SHA512_Update,
98		(finalFcn)CC_SHA512_Final, (oneShotFcn)CC_SHA512
99	},
100};
101#define NUM_DIGESTS		(sizeof(digests) / sizeof(digests[0]))
102
103static const CommonDigestInfo *findDigestInfo(unsigned alg)
104{
105	unsigned dex;
106	for(dex=0; dex<NUM_DIGESTS; dex++) {
107		if((unsigned)(digests[dex].alg) == alg) {
108			return &digests[dex];
109		}
110	}
111	return NULL;
112}
113
114
115/*
116 * These consts let us allocate context and digest buffers for
117 * any arbitrary algorithm.
118 */
119#define MAX_DIGEST_SIZE		64
120#define MAX_CONTEXT_SIZE	sizeof(CC_SHA512_CTX)
121
122/* staged digest with random updates */
123static void doStaged(
124	const CommonDigestInfo *digestInfo,
125	const unsigned char *ptext,
126	unsigned ptextLen,
127	unsigned char *md)
128{
129	char ctx[MAX_CONTEXT_SIZE];
130	unsigned thisMove;
131
132	digestInfo->init(ctx);
133	while(ptextLen) {
134		thisMove = genRand(1, ptextLen);
135		digestInfo->update(ctx, ptext, thisMove);
136		ptext += thisMove;
137		ptextLen -= thisMove;
138	}
139	digestInfo->final(md, ctx);
140}
141
142static int doTest(
143	const CommonDigestInfo *digestInfo,
144	const unsigned char *ptext,
145	unsigned ptextLen,
146	bool quiet)
147{
148	unsigned char mdStaged[MAX_DIGEST_SIZE];
149	unsigned char mdOneShot[MAX_DIGEST_SIZE];
150
151	digestInfo->oneShot(ptext, ptextLen, mdOneShot);
152	doStaged(digestInfo, ptext, ptextLen, mdStaged);
153	if(memcmp(mdStaged, mdOneShot, digestInfo->digestSize)) {
154		printf("***Digest miscompare for %s\n", digestInfo->algName);
155		if(testError(quiet)) {
156			return 1;
157		}
158	}
159	return 0;
160}
161
162int main(int argc, char **argv)
163{
164	int						arg;
165	char					*argp;
166	unsigned				loop;
167	uint8					*ptext;
168	size_t					ptextLen;
169	unsigned				currAlg;
170	const CommonDigestInfo	*digestInfo;
171	int						rtn = 0;
172	int						i;
173
174	/*
175	 * User-spec'd params
176	 */
177	unsigned	loops = LOOPS_DEF;
178	bool		quiet = false;
179
180	for(arg=1; arg<argc; arg++) {
181		argp = argv[arg];
182		switch(argp[0]) {
183		    case 'l':
184				loops = atoi(&argp[2]);
185				break;
186		    case 'q':
187		    	quiet = true;
188				break;
189		    case 'h':
190		    default:
191				usage(argv);
192		}
193	}
194	ptext = (uint8 *)malloc(MAX_DATA_SIZE);
195	if(ptext == NULL) {
196		printf("Insufficient heap space\n");
197		exit(1);
198	}
199	/* ptext length set in test loop */
200
201	printf("Starting ccOneShot; args: ");
202	for(i=1; i<argc; i++) {
203		printf("%s ", argv[i]);
204	}
205	printf("\n");
206
207	for(currAlg=ALG_FIRST; currAlg<=ALG_LAST; currAlg++) {
208		digestInfo = findDigestInfo(currAlg);
209		if(!quiet) {
210			printf("Testing alg %s\n", digestInfo->algName);
211		}
212		for(loop=1; ; loop++) {
213			ptextLen = genRand(MIN_DATA_SIZE, MAX_DATA_SIZE);
214			appGetRandomBytes(ptext, ptextLen);
215			if(!quiet) {
216			   	if((loop % LOOP_NOTIFY) == 0) {
217					printf("..loop %d ptextLen %lu\n",
218						loop, (unsigned long)ptextLen);
219				}
220			}
221
222			if(doTest(digestInfo, ptext, ptextLen, quiet)) {
223				rtn = 1;
224				break;
225			}
226			if(loops && (loop == loops)) {
227				break;
228			}
229		}	/* main loop */
230		if(rtn) {
231			break;
232		}
233
234	}	/* for algs */
235
236	if((rtn == 0) && !quiet) {
237		printf("%s test complete\n", argv[0]);
238	}
239	free(ptext);
240	return rtn;
241}
242
243
244