/ OSX / sec / Security / SecDH.c
SecDH.c
  1  /*
  2   * Copyright (c) 2007-2008,2010,2012-2013 Apple Inc. All Rights Reserved.
  3   * 
  4   * @APPLE_LICENSE_HEADER_START@
  5   * 
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   * 
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   * 
 21   * @APPLE_LICENSE_HEADER_END@
 22   */
 23  
 24  /*
 25   * SecDH.c - Implement the crypto required for a Diffie-Hellman key exchange.
 26   */
 27  
 28  #include "SecDH.h"
 29  #include <libDER/DER_Keys.h>
 30  #include <corecrypto/ccdh.h>
 31  #include <libDER/DER_Keys.h>
 32  #include <libDER/DER_Encode.h>
 33  #include <libDER/asn1Types.h>
 34  #include <libkern/OSByteOrder.h>
 35  #include "utilities/debugging.h"
 36  #include <Security/SecInternal.h>
 37  #include <Security/SecRandom.h>
 38  #include <stdlib.h>
 39  #include <Security/SecBase.h>
 40  #include <Security/SecBasePriv.h>
 41  
 42  #ifdef DEBUG
 43  #define DH_DEBUG  1
 44  #endif
 45  
 46  /* SecDHContext memory layout
 47  +-----------------+
 48  | ccdh_gp         |
 49  +-----------------+
 50  | ccdh_full_ctx   |
 51  +-----------------+
 52  */
 53  
 54  static inline ccdh_gp_t SecDH_gp(SecDHContext dh)
 55  {
 56      return (ccdh_gp_t)dh;
 57  }
 58  
 59  static inline ccdh_full_ctx_t SecDH_priv(SecDHContext dh)
 60  {
 61      ccdh_gp_t gp = SecDH_gp(dh);
 62      cc_size s = ccn_sizeof_n(ccdh_gp_n(gp));
 63      return (ccdh_full_ctx_t)cc_pad_align((uintptr_t)dh + ccdh_gp_size(s));
 64  }
 65  
 66  size_t SecDHGetMaxKeyLength(SecDHContext dh) {
 67  
 68      ccdh_gp_t gp = SecDH_gp(dh);
 69      return ccn_sizeof_n(ccdh_gp_n(gp));
 70  }
 71  
 72  static inline size_t SecDH_context_size(size_t p_len)
 73  {
 74      cc_size real_p_len = ccn_sizeof_size(p_len);
 75  
 76      // Add padding to allow proper alignment of the ccdh_full_ctx.
 77      return ccdh_gp_size(real_p_len) + (CC_MAX_ALIGNMENT - 1) + ccdh_full_ctx_size(real_p_len);
 78  }
 79  
 80  /* Shared static functions. */
 81  
 82  static OSStatus
 83  der2OSStatus(DERReturn derReturn)
 84  {
 85  	switch(derReturn)
 86  	{
 87  	case DR_Success:				return errSecSuccess;
 88  	case DR_EndOfSequence:			return errSecDecode; 
 89  	case DR_UnexpectedTag:			return errSecDecode;
 90  	case DR_DecodeError:			return errSecDecode;
 91  	case DR_Unimplemented:			return errSecUnimplemented;
 92  	case DR_IncompleteSeq:			return errSecDecode;
 93  	case DR_ParamErr:				return errSecParam;
 94  	case DR_BufOverflow:			return errSecBufferTooSmall;
 95  	default:						return errSecInternal;
 96  	}
 97  }
 98  
 99  static int dhRngCallback(struct ccrng_state *rng, unsigned long outlen, void *out)
100  {
101      return SecRandomCopyBytes(kSecRandomDefault, outlen, out);
102  }
103  
104  static struct ccrng_state dhrng = {
105      .generate = dhRngCallback
106  };
107  
108  OSStatus SecDHCreate(uint32_t g, const uint8_t *p, size_t p_len,
109  	uint32_t l, const uint8_t *recip, size_t recip_len, SecDHContext *pdh)
110  {
111      cc_size n = ccn_nof_size(p_len);
112      size_t context_size = SecDH_context_size(p_len);
113      void *context = malloc(context_size);
114      cc_clear(context_size, context);
115  
116      ccdh_gp_t gp;
117      gp  = context;
118  
119      CCDH_GP_N(gp) = n;
120      CCDH_GP_L(gp) = l;
121  
122      if(ccn_read_uint(n, CCDH_GP_PRIME(gp), p_len, p))
123          goto errOut;
124      if(recip) {
125          if(ccn_read_uint(n+1, CCDH_GP_RECIP(gp), recip_len, recip))
126              goto errOut;
127          cczp_init_with_recip(CCDH_GP_ZP(gp), CCDH_GP_RECIP(gp));
128      } else if (cczp_init(CCDH_GP_ZP(gp))) {
129          goto errOut;
130      }
131      ccn_seti(n, CCDH_GP_G(gp), g);
132  
133      *pdh = (SecDHContext) context;
134  
135      return errSecSuccess;
136  
137  errOut:
138      SecDHDestroy(context);
139      *pdh = NULL;
140      return errSecInternal;
141  
142  }
143  
144  /* this used to be in libgDH */
145  /*
146   * Support for encoding and decoding DH parameter blocks.
147   * Apple form encodes the reciprocal of the prime p.
148   */
149  /* PKCS3, Openssl compatible */
150  typedef struct {
151  	DERItem				p;
152  	DERItem				g;
153  	DERItem				l;
154  	DERItem				recip; /* Only used in Apple Custom blocks. */
155  } DER_DHParams;
156  
157  static const DERItemSpec DER_DHParamsItemSpecs[] =
158  {
159  	{ DER_OFFSET(DER_DHParams, p),
160          ASN1_INTEGER,
161          DER_DEC_NO_OPTS | DER_ENC_SIGNED_INT },
162  	{ DER_OFFSET(DER_DHParams, g),
163          ASN1_INTEGER,
164          DER_DEC_NO_OPTS | DER_ENC_SIGNED_INT },
165  	{ DER_OFFSET(DER_DHParams, l),
166          ASN1_INTEGER,
167          DER_DEC_OPTIONAL | DER_ENC_SIGNED_INT },
168      /* Not part of PKCS3 per-se, but we add it on just for kicks.  Since
169       it's optional we will automatically decode any apple specific
170       params, but we won't add this section unless the caller asks
171       us to.  */
172  	{ DER_OFFSET(DER_DHParams, recip),
173          ASN1_PRIVATE | ASN1_PRIMITIVE | 0,
174          DER_DEC_OPTIONAL | DER_ENC_SIGNED_INT },
175  };
176  static const DERSize DER_NumDHParamsItemSpecs =
177  sizeof(DER_DHParamsItemSpecs) / sizeof(DERItemSpec);
178  
179  
180  OSStatus SecDHCreateFromParameters(const uint8_t *params,
181  	size_t params_len, SecDHContext *pdh)
182  {
183      // We support DomainParameters as specified in PKCS#3
184      // (http://www.emc.com/emc-plus/rsa-labs/standards-initiatives/pkcs-3-diffie-hellman-key-agreement-standar.htm)
185      // DHParameter ::= SEQUENCE {
186      //   prime INTEGER, -- p
187      //   base INTEGER, -- g
188      //   privateValueLength INTEGER OPTIONAL }
189  
190      DERReturn drtn;
191  	DERItem paramItem = {(DERByte *)params, params_len};
192  	DER_DHParams decodedParams;
193      uint32_t l = 0;
194  
195      drtn = DERParseSequence(&paramItem,
196                              DER_NumDHParamsItemSpecs, DER_DHParamsItemSpecs,
197                              &decodedParams, sizeof(decodedParams));
198      if(drtn)
199          return drtn;
200  
201      if (decodedParams.l.length > 0) {
202          drtn = DERParseInteger(&decodedParams.l, &l);
203          if(drtn)
204              return drtn;
205      }
206      cc_size n = ccn_nof_size(decodedParams.p.length);
207      cc_size p_len = ccn_sizeof_n(n);
208      size_t context_size = SecDH_context_size(p_len);
209      void *context = malloc(context_size);
210      if(context==NULL)
211          return errSecAllocate;
212  
213      bzero(context, context_size);
214  
215      ccdh_gp_t gp = context;
216  
217      CCDH_GP_N(gp) = n;
218      CCDH_GP_L(gp) = l;
219  
220      if(ccn_read_uint(n, CCDH_GP_PRIME(gp), decodedParams.p.length, decodedParams.p.data))
221          goto errOut;
222      if(decodedParams.recip.length) {
223          if(ccn_read_uint(n+1, CCDH_GP_RECIP(gp), decodedParams.recip.length, decodedParams.recip.data))
224              goto errOut;
225          cczp_init_with_recip(CCDH_GP_ZP(gp), CCDH_GP_RECIP(gp));
226      } else if (cczp_init(CCDH_GP_ZP(gp))) {
227          goto errOut;
228      }
229  
230      if(ccn_read_uint(n, CCDH_GP_G(gp), decodedParams.g.length, decodedParams.g.data))
231          goto errOut;
232  
233      *pdh = (SecDHContext) context;
234      return errSecSuccess;
235  
236  errOut:
237      SecDHDestroy(context);
238      *pdh = NULL;
239      return errSecInvalidKey;
240  }
241  
242  OSStatus SecDHCreateFromAlgorithmId(const uint8_t *alg, size_t alg_len,
243  	SecDHContext *pdh) {
244  	DERAlgorithmId algorithmId;
245  	DERItem algId;
246  
247  	algId.data = (uint8_t *)alg;
248  	algId.length = alg_len;
249  
250  	DERReturn drtn = DERParseSequence(&algId,
251  		DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs,
252  		&algorithmId, sizeof(algorithmId));
253  	if (drtn != DR_Success)
254  		return der2OSStatus(drtn);
255  
256      return SecDHCreateFromParameters(algorithmId.params.data,
257  		algorithmId.params.length, pdh);
258  }
259  
260  OSStatus SecDHGenerateKeypair(SecDHContext dh, uint8_t *pub_key,
261  	size_t *pub_key_len)
262  {
263      int result;
264      ccdh_gp_t gp = SecDH_gp(dh);
265      ccdh_full_ctx_t priv = SecDH_priv(dh);
266  
267      if((result = ccdh_generate_key(gp, &dhrng, priv)))
268          return result;
269  
270      /* output y as a big endian byte buffer */
271      size_t ylen = ccn_write_uint_size(ccdh_gp_n(gp), ccdh_ctx_y(priv));
272      if(*pub_key_len < ylen)
273         return errSecBufferTooSmall;
274      ccn_write_uint(ccdh_gp_n(gp),ccdh_ctx_y(priv), ylen, pub_key);
275      *pub_key_len = ylen;
276  
277      return errSecSuccess;
278  }
279  
280  OSStatus SecDHComputeKey(SecDHContext dh,
281  	const uint8_t *pub_key, size_t pub_key_len,
282      uint8_t *computed_key, size_t *computed_key_len)
283  {
284      ccdh_gp_t gp = SecDH_gp(dh);
285      ccdh_full_ctx_t priv = SecDH_priv(dh);
286      ccdh_pub_ctx_decl_gp(gp, pub);
287      cc_size n = ccdh_gp_n(gp);
288      cc_unit r[n];
289  
290      if(ccdh_import_pub(gp, pub_key_len, pub_key, pub))
291          return errSecInvalidKey;
292  
293      //ccdh_compute_shared_secret() cannot be used directly, because it doesn't allow truncated output. Buffering is needed.
294      if(ccdh_compute_key(priv, pub, r))
295          return errSecInvalidKey;
296  
297      ccn_write_uint(n, r, *computed_key_len, computed_key);
298      size_t out_size = ccn_write_uint_size(n, r);
299      if(out_size < *computed_key_len)
300          *computed_key_len=out_size;
301  
302      return errSecSuccess;
303  }
304  
305  void SecDHDestroy(SecDHContext dh) {
306  	/* Zero out key material. */
307      ccdh_gp_t gp = SecDH_gp(dh);
308      cc_size p_len = ccn_sizeof_n(ccdh_gp_n(gp));
309      size_t context_size = SecDH_context_size(p_len);
310  
311      cc_clear(context_size, dh);
312      free(dh);
313  }
314  
315  /* Max encoded size for standard (PKCS3) parameters */
316  #define DH_ENCODED_PARAM_SIZE(primeSizeInBytes)					\
317  DER_MAX_ENCODED_SIZE(										\
318  DER_MAX_ENCODED_SIZE(primeSizeInBytes) +		/* g */		\
319  DER_MAX_ENCODED_SIZE(primeSizeInBytes) +		/* p */     \
320  DER_MAX_ENCODED_SIZE(4))                        /* l */
321  
322  
323  OSStatus SecDHEncodeParams(CFDataRef g, CFDataRef p,
324                             CFDataRef l, CFDataRef recip,
325                             CFDataRef *params)
326  {
327      OSStatus ortn;
328      DER_DHParams derParams =
329      {
330          .p = {
331              .length = CFDataGetLength(p),
332              .data = (DERByte *)CFDataGetBytePtr(p),
333          },
334          .g = {
335              .length = CFDataGetLength(g),
336              .data = (DERByte *)CFDataGetBytePtr(g),
337          },
338          .l = {
339              .length = l?CFDataGetLength(l):0,
340              .data = (DERByte *)(l?CFDataGetBytePtr(l):NULL),
341          },
342          .recip = {
343              .length = recip?CFDataGetLength(recip):0,
344              .data = (DERByte *)(recip?CFDataGetBytePtr(recip):NULL),
345          },
346      };
347  
348      DERSize ioLen = DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE,
349                                                 &derParams,
350                                                 DER_NumDHParamsItemSpecs, DER_DHParamsItemSpecs);
351  
352      DERByte *der = malloc(ioLen);
353      // FIXME: What if this fails - we should probably not have a malloc here ?
354      assert(der);
355      ortn = (int)DEREncodeSequence(ASN1_CONSTR_SEQUENCE,
356                                    &derParams,
357                                    DER_NumDHParamsItemSpecs, DER_DHParamsItemSpecs,
358                                    der,
359                                    &ioLen);
360  
361      if(!ortn && params)
362          *params=CFDataCreate(kCFAllocatorDefault, der, ioLen);
363  
364      // FIXME: we should just allocate the CFDataRef
365  
366      free(der);
367      return ortn;
368  }
369  
370  
371  OSStatus SecDHDecodeParams(CFDataRef *g, CFDataRef *p,
372                             CFDataRef *l, CFDataRef *r,
373                             CFDataRef params)
374  {
375      DERReturn drtn;
376  	DERItem paramItem = {(DERByte *)CFDataGetBytePtr(params), CFDataGetLength(params)};
377  	DER_DHParams decodedParams;
378  
379      drtn = DERParseSequence(&paramItem,
380                              DER_NumDHParamsItemSpecs, DER_DHParamsItemSpecs,
381                              &decodedParams, sizeof(decodedParams));
382      if(drtn)
383          return drtn;
384  
385      if(g) *g=CFDataCreate(kCFAllocatorDefault, decodedParams.g.data, decodedParams.g.length);
386      if(p) *p=CFDataCreate(kCFAllocatorDefault, decodedParams.p.data, decodedParams.p.length);
387      if(l) *l=CFDataCreate(kCFAllocatorDefault, decodedParams.l.data, decodedParams.l.length);
388      if(r) *r=CFDataCreate(kCFAllocatorDefault, decodedParams.recip.data, decodedParams.recip.length);
389  
390      return errSecSuccess;
391  }