]> git.saurik.com Git - apple/security.git/blobdiff - sec/Security/SecDH.c
Security-55163.44.tar.gz
[apple/security.git] / sec / Security / SecDH.c
diff --git a/sec/Security/SecDH.c b/sec/Security/SecDH.c
new file mode 100644 (file)
index 0000000..0071d46
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2007-2008,2010 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@
+ */
+
+/*
+ * SecDH.c - Implement the crypto required for a Diffie-Hellman key exchange.
+ */
+
+#include "SecDH.h"
+#include <libDER/DER_Keys.h>
+#include <corecrypto/ccdh.h>
+#include <libDER/DER_Keys.h>
+#include <libDER/DER_Encode.h>
+#include <libDER/asn1Types.h>
+#include <libkern/OSByteOrder.h>
+#include <security_utilities/debugging.h>
+#include <Security/SecInternal.h>
+#include <Security/SecRandom.h>
+#include <stdlib.h>
+#include <MacErrors.h>
+#include <Security/SecBasePriv.h>
+
+#ifdef DEBUG
+#define DH_DEBUG  1
+#endif
+
+static inline ccdh_gp_t SecDH_gp(SecDHContext dh)
+{
+    void *p = dh;
+    ccdh_gp_t gp = { .gp = p };
+    return gp;
+}
+
+static inline ccdh_full_ctx_t SecDH_priv(SecDHContext dh)
+{
+    void *p = dh;
+    cczp_t zp = { .u = p };
+    cc_size s = ccn_sizeof_n(cczp_n(zp));
+    ccdh_full_ctx_t priv = { .hdr = (struct ccdh_ctx_header *)(p+ccdh_gp_size(s)) };
+    return priv;
+}
+
+static inline size_t SecDH_context_size(size_t p_len)
+{
+    cc_size n = ccn_nof_size(p_len);
+    cc_size real_p_len = ccn_sizeof_n(n);
+    size_t context_size = ccdh_gp_size(real_p_len)+ccdh_full_ctx_size(real_p_len);
+    return context_size;
+}
+
+/* Shared static functions. */
+
+static OSStatus
+der2OSStatus(DERReturn derReturn)
+{
+       switch(derReturn)
+       {
+       case DR_Success:                                return noErr;
+       case DR_EndOfSequence:                  return errSecDecode; 
+       case DR_UnexpectedTag:                  return errSecDecode;
+       case DR_DecodeError:                    return errSecDecode;
+       case DR_Unimplemented:                  return unimpErr;
+       case DR_IncompleteSeq:                  return errSecDecode;
+       case DR_ParamErr:                               return paramErr;
+       case DR_BufOverflow:                    return errSecBufferTooSmall;
+       default:                                                return errSecInternal;
+       }
+}
+
+static int dhRngCallback(struct ccrng_state *rng, unsigned long outlen, void *out)
+{
+    return SecRandomCopyBytes(kSecRandomDefault, outlen, out);
+}
+
+static struct ccrng_state dhrng = {
+    .generate = dhRngCallback
+};
+
+OSStatus SecDHCreate(uint32_t g, const uint8_t *p, size_t p_len,
+       uint32_t l, const uint8_t *recip, size_t recip_len, SecDHContext *pdh)
+{
+    cc_size n = ccn_nof_size(p_len);
+    size_t context_size = SecDH_context_size(p_len);
+    void *context = malloc(context_size);
+    bzero(context, context_size);
+
+    ccdh_gp_t gp;
+    gp.gp = context;
+
+    CCDH_GP_N(gp) = n;
+    CCDH_GP_L(gp) = l;
+
+    if(ccn_read_uint(n, CCDH_GP_PRIME(gp), p_len, p))
+        goto errOut;
+    if(recip) {
+        if(ccn_read_uint(n+1, CCDH_GP_RECIP(gp), recip_len, recip))
+            goto errOut;
+        gp.zp.zp->mod_prime = cczp_mod;
+    } else {
+        cczp_init(gp.zp);
+    };
+    ccn_seti(n, CCDH_GP_G(gp), g);
+
+    *pdh = (SecDHContext) context;
+
+    return noErr;
+
+errOut:
+    SecDHDestroy(context);
+    *pdh = NULL;
+    return errSecInternal;
+
+}
+
+/* this used to be in libgDH */
+/*
+ * Support for encoding and decoding DH parameter blocks.
+ * Apple form encodes the reciprocal of the prime p.
+ */
+/* PKCS3, Openssl compatible */
+typedef struct {
+       DERItem                         p;
+       DERItem                         g;
+       DERItem                         l;
+       DERItem                         recip; /* Only used in Apple Custom blocks. */
+} 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 },
+    /* Not part of PKCS3 per-se, but we add it on just for kicks.  Since
+     it's optional we will automatically decode any apple specific
+     params, but we won't add this section unless the caller asks
+     us to.  */
+       { DER_OFFSET(DER_DHParams, recip),
+        ASN1_PRIVATE | ASN1_PRIMITIVE | 0,
+        DER_DEC_OPTIONAL | DER_ENC_SIGNED_INT },
+};
+static const DERSize DER_NumDHParamsItemSpecs =
+sizeof(DER_DHParamsItemSpecs) / sizeof(DERItemSpec);
+
+
+OSStatus SecDHCreateFromParameters(const uint8_t *params,
+       size_t params_len, SecDHContext *pdh)
+{
+    DERReturn drtn;
+       DERItem paramItem = {(DERByte *)params, params_len};
+       DER_DHParams decodedParams;
+    uint32_t l;
+
+    drtn = DERParseSequence(&paramItem,
+                            DER_NumDHParamsItemSpecs, DER_DHParamsItemSpecs,
+                            &decodedParams, sizeof(decodedParams));
+    if(drtn)
+        return drtn;
+
+    drtn = DERParseInteger(&decodedParams.l, &l);
+    if(drtn)
+        return drtn;
+    cc_size n = ccn_nof_size(decodedParams.p.length);
+    cc_size p_len = ccn_sizeof_n(n);
+    size_t context_size = ccdh_gp_size(p_len)+ccdh_full_ctx_size(p_len);
+    void *context = malloc(context_size);
+    if(context==NULL)
+        return memFullErr;
+
+    bzero(context, context_size);
+
+    ccdh_gp_t gp;
+    gp.gp = context;
+
+    CCDH_GP_N(gp) = n;
+    CCDH_GP_L(gp) = l;
+
+    if(ccn_read_uint(n, CCDH_GP_PRIME(gp), decodedParams.p.length, decodedParams.p.data))
+        goto errOut;
+    if(decodedParams.recip.length) {
+        if(ccn_read_uint(n+1, CCDH_GP_RECIP(gp), decodedParams.recip.length, decodedParams.recip.data))
+            goto errOut;
+        gp.zp.zp->mod_prime = cczp_mod;
+    } else {
+        cczp_init(gp.zp);
+    };
+
+    if(ccn_read_uint(n, CCDH_GP_G(gp), decodedParams.g.length, decodedParams.g.data))
+        goto errOut;
+
+    *pdh = (SecDHContext) context;
+    return noErr;
+
+errOut:
+    SecDHDestroy(context);
+    *pdh = NULL;
+    return errSecInvalidKey;
+}
+
+OSStatus SecDHCreateFromAlgorithmId(const uint8_t *alg, size_t alg_len,
+       SecDHContext *pdh) {
+       DERAlgorithmId algorithmId;
+       DERItem algId;
+
+       algId.data = (uint8_t *)alg;
+       algId.length = alg_len;
+
+       DERReturn drtn = DERParseSequence(&algId,
+               DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs,
+               &algorithmId, sizeof(algorithmId));
+       if (drtn != DR_Success)
+               return der2OSStatus(drtn);
+
+    return SecDHCreateFromParameters(algorithmId.params.data,
+               algorithmId.params.length, pdh);
+}
+
+size_t SecDHGetMaxKeyLength(SecDHContext dh) {
+    cczp_const_t zp;
+    zp.u = (cc_unit *)dh;
+
+    return ccn_sizeof_n(cczp_n(zp));
+}
+
+
+OSStatus SecDHGenerateKeypair(SecDHContext dh, uint8_t *pub_key,
+       size_t *pub_key_len)
+{
+    int result;
+    ccdh_gp_t gp = SecDH_gp(dh);
+    ccdh_full_ctx_t priv = SecDH_priv(dh);
+
+    if((result = ccdh_generate_key(gp, &dhrng, priv)))
+        return result;
+
+    /* output y as a big endian byte buffer */
+    size_t ylen = ccn_write_uint_size(ccdh_gp_n(gp), ccdh_ctx_y(priv));
+    if(*pub_key_len < ylen)
+       return errSecBufferTooSmall;
+    ccn_write_uint(ccdh_gp_n(gp),ccdh_ctx_y(priv), ylen, pub_key);
+    *pub_key_len = ylen;
+
+    return noErr;
+}
+
+OSStatus SecDHComputeKey(SecDHContext dh,
+       const uint8_t *pub_key, size_t pub_key_len,
+    uint8_t *computed_key, size_t *computed_key_len)
+{
+    ccdh_gp_t gp = SecDH_gp(dh);
+    ccdh_full_ctx_t priv = SecDH_priv(dh);
+    ccdh_pub_ctx_decl_gp(gp, pub);
+    cc_size n = ccdh_gp_n(gp);
+    cc_unit r[n];
+
+    if(ccdh_import_pub(gp, pub_key_len, pub_key, pub))
+        return errSecInvalidKey;
+
+    if(ccdh_compute_key(priv, pub, r))
+        return errSecInvalidKey;
+
+    ccn_write_uint(n, r, *computed_key_len, computed_key);
+    size_t out_size = ccn_write_uint_size(n, r);
+    if(out_size < *computed_key_len)
+        *computed_key_len=out_size;
+
+    return noErr;
+}
+
+void SecDHDestroy(SecDHContext dh) {
+       /* Zero out key material. */
+    ccdh_gp_t gp = SecDH_gp(dh);
+    cc_size p_len = ccn_sizeof_n(ccdh_gp_n(gp));
+    size_t context_size = SecDH_context_size(p_len);
+
+    bzero(dh, context_size);
+    free(dh);
+}