/* * Copyright (c) 2006-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@ */ /* * sslCrypto.c - interface between SSL and crypto libraries */ #include "sslCrypto.h" #include "CipherSuite.h" #include "ssl.h" #include "sslContext.h" #include "sslMemory.h" #include "sslUtils.h" #include "sslDebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "utilities/SecCFRelease.h" #if TARGET_OS_IPHONE #include #include #include #endif #ifndef _SSL_KEYCHAIN_H_ #include "sslKeychain.h" #endif #if APPLE_DH #include #include #include #include #include #endif #include #include #include #if TARGET_OS_IPHONE #define CCRNGSTATE ccrng_seckey #else /* extern struct ccrng_state *ccDRBGGetRngState(); */ #include #define CCRNGSTATE ccDRBGGetRngState() #endif /* * Free a pubKey object. */ extern OSStatus sslFreePubKey(SSLPubKey **pubKey) { if (pubKey && *pubKey) { CFReleaseNull(SECKEYREF(*pubKey)); } return errSecSuccess; } /* * Free a privKey object. */ extern OSStatus sslFreePrivKey(SSLPrivKey **privKey) { if (privKey && *privKey) { CFReleaseNull(SECKEYREF(*privKey)); } return errSecSuccess; } /* * Get algorithm id for a SSLPubKey object. */ CFIndex sslPubKeyGetAlgorithmID(SSLPubKey *pubKey) { #if TARGET_OS_IPHONE return SecKeyGetAlgorithmID(SECKEYREF(pubKey)); #else return SecKeyGetAlgorithmId(SECKEYREF(pubKey)); #endif } /* * Get algorithm id for a SSLPrivKey object. */ CFIndex sslPrivKeyGetAlgorithmID(SSLPrivKey *privKey) { #if TARGET_OS_IPHONE return SecKeyGetAlgorithmID(SECKEYREF(privKey)); #else return SecKeyGetAlgorithmId(SECKEYREF(privKey)); #endif } /* * Raw RSA/DSA sign/verify. */ OSStatus sslRawSign( SSLContext *ctx, SSLPrivKey *privKey, const uint8_t *plainText, size_t plainTextLen, uint8_t *sig, // mallocd by caller; RETURNED size_t sigLen, // available size_t *actualBytes) // RETURNED { #if 0 RSAStatus rsaStatus; #if RSA_SIG_SHARE_GIANT RSASignBuffer *signBuffer = (RSASignBuffer *)sig; assert(sigLen >= sizeof(RSASignBuffer)); #endif assert(actualBytes != NULL); /* @@@ Shouldn't need to init giSigLen according to libgRSA docs. */ gi_uint16 giSigLen = sigLen; rsaStatus = RSA_Sign(&privKey->rsaKey, RP_PKCS1, plainText, plainTextLen, #if RSA_SIG_SHARE_GIANT signBuffer, #else sig, #endif &giSigLen); *actualBytes = giSigLen; return rsaStatus ? rsaStatusToSSL(rsaStatus) : errSecSuccess; #else size_t inOutSigLen = sigLen; assert(actualBytes != NULL); OSStatus status = SecKeyRawSign(SECKEYREF(privKey), kSecPaddingPKCS1, plainText, plainTextLen, sig, &inOutSigLen); if (status) { sslErrorLog("sslRawSign: SecKeyRawSign failed (error %d)\n", (int)status); } /* Since the KeyExchange already allocated modulus size bytes we'll use all of them. SecureTransport has always sent that many bytes, so we're not going to deviate, to avoid interoperability issues. */ if (!status && (inOutSigLen < sigLen)) { size_t offset = sigLen - inOutSigLen; memmove(sig + offset, sig, inOutSigLen); memset(sig, 0, offset); inOutSigLen = sigLen; } *actualBytes = inOutSigLen; return status; #endif } /* TLS 1.2 RSA signature */ OSStatus sslRsaSign( SSLContext *ctx, SSLPrivKey *privKey, const SecAsn1AlgId *algId, const uint8_t *plainText, size_t plainTextLen, uint8_t *sig, // mallocd by caller; RETURNED size_t sigLen, // available size_t *actualBytes) // RETURNED { size_t inOutSigLen = sigLen; assert(actualBytes != NULL); OSStatus status = SecKeySignDigest(SECKEYREF(privKey), algId, plainText, plainTextLen, sig, &inOutSigLen); if (status) { sslErrorLog("sslRsaSign: SecKeySignDigest failed (error %d)\n", (int) status); } /* Since the KeyExchange already allocated modulus size bytes we'll use all of them. SecureTransport has always sent that many bytes, so we're not going to deviate, to avoid interoperability issues. */ if (!status && (inOutSigLen < sigLen)) { size_t offset = sigLen - inOutSigLen; memmove(sig + offset, sig, inOutSigLen); memset(sig, 0, offset); inOutSigLen = sigLen; } *actualBytes = inOutSigLen; return status; } OSStatus sslRawVerify( SSLContext *ctx, SSLPubKey *pubKey, const uint8_t *plainText, size_t plainTextLen, const uint8_t *sig, size_t sigLen) // available { #if 0 RSAStatus rsaStatus; rsaStatus = RSA_SigVerify(&pubKey->rsaKey, RP_PKCS1, plainText, plainTextLen, sig, sigLen); return rsaStatus ? rsaStatusToSSL(rsaStatus) : errSecSuccess; #else OSStatus status = SecKeyRawVerify(SECKEYREF(pubKey), kSecPaddingPKCS1, plainText, plainTextLen, sig, sigLen); if (status) { sslErrorLog("sslRawVerify: SecKeyRawVerify failed (error %d)\n", (int) status); } return status; #endif } /* TLS 1.2 RSA verify */ OSStatus sslRsaVerify( SSLContext *ctx, SSLPubKey *pubKey, const SecAsn1AlgId *algId, const uint8_t *plainText, size_t plainTextLen, const uint8_t *sig, size_t sigLen) // available { OSStatus status = SecKeyVerifyDigest(SECKEYREF(pubKey), algId, plainText, plainTextLen, sig, sigLen); if (status) { sslErrorLog("sslRsaVerify: SecKeyVerifyDigest failed (error %d)\n", (int) status); } return status; } /* * Encrypt/Decrypt */ OSStatus sslRsaEncrypt( SSLContext *ctx, SSLPubKey *pubKey, const uint32_t padding, const uint8_t *plainText, size_t plainTextLen, uint8_t *cipherText, // mallocd by caller; RETURNED size_t cipherTextLen, // available size_t *actualBytes) // RETURNED { #if 0 gi_uint16 giCipherTextLen = cipherTextLen; RSAStatus rsaStatus; assert(actualBytes != NULL); rsaStatus = RSA_Encrypt(&pubKey->rsaKey, RP_PKCS1, getRandomByte, plainText, plainTextLen, cipherText, &giCipherTextLen); *actualBytes = giCipherTextLen; return rsaStatus ? rsaStatusToSSL(rsaStatus) : errSecSuccess; #else size_t ctlen = cipherTextLen; assert(actualBytes != NULL); #if RSA_PUB_KEY_USAGE_HACK /* Force key usage to allow encryption with public key */ #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) CSSM_KEY *cssmKey = NULL; if (SecKeyGetCSSMKey(SECKEYREF(pubKey), (const CSSM_KEY **)&cssmKey)==errSecSuccess && cssmKey) cssmKey->KeyHeader.KeyUsage |= CSSM_KEYUSE_ENCRYPT; #endif #endif OSStatus status = SecKeyEncrypt(SECKEYREF(pubKey), padding, plainText, plainTextLen, cipherText, &ctlen); if (status) { sslErrorLog("sslRsaEncrypt: SecKeyEncrypt failed (error %d)\n", (int)status); } /* Since the KeyExchange already allocated modulus size bytes we'll use all of them. SecureTransport has always sent that many bytes, so we're not going to deviate, to avoid interoperability issues. */ if (!status && (ctlen < cipherTextLen)) { size_t offset = cipherTextLen - ctlen; memmove(cipherText + offset, cipherText, ctlen); memset(cipherText, 0, offset); ctlen = cipherTextLen; } if (actualBytes) *actualBytes = ctlen; if (status) { sslErrorLog("***sslRsaEncrypt: error %d\n", (int)status); } return status; #endif } OSStatus sslRsaDecrypt( SSLContext *ctx, SSLPrivKey *privKey, const uint32_t padding, const uint8_t *cipherText, size_t cipherTextLen, uint8_t *plainText, // mallocd by caller; RETURNED size_t plainTextLen, // available size_t *actualBytes) // RETURNED { #if 0 gi_uint16 giPlainTextLen = plainTextLen; RSAStatus rsaStatus; assert(actualBytes != NULL); rsaStatus = RSA_Decrypt(&privKey->rsaKey, RP_PKCS1, cipherText, cipherTextLen, plainText, &giPlainTextLen); *actualBytes = giPlainTextLen; return rsaStatus ? rsaStatusToSSL(rsaStatus) : errSecSuccess; #else size_t ptlen = plainTextLen; assert(actualBytes != NULL); OSStatus status = SecKeyDecrypt(SECKEYREF(privKey), padding, cipherText, cipherTextLen, plainText, &ptlen); *actualBytes = ptlen; if (status) { sslErrorLog("sslRsaDecrypt: SecKeyDecrypt failed (error %d)\n", (int)status); } return status; #endif } /* * Obtain size of the modulus of privKey in bytes. */ size_t sslPrivKeyLengthInBytes(SSLPrivKey *privKey) { #if 0 /* Get the length of p + q (which is the size of the modulus) in bits. */ gi_uint16 bitLen = bitlen(&privKey->rsaKey.p.g) + bitlen(&privKey->rsaKey.q.g); /* Convert it to bytes. */ return (bitLen + 7) / 8; #else return SecKeyGetBlockSize(SECKEYREF(privKey)); #endif } /* * Obtain size of the modulus of pubKey in bytes. */ size_t sslPubKeyLengthInBytes(SSLPubKey *pubKey) { #if 0 /* Get the length of the modulus in bytes. */ return giantNumBytes(&pubKey->rsaKey.n.g); #else return SecKeyGetBlockSize(SECKEYREF(pubKey)); #endif } /* * Obtain maximum size of signature in bytes. A bit of a kludge; we could * ask the CSP to do this but that would be kind of expensive. */ OSStatus sslGetMaxSigSize( SSLPrivKey *privKey, size_t *maxSigSize) { assert(maxSigSize != NULL); #if 0 #if RSA_SIG_SHARE_GIANT *maxSigSize = sizeof(RSASignBuffer); #else *maxSigSize = MAX_PRIME_SIZE_BYTES; #endif #else *maxSigSize = SecKeyGetBlockSize(SECKEYREF(privKey)); #endif return errSecSuccess; } #if 0 static OSStatus sslGiantToBuffer( SSLContext *ctx, // Currently unused. giant g, SSLBuffer *buffer) { gi_uint8 *chars; gi_uint16 ioLen; gi_uint16 zeroCount; GIReturn giReturn; OSStatus status; ioLen = serializeGiantBytes(g); status = SSLAllocBuffer(buffer, ioLen); if (status) return status; chars = buffer->data; /* Serialize the giant g into chars. */ giReturn = serializeGiant(g, chars, &ioLen); if(giReturn) { SSLFreeBuffer(buffer); return giReturnToSSL(giReturn); } /* Trim off leading zeroes (but leave one zero if that's all there is). */ for (zeroCount = 0; zeroCount < (ioLen - 1); ++zeroCount) if (chars[zeroCount]) break; if (zeroCount > 0) { buffer->length = ioLen - zeroCount; memmove(chars, chars + zeroCount, buffer->length); } return status; } /* * Get raw key bits from an RSA public key. */ OSStatus sslGetPubKeyBits( SSLContext *ctx, // Currently unused. SSLPubKey *pubKey, SSLBuffer *modulus, // data mallocd and RETURNED SSLBuffer *exponent) // data mallocd and RETURNED { OSStatus status; status = sslGiantToBuffer(ctx, &pubKey->rsaKey.n.g, modulus); if(status) return status; status = sslGiantToBuffer(ctx, &pubKey->rsaKey.e.g, exponent); if(status) { SSLFreeBuffer(modulus); return status; } return status; } #endif /* * Given raw RSA key bits, cook up a SSLPubKey. Used in * Server-initiated key exchange. */ OSStatus sslGetPubKeyFromBits( SSLContext *ctx, const SSLBuffer *modulus, const SSLBuffer *exponent, SSLPubKey **pubKey) // mallocd and RETURNED { if (!pubKey) return errSecParam; #if 0 SSLPubKey *key; RSAStatus rsaStatus; RSAPubKey apiKey = { modulus->data, modulus->length, NULL, 0, exponent->data, exponent->length }; key = sslMalloc(sizeof(*key)); rsaStatus = rsaInitPubGKey(&apiKey, &key->rsaKey); if (rsaStatus) { sslFree(key); return rsaStatusToSSL(rsaStatus); } *pubKey = key; return errSecSuccess; #else check(pubKey); SecRSAPublicKeyParams params = { modulus->data, modulus->length, exponent->data, exponent->length }; #if SSL_DEBUG sslDebugLog("Creating RSA pub key from modulus=%p len=%lu exponent=%p len=%lu\n", modulus->data, modulus->length, exponent->data, exponent->length); #endif SecKeyRef key = SecKeyCreateRSAPublicKey(NULL, (const uint8_t *)¶ms, sizeof(params), kSecKeyEncodingRSAPublicParams); if (!key) { sslErrorLog("sslGetPubKeyFromBits: SecKeyCreateRSAPublicKey failed\n"); return errSSLCrypto; } #if SSL_DEBUG sslDebugLog("sslGetPubKeyFromBits: RSA pub key block size=%lu\n", SecKeyGetBlockSize(key)); #endif *pubKey = (SSLPubKey*)key; return errSecSuccess; #endif } // MARK: - // MARK: Public Certificate Functions #ifdef USE_SSLCERTIFICATE /* * Given a SSLCertificate cert, obtain its public key as a SSLPubKey. * Caller must sslFreePubKey and free the SSLPubKey itself. */ OSStatus sslPubKeyFromCert( SSLContext *ctx, const SSLCertificate *cert, SSLPubKey **pubKey) // RETURNED { DERItem der; DERSignedCertCrl signedCert; DERTBSCert tbsCert; DERSubjPubKeyInfo pubKeyInfo; DERByte numUnused; DERItem pubKeyPkcs1; SSLPubKey *key; DERReturn drtn; RSAStatus rsaStatus; assert(cert); assert(pubKey != NULL); der.data = cert->derCert.data; der.length = cert->derCert.length; /* top level decode */ drtn = DERParseSequence(&der, DERNumSignedCertCrlItemSpecs, DERSignedCertCrlItemSpecs, &signedCert, sizeof(signedCert)); if(drtn) return errSSLBadCert; /* decode the TBSCert - it was saved in full DER form */ drtn = DERParseSequence(&signedCert.tbs, DERNumTBSCertItemSpecs, DERTBSCertItemSpecs, &tbsCert, sizeof(tbsCert)); if(drtn) return errSSLBadCert; /* sequence we're given: encoded DERSubjPubKeyInfo */ drtn = DERParseSequenceContent(&tbsCert.subjectPubKey, DERNumSubjPubKeyInfoItemSpecs, DERSubjPubKeyInfoItemSpecs, &pubKeyInfo, sizeof(pubKeyInfo)); if(drtn) return errSSLBadCert; /* @@@ verify that this is an RSA key by decoding the AlgId */ /* * The contents of pubKeyInfo.pubKey is a bit string whose contents * are a PKCS1 format RSA key. */ drtn = DERParseBitString(&pubKeyInfo.pubKey, &pubKeyPkcs1, &numUnused); if(drtn) return errSSLBadCert; #if TARGET_OS_IPHONE /* Now we have the public key in pkcs1 format. Let's make a public key object out of it. */ key = sslMalloc(sizeof(*key)); rsaStatus = RSA_DecodePubKey(pubKeyPkcs1.data, pubKeyPkcs1.length, &key->rsaKey); if (rsaStatus) { sslFree(key); } #else SecKeyRef rsaPubKeyRef = SecKeyCreateRSAPublicKey(NULL, pubKeyPkcs1.data, pubKeyPkcs1.length, kSecKeyEncodingRSAPublicParams); rsaStatus = (rsaPubKeyRef) ? 0 : 1; key = (SSLPubKey*)rsaPubKeyRef; #endif if (rsaStatus) { return rsaStatusToSSL(rsaStatus); } *pubKey = key; return errSecSuccess; } /* * Verify a chain of DER-encoded certs. * First cert in a chain is root; this must also be present * in ctx->trustedCerts. * * If arePeerCerts is true, host name verification is enabled and we * save the resulting SecTrustRef in ctx->peerSecTrust. Otherwise * we're just validating our own certs; no host name checking and * peerSecTrust is transient. */ OSStatus sslVerifyCertChain( SSLContext *ctx, const SSLCertificate *certChain, bool arePeerCerts) { OSStatus ortn = errSecSuccess; assert(certChain); /* No point checking our own certs, our clients can do that. */ if (!arePeerCerts) return errSecSuccess; CertVerifyReturn cvrtn; /* @@@ Add real cert checking. */ if (certChain->next) { DERItem subject, issuer; issuer.data = certChain->derCert.data; issuer.length = certChain->derCert.length; subject.data = certChain->next->derCert.data; subject.length = certChain->next->derCert.length; cvrtn = certVerify(&subject, &issuer); if (cvrtn != CVR_Success) ortn = errSSLBadCert; } else { sslErrorLog("***sslVerifyCertChain: only one cert in chain\n"); } return ortn; } #else /* !USE_SSLCERTIFICATE */ OSStatus sslCreateSecTrust( SSLContext *ctx, CFArrayRef certChain, bool arePeerCerts, SecTrustRef *pTrust) /* RETURNED */ { OSStatus status = errSecAllocate; CFStringRef peerDomainName = NULL; CFTypeRef policies = NULL; SecTrustRef trust = NULL; if (CFArrayGetCount(certChain) == 0) { status = errSSLBadCert; goto errOut; } if (arePeerCerts) { if (ctx->peerDomainNameLen && ctx->peerDomainName) { CFIndex len = ctx->peerDomainNameLen; if (ctx->peerDomainName[len - 1] == 0) { len--; //secwarning("peerDomainName is zero terminated!"); } /* @@@ Double check that this is the correct encoding. */ require(peerDomainName = CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8 *)ctx->peerDomainName, len, kCFStringEncodingUTF8, false), errOut); } } /* If we are the client, our peer certificates must satisfy the ssl server policy. */ bool server = ctx->protocolSide == kSSLClientSide; require(policies = SecPolicyCreateSSL(server, peerDomainName), errOut); require_noerr(status = SecTrustCreateWithCertificates(certChain, policies, &trust), errOut); /* If we have trustedAnchors we set them here. */ if (ctx->trustedCerts) { require_noerr(status = SecTrustSetAnchorCertificates(trust, ctx->trustedCerts), errOut); require_noerr(status = SecTrustSetAnchorCertificatesOnly(trust, ctx->trustedCertsOnly), errOut); } status = errSecSuccess; errOut: CFReleaseSafe(peerDomainName); CFReleaseSafe(policies); *pTrust = trust; return status; } /* Return the first certificate reference from the supplied array * whose data matches the given certificate, or NULL if none match. */ static SecCertificateRef sslGetMatchingCertInArray( SecCertificateRef certRef, CFArrayRef certArray) { SecCertificateRef matchedCert = NULL; if (certRef == NULL || certArray == NULL) { return NULL; } CFDataRef certData = SecCertificateCopyData(certRef); if (certData) { CFIndex idx, count = CFArrayGetCount(certArray); for(idx=0; idxtrustedCerts. * * If arePeerCerts is true, host name verification is enabled and we * save the resulting SecTrustRef in ctx->peerSecTrust. Otherwise * we're just validating our own certs; no host name checking and * peerSecTrust is transient. */ extern OSStatus sslVerifyCertChain( SSLContext *ctx, CFArrayRef certChain, bool arePeerCerts) { OSStatus status; SecTrustRef trust = NULL; assert(certChain); if (arePeerCerts) { /* renegotiate - start with a new SecTrustRef */ CFReleaseNull(ctx->peerSecTrust); } status = sslCreateSecTrust(ctx, certChain, arePeerCerts, &trust); if (!ctx->enableCertVerify) { /* trivial case, this is caller's responsibility */ status = errSecSuccess; goto errOut; } SecTrustResultType secTrustResult; require_noerr(status = SecTrustEvaluate(trust, &secTrustResult), errOut); switch (secTrustResult) { case kSecTrustResultUnspecified: /* cert chain valid, no special UserTrust assignments */ case kSecTrustResultProceed: /* cert chain valid AND user explicitly trusts this */ status = errSecSuccess; break; case kSecTrustResultDeny: case kSecTrustResultConfirm: case kSecTrustResultRecoverableTrustFailure: default: if(ctx->allowAnyRoot) { sslErrorLog("***Warning: accepting unverified cert chain\n"); status = errSecSuccess; } else { /* * If the caller provided a list of trusted leaf certs, check them here */ if(ctx->trustedLeafCerts) { if (sslGetMatchingCertInArray((SecCertificateRef)CFArrayGetValueAtIndex(certChain, 0), ctx->trustedLeafCerts)) { status = errSecSuccess; goto errOut; } } status = errSSLXCertChainInvalid; } /* Do we really need to return things like: errSSLNoRootCert errSSLUnknownRootCert errSSLCertExpired errSSLCertNotYetValid errSSLHostNameMismatch for our client to see what went wrong, or should we just always return errSSLXCertChainInvalid when something is wrong? */ break; } errOut: if (arePeerCerts) ctx->peerSecTrust = trust; else CFReleaseSafe(trust); return status; } /* * Given a SecCertificateRef cert, obtain its public key as a SSLPubKey. * Caller must sslFreePubKey and free the SSLPubKey itself. */ extern OSStatus sslCopyPeerPubKey( SSLContext *ctx, SSLPubKey **pubKey) { check(pubKey); check(ctx->peerSecTrust); SecKeyRef key = SecTrustCopyPublicKey(ctx->peerSecTrust); if (!key) { sslErrorLog("sslCopyPeerPubKey: %s, ctx->peerSecTrust=%p\n", "SecTrustCopyPublicKey failed", ctx->peerSecTrust); return errSSLBadCert; } *pubKey = (SSLPubKey*)key; return errSecSuccess; } #endif /* !USE_SSLCERTIFICATE */ #ifndef NDEBUG void stPrintCdsaError(const char *op, OSStatus crtn) { assert(FALSE); } #endif /* * After ciphersuite negotiation is complete, verify that we have * the capability of actually performing the selected cipher. * Currently we just verify that we have a cert and private signing * key, if needed, and that the signing key's algorithm matches the * expected key exchange method. * * This is currently called from FindCipherSpec(), after it sets * ctx->selectedCipherSpec to a (supposedly) valid value, and from * sslBuildCipherSpecArray(), in server mode (pre-negotiation) only. */ OSStatus sslVerifySelectedCipher(SSLContext *ctx) { if(ctx->protocolSide == kSSLClientSide) { return errSecSuccess; } #if SSL_PAC_SERVER_ENABLE if((ctx->masterSecretCallback != NULL) && (ctx->sessionTicket.data != NULL)) { /* EAP via PAC resumption; we can do it */ return errSecSuccess; } #endif /* SSL_PAC_SERVER_ENABLE */ CFIndex requireAlg; switch (ctx->selectedCipherSpecParams.keyExchangeMethod) { case SSL_RSA: case SSL_RSA_EXPORT: case SSL_DH_RSA: case SSL_DH_RSA_EXPORT: case SSL_DHE_RSA: case SSL_DHE_RSA_EXPORT: requireAlg = kSecRSAAlgorithmID; break; case SSL_DHE_DSS: case SSL_DHE_DSS_EXPORT: case SSL_DH_DSS: case SSL_DH_DSS_EXPORT: requireAlg = kSecDSAAlgorithmID; break; case SSL_DH_anon: case SSL_DH_anon_EXPORT: case TLS_PSK: requireAlg = kSecNullAlgorithmID; /* no signing key */ break; /* * When SSL_ECDSA_SERVER is true and we support ECDSA on the server side, * we'll need to add some logic here... */ #if SSL_ECDSA_SERVER case SSL_ECDHE_ECDSA: case SSL_ECDHE_RSA: case SSL_ECDH_ECDSA: case SSL_ECDH_RSA: case SSL_ECDH_anon: requireAlg = kSecECDSAAlgorithmID; break; #endif default: /* needs update per cipherSpecs.c */ assert(0); sslErrorLog("sslVerifySelectedCipher: unknown key exchange method\n"); return errSSLInternal; } if(requireAlg == kSecNullAlgorithmID) { return errSecSuccess; } /* private signing key required */ if(ctx->signingPrivKeyRef == NULL) { sslErrorLog("sslVerifySelectedCipher: no signing key\n"); return errSSLBadConfiguration; } /* Check the alg of our signing key. */ CFIndex keyAlg = sslPrivKeyGetAlgorithmID(ctx->signingPrivKeyRef); if (requireAlg != keyAlg) { sslErrorLog("sslVerifySelectedCipher: signing key alg mismatch\n"); return errSSLBadConfiguration; } return errSecSuccess; } #if APPLE_DH /* FIXME: This is duplicated in SecDH */ typedef struct { DERItem p; DERItem g; DERItem l; } DER_DHParams; static const DERItemSpec DER_DHParamsItemSpecs[] = { { DER_OFFSET(DER_DHParams, p), ASN1_INTEGER, DER_DEC_NO_OPTS | DER_ENC_SIGNED_INT }, { DER_OFFSET(DER_DHParams, g), ASN1_INTEGER, DER_DEC_NO_OPTS | DER_ENC_SIGNED_INT }, { DER_OFFSET(DER_DHParams, l), ASN1_INTEGER, DER_DEC_OPTIONAL | DER_ENC_SIGNED_INT }, }; static const DERSize DER_NumDHParamsItemSpecs = sizeof(DER_DHParamsItemSpecs) / sizeof(DERItemSpec); /* Max encoded size for standard (PKCS3) parameters */ #define DH_ENCODED_PARAM_SIZE(primeSizeInBytes) \ DER_MAX_ENCODED_SIZE( \ DER_MAX_ENCODED_SIZE(primeSizeInBytes) + /* g */ \ DER_MAX_ENCODED_SIZE(primeSizeInBytes) + /* p */ \ DER_MAX_ENCODED_SIZE(4)) /* l */ OSStatus sslDecodeDhParams( const SSLBuffer *blob, /* Input - PKCS-3 encoded */ SSLBuffer *prime, /* Output - wire format */ SSLBuffer *generator) /* Output - wire format */ { OSStatus ortn = errSecSuccess; DERReturn drtn; DERItem paramItem = {(DERByte *)blob->data, blob->length}; DER_DHParams decodedParams; drtn = DERParseSequence(¶mItem, DER_NumDHParamsItemSpecs, DER_DHParamsItemSpecs, &decodedParams, sizeof(decodedParams)); if(drtn) return drtn; prime->data = decodedParams.p.data; prime->length = decodedParams.p.length; generator->data = decodedParams.g.data; generator->length = decodedParams.g.length; return ortn; } OSStatus sslEncodeDhParams(SSLBuffer *blob, /* data mallocd and RETURNED PKCS-3 encoded */ const SSLBuffer *prime, /* Wire format */ const SSLBuffer *generator) /* Wire format */ { OSStatus ortn = errSecSuccess; DER_DHParams derParams = { .p = { .length = prime->length, .data = prime->data, }, .g = { .length = generator->length, .data = generator->data, }, .l = { .length = 0, .data = NULL, } }; DERSize ioLen = DH_ENCODED_PARAM_SIZE(derParams.p.length); DERByte *der = sslMalloc(ioLen); // FIXME: What if this fails - we should probably not have a malloc here ? assert(der); ortn = (OSStatus)DEREncodeSequence(ASN1_CONSTR_SEQUENCE, &derParams, DER_NumDHParamsItemSpecs, DER_DHParamsItemSpecs, der, &ioLen); // This should never fail blob->length=ioLen; blob->data=der; return ortn; } OSStatus sslDhCreateKey(SSLContext *ctx) { if (ctx->secDHContext) { SecDHDestroy(ctx->secDHContext); ctx->secDHContext = NULL; } /* Server params are set using encoded dh params */ if (!(ctx->dhParamsEncoded.length && ctx->dhParamsEncoded.data)) return errSSLInternal; if (SecDHCreateFromParameters(ctx->dhParamsEncoded.data, ctx->dhParamsEncoded.length, &ctx->secDHContext)) return errSSLCrypto; return errSecSuccess; } OSStatus sslDhGenerateKeyPair(SSLContext *ctx) { OSStatus ortn = errSecSuccess; require_noerr(ortn = SSLAllocBuffer(&ctx->dhExchangePublic, SecDHGetMaxKeyLength(ctx->secDHContext)), out); require_noerr(ortn = SecDHGenerateKeypair(ctx->secDHContext, ctx->dhExchangePublic.data, &ctx->dhExchangePublic.length), out); out: return ortn; } OSStatus sslDhKeyExchange(SSLContext *ctx) { OSStatus ortn = errSecSuccess; if (ctx == NULL || ctx->secDHContext == NULL || ctx->dhPeerPublic.length == 0) { /* comes from peer, don't panic */ sslErrorLog("sslDhKeyExchange: null peer public key\n"); return errSSLProtocol; } require_noerr(ortn = SSLAllocBuffer(&ctx->preMasterSecret, SecDHGetMaxKeyLength(ctx->secDHContext)), out); require_noerr(ortn = SecDHComputeKey(ctx->secDHContext, ctx->dhPeerPublic.data, ctx->dhPeerPublic.length, ctx->preMasterSecret.data, &ctx->preMasterSecret.length), out); return ortn; out: sslErrorLog("sslDhKeyExchange: failed to compute key (error %d)\n", (int)ortn); return ortn; } #endif /* APPLE_DH */ /* * Given an ECDSA key in SecKey format, extract the SSL_ECDSA_NamedCurve * from its algorithm parameters. */ OSStatus sslEcdsaPeerCurve( SSLPubKey *pubKey, SSL_ECDSA_NamedCurve *namedCurve) { /* Cast is safe because enums are kept in sync. */ *namedCurve = (SSL_ECDSA_NamedCurve)SecECKeyGetNamedCurve(SECKEYREF(pubKey)); if (*namedCurve == kSecECCurveNone) { sslErrorLog("sslEcdsaPeerCurve: no named curve for public key\n"); return errSSLProtocol; } return errSecSuccess; } /* * Generate ECDH key pair with the given SSL_ECDSA_NamedCurve. * Private key, in ref form, is placed in ctx->ecdhPrivate. * Public key, in ECPoint form - which can NOT be used as * a key in any CSP ops - is placed in ecdhExchangePublic. */ OSStatus sslEcdhGenerateKeyPair( SSLContext *ctx, SSL_ECDSA_NamedCurve namedCurve) { OSStatus ortn = errSecSuccess; ccec_const_cp_t cp; switch (namedCurve) { case SSL_Curve_secp256r1: cp = ccec_cp_256(); break; case SSL_Curve_secp384r1: cp = ccec_cp_384(); break; case SSL_Curve_secp521r1: cp = ccec_cp_521(); break; default: /* should not have gotten this far */ sslErrorLog("sslEcdhGenerateKeyPair: bad namedCurve (%u)\n", (unsigned)namedCurve); return errSSLInternal; } ccec_generate_key(cp, CCRNGSTATE, ctx->ecdhContext); size_t pub_size = ccec_export_pub_size(ctx->ecdhContext); SSLFreeBuffer(&ctx->ecdhExchangePublic); require_noerr(ortn = SSLAllocBuffer(&ctx->ecdhExchangePublic, pub_size), errOut); ccec_export_pub(ctx->ecdhContext, ctx->ecdhExchangePublic.data); sslDebugLog("sslEcdhGenerateKeyPair: pub key size=%ld, data=%p\n", pub_size, ctx->ecdhExchangePublic.data); errOut: return ortn; } /* * Perform ECDH key exchange. Obtained key material is the same * size as our private key. * * On entry, ecdhPrivate is our private key. The peer's public key * is either ctx->ecdhPeerPublic for ECDHE exchange, or * ctx->peerPubKey for ECDH exchange. */ OSStatus sslEcdhKeyExchange( SSLContext *ctx, SSLBuffer *exchanged) { OSStatus ortn = errSecSuccess; CFDataRef pubKeyData = NULL; const unsigned char *pubKeyBits; unsigned long pubKeyLen; switch(ctx->selectedCipherSpecParams.keyExchangeMethod) { case SSL_ECDHE_ECDSA: case SSL_ECDHE_RSA: /* public key passed in as CSSM_DATA *Param */ if(ctx->ecdhPeerPublic.length == 0) { /* comes from peer, don't panic */ sslErrorLog("sslEcdhKeyExchange: null peer public key\n"); ortn = errSSLProtocol; goto errOut; } pubKeyBits = ctx->ecdhPeerPublic.data; pubKeyLen = ctx->ecdhPeerPublic.length; break; case SSL_ECDH_ECDSA: case SSL_ECDH_RSA: /* Use the public key provided by the peer. */ if(ctx->peerPubKey == NULL) { sslErrorLog("sslEcdhKeyExchange: no peer key\n"); ortn = errSSLInternal; goto errOut; } pubKeyData = SecECKeyCopyPublicBits(SECKEYREF(ctx->peerPubKey)); if (!pubKeyData) { sslErrorLog("sslEcdhKeyExchange: SecECKeyCopyPublicBits failed\n"); ortn = errSSLProtocol; goto errOut; } pubKeyBits = CFDataGetBytePtr(pubKeyData); pubKeyLen = CFDataGetLength(pubKeyData); break; default: /* shouldn't be here */ sslErrorLog("sslEcdhKeyExchange: unknown keyExchangeMethod (%d)\n", ctx->selectedCipherSpecParams.keyExchangeMethod); assert(0); ortn = errSSLInternal; goto errOut; } ccec_const_cp_t cp = ccec_ctx_cp(ctx->ecdhContext); ccec_pub_ctx_decl(ccn_sizeof(521), pubKey); ccec_import_pub(cp, pubKeyLen, pubKeyBits, pubKey); size_t len = 1 + 2 * ccec_ccn_size(cp); require_noerr(ortn = SSLAllocBuffer(exchanged, len), errOut); require_noerr(ccec_compute_key(ctx->ecdhContext, pubKey, &exchanged->length, exchanged->data), errOut); sslDebugLog("sslEcdhKeyExchange: exchanged key length=%ld, data=%p\n", exchanged->length, exchanged->data); errOut: CFReleaseSafe(pubKeyData); return ortn; }