]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/SecDH.c
Security-59754.41.1.tar.gz
[apple/security.git] / OSX / sec / Security / 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 }