]> git.saurik.com Git - apple/security.git/blobdiff - libsecurity_ssl/lib/sslBER.c
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_ssl / lib / sslBER.c
diff --git a/libsecurity_ssl/lib/sslBER.c b/libsecurity_ssl/lib/sslBER.c
new file mode 100644 (file)
index 0000000..df938a6
--- /dev/null
@@ -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 <string.h>
+
+#include "appleCdsa.h"
+#include "SecureTransportPriv.h"
+
+#include <string.h>
+#include <Security/SecAsn1Coder.h>
+#include <Security/keyTemplates.h>
+#include <security_asn1/nssUtils.h>
+#include <Security/oidsattr.h>
+#include <Security/oidsalg.h>
+
+/* we should get rid of this low level stuff and use SecAsn1Coder throughout... */
+#include <security_asn1/secasn1.h>
+
+#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, &paramBlock.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(&paramBlock, 0, sizeof(paramBlock));
+        rv = SEC_ASN1Decode(pool, &paramBlock,
+            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 */