X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/libsecurity_ssl/lib/sslBER.c diff --git a/libsecurity_ssl/lib/sslBER.c b/libsecurity_ssl/lib/sslBER.c new file mode 100644 index 00000000..df938a6c --- /dev/null +++ b/libsecurity_ssl/lib/sslBER.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) 1999-2001,2005-2007,2010-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@ + */ + +/* + * sslBER.c - BER routines + */ + +#if USE_CDSA_CRYPTO + +#include "ssl.h" +#include "sslMemory.h" +#include "sslDebug.h" +#include "sslBER.h" +#include "sslCrypto.h" + +#include + +#include "appleCdsa.h" +#include "SecureTransportPriv.h" + +#include +#include +#include +#include +#include +#include + +/* we should get rid of this low level stuff and use SecAsn1Coder throughout... */ +#include + +#define SSLBUF_TO_SECITEM(sb, cd) { \ + (cd)->Length = (sb)->length; \ + (cd)->Data = (sb)->data; \ +} + +/* + * Given a PKCS-1 encoded RSA public key, extract the + * modulus and public exponent. + * + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e } + */ + +/* + * Default chunk size for new arena pool. + * FIXME: analyze & measure different defaults here. I'm pretty sure + * that only performance - not correct behavior - is affected by + * an arena pool's chunk size. + */ +#define CHUNKSIZE_DEF 1024 + +OSStatus sslDecodeRsaBlob( + const SSLBuffer *blob, /* PKCS-1 encoded */ + SSLBuffer *modulus, /* data mallocd and RETURNED */ + SSLBuffer *exponent) /* data mallocd and RETURNED */ +{ + SECStatus rv; + OSStatus srtn; + NSS_RSAPublicKeyPKCS1 nssPubKey = {}; + PLArenaPool *pool; + + assert(blob != NULL); + assert(modulus != NULL); + assert(exponent != NULL); + + /* DER-decode the blob */ + pool = PORT_NewArena(CHUNKSIZE_DEF); + rv = SEC_ASN1Decode(pool, &nssPubKey, + kSecAsn1RSAPublicKeyPKCS1Template, (const char *)blob->data, blob->length); + if (rv != SECSuccess) + srtn = errSSLBadCert; + else { + /* malloc & copy components */ + srtn = SSLCopyBufferFromData(nssPubKey.modulus.Data, + nssPubKey.modulus.Length, modulus); + if(!srtn) { + srtn = SSLCopyBufferFromData(nssPubKey.publicExponent.Data, + nssPubKey.publicExponent.Length, exponent); + } + } + PORT_FreeArena(pool, PR_TRUE); + return srtn; +} + +/* + * Given a raw modulus and exponent, cook up a + * BER-encoded RSA public key blob. + */ +OSStatus sslEncodeRsaBlob( + const SSLBuffer *modulus, + const SSLBuffer *exponent, + SSLBuffer *blob) /* data mallocd and RETURNED */ +{ + PLArenaPool *pool; + OSStatus srtn; + SECItem *encBlob, dest = {}; + NSS_RSAPublicKeyPKCS1 nssPubKey; + + assert((modulus != NULL) && (exponent != NULL)); + + /* convert to NSS_RSAPublicKeyPKCS1 */ + SSLBUF_TO_SECITEM(modulus, &nssPubKey.modulus); + SSLBUF_TO_SECITEM(exponent, &nssPubKey.publicExponent); + + /* DER encode */ + pool = PORT_NewArena(CHUNKSIZE_DEF); + encBlob = SEC_ASN1EncodeItem(pool, &dest, &nssPubKey, + kSecAsn1RSAPublicKeyPKCS1Template); + if (!encBlob) + srtn = memFullErr; + else { + /* copy out to caller */ + srtn = SSLCopyBufferFromData(encBlob->Data, encBlob->Length, blob); + } + + PORT_FreeArena(pool, PR_TRUE); + return srtn; +} + +#if APPLE_DH +/* + * Given a DER encoded DHParameterBlock, extract the prime and generator. + * modulus and public exponent. + * This will work with either PKCS-1 encoded DHParameterBlock or + * openssl-style DHParameter. + */ +OSStatus sslDecodeDhParams( + const SSLBuffer *blob, /* PKCS-1 encoded */ + SSLBuffer *prime, /* data mallocd and RETURNED */ + SSLBuffer *generator) /* data mallocd and RETURNED */ +{ + SECStatus rv; + OSStatus srtn; + NSS_DHParameterBlock paramBlock = {}; + PLArenaPool *pool; + + assert(blob != NULL); + assert(prime != NULL); + assert(generator != NULL); + + pool = PORT_NewArena(CHUNKSIZE_DEF); + /* + * Since the common case here is to decode a parameter block coming + * over the wire, which is in openssl format, let's try that format first. + */ + rv = SEC_ASN1Decode(pool, ¶mBlock.params, + kSecAsn1DHParameterTemplate, (const char *)blob->data, blob->length); + if (rv != SECSuccess) { + /* + * OK, that failed when trying as a CDSA_formatted parameter + * block DHParameterBlock). Openssl uses a subset of that, + * a DHParameter. Try that instead. + */ + memset(¶mBlock, 0, sizeof(paramBlock)); + rv = SEC_ASN1Decode(pool, ¶mBlock, + kSecAsn1DHParameterBlockTemplate, + (const char *)blob->data, blob->length); + } + + if (rv != SECSuccess) { + /* Ah well, we tried. */ + sslErrorLog("sslDecodeDhParams: both CDSA and openssl format" + "failed\n"); + srtn = errSSLCrypto; + } + else { + /* copy out components */ + srtn = SSLCopyBufferFromData(paramBlock.params.prime.Data, + paramBlock.params.prime.Length, prime); + if(!srtn) { + srtn = SSLCopyBufferFromData(paramBlock.params.base.Data, + paramBlock.params.base.Length, generator); + } + } + + PORT_FreeArena(pool, PR_TRUE); + return srtn; +} + +/* + * Given a prime and generator, cook up a BER-encoded DHParameter blob. + */ +OSStatus sslEncodeDhParams( + const SSLBuffer *prime, + const SSLBuffer *generator, + SSLBuffer *blob) /* data mallocd and RETURNED */ +{ + PLArenaPool *pool; + OSStatus srtn; + SECItem *encBlob, dest = {}; + NSS_DHParameter dhParams; + + assert((prime != NULL) && (generator != NULL)); + + /* convert to NSS_DHParameter */ + SSLBUF_TO_SECITEM(prime, &dhParams.prime); + SSLBUF_TO_SECITEM(generator, &dhParams.base); + dhParams.privateValueLength.Data = NULL; + dhParams.privateValueLength.Length = 0; + + /* DER encode */ + pool = PORT_NewArena(CHUNKSIZE_DEF); + encBlob = SEC_ASN1EncodeItem(pool, &dest, &dhParams, + kSecAsn1DHParameterTemplate); + if (!encBlob) + srtn = memFullErr; + else { + /* copy out to caller */ + srtn = SSLCopyBufferFromData(encBlob->Data, encBlob->Length, blob); + } + + PORT_FreeArena(pool, PR_TRUE); + return srtn; +} +#endif /* APPLE_DH */ + +/* + * Given an ECDSA key in CSSM format, extract the SSL_ECDSA_NamedCurve + * from its algorithm parameters. + */ +OSStatus sslEcdsaPeerCurve( + CSSM_KEY_PTR pubKey, + SSL_ECDSA_NamedCurve *namedCurve) +{ + SecAsn1CoderRef coder = NULL; + CSSM_X509_SUBJECT_PUBLIC_KEY_INFO subjPubKeyInfo; + CSSM_X509_ALGORITHM_IDENTIFIER *algId = &subjPubKeyInfo.algorithm; + CSSM_OID curveOid; + OSStatus ortn; + + CSSM_KEYHEADER *hdr = &pubKey->KeyHeader; + if(hdr->AlgorithmId != CSSM_ALGID_ECDSA) { + sslErrorLog("sslEcdsaPeerCurve: bad peer key algorithm\n"); + return errSSLProtocol; + } + if(hdr->BlobType != CSSM_KEYBLOB_RAW) { + /* No can do - this must be raw format, it came from the CL */ + sslErrorLog("sslEcdsaPeerCurve: bad peer key algorithm\n"); + return errSSLProtocol; + } + if(hdr->Format != CSSM_KEYBLOB_RAW_FORMAT_X509) { + sslErrorLog("sslEcdsaPeerCurve: bad peer key format\n"); + return errSSLProtocol; + } + + /* KeyData is an encoded CSSM_X509_SUBJECT_PUBLIC_KEY_INFO */ + ortn = SecAsn1CoderCreate(&coder); + if(ortn) { + return errSSLInternal; + } + /* subsequent errors to errOut: */ + + memset(&subjPubKeyInfo, 0, sizeof(subjPubKeyInfo)); + ortn = SecAsn1DecodeData(coder, &pubKey->KeyData, kSecAsn1SubjectPublicKeyInfoTemplate, + &subjPubKeyInfo); + if(ortn) { + printf("sslEcdsaPeerCurve: error decoding public key\n"); + goto errOut; + } + + if(!nssCompareCssmData(&algId->algorithm, &CSSMOID_ecPublicKey)) { + printf("sslEcdsaPeerCurve: unexpected algorithm ID in public key\n"); + ortn = errSSLProtocol; + goto errOut; + } + if((algId->parameters.Data[0] != BER_TAG_OID) || + (algId->parameters.Length < 2)) { + printf("sslEcdsaPeerCurve: missing algorithm parameters in public key\n"); + ortn = errSSLProtocol; + goto errOut; + } + + /* + * The curve OID is DER-encoded since the parameters are ASN_ANY. + * Quickie decode for further processing... + */ + curveOid.Data = algId->parameters.Data + 2; + curveOid.Length = algId->parameters.Length - 2; + + /* algId->parameters is the curve OID */ + if(nssCompareCssmData(&curveOid, &CSSMOID_secp256r1)) { + *namedCurve = SSL_Curve_secp256r1; + } + else if(nssCompareCssmData(&curveOid, &CSSMOID_secp384r1)) { + *namedCurve = SSL_Curve_secp384r1; + } + else if(nssCompareCssmData(&curveOid, &CSSMOID_secp521r1)) { + *namedCurve = SSL_Curve_secp521r1; + } + /* Others? Later. That's all we support for now. */ + else { + printf("sslEcdsaPeerCurve: missing algorithm parameters in public key\n"); + ortn = errSSLProtocol; + } + +errOut: + SecAsn1CoderRelease(coder); + return ortn; +} + +/* + * Given an ECDSA public key in X509 format, extract the raw public key + * bits in ECPOint format. + */ +OSStatus sslEcdsaPubKeyBits( + CSSM_KEY_PTR pubKey, + SSLBuffer *pubBits) /* data mallocd and RETURNED */ +{ + SecAsn1CoderRef coder = NULL; + CSSM_X509_SUBJECT_PUBLIC_KEY_INFO subjPubKeyInfo; + OSStatus ortn = noErr; + + CSSM_KEYHEADER *hdr = &pubKey->KeyHeader; + if(hdr->AlgorithmId != CSSM_ALGID_ECDSA) { + sslErrorLog("sslEcdsaPubKeyBits: bad peer key algorithm\n"); + return errSSLProtocol; + } + if(hdr->BlobType != CSSM_KEYBLOB_RAW) { + /* No can do - this must be raw format, it came from the CL */ + sslErrorLog("sslEcdsaPubKeyBits: bad peer key algorithm\n"); + return errSSLProtocol; + } + if(hdr->Format != CSSM_KEYBLOB_RAW_FORMAT_X509) { + sslErrorLog("sslEcdsaPubKeyBits: bad peer key format\n"); + return errSSLProtocol; + } + + /* KeyData is an encoded CSSM_X509_SUBJECT_PUBLIC_KEY_INFO */ + ortn = SecAsn1CoderCreate(&coder); + if(ortn) { + return errSSLInternal; + } + /* subsequent errors to errOut: */ + + memset(&subjPubKeyInfo, 0, sizeof(subjPubKeyInfo)); + ortn = SecAsn1DecodeData(coder, &pubKey->KeyData, kSecAsn1SubjectPublicKeyInfoTemplate, + &subjPubKeyInfo); + if(ortn) { + printf("sslEcdsaPubKeyBits: error decoding public key\n"); + goto errOut; + } + /* that key data is a BITSTRING */ + ortn = SSLCopyBufferFromData(subjPubKeyInfo.subjectPublicKey.Data, + subjPubKeyInfo.subjectPublicKey.Length >> 3, pubBits); +errOut: + SecAsn1CoderRelease(coder); + return ortn; +} + +#endif /* USE_CDSA_CRYPTO */