--- /dev/null
+/* Copyright (c) 1998,2003-2005,2008 Apple Inc.
+ *
+ * pbeTest.c - test CSP PBE-style DeriveKey().
+ *
+ * Revision History
+ * ----------------
+ * 15 May 2000 Doug Mitchell
+ * Ported to X/CDSA2.
+ * 13 Aug 1998 Doug Mitchell at NeXT
+ * Created.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <Security/cssm.h>
+#include "cspwrap.h"
+#include "common.h"
+#include "cspdlTesting.h"
+
+/* we need to know a little bit about AES for this test.... */
+#define AES_BLOCK_SIZE 16 /* bytes */
+
+/*
+ * Defaults.
+ */
+#define LOOPS_DEF 10
+#define MIN_PTEXT_SIZE AES_BLOCK_SIZE /* for non-padding tests */
+#define MAX_PTEXT_SIZE 1000
+#define MAX_PASSWORD_SIZE 64
+#define MAX_SALT_SIZE 32
+#define MIN_ITER_COUNT 1000
+#define MAX_ITER_COUNT 2000
+#define MAX_IV_SIZE AES_BLOCK_SIZE
+
+/* min values not currently exported by CSP */
+#define APPLE_PBE_MIN_PASSWORD 8
+#define APPLE_PBE_MIN_SALT 8
+
+/* static IV for derive algorithms which don't create one */
+CSSM_DATA staticIv = {MAX_IV_SIZE, (uint8 *)"someIvOrOther..."};
+
+/*
+ * Enumerate algs our own way to allow iteration.
+ */
+typedef unsigned privAlg;
+enum {
+ /* PBE algs */
+ pbe_pbkdf2 = 1,
+ // other unsupported for now
+ pbe_PKCS12 = 1,
+ pbe_MD5,
+ pbe_MD2,
+ pbe_SHA1,
+
+ /* key gen algs */
+ pka_ASC,
+ pka_RC4,
+ pka_DES,
+ pka_RC2,
+ pka_RC5,
+ pka_3DES,
+ pka_AES
+};
+
+#define PBE_ALG_FIRST pbe_pbkdf2
+#define PBE_ALG_LAST pbe_pbkdf2
+#define ENCR_ALG_FIRST pka_ASC
+#define ENCR_ALG_LAST pka_AES
+#define ENCR_ALG_LAST_EXPORT pka_RC5
+
+/*
+ * Args passed to each test
+ */
+typedef struct {
+ CSSM_CSP_HANDLE cspHand;
+ CSSM_ALGORITHMS keyAlg;
+ CSSM_ALGORITHMS encrAlg;
+ uint32 keySizeInBits;
+ uint32 effectiveKeySizeInBits; // 0 means not used
+ const char *keyAlgStr;
+ CSSM_ENCRYPT_MODE encrMode;
+ CSSM_PADDING encrPad;
+ CSSM_ALGORITHMS deriveAlg;
+ const char *deriveAlgStr;
+ CSSM_DATA_PTR ptext;
+ CSSM_DATA_PTR password;
+ CSSM_DATA_PTR salt;
+ uint32 iterCount;
+ CSSM_BOOL useInitVector; // encrypt needs an IV
+ uint32 ivSize;
+ CSSM_BOOL genInitVector; // DeriveKey generates an IV
+ CSSM_BOOL useRefKey;
+ CSSM_BOOL quiet;
+} testArgs;
+
+static void usage(char **argv)
+{
+ printf("usage: %s [options]\n", argv[0]);
+ printf(" Options:\n");
+ printf(" l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
+ printf(" e(xport)\n");
+ printf(" r(epeatOnly)\n");
+ printf(" z(ero length password)\n");
+ printf(" p(ause after each loop)\n");
+ printf(" D (CSP/DL; default = bare CSP)\n");
+ printf(" q(uiet)\n");
+ printf(" h(elp)\n");
+ exit(1);
+}
+
+/*
+ * Given a privAlg value, return various associated stuff.
+ */
+static void algInfo(privAlg alg, // pbe_MD5, etc.
+ CSSM_ALGORITHMS *cdsaAlg, // CSSM_ALGID_MD5_PBE, etc. RETURNED
+ // key alg for key gen algs
+ CSSM_ALGORITHMS *encrAlg, // encrypt/decrypt alg for key
+ // gen algs
+ CSSM_ENCRYPT_MODE *mode, // RETURNED
+ CSSM_PADDING *padding, // RETURNED
+ CSSM_BOOL *useInitVector, // RETURNED, for encrypt/decrypt
+ uint32 *ivSize, // RETURNED, in bytes
+ CSSM_BOOL *genInitVector, // RETURNED, for deriveKey
+ const char **algStr) // RETURNED
+{
+ /* default or irrelevant fields */
+ *mode = CSSM_ALGMODE_NONE;
+ *useInitVector = CSSM_FALSE;
+ *genInitVector = CSSM_FALSE; // DeriveKey doesn't do this now
+ *padding = CSSM_PADDING_PKCS1;
+ *ivSize = 8; // thje usual size, if needed
+ *encrAlg = CSSM_ALGID_NONE;
+
+ switch(alg) {
+ case pbe_pbkdf2:
+ *cdsaAlg = CSSM_ALGID_PKCS5_PBKDF2;
+ *algStr = "PBKDF2";
+ return;
+ /* these are not supported */
+ #if 0
+ case pbe_PKCS12:
+ *cdsaAlg = CSSM_ALGID_SHA1_PBE_PKCS12;
+ *algStr = "PKCS12";
+ return;
+ case pbe_MD5:
+ *cdsaAlg = CSSM_ALGID_MD5_PBE;
+ *algStr = "MD5";
+ return;
+ case pbe_MD2:
+ *cdsaAlg = CSSM_ALGID_MD2_PBE;
+ *algStr = "MD2";
+ return;
+ case pbe_SHA1:
+ *cdsaAlg = CSSM_ALGID_SHA1_PBE;
+ *algStr = "SHA1";
+ return;
+ case pka_ASC:
+ *cdsaAlg = CSSM_ALGID_ASC;
+ *algStr = "ASC";
+ return;
+ #endif
+ case pka_DES:
+ *cdsaAlg = *encrAlg = CSSM_ALGID_DES;
+ *useInitVector = CSSM_TRUE;
+ *mode = CSSM_ALGMODE_CBCPadIV8;
+ *algStr = "DES";
+ return;
+ case pka_3DES:
+ *cdsaAlg = CSSM_ALGID_3DES_3KEY;
+ *encrAlg = CSSM_ALGID_3DES_3KEY_EDE;
+ *useInitVector = CSSM_TRUE;
+ *mode = CSSM_ALGMODE_CBCPadIV8;
+ *algStr = "3DES";
+ return;
+ case pka_AES:
+ *cdsaAlg = *encrAlg = CSSM_ALGID_AES;
+ *useInitVector = CSSM_TRUE;
+ *mode = CSSM_ALGMODE_CBCPadIV8;
+ *padding = CSSM_PADDING_PKCS5;
+ *ivSize = AES_BLOCK_SIZE; // per the default block size
+ *algStr = "AES";
+ return;
+ case pka_RC2:
+ *cdsaAlg = *encrAlg = CSSM_ALGID_RC2;
+ *useInitVector = CSSM_TRUE;
+ *mode = CSSM_ALGMODE_CBCPadIV8;
+ *algStr = "RC2";
+ return;
+ case pka_RC4:
+ *cdsaAlg = *encrAlg = CSSM_ALGID_RC4;
+ /* initVector false */
+ *mode = CSSM_ALGMODE_NONE;
+ *algStr = "RC4";
+ return;
+ case pka_RC5:
+ *cdsaAlg = *encrAlg = CSSM_ALGID_RC5;
+ *algStr = "RC5";
+ *mode = CSSM_ALGMODE_CBCPadIV8;
+ *useInitVector = CSSM_TRUE;
+ return;
+ case pka_ASC:
+ *cdsaAlg = *encrAlg = CSSM_ALGID_ASC;
+ /* initVector false */
+ *algStr = "ASC";
+ *mode = CSSM_ALGMODE_NONE;
+ return;
+ default:
+ printf("BRRZZZT! Update algInfo()!\n");
+ testError(CSSM_TRUE);
+ }
+}
+
+/* a handy "compare two CSSM_DATAs" ditty */
+static CSSM_BOOL compareData(const CSSM_DATA_PTR d1,
+ const CSSM_DATA_PTR d2)
+{
+ if(d1->Length != d2->Length) {
+ return CSSM_FALSE;
+ }
+ if(memcmp(d1->Data, d2->Data, d1->Length)) {
+ return CSSM_FALSE;
+ }
+ return CSSM_TRUE;
+}
+
+/* generate random one-bit byte */
+static uint8 randBit()
+{
+ return 1 << genRand(0, 7);
+}
+
+/*
+ * Writer debug - assertion failure when ctext[1].Data is NULL
+ * but length is nonzero
+ */
+#define SAFE_CTEXT_ARRAY 0
+
+/*
+ * Encrypt ptext using specified key, IV, effectiveKeySizeInBits
+ */
+static int encryptCom(CSSM_CSP_HANDLE cspHand,
+ const char *testName,
+ CSSM_DATA_PTR ptext,
+ CSSM_KEY_PTR key,
+ CSSM_ALGORITHMS alg,
+ CSSM_ENCRYPT_MODE mode,
+ CSSM_PADDING padding, // CSSM_PADDING_PKCS1, etc.
+ CSSM_DATA_PTR iv, // may be NULL
+ uint32 effectiveKeySizeInBits, // may be 0
+ CSSM_DATA_PTR ctext, // RETURNED
+ CSSM_BOOL quiet)
+{
+ CSSM_CC_HANDLE cryptHand;
+ CSSM_RETURN crtn;
+ CSSM_SIZE bytesEncrypted;
+ CSSM_DATA remData;
+ int rtn;
+ #if SAFE_CTEXT_ARRAY
+ CSSM_DATA safeCtext[2];
+ safeCtext[0] = *ctext;
+ safeCtext[1].Data = NULL;
+ safeCtext[1].Length = 10; // lie, but shouldn't use this!
+ #else
+ // printf("+++ ctext[0] = %d:0x%x; ctext[1] = %d:0x%x\n",
+ // ctext[0].Length, ctext[0].Data,
+ // ctext[1].Length, ctext[1].Data);
+ #endif
+
+ cryptHand = genCryptHandle(cspHand,
+ alg,
+ mode,
+ padding,
+ key,
+ NULL, // no 2nd key
+ iv, // InitVector
+ effectiveKeySizeInBits,
+ 0); // rounds
+ if(cryptHand == 0) {
+ return testError(quiet);
+ }
+
+ remData.Data = NULL;
+ remData.Length = 0;
+ crtn = CSSM_EncryptData(cryptHand,
+ ptext,
+ 1,
+ #if SAFE_CTEXT_ARRAY
+ &safeCtext[0],
+ #else
+ ctext,
+ #endif
+ 1,
+ &bytesEncrypted,
+ &remData);
+ #if SAFE_CTEXT_ARRAY
+ *ctext = safeCtext[0];
+ #endif
+
+ if(crtn) {
+ printError("CSSM_EncryptData", crtn);
+ rtn = testError(quiet);
+ goto done;
+ }
+ if(remData.Length != 0) {
+ //printf("***WARNING: nonzero remData on encrypt!\n");
+ /* new for CDSA2 - possible remData even if we ask the CSP to
+ * malloc ctext */
+ ctext->Data = (uint8 *)appRealloc(ctext->Data, bytesEncrypted, NULL);
+ memmove(ctext->Data + ctext->Length,
+ remData.Data,
+ bytesEncrypted - ctext->Length);
+ appFreeCssmData(&remData, CSSM_FALSE);
+ }
+ /* new for CDSA 2 */
+ ctext->Length = bytesEncrypted;
+ rtn = 0;
+done:
+ if(CSSM_DeleteContext(cryptHand)) {
+ printError("CSSM_DeleteContext", 0);
+ rtn = 1;
+ }
+ return rtn;
+}
+
+/*
+ * Decrypt ctext using specified key, IV, effectiveKeySizeInBits
+ */
+static int decryptCom(CSSM_CSP_HANDLE cspHand,
+ const char *testName,
+ CSSM_DATA_PTR ctext,
+ CSSM_KEY_PTR key,
+ CSSM_ALGORITHMS alg,
+ CSSM_ENCRYPT_MODE mode,
+ CSSM_PADDING padding,
+ CSSM_DATA_PTR iv, // may be NULL
+ uint32 effectiveKeySizeInBits, // may be 0
+ CSSM_DATA_PTR ptext, // RETURNED
+ CSSM_BOOL quiet)
+{
+ CSSM_CC_HANDLE cryptHand;
+ CSSM_RETURN crtn;
+ CSSM_SIZE bytesDecrypted;
+ CSSM_DATA remData;
+ int rtn;
+
+ cryptHand = genCryptHandle(cspHand,
+ alg,
+ mode,
+ padding,
+ key,
+ NULL, // no 2nd key
+ iv, // InitVector
+ effectiveKeySizeInBits,
+ 0); // rounds
+ if(cryptHand == 0) {
+ return testError(quiet);
+ }
+ remData.Data = NULL;
+ remData.Length = 0;
+ crtn = CSSM_DecryptData(cryptHand,
+ ctext,
+ 1,
+ ptext,
+ 1,
+ &bytesDecrypted,
+ &remData);
+ if(crtn) {
+ printError("CSSM_DecryptData", crtn);
+ rtn = testError(quiet);
+ goto done;
+ }
+ if(remData.Length != 0) {
+ //printf("***WARNING: nonzero remData on decrypt!\n");
+ /* new for CDSA2 - possible remData even if we ask the CSP to
+ * malloc ptext */
+ ptext->Data = (uint8 *)appRealloc(ptext->Data, bytesDecrypted, NULL);
+ memmove(ptext->Data + ptext->Length,
+ remData.Data,
+ bytesDecrypted - ptext->Length);
+ appFreeCssmData(&remData, CSSM_FALSE);
+ }
+ /* new for CDSA 2 */
+ ptext->Length = bytesDecrypted;
+ rtn = 0;
+done:
+ if(CSSM_DeleteContext(cryptHand)) {
+ printError("CSSM_DeleteContext", 0);
+ rtn = 1;
+ }
+ return rtn;
+}
+
+/*
+ * Common test portion
+ * encrypt ptext with key1, iv1
+ * encrypt ptext with key2, iv2
+ * compare 2 ctexts; expect failure;
+ */
+
+#define TRAP_WRITER_ERR 1
+
+static int testCommon(CSSM_CSP_HANDLE cspHand,
+ const char *testName,
+ CSSM_ALGORITHMS encrAlg,
+ CSSM_ENCRYPT_MODE encrMode,
+ CSSM_PADDING encrPad,
+ uint32 effectiveKeySizeInBits,
+ CSSM_DATA_PTR ptext,
+ CSSM_KEY_PTR key1,
+ CSSM_DATA_PTR iv1,
+ CSSM_KEY_PTR key2,
+ CSSM_DATA_PTR iv2,
+ CSSM_BOOL quiet)
+{
+ CSSM_DATA ctext1;
+ CSSM_DATA ctext2;
+ ctext1.Data = NULL;
+ ctext1.Length = 0;
+ ctext2.Data = NULL;
+ ctext2.Length = 0;
+ if(encryptCom(cspHand,
+ testName,
+ ptext,
+ key1,
+ encrAlg,
+ encrMode,
+ encrPad,
+ iv1,
+ effectiveKeySizeInBits,
+ &ctext1,
+ quiet)) {
+ return 1;
+ }
+ #if TRAP_WRITER_ERR
+ if(ctext2.Data != NULL){
+ printf("Hey! encryptCom(ptext, ctext1 modified ctext2!\n");
+ if(testError(quiet)) {
+ return 1;
+ }
+ }
+ #endif
+ if(encryptCom(cspHand,
+ testName,
+ ptext,
+ key2,
+ encrAlg,
+ encrMode,
+ encrPad,
+ iv2,
+ effectiveKeySizeInBits,
+ &ctext2,
+ quiet)) {
+ return 1;
+ }
+ if(compareData(&ctext1, &ctext2)) {
+ printf("***%s: Unexpected Data compare!\n", testName);
+ return testError(quiet);
+ }
+ appFreeCssmData(&ctext1, CSSM_FALSE);
+ appFreeCssmData(&ctext2, CSSM_FALSE);
+ return 0;
+}
+
+/**
+ ** inidividual tests.
+ **/
+#define KEY_LABEL1 "noLabel1"
+#define KEY_LABEL2 "noLabel2"
+#define KEY_LABEL_LEN strlen(KEY_LABEL1)
+#define REPEAT_ON_ERROR 1
+
+/* test repeatability - the only test here which actually decrypts */
+static int repeatTest(testArgs *targs)
+{
+ /*
+ generate two keys with same params;
+ encrypt ptext with key1;
+ decrypt ctext with key2;
+ compare; expect success;
+ */
+ CSSM_KEY_PTR key1;
+ CSSM_KEY_PTR key2;
+ CSSM_DATA iv1;
+ CSSM_DATA iv2;
+ CSSM_DATA_PTR ivp1;
+ CSSM_DATA_PTR ivp2;
+ CSSM_DATA ctext;
+ CSSM_DATA rptext;
+ CSSM_BOOL gotErr = CSSM_FALSE;
+
+ if(targs->useInitVector) {
+ if(targs->genInitVector) {
+ ivp1 = &iv1;
+ ivp2 = &iv2;
+ }
+ else {
+ staticIv.Length = targs->ivSize;
+ ivp1 = ivp2 = &staticIv;
+ }
+ }
+ else {
+ ivp1 = ivp2 = NULL;
+ }
+ /* these need to be init'd regardless */
+ iv1.Data = NULL;
+ iv1.Length = 0;
+ iv2.Data = NULL;
+ iv2.Length = 0;
+ ctext.Data = NULL;
+ ctext.Length = 0;
+ rptext.Data = NULL;
+ rptext.Length = 0;
+repeatDerive:
+ key1 = cspDeriveKey(targs->cspHand,
+ targs->deriveAlg,
+ targs->keyAlg,
+ KEY_LABEL1,
+ KEY_LABEL_LEN,
+ CSSM_KEYUSE_ENCRYPT,
+ targs->keySizeInBits,
+ targs->useRefKey,
+ targs->password,
+ targs->salt,
+ targs->iterCount,
+ &iv1);
+ if(key1 == NULL) {
+ return testError(targs->quiet);
+ }
+ key2 = cspDeriveKey(targs->cspHand,
+ targs->deriveAlg,
+ targs->keyAlg,
+ KEY_LABEL2,
+ KEY_LABEL_LEN,
+ CSSM_KEYUSE_DECRYPT,
+ targs->keySizeInBits,
+ targs->useRefKey,
+ targs->password,
+ targs->salt,
+ targs->iterCount,
+ &iv2);
+ if(key2 == NULL) {
+ return testError(targs->quiet);
+ }
+repeatEnc:
+ if(encryptCom(targs->cspHand,
+ "repeatTest",
+ targs->ptext,
+ key1,
+ targs->encrAlg,
+ targs->encrMode,
+ targs->encrPad,
+ ivp1,
+ targs->effectiveKeySizeInBits,
+ &ctext,
+ targs->quiet)) {
+ return 1;
+ }
+ if(decryptCom(targs->cspHand,
+ "repeatTest",
+ &ctext,
+ key2,
+ targs->encrAlg,
+ targs->encrMode,
+ targs->encrPad,
+ ivp2,
+ targs->effectiveKeySizeInBits,
+ &rptext,
+ targs->quiet)) {
+ return 1;
+ }
+ if(gotErr || !compareData(targs->ptext, &rptext)) {
+ printf("***Data miscompare in repeatTest\n");
+ if(REPEAT_ON_ERROR) {
+ char str;
+
+ gotErr = CSSM_TRUE;
+ fpurge(stdin);
+ printf("Repeat enc/dec (r), repeat derive (d), continue (c), abort (any)? ");
+ str = getchar();
+ switch(str) {
+ case 'r':
+ appFreeCssmData(&ctext, CSSM_FALSE);
+ appFreeCssmData(&rptext, CSSM_FALSE);
+ goto repeatEnc;
+ case 'd':
+ appFreeCssmData(&ctext, CSSM_FALSE);
+ appFreeCssmData(&rptext, CSSM_FALSE);
+ appFreeCssmData(&iv1, CSSM_FALSE);
+ appFreeCssmData(&iv2, CSSM_FALSE);
+ cspFreeKey(targs->cspHand, key1);
+ cspFreeKey(targs->cspHand, key2);
+ goto repeatDerive;
+ case 'c':
+ break;
+ default:
+ return 1;
+ }
+ }
+ else {
+ return testError(targs->quiet);
+ }
+ }
+ appFreeCssmData(&ctext, CSSM_FALSE);
+ appFreeCssmData(&rptext, CSSM_FALSE);
+ appFreeCssmData(&iv1, CSSM_FALSE);
+ appFreeCssmData(&iv2, CSSM_FALSE);
+ cspFreeKey(targs->cspHand, key1);
+ cspFreeKey(targs->cspHand, key2);
+ CSSM_FREE(key1);
+ CSSM_FREE(key2);
+ return 0;
+}
+
+/* ensure iterCount alters key */
+static int iterTest(testArgs *targs)
+{
+ /*
+ generate key1(iterCount), key2(iterCount+1);
+ encrypt ptext with key1;
+ encrypt ptext with key2;
+ compare 2 ctexts; expect failure;
+ */
+ CSSM_KEY_PTR key1;
+ CSSM_KEY_PTR key2;
+ CSSM_DATA iv1;
+ CSSM_DATA iv2;
+ CSSM_DATA_PTR ivp1;
+ CSSM_DATA_PTR ivp2;
+ if(targs->useInitVector) {
+ if(targs->genInitVector) {
+ ivp1 = &iv1;
+ ivp2 = &iv2;
+ }
+ else {
+ staticIv.Length = targs->ivSize;
+ ivp1 = ivp2 = &staticIv;
+ }
+ }
+ else {
+ ivp1 = ivp2 = NULL;
+ }
+ /* these need to be init'd regardless */
+ iv1.Data = NULL;
+ iv1.Length = 0;
+ iv2.Data = NULL;
+ iv2.Length = 0;
+ key1 = cspDeriveKey(targs->cspHand,
+ targs->deriveAlg,
+ targs->keyAlg,
+ KEY_LABEL1,
+ KEY_LABEL_LEN,
+ CSSM_KEYUSE_ENCRYPT,
+ targs->keySizeInBits,
+ targs->useRefKey,
+ targs->password,
+ targs->salt,
+ targs->iterCount,
+ &iv1);
+ if(key1 == NULL) {
+ return testError(targs->quiet);
+ }
+ key2 = cspDeriveKey(targs->cspHand,
+ targs->deriveAlg,
+ targs->keyAlg,
+ KEY_LABEL2,
+ KEY_LABEL_LEN,
+ CSSM_KEYUSE_ENCRYPT,
+ targs->keySizeInBits,
+ targs->useRefKey,
+ targs->password,
+ targs->salt,
+ targs->iterCount + 1, // the changed param
+ &iv2);
+ if(key2 == NULL) {
+ return testError(targs->quiet);
+ }
+ if(testCommon(targs->cspHand,
+ "iterTest",
+ targs->encrAlg,
+ targs->encrMode,
+ targs->encrPad,
+ targs->effectiveKeySizeInBits,
+ targs->ptext,
+ key1,
+ ivp1,
+ key2,
+ ivp2,
+ targs->quiet)) {
+ return 1;
+ }
+ appFreeCssmData(&iv1, CSSM_FALSE);
+ appFreeCssmData(&iv2, CSSM_FALSE);
+ cspFreeKey(targs->cspHand, key1);
+ cspFreeKey(targs->cspHand, key2);
+ CSSM_FREE(key1);
+ CSSM_FREE(key2);
+ return 0;
+}
+
+/* ensure password alters key */
+static int passwordTest(testArgs *targs)
+{
+ /*
+ generate key1(password), key2(munged password);
+ encrypt ptext with key1;
+ encrypt ptext with key2;
+ compare 2 ctexts; expect failure;
+ */
+ CSSM_KEY_PTR key1;
+ CSSM_KEY_PTR key2;
+ CSSM_DATA iv1;
+ CSSM_DATA iv2;
+ CSSM_DATA_PTR ivp1;
+ CSSM_DATA_PTR ivp2;
+ uint32 mungeDex;
+ uint32 mungeBits;
+ if(targs->useInitVector) {
+ if(targs->genInitVector) {
+ ivp1 = &iv1;
+ ivp2 = &iv2;
+ }
+ else {
+ staticIv.Length = targs->ivSize;
+ ivp1 = ivp2 = &staticIv;
+ }
+ }
+ else {
+ ivp1 = ivp2 = NULL;
+ }
+ /* these need to be init'd regardless */
+ iv1.Data = NULL;
+ iv1.Length = 0;
+ iv2.Data = NULL;
+ iv2.Length = 0;
+ key1 = cspDeriveKey(targs->cspHand,
+ targs->deriveAlg,
+ targs->keyAlg,
+ KEY_LABEL1,
+ KEY_LABEL_LEN,
+ CSSM_KEYUSE_ENCRYPT,
+ targs->keySizeInBits,
+ targs->useRefKey,
+ targs->password,
+ targs->salt,
+ targs->iterCount,
+ &iv1);
+ if(key1 == NULL) {
+ return testError(targs->quiet);
+ }
+ /* munge password */
+ mungeDex = genRand(0, targs->password->Length - 1);
+ mungeBits = randBit();
+ targs->password->Data[mungeDex] ^= mungeBits;
+ key2 = cspDeriveKey(targs->cspHand,
+ targs->deriveAlg,
+ targs->keyAlg,
+ KEY_LABEL2,
+ KEY_LABEL_LEN,
+ CSSM_KEYUSE_ENCRYPT,
+ targs->keySizeInBits,
+ targs->useRefKey,
+ targs->password, // the changed param
+ targs->salt,
+ targs->iterCount,
+ &iv2);
+ if(key2 == NULL) {
+ return testError(targs->quiet);
+ }
+ if(testCommon(targs->cspHand,
+ "passwordTest",
+ targs->encrAlg,
+ targs->encrMode,
+ targs->encrPad,
+ targs->effectiveKeySizeInBits,
+ targs->ptext,
+ key1,
+ ivp1,
+ key2,
+ ivp2,
+ targs->quiet)) {
+ return 1;
+ }
+ /* restore */
+ targs->password->Data[mungeDex] ^= mungeBits;
+ appFreeCssmData(&iv1, CSSM_FALSE);
+ appFreeCssmData(&iv2, CSSM_FALSE);
+ cspFreeKey(targs->cspHand, key1);
+ cspFreeKey(targs->cspHand, key2);
+ CSSM_FREE(key1);
+ CSSM_FREE(key2);
+ return 0;
+}
+
+/* ensure salt alters key */
+static int saltTest(testArgs *targs)
+{
+ /*
+ generate key1(seed), key2(munged seed);
+ encrypt ptext with key1;
+ encrypt ptext with key2;
+ compare 2 ctexts; expect failure;
+ */
+ CSSM_KEY_PTR key1;
+ CSSM_KEY_PTR key2;
+ CSSM_DATA iv1;
+ CSSM_DATA iv2;
+ CSSM_DATA_PTR ivp1;
+ CSSM_DATA_PTR ivp2;
+ uint32 mungeDex;
+ uint32 mungeBits;
+ if(targs->useInitVector) {
+ if(targs->genInitVector) {
+ ivp1 = &iv1;
+ ivp2 = &iv2;
+ }
+ else {
+ staticIv.Length = targs->ivSize;
+ ivp1 = ivp2 = &staticIv;
+ }
+ }
+ else {
+ ivp1 = ivp2 = NULL;
+ }
+ /* these need to be init'd regardless */
+ iv1.Data = NULL;
+ iv1.Length = 0;
+ iv2.Data = NULL;
+ iv2.Length = 0;
+ key1 = cspDeriveKey(targs->cspHand,
+ targs->deriveAlg,
+ targs->keyAlg,
+ KEY_LABEL1,
+ KEY_LABEL_LEN,
+ CSSM_KEYUSE_ENCRYPT,
+ targs->keySizeInBits,
+ targs->useRefKey,
+ targs->password,
+ targs->salt,
+ targs->iterCount,
+ &iv1);
+ if(key1 == NULL) {
+ return testError(targs->quiet);
+ }
+ /* munge salt */
+ mungeDex = genRand(0, targs->salt->Length - 1);
+ mungeBits = randBit();
+ targs->salt->Data[mungeDex] ^= mungeBits;
+ key2 = cspDeriveKey(targs->cspHand,
+ targs->deriveAlg,
+ targs->keyAlg,
+ KEY_LABEL2,
+ KEY_LABEL_LEN,
+ CSSM_KEYUSE_ENCRYPT,
+ targs->keySizeInBits,
+ targs->useRefKey,
+ targs->password,
+ targs->salt, // the changed param
+ targs->iterCount,
+ &iv2);
+ if(key2 == NULL) {
+ return testError(targs->quiet);
+ }
+ if(testCommon(targs->cspHand,
+ "saltTest",
+ targs->encrAlg,
+ targs->encrMode,
+ targs->encrPad,
+ targs->effectiveKeySizeInBits,
+ targs->ptext,
+ key1,
+ ivp1,
+ key2,
+ ivp2,
+ targs->quiet)) {
+ return 1;
+ }
+ /* restore */
+ targs->salt->Data[mungeDex] ^= mungeBits;
+ appFreeCssmData(&iv1, CSSM_FALSE);
+ appFreeCssmData(&iv2, CSSM_FALSE);
+ cspFreeKey(targs->cspHand, key1);
+ cspFreeKey(targs->cspHand, key2);
+ CSSM_FREE(key1);
+ CSSM_FREE(key2);
+ return 0;
+}
+
+/* ensure initVector alters ctext. This isn't testing PBE per se, but
+ * it's a handy place to verify this function. */
+static int initVectTest(testArgs *targs)
+{
+ /*
+ generate key1;
+ encrypt ptext with key1 and initVector;
+ encrypt ptext with key1 and munged initVector;
+ compare 2 ctexts; expect failure;
+ */
+ CSSM_KEY_PTR key1;
+ CSSM_DATA iv1;
+ CSSM_DATA iv2;
+ uint32 mungeDex;
+ uint32 mungeBits;
+
+ if(targs->genInitVector) {
+ iv1.Data = NULL;
+ iv1.Length = 0;
+ }
+ else {
+ iv1 = staticIv;
+ }
+ key1 = cspDeriveKey(targs->cspHand,
+ targs->deriveAlg,
+ targs->keyAlg,
+ KEY_LABEL1,
+ KEY_LABEL_LEN,
+ CSSM_KEYUSE_ENCRYPT,
+ targs->keySizeInBits,
+ targs->useRefKey,
+ targs->password,
+ targs->salt,
+ targs->iterCount,
+ &iv1);
+ if(key1 == NULL) {
+ return testError(targs->quiet);
+ }
+
+ /* get munged copy of iv */
+ iv2.Data = (uint8 *)CSSM_MALLOC(iv1.Length);
+ iv2.Length = iv1.Length;
+ memmove(iv2.Data, iv1.Data, iv1.Length);
+ mungeDex = genRand(0, iv1.Length - 1);
+ mungeBits = randBit();
+ iv2.Data[mungeDex] ^= mungeBits;
+ if(testCommon(targs->cspHand,
+ "initVectTest",
+ targs->encrAlg,
+ targs->encrMode,
+ targs->encrPad,
+ targs->effectiveKeySizeInBits,
+ targs->ptext,
+ key1,
+ &iv1,
+ key1,
+ &iv2, // the changed param
+ targs->quiet)) {
+ return 1;
+ }
+ if(targs->genInitVector) {
+ appFreeCssmData(&iv1, CSSM_FALSE);
+ }
+ appFreeCssmData(&iv2, CSSM_FALSE);
+ cspFreeKey(targs->cspHand, key1);
+ CSSM_FREE(key1);
+ return 0;
+}
+
+#if 0
+/* only one algorithm supported */
+/* ensure deriveAlg alters key */
+static int deriveAlgTest(testArgs *targs)
+{
+ /*
+ generate key1(deriveAlg), key2(some other deriveAlg);
+ encrypt ptext with key1;
+ encrypt ptext with key2;
+ compare 2 ctexts; expect failure;
+ */
+ CSSM_KEY_PTR key1;
+ CSSM_KEY_PTR key2;
+ CSSM_DATA iv1;
+ CSSM_DATA iv2;
+ CSSM_DATA_PTR ivp1;
+ CSSM_DATA_PTR ivp2;
+ uint32 mungeAlg;
+
+ if(targs->useInitVector) {
+ if(targs->genInitVector) {
+ ivp1 = &iv1;
+ ivp2 = &iv2;
+ }
+ else {
+ staticIv.Length = targs->ivSize;
+ ivp1 = ivp2 = &staticIv;
+ }
+ }
+ else {
+ ivp1 = ivp2 = NULL;
+ }
+
+ /* these need to be init'd regardless */
+ iv1.Data = NULL;
+ iv1.Length = 0;
+ iv2.Data = NULL;
+ iv2.Length = 0;
+ key1 = cspDeriveKey(targs->cspHand,
+ targs->deriveAlg,
+ targs->keyAlg,
+ KEY_LABEL1,
+ KEY_LABEL_LEN,
+ CSSM_KEYUSE_ENCRYPT,
+ targs->keySizeInBits,
+ targs->useRefKey,
+ targs->password,
+ targs->salt,
+ targs->iterCount,
+ &iv1);
+ if(key1 == NULL) {
+ return testError(quiet);
+ }
+
+ /* munge deriveAlg */
+ switch(targs->deriveAlg) {
+ case CSSM_ALGID_MD5_PBE:
+ mungeAlg = CSSM_ALGID_MD2_PBE;
+ break;
+ case CSSM_ALGID_MD2_PBE:
+ mungeAlg = CSSM_ALGID_SHA1_PBE;
+ break;
+ case CSSM_ALGID_SHA1_PBE:
+ mungeAlg = CSSM_ALGID_SHA1_PBE_PKCS12;
+ break;
+ case CSSM_ALGID_SHA1_PBE_PKCS12:
+ mungeAlg = CSSM_ALGID_MD5_PBE;
+ break;
+ default:
+ printf("BRRRZZZT! Update deriveAlgTest()!\n");
+ return testError(quiet);
+ }
+ key2 = cspDeriveKey(targs->cspHand,
+ mungeAlg, // the changed param
+ targs->keyAlg,
+ KEY_LABEL2,
+ KEY_LABEL_LEN,
+ CSSM_KEYUSE_ENCRYPT,
+ targs->keySizeInBits,
+ targs->useRefKey,
+ targs->password, // the changed param
+ targs->salt,
+ targs->iterCount,
+ &iv2);
+ if(key2 == NULL) {
+ return testError(quiet);
+ }
+ if(testCommon(targs->cspHand,
+ "deriveAlgTest",
+ targs->encrAlg,
+ targs->encrMode,
+ targs->encrPad,
+ targs->effectiveKeySizeInBits,
+ targs->ptext,
+ key1,
+ ivp1,
+ key2,
+ ivp2,
+ targs->quiet)) {
+ return 1;
+ }
+ appFreeCssmData(&iv1, CSSM_FALSE);
+ appFreeCssmData(&iv2, CSSM_FALSE);
+ cspFreeKey(targs->cspHand, key1);
+ cspFreeKey(targs->cspHand, key2);
+ CSSM_FREE(key1);
+ CSSM_FREE(key2);
+ return 0;
+}
+#endif
+
+int main(int argc, char **argv)
+{
+ int arg;
+ char *argp;
+ unsigned loop;
+ CSSM_DATA ptext;
+ testArgs targs;
+ CSSM_DATA pwd;
+ CSSM_DATA salt;
+ privAlg pbeAlg;
+ privAlg encrAlg;
+ privAlg lastEncrAlg;
+ int rtn = 0;
+ CSSM_BOOL fooBool;
+ CSSM_BOOL refKeysOnly = CSSM_FALSE;
+ int i;
+
+ /*
+ * User-spec'd params
+ */
+ unsigned loops = LOOPS_DEF;
+ CSSM_BOOL quiet = CSSM_FALSE;
+ CSSM_BOOL doPause = CSSM_FALSE;
+ CSSM_BOOL doExport = CSSM_FALSE;
+ CSSM_BOOL repeatOnly = CSSM_FALSE;
+ CSSM_BOOL bareCsp = CSSM_TRUE;
+ CSSM_BOOL zeroLenPassword = CSSM_FALSE;
+
+ #if macintosh
+ argc = ccommand(&argv);
+ #endif
+ for(arg=1; arg<argc; arg++) {
+ argp = argv[arg];
+ switch(argp[0]) {
+ case 'l':
+ loops = atoi(&argp[2]);
+ break;
+ case 'q':
+ quiet = CSSM_TRUE;
+ break;
+ case 'D':
+ bareCsp = CSSM_FALSE;
+ #if CSPDL_ALL_KEYS_ARE_REF
+ refKeysOnly = CSSM_TRUE;
+ #endif
+ break;
+ case 'p':
+ doPause = CSSM_TRUE;
+ break;
+ case 'e':
+ doExport = CSSM_TRUE;
+ break;
+ case 'r':
+ repeatOnly = CSSM_TRUE;
+ break;
+ case 'z':
+ zeroLenPassword = CSSM_TRUE;
+ break;
+ case 'h':
+ default:
+ usage(argv);
+ }
+ }
+
+ /* statically allocate ptext, password and seed; data and length
+ * change in test loop */
+ pwd.Data = (uint8 *)CSSM_MALLOC(MAX_PASSWORD_SIZE);
+ ptext.Data = (uint8 *)CSSM_MALLOC(MAX_PTEXT_SIZE);
+ salt.Data = (uint8 *)CSSM_MALLOC(MAX_SALT_SIZE);
+ printf("Starting pbeTest; args: ");
+ for(i=1; i<argc; i++) {
+ printf("%s ", argv[i]);
+ }
+ printf("\n");
+ targs.cspHand = cspDlDbStartup(bareCsp, NULL);
+ if(targs.cspHand == 0) {
+ exit(1);
+ }
+ targs.ptext = &ptext;
+ targs.password = &pwd;
+ targs.salt = &salt;
+ targs.quiet = quiet;
+ if(doExport) {
+ lastEncrAlg = ENCR_ALG_LAST_EXPORT;
+ }
+ else {
+ lastEncrAlg = ENCR_ALG_LAST;
+ }
+ for(loop=1; ; loop++) {
+ if(!quiet) {
+ printf("...loop %d\n", loop);
+ }
+ /* change once per outer loop */
+ simpleGenData(&ptext, MIN_PTEXT_SIZE, MAX_PTEXT_SIZE);
+ if(zeroLenPassword) {
+ pwd.Length = 0; // fixed
+ }
+ else {
+ simpleGenData(&pwd, APPLE_PBE_MIN_PASSWORD, MAX_PASSWORD_SIZE);
+ }
+ simpleGenData(&salt, APPLE_PBE_MIN_SALT, MAX_SALT_SIZE);
+ targs.iterCount = genRand(MIN_ITER_COUNT, MAX_ITER_COUNT);
+ if(refKeysOnly) {
+ targs.useRefKey = CSSM_TRUE;
+ }
+ else {
+ targs.useRefKey = (loop & 1) ? CSSM_FALSE : CSSM_TRUE;
+ }
+
+ for(encrAlg=ENCR_ALG_FIRST; encrAlg<=lastEncrAlg; encrAlg++) {
+ /* Cook up encryption-related args */
+ algInfo(encrAlg,
+ &targs.keyAlg,
+ &targs.encrAlg,
+ &targs.encrMode,
+ &targs.encrPad,
+ &targs.useInitVector,
+ &targs.ivSize,
+ &fooBool, // genInitVector
+ &targs.keyAlgStr);
+ /* random key size */
+ targs.effectiveKeySizeInBits = randKeySizeBits(targs.keyAlg, OT_Encrypt);
+ targs.keySizeInBits = (targs.effectiveKeySizeInBits + 7) & ~7;
+ if(targs.keySizeInBits == targs.effectiveKeySizeInBits) {
+ /* same size, ignore effective */
+ targs.effectiveKeySizeInBits = 0;
+ }
+ if(!quiet) {
+ printf(" ...Encrypt alg %s keySizeInBits %u effectKeySize %u\n",
+ targs.keyAlgStr, (unsigned)targs.keySizeInBits,
+ (unsigned)targs.effectiveKeySizeInBits);
+ }
+ for(pbeAlg=PBE_ALG_FIRST; pbeAlg<=PBE_ALG_LAST; pbeAlg++) {
+ /* Cook up pbe-related args */
+ uint32 foo;
+ algInfo(pbeAlg,
+ &targs.deriveAlg,
+ &foo, // encrAlg
+ &foo, // mode
+ &foo,
+ &fooBool, // useInitVector
+ &foo, // ivSize
+ &targs.genInitVector,
+ &targs.deriveAlgStr);
+ if(!quiet) {
+ printf(" ...PBE alg %s\n", targs.deriveAlgStr);
+ }
+ /* grind thru the tests */
+ if(repeatTest(&targs)) {
+ rtn = 1;
+ goto testDone;
+ }
+ if(repeatOnly) {
+ continue;
+ }
+ if(iterTest(&targs)) {
+ rtn = 1;
+ goto testDone;
+ }
+ #if 0
+ // not supported yet
+ if(deriveAlgTest(&targs)) {
+ rtn = 1;
+ goto testDone;
+ }
+ #endif
+ if(!zeroLenPassword) {
+ /* won't work with zero length password */
+ if(passwordTest(&targs)) {
+ rtn = 1;
+ goto testDone;
+ }
+ }
+ if(saltTest(&targs)) {
+ rtn = 1;
+ goto testDone;
+ }
+ if(targs.useInitVector) {
+ if(initVectTest(&targs)) {
+ rtn = 1;
+ goto testDone;
+ }
+ }
+ } /* for pbeAlg */
+ } /* for encrAlg */
+ if(doPause) {
+ if(testError(quiet)) {
+ break;
+ }
+ }
+ if(loops && (loop == loops)) {
+ break;
+ }
+ } /* for loop */
+
+testDone:
+ CSSM_ModuleDetach(targs.cspHand);
+ if(!quiet && (rtn == 0)) {
+ printf("%s test complete\n", argv[0]);
+ }
+ return rtn;
+}