#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <Security/cssm.h>
#include "cspwrap.h"
#include "common.h"
#include "cspdlTesting.h"
#define USAGE_NAME "noUsage"
#define USAGE_NAME_LEN (strlen(USAGE_NAME))
#define USAGE2_NAME "noUsage2"
#define USAGE2_NAME_LEN (strlen(USAGE2_NAME))
#define LOOPS_DEF 10
#define MIN_EXP 2
#define DEFAULT_MAX_EXP 2
#define MAX_EXP 4
#define ALG_RSA 1
#define ALG_FEED 2
#define ALG_FEEDEXP 3
#define ALG_FEE_CFILE 4
#define ALG_FIRST ALG_RSA
#define ALG_LAST ALG_FEEDEXP
#define MAX_DATA_SIZE (10000 + 100)
#define FEE_PASSWD_LEN 32
#define DUMP_KEY_DATA 0
#define RSA_PLAINTEXT_LIMIT 0
static void usage(char **argv)
{
printf("usage: %s [options]\n", argv[0]);
printf(" Options:\n");
printf(" a=algorithm (f=FEED; x=FEEDExp; c=FEE_Cfile; r=RSA; default=all)\n");
printf(" l=loops (default=%d; 0=forever)\n", LOOPS_DEF);
printf(" n=minExp (default=%d)\n", MIN_EXP);
printf(" x=maxExp (default=%d, max=%d)\n", DEFAULT_MAX_EXP, MAX_EXP);
printf(" k=keySize\n");
printf(" P=primeType (m=Mersenne, f=FEE, g=general; FEE only)\n");
printf(" C=curveType (m=Montgomery, w=Weierstrass, g=general; FEE only)\n");
printf(" e(xport)\n");
printf(" r(eference keys only)\n");
printf(" K (skip decrypt)\n");
printf(" N(o padding, RSA only)\n");
printf(" S (no staging)\n");
printf(" D (CSP/DL; default = bare CSP)\n");
printf(" p (pause on each loop)\n");
printf(" u (quick; small keys)\n");
printf(" t=plainTextSize; default=random\n");
printf(" z(ero data)\n");
printf(" v(erbose)\n");
printf(" q(uiet)\n");
printf(" h(elp)\n");
exit(1);
}
static int doTest(CSSM_CSP_HANDLE cspHand,
CSSM_ALGORITHMS alg, CSSM_PADDING padding,
CSSM_DATA_PTR ptext,
CSSM_BOOL verbose,
CSSM_BOOL quiet,
uint32 keySizeInBits, uint32 primeType, uint32 curveType, CSSM_BOOL pubIsRef,
CSSM_KEYBLOB_FORMAT rawPubForm,
CSSM_BOOL privIsRef,
CSSM_KEYBLOB_FORMAT rawPrivForm,
CSSM_BOOL secondPubIsRaw, CSSM_BOOL stagedEncr,
CSSM_BOOL stagedDecr,
CSSM_BOOL mallocPtext, CSSM_BOOL mallocCtext, CSSM_BOOL skipDecrypt,
CSSM_BOOL genSeed) {
CSSM_KEY recvPubKey;
CSSM_KEY recvPrivKey;
CSSM_KEY sendPubKey;
CSSM_KEY sendPrivKey;
CSSM_KEY sendPubKeyRaw;
CSSM_KEY recvPubKeyRaw;
CSSM_BOOL rawPubKeysCreated = CSSM_FALSE;
CSSM_KEY_PTR sendPubKeyPtr = NULL;
CSSM_KEY_PTR recvPubKeyPtr = NULL;
CSSM_DATA ctext = {0, NULL};
CSSM_DATA rptext = {0, NULL};
CSSM_RETURN crtn;
int rtn = 0;
uint32 keyGenAlg;
uint32 mode = CSSM_ALGMODE_NONE; CSSM_BOOL twoKeys = CSSM_FALSE;
switch(alg) {
case CSSM_ALGID_FEED:
case CSSM_ALGID_FEECFILE:
twoKeys = CSSM_TRUE;
case CSSM_ALGID_FEEDEXP:
keyGenAlg = CSSM_ALGID_FEE;
break;
case CSSM_ALGID_RSA:
keyGenAlg = CSSM_ALGID_RSA;
break;
default:
printf("bogus algorithm\n");
return 1;
}
if(keyGenAlg == CSSM_ALGID_FEE) {
uint8 passwd[FEE_PASSWD_LEN];
CSSM_DATA pwdData = {FEE_PASSWD_LEN, passwd};
CSSM_DATA_PTR pwdDataPtr;
if(genSeed) {
simpleGenData(&pwdData, FEE_PASSWD_LEN, FEE_PASSWD_LEN);
pwdDataPtr = &pwdData;
}
else {
pwdDataPtr = NULL;
}
rtn = cspGenFEEKeyPair(cspHand,
USAGE_NAME,
USAGE_NAME_LEN,
keySizeInBits,
primeType,
curveType,
&recvPubKey,
pubIsRef,
CSSM_KEYUSE_ANY,
CSSM_KEYBLOB_RAW_FORMAT_NONE,
&recvPrivKey,
privIsRef,
CSSM_KEYUSE_ANY,
CSSM_KEYBLOB_RAW_FORMAT_NONE,
pwdDataPtr);
if(rtn) {
return testError(quiet);
}
if(twoKeys) {
rtn = cspGenFEEKeyPair(cspHand,
USAGE2_NAME,
USAGE2_NAME_LEN,
keySizeInBits,
primeType,
curveType,
&sendPubKey,
pubIsRef,
CSSM_KEYUSE_ANY,
CSSM_KEYBLOB_RAW_FORMAT_NONE,
&sendPrivKey,
privIsRef,
CSSM_KEYUSE_ANY,
CSSM_KEYBLOB_RAW_FORMAT_NONE,
pwdDataPtr);
if(rtn) {
return testError(quiet);
}
}
if(twoKeys) {
if(secondPubIsRaw && pubIsRef) {
crtn = cspRefKeyToRaw(cspHand, &sendPubKey, &sendPubKeyRaw);
if(crtn) {
return testError(quiet);
}
crtn = cspRefKeyToRaw(cspHand, &recvPubKey, &recvPubKeyRaw);
if(crtn) {
return testError(quiet);
}
sendPubKeyPtr = &sendPubKeyRaw;
recvPubKeyPtr = &recvPubKeyRaw;
rawPubKeysCreated = CSSM_TRUE;
}
else {
sendPubKeyPtr = &sendPubKey;
recvPubKeyPtr = &recvPubKey;
}
}
else {
sendPubKeyPtr = NULL;
recvPubKeyPtr = &recvPubKey;
}
}
else {
CSSM_KEYBLOB_FORMAT expectPubForm = rawPubForm;
CSSM_KEYBLOB_FORMAT expectPrivForm = rawPrivForm;
rtn = cspGenKeyPair(cspHand,
keyGenAlg,
USAGE_NAME,
USAGE_NAME_LEN,
keySizeInBits,
&recvPubKey,
pubIsRef,
twoKeys ? CSSM_KEYUSE_ANY : CSSM_KEYUSE_ENCRYPT,
rawPubForm,
&recvPrivKey,
privIsRef,
twoKeys ? CSSM_KEYUSE_ANY : CSSM_KEYUSE_DECRYPT,
rawPrivForm,
genSeed);
if(rtn) {
return testError(quiet);
}
sendPubKeyPtr = NULL;
recvPubKeyPtr = &recvPubKey;
if(!pubIsRef) {
if(rawPubForm == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
expectPubForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
}
if(recvPubKey.KeyHeader.Format != expectPubForm) {
printf("***Bad raw RSA pub key format - exp %u got %u\n",
(unsigned)expectPubForm,
(unsigned)recvPubKey.KeyHeader.Format);
if(testError(quiet)) {
return 1;
}
}
}
if(!privIsRef) {
if(rawPrivForm == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
expectPrivForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS8;
}
if(recvPrivKey.KeyHeader.Format != expectPrivForm) {
printf("***Bad raw RSA priv key format - exp %u got %u\n",
(unsigned)expectPrivForm,
(unsigned)recvPrivKey.KeyHeader.Format);
if(testError(quiet)) {
return 1;
}
}
}
}
#if DUMP_KEY_DATA
dumpBuffer("Pub Key Data", recvPubKey.KeyData.Data, recvPubKey.KeyData.Length);
dumpBuffer("Priv Key Data", recvPrivKey.KeyData.Data, recvPrivKey.KeyData.Length);
#endif
if(stagedEncr) {
crtn = cspStagedEncrypt(cspHand,
alg,
mode,
padding,
twoKeys ? &sendPrivKey : &recvPubKey,
twoKeys ? recvPubKeyPtr : NULL,
0, 0, 0, NULL, ptext,
&ctext,
CSSM_TRUE); }
else {
crtn = cspEncrypt(cspHand,
alg,
mode,
padding,
twoKeys ? &sendPrivKey : &recvPubKey,
twoKeys ? recvPubKeyPtr : NULL,
0, 0, NULL, ptext,
&ctext,
mallocCtext);
}
if(crtn) {
rtn = testError(quiet);
goto abort;
}
if(verbose) {
printf(" ..ptext size %lu ctext size %lu\n",
(unsigned long)ptext->Length, (unsigned long)ctext.Length);
}
if(skipDecrypt) {
goto abort;
}
if(stagedDecr) {
crtn = cspStagedDecrypt(cspHand,
alg,
mode,
padding,
&recvPrivKey,
sendPubKeyPtr,
0, 0, 0, NULL, &ctext,
&rptext,
CSSM_TRUE); }
else {
crtn = cspDecrypt(cspHand,
alg,
mode,
padding,
&recvPrivKey,
sendPubKeyPtr,
0, 0, NULL, &ctext,
&rptext,
mallocPtext);
}
if(crtn) {
rtn = testError(quiet);
goto abort;
}
if(ptext->Length != rptext.Length) {
printf("Ptext length mismatch: expect %lu, got %lu\n",
(unsigned long)ptext->Length, (unsigned long)rptext.Length);
rtn = testError(quiet);
if(rtn) {
goto abort;
}
}
if(memcmp(ptext->Data, rptext.Data, ptext->Length)) {
printf("***data miscompare\n");
rtn = testError(quiet);
}
abort:
if(cspFreeKey(cspHand, &recvPubKey)) {
printf("Error freeing recvPubKey\n");
rtn = 1;
}
if(cspFreeKey(cspHand, &recvPrivKey)) {
printf("Error freeing recvPrivKey\n");
rtn = 1;
}
if(twoKeys) {
if(cspFreeKey(cspHand, &sendPubKey)) {
printf("Error freeing sendPubKey\n");
rtn = 1;
}
if(cspFreeKey(cspHand, &sendPrivKey)) {
printf("Error freeing sendPrivKey\n");
rtn = 1;
}
if(rawPubKeysCreated) {
if(cspFreeKey(cspHand, &sendPubKeyRaw)) {
printf("Error freeing sendPubKeyRaw\n");
rtn = 1;
}
if(cspFreeKey(cspHand, &recvPubKeyRaw)) {
printf("Error freeing recvPubKeyRaw\n");
rtn = 1;
}
}
}
appFreeCssmData(&rptext, CSSM_FALSE);
appFreeCssmData(&ctext, CSSM_FALSE);
return rtn;
}
static const char *formStr(
CSSM_KEYBLOB_FORMAT form)
{
switch(form) {
case CSSM_KEYBLOB_RAW_FORMAT_NONE: return "NONE";
case CSSM_KEYBLOB_RAW_FORMAT_PKCS1: return "PKCS1";
case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: return "PKCS8";
case CSSM_KEYBLOB_RAW_FORMAT_X509: return "X509";
case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH: return "SSH1";
case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2: return "SSH2";
default:
printf("***BRRRZAP! formStr needs work\n");
exit(1);
}
}
int main(int argc, char **argv)
{
int arg;
char *argp;
unsigned loop;
CSSM_DATA ptext;
CSSM_CSP_HANDLE cspHand;
CSSM_BOOL pubIsRef = CSSM_TRUE;
CSSM_BOOL privIsRef = CSSM_TRUE;
CSSM_BOOL stagedEncr;
CSSM_BOOL stagedDecr;
const char *algStr;
uint32 encAlg; unsigned currAlg; int i;
CSSM_BOOL mallocCtext;
CSSM_BOOL mallocPtext;
int rtn = 0;
CSSM_BOOL genSeed; CSSM_PADDING padding;
CSSM_KEYBLOB_FORMAT rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
CSSM_KEYBLOB_FORMAT rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
unsigned loops = LOOPS_DEF;
CSSM_BOOL verbose = CSSM_FALSE;
unsigned minExp = MIN_EXP;
unsigned maxExp = DEFAULT_MAX_EXP;
CSSM_BOOL quiet = CSSM_FALSE;
uint32 keySizeInBits = CSP_KEY_SIZE_DEFAULT;
CSSM_BOOL keySizeSpec = CSSM_FALSE;
unsigned minAlg = ALG_FIRST;
uint32 maxAlg = ALG_LAST;
CSSM_BOOL skipDecrypt = CSSM_FALSE;
CSSM_BOOL bareCsp = CSSM_TRUE;
CSSM_BOOL doPause = CSSM_FALSE;
CSSM_BOOL smallKeys = CSSM_FALSE;
uint32 primeType = CSSM_FEE_PRIME_TYPE_DEFAULT; uint32 curveType = CSSM_FEE_CURVE_TYPE_DEFAULT; uint32 ptextSize = 0; dataType dtype = DT_Random;
CSSM_BOOL refKeysOnly = CSSM_FALSE;
CSSM_BOOL noPadding = CSSM_FALSE;
CSSM_BOOL stagingEnabled = CSSM_TRUE;
for(arg=1; arg<argc; arg++) {
argp = argv[arg];
switch(argp[0]) {
case 'a':
if(argp[1] != '=') {
usage(argv);
}
switch(argp[2]) {
case 'f':
minAlg = maxAlg = ALG_FEED;
break;
case 'x':
minAlg = maxAlg = ALG_FEEDEXP;
break;
case 'c':
minAlg = maxAlg = ALG_FEE_CFILE;
break;
case 'r':
minAlg = maxAlg = ALG_RSA;
break;
case 'a':
minAlg = ALG_FIRST;
maxAlg = ALG_LAST;
break;
default:
usage(argv);
}
break;
case 'l':
loops = atoi(&argp[2]);
break;
case 'n':
minExp = atoi(&argp[2]);
break;
case 'x':
maxExp = atoi(&argp[2]);
if(maxExp > MAX_EXP) {
usage(argv);
}
break;
case 'k':
keySizeInBits = atoi(&argv[arg][2]);
keySizeSpec = CSSM_TRUE;
break;
case 'K':
skipDecrypt = CSSM_TRUE;
break;
case 't':
ptextSize = atoi(&argp[2]);
break;
case 'D':
bareCsp = CSSM_FALSE;
#if CSPDL_ALL_KEYS_ARE_REF
refKeysOnly = CSSM_TRUE;
#endif
break;
case 'u':
smallKeys = CSSM_TRUE;
break;
case 'N':
noPadding = CSSM_TRUE;
break;
case 'z':
dtype = DT_Zero;
break;
case 'v':
verbose = CSSM_TRUE;
break;
case 'r':
refKeysOnly = CSSM_TRUE;
break;
case 'S':
stagingEnabled = CSSM_FALSE;
break;
case 'p':
doPause = CSSM_TRUE;
break;
case 'q':
quiet = CSSM_TRUE;
break;
case 'C':
switch(argp[2]) {
case 'm':
curveType = CSSM_FEE_CURVE_TYPE_MONTGOMERY;
break;
case 'w':
curveType = CSSM_FEE_CURVE_TYPE_WEIERSTRASS;
break;
default:
usage(argv);
}
break;
case 'P':
switch(argp[2]) {
case 'm':
primeType = CSSM_FEE_PRIME_TYPE_MERSENNE;
break;
case 'f':
primeType = CSSM_FEE_PRIME_TYPE_FEE;
break;
case 'g':
primeType = CSSM_FEE_PRIME_TYPE_GENERAL;
break;
default:
usage(argv);
}
break;
case 'h':
default:
usage(argv);
}
}
ptext.Data = (uint8 *)CSSM_MALLOC(MAX_DATA_SIZE);
if(ptext.Data == NULL) {
printf("Insufficient heap\n");
exit(1);
}
if(noPadding) {
if(ptextSize == 0) {
printf("**WARNING NoPad mode will fail with random plaintext size\n");
}
if(!keySizeSpec) {
printf("**WARNING NoPad mode will fail with random key size\n");
}
else {
uint32 keyBytes = keySizeInBits / 8;
if(ptextSize != keyBytes) {
printf("***WARNING NoPad mode requires plaintext size = key size\n");
}
}
}
printf("Starting asymTest; args: ");
for(i=1; i<argc; i++) {
printf("%s ", argv[i]);
}
printf("\n");
cspHand = cspDlDbStartup(bareCsp, NULL);
if(cspHand == 0) {
exit(1);
}
for(currAlg=minAlg; currAlg<=maxAlg; currAlg++) {
switch(currAlg) {
case ALG_FEED:
encAlg = CSSM_ALGID_FEED;
algStr = "FEED";
padding = CSSM_PADDING_NONE;
break;
case ALG_FEEDEXP:
encAlg = CSSM_ALGID_FEEDEXP;
algStr = "FEEDExp";
padding = CSSM_PADDING_NONE;
break;
case ALG_FEE_CFILE:
encAlg = CSSM_ALGID_FEECFILE;
algStr = "FEE_CFILE";
padding = CSSM_PADDING_NONE;
break;
case ALG_RSA:
encAlg = CSSM_ALGID_RSA;
algStr = "RSA";
if(noPadding) {
padding = CSSM_PADDING_NONE;
}
else {
padding = CSSM_PADDING_PKCS1;
}
break;
}
if(!quiet) {
printf("Testing alg %s\n", algStr);
}
for(loop=1; ; loop++) {
if(doPause) {
fpurge(stdin);
printf("Top of loop; hit CR to proceed: ");
getchar();
}
if(ptextSize) {
if(dtype == DT_Zero) {
memset(ptext.Data, 0, ptextSize);
ptext.Length = ptextSize;
}
else {
simpleGenData(&ptext, ptextSize, ptextSize);
}
}
else {
ptext.Length = genData(ptext.Data, minExp, maxExp, dtype);
}
if((encAlg == CSSM_ALGID_RSA) && (padding == CSSM_PADDING_NONE)) {
ptext.Data[0] &= 0x3f;
}
if(!keySizeSpec) {
if(encAlg == CSSM_ALGID_RSA) {
if(smallKeys) {
keySizeInBits = CSP_RSA_KEY_SIZE_DEFAULT;
}
else {
keySizeInBits = randKeySizeBits(CSSM_ALGID_RSA, OT_Encrypt);
}
}
else {
if(smallKeys) {
keySizeInBits = 127;
}
else {
randFeeKeyParams(encAlg,
&keySizeInBits,
&primeType,
&curveType);
}
}
}
#if RSA_PLAINTEXT_LIMIT
if(encAlg == CSSM_ALGID_RSA) {
unsigned modSize;
unsigned maxSize;
if(keySizeInBits == CSP_KEY_SIZE_DEFAULT) {
modSize = CSP_RSA_KEY_SIZE_DEFAULT / 8;
}
else {
modSize = keySizeInBits / 8;
}
maxSize = modSize - 11;
ptext.Length = genRand(1, maxSize);
}
#endif
if(!quiet) {
if(encAlg == CSSM_ALGID_RSA) {
printf("..loop %d text size %lu keySize %u\n",
loop, (unsigned long)ptext.Length, (unsigned)keySizeInBits);
}
else {
printf("..loop %d text size %lu keySize %u primeType %s "
"curveType %s\n",
loop, (unsigned long)ptext.Length, (unsigned)keySizeInBits,
primeTypeStr(primeType), curveTypeStr(curveType));
}
}
if(!refKeysOnly) {
pubIsRef = (loop & 1) ? CSSM_TRUE : CSSM_FALSE;
privIsRef = (loop & 8) ? CSSM_TRUE : CSSM_FALSE;
}
if((currAlg == ALG_FEE_CFILE) || !stagingEnabled) {
stagedEncr = CSSM_FALSE;
stagedDecr = CSSM_FALSE;
}
else {
stagedEncr = (loop & 2) ? CSSM_TRUE : CSSM_FALSE;
stagedDecr = (loop & 4) ? CSSM_TRUE : CSSM_FALSE;
}
if(!stagedEncr) {
mallocCtext = (ptext.Data[0] & 1) ? CSSM_TRUE : CSSM_FALSE;
}
else {
mallocCtext = CSSM_FALSE;
}
if(!stagedDecr) {
mallocPtext = (ptext.Data[0] & 2) ? CSSM_TRUE : CSSM_FALSE;
}
else {
mallocPtext = CSSM_FALSE;
}
switch(currAlg) {
case ALG_FEED:
case ALG_FEEDEXP:
genSeed = (ptext.Data[0] & 4) ? CSSM_TRUE : CSSM_FALSE;
break;
default:
genSeed = CSSM_FALSE;
}
rawPubFormat = rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
if(currAlg == ALG_RSA) {
unsigned die;
if(!pubIsRef) {
die = ptext.Data[1] % 5;
switch(die) {
case 0:
rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
break;
case 1:
rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
break;
case 2:
rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_OPENSSH;
break;
case 3:
rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2;
break;
default:
rawPubFormat = CSSM_KEYBLOB_RAW_FORMAT_X509;
break;
}
}
if(!privIsRef) {
die = ptext.Data[2] % 4;
switch(die) {
case 0:
rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
break;
case 1:
rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
break;
case 2:
rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_OPENSSH;
break;
default:
rawPrivFormat = CSSM_KEYBLOB_RAW_FORMAT_PKCS8;
break;
}
}
}
#if 0
if(encAlg == CSSM_ALGID_RSA) {
stagedEncr = stagedDecr = CSSM_TRUE;
}
#endif
if(!quiet) {
printf(" pubRef %d privRef %d stgEncr %d stgdDecr %d "
"malPtext %d malCtext %d genSeed %d pubForm %s privForm %s\n",
(int)pubIsRef, (int)privIsRef, (int)stagedEncr, (int)stagedDecr,
(int)mallocPtext, (int)mallocCtext, (int)genSeed,
formStr(rawPubFormat), formStr(rawPrivFormat));
}
if(doTest(cspHand,
encAlg,
padding,
&ptext,
verbose,
quiet,
keySizeInBits,
primeType,
curveType,
pubIsRef,
rawPubFormat,
privIsRef,
rawPrivFormat,
#if CSPDL_2ND_PUB_KEY_IS_RAW
bareCsp ? CSSM_FALSE : CSSM_TRUE,
#else
CSSM_FALSE,
#endif
stagedEncr,
stagedDecr,
mallocCtext,
mallocPtext,
skipDecrypt,
genSeed)) {
rtn = 1;
goto testDone;
}
if(loops && (loop == loops)) {
break;
}
}
}
testDone:
CSSM_ModuleDetach(cspHand);
if((rtn == 0) && !quiet) {
printf("%s test complete\n", argv[0]);
}
return rtn;
}