--- /dev/null
+/*
+ * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
+ *
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please obtain
+ * a copy of the License at http://www.apple.com/publicsource and read it before
+ * using this file.
+ *
+ * This Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
+ * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
+ * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
+ * specific language governing rights and limitations under the License.
+ */
+
+
+/*
+ File: sslKeyExchange.c
+
+ Contains: Support for key exchange and server key exchange
+
+ Written by: Doug Mitchell
+
+ Copyright: (c) 1999 by Apple Computer, Inc., all rights reserved.
+
+*/
+
+#include "sslContext.h"
+#include "sslHandshake.h"
+#include "sslMemory.h"
+#include "sslDebug.h"
+#include "sslUtils.h"
+#include "appleCdsa.h"
+#include "sslDigests.h"
+
+#include <assert.h>
+#include <string.h>
+
+/*
+ * Client RSA Key Exchange msgs actually start with a two-byte
+ * length field, contrary to the first version of RFC 2246, dated
+ * January 1999. See RFC 2246, March 2002, section 7.4.7.1 for
+ * updated requirements.
+ */
+#define RSA_CLIENT_KEY_ADD_LENGTH 1
+
+typedef CSSM_KEY_PTR SSLRSAPrivateKey;
+
+static OSStatus SSLEncodeRSAServerKeyExchange(SSLRecord &keyExch, SSLContext *ctx);
+static OSStatus SSLEncodeRSAKeyParams(SSLBuffer *keyParams, SSLRSAPrivateKey *key, SSLContext *ctx);
+static OSStatus SSLProcessRSAServerKeyExchange(SSLBuffer message, SSLContext *ctx);
+static OSStatus SSLDecodeRSAKeyExchange(SSLBuffer keyExchange, SSLContext *ctx);
+static OSStatus SSLEncodeRSAKeyExchange(SSLRecord &keyExchange, SSLContext *ctx);
+#if APPLE_DH
+static OSStatus SSLEncodeDHanonServerKeyExchange(SSLRecord &keyExch, SSLContext *ctx);
+static OSStatus SSLEncodeDHanonKeyExchange(SSLRecord &keyExchange, SSLContext *ctx);
+static OSStatus SSLDecodeDHanonKeyExchange(SSLBuffer keyExchange, SSLContext *ctx);
+static OSStatus SSLProcessDHanonServerKeyExchange(SSLBuffer message, SSLContext *ctx);
+#endif
+
+OSStatus
+SSLEncodeServerKeyExchange(SSLRecord &keyExch, SSLContext *ctx)
+{ OSStatus err;
+
+ switch (ctx->selectedCipherSpec->keyExchangeMethod)
+ { case SSL_RSA:
+ case SSL_RSA_EXPORT:
+ if ((err = SSLEncodeRSAServerKeyExchange(keyExch, ctx)) != 0)
+ return err;
+ break;
+ #if APPLE_DH
+ case SSL_DH_anon:
+ if ((err = SSLEncodeDHanonServerKeyExchange(keyExch, ctx)) != 0)
+ return err;
+ break;
+ #endif
+ default:
+ return unimpErr;
+ }
+
+ return noErr;
+}
+
+static OSStatus
+SSLEncodeRSAServerKeyExchange(SSLRecord &keyExch, SSLContext *ctx)
+{ OSStatus err;
+ UInt8 *charPtr;
+ int length;
+ UInt32 outputLen, localKeyModulusLen;
+ UInt8 hashes[36];
+ SSLBuffer exportKey,clientRandom,serverRandom,hashCtx, hash;
+
+ exportKey.data = 0;
+ hashCtx.data = 0;
+
+ /* we have a public key here... */
+ assert(ctx->encryptPubKey != NULL);
+ assert(ctx->protocolSide == SSL_ServerSide);
+
+ if ((err = SSLEncodeRSAKeyParams(&exportKey, &ctx->encryptPubKey, ctx)) != 0)
+ goto fail;
+
+ assert(ctx->signingPubKey != NULL);
+ localKeyModulusLen = sslKeyLengthInBytes(ctx->signingPubKey);
+
+ length = exportKey.length + 2 + localKeyModulusLen;
+ /* RSA ouputs a block as long as the modulus */
+
+ assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
+ (ctx->negProtocolVersion == TLS_Version_1_0));
+ keyExch.protocolVersion = ctx->negProtocolVersion;
+ keyExch.contentType = SSL_RecordTypeHandshake;
+ if ((err = SSLAllocBuffer(keyExch.contents, length+4, ctx)) != 0)
+ goto fail;
+
+ charPtr = keyExch.contents.data;
+ *charPtr++ = SSL_HdskServerKeyExchange;
+ charPtr = SSLEncodeInt(charPtr, length, 3);
+
+ memcpy(charPtr, exportKey.data, exportKey.length);
+ charPtr += exportKey.length;
+
+ clientRandom.data = ctx->clientRandom;
+ clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+ serverRandom.data = ctx->serverRandom;
+ serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+
+ hash.data = &hashes[0];
+ hash.length = 16;
+ if ((err = ReadyHash(SSLHashMD5, hashCtx, ctx)) != 0)
+ goto fail;
+ if ((err = SSLHashMD5.update(hashCtx, clientRandom)) != 0)
+ goto fail;
+ if ((err = SSLHashMD5.update(hashCtx, serverRandom)) != 0)
+ goto fail;
+ if ((err = SSLHashMD5.update(hashCtx, exportKey)) != 0)
+ goto fail;
+ if ((err = SSLHashMD5.final(hashCtx, hash)) != 0)
+ goto fail;
+ if ((err = SSLFreeBuffer(hashCtx, ctx)) != 0)
+ goto fail;
+
+ hash.data = &hashes[16];
+ hash.length = 20;
+ if ((err = ReadyHash(SSLHashSHA1, hashCtx, ctx)) != 0)
+ goto fail;
+ if ((err = SSLHashSHA1.update(hashCtx, clientRandom)) != 0)
+ goto fail;
+ if ((err = SSLHashSHA1.update(hashCtx, serverRandom)) != 0)
+ goto fail;
+ if ((err = SSLHashSHA1.update(hashCtx, exportKey)) != 0)
+ goto fail;
+ if ((err = SSLHashSHA1.final(hashCtx, hash)) != 0)
+ goto fail;
+ if ((err = SSLFreeBuffer(hashCtx, ctx)) != 0)
+ goto fail;
+
+ charPtr = SSLEncodeInt(charPtr, localKeyModulusLen, 2);
+ err = sslRsaRawSign(ctx,
+ ctx->signingPrivKey,
+ ctx->signingKeyCsp,
+ hashes,
+ 36,
+ charPtr,
+ length,
+ &outputLen);
+ if(err) {
+ goto fail;
+ }
+ assert(outputLen == localKeyModulusLen);
+
+ err = noErr;
+
+fail:
+ SSLFreeBuffer(hashCtx, ctx);
+ SSLFreeBuffer(exportKey, ctx);
+
+ return err;
+}
+
+static OSStatus
+SSLEncodeRSAKeyParams(SSLBuffer *keyParams, SSLRSAPrivateKey *key, SSLContext *ctx)
+{ OSStatus err;
+ SSLBuffer modulus, exponent;
+ UInt8 *charPtr;
+
+ err = sslGetPubKeyBits(ctx,
+ *key,
+ ctx->encryptKeyCsp,
+ &modulus,
+ &exponent);
+ if(err) {
+ SSLFreeBuffer(modulus, ctx);
+ SSLFreeBuffer(exponent, ctx);
+ return err;
+ }
+
+ if ((err = SSLAllocBuffer(*keyParams, modulus.length + exponent.length + 4, ctx)) != 0)
+ return err;
+ charPtr = keyParams->data;
+ charPtr = SSLEncodeInt(charPtr, modulus.length, 2);
+ memcpy(charPtr, modulus.data, modulus.length);
+ charPtr += modulus.length;
+ charPtr = SSLEncodeInt(charPtr, exponent.length, 2);
+ memcpy(charPtr, exponent.data, exponent.length);
+
+ /* these were mallocd by sslGetPubKeyBits() */
+ SSLFreeBuffer(modulus, ctx);
+ SSLFreeBuffer(exponent, ctx);
+ return noErr;
+}
+
+#if APPLE_DH
+static OSStatus
+SSLEncodeDHanonServerKeyExchange(SSLRecord &keyExch, SSLContext *ctx)
+{ OSStatus err;
+ UInt32 length;
+ UInt8 *charPtr;
+ SSLRandomCtx random;
+ int rsaErr;
+
+#if RSAREF
+ length = 6 + ctx->dhAnonParams.primeLen + ctx->dhAnonParams.generatorLen +
+ ctx->dhExchangePublic.length;
+
+ assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
+ (ctx->negProtocolVersion == TLS_Version_1_0));
+ keyExch.protocolVersion = ctx->negProtocolVersion;
+ keyExch.contentType = SSL_RecordTypeHandshake;
+ if ((err = SSLAllocBuffer(keyExch.contents, length+4, ctx)) != 0)
+ return err;
+
+ charPtr = keyExch.contents.data;
+ *charPtr++ = SSL_HdskServerKeyExchange;
+ charPtr = SSLEncodeInt(charPtr, length, 3);
+
+ charPtr = SSLEncodeInt(charPtr, ctx->dhAnonParams.primeLen, 2);
+ memcpy(charPtr, ctx->dhAnonParams.prime, ctx->dhAnonParams.primeLen);
+ charPtr += ctx->dhAnonParams.primeLen;
+
+ charPtr = SSLEncodeInt(charPtr, ctx->dhAnonParams.generatorLen, 2);
+ memcpy(charPtr, ctx->dhAnonParams.generator, ctx->dhAnonParams.generatorLen);
+ charPtr += ctx->dhAnonParams.generatorLen;
+
+ if ((err = SSLAllocBuffer(ctx->dhExchangePublic,
+ ctx->peerDHParams.primeLen, ctx)) != 0)
+ return err;
+ if ((err = SSLAllocBuffer(ctx->dhPrivate,
+ ctx->dhExchangePublic.length - 16, ctx)) != 0)
+ return err;
+
+ if ((err = ReadyRandom(&random, ctx)) != 0)
+ return err;
+
+ if ((rsaErr = R_SetupDHAgreement(ctx->dhExchangePublic.data, ctx->dhPrivate.data,
+ ctx->dhPrivate.length, &ctx->dhAnonParams, &random)) != 0)
+ { err = SSLUnknownErr;
+ return err;
+ }
+
+ charPtr = SSLEncodeInt(charPtr, ctx->dhExchangePublic.length, 2);
+ memcpy(charPtr, ctx->dhExchangePublic.data, ctx->dhExchangePublic.length);
+ charPtr += ctx->dhExchangePublic.length;
+
+#elif BSAFE
+ { A_DH_KEY_AGREE_PARAMS *params;
+ unsigned int outputLen;
+
+ if ((rsaErr = B_GetAlgorithmInfo((POINTER*)¶ms, ctx->dhAnonParams, AI_DHKeyAgree)) != 0)
+ return SSLUnknownErr;
+ if ((err = ReadyRandom(&random, ctx)) != 0)
+ return err;
+ if ((err = SSLAllocBuffer(ctx->dhExchangePublic, 128, ctx)) != 0)
+ return err;
+ if ((rsaErr = B_KeyAgreePhase1(ctx->dhAnonParams, ctx->dhExchangePublic.data,
+ &outputLen, 128, random, NO_SURR)) != 0)
+ { err = SSLUnknownErr;
+ return err;
+ }
+ ctx->dhExchangePublic.length = outputLen;
+
+ length = 6 + params->prime.len + params->base.len + ctx->dhExchangePublic.length;
+
+ assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
+ (ctx->negProtocolVersion == TLS_Version_1_0));
+ keyExch.protocolVersion = ctx->negProtocolVersion;
+ keyExch.contentType = SSL_RecordTypeHandshake;
+ if ((err = SSLAllocBuffer(keyExch.contents, length+4, ctx)) != 0)
+ return err;
+
+ charPtr = keyExch.contents.data;
+ *charPtr++ = SSL_HdskServerKeyExchange;
+ charPtr = SSLEncodeInt(charPtr, length, 3);
+
+ charPtr = SSLEncodeInt(charPtr, params->prime.len, 2);
+ memcpy(charPtr, params->prime.data, params->prime.len);
+ charPtr += params->prime.len;
+
+ charPtr = SSLEncodeInt(charPtr, params->base.len, 2);
+ memcpy(charPtr, params->base.data, params->base.len);
+ charPtr += params->base.len;
+
+ charPtr = SSLEncodeInt(charPtr, ctx->dhExchangePublic.length, 2);
+ memcpy(charPtr, ctx->dhExchangePublic.data, ctx->dhExchangePublic.length);
+ charPtr += ctx->dhExchangePublic.length;
+ }
+#endif /* RSAREF / BSAFE */
+
+ assert(charPtr == keyExch.contents.data + keyExch.contents.length);
+
+ return noErr;
+}
+
+#endif /* APPLE_DH */
+
+OSStatus
+SSLProcessServerKeyExchange(SSLBuffer message, SSLContext *ctx)
+{ OSStatus err;
+
+ switch (ctx->selectedCipherSpec->keyExchangeMethod)
+ { case SSL_RSA:
+ case SSL_RSA_EXPORT:
+ if ((err = SSLProcessRSAServerKeyExchange(message, ctx)) != 0)
+ return err;
+ break;
+ #if APPLE_DH
+ case SSL_DH_anon:
+ if ((err = SSLProcessDHanonServerKeyExchange(message, ctx)) != 0)
+ return err;
+ break;
+ #endif
+ default:
+ return unimpErr;
+ }
+
+ return noErr;
+}
+
+static OSStatus
+SSLProcessRSAServerKeyExchange(SSLBuffer message, SSLContext *ctx)
+{
+ OSStatus err;
+ SSLBuffer tempPubKey, hashOut, hashCtx, clientRandom, serverRandom;
+ UInt16 modulusLen, exponentLen, signatureLen;
+ UInt8 *charPtr, *modulus, *exponent, *signature;
+ UInt8 hash[36];
+ SSLBuffer signedHashes;
+
+ signedHashes.data = 0;
+ hashCtx.data = 0;
+
+ if (message.length < 2) {
+ sslErrorLog("SSLProcessRSAServerKeyExchange: msg len error 2\n");
+ return errSSLProtocol;
+ }
+ charPtr = message.data;
+ modulusLen = SSLDecodeInt(charPtr, 2);
+ modulus = charPtr + 2;
+ charPtr += 2+modulusLen;
+ if (message.length < (unsigned)(4 + modulusLen)) {
+ sslErrorLog("SSLProcessRSAServerKeyExchange: msg len error 2\n");
+ return errSSLProtocol;
+ }
+ exponentLen = SSLDecodeInt(charPtr, 2);
+ exponent = charPtr + 2;
+ charPtr += 2+exponentLen;
+ if (message.length < (unsigned)(6 + modulusLen + exponentLen)) {
+ sslErrorLog("SSLProcessRSAServerKeyExchange: msg len error 2\n");
+ return errSSLProtocol;
+ }
+ signatureLen = SSLDecodeInt(charPtr, 2);
+ signature = charPtr + 2;
+ if (message.length != (unsigned)(6 + modulusLen + exponentLen + signatureLen)) {
+ sslErrorLog("SSLProcessRSAServerKeyExchange: msg len error 3\n");
+ return errSSLProtocol;
+ }
+
+ clientRandom.data = ctx->clientRandom;
+ clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+ serverRandom.data = ctx->serverRandom;
+ serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE;
+ tempPubKey.data = message.data;
+ tempPubKey.length = modulusLen + exponentLen + 4;
+ hashOut.data = hash;
+
+ hashOut.length = 16;
+ if ((err = ReadyHash(SSLHashMD5, hashCtx, ctx)) != 0)
+ goto fail;
+ if ((err = SSLHashMD5.update(hashCtx, clientRandom)) != 0)
+ goto fail;
+ if ((err = SSLHashMD5.update(hashCtx, serverRandom)) != 0)
+ goto fail;
+ if ((err = SSLHashMD5.update(hashCtx, tempPubKey)) != 0)
+ goto fail;
+ if ((err = SSLHashMD5.final(hashCtx, hashOut)) != 0)
+ goto fail;
+
+ /*
+ * SHA hash goes right after the MD5 hash
+ */
+ hashOut.data = hash + 16;
+ hashOut.length = 20;
+ if ((err = SSLFreeBuffer(hashCtx, ctx)) != 0)
+ goto fail;
+
+ if ((err = ReadyHash(SSLHashSHA1, hashCtx, ctx)) != 0)
+ goto fail;
+ if ((err = SSLHashSHA1.update(hashCtx, clientRandom)) != 0)
+ goto fail;
+ if ((err = SSLHashSHA1.update(hashCtx, serverRandom)) != 0)
+ goto fail;
+ if ((err = SSLHashSHA1.update(hashCtx, tempPubKey)) != 0)
+ goto fail;
+ if ((err = SSLHashSHA1.final(hashCtx, hashOut)) != 0)
+ goto fail;
+
+ err = sslRsaRawVerify(ctx,
+ ctx->peerPubKey,
+ ctx->peerPubKeyCsp,
+ hash, /* plaintext */
+ 36, /* plaintext length */
+ signature,
+ signatureLen);
+ if(err) {
+ sslErrorLog("SSLProcessRSAServerKeyExchange: sslRsaRawVerify returned %d\n",
+ (int)err);
+ goto fail;
+ }
+
+ /* Signature matches; now replace server key with new key */
+ {
+ SSLBuffer modBuf;
+ SSLBuffer expBuf;
+
+ /* first free existing peerKey */
+ sslFreeKey(ctx->peerPubKeyCsp,
+ &ctx->peerPubKey,
+ NULL); /* no KCItem */
+
+ /* and cook up a new one from raw bits */
+ modBuf.data = modulus;
+ modBuf.length = modulusLen;
+ expBuf.data = exponent;
+ expBuf.length = exponentLen;
+ err = sslGetPubKeyFromBits(ctx,
+ &modBuf,
+ &expBuf,
+ &ctx->peerPubKey,
+ &ctx->peerPubKeyCsp);
+ }
+fail:
+ SSLFreeBuffer(signedHashes, ctx);
+ SSLFreeBuffer(hashCtx, ctx);
+ return err;
+}
+
+#if APPLE_DH
+static OSStatus
+SSLProcessDHanonServerKeyExchange(SSLBuffer message, SSLContext *ctx)
+{ OSStatus err;
+ UInt8 *charPtr;
+ unsigned int totalLength;
+
+ if (message.length < 6) {
+ sslErrorLog("SSLProcessDHanonServerKeyExchange error: msg len %d\n",
+ message.length);
+ return errSSLProtocol;
+ }
+ charPtr = message.data;
+ totalLength = 0;
+
+#if RSAREF
+ { SSLBuffer alloc;
+ UInt8 *prime, *generator, *publicVal;
+
+ ctx->peerDHParams.primeLen = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ prime = charPtr;
+ charPtr += ctx->peerDHParams.primeLen;
+ totalLength += ctx->peerDHParams.primeLen;
+ if (message.length < 6 + totalLength)
+ return errSSLProtocol;
+
+ ctx->peerDHParams.generatorLen = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ generator = charPtr;
+ charPtr += ctx->peerDHParams.generatorLen;
+ totalLength += ctx->peerDHParams.generatorLen;
+ if (message.length < 6 + totalLength)
+ return errSSLProtocol;
+
+ ctx->dhPeerPublic.length = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ publicVal = charPtr;
+ charPtr += ctx->dhPeerPublic.length;
+ totalLength += ctx->dhPeerPublic.length;
+ if (message.length != 6 + totalLength)
+ return errSSLProtocol;
+
+ assert(charPtr == message.data + message.length);
+
+ if ((err = SSLAllocBuffer(alloc, ctx->peerDHParams.primeLen +
+ ctx->peerDHParams.generatorLen, ctx)) != 0)
+ return err;
+
+ ctx->peerDHParams.prime = alloc.data;
+ memcpy(ctx->peerDHParams.prime, prime, ctx->peerDHParams.primeLen);
+ ctx->peerDHParams.generator = alloc.data + ctx->peerDHParams.primeLen;
+ memcpy(ctx->peerDHParams.generator, generator, ctx->peerDHParams.generatorLen);
+
+ if ((err = SSLAllocBuffer(ctx->dhPeerPublic,
+ ctx->dhPeerPublic.length, ctx)) != 0)
+ return err;
+
+ memcpy(ctx->dhPeerPublic.data, publicVal, ctx->dhPeerPublic.length);
+ }
+#elif BSAFE
+ { int rsaErr;
+ unsigned char *publicVal;
+ A_DH_KEY_AGREE_PARAMS params;
+ B_ALGORITHM_METHOD *chooser[] = { &AM_DH_KEY_AGREE, 0 };
+
+ params.prime.len = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ params.prime.data = charPtr;
+ charPtr += params.prime.len;
+ totalLength += params.prime.len;
+ if (message.length < 6 + totalLength)
+ return errSSLProtocol;
+
+ params.base.len = SSLDecodeInt(charPtr, 2);
+ charPtr += 2;
+ params.base.data = charPtr;
+ charPtr += params.base.len;
+ totalLength += params.base.len;
+ if (message.length < 6 + totalLength)
+ return errSSLProtocol;
+
+ ctx->dhPeerPublic.length = SSLDecodeInt(charPtr, 2);
+ if ((err = SSLAllocBuffer(ctx->dhPeerPublic, ctx->dhPeerPublic.length, ctx)) != 0)
+ return err;
+
+ charPtr += 2;
+ publicVal = charPtr;
+ charPtr += ctx->dhPeerPublic.length;
+ totalLength += ctx->dhPeerPublic.length;
+ memcpy(ctx->dhPeerPublic.data, publicVal, ctx->dhPeerPublic.length);
+ if (message.length != 6 + totalLength)
+ return errSSLProtocol;
+
+ params.exponentBits = 8 * ctx->dhPeerPublic.length - 1;
+
+ if ((rsaErr = B_CreateAlgorithmObject(&ctx->peerDHParams)) != 0)
+ return SSLUnknownErr;
+ if ((rsaErr = B_SetAlgorithmInfo(ctx->peerDHParams, AI_DHKeyAgree, (POINTER)¶ms)) != 0)
+ return SSLUnknownErr;
+ if ((rsaErr = B_KeyAgreeInit(ctx->peerDHParams, (B_KEY_OBJ) 0, chooser, NO_SURR)) != 0)
+ return SSLUnknownErr;
+ }
+#endif
+
+ return noErr;
+}
+
+#endif
+
+OSStatus
+SSLProcessKeyExchange(SSLBuffer keyExchange, SSLContext *ctx)
+{ OSStatus err;
+
+ switch (ctx->selectedCipherSpec->keyExchangeMethod)
+ { case SSL_RSA:
+ case SSL_RSA_EXPORT:
+ if ((err = SSLDecodeRSAKeyExchange(keyExchange, ctx)) != 0)
+ return err;
+ break;
+ #if APPLE_DH
+ case SSL_DH_anon:
+ if ((err = SSLDecodeDHanonKeyExchange(keyExchange, ctx)) != 0)
+ return err;
+ break;
+ #endif
+ default:
+ return unimpErr;
+ }
+
+ return noErr;
+}
+
+static OSStatus
+SSLDecodeRSAKeyExchange(SSLBuffer keyExchange, SSLContext *ctx)
+{ OSStatus err;
+ SSLBuffer result;
+ UInt32 outputLen, localKeyModulusLen;
+ CSSM_KEY_PTR *key;
+ SSLProtocolVersion version;
+ Boolean useEncryptKey = false;
+ UInt8 *src = NULL;
+
+
+ /* different key names, also need CSP handle */
+ CSSM_CSP_HANDLE cspHand;
+
+ assert(ctx->protocolSide == SSL_ServerSide);
+
+ #if SSL_SERVER_KEYEXCH_HACK
+ /*
+ * the way we work with Netscape.
+ * FIXME - maybe we should *require* an encryptPrivKey in this
+ * situation?
+ */
+ if((ctx->selectedCipherSpec->keyExchangeMethod == SSL_RSA_EXPORT) &&
+ (ctx->encryptPrivKey != NULL)) {
+ useEncryptKey = true;
+ }
+
+ #else /* !SSL_SERVER_KEYEXCH_HACK */
+ /* The "correct" way, I think, which doesn't work with Netscape */
+ if (ctx->encryptPrivKey) {
+ useEncryptKey = true;
+ }
+ #endif /* SSL_SERVER_KEYEXCH_HACK */
+ if (useEncryptKey) {
+ key = &ctx->encryptPrivKey;
+ cspHand = ctx->encryptKeyCsp;
+ }
+ else {
+ key = &ctx->signingPrivKey;
+ cspHand = ctx->signingKeyCsp;
+ }
+
+ localKeyModulusLen = sslKeyLengthInBytes(*key);
+
+ /*
+ * We have to tolerate incoming key exchange msgs with and without the
+ * two-byte "encrypted length" field.
+ */
+ if (keyExchange.length == localKeyModulusLen) {
+ /* no length encoded */
+ src = keyExchange.data;
+ }
+ else if((keyExchange.length == (localKeyModulusLen + 2)) &&
+ (ctx->negProtocolVersion >= TLS_Version_1_0)) {
+ /* TLS only - skip the length bytes */
+ src = keyExchange.data + 2;
+ }
+ else {
+ sslErrorLog("SSLDecodeRSAKeyExchange: length error (exp %u got %u)\n",
+ (unsigned)localKeyModulusLen, (unsigned)keyExchange.length);
+ return errSSLProtocol;
+ }
+ err = SSLAllocBuffer(result, localKeyModulusLen, ctx);
+ if(err != 0) {
+ return err;
+ }
+
+ err = sslRsaDecrypt(ctx,
+ *key,
+ cspHand,
+ src,
+ localKeyModulusLen,
+ result.data,
+ 48,
+ &outputLen);
+ if(err) {
+ goto fail;
+ }
+
+ if (outputLen != 48)
+ {
+ sslErrorLog("SSLDecodeRSAKeyExchange: outputLen error\n");
+ err = errSSLProtocol;
+ goto fail;
+ }
+ result.length = outputLen;
+
+ version = (SSLProtocolVersion)SSLDecodeInt(result.data, 2);
+ /* Modify this check to check against our maximum version with
+ * protocol revisions */
+ if (version > ctx->negProtocolVersion && version < SSL_Version_3_0) {
+ sslErrorLog("SSLDecodeRSAKeyExchange: version error\n");
+ err = errSSLProtocol;
+ goto fail;
+ }
+ if ((err = SSLAllocBuffer(ctx->preMasterSecret,
+ SSL_RSA_PREMASTER_SECRET_SIZE, ctx)) != 0)
+ goto fail;
+ memcpy(ctx->preMasterSecret.data, result.data,
+ SSL_RSA_PREMASTER_SECRET_SIZE);
+
+ err = noErr;
+fail:
+ SSLFreeBuffer(result, ctx);
+ return err;
+}
+
+#if APPLE_DH
+static OSStatus
+SSLDecodeDHanonKeyExchange(SSLBuffer keyExchange, SSLContext *ctx)
+{ OSStatus err;
+ unsigned int publicLen;
+ int rsaResult;
+
+ publicLen = SSLDecodeInt(keyExchange.data, 2);
+
+#if RSAREF
+ if (keyExchange.length != publicLen + 2 ||
+ publicLen != ctx->dhAnonParams.primeLen)
+ return errSSLProtocol;
+
+ if ((err = SSLAllocBuffer(ctx->preMasterSecret, ctx->dhAnonParams.primeLen, ctx)) != 0)
+ return err;
+
+ if ((rsaResult = R_ComputeDHAgreedKey (ctx->preMasterSecret.data, ctx->dhPeerPublic.data,
+ ctx->dhPrivate.data, ctx->dhPrivate.length, &ctx->dhAnonParams)) != 0)
+ { err = SSLUnknownErr;
+ return err;
+ }
+
+#elif BSAFE
+ { unsigned int amount;
+ if (keyExchange.length != publicLen + 2)
+ return errSSLProtocol;
+
+ if ((err = SSLAllocBuffer(ctx->preMasterSecret, 128, ctx)) != 0)
+ return err;
+
+ if ((rsaResult = B_KeyAgreePhase2(ctx->dhAnonParams, ctx->preMasterSecret.data,
+ &amount, 128, keyExchange.data+2, publicLen, NO_SURR)) != 0)
+ return err;
+
+ ctx->preMasterSecret.length = amount;
+ }
+#endif
+
+ return noErr;
+}
+#endif /* APPLE_DH */
+
+OSStatus
+SSLEncodeKeyExchange(SSLRecord &keyExchange, SSLContext *ctx)
+{ OSStatus err;
+
+ assert(ctx->protocolSide == SSL_ClientSide);
+
+ switch (ctx->selectedCipherSpec->keyExchangeMethod)
+ { case SSL_RSA:
+ case SSL_RSA_EXPORT:
+ if ((err = SSLEncodeRSAKeyExchange(keyExchange, ctx)) != 0)
+ return err;
+ break;
+ #if APPLE_DH
+ case SSL_DH_anon:
+ if ((err = SSLEncodeDHanonKeyExchange(keyExchange, ctx)) != 0)
+ return err;
+ break;
+ #endif
+ default:
+ return unimpErr;
+ }
+
+ return noErr;
+}
+
+static OSStatus
+SSLEncodeRSAKeyExchange(SSLRecord &keyExchange, SSLContext *ctx)
+{ OSStatus err;
+ UInt32 outputLen, peerKeyModulusLen;
+ UInt32 bufLen;
+ UInt8 *dst;
+ bool encodeLen = false;
+
+ if ((err = SSLEncodeRSAPremasterSecret(ctx)) != 0)
+ return err;
+
+ keyExchange.contentType = SSL_RecordTypeHandshake;
+ assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
+ (ctx->negProtocolVersion == TLS_Version_1_0));
+ keyExchange.protocolVersion = ctx->negProtocolVersion;
+
+ peerKeyModulusLen = sslKeyLengthInBytes(ctx->peerPubKey);
+ bufLen = peerKeyModulusLen + 4;
+ #if RSA_CLIENT_KEY_ADD_LENGTH
+ if(ctx->negProtocolVersion >= TLS_Version_1_0) {
+ bufLen += 2;
+ encodeLen = true;
+ }
+ #endif
+ if ((err = SSLAllocBuffer(keyExchange.contents,
+ bufLen,ctx)) != 0)
+ {
+ return err;
+ }
+ dst = keyExchange.contents.data + 4;
+ if(encodeLen) {
+ dst += 2;
+ }
+ keyExchange.contents.data[0] = SSL_HdskClientKeyExchange;
+
+ /* this is the record payload length */
+ SSLEncodeInt(keyExchange.contents.data + 1, bufLen - 4, 3);
+ if(encodeLen) {
+ /* the length of the encrypted pre_master_secret */
+ SSLEncodeInt(keyExchange.contents.data + 4,
+ peerKeyModulusLen, 2);
+ }
+ err = sslRsaEncrypt(ctx,
+ ctx->peerPubKey,
+ /* FIXME - maybe this should be ctx->cspHand */
+ ctx->peerPubKeyCsp,
+ ctx->preMasterSecret.data,
+ SSL_RSA_PREMASTER_SECRET_SIZE,
+ dst,
+ peerKeyModulusLen,
+ &outputLen);
+ if(err) {
+ return err;
+ }
+
+ assert(outputLen == encodeLen ?
+ keyExchange.contents.length - 6 :
+ keyExchange.contents.length - 4 );
+
+ return noErr;
+}
+
+#if APPLE_DH
+static OSStatus
+SSLEncodeDHanonKeyExchange(SSLRecord &keyExchange, SSLContext *ctx)
+{ OSStatus err;
+ unsigned int outputLen;
+
+ if ((err = SSLEncodeDHPremasterSecret(ctx)) != 0)
+ return err;
+
+ outputLen = ctx->dhExchangePublic.length + 2;
+
+ keyExchange.contentType = SSL_RecordTypeHandshake;
+ assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
+ (ctx->negProtocolVersion == TLS_Version_1_0));
+ keyExchange.protocolVersion = ctx->negProtocolVersion;
+
+ if ((err = SSLAllocBuffer(keyExchange.contents,outputLen + 4,ctx)) != 0)
+ return err;
+
+ keyExchange.contents.data[0] = SSL_HdskClientKeyExchange;
+ SSLEncodeInt(keyExchange.contents.data+1, ctx->dhExchangePublic.length+2, 3);
+
+ SSLEncodeInt(keyExchange.contents.data+4, ctx->dhExchangePublic.length, 2);
+ memcpy(keyExchange.contents.data+6, ctx->dhExchangePublic.data, ctx->dhExchangePublic.length);
+
+ return noErr;
+}
+#endif
+
+OSStatus
+SSLEncodeRSAPremasterSecret(SSLContext *ctx)
+{ SSLBuffer randData;
+ OSStatus err;
+
+ if ((err = SSLAllocBuffer(ctx->preMasterSecret,
+ SSL_RSA_PREMASTER_SECRET_SIZE, ctx)) != 0)
+ return err;
+
+ assert((ctx->negProtocolVersion == SSL_Version_3_0) ||
+ (ctx->negProtocolVersion == TLS_Version_1_0));
+ SSLEncodeInt(ctx->preMasterSecret.data, ctx->maxProtocolVersion, 2);
+ randData.data = ctx->preMasterSecret.data+2;
+ randData.length = SSL_RSA_PREMASTER_SECRET_SIZE - 2;
+ if ((err = sslRand(ctx, &randData)) != 0)
+ return err;
+ return noErr;
+}
+
+#if APPLE_DH
+
+OSStatus
+SSLEncodeDHPremasterSecret(SSLContext *ctx)
+{
+ #if !APPLE_DH
+ return unimpErr;
+ #else
+
+ OSStatus err;
+ int rsaResult;
+ SSLRandomCtx rsaRandom;
+
+/* Given the server's Diffie-Hellman parameters, prepare a public & private value,
+ * then use the public value provided by the server and our private value to
+ * generate a shared key (the premaster secret). Save our public value in
+ * ctx->dhExchangePublic to send to the server so it can calculate the matching
+ * key on its end
+ */
+ if ((err = ReadyRandom(&rsaRandom, ctx)) != 0)
+ return err;
+
+#if RSAREF
+ { privateValue.data = 0;
+
+ if ((err = SSLAllocBuffer(ctx->dhExchangePublic, ctx->peerDHParams.primeLen, ctx)) != 0)
+ goto fail;
+ if ((err = SSLAllocBuffer(privateValue, ctx->dhExchangePublic.length - 16, ctx)) != 0)
+ goto fail;
+
+ if ((rsaResult = R_SetupDHAgreement(ctx->dhExchangePublic.data, privateValue.data,
+ privateValue.length, &ctx->peerDHParams, &rsaRandom)) != 0)
+ { err = SSLUnknownErr;
+ goto fail;
+ }
+
+ if ((err = SSLAllocBuffer(ctx->preMasterSecret, ctx->peerDHParams.primeLen, ctx)) != 0)
+ goto fail;
+
+ if ((rsaResult = R_ComputeDHAgreedKey (ctx->preMasterSecret.data, ctx->dhPeerPublic.data,
+ privateValue.data, privateValue.length, &ctx->peerDHParams)) != 0)
+ { err = SSLUnknownErr;
+ goto fail;
+ }
+ }
+#elif BSAFE
+ { unsigned int outputLen;
+
+ if ((err = SSLAllocBuffer(ctx->dhExchangePublic, 128, ctx)) != 0)
+ goto fail;
+ if ((rsaResult = B_KeyAgreePhase1(ctx->peerDHParams, ctx->dhExchangePublic.data,
+ &outputLen, 128, rsaRandom, NO_SURR)) != 0)
+ { err = SSLUnknownErr;
+ goto fail;
+ }
+ ctx->dhExchangePublic.length = outputLen;
+ if ((err = SSLAllocBuffer(ctx->preMasterSecret, 128, ctx)) != 0)
+ goto fail;
+ if ((rsaResult = B_KeyAgreePhase2(ctx->peerDHParams, ctx->preMasterSecret.data,
+ &outputLen, 128, ctx->dhPeerPublic.data, ctx->dhPeerPublic.length,
+ NO_SURR)) != 0)
+ { err = SSLUnknownErr;
+ goto fail;
+ }
+ ctx->preMasterSecret.length = outputLen;
+ }
+ #endif
+
+ err = noErr;
+fail:
+#if RSAREF
+ SSLFreeBuffer(privateValue, ctx);
+ R_RandomFinal(&rsaRandom);
+#elif BSAFE
+ B_DestroyAlgorithmObject(&rsaRandom);
+#endif
+ return err;
+ #endif
+}
+
+#endif /* APPLE_DH */
+
+OSStatus
+SSLInitPendingCiphers(SSLContext *ctx)
+{ OSStatus err;
+ SSLBuffer key;
+ UInt8 *keyDataProgress, *keyPtr, *ivPtr;
+ int keyDataLen;
+ CipherContext *serverPending, *clientPending;
+
+ key.data = 0;
+
+ ctx->readPending.macRef = ctx->selectedCipherSpec->macAlgorithm;
+ ctx->writePending.macRef = ctx->selectedCipherSpec->macAlgorithm;
+ ctx->readPending.symCipher = ctx->selectedCipherSpec->cipher;
+ ctx->writePending.symCipher = ctx->selectedCipherSpec->cipher;
+ ctx->readPending.sequenceNum.high = ctx->readPending.sequenceNum.low = 0;
+ ctx->writePending.sequenceNum.high = ctx->writePending.sequenceNum.low = 0;
+
+ keyDataLen = ctx->selectedCipherSpec->macAlgorithm->hash->digestSize +
+ ctx->selectedCipherSpec->cipher->secretKeySize;
+ if (ctx->selectedCipherSpec->isExportable == NotExportable)
+ keyDataLen += ctx->selectedCipherSpec->cipher->ivSize;
+ keyDataLen *= 2; /* two of everything */
+
+ if ((err = SSLAllocBuffer(key, keyDataLen, ctx)) != 0)
+ return err;
+ assert(ctx->sslTslCalls != NULL);
+ if ((err = ctx->sslTslCalls->generateKeyMaterial(key, ctx)) != 0)
+ goto fail;
+
+ if (ctx->protocolSide == SSL_ServerSide)
+ { serverPending = &ctx->writePending;
+ clientPending = &ctx->readPending;
+ }
+ else
+ { serverPending = &ctx->readPending;
+ clientPending = &ctx->writePending;
+ }
+
+ keyDataProgress = key.data;
+ memcpy(clientPending->macSecret, keyDataProgress,
+ ctx->selectedCipherSpec->macAlgorithm->hash->digestSize);
+ keyDataProgress += ctx->selectedCipherSpec->macAlgorithm->hash->digestSize;
+ memcpy(serverPending->macSecret, keyDataProgress,
+ ctx->selectedCipherSpec->macAlgorithm->hash->digestSize);
+ keyDataProgress += ctx->selectedCipherSpec->macAlgorithm->hash->digestSize;
+
+ /* init the reusable-per-record MAC contexts */
+ err = ctx->sslTslCalls->initMac(clientPending, ctx);
+ if(err) {
+ goto fail;
+ }
+ err = ctx->sslTslCalls->initMac(serverPending, ctx);
+ if(err) {
+ goto fail;
+ }
+
+ if (ctx->selectedCipherSpec->isExportable == NotExportable)
+ { keyPtr = keyDataProgress;
+ keyDataProgress += ctx->selectedCipherSpec->cipher->secretKeySize;
+ /* Skip server write key to get to IV */
+ ivPtr = keyDataProgress + ctx->selectedCipherSpec->cipher->secretKeySize;
+ if ((err = ctx->selectedCipherSpec->cipher->initialize(keyPtr, ivPtr,
+ clientPending, ctx)) != 0)
+ goto fail;
+ keyPtr = keyDataProgress;
+ keyDataProgress += ctx->selectedCipherSpec->cipher->secretKeySize;
+ /* Skip client write IV to get to server write IV */
+ ivPtr = keyDataProgress + ctx->selectedCipherSpec->cipher->ivSize;
+ if ((err = ctx->selectedCipherSpec->cipher->initialize(keyPtr, ivPtr,
+ serverPending, ctx)) != 0)
+ goto fail;
+ }
+ else {
+ UInt8 clientExportKey[16], serverExportKey[16],
+ clientExportIV[16], serverExportIV[16];
+ SSLBuffer clientWrite, serverWrite;
+ SSLBuffer finalClientWrite, finalServerWrite;
+ SSLBuffer finalClientIV, finalServerIV;
+
+ assert(ctx->selectedCipherSpec->cipher->keySize <= 16);
+ assert(ctx->selectedCipherSpec->cipher->ivSize <= 16);
+
+ /* Inputs to generateExportKeyAndIv are clientRandom, serverRandom,
+ * clientWriteKey, serverWriteKey. The first two are already present
+ * in ctx.
+ * Outputs are a key and IV for each of {server, client}.
+ */
+ clientWrite.data = keyDataProgress;
+ clientWrite.length = ctx->selectedCipherSpec->cipher->secretKeySize;
+ serverWrite.data = keyDataProgress + clientWrite.length;
+ serverWrite.length = ctx->selectedCipherSpec->cipher->secretKeySize;
+ finalClientWrite.data = clientExportKey;
+ finalServerWrite.data = serverExportKey;
+ finalClientIV.data = clientExportIV;
+ finalServerIV.data = serverExportIV;
+ finalClientWrite.length = 16;
+ finalServerWrite.length = 16;
+ /* these can be zero */
+ finalClientIV.length = ctx->selectedCipherSpec->cipher->ivSize;
+ finalServerIV.length = ctx->selectedCipherSpec->cipher->ivSize;
+
+ assert(ctx->sslTslCalls != NULL);
+ err = ctx->sslTslCalls->generateExportKeyAndIv(ctx, clientWrite, serverWrite,
+ finalClientWrite, finalServerWrite, finalClientIV, finalServerIV);
+ if(err) {
+ goto fail;
+ }
+ if ((err = ctx->selectedCipherSpec->cipher->initialize(clientExportKey,
+ clientExportIV, clientPending, ctx)) != 0)
+ goto fail;
+ if ((err = ctx->selectedCipherSpec->cipher->initialize(serverExportKey,
+ serverExportIV, serverPending, ctx)) != 0)
+ goto fail;
+ }
+
+ /* Ciphers are ready for use */
+ ctx->writePending.ready = 1;
+ ctx->readPending.ready = 1;
+
+ /* Ciphers get swapped by sending or receiving a change cipher spec message */
+
+ err = noErr;
+fail:
+ SSLFreeBuffer(key, ctx);
+ return err;
+}
+