1/*
2 * symReference.c - write keys and ciphertext blobs, read them back
3 *                  and decrypt on (possibly) a different platfrom.
4 *					Intended for use in testing multiplatform
5 *					compatibility (e.g. encrypt on 32 bit G4, decrypt
6 *					on 64-bit G5).
7 *
8 * Created by Doug Mitchell 10/31/05.
9 */
10
11#include <stdlib.h>
12#include <stdio.h>
13#include <time.h>
14#include <Security/cssm.h>
15#include <Security/cssmapple.h>
16#include "cspwrap.h"
17#include <security_cdsa_utils/cuFileIo.h>
18#include "common.h"
19#include <string.h>
20#include "cspdlTesting.h"
21#include <unistd.h>
22
23/*
24 * Defaults.
25 */
26#define LOOPS_DEF		200
27#define PTEXT_SIZE_DEF	256
28#define BLOCK_SIZE_MAX	32		/* bytes */
29
30/*
31 * Enumerate algs our own way to allow iteration.
32 */
33typedef enum {
34	ALG_ASC = 0,		/* first must be 0 */
35	ALG_DES,
36	ALG_RC2,
37	ALG_RC4,
38	ALG_RC5,
39	ALG_3DES,
40	ALG_AES,
41	ALG_AES192,
42	ALG_AES256,
43	ALG_BFISH,
44	ALG_CAST
45} SymAlg;
46
47#define ALG_FIRST			ALG_ASC
48#define ALG_LAST			ALG_CAST
49
50static void usage(char **argv)
51{
52	printf("usage: %s e|d dirName [options]\n", argv[0]);
53	printf("  e=encrypt, d=decrypt; blobs read/written in dirName\n");
54	printf("   Options:\n");
55	printf("   a=algorithm (d=DES; 3=3DES3; 2=RC2; 4=RC4; 5=RC5; a=AES; b=Blowfish; \n");
56	printf("                c=CAST; s=ASC, default=all)\n");
57	printf("   p=ptextSize (default=%d)\n", PTEXT_SIZE_DEF);
58	printf("   D (CSP/DL; default = bare CSP)\n");
59	printf("   v(erbose)\n");
60	printf("   q(uiet)\n");
61	printf("   h(elp)\n");
62	exit(1);
63}
64
65/*
66 * map SymAlg to test params
67 */
68typedef struct {
69	SymAlg				alg;
70	const char			*algStr;
71	CSSM_ALGORITHMS		cssmAlg;
72	CSSM_ENCRYPT_MODE	mode;
73	CSSM_PADDING		padding;
74	CSSM_SIZE			keySizeBits;
75	CSSM_SIZE			ivLen;		// in bytes
76} SymAlgParams;
77
78static const SymAlgParams symAlgParams[] =
79{
80	{ ALG_ASC, "ASC", CSSM_ALGID_ASC, CSSM_ALGMODE_NONE, CSSM_PADDING_NONE,
81		CSP_ASC_KEY_SIZE_DEFAULT, 0 },
82	{ ALG_DES, "DES", CSSM_ALGID_DES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
83		CSP_DES_KEY_SIZE_DEFAULT, 8 },
84	{ ALG_RC2, "RC2", CSSM_ALGID_RC2, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
85		CSP_RC2_KEY_SIZE_DEFAULT, 8 },
86	{ ALG_RC4, "RC4", CSSM_ALGID_RC4, CSSM_ALGMODE_NONE, CSSM_PADDING_NONE,
87		CSP_RC4_KEY_SIZE_DEFAULT, 0 },
88	{ ALG_RC5, "RC5", CSSM_ALGID_RC5, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
89		CSP_RC5_KEY_SIZE_DEFAULT, 8 },
90	{ ALG_3DES, "3DES", CSSM_ALGID_3DES_3KEY_EDE, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
91		CSP_DES3_KEY_SIZE_DEFAULT, 8 },
92	{ ALG_AES, "AES", CSSM_ALGID_AES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
93		CSP_AES_KEY_SIZE_DEFAULT, 16 },
94	{ ALG_AES192, "AES192", CSSM_ALGID_AES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
95		192, 24 },
96	{ ALG_AES256, "AES256", CSSM_ALGID_AES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
97		256, 32 },
98	{ ALG_BFISH, "Blowfish", CSSM_ALGID_BLOWFISH, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
99		CSP_BFISH_KEY_SIZE_DEFAULT,  8 },
100	{ ALG_CAST, "CAST", CSSM_ALGID_CAST, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
101		CSP_CAST_KEY_SIZE_DEFAULT,  8 }
102};
103
104static void genFileNames(
105	const char	*algStr,
106	char		*keyFile,
107	char		*ptextFile,
108	char		*ctextFile,
109	char		*ivFile)
110{
111	sprintf(keyFile,	"key_%s", algStr);
112	sprintf(ptextFile,	"ptext_%s", algStr);
113	sprintf(ctextFile,	"ctext_%s", algStr);
114	sprintf(ivFile,		"iv_%s", algStr);
115}
116
117/* encrypt, write blobs (key, plaintext, ciphertext, optional IV) to disk */
118static int doEncrypt(
119	CSSM_CSP_HANDLE		cspHand,
120	const SymAlgParams	*algParams,
121	CSSM_DATA			*ptext,		// mallocd, length valid, we fill data
122	CSSM_BOOL			quiet,
123	CSSM_BOOL			verbose)
124{
125	CSSM_KEY_PTR	symKey = NULL;
126	CSSM_KEY		rawKey;
127	CSSM_RETURN		crtn;
128	CSSM_DATA		ctext = {0, NULL};
129	uint8			iv[BLOCK_SIZE_MAX];
130	CSSM_DATA		ivd = {BLOCK_SIZE_MAX, iv};
131	CSSM_DATA		*ivp = NULL;
132	uint32			blockSize = 0;
133	char			keyFile[FILENAME_MAX];
134	char			ptextFile[FILENAME_MAX];
135	char			ctextFile[FILENAME_MAX];
136	char			ivFile[FILENAME_MAX];
137
138	if(!quiet) {
139		printf("...encrypting, alg %s\n", algParams->algStr);
140	}
141
142	/* generate reference key (works with CSPDL) */
143	symKey = cspGenSymKey(cspHand, algParams->cssmAlg,
144		"noLabel", 7,
145		CSSM_KEYUSE_ANY, algParams->keySizeBits, CSSM_TRUE);
146	if(symKey == NULL) {
147		printf("***Error generating key for alg %s size %u bits\n",
148			algParams->algStr, (unsigned)algParams->keySizeBits);
149		return testError(quiet);
150	}
151
152	/* get key in raw format (to get the raw blob we write to disk) */
153	crtn = cspRefKeyToRaw(cspHand, symKey, &rawKey);
154	if(crtn) {
155		printf("***Error generating raw key for alg %s size %u bits\n",
156			algParams->algStr, (unsigned)algParams->keySizeBits);
157		return testError(quiet);
158	}
159
160	appGetRandomBytes(ptext->Data, (unsigned)ptext->Length);
161
162	/*
163	 * Hack: we only need to specify block size for AES192 and AES256, which
164	 * we detect by their having an ivLen of greater than 16.
165	 */
166	if(algParams->ivLen > 16) {
167		blockSize = algParams->ivLen;
168	}
169	if(algParams->ivLen) {
170		appGetRandomBytes(iv, algParams->ivLen);
171		ivd.Length = algParams->ivLen;
172		ivp = &ivd;
173	}
174
175	crtn = cspStagedEncrypt(cspHand,
176		algParams->cssmAlg, algParams->mode, algParams->padding,
177		symKey, NULL,
178		0, blockSize, 0,
179		ivp, ptext,
180		&ctext,
181		CSSM_FALSE);
182	if(crtn) {
183		printf("***Error encrypting for alg %s size %u bits\n",
184			algParams->algStr, (unsigned)algParams->keySizeBits);
185		return testError(quiet);
186	}
187
188	/* write: key, IV, ptext, ctext */
189	genFileNames(algParams->algStr, keyFile, ptextFile, ctextFile, ivFile);
190	if(writeFile(keyFile, rawKey.KeyData.Data, (unsigned)rawKey.KeyData.Length) ||
191	   writeFile(ptextFile, ptext->Data, (unsigned)ptext->Length) ||
192	   writeFile(ctextFile, ctext.Data, (unsigned)ctext.Length)) {
193		printf("***Error writing result of alg %s size %u bits\n",
194			algParams->algStr, (unsigned)algParams->keySizeBits);
195		return testError(quiet);
196	}
197	if(ivp != NULL) {
198		if(writeFile(ivFile, ivp->Data, (unsigned)ivp->Length)) {
199			printf("***Error writing IV for alg %s size %u bits\n",
200				algParams->algStr, (unsigned)algParams->keySizeBits);
201			return testError(quiet);
202		}
203	}
204
205	/* Free resources */
206	CSSM_FreeKey(cspHand, NULL, symKey, CSSM_FALSE);
207	CSSM_FreeKey(cspHand, NULL, &rawKey, CSSM_FALSE);
208	CSSM_FREE(ctext.Data);
209	return 0;
210}
211
212/* read blobs (key, plaintext, ciphertext, optional IV) from disk, decrypt, compare plaintext */
213static int doDecrypt(
214	CSSM_CSP_HANDLE		cspHand,
215	const SymAlgParams	*algParams,
216	CSSM_BOOL			quiet,
217	CSSM_BOOL			verbose)
218{
219	CSSM_KEY		symKey;
220	uint8			*symKeyBits;
221	unsigned		symKeyLen;				// in bytes
222	CSSM_DATA		symKeyData;
223	CSSM_RETURN		crtn;
224	uint8			*ctextChars;
225	unsigned		ctextLen = 0;
226	CSSM_DATA		ctext;
227	CSSM_DATA		rptext = {0, NULL};		// recovered/decrytped
228	uint8			*refPTextChars;
229	unsigned		refPtextLen;
230	CSSM_DATA		refPtext = {0, NULL};	// expected
231	uint8			*iv = NULL;
232	unsigned		ivLen;
233	CSSM_DATA		ivd = {BLOCK_SIZE_MAX, iv};
234	CSSM_DATA		*ivp = NULL;
235	uint32			blockSize = 0;
236	char			keyFile[FILENAME_MAX];
237	char			ptextFile[FILENAME_MAX];
238	char			ctextFile[FILENAME_MAX];
239	char			ivFile[FILENAME_MAX];
240
241	if(!quiet) {
242		printf("...decrypting, alg %s\n", algParams->algStr);
243	}
244
245	/*
246	 * Hack: we only need to specify block size for AES192 and AES256, which
247	 * we detect by their having an ivLen of greater than 16.
248	 */
249	if(algParams->ivLen > 16) {
250		blockSize = algParams->ivLen;
251	}
252	if(algParams->ivLen) {
253		ivp = &ivd;
254		ivd.Length = algParams->ivLen;
255	}
256
257	/* read: key, IV, ptext, ctext */
258	genFileNames(algParams->algStr, keyFile, ptextFile, ctextFile, ivFile);
259	if(readFile(keyFile, &symKeyBits, &symKeyLen) ||
260	   readFile(ptextFile, &refPTextChars, &refPtextLen) ||
261	   readFile(ctextFile, &ctextChars, &ctextLen)) {
262		printf("***Error reading reference blobs for alg %s size %u bits\n",
263			algParams->algStr, (unsigned)algParams->keySizeBits);
264		return testError(quiet);
265	}
266	if(ivp != NULL) {
267		if(readFile(ivFile, &iv, &ivLen)) {
268			printf("***Error writing IV for alg %s size %u bits\n",
269				algParams->algStr, (unsigned)algParams->keySizeBits);
270			return testError(quiet);
271		}
272		if(ivLen != algParams->ivLen) {
273			printf("***Unexpected IV length: expect %u found %u\n",
274				(unsigned)algParams->ivLen, (unsigned)ivLen);
275			if(testError(quiet)) {
276				return 1;
277			}
278		}
279		ivd.Data = iv;
280	}
281	ctext.Data = ctextChars;
282	ctext.Length = ctextLen;
283	refPtext.Data = refPTextChars;
284	refPtext.Length = refPtextLen;
285
286	/* generate key */
287	symKeyData.Data = symKeyBits;
288	symKeyData.Length = symKeyLen;
289
290	crtn = cspGenSymKeyWithBits(cspHand, algParams->cssmAlg,
291		CSSM_KEYUSE_ANY, &symKeyData, symKeyLen, &symKey);
292	if(crtn) {
293		printf("***Error creating key for alg %s keySize %u\n",
294			algParams->algStr, (unsigned)algParams->keySizeBits);
295		return testError(quiet);
296	}
297
298	crtn = cspStagedDecrypt(cspHand,
299		algParams->cssmAlg, algParams->mode, algParams->padding,
300		&symKey, NULL,
301		0, blockSize, 0,
302		ivp, &ctext,
303		&rptext,
304		CSSM_FALSE);
305	if(crtn) {
306		printf("***Error decrypting for alg %s size %u bits\n",
307			algParams->algStr, (unsigned)algParams->keySizeBits);
308		return testError(quiet);
309	}
310
311	/* moment of truth */
312	if(!appCompareCssmData(&rptext, &refPtext)) {
313		printf("***DATA MISCOMPARE AFTER DECRYPT alg %s size %u bits\n",
314			algParams->algStr, (unsigned)algParams->keySizeBits);
315		return testError(quiet);
316	}
317
318	/* Free resources */
319	CSSM_FreeKey(cspHand, NULL, &symKey, CSSM_FALSE);
320	free(symKeyBits);		// mallocd by readFile()
321	free(refPTextChars);
322	free(ctextChars);
323	CSSM_FREE(rptext.Data);	// mallocd by CSP
324	if(iv) {
325		free(iv);
326	}
327	return 0;
328}
329
330
331int main(int argc, char **argv)
332{
333	int					arg;
334	char				*argp;
335	CSSM_DATA			ptext;
336	CSSM_CSP_HANDLE 	cspHand;
337	unsigned			currAlg;				// ALG_xxx
338	int					rtn = 0;
339
340	/*
341	 * User-spec'd params
342	 */
343	unsigned	minAlg = ALG_FIRST;
344	unsigned	maxAlg = ALG_LAST;
345	CSSM_BOOL	verbose = CSSM_FALSE;
346	CSSM_BOOL	quiet = CSSM_FALSE;
347	CSSM_BOOL	bareCsp = CSSM_TRUE;
348	bool		encrypt = false;
349	unsigned	ptextSize = PTEXT_SIZE_DEF;
350	char		*dirName;
351
352	if(argc < 3) {
353		usage(argv);
354	}
355	switch(argv[1][0]) {
356		case 'e':
357			encrypt = true;
358			break;
359		case 'd':
360			encrypt = false;
361			break;
362		default:
363			usage(argv);
364	}
365	dirName = argv[2];
366
367	for(arg=3; arg<argc; arg++) {
368		argp = argv[arg];
369		switch(argp[0]) {
370			case 'a':
371				if(argp[1] != '=') {
372					usage(argv);
373				}
374				switch(argp[2]) {
375					case 's':
376						minAlg = maxAlg = ALG_ASC;
377						break;
378					case 'd':
379						minAlg = maxAlg = ALG_DES;
380						break;
381					case '3':
382						minAlg = maxAlg = ALG_3DES;
383						break;
384					case '2':
385						minAlg = maxAlg = ALG_RC2;
386						break;
387					case '4':
388						minAlg = maxAlg = ALG_RC4;
389						break;
390					case '5':
391						minAlg = maxAlg = ALG_RC5;
392						break;
393					case 'a':
394						minAlg = maxAlg = ALG_AES;
395						break;
396					case 'b':
397						minAlg = maxAlg = ALG_BFISH;
398						break;
399					case 'c':
400						minAlg = maxAlg = ALG_CAST;
401						break;
402					default:
403						usage(argv);
404				}
405				break;
406		    case 'v':
407		    	verbose = CSSM_TRUE;
408				break;
409			case 'D':
410				bareCsp = CSSM_FALSE;
411				break;
412			case 'p':
413				ptextSize = atoi(&argp[2]);
414				break;
415		    case 'q':
416		    	quiet = CSSM_TRUE;
417				break;
418		    case 'h':
419		    default:
420				usage(argv);
421		}
422	}
423	ptext.Data = (uint8 *)CSSM_MALLOC(ptextSize);
424	if(ptext.Data == NULL) {
425		printf("Insufficient heap space\n");
426		exit(1);
427	}
428	ptext.Length = ptextSize;
429
430	testStartBanner("symReference", argc, argv);
431
432	cspHand = cspDlDbStartup(bareCsp, NULL);
433	if(cspHand == 0) {
434		exit(1);
435	}
436
437	if(chdir(dirName)) {
438		perror(dirName);
439		printf("Error accessing directory %s. Aborting.\n", dirName);
440		exit(1);
441	}
442	for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
443		const SymAlgParams *algParams = &symAlgParams[currAlg];
444
445		if(encrypt) {
446			rtn = doEncrypt(cspHand, algParams, &ptext, quiet, verbose);
447		}
448		else {
449			rtn = doDecrypt(cspHand, algParams, quiet, verbose);
450		}
451		if(rtn) {
452			break;
453		}
454	}	/* for algs */
455
456	cspShutdown(cspHand, bareCsp);
457	if((rtn == 0) && !quiet) {
458		printf("%s test complete\n", argv[0]);
459	}
460	CSSM_FREE(ptext.Data);
461	return rtn;
462}
463
464
465