--- /dev/null
+/*
+ * symReference.c - write keys and ciphertext blobs, read them back
+ * and decrypt on (possibly) a different platfrom.
+ * Intended for use in testing multiplatform
+ * compatibility (e.g. encrypt on 32 bit G4, decrypt
+ * on 64-bit G5).
+ *
+ * Created by Doug Mitchell 10/31/05.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <Security/cssm.h>
+#include <Security/cssmapple.h>
+#include "cspwrap.h"
+#include <security_cdsa_utils/cuFileIo.h>
+#include "common.h"
+#include <string.h>
+#include "cspdlTesting.h"
+#include <unistd.h>
+
+/*
+ * Defaults.
+ */
+#define LOOPS_DEF 200
+#define PTEXT_SIZE_DEF 256
+#define BLOCK_SIZE_MAX 32 /* bytes */
+
+/*
+ * Enumerate algs our own way to allow iteration.
+ */
+typedef enum {
+ ALG_ASC = 0, /* first must be 0 */
+ ALG_DES,
+ ALG_RC2,
+ ALG_RC4,
+ ALG_RC5,
+ ALG_3DES,
+ ALG_AES,
+ ALG_AES192,
+ ALG_AES256,
+ ALG_BFISH,
+ ALG_CAST
+} SymAlg;
+
+#define ALG_FIRST ALG_ASC
+#define ALG_LAST ALG_CAST
+
+static void usage(char **argv)
+{
+ printf("usage: %s e|d dirName [options]\n", argv[0]);
+ printf(" e=encrypt, d=decrypt; blobs read/written in dirName\n");
+ printf(" Options:\n");
+ printf(" a=algorithm (d=DES; 3=3DES3; 2=RC2; 4=RC4; 5=RC5; a=AES; b=Blowfish; \n");
+ printf(" c=CAST; s=ASC, default=all)\n");
+ printf(" p=ptextSize (default=%d)\n", PTEXT_SIZE_DEF);
+ printf(" D (CSP/DL; default = bare CSP)\n");
+ printf(" v(erbose)\n");
+ printf(" q(uiet)\n");
+ printf(" h(elp)\n");
+ exit(1);
+}
+
+/*
+ * map SymAlg to test params
+ */
+typedef struct {
+ SymAlg alg;
+ const char *algStr;
+ CSSM_ALGORITHMS cssmAlg;
+ CSSM_ENCRYPT_MODE mode;
+ CSSM_PADDING padding;
+ CSSM_SIZE keySizeBits;
+ CSSM_SIZE ivLen; // in bytes
+} SymAlgParams;
+
+static const SymAlgParams symAlgParams[] =
+{
+ { ALG_ASC, "ASC", CSSM_ALGID_ASC, CSSM_ALGMODE_NONE, CSSM_PADDING_NONE,
+ CSP_ASC_KEY_SIZE_DEFAULT, 0 },
+ { ALG_DES, "DES", CSSM_ALGID_DES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
+ CSP_DES_KEY_SIZE_DEFAULT, 8 },
+ { ALG_RC2, "RC2", CSSM_ALGID_RC2, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
+ CSP_RC2_KEY_SIZE_DEFAULT, 8 },
+ { ALG_RC4, "RC4", CSSM_ALGID_RC4, CSSM_ALGMODE_NONE, CSSM_PADDING_NONE,
+ CSP_RC4_KEY_SIZE_DEFAULT, 0 },
+ { ALG_RC5, "RC5", CSSM_ALGID_RC5, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
+ CSP_RC5_KEY_SIZE_DEFAULT, 8 },
+ { ALG_3DES, "3DES", CSSM_ALGID_3DES_3KEY_EDE, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
+ CSP_DES3_KEY_SIZE_DEFAULT, 8 },
+ { ALG_AES, "AES", CSSM_ALGID_AES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
+ CSP_AES_KEY_SIZE_DEFAULT, 16 },
+ { ALG_AES192, "AES192", CSSM_ALGID_AES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
+ 192, 24 },
+ { ALG_AES256, "AES256", CSSM_ALGID_AES, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
+ 256, 32 },
+ { ALG_BFISH, "Blowfish", CSSM_ALGID_BLOWFISH, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
+ CSP_BFISH_KEY_SIZE_DEFAULT, 8 },
+ { ALG_CAST, "CAST", CSSM_ALGID_CAST, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS5,
+ CSP_CAST_KEY_SIZE_DEFAULT, 8 }
+};
+
+static void genFileNames(
+ const char *algStr,
+ char *keyFile,
+ char *ptextFile,
+ char *ctextFile,
+ char *ivFile)
+{
+ sprintf(keyFile, "key_%s", algStr);
+ sprintf(ptextFile, "ptext_%s", algStr);
+ sprintf(ctextFile, "ctext_%s", algStr);
+ sprintf(ivFile, "iv_%s", algStr);
+}
+
+/* encrypt, write blobs (key, plaintext, ciphertext, optional IV) to disk */
+static int doEncrypt(
+ CSSM_CSP_HANDLE cspHand,
+ const SymAlgParams *algParams,
+ CSSM_DATA *ptext, // mallocd, length valid, we fill data
+ CSSM_BOOL quiet,
+ CSSM_BOOL verbose)
+{
+ CSSM_KEY_PTR symKey = NULL;
+ CSSM_KEY rawKey;
+ CSSM_RETURN crtn;
+ CSSM_DATA ctext = {0, NULL};
+ uint8 iv[BLOCK_SIZE_MAX];
+ CSSM_DATA ivd = {BLOCK_SIZE_MAX, iv};
+ CSSM_DATA *ivp = NULL;
+ uint32 blockSize = 0;
+ char keyFile[FILENAME_MAX];
+ char ptextFile[FILENAME_MAX];
+ char ctextFile[FILENAME_MAX];
+ char ivFile[FILENAME_MAX];
+
+ if(!quiet) {
+ printf("...encrypting, alg %s\n", algParams->algStr);
+ }
+
+ /* generate reference key (works with CSPDL) */
+ symKey = cspGenSymKey(cspHand, algParams->cssmAlg,
+ "noLabel", 7,
+ CSSM_KEYUSE_ANY, algParams->keySizeBits, CSSM_TRUE);
+ if(symKey == NULL) {
+ printf("***Error generating key for alg %s size %u bits\n",
+ algParams->algStr, (unsigned)algParams->keySizeBits);
+ return testError(quiet);
+ }
+
+ /* get key in raw format (to get the raw blob we write to disk) */
+ crtn = cspRefKeyToRaw(cspHand, symKey, &rawKey);
+ if(crtn) {
+ printf("***Error generating raw key for alg %s size %u bits\n",
+ algParams->algStr, (unsigned)algParams->keySizeBits);
+ return testError(quiet);
+ }
+
+ appGetRandomBytes(ptext->Data, (unsigned)ptext->Length);
+
+ /*
+ * Hack: we only need to specify block size for AES192 and AES256, which
+ * we detect by their having an ivLen of greater than 16.
+ */
+ if(algParams->ivLen > 16) {
+ blockSize = algParams->ivLen;
+ }
+ if(algParams->ivLen) {
+ appGetRandomBytes(iv, algParams->ivLen);
+ ivd.Length = algParams->ivLen;
+ ivp = &ivd;
+ }
+
+ crtn = cspStagedEncrypt(cspHand,
+ algParams->cssmAlg, algParams->mode, algParams->padding,
+ symKey, NULL,
+ 0, blockSize, 0,
+ ivp, ptext,
+ &ctext,
+ CSSM_FALSE);
+ if(crtn) {
+ printf("***Error encrypting for alg %s size %u bits\n",
+ algParams->algStr, (unsigned)algParams->keySizeBits);
+ return testError(quiet);
+ }
+
+ /* write: key, IV, ptext, ctext */
+ genFileNames(algParams->algStr, keyFile, ptextFile, ctextFile, ivFile);
+ if(writeFile(keyFile, rawKey.KeyData.Data, (unsigned)rawKey.KeyData.Length) ||
+ writeFile(ptextFile, ptext->Data, (unsigned)ptext->Length) ||
+ writeFile(ctextFile, ctext.Data, (unsigned)ctext.Length)) {
+ printf("***Error writing result of alg %s size %u bits\n",
+ algParams->algStr, (unsigned)algParams->keySizeBits);
+ return testError(quiet);
+ }
+ if(ivp != NULL) {
+ if(writeFile(ivFile, ivp->Data, (unsigned)ivp->Length)) {
+ printf("***Error writing IV for alg %s size %u bits\n",
+ algParams->algStr, (unsigned)algParams->keySizeBits);
+ return testError(quiet);
+ }
+ }
+
+ /* Free resources */
+ CSSM_FreeKey(cspHand, NULL, symKey, CSSM_FALSE);
+ CSSM_FreeKey(cspHand, NULL, &rawKey, CSSM_FALSE);
+ CSSM_FREE(ctext.Data);
+ return 0;
+}
+
+/* read blobs (key, plaintext, ciphertext, optional IV) from disk, decrypt, compare plaintext */
+static int doDecrypt(
+ CSSM_CSP_HANDLE cspHand,
+ const SymAlgParams *algParams,
+ CSSM_BOOL quiet,
+ CSSM_BOOL verbose)
+{
+ CSSM_KEY symKey;
+ uint8 *symKeyBits;
+ unsigned symKeyLen; // in bytes
+ CSSM_DATA symKeyData;
+ CSSM_RETURN crtn;
+ uint8 *ctextChars;
+ unsigned ctextLen = 0;
+ CSSM_DATA ctext;
+ CSSM_DATA rptext = {0, NULL}; // recovered/decrytped
+ uint8 *refPTextChars;
+ unsigned refPtextLen;
+ CSSM_DATA refPtext = {0, NULL}; // expected
+ uint8 *iv = NULL;
+ unsigned ivLen;
+ CSSM_DATA ivd = {BLOCK_SIZE_MAX, iv};
+ CSSM_DATA *ivp = NULL;
+ uint32 blockSize = 0;
+ char keyFile[FILENAME_MAX];
+ char ptextFile[FILENAME_MAX];
+ char ctextFile[FILENAME_MAX];
+ char ivFile[FILENAME_MAX];
+
+ if(!quiet) {
+ printf("...decrypting, alg %s\n", algParams->algStr);
+ }
+
+ /*
+ * Hack: we only need to specify block size for AES192 and AES256, which
+ * we detect by their having an ivLen of greater than 16.
+ */
+ if(algParams->ivLen > 16) {
+ blockSize = algParams->ivLen;
+ }
+ if(algParams->ivLen) {
+ ivp = &ivd;
+ ivd.Length = algParams->ivLen;
+ }
+
+ /* read: key, IV, ptext, ctext */
+ genFileNames(algParams->algStr, keyFile, ptextFile, ctextFile, ivFile);
+ if(readFile(keyFile, &symKeyBits, &symKeyLen) ||
+ readFile(ptextFile, &refPTextChars, &refPtextLen) ||
+ readFile(ctextFile, &ctextChars, &ctextLen)) {
+ printf("***Error reading reference blobs for alg %s size %u bits\n",
+ algParams->algStr, (unsigned)algParams->keySizeBits);
+ return testError(quiet);
+ }
+ if(ivp != NULL) {
+ if(readFile(ivFile, &iv, &ivLen)) {
+ printf("***Error writing IV for alg %s size %u bits\n",
+ algParams->algStr, (unsigned)algParams->keySizeBits);
+ return testError(quiet);
+ }
+ if(ivLen != algParams->ivLen) {
+ printf("***Unexpected IV length: expect %u found %u\n",
+ (unsigned)algParams->ivLen, (unsigned)ivLen);
+ if(testError(quiet)) {
+ return 1;
+ }
+ }
+ ivd.Data = iv;
+ }
+ ctext.Data = ctextChars;
+ ctext.Length = ctextLen;
+ refPtext.Data = refPTextChars;
+ refPtext.Length = refPtextLen;
+
+ /* generate key */
+ symKeyData.Data = symKeyBits;
+ symKeyData.Length = symKeyLen;
+
+ crtn = cspGenSymKeyWithBits(cspHand, algParams->cssmAlg,
+ CSSM_KEYUSE_ANY, &symKeyData, symKeyLen, &symKey);
+ if(crtn) {
+ printf("***Error creating key for alg %s keySize %u\n",
+ algParams->algStr, (unsigned)algParams->keySizeBits);
+ return testError(quiet);
+ }
+
+ crtn = cspStagedDecrypt(cspHand,
+ algParams->cssmAlg, algParams->mode, algParams->padding,
+ &symKey, NULL,
+ 0, blockSize, 0,
+ ivp, &ctext,
+ &rptext,
+ CSSM_FALSE);
+ if(crtn) {
+ printf("***Error decrypting for alg %s size %u bits\n",
+ algParams->algStr, (unsigned)algParams->keySizeBits);
+ return testError(quiet);
+ }
+
+ /* moment of truth */
+ if(!appCompareCssmData(&rptext, &refPtext)) {
+ printf("***DATA MISCOMPARE AFTER DECRYPT alg %s size %u bits\n",
+ algParams->algStr, (unsigned)algParams->keySizeBits);
+ return testError(quiet);
+ }
+
+ /* Free resources */
+ CSSM_FreeKey(cspHand, NULL, &symKey, CSSM_FALSE);
+ free(symKeyBits); // mallocd by readFile()
+ free(refPTextChars);
+ free(ctextChars);
+ CSSM_FREE(rptext.Data); // mallocd by CSP
+ if(iv) {
+ free(iv);
+ }
+ return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+ int arg;
+ char *argp;
+ CSSM_DATA ptext;
+ CSSM_CSP_HANDLE cspHand;
+ unsigned currAlg; // ALG_xxx
+ int rtn = 0;
+
+ /*
+ * User-spec'd params
+ */
+ unsigned minAlg = ALG_FIRST;
+ unsigned maxAlg = ALG_LAST;
+ CSSM_BOOL verbose = CSSM_FALSE;
+ CSSM_BOOL quiet = CSSM_FALSE;
+ CSSM_BOOL bareCsp = CSSM_TRUE;
+ bool encrypt = false;
+ unsigned ptextSize = PTEXT_SIZE_DEF;
+ char *dirName;
+
+ if(argc < 3) {
+ usage(argv);
+ }
+ switch(argv[1][0]) {
+ case 'e':
+ encrypt = true;
+ break;
+ case 'd':
+ encrypt = false;
+ break;
+ default:
+ usage(argv);
+ }
+ dirName = argv[2];
+
+ for(arg=3; arg<argc; arg++) {
+ argp = argv[arg];
+ switch(argp[0]) {
+ case 'a':
+ if(argp[1] != '=') {
+ usage(argv);
+ }
+ switch(argp[2]) {
+ case 's':
+ minAlg = maxAlg = ALG_ASC;
+ break;
+ case 'd':
+ minAlg = maxAlg = ALG_DES;
+ break;
+ case '3':
+ minAlg = maxAlg = ALG_3DES;
+ break;
+ case '2':
+ minAlg = maxAlg = ALG_RC2;
+ break;
+ case '4':
+ minAlg = maxAlg = ALG_RC4;
+ break;
+ case '5':
+ minAlg = maxAlg = ALG_RC5;
+ break;
+ case 'a':
+ minAlg = maxAlg = ALG_AES;
+ break;
+ case 'b':
+ minAlg = maxAlg = ALG_BFISH;
+ break;
+ case 'c':
+ minAlg = maxAlg = ALG_CAST;
+ break;
+ default:
+ usage(argv);
+ }
+ break;
+ case 'v':
+ verbose = CSSM_TRUE;
+ break;
+ case 'D':
+ bareCsp = CSSM_FALSE;
+ break;
+ case 'p':
+ ptextSize = atoi(&argp[2]);
+ break;
+ case 'q':
+ quiet = CSSM_TRUE;
+ break;
+ case 'h':
+ default:
+ usage(argv);
+ }
+ }
+ ptext.Data = (uint8 *)CSSM_MALLOC(ptextSize);
+ if(ptext.Data == NULL) {
+ printf("Insufficient heap space\n");
+ exit(1);
+ }
+ ptext.Length = ptextSize;
+
+ testStartBanner("symReference", argc, argv);
+
+ cspHand = cspDlDbStartup(bareCsp, NULL);
+ if(cspHand == 0) {
+ exit(1);
+ }
+
+ if(chdir(dirName)) {
+ perror(dirName);
+ printf("Error accessing directory %s. Aborting.\n", dirName);
+ exit(1);
+ }
+ for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
+ const SymAlgParams *algParams = &symAlgParams[currAlg];
+
+ if(encrypt) {
+ rtn = doEncrypt(cspHand, algParams, &ptext, quiet, verbose);
+ }
+ else {
+ rtn = doDecrypt(cspHand, algParams, quiet, verbose);
+ }
+ if(rtn) {
+ break;
+ }
+ } /* for algs */
+
+ cspShutdown(cspHand, bareCsp);
+ if((rtn == 0) && !quiet) {
+ printf("%s test complete\n", argv[0]);
+ }
+ CSSM_FREE(ptext.Data);
+ return rtn;
+}
+
+