--- /dev/null
+/*
+ * 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(¶mItem,
+ 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);
+}