/* * 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 "ModuleAttacher.h" #include "sslBER.h" #include #include #include #include #pragma mark - #pragma mark *** forward static declarations *** static OSStatus SSLGenServerDHParamsAndKey(SSLContext *ctx); static OSStatus SSLEncodeDHKeyParams(SSLContext *ctx, UInt8 *charPtr); static OSStatus SSLDecodeDHKeyParams(SSLContext *ctx, UInt8 *&charPtr, UInt32 length); #define DH_PARAM_DUMP 0 #if DH_PARAM_DUMP static void dumpBuf(const char *name, SSLBuffer &buf) { printf("%s:\n", name); UInt8 *cp = buf.data; UInt8 *endCp = cp + buf.length; do { for(unsigned i=0; i<16; i++) { printf("%02x ", *cp++); if(cp == endCp) { break; } } if(cp == endCp) { break; } printf("\n"); } while(cp < endCp); printf("\n"); } #else #define dumpBuf(n, b) #endif /* DH_PARAM_DUMP */ #if APPLE_DH #pragma mark - #pragma mark *** local D-H parameter generator *** /* * Process-wide server-supplied Diffie-Hellman parameters. * This might be overridden by some API_supplied parameters * in the future. */ class ServerDhParams { public: ServerDhParams(); ~ServerDhParams(); const SSLBuffer &prime() { return mPrime; } const SSLBuffer &generator() { return mGenerator; } const SSLBuffer ¶mBlock() { return mParamBlock; } private: /* these two for sending over the wire */ SSLBuffer mPrime; SSLBuffer mGenerator; /* this one for sending to the CSP at key gen time */ SSLBuffer mParamBlock; }; ServerDhParams::ServerDhParams() { mPrime.data = NULL; mPrime.length = 0; mGenerator.data = NULL; mGenerator.length = 0; mParamBlock.data = NULL; mParamBlock.length = 0; CSSM_CSP_HANDLE cspHand; CSSM_CL_HANDLE clHand; // not used here, just for // attachToModules() CSSM_TP_HANDLE tpHand; // ditto CSSM_RETURN crtn; crtn = attachToModules(&cspHand, &clHand, &tpHand); if(crtn) { MacOSError::throwMe(errSSLModuleAttach); } CSSM_CC_HANDLE ccHandle; CSSM_DATA cParams = {0, NULL}; crtn = CSSM_CSP_CreateKeyGenContext(cspHand, CSSM_ALGID_DH, SSL_DH_DEFAULT_PRIME_SIZE, NULL, // Seed NULL, // Salt NULL, // StartDate NULL, // EndDate &cParams, // Params, may be NULL &ccHandle); if(crtn) { stPrintCdsaError("ServerDhParams CSSM_CSP_CreateKeyGenContext", crtn); MacOSError::throwMe(errSSLCrypto); } /* explicitly generate params and save them */ sslDhDebug("^^^generating Diffie-Hellman parameters..."); crtn = CSSM_GenerateAlgorithmParams(ccHandle, SSL_DH_DEFAULT_PRIME_SIZE, &cParams); if(crtn) { stPrintCdsaError("ServerDhParams CSSM_GenerateAlgorithmParams", crtn); CSSM_DeleteContext(ccHandle); MacOSError::throwMe(errSSLCrypto); } CSSM_TO_SSLBUF(&cParams, &mParamBlock); OSStatus ortn = sslDecodeDhParams(&mParamBlock, &mPrime, &mGenerator); if(ortn) { sslErrorLog("ServerDhParams: param decode error\n"); MacOSError::throwMe(ortn); } CSSM_DeleteContext(ccHandle); } ServerDhParams::~ServerDhParams() { sslFree(mPrime.data); sslFree(mGenerator.data); sslFree(mParamBlock.data); } /* the single global thing */ static ModuleNexus serverDhParams; #endif /* APPLE_DH */ #pragma mark - #pragma mark *** RSA key exchange *** /* * 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 SSLEncodeRSAKeyParams(SSLBuffer *keyParams, SSLRSAPrivateKey *key, SSLContext *ctx) { OSStatus err; SSLBuffer modulus, exponent; UInt8 *charPtr; if(err = attachToCsp(ctx)) { return err; } /* Note currently ALL public keys are raw, obtained from the CL... */ assert((*key)->KeyHeader.BlobType == CSSM_KEYBLOB_RAW); err = sslGetPubKeyBits(ctx, *key, ctx->cspHand, &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; } static OSStatus SSLEncodeRSAPremasterSecret(SSLContext *ctx) { SSLBuffer randData; OSStatus err; SSLProtocolVersion maxVersion; 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)); sslGetMaxProtVersion(ctx, &maxVersion); SSLEncodeInt(ctx->preMasterSecret.data, maxVersion, 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; } /* * Generate a server key exchange message signed by our RSA or DSA private key. */ static OSStatus SSLEncodeSignedServerKeyExchange(SSLRecord &keyExch, SSLContext *ctx) { OSStatus err; UInt8 *charPtr; int outputLen; UInt8 hashes[SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN]; SSLBuffer exchangeParams,clientRandom,serverRandom,hashCtx, hash; UInt8 *dataToSign; UInt32 dataToSignLen; bool isRsa = true; UInt32 maxSigLen; UInt32 actSigLen; SSLBuffer signature; const CSSM_KEY *cssmKey; assert(ctx->protocolSide == SSL_ServerSide); assert(ctx->signingPubKey != NULL); assert((ctx->negProtocolVersion == SSL_Version_3_0) || (ctx->negProtocolVersion == TLS_Version_1_0)); exchangeParams.data = 0; hashCtx.data = 0; signature.data = 0; /* Set up parameter block to hash ==> exchangeParams */ switch(ctx->selectedCipherSpec->keyExchangeMethod) { case SSL_RSA: case SSL_RSA_EXPORT: /* * Parameter block = encryption public key. * If app hasn't supplied a separate encryption cert, abort. */ if(ctx->encryptPubKey == NULL) { sslErrorLog("RSAServerKeyExchange: no encrypt cert\n"); return errSSLBadConfiguration; } err = SSLEncodeRSAKeyParams(&exchangeParams, &ctx->encryptPubKey, ctx); break; #if APPLE_DH case SSL_DHE_DSS: case SSL_DHE_DSS_EXPORT: isRsa = false; /* and fall through */ case SSL_DHE_RSA: case SSL_DHE_RSA_EXPORT: { /* * Parameter block = {prime, generator, public key} * Obtain D-H parameters (if we don't have them) and a key pair. */ err = SSLGenServerDHParamsAndKey(ctx); if(err) { return err; } UInt32 len = ctx->dhParamsPrime.length + ctx->dhParamsGenerator.length + ctx->dhExchangePublic.length + 6 /* 3 length fields */; err = SSLAllocBuffer(exchangeParams, len, ctx); if(err) { goto fail; } err = SSLEncodeDHKeyParams(ctx, exchangeParams.data); break; } #endif /* APPLE_DH */ default: /* shouldn't be here */ assert(0); return errSSLInternal; } if(err) { goto fail; } /* cook up hash(es) for raw sign */ clientRandom.data = ctx->clientRandom; clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE; serverRandom.data = ctx->serverRandom; serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE; if(isRsa) { /* skip this if signing with DSA */ dataToSign = hashes; dataToSignLen = SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN; hash.data = &hashes[0]; hash.length = SSL_MD5_DIGEST_LEN; 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, exchangeParams)) != 0) goto fail; if ((err = SSLHashMD5.final(hashCtx, hash)) != 0) goto fail; if ((err = SSLFreeBuffer(hashCtx, ctx)) != 0) goto fail; } else { /* DSA - just use the SHA1 hash */ dataToSign = &hashes[SSL_MD5_DIGEST_LEN]; dataToSignLen = SSL_SHA1_DIGEST_LEN; } hash.data = &hashes[SSL_MD5_DIGEST_LEN]; hash.length = SSL_SHA1_DIGEST_LEN; 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, exchangeParams)) != 0) goto fail; if ((err = SSLHashSHA1.final(hashCtx, hash)) != 0) goto fail; if ((err = SSLFreeBuffer(hashCtx, ctx)) != 0) goto fail; /* preallocate a buffer for signing */ err = SecKeyGetCSSMKey(ctx->signingPrivKeyRef, &cssmKey); if(err) { sslErrorLog("SSLEncodeSignedServerKeyExchange: SecKeyGetCSSMKey err %d\n", (int)err); goto fail; } err = sslGetMaxSigSize(cssmKey, maxSigLen); if(err) { goto fail; } err = SSLAllocBuffer(signature, maxSigLen, ctx); if(err) { goto fail; } err = sslRawSign(ctx, ctx->signingPrivKeyRef, dataToSign, // one or two hashes dataToSignLen, signature.data, maxSigLen, &actSigLen); if(err) { goto fail; } assert(actSigLen <= maxSigLen); /* package it all up */ outputLen = exchangeParams.length + 2 + actSigLen; keyExch.protocolVersion = ctx->negProtocolVersion; keyExch.contentType = SSL_RecordTypeHandshake; if ((err = SSLAllocBuffer(keyExch.contents, outputLen+4, ctx)) != 0) goto fail; charPtr = keyExch.contents.data; *charPtr++ = SSL_HdskServerKeyExchange; charPtr = SSLEncodeInt(charPtr, outputLen, 3); memcpy(charPtr, exchangeParams.data, exchangeParams.length); charPtr += exchangeParams.length; charPtr = SSLEncodeInt(charPtr, actSigLen, 2); memcpy(charPtr, signature.data, actSigLen); assert((charPtr + actSigLen) == (keyExch.contents.data + keyExch.contents.length)); err = noErr; fail: SSLFreeBuffer(hashCtx, ctx); SSLFreeBuffer(exchangeParams, ctx); SSLFreeBuffer(signature, ctx); return err; } /* * Decode and verify a server key exchange message signed by server's * public key. */ static OSStatus SSLDecodeSignedServerKeyExchange(SSLBuffer message, SSLContext *ctx) { OSStatus err; SSLBuffer hashOut, hashCtx, clientRandom, serverRandom; UInt16 modulusLen, exponentLen, signatureLen; UInt8 *modulus, *exponent, *signature; UInt8 hashes[SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN]; SSLBuffer signedHashes; UInt8 *dataToSign; UInt32 dataToSignLen; bool isRsa = true; assert(ctx->protocolSide == SSL_ClientSide); signedHashes.data = 0; hashCtx.data = 0; if (message.length < 2) { sslErrorLog("SSLDecodeSignedServerKeyExchange: msg len error 1\n"); return errSSLProtocol; } /* first extract the key-exchange-method-specific parameters */ UInt8 *charPtr = message.data; UInt8 *endCp = charPtr + message.length; switch(ctx->selectedCipherSpec->keyExchangeMethod) { case SSL_RSA: case SSL_RSA_EXPORT: modulusLen = SSLDecodeInt(charPtr, 2); charPtr += 2; if((charPtr + modulusLen) > endCp) { sslErrorLog("signedServerKeyExchange: msg len error 2\n"); return errSSLProtocol; } modulus = charPtr; charPtr += modulusLen; exponentLen = SSLDecodeInt(charPtr, 2); charPtr += 2; if((charPtr + exponentLen) > endCp) { sslErrorLog("signedServerKeyExchange: msg len error 3\n"); return errSSLProtocol; } exponent = charPtr; charPtr += exponentLen; break; #if APPLE_DH case SSL_DHE_DSS: case SSL_DHE_DSS_EXPORT: isRsa = false; /* and fall through */ case SSL_DHE_RSA: case SSL_DHE_RSA_EXPORT: err = SSLDecodeDHKeyParams(ctx, charPtr, message.length); if(err) { return err; } break; #endif /* APPLE_DH */ default: assert(0); return errSSLInternal; } /* this is what's hashed */ SSLBuffer signedParams; signedParams.data = message.data; signedParams.length = charPtr - message.data; signatureLen = SSLDecodeInt(charPtr, 2); charPtr += 2; if((charPtr + signatureLen) != endCp) { sslErrorLog("signedServerKeyExchange: msg len error 4\n"); return errSSLProtocol; } signature = charPtr; clientRandom.data = ctx->clientRandom; clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE; serverRandom.data = ctx->serverRandom; serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE; if(isRsa) { /* skip this if signing with DSA */ dataToSign = hashes; dataToSignLen = SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN; hashOut.data = hashes; hashOut.length = SSL_MD5_DIGEST_LEN; 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, signedParams)) != 0) goto fail; if ((err = SSLHashMD5.final(hashCtx, hashOut)) != 0) goto fail; } else { /* DSA - just use the SHA1 hash */ dataToSign = &hashes[SSL_MD5_DIGEST_LEN]; dataToSignLen = SSL_SHA1_DIGEST_LEN; } hashOut.data = hashes + SSL_MD5_DIGEST_LEN; hashOut.length = SSL_SHA1_DIGEST_LEN; 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, signedParams)) != 0) goto fail; if ((err = SSLHashSHA1.final(hashCtx, hashOut)) != 0) goto fail; err = sslRawVerify(ctx, ctx->peerPubKey, ctx->peerPubKeyCsp, dataToSign, /* plaintext */ dataToSignLen, /* plaintext length */ signature, signatureLen); if(err) { sslErrorLog("SSLDecodeSignedServerKeyExchange: sslRawVerify " "returned %d\n", (int)err); goto fail; } /* Signature matches; now replace server key with new key */ switch(ctx->selectedCipherSpec->keyExchangeMethod) { case SSL_RSA: case SSL_RSA_EXPORT: { 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); break; } case SSL_DHE_RSA: case SSL_DHE_RSA_EXPORT: case SSL_DHE_DSS: case SSL_DHE_DSS_EXPORT: break; /* handled above */ default: assert(0); /* handled above */ } fail: SSLFreeBuffer(signedHashes, ctx); SSLFreeBuffer(hashCtx, ctx); return err; } static OSStatus SSLDecodeRSAKeyExchange(SSLBuffer keyExchange, SSLContext *ctx) { OSStatus err; UInt32 outputLen, localKeyModulusLen; SSLProtocolVersion version; Boolean useEncryptKey = false; UInt8 *src = NULL; SecKeyRef keyRef = NULL; const CSSM_KEY *cssmKey; 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->encryptPrivKeyRef) { useEncryptKey = true; } #endif /* SSL_SERVER_KEYEXCH_HACK */ if (useEncryptKey) { keyRef = ctx->encryptPrivKeyRef; /* FIXME: when 3420180 is implemented, pick appropriate creds here */ } else { keyRef = ctx->signingPrivKeyRef; /* FIXME: when 3420180 is implemented, pick appropriate creds here */ } err = SecKeyGetCSSMKey(keyRef, &cssmKey); if(err) { sslErrorLog("SSLDecodeRSAKeyExchange: SecKeyGetCSSMKey err %d\n", (int)err); return err; } localKeyModulusLen = sslKeyLengthInBytes(cssmKey); /* * 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(ctx->preMasterSecret, SSL_RSA_PREMASTER_SECRET_SIZE, ctx); if(err != 0) { return err; } /* * From this point on, to defend against the Bleichenbacher attack * and its Klima-Pokorny-Rosa variant, any errors we detect are *not* * reported to the caller or the peer. If we detect any error during * decryption (e.g., bad PKCS1 padding) or in the testing of the version * number in the premaster secret, we proceed by generating a random * premaster secret, with the correct version number, and tell our caller * that everything is fine. This session will fail as soon as the * finished messages are sent, since we will be using a bogus premaster * secret (and hence bogus session and MAC keys). Meanwhile we have * not provided any side channel information relating to the cause of * the failure. * * See http://eprint.iacr.org/2003/052/ for more info. */ err = sslRsaDecrypt(ctx, keyRef, src, localKeyModulusLen, // ciphertext len ctx->preMasterSecret.data, SSL_RSA_PREMASTER_SECRET_SIZE, // plaintext buf available &outputLen); if(err != noErr) { /* possible Bleichenbacher attack */ sslLogNegotiateDebug("SSLDecodeRSAKeyExchange: RSA decrypt fail"); } else if(outputLen != SSL_RSA_PREMASTER_SECRET_SIZE) { sslLogNegotiateDebug("SSLDecodeRSAKeyExchange: premaster secret size error"); err = errSSLProtocol; // not passed back to caller } if(err == noErr) { /* * Two legal values here - the one we actually negotiated (which is * technically incorrect but not uncommon), and the one the client * sent as its preferred version in the client hello msg. */ version = (SSLProtocolVersion)SSLDecodeInt(ctx->preMasterSecret.data, 2); if((version != ctx->negProtocolVersion) && (version != ctx->clientReqProtocol)) { /* possible Klima-Pokorny-Rosa attack */ sslLogNegotiateDebug("SSLDecodeRSAKeyExchange: version error"); err = errSSLProtocol; } } if(err != noErr) { /* * Obfuscate failures for defense against Bleichenbacher and * Klima-Pokorny-Rosa attacks. */ SSLEncodeInt(ctx->preMasterSecret.data, ctx->negProtocolVersion, 2); SSLBuffer tmpBuf; tmpBuf.data = ctx->preMasterSecret.data + 2; tmpBuf.length = SSL_RSA_PREMASTER_SECRET_SIZE - 2; /* must ignore failures here */ sslRand(ctx, &tmpBuf); } /* in any case, save premaster secret (good or bogus) and proceed */ return noErr; } static OSStatus SSLEncodeRSAKeyExchange(SSLRecord &keyExchange, SSLContext *ctx) { OSStatus err; UInt32 outputLen, peerKeyModulusLen; UInt32 bufLen; UInt8 *dst; bool encodeLen = false; assert(ctx->protocolSide == SSL_ClientSide); 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 #pragma mark - #pragma mark *** Diffie-Hellman key exchange *** /* * Diffie-Hellman setup, server side. On successful return, the * following SSLContext members are valid: * * dhParamsPrime * dhParamsGenerator * dhPrivate * dhExchangePublic */ static OSStatus SSLGenServerDHParamsAndKey( SSLContext *ctx) { OSStatus ortn; assert(ctx->protocolSide == SSL_ServerSide); /* * Obtain D-H parameters if we don't have them. */ if(ctx->dhParamsPrime.data == NULL) { assert(ctx->dhParamsGenerator.data == NULL); const SSLBuffer &pr = serverDhParams().prime(); ortn = SSLCopyBuffer(pr, ctx->dhParamsPrime); if(ortn) { return ortn; } const SSLBuffer &gen = serverDhParams().generator(); ortn = SSLCopyBuffer(gen, ctx->dhParamsGenerator); if(ortn) { return ortn; } const SSLBuffer &block = serverDhParams().paramBlock(); ortn = SSLCopyBuffer(block, ctx->dhParamsEncoded); if(ortn) { return ortn; } } /* generate per-session D-H key pair */ sslFreeKey(ctx->cspHand, &ctx->dhPrivate, NULL); SSLFreeBuffer(ctx->dhExchangePublic, ctx); ctx->dhPrivate = (CSSM_KEY *)sslMalloc(sizeof(CSSM_KEY)); CSSM_KEY pubKey; ortn = sslDhGenerateKeyPair(ctx, ctx->dhParamsEncoded, ctx->dhParamsPrime.length * 8, &pubKey, ctx->dhPrivate); if(ortn) { return ortn; } CSSM_TO_SSLBUF(&pubKey.KeyData, &ctx->dhExchangePublic); return noErr; } /* * Encode DH params and public key in caller-supplied buffer. */ static OSStatus SSLEncodeDHKeyParams( SSLContext *ctx, UInt8 *charPtr) { assert(ctx->protocolSide == SSL_ServerSide); assert(ctx->dhParamsPrime.data != NULL); assert(ctx->dhParamsGenerator.data != NULL); assert(ctx->dhExchangePublic.data != NULL); charPtr = SSLEncodeInt(charPtr, ctx->dhParamsPrime.length, 2); memcpy(charPtr, ctx->dhParamsPrime.data, ctx->dhParamsPrime.length); charPtr += ctx->dhParamsPrime.length; charPtr = SSLEncodeInt(charPtr, ctx->dhParamsGenerator.length, 2); memcpy(charPtr, ctx->dhParamsGenerator.data, ctx->dhParamsGenerator.length); charPtr += ctx->dhParamsGenerator.length; charPtr = SSLEncodeInt(charPtr, ctx->dhExchangePublic.length, 2); memcpy(charPtr, ctx->dhExchangePublic.data, ctx->dhExchangePublic.length); dumpBuf("server prime", ctx->dhParamsPrime); dumpBuf("server generator", ctx->dhParamsGenerator); dumpBuf("server pub key", ctx->dhExchangePublic); return noErr; } /* * Decode DH params and server public key. */ static OSStatus SSLDecodeDHKeyParams( SSLContext *ctx, UInt8 *&charPtr, // IN/OUT UInt32 length) { OSStatus err = noErr; assert(ctx->protocolSide == SSL_ClientSide); UInt8 *endCp = charPtr + length; /* Allow reuse via renegotiation */ SSLFreeBuffer(ctx->dhParamsPrime, ctx); SSLFreeBuffer(ctx->dhParamsGenerator, ctx); SSLFreeBuffer(ctx->dhPeerPublic, ctx); /* Prime, with a two-byte length */ UInt32 len = SSLDecodeInt(charPtr, 2); charPtr += 2; if((charPtr + len) > endCp) { return errSSLProtocol; } err = SSLAllocBuffer(ctx->dhParamsPrime, len, ctx); if(err) { return err; } memmove(ctx->dhParamsPrime.data, charPtr, len); charPtr += len; /* Generator, with a two-byte length */ len = SSLDecodeInt(charPtr, 2); charPtr += 2; if((charPtr + len) > endCp) { return errSSLProtocol; } err = SSLAllocBuffer(ctx->dhParamsGenerator, len, ctx); if(err) { return err; } memmove(ctx->dhParamsGenerator.data, charPtr, len); charPtr += len; /* peer public key, with a two-byte length */ len = SSLDecodeInt(charPtr, 2); charPtr += 2; err = SSLAllocBuffer(ctx->dhPeerPublic, len, ctx); if(err) { return err; } memmove(ctx->dhPeerPublic.data, charPtr, len); charPtr += len; dumpBuf("client peer pub", ctx->dhPeerPublic); dumpBuf("client prime", ctx->dhParamsPrime); dumpBuf("client generator", ctx->dhParamsGenerator); return err; } /* * Given the server's Diffie-Hellman parameters, generate our * own DH key pair, and perform key exchange using the server's * public key and our private key. The result is the premaster * secret. * * SSLContext members valid on entry: * dhParamsPrime * dhParamsGenerator * dhPeerPublic * * SSLContext members valid on successful return: * dhPrivate * dhExchangePublic * preMasterSecret */ static OSStatus SSLGenClientDHKeyAndExchange(SSLContext *ctx) { OSStatus ortn; assert(ctx->protocolSide == SSL_ClientSide); if((ctx->dhParamsPrime.data == NULL) || (ctx->dhParamsGenerator.data == NULL) || (ctx->dhPeerPublic.data == NULL)) { sslErrorLog("SSLGenClientDHKeyAndExchange: incomplete server params\n"); return errSSLProtocol; } /* generate two keys */ CSSM_KEY pubKey; ctx->dhPrivate = (CSSM_KEY *)sslMalloc(sizeof(CSSM_KEY)); ortn = sslDhGenKeyPairClient(ctx, ctx->dhParamsPrime, ctx->dhParamsGenerator, &pubKey, ctx->dhPrivate); if(ortn) { sslFree(ctx->dhPrivate); ctx->dhPrivate = NULL; return ortn; } /* do the exchange, size of prime */ ortn = sslDhKeyExchange(ctx, ctx->dhParamsPrime.length * 8, &ctx->preMasterSecret); if(ortn) { return ortn; } CSSM_TO_SSLBUF(&pubKey.KeyData, &ctx->dhExchangePublic); return noErr; } static OSStatus SSLEncodeDHanonServerKeyExchange(SSLRecord &keyExch, SSLContext *ctx) { OSStatus ortn = noErr; assert((ctx->negProtocolVersion == SSL_Version_3_0) || (ctx->negProtocolVersion == TLS_Version_1_0)); assert(ctx->protocolSide == SSL_ServerSide); /* * Obtain D-H parameters (if we don't have them) and a key pair. */ ortn = SSLGenServerDHParamsAndKey(ctx); if(ortn) { return ortn; } UInt32 length = 6 + ctx->dhParamsPrime.length + ctx->dhParamsGenerator.length + ctx->dhExchangePublic.length; keyExch.protocolVersion = ctx->negProtocolVersion; keyExch.contentType = SSL_RecordTypeHandshake; if ((ortn = SSLAllocBuffer(keyExch.contents, length+4, ctx)) != 0) return ortn; UInt8 *charPtr = keyExch.contents.data; *charPtr++ = SSL_HdskServerKeyExchange; charPtr = SSLEncodeInt(charPtr, length, 3); /* encode prime, generator, our public key */ return SSLEncodeDHKeyParams(ctx, charPtr); } static OSStatus SSLDecodeDHanonServerKeyExchange(SSLBuffer message, SSLContext *ctx) { OSStatus err = noErr; assert(ctx->protocolSide == SSL_ClientSide); if (message.length < 6) { sslErrorLog("SSLDecodeDHanonServerKeyExchange error: msg len %u\n", (unsigned)message.length); return errSSLProtocol; } UInt8 *charPtr = message.data; err = SSLDecodeDHKeyParams(ctx, charPtr, message.length); if(err == noErr) { if((message.data + message.length) != charPtr) { err = errSSLProtocol; } } return err; } static OSStatus SSLDecodeDHClientKeyExchange(SSLBuffer keyExchange, SSLContext *ctx) { OSStatus ortn = noErr; unsigned int publicLen; assert(ctx->protocolSide == SSL_ServerSide); if(ctx->dhParamsPrime.data == NULL) { /* should never happen */ assert(0); return errSSLInternal; } /* this message simply contains the client's public DH key */ UInt8 *charPtr = keyExchange.data; publicLen = SSLDecodeInt(charPtr, 2); charPtr += 2; if((keyExchange.length != publicLen + 2) || (publicLen > ctx->dhParamsPrime.length)) { return errSSLProtocol; } SSLFreeBuffer(ctx->dhPeerPublic, ctx); // allow reuse via renegotiation ortn = SSLAllocBuffer(ctx->dhPeerPublic, publicLen, ctx); if(ortn) { return ortn; } memmove(ctx->dhPeerPublic.data, charPtr, publicLen); /* DH Key exchange, result --> premaster secret */ SSLFreeBuffer(ctx->preMasterSecret, ctx); ortn = sslDhKeyExchange(ctx, ctx->dhParamsPrime.length * 8, &ctx->preMasterSecret); dumpBuf("server peer pub", ctx->dhPeerPublic); dumpBuf("server premaster", ctx->preMasterSecret); return ortn; } static OSStatus SSLEncodeDHClientKeyExchange(SSLRecord &keyExchange, SSLContext *ctx) { OSStatus err; unsigned int outputLen; assert(ctx->protocolSide == SSL_ClientSide); if ((err = SSLGenClientDHKeyAndExchange(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); dumpBuf("client pub key", ctx->dhExchangePublic); dumpBuf("client premaster", ctx->preMasterSecret); return noErr; } #endif /* APPLE_DH */ #pragma mark - #pragma mark *** Public Functions *** OSStatus SSLEncodeServerKeyExchange(SSLRecord &keyExch, SSLContext *ctx) { OSStatus err; switch (ctx->selectedCipherSpec->keyExchangeMethod) { case SSL_RSA: case SSL_RSA_EXPORT: #if APPLE_DH case SSL_DHE_RSA: case SSL_DHE_RSA_EXPORT: case SSL_DHE_DSS: case SSL_DHE_DSS_EXPORT: #endif /* APPLE_DH */ if ((err = SSLEncodeSignedServerKeyExchange(keyExch, ctx)) != 0) return err; break; #if APPLE_DH case SSL_DH_anon: case SSL_DH_anon_EXPORT: if ((err = SSLEncodeDHanonServerKeyExchange(keyExch, ctx)) != 0) return err; break; #endif default: return unimpErr; } return noErr; } OSStatus SSLProcessServerKeyExchange(SSLBuffer message, SSLContext *ctx) { OSStatus err; switch (ctx->selectedCipherSpec->keyExchangeMethod) { case SSL_RSA: case SSL_RSA_EXPORT: #if APPLE_DH case SSL_DHE_RSA: case SSL_DHE_RSA_EXPORT: case SSL_DHE_DSS: case SSL_DHE_DSS_EXPORT: #endif err = SSLDecodeSignedServerKeyExchange(message, ctx); break; #if APPLE_DH case SSL_DH_anon: case SSL_DH_anon_EXPORT: err = SSLDecodeDHanonServerKeyExchange(message, ctx); break; #endif default: err = unimpErr; break; } return err; } OSStatus SSLEncodeKeyExchange(SSLRecord &keyExchange, SSLContext *ctx) { OSStatus err; assert(ctx->protocolSide == SSL_ClientSide); switch (ctx->selectedCipherSpec->keyExchangeMethod) { case SSL_RSA: case SSL_RSA_EXPORT: err = SSLEncodeRSAKeyExchange(keyExchange, ctx); break; #if APPLE_DH case SSL_DHE_RSA: case SSL_DHE_RSA_EXPORT: case SSL_DHE_DSS: case SSL_DHE_DSS_EXPORT: case SSL_DH_anon: case SSL_DH_anon_EXPORT: err = SSLEncodeDHClientKeyExchange(keyExchange, ctx); break; #endif default: err = unimpErr; } return err; } 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: case SSL_DHE_DSS: case SSL_DHE_DSS_EXPORT: case SSL_DHE_RSA: case SSL_DHE_RSA_EXPORT: case SSL_DH_anon_EXPORT: if ((err = SSLDecodeDHClientKeyExchange(keyExchange, ctx)) != 0) return err; break; #endif default: return unimpErr; } return noErr; } 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; }