--- /dev/null
+/* Copyright (c) 2005-2006 Apple Computer, Inc.
+ *
+ * ssl2Padding.cpp - test CSSM_PADDING_APPLE_SSLv2.
+ */
+
+
+ /*
+ * This table illustrates the combinations of:
+ *
+ * -- SSLv2 (v2) and SSLv3/TLSv1 (v3+) enables (0/1) on the client and server side
+ * -- the resulting negotiated protocols (including those forced by a man-in-the-middle
+ * attacker, denoted by (m))
+ * -- the padding generated by the client (client pad)
+ * -- the padding style checked by the server (server pad)
+ * -- and the end results
+ *
+ * client server
+ * ------ ------
+ * v2 v3+ v2 v3+ negotiate client pad server pad result
+ * -- -- -- -- --------- ---------- ---------- ------
+ * 0 0 x x impossible
+ * x x 0 0 impossible
+ * 0 1 0 1 v3+ PKCS1 PKCS1 normal
+ * 0 1 0 1 v2 (m) Attack fails, client rejects server hello
+ * 0 1 1 0 fail incompatible
+ * 0 1 1 1 v3+ PKCS1 PKCS1 normal
+ * 0 1 1 1 v2 (m) Attack fails, client rejects server hello
+ * 1 0 0 1 fail incompatible
+ * 1 0 1 0 v2 PKCS1 PKCS1 normal, both sides are dumb SSL2
+ * 1 0 1 0 v3+ Attack fails, server rejects client hello
+ * 1 0 1 1 v2 PKCS1 SSLv2 normal, dumb client
+ * 1 0 1 1 v3+ (m) Attack fails, client rejects server hello
+ * 1 1 0 1 v3+ PKCS1 PKCS1 normal
+ * 1 1 0 1 v2 (m) Attack fails, server rejects SSL2 handshake
+ * 1 1 1 0 v2 SSLv2 PKCS1 normal, dumb server
+ * 1 1 1 0 v3+ (m) Attack fails, server rejects SSL3 handshakes
+ * 1 1 1 1 v3+ PKCS1 PKCS1 normal
+ * 1 1 1 1 v2 (m) SSLv2 SSLv2 Attack fails due to SSLv2 pad detect
+ *
+ * The client generates SSLv2 padding if it's capable of v3+ but is currently operating
+ * in v2 per negotiation.
+ *
+ * The server checks for SSLv2 padding if it's capable of v3+ but is currently operating
+ * in v2 per negotiation. If SSLv2 padding is seen, fail.
+ */
+
+#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 LOOPS_DEF 10
+
+#define KEY_SIZE_DEF 1024
+#define KEY_SIZE_SMALL 512
+
+#define PTEXT_LEN 32 /* bytes */
+
+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(" k=keySizeInBits; default=%d\n", KEY_SIZE_DEF);
+ printf(" D (CSP/DL; default = bare CSP)\n");
+ printf(" p (pause on each loop)\n");
+ printf(" u (quick; small keys)\n");
+ printf(" v(erbose)\n");
+ printf(" q(uiet)\n");
+ printf(" h(elp)\n");
+ exit(1);
+}
+
+/* special-purpose generate-context, encrypt, and decrypt routines just for this test */
+static int genRsaCryptContext(
+ CSSM_CSP_HANDLE cspHand,
+ CSSM_KEY_PTR key,
+ CSSM_PADDING padding,
+ CSSM_BOOL quiet,
+ CSSM_CC_HANDLE &ccHand) // RETURNED
+{
+ CSSM_RETURN crtn;
+ CSSM_ACCESS_CREDENTIALS creds;
+
+ memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
+ crtn = CSSM_CSP_CreateAsymmetricContext(cspHand,
+ CSSM_ALGID_RSA,
+ &creds, // access
+ key,
+ padding,
+ &ccHand);
+ if(crtn) {
+ cssmPerror("CSSM_CSP_CreateAsymmetricContext", crtn);
+ return testError(quiet);
+ }
+ return 0;
+}
+
+static int doRsaEncrypt(
+ CSSM_CSP_HANDLE cspHand,
+ CSSM_KEY_PTR key,
+ CSSM_PADDING padding,
+ CSSM_BOOL quiet,
+ CSSM_DATA *ptext,
+ CSSM_DATA *ctext)
+{
+ CSSM_CC_HANDLE ccHand;
+ int rtn;
+ CSSM_RETURN crtn;
+ CSSM_SIZE bytesMoved;
+ CSSM_DATA remData = {0, NULL};
+
+ rtn = genRsaCryptContext(cspHand, key, padding, quiet, ccHand);
+ if(rtn) {
+ return rtn;
+ }
+ crtn = CSSM_EncryptData(ccHand,
+ ptext,
+ 1,
+ ctext,
+ 1,
+ &bytesMoved,
+ &remData);
+ CSSM_DeleteContext(ccHand);
+ if(crtn == CSSM_OK) {
+ /*
+ * Deal with remData - its contents are included in bytesMoved.
+ */
+ if(remData.Length != 0) {
+ /* malloc and copy a new one */
+ uint8 *newCdata = (uint8 *)appMalloc(bytesMoved, NULL);
+ memmove(newCdata, ctext->Data, ctext->Length);
+ memmove(newCdata+ctext->Length, remData.Data, remData.Length);
+ CSSM_FREE(ctext->Data);
+ ctext->Data = newCdata;
+ }
+ ctext->Length = bytesMoved;
+ return 0;
+ }
+ else {
+ cssmPerror("CSSM_EncryptData", crtn);
+ return testError(quiet);
+ }
+}
+
+static int doRsaDecrypt(
+ CSSM_CSP_HANDLE cspHand,
+ CSSM_KEY_PTR key,
+ CSSM_PADDING padding,
+ CSSM_BOOL quiet,
+ CSSM_RETURN expectRtn,
+ CSSM_DATA *ctext,
+ CSSM_DATA *rptext)
+{
+ CSSM_CC_HANDLE ccHand;
+ int rtn;
+ CSSM_RETURN crtn;
+ CSSM_SIZE bytesMoved;
+ CSSM_DATA remData = {0, NULL};
+
+ rtn = genRsaCryptContext(cspHand, key, padding, quiet, ccHand);
+ if(rtn) {
+ return rtn;
+ }
+ crtn = CSSM_DecryptData(ccHand,
+ ctext,
+ 1,
+ rptext,
+ 1,
+ &bytesMoved,
+ &remData);
+ CSSM_DeleteContext(ccHand);
+ if(crtn != expectRtn) {
+ printf(" CSSM_DecryptData: expect %s\n", cssmErrToStr(expectRtn));
+ printf(" CSSM_DecryptData: got %s\n", cssmErrToStr(crtn));
+ return testError(quiet);
+ }
+ if(crtn) {
+ /* no need to process further */
+ return 0;
+ }
+ if(crtn == CSSM_OK) {
+ /*
+ * Deal with remData - its contents are included in bytesMoved.
+ */
+ if(remData.Length != 0) {
+ /* malloc and copy a new one */
+ uint8 *newRpdata = (uint8 *)appMalloc(bytesMoved, NULL);
+ memmove(newRpdata, rptext->Data, rptext->Length);
+ memmove(newRpdata+rptext->Length, remData.Data, remData.Length);
+ CSSM_FREE(rptext->Data);
+ rptext->Data = newRpdata;
+ }
+ rptext->Length = bytesMoved;
+ return 0;
+ }
+ else {
+ cssmPerror("CSSM_DecryptData", crtn);
+ return testError(quiet);
+ }
+}
+
+/*
+ * encrypt with specified pad
+ * decrypt with specified pad, verify expected result (which may be failure)
+ */
+static int doTest(
+ CSSM_CSP_HANDLE cspHand,
+ CSSM_KEY_PTR pubKey,
+ CSSM_KEY_PTR privKey,
+ CSSM_PADDING encrPad,
+ CSSM_PADDING decrPad,
+ CSSM_BOOL quiet,
+ CSSM_RETURN expectResult)
+{
+ int rtn;
+ uint8 ptext[PTEXT_LEN];
+ CSSM_DATA ptextData = {PTEXT_LEN, ptext};
+ CSSM_DATA ctext = {0, NULL};
+ CSSM_DATA rptext = {0, NULL};
+
+ simpleGenData(&ptextData, PTEXT_LEN, PTEXT_LEN);
+ rtn = doRsaEncrypt(cspHand, pubKey, encrPad, quiet, &ptextData, &ctext);
+ if(rtn) {
+ goto errOut;
+ }
+ rtn = doRsaDecrypt(cspHand, privKey, decrPad, quiet, expectResult, &ctext, &rptext);
+ if(rtn) {
+ goto errOut;
+ }
+ if(expectResult == CSSM_OK) {
+ if(memcmp(rptext.Data, ptextData.Data, PTEXT_LEN)) {
+ printf("***Data miscomapare after decrypt\n");
+ rtn = testError(quiet);
+ }
+ }
+errOut:
+ if(ctext.Data) {
+ CSSM_FREE(ctext.Data);
+ }
+ if(rptext.Data) {
+ CSSM_FREE(rptext.Data);
+ }
+ return rtn;
+}
+
+int main(int argc, char **argv)
+{
+ int arg;
+ char *argp;
+ unsigned loop;
+ CSSM_CSP_HANDLE cspHand;
+ int rtn = 0;
+
+ /*
+ * User-spec'd params
+ */
+ unsigned loops = LOOPS_DEF;
+ CSSM_BOOL verbose = CSSM_FALSE;
+ CSSM_BOOL quiet = CSSM_FALSE;
+ uint32 keySizeInBits = KEY_SIZE_DEF;
+ CSSM_BOOL bareCsp = CSSM_TRUE;
+ CSSM_BOOL doPause = CSSM_FALSE;
+
+ for(arg=1; arg<argc; arg++) {
+ argp = argv[arg];
+ switch(argp[0]) {
+ case 'l':
+ loops = atoi(&argp[2]);
+ break;
+ case 'k':
+ keySizeInBits = atoi(&argv[arg][2]);
+ break;
+ case 'D':
+ bareCsp = CSSM_FALSE;
+ break;
+ case 'u':
+ keySizeInBits = KEY_SIZE_SMALL;
+ break;
+ case 'v':
+ verbose = CSSM_TRUE;
+ break;
+ case 'p':
+ doPause = CSSM_TRUE;
+ break;
+ case 'q':
+ quiet = CSSM_TRUE;
+ break;
+ case 'h':
+ default:
+ usage(argv);
+ }
+ }
+
+ testStartBanner("ssl2Padding", argc, argv);
+
+ cspHand = cspDlDbStartup(bareCsp, NULL);
+ if(cspHand == 0) {
+ exit(1);
+ }
+ CSSM_KEY pubKey;
+ CSSM_KEY privKey;
+
+ CSSM_RETURN crtn = cspGenKeyPair(cspHand, CSSM_ALGID_RSA,
+ USAGE_NAME, USAGE_NAME_LEN,
+ keySizeInBits,
+ &pubKey, CSSM_TRUE /* ref */, CSSM_KEYUSE_ANY, CSSM_KEYBLOB_RAW_FORMAT_NONE,
+ &privKey, CSSM_TRUE /* ref */, CSSM_KEYUSE_ANY, CSSM_KEYBLOB_RAW_FORMAT_NONE,
+ CSSM_FALSE);
+ if(crtn) {
+ printf("***Error generating key pair. Aborting.\n");
+ exit(1);
+ }
+ for(loop=1; ; loop++) {
+ if(doPause) {
+ fpurge(stdin);
+ printf("Top of loop; hit CR to proceed: ");
+ getchar();
+ }
+
+ /* encrypt by client, decrypt by server. */
+
+ /*
+ * SSLv3+ negotiated, normal case, or
+ * both sides support SSLv2 only
+ */
+ if(!quiet) {
+ printf("...loop %u\n", loop);
+ printf(" encrPad PKCS1 decrPad PKCS1\n");
+ }
+ rtn = doTest(cspHand, &pubKey, &privKey,
+ CSSM_PADDING_PKCS1, CSSM_PADDING_PKCS1,
+ quiet, CSSM_OK);
+ if(rtn) {
+ break;
+ }
+
+ /*
+ * Server supports SSLv2 and SSLv3+, client supports SSLv2 only
+ */
+ if(!quiet) {
+ printf(" encrPad PKCS1 decrPad SSLv2\n");
+ }
+ rtn = doTest(cspHand, &pubKey, &privKey,
+ CSSM_PADDING_PKCS1, CSSM_PADDING_APPLE_SSLv2,
+ quiet, CSSM_OK);
+ if(rtn) {
+ break;
+ }
+
+ /*
+ * Server supports SSLv2 only, client supports SSLv2 and SSLv3+
+ */
+ if(!quiet) {
+ printf(" encrPad SSLv2 decrPad PKCS1\n");
+ }
+ rtn = doTest(cspHand, &pubKey, &privKey,
+ CSSM_PADDING_APPLE_SSLv2, CSSM_PADDING_PKCS1,
+ quiet, CSSM_OK);
+ if(rtn) {
+ break;
+ }
+
+ /*
+ * Both sides support SSLv3+ but a man in the middle has forced the
+ * negotiated protocol down to SSLv2
+ */
+ if(!quiet) {
+ printf(" encrPad SSLv2 decrPad SSLv2, expect failure\n");
+ }
+ rtn = doTest(cspHand, &pubKey, &privKey,
+ CSSM_PADDING_APPLE_SSLv2, CSSM_PADDING_APPLE_SSLv2,
+ quiet, CSSMERR_CSP_APPLE_SSLv2_ROLLBACK);
+ if(rtn) {
+ break;
+ }
+
+ if(loops && (loop == loops)) {
+ break;
+ }
+ } /* for loop */
+
+ CSSM_ModuleDetach(cspHand);
+ if((rtn == 0) && !quiet) {
+ printf("%s test complete\n", argv[0]);
+ }
+ return rtn;
+}