X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/67c7378dcb8de24c86b7fedff90b4b496f2e474c..5a719ac813caa6f2ceaa192274fad2e1c2cec162:/SecureTransport/sslKeyExchange.cpp diff --git a/SecureTransport/sslKeyExchange.cpp b/SecureTransport/sslKeyExchange.cpp new file mode 100644 index 00000000..6ea3fab5 --- /dev/null +++ b/SecureTransport/sslKeyExchange.cpp @@ -0,0 +1,1085 @@ +/* + * 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 +#include + +/* + * 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; +} +