X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_ssl/lib/sslKeyExchange.c?ds=inline diff --git a/libsecurity_ssl/lib/sslKeyExchange.c b/libsecurity_ssl/lib/sslKeyExchange.c new file mode 100644 index 00000000..a0e5549b --- /dev/null +++ b/libsecurity_ssl/lib/sslKeyExchange.c @@ -0,0 +1,2008 @@ +/* + * Copyright (c) 1999-2001,2005-2012 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The 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. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * sslKeyExchange.c - Support for key exchange and server key exchange + */ + +#include "ssl.h" +#include "sslContext.h" +#include "sslHandshake.h" +#include "sslMemory.h" +#include "sslDebug.h" +#include "sslUtils.h" +#include "sslCrypto.h" + +#include "sslDigests.h" + +#include +#include + +#include + +#include + +#ifdef USE_CDSA_CRYPTO +//#include +//#include +#include +#include +#include "ModuleAttacher.h" +#else +#include +#include +#include +#if APPLE_DH + + +static OSStatus SSLGenServerDHParamsAndKey(SSLContext *ctx); +static size_t SSLEncodedDHKeyParamsLen(SSLContext *ctx); +static OSStatus SSLEncodeDHKeyParams(SSLContext *ctx, uint8_t *charPtr); + +#endif /* APPLE_DH */ +#endif /* USE_CDSA_CRYPTO */ + +#include + +#pragma mark - +#pragma mark Forward Static Declarations + +#if APPLE_DH +#if USE_CDSA_CRYPTO +static OSStatus SSLGenServerDHParamsAndKey(SSLContext *ctx); +static OSStatus SSLEncodeDHKeyParams(SSLContext *ctx, uint8_t *charPtr); +#endif +static OSStatus SSLDecodeDHKeyParams(SSLContext *ctx, uint8_t **charPtr, + size_t length); +#endif +static OSStatus SSLDecodeECDHKeyParams(SSLContext *ctx, uint8_t **charPtr, + size_t length); + +#define DH_PARAM_DUMP 0 +#if DH_PARAM_DUMP + +static void dumpBuf(const char *name, SSLBuffer *buf) +{ + printf("%s:\n", name); + uint8_t *cp = buf->data; + uint8_t *endCp = cp + buf->length; + + do { + unsigned i; + for(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 Diffie-Hellman Parameter Generator + +/* + * Process-wide server-supplied Diffie-Hellman parameters. + * This might be overridden by some API_supplied parameters + * in the future. + */ +struct ServerDhParams +{ + /* these two for sending over the wire */ + SSLBuffer prime; + SSLBuffer generator; + /* this one for sending to the CSP at key gen time */ + SSLBuffer paramBlock; +}; + + +#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 + +static OSStatus +SSLEncodeRSAKeyParams(SSLBuffer *keyParams, SSLPubKey *key, SSLContext *ctx) +{ +#if 0 + SSLBuffer modulus, exponent; + uint8_t *charPtr; + +#ifdef USE_CDSA_CRYPTO + if(err = attachToCsp(ctx)) { + return err; + } + + /* Note currently ALL public keys are raw, obtained from the CL... */ + assert((*key)->KeyHeader.BlobType == CSSM_KEYBLOB_RAW); +#endif /* USE_CDSA_CRYPTO */ + + err = sslGetPubKeyBits(ctx, + key, + &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; +#else + CFDataRef modulus = SecKeyCopyModulus(SECKEYREF(key)); + if (!modulus) { + sslErrorLog("SSLEncodeRSAKeyParams: SecKeyCopyModulus failed\n"); + return errSSLCrypto; + } + CFDataRef exponent = SecKeyCopyExponent(SECKEYREF(key)); + if (!exponent) { + sslErrorLog("SSLEncodeRSAKeyParams: SecKeyCopyExponent failed\n"); + CFRelease(modulus); + return errSSLCrypto; + } + + CFIndex modulusLength = CFDataGetLength(modulus); + CFIndex exponentLength = CFDataGetLength(exponent); + sslDebugLog("SSLEncodeRSAKeyParams: modulus len=%ld, exponent len=%ld\n", + modulusLength, exponentLength); + OSStatus err; + if ((err = SSLAllocBuffer(keyParams, + modulusLength + exponentLength + 4, ctx)) != 0) { + return err; + } + uint8_t *charPtr = keyParams->data; + charPtr = SSLEncodeSize(charPtr, modulusLength, 2); + memcpy(charPtr, CFDataGetBytePtr(modulus), modulusLength); + charPtr += modulusLength; + charPtr = SSLEncodeSize(charPtr, exponentLength, 2); + memcpy(charPtr, CFDataGetBytePtr(exponent), exponentLength); + CFRelease(modulus); + CFRelease(exponent); + return noErr; +#endif +} + +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); + 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 +SSLSignServerKeyExchangeTls12(SSLContext *ctx, SSLSignatureAndHashAlgorithm sigAlg, SSLBuffer exchangeParams, SSLBuffer signature, size_t *actSigLen) +{ + OSStatus err; + SSLBuffer hashOut, hashCtx, clientRandom, serverRandom; + uint8_t hashes[SSL_MAX_DIGEST_LEN]; + SSLBuffer signedHashes; + uint8_t *dataToSign; + size_t dataToSignLen; + const HashReference *hashRef; + SecAsn1AlgId algId; + + signedHashes.data = 0; + hashCtx.data = 0; + + clientRandom.data = ctx->clientRandom; + clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE; + serverRandom.data = ctx->serverRandom; + serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE; + + switch (sigAlg.hash) { + case SSL_HashAlgorithmSHA1: + hashRef = &SSLHashSHA1; + algId.algorithm = CSSMOID_SHA1WithRSA; + break; + case SSL_HashAlgorithmSHA256: + hashRef = &SSLHashSHA256; + algId.algorithm = CSSMOID_SHA256WithRSA; + break; + case SSL_HashAlgorithmSHA384: + hashRef = &SSLHashSHA384; + algId.algorithm = CSSMOID_SHA384WithRSA; + break; + default: + sslErrorLog("SSLVerifySignedServerKeyExchangeTls12: unsupported hash %d\n", sigAlg.hash); + return errSSLProtocol; + } + + + dataToSign = hashes; + dataToSignLen = hashRef->digestSize; + hashOut.data = hashes; + hashOut.length = hashRef->digestSize; + + if ((err = ReadyHash(hashRef, &hashCtx, ctx)) != 0) + goto fail; + if ((err = hashRef->update(&hashCtx, &clientRandom)) != 0) + goto fail; + if ((err = hashRef->update(&hashCtx, &serverRandom)) != 0) + goto fail; + if ((err = hashRef->update(&hashCtx, &exchangeParams)) != 0) + goto fail; + if ((err = hashRef->final(&hashCtx, &hashOut)) != 0) + goto fail; + + if(sigAlg.signature==SSL_SignatureAlgorithmRSA) { + err = sslRsaSign(ctx, + ctx->signingPrivKeyRef, + &algId, + dataToSign, + dataToSignLen, + signature.data, + signature.length, + actSigLen); + } else { + err = sslRawSign(ctx, + ctx->signingPrivKeyRef, + dataToSign, // one or two hashes + dataToSignLen, + signature.data, + signature.length, + actSigLen); + } + + if(err) { + sslErrorLog("SSLDecodeSignedServerKeyExchangeTls12: sslRawVerify " + "returned %d\n", (int)err); + goto fail; + } + +fail: + SSLFreeBuffer(&signedHashes, ctx); + SSLFreeBuffer(&hashCtx, ctx); + return err; +} + +static OSStatus +SSLSignServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer exchangeParams, SSLBuffer signature, size_t *actSigLen) +{ + OSStatus err; + uint8_t hashes[SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN]; + SSLBuffer clientRandom,serverRandom,hashCtx, hash; + uint8_t *dataToSign; + size_t dataToSignLen; + + hashCtx.data = 0; + + /* 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; + + + err = sslRawSign(ctx, + ctx->signingPrivKeyRef, + dataToSign, // one or two hashes + dataToSignLen, + signature.data, + signature.length, + actSigLen); + if(err) { + goto fail; + } + +fail: + SSLFreeBuffer(&hashCtx, ctx); + + return err; +} + +static +OSStatus FindSigAlg(SSLContext *ctx, + SSLSignatureAndHashAlgorithm *alg) +{ + unsigned i; + + assert(ctx->protocolSide == kSSLServerSide); + assert(ctx->negProtocolVersion >= TLS_Version_1_2); + assert(!ctx->isDTLS); + + if((ctx->numClientSigAlgs==0) ||(ctx->clientSigAlgs==NULL)) + return errSSLInternal; + + //FIXME: Need a better way to select here + for(i=0; inumClientSigAlgs; i++) { + alg->hash = ctx->clientSigAlgs[i].hash; + alg->signature = ctx->clientSigAlgs[i].signature; + //We only support RSA for certs on the server side - but we should test against the cert type + if(ctx->clientSigAlgs[i].signature != SSL_SignatureAlgorithmRSA) + continue; + //Let's only support SHA1 and SHA256. SHA384 does not work with 512 bits keys. + // We should actually test against what the cert can do. + if((alg->hash==SSL_HashAlgorithmSHA1) || (alg->hash==SSL_HashAlgorithmSHA256)) { + return noErr; + } + } + // We could not find a supported signature and hash algorithm + return errSSLProtocol; +} + +static OSStatus +SSLEncodeSignedServerKeyExchange(SSLRecord *keyExch, SSLContext *ctx) +{ OSStatus err; + uint8_t *charPtr; + size_t outputLen; + bool isRsa = true; + size_t maxSigLen; + size_t actSigLen; + SSLBuffer signature; + int head = 4; + SSLBuffer exchangeParams; + + assert(ctx->protocolSide == kSSLServerSide); + assert(ctx->signingPubKey != NULL); + assert(ctx->negProtocolVersion >= SSL_Version_3_0); + exchangeParams.data = 0; + signature.data = 0; + +#if ENABLE_DTLS + if(ctx->negProtocolVersion == DTLS_Version_1_0) { + head+=8; + } +#endif + + + /* 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; + } + size_t len = SSLEncodedDHKeyParamsLen(ctx); + 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; + } + + SSLSignatureAndHashAlgorithm sigAlg; + + + /* preallocate a buffer for signing */ + err = sslGetMaxSigSize(ctx->signingPrivKeyRef, &maxSigLen); + if(err) { + goto fail; + } + err = SSLAllocBuffer(&signature, maxSigLen, ctx); + if(err) { + goto fail; + } + + outputLen = exchangeParams.length + 2; + + if (sslVersionIsLikeTls12(ctx)) + { + err=FindSigAlg(ctx, &sigAlg); + if(err) + goto fail; + + outputLen += 2; + err = SSLSignServerKeyExchangeTls12(ctx, sigAlg, exchangeParams, + signature, &actSigLen); + } else { + err = SSLSignServerKeyExchange(ctx, isRsa, exchangeParams, + signature, &actSigLen); + } + + if(err) + goto fail; + + assert(actSigLen <= maxSigLen); + + outputLen += actSigLen; + + /* package it all up */ + keyExch->protocolVersion = ctx->negProtocolVersion; + keyExch->contentType = SSL_RecordTypeHandshake; + if ((err = SSLAllocBuffer(&keyExch->contents, outputLen+head, ctx)) != 0) + goto fail; + + charPtr = SSLEncodeHandshakeHeader(ctx, keyExch, SSL_HdskServerKeyExchange, outputLen); + + memcpy(charPtr, exchangeParams.data, exchangeParams.length); + charPtr += exchangeParams.length; + + if (sslVersionIsLikeTls12(ctx)) + { + *charPtr++=sigAlg.hash; + *charPtr++=sigAlg.signature; + } + + charPtr = SSLEncodeInt(charPtr, actSigLen, 2); + memcpy(charPtr, signature.data, actSigLen); + assert((charPtr + actSigLen) == + (keyExch->contents.data + keyExch->contents.length)); + + err = noErr; + +fail: + SSLFreeBuffer(&exchangeParams, ctx); + SSLFreeBuffer(&signature, ctx); + return err; +} + +static OSStatus +SSLVerifySignedServerKeyExchange(SSLContext *ctx, bool isRsa, SSLBuffer signedParams, + uint8_t *signature, UInt16 signatureLen) +{ + OSStatus err; + SSLBuffer hashOut, hashCtx, clientRandom, serverRandom; + uint8_t hashes[SSL_SHA1_DIGEST_LEN + SSL_MD5_DIGEST_LEN]; + SSLBuffer signedHashes; + uint8_t *dataToSign; + size_t dataToSignLen; + + signedHashes.data = 0; + hashCtx.data = 0; + + 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, ECDSA - 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, + dataToSign, /* plaintext */ + dataToSignLen, /* plaintext length */ + signature, + signatureLen); + if(err) { + sslErrorLog("SSLDecodeSignedServerKeyExchange: sslRawVerify " + "returned %d\n", (int)err); + goto fail; + } + +fail: + SSLFreeBuffer(&signedHashes, ctx); + SSLFreeBuffer(&hashCtx, ctx); + return err; + +} + +static OSStatus +SSLVerifySignedServerKeyExchangeTls12(SSLContext *ctx, SSLSignatureAndHashAlgorithm sigAlg, SSLBuffer signedParams, + uint8_t *signature, UInt16 signatureLen) +{ + OSStatus err; + SSLBuffer hashOut, hashCtx, clientRandom, serverRandom; + uint8_t hashes[SSL_MAX_DIGEST_LEN]; + SSLBuffer signedHashes; + uint8_t *dataToSign; + size_t dataToSignLen; + const HashReference *hashRef; + SecAsn1AlgId algId; + + signedHashes.data = 0; + hashCtx.data = 0; + + clientRandom.data = ctx->clientRandom; + clientRandom.length = SSL_CLIENT_SRVR_RAND_SIZE; + serverRandom.data = ctx->serverRandom; + serverRandom.length = SSL_CLIENT_SRVR_RAND_SIZE; + + switch (sigAlg.hash) { + case SSL_HashAlgorithmSHA1: + hashRef = &SSLHashSHA1; + algId.algorithm = CSSMOID_SHA1WithRSA; + break; + case SSL_HashAlgorithmSHA256: + hashRef = &SSLHashSHA256; + algId.algorithm = CSSMOID_SHA256WithRSA; + break; + case SSL_HashAlgorithmSHA384: + hashRef = &SSLHashSHA384; + algId.algorithm = CSSMOID_SHA384WithRSA; + break; + default: + sslErrorLog("SSLVerifySignedServerKeyExchangeTls12: unsupported hash %d\n", sigAlg.hash); + return errSSLProtocol; + } + + + dataToSign = hashes; + dataToSignLen = hashRef->digestSize; + hashOut.data = hashes; + hashOut.length = hashRef->digestSize; + + if ((err = ReadyHash(hashRef, &hashCtx, ctx)) != 0) + goto fail; + if ((err = hashRef->update(&hashCtx, &clientRandom)) != 0) + goto fail; + if ((err = hashRef->update(&hashCtx, &serverRandom)) != 0) + goto fail; + if ((err = hashRef->update(&hashCtx, &signedParams)) != 0) + goto fail; + if ((err = hashRef->final(&hashCtx, &hashOut)) != 0) + goto fail; + + if(sigAlg.signature==SSL_SignatureAlgorithmRSA) { + err = sslRsaVerify(ctx, + ctx->peerPubKey, + &algId, + dataToSign, + dataToSignLen, + signature, + signatureLen); + } else { + err = sslRawVerify(ctx, + ctx->peerPubKey, + dataToSign, /* plaintext */ + dataToSignLen, /* plaintext length */ + signature, + signatureLen); + } + + if(err) { + sslErrorLog("SSLDecodeSignedServerKeyExchangeTls12: sslRawVerify " + "returned %d\n", (int)err); + goto fail; + } + +fail: + SSLFreeBuffer(&signedHashes, ctx); + SSLFreeBuffer(&hashCtx, 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; + UInt16 modulusLen = 0, exponentLen = 0, signatureLen; + uint8_t *modulus = NULL, *exponent = NULL, *signature; + bool isRsa = true; + + assert(ctx->protocolSide == kSSLClientSide); + + if (message.length < 2) { + sslErrorLog("SSLDecodeSignedServerKeyExchange: msg len error 1\n"); + return errSSLProtocol; + } + + /* first extract the key-exchange-method-specific parameters */ + uint8_t *charPtr = message.data; + uint8_t *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 */ + + case SSL_ECDHE_ECDSA: + isRsa = false; + /* and fall through */ + case SSL_ECDHE_RSA: + err = SSLDecodeECDHKeyParams(ctx, &charPtr, message.length); + if(err) { + return err; + } + break; + default: + assert(0); + return errSSLInternal; + } + + /* this is what's hashed */ + SSLBuffer signedParams; + signedParams.data = message.data; + signedParams.length = charPtr - message.data; + + SSLSignatureAndHashAlgorithm sigAlg; + + if (sslVersionIsLikeTls12(ctx)) { + /* Parse the algorithm field added in TLS1.2 */ + if((charPtr + 2) > endCp) { + sslErrorLog("signedServerKeyExchange: msg len error 499\n"); + return errSSLProtocol; + } + sigAlg.hash = *charPtr++; + sigAlg.signature = *charPtr++; + } + + signatureLen = SSLDecodeInt(charPtr, 2); + charPtr += 2; + if((charPtr + signatureLen) != endCp) { + sslErrorLog("signedServerKeyExchange: msg len error 4\n"); + return errSSLProtocol; + } + signature = charPtr; + + if (sslVersionIsLikeTls12(ctx)) + { + err = SSLVerifySignedServerKeyExchangeTls12(ctx, sigAlg, signedParams, + signature, signatureLen); + } else { + err = SSLVerifySignedServerKeyExchange(ctx, isRsa, signedParams, + signature, signatureLen); + } + + if(err) + goto fail; + + /* Signature matches; now replace server key with new key (RSA only) */ + switch(ctx->selectedCipherSpec.keyExchangeMethod) { + case SSL_RSA: + case SSL_RSA_EXPORT: + { + SSLBuffer modBuf; + SSLBuffer expBuf; + + /* first free existing peerKey */ + sslFreePubKey(&ctx->peerPubKey); /* 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); + break; + } + case SSL_DHE_RSA: + case SSL_DHE_RSA_EXPORT: + case SSL_DHE_DSS: + case SSL_DHE_DSS_EXPORT: + case SSL_ECDHE_ECDSA: + case SSL_ECDHE_RSA: + break; /* handled above */ + default: + assert(0); + } +fail: + return err; +} + +static OSStatus +SSLDecodeRSAKeyExchange(SSLBuffer keyExchange, SSLContext *ctx) +{ OSStatus err; + size_t outputLen, localKeyModulusLen; + SSLProtocolVersion version; + Boolean useEncryptKey = false; + uint8_t *src = NULL; + SSLPrivKey *keyRef = NULL; + + assert(ctx->protocolSide == kSSLServerSide); + + #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 */ + } + + localKeyModulusLen = sslPrivKeyLengthInBytes(keyRef); + if (localKeyModulusLen == 0) { + sslErrorLog("SSLDecodeRSAKeyExchange: private key modulus is 0\n"); + return errSSLCrypto; + } + + /* + * 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, +#if USE_CDSA_CRYPTO + CSSM_PADDING_PKCS1, +#else + kSecPaddingPKCS1, +#endif + 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; + size_t outputLen, peerKeyModulusLen; + size_t bufLen; + uint8_t *dst; + bool encodeLen = false; + uint8_t *p; + int head; + size_t msglen; + + assert(ctx->protocolSide == kSSLClientSide); + if ((err = SSLEncodeRSAPremasterSecret(ctx)) != 0) + return err; + + keyExchange->contentType = SSL_RecordTypeHandshake; + assert(ctx->negProtocolVersion >= SSL_Version_3_0); + keyExchange->protocolVersion = ctx->negProtocolVersion; + + peerKeyModulusLen = sslPubKeyLengthInBytes(ctx->peerPubKey); + if (peerKeyModulusLen == 0) { + sslErrorLog("SSLEncodeRSAKeyExchange: peer key modulus is 0\n"); + /* FIXME: we don't return an error here... is this condition ever expected? */ + } +#if SSL_DEBUG + sslDebugLog("SSLEncodeRSAKeyExchange: peer key modulus length = %lu\n", peerKeyModulusLen); +#endif + msglen = peerKeyModulusLen; + #if RSA_CLIENT_KEY_ADD_LENGTH + if(ctx->negProtocolVersion >= TLS_Version_1_0) { + msglen += 2; + encodeLen = true; + } + #endif + head = SSLHandshakeHeaderSize(keyExchange); + bufLen = msglen + head; + if ((err = SSLAllocBuffer(&keyExchange->contents, + bufLen,ctx)) != 0) + { + return err; + } + dst = keyExchange->contents.data + head; + if(encodeLen) { + dst += 2; + } + + /* FIXME: can this line be removed? */ + p = keyExchange->contents.data; + + p = SSLEncodeHandshakeHeader(ctx, keyExchange, SSL_HdskClientKeyExchange, msglen); + + if(encodeLen) { + /* the length of the encrypted pre_master_secret */ + SSLEncodeSize(keyExchange->contents.data + head, + peerKeyModulusLen, 2); + } + err = sslRsaEncrypt(ctx, + ctx->peerPubKey, +#if USE_CDSA_CRYPTO + CSSM_PADDING_PKCS1, +#else + kSecPaddingPKCS1, +#endif + ctx->preMasterSecret.data, + SSL_RSA_PREMASTER_SECRET_SIZE, + dst, + peerKeyModulusLen, + &outputLen); + if(err) { + sslErrorLog("SSLEncodeRSAKeyExchange: error %d\n", err); + return err; + } + + assert(outputLen == (encodeLen ? msglen - 2 : msglen)); + + 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 == kSSLServerSide); + + + /* + * Obtain D-H parameters if we don't have them. + */ + if(ctx->dhParamsEncoded.data == NULL) { + /* TODO: Pick appropriate group based on cipher suite */ + ccdh_const_gp_t gp = ccdh_gp_rfc5114_MODP_2048_256(); + cc_size n = ccdh_gp_n(gp); + size_t s = ccdh_gp_prime_size(gp); + uint8_t p[s]; + uint8_t g[s]; + + ccn_write_uint(n, ccdh_gp_prime(gp), s, p); + ccn_write_uint(n, ccdh_gp_g(gp), s, g); + + const SSLBuffer prime = { + .data = p, + .length = s, + }; + const SSLBuffer generator = { + .data = g, + .length = s, + }; + + ortn=sslEncodeDhParams(&ctx->dhParamsEncoded, /* data mallocd and RETURNED PKCS-3 encoded */ + &prime, /* Wire format */ + &generator); /* Wire format */ + + if(ortn) + return ortn; + } + +#if USE_CDSA_CRYPTO + /* 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); +#else + if (!ctx->secDHContext) { + ortn = sslDhCreateKey(ctx); + if(ortn) + return ortn; + } + return sslDhGenerateKeyPair(ctx); +#endif + return noErr; +} + +/* + * size of DH param and public key, in wire format + */ +static size_t +SSLEncodedDHKeyParamsLen(SSLContext *ctx) +{ + SSLBuffer prime; + SSLBuffer generator; + + sslDecodeDhParams(&ctx->dhParamsEncoded, &prime, &generator); + + return (2+prime.length+2+generator.length+2+ctx->dhExchangePublic.length); +} + +/* + * Encode DH params and public key, in wire format, in caller-supplied buffer. + */ +static OSStatus +SSLEncodeDHKeyParams( + SSLContext *ctx, + uint8_t *charPtr) +{ + assert(ctx->protocolSide == kSSLServerSide); + assert(ctx->dhParamsEncoded.data != NULL); + assert(ctx->dhExchangePublic.data != NULL); + + SSLBuffer prime; + SSLBuffer generator; + + sslDecodeDhParams(&ctx->dhParamsEncoded, &prime, &generator); + + charPtr = SSLEncodeInt(charPtr, prime.length, 2); + memcpy(charPtr, prime.data, prime.length); + charPtr += prime.length; + + charPtr = SSLEncodeInt(charPtr, generator.length, 2); + memcpy(charPtr, generator.data, + generator.length); + charPtr += generator.length; + + /* TODO: hum.... sounds like this one should be in the SecDHContext */ + charPtr = SSLEncodeInt(charPtr, ctx->dhExchangePublic.length, 2); + memcpy(charPtr, ctx->dhExchangePublic.data, + ctx->dhExchangePublic.length); + + dumpBuf("server prime", &prime); + dumpBuf("server generator", &generator); + dumpBuf("server pub key", &ctx->dhExchangePublic); + + return noErr; +} + +/* + * Decode DH params and server public key. + */ +static OSStatus +SSLDecodeDHKeyParams( + SSLContext *ctx, + uint8_t **charPtr, // IN/OUT + size_t length) +{ + OSStatus err = noErr; + SSLBuffer prime; + SSLBuffer generator; + + assert(ctx->protocolSide == kSSLClientSide); + uint8_t *endCp = *charPtr + length; + + /* Allow reuse via renegotiation */ + SSLFreeBuffer(&ctx->dhPeerPublic, ctx); + + /* Prime, with a two-byte length */ + UInt32 len = SSLDecodeInt(*charPtr, 2); + (*charPtr) += 2; + if((*charPtr + len) > endCp) { + return errSSLProtocol; + } + + prime.data = *charPtr; + prime.length = len; + + (*charPtr) += len; + + /* Generator, with a two-byte length */ + len = SSLDecodeInt(*charPtr, 2); + (*charPtr) += 2; + if((*charPtr + len) > endCp) { + return errSSLProtocol; + } + + generator.data = *charPtr; + generator.length = len; + + (*charPtr) += len; + + sslEncodeDhParams(&ctx->dhParamsEncoded, &prime, &generator); + + /* 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; + +#if USE_CDSA_CRYPTO + + 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); +#else + ortn=errSSLProtocol; + require(ctx->dhParamsEncoded.data, out); + require_noerr(ortn = sslDhCreateKey(ctx), out); + require_noerr(ortn = sslDhGenerateKeyPair(ctx), out); + require_noerr(ortn = sslDhKeyExchange(ctx), out); +out: +#endif + return ortn; +} + + +static OSStatus +SSLEncodeDHanonServerKeyExchange(SSLRecord *keyExch, SSLContext *ctx) +{ + OSStatus ortn = noErr; + int head; + + assert(ctx->negProtocolVersion >= SSL_Version_3_0); + assert(ctx->protocolSide == kSSLServerSide); + + /* + * Obtain D-H parameters (if we don't have them) and a key pair. + */ + ortn = SSLGenServerDHParamsAndKey(ctx); + if(ortn) { + return ortn; + } + + size_t length = SSLEncodedDHKeyParamsLen(ctx); + + keyExch->protocolVersion = ctx->negProtocolVersion; + keyExch->contentType = SSL_RecordTypeHandshake; + head = SSLHandshakeHeaderSize(keyExch); + if ((ortn = SSLAllocBuffer(&keyExch->contents, length+head, ctx)) != 0) + return ortn; + + uint8_t *charPtr = SSLEncodeHandshakeHeader(ctx, keyExch, SSL_HdskServerKeyExchange, length); + + /* encode prime, generator, our public key */ + return SSLEncodeDHKeyParams(ctx, charPtr); +} + +static OSStatus +SSLDecodeDHanonServerKeyExchange(SSLBuffer message, SSLContext *ctx) +{ + OSStatus err = noErr; + + assert(ctx->protocolSide == kSSLClientSide); + if (message.length < 6) { + sslErrorLog("SSLDecodeDHanonServerKeyExchange error: msg len %u\n", + (unsigned)message.length); + return errSSLProtocol; + } + uint8_t *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 == kSSLServerSide); + if(ctx->dhParamsEncoded.data == NULL) { + /* should never happen */ + assert(0); + return errSSLInternal; + } + + /* this message simply contains the client's public DH key */ + uint8_t *charPtr = keyExchange.data; + publicLen = SSLDecodeInt(charPtr, 2); + charPtr += 2; + /* TODO : Check the len here ? Will fail in sslDhKeyExchange anyway */ + /* + 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); +#if USE_CDSA_CRYPTO + ortn = sslDhKeyExchange(ctx, ctx->dhParamsPrime.length * 8, + &ctx->preMasterSecret); +#else + ortn = sslDhKeyExchange(ctx); +#endif + dumpBuf("server peer pub", &ctx->dhPeerPublic); + dumpBuf("server premaster", &ctx->preMasterSecret); + return ortn; +} + +static OSStatus +SSLEncodeDHClientKeyExchange(SSLRecord *keyExchange, SSLContext *ctx) +{ OSStatus err; + size_t outputLen; + int head; + + assert(ctx->protocolSide == kSSLClientSide); + assert(ctx->negProtocolVersion >= SSL_Version_3_0); + + keyExchange->contentType = SSL_RecordTypeHandshake; + keyExchange->protocolVersion = ctx->negProtocolVersion; + + if ((err = SSLGenClientDHKeyAndExchange(ctx)) != 0) + return err; + + outputLen = ctx->dhExchangePublic.length + 2; + head = SSLHandshakeHeaderSize(keyExchange); + if ((err = SSLAllocBuffer(&keyExchange->contents,outputLen + head,ctx)) != 0) + return err; + + uint8_t *charPtr = SSLEncodeHandshakeHeader(ctx, keyExchange, SSL_HdskClientKeyExchange, outputLen); + + charPtr = SSLEncodeSize(charPtr, ctx->dhExchangePublic.length, 2); + memcpy(charPtr, 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 ECDSA Key Exchange + +/* + * Given the server's ECDH curve params and public key, generate our + * own ECDH 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: + * if keyExchangeMethod == SSL_ECDHE_ECDSA or SSL_ECDHE_RSA: + * ecdhPeerPublic + * ecdhPeerCurve + * if keyExchangeMethod == SSL_ECDH_ECDSA or SSL_ECDH_RSA: + * peerPubKey, from which we infer ecdhPeerCurve + * + * SSLContext members valid on successful return: + * ecdhPrivate + * ecdhExchangePublic + * preMasterSecret + */ +static OSStatus +SSLGenClientECDHKeyAndExchange(SSLContext *ctx) +{ + OSStatus ortn; + + assert(ctx->protocolSide == kSSLClientSide); + + switch(ctx->selectedCipherSpec.keyExchangeMethod) { + case SSL_ECDHE_ECDSA: + case SSL_ECDHE_RSA: + /* Server sent us an ephemeral key with peer curve specified */ + if(ctx->ecdhPeerPublic.data == NULL) { + sslErrorLog("SSLGenClientECDHKeyAndExchange: incomplete server params\n"); + return errSSLProtocol; + } + break; + case SSL_ECDH_ECDSA: + case SSL_ECDH_RSA: + { + /* No server key exchange; we have to get the curve from the key */ + if(ctx->peerPubKey == NULL) { + sslErrorLog("SSLGenClientECDHKeyAndExchange: no peer key\n"); + return errSSLInternal; + } + + /* The peer curve is in the key's CSSM_X509_ALGORITHM_IDENTIFIER... */ + ortn = sslEcdsaPeerCurve(ctx->peerPubKey, &ctx->ecdhPeerCurve); + if(ortn) { + return ortn; + } + sslEcdsaDebug("SSLGenClientECDHKeyAndExchange: derived peerCurve %u", + (unsigned)ctx->ecdhPeerCurve); + break; + } + default: + /* shouldn't be here */ + assert(0); + return errSSLInternal; + } + + /* Generate our (ephemeral) pair, or extract it from our signing identity */ + if((ctx->negAuthType == SSLClientAuth_RSAFixedECDH) || + (ctx->negAuthType == SSLClientAuth_ECDSAFixedECDH)) { + /* + * Client auth with a fixed ECDH key in the cert. Convert private key + * from SecKeyRef to CSSM format. We don't need ecdhExchangePublic + * because the server gets that from our cert. + */ + assert(ctx->signingPrivKeyRef != NULL); +#if USE_CDSA_CRYPTO + //assert(ctx->cspHand != 0); + sslFreeKey(ctx->cspHand, &ctx->ecdhPrivate, NULL); + SSLFreeBuffer(&ctx->ecdhExchangePublic, ctx); + ortn = SecKeyGetCSSMKey(ctx->signingPrivKeyRef, (const CSSM_KEY **)&ctx->ecdhPrivate); + if(ortn) { + return ortn; + } + ortn = SecKeyGetCSPHandle(ctx->signingPrivKeyRef, &ctx->ecdhPrivCspHand); + if(ortn) { + sslErrorLog("SSLGenClientECDHKeyAndExchange: SecKeyGetCSPHandle err %d\n", + (int)ortn); + } +#endif + sslEcdsaDebug("+++ Extracted ECDH private key"); + } + else { + /* generate a new pair */ + ortn = sslEcdhGenerateKeyPair(ctx, ctx->ecdhPeerCurve); + if(ortn) { + return ortn; + } +#if USE_CDSA_CRYPTO + sslEcdsaDebug("+++ Generated %u bit (%u byte) ECDH key pair", + (unsigned)ctx->ecdhPrivate->KeyHeader.LogicalKeySizeInBits, + (unsigned)((ctx->ecdhPrivate->KeyHeader.LogicalKeySizeInBits + 7) / 8)); +#endif + } + + + /* do the exchange --> premaster secret */ + ortn = sslEcdhKeyExchange(ctx, &ctx->preMasterSecret); + if(ortn) { + return ortn; + } + return noErr; +} + + +/* + * Decode ECDH params and server public key. + */ +static OSStatus +SSLDecodeECDHKeyParams( + SSLContext *ctx, + uint8_t **charPtr, // IN/OUT + size_t length) +{ + OSStatus err = noErr; + + sslEcdsaDebug("+++ Decoding ECDH Server Key Exchange"); + + assert(ctx->protocolSide == kSSLClientSide); + uint8_t *endCp = *charPtr + length; + + /* Allow reuse via renegotiation */ + SSLFreeBuffer(&ctx->ecdhPeerPublic, ctx); + + /*** ECParameters - just a curveType and a named curve ***/ + + /* 1-byte curveType, we only allow one type */ + uint8_t curveType = **charPtr; + if(curveType != SSL_CurveTypeNamed) { + sslEcdsaDebug("+++ SSLDecodeECDHKeyParams: Bad curveType (%u)\n", (unsigned)curveType); + return errSSLProtocol; + } + (*charPtr)++; + if(*charPtr > endCp) { + return errSSLProtocol; + } + + /* two-byte curve */ + ctx->ecdhPeerCurve = SSLDecodeInt(*charPtr, 2); + (*charPtr) += 2; + if(*charPtr > endCp) { + return errSSLProtocol; + } + switch(ctx->ecdhPeerCurve) { + case SSL_Curve_secp256r1: + case SSL_Curve_secp384r1: + case SSL_Curve_secp521r1: + break; + default: + sslEcdsaDebug("+++ SSLDecodeECDHKeyParams: Bad curve (%u)\n", + (unsigned)ctx->ecdhPeerCurve); + return errSSLProtocol; + } + + sslEcdsaDebug("+++ SSLDecodeECDHKeyParams: ecdhPeerCurve %u", + (unsigned)ctx->ecdhPeerCurve); + + /*** peer public key as an ECPoint ***/ + + /* + * The spec says the the max length of an ECPoint is 255 bytes, limiting + * this whole mechanism to a max modulus size of 1020 bits, which I find + * hard to believe... + */ + UInt32 len = SSLDecodeInt(*charPtr, 1); + (*charPtr)++; + if((*charPtr + len) > endCp) { + return errSSLProtocol; + } + err = SSLAllocBuffer(&ctx->ecdhPeerPublic, len, ctx); + if(err) { + return err; + } + memmove(ctx->ecdhPeerPublic.data, *charPtr, len); + (*charPtr) += len; + + dumpBuf("client peer pub", &ctx->ecdhPeerPublic); + + return err; +} + + +static OSStatus +SSLEncodeECDHClientKeyExchange(SSLRecord *keyExchange, SSLContext *ctx) +{ OSStatus err; + size_t outputLen; + int head; + + assert(ctx->protocolSide == kSSLClientSide); + if ((err = SSLGenClientECDHKeyAndExchange(ctx)) != 0) + return err; + + /* + * Per RFC 4492 5.7, if we're doing ECDSA_fixed_ECDH or RSA_fixed_ECDH + * client auth, we still send this message, but it's empty (because the + * server gets our public key from our cert). + */ + bool emptyMsg = false; + switch(ctx->negAuthType) { + case SSLClientAuth_RSAFixedECDH: + case SSLClientAuth_ECDSAFixedECDH: + emptyMsg = true; + break; + default: + break; + } + if(emptyMsg) { + outputLen = 0; + } + else { + outputLen = ctx->ecdhExchangePublic.length + 1; + } + + keyExchange->contentType = SSL_RecordTypeHandshake; + assert(ctx->negProtocolVersion >= SSL_Version_3_0); + keyExchange->protocolVersion = ctx->negProtocolVersion; + head = SSLHandshakeHeaderSize(keyExchange); + if ((err = SSLAllocBuffer(&keyExchange->contents,outputLen + head,ctx)) != 0) + return err; + + uint8_t *charPtr = SSLEncodeHandshakeHeader(ctx, keyExchange, SSL_HdskClientKeyExchange, outputLen); + if(emptyMsg) { + sslEcdsaDebug("+++ Sending EMPTY ECDH Client Key Exchange"); + } + else { + /* just a 1-byte length here... */ + charPtr = SSLEncodeSize(charPtr, ctx->ecdhExchangePublic.length, 1); + memcpy(charPtr, ctx->ecdhExchangePublic.data, ctx->ecdhExchangePublic.length); + sslEcdsaDebug("+++ Encoded ECDH Client Key Exchange"); + } + + dumpBuf("client pub key", &ctx->ecdhExchangePublic); + dumpBuf("client premaster", &ctx->preMasterSecret); + return noErr; +} + +#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 + case SSL_ECDHE_ECDSA: + case SSL_ECDHE_RSA: + 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 == kSSLClientSide); + + switch (ctx->selectedCipherSpec.keyExchangeMethod) { + case SSL_RSA: + case SSL_RSA_EXPORT: + sslDebugLog("SSLEncodeKeyExchange: RSA method\n"); + 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: + sslDebugLog("SSLEncodeKeyExchange: DH method\n"); + err = SSLEncodeDHClientKeyExchange(keyExchange, ctx); + break; +#endif + case SSL_ECDH_ECDSA: + case SSL_ECDHE_ECDSA: + case SSL_ECDH_RSA: + case SSL_ECDHE_RSA: + case SSL_ECDH_anon: + sslDebugLog("SSLEncodeKeyExchange: ECDH method\n"); + err = SSLEncodeECDHClientKeyExchange(keyExchange, ctx); + break; + default: + sslDebugLog("SSLEncodeKeyExchange: unknown method (%d)\n", + ctx->selectedCipherSpec.keyExchangeMethod); + err = unimpErr; + } + + return err; +} + +OSStatus +SSLProcessKeyExchange(SSLBuffer keyExchange, SSLContext *ctx) +{ OSStatus err; + + switch (ctx->selectedCipherSpec.keyExchangeMethod) + { case SSL_RSA: + case SSL_RSA_EXPORT: + sslDebugLog("SSLProcessKeyExchange: processing RSA key exchange (%d)\n", + ctx->selectedCipherSpec.keyExchangeMethod); + 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: + sslDebugLog("SSLProcessKeyExchange: processing DH key exchange (%d)\n", + ctx->selectedCipherSpec.keyExchangeMethod); + if ((err = SSLDecodeDHClientKeyExchange(keyExchange, ctx)) != 0) + return err; + break; +#endif + default: + sslErrorLog("SSLProcessKeyExchange: unknown keyExchangeMethod (%d)\n", + ctx->selectedCipherSpec.keyExchangeMethod); + return unimpErr; + } + + return noErr; +} + +OSStatus +SSLInitPendingCiphers(SSLContext *ctx) +{ OSStatus err; + SSLBuffer key; + uint8_t *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; + + if(ctx->negProtocolVersion == DTLS_Version_1_0) + { + ctx->readPending.sequenceNum.high = (ctx->readPending.sequenceNum.high & (0xffff<<16)) + (1<<16); + ctx->writePending.sequenceNum.high = (ctx->writePending.sequenceNum.high & (0xffff<<16)) + (1<<16); + } else { + ctx->writePending.sequenceNum.high=0; + ctx->readPending.sequenceNum.high=0; + } + ctx->readPending.sequenceNum.low = 0; + 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 == kSSLServerSide) + { 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 */ + UInt8 ivSize = ctx->selectedCipherSpec.cipher->ivSize; + + if (ivSize == 0) + { + ivPtr = NULL; + } + else + { + 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 */ + if (ivSize == 0) + { + ivPtr = NULL; + } + else + { + ivPtr = keyDataProgress + ctx->selectedCipherSpec.cipher->ivSize; + } + + if ((err = ctx->selectedCipherSpec.cipher->initialize(keyPtr, ivPtr, + serverPending, ctx)) != 0) + goto fail; + } + else { + uint8_t 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; +}