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
46static inline ccdh_gp_t SecDH_gp(SecDHContext dh)
47{
48    void *p = dh;
49    ccdh_gp_t gp = { .gp = p };
50    return gp;
51}
52
53static inline ccdh_full_ctx_t SecDH_priv(SecDHContext dh)
54{
55    void *p = dh;
56    cczp_t zp = { .u = p };
57    cc_size s = ccn_sizeof_n(cczp_n(zp));
58    ccdh_full_ctx_t priv = { .hdr = (struct ccdh_ctx_header *)(p+ccdh_gp_size(s)) };
59    return priv;
60}
61
62static inline size_t SecDH_context_size(size_t p_len)
63{
64    cc_size n = ccn_nof_size(p_len);
65    cc_size real_p_len = ccn_sizeof_n(n);
66    size_t context_size = ccdh_gp_size(real_p_len)+ccdh_full_ctx_size(real_p_len);
67    return context_size;
68}
69
70/* Shared static functions. */
71
72static OSStatus
73der2OSStatus(DERReturn derReturn)
74{
75	switch(derReturn)
76	{
77	case DR_Success:				return errSecSuccess;
78	case DR_EndOfSequence:			return errSecDecode;
79	case DR_UnexpectedTag:			return errSecDecode;
80	case DR_DecodeError:			return errSecDecode;
81	case DR_Unimplemented:			return errSecUnimplemented;
82	case DR_IncompleteSeq:			return errSecDecode;
83	case DR_ParamErr:				return errSecParam;
84	case DR_BufOverflow:			return errSecBufferTooSmall;
85	default:						return errSecInternal;
86	}
87}
88
89static int dhRngCallback(struct ccrng_state *rng, unsigned long outlen, void *out)
90{
91    return SecRandomCopyBytes(kSecRandomDefault, outlen, out);
92}
93
94static struct ccrng_state dhrng = {
95    .generate = dhRngCallback
96};
97
98OSStatus SecDHCreate(uint32_t g, const uint8_t *p, size_t p_len,
99	uint32_t l, const uint8_t *recip, size_t recip_len, SecDHContext *pdh)
100{
101    cc_size n = ccn_nof_size(p_len);
102    size_t context_size = SecDH_context_size(p_len);
103    void *context = malloc(context_size);
104    bzero(context, context_size);
105
106    ccdh_gp_t gp;
107    gp.gp = context;
108
109    CCDH_GP_N(gp) = n;
110    CCDH_GP_L(gp) = l;
111
112    if(ccn_read_uint(n, CCDH_GP_PRIME(gp), p_len, p))
113        goto errOut;
114    if(recip) {
115        if(ccn_read_uint(n+1, CCDH_GP_RECIP(gp), recip_len, recip))
116            goto errOut;
117        gp.zp.zp->mod_prime = cczp_mod;
118    } else {
119        cczp_init(gp.zp);
120    };
121    ccn_seti(n, CCDH_GP_G(gp), g);
122
123    *pdh = (SecDHContext) context;
124
125    return errSecSuccess;
126
127errOut:
128    SecDHDestroy(context);
129    *pdh = NULL;
130    return errSecInternal;
131
132}
133
134/* this used to be in libgDH */
135/*
136 * Support for encoding and decoding DH parameter blocks.
137 * Apple form encodes the reciprocal of the prime p.
138 */
139/* PKCS3, Openssl compatible */
140typedef struct {
141	DERItem				p;
142	DERItem				g;
143	DERItem				l;
144	DERItem				recip; /* Only used in Apple Custom blocks. */
145} DER_DHParams;
146
147static const DERItemSpec DER_DHParamsItemSpecs[] =
148{
149	{ DER_OFFSET(DER_DHParams, p),
150        ASN1_INTEGER,
151        DER_DEC_NO_OPTS | DER_ENC_SIGNED_INT },
152	{ DER_OFFSET(DER_DHParams, g),
153        ASN1_INTEGER,
154        DER_DEC_NO_OPTS | DER_ENC_SIGNED_INT },
155	{ DER_OFFSET(DER_DHParams, l),
156        ASN1_INTEGER,
157        DER_DEC_OPTIONAL | DER_ENC_SIGNED_INT },
158    /* Not part of PKCS3 per-se, but we add it on just for kicks.  Since
159     it's optional we will automatically decode any apple specific
160     params, but we won't add this section unless the caller asks
161     us to.  */
162	{ DER_OFFSET(DER_DHParams, recip),
163        ASN1_PRIVATE | ASN1_PRIMITIVE | 0,
164        DER_DEC_OPTIONAL | DER_ENC_SIGNED_INT },
165};
166static const DERSize DER_NumDHParamsItemSpecs =
167sizeof(DER_DHParamsItemSpecs) / sizeof(DERItemSpec);
168
169
170OSStatus SecDHCreateFromParameters(const uint8_t *params,
171	size_t params_len, SecDHContext *pdh)
172{
173    DERReturn drtn;
174	DERItem paramItem = {(DERByte *)params, params_len};
175	DER_DHParams decodedParams;
176    uint32_t l;
177
178    drtn = DERParseSequence(&paramItem,
179                            DER_NumDHParamsItemSpecs, DER_DHParamsItemSpecs,
180                            &decodedParams, sizeof(decodedParams));
181    if(drtn)
182        return drtn;
183
184    drtn = DERParseInteger(&decodedParams.l, &l);
185    if(drtn)
186        return drtn;
187    cc_size n = ccn_nof_size(decodedParams.p.length);
188    cc_size p_len = ccn_sizeof_n(n);
189    size_t context_size = ccdh_gp_size(p_len)+ccdh_full_ctx_size(p_len);
190    void *context = malloc(context_size);
191    if(context==NULL)
192        return errSecAllocate;
193
194    bzero(context, context_size);
195
196    ccdh_gp_t gp;
197    gp.gp = context;
198
199    CCDH_GP_N(gp) = n;
200    CCDH_GP_L(gp) = l;
201
202    if(ccn_read_uint(n, CCDH_GP_PRIME(gp), decodedParams.p.length, decodedParams.p.data))
203        goto errOut;
204    if(decodedParams.recip.length) {
205        if(ccn_read_uint(n+1, CCDH_GP_RECIP(gp), decodedParams.recip.length, decodedParams.recip.data))
206            goto errOut;
207        gp.zp.zp->mod_prime = cczp_mod;
208    } else {
209        cczp_init(gp.zp);
210    };
211
212    if(ccn_read_uint(n, CCDH_GP_G(gp), decodedParams.g.length, decodedParams.g.data))
213        goto errOut;
214
215    *pdh = (SecDHContext) context;
216    return errSecSuccess;
217
218errOut:
219    SecDHDestroy(context);
220    *pdh = NULL;
221    return errSecInvalidKey;
222}
223
224OSStatus SecDHCreateFromAlgorithmId(const uint8_t *alg, size_t alg_len,
225	SecDHContext *pdh) {
226	DERAlgorithmId algorithmId;
227	DERItem algId;
228
229	algId.data = (uint8_t *)alg;
230	algId.length = alg_len;
231
232	DERReturn drtn = DERParseSequence(&algId,
233		DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs,
234		&algorithmId, sizeof(algorithmId));
235	if (drtn != DR_Success)
236		return der2OSStatus(drtn);
237
238    return SecDHCreateFromParameters(algorithmId.params.data,
239		algorithmId.params.length, pdh);
240}
241
242size_t SecDHGetMaxKeyLength(SecDHContext dh) {
243    cczp_const_t zp;
244    zp.u = (cc_unit *)dh;
245
246    return ccn_sizeof_n(cczp_n(zp));
247}
248
249
250OSStatus SecDHGenerateKeypair(SecDHContext dh, uint8_t *pub_key,
251	size_t *pub_key_len)
252{
253    int result;
254    ccdh_gp_t gp = SecDH_gp(dh);
255    ccdh_full_ctx_t priv = SecDH_priv(dh);
256
257    if((result = ccdh_generate_key(gp, &dhrng, priv)))
258        return result;
259
260    /* output y as a big endian byte buffer */
261    size_t ylen = ccn_write_uint_size(ccdh_gp_n(gp), ccdh_ctx_y(priv));
262    if(*pub_key_len < ylen)
263       return errSecBufferTooSmall;
264    ccn_write_uint(ccdh_gp_n(gp),ccdh_ctx_y(priv), ylen, pub_key);
265    *pub_key_len = ylen;
266
267    return errSecSuccess;
268}
269
270OSStatus SecDHComputeKey(SecDHContext dh,
271	const uint8_t *pub_key, size_t pub_key_len,
272    uint8_t *computed_key, size_t *computed_key_len)
273{
274    ccdh_gp_t gp = SecDH_gp(dh);
275    ccdh_full_ctx_t priv = SecDH_priv(dh);
276    ccdh_pub_ctx_decl_gp(gp, pub);
277    cc_size n = ccdh_gp_n(gp);
278    cc_unit r[n];
279
280    if(ccdh_import_pub(gp, pub_key_len, pub_key, pub))
281        return errSecInvalidKey;
282
283    if(ccdh_compute_key(priv, pub, r))
284        return errSecInvalidKey;
285
286    ccn_write_uint(n, r, *computed_key_len, computed_key);
287    size_t out_size = ccn_write_uint_size(n, r);
288    if(out_size < *computed_key_len)
289        *computed_key_len=out_size;
290
291    return errSecSuccess;
292}
293
294void SecDHDestroy(SecDHContext dh) {
295	/* Zero out key material. */
296    ccdh_gp_t gp = SecDH_gp(dh);
297    cc_size p_len = ccn_sizeof_n(ccdh_gp_n(gp));
298    size_t context_size = SecDH_context_size(p_len);
299
300    bzero(dh, context_size);
301    free(dh);
302}
303
304/* Max encoded size for standard (PKCS3) parameters */
305#define DH_ENCODED_PARAM_SIZE(primeSizeInBytes)					\
306DER_MAX_ENCODED_SIZE(										\
307DER_MAX_ENCODED_SIZE(primeSizeInBytes) +		/* g */		\
308DER_MAX_ENCODED_SIZE(primeSizeInBytes) +		/* p */     \
309DER_MAX_ENCODED_SIZE(4))                        /* l */
310
311
312OSStatus SecDHEncodeParams(CFDataRef g, CFDataRef p,
313                           CFDataRef l, CFDataRef recip,
314                           CFDataRef *params)
315{
316    OSStatus ortn;
317    DER_DHParams derParams =
318    {
319        .p = {
320            .length = CFDataGetLength(p),
321            .data = (DERByte *)CFDataGetBytePtr(p),
322        },
323        .g = {
324            .length = CFDataGetLength(g),
325            .data = (DERByte *)CFDataGetBytePtr(g),
326        },
327        .l = {
328            .length = l?CFDataGetLength(l):0,
329            .data = (DERByte *)(l?CFDataGetBytePtr(l):NULL),
330        },
331        .recip = {
332            .length = recip?CFDataGetLength(recip):0,
333            .data = (DERByte *)(recip?CFDataGetBytePtr(recip):NULL),
334        },
335    };
336
337    DERSize ioLen = DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE,
338                                               &derParams,
339                                               DER_NumDHParamsItemSpecs, DER_DHParamsItemSpecs);
340
341    DERByte *der = malloc(ioLen);
342    // FIXME: What if this fails - we should probably not have a malloc here ?
343    assert(der);
344    ortn = (int)DEREncodeSequence(ASN1_CONSTR_SEQUENCE,
345                                  &derParams,
346                                  DER_NumDHParamsItemSpecs, DER_DHParamsItemSpecs,
347                                  der,
348                                  &ioLen);
349
350    if(!ortn && params)
351        *params=CFDataCreate(kCFAllocatorDefault, der, ioLen);
352
353    // FIXME: we should just allocate the CFDataRef
354
355    free(der);
356    return ortn;
357}
358
359
360OSStatus SecDHDecodeParams(CFDataRef *g, CFDataRef *p,
361                           CFDataRef *l, CFDataRef *r,
362                           CFDataRef params)
363{
364    DERReturn drtn;
365	DERItem paramItem = {(DERByte *)CFDataGetBytePtr(params), CFDataGetLength(params)};
366	DER_DHParams decodedParams;
367
368    drtn = DERParseSequence(&paramItem,
369                            DER_NumDHParamsItemSpecs, DER_DHParamsItemSpecs,
370                            &decodedParams, sizeof(decodedParams));
371    if(drtn)
372        return drtn;
373
374    if(g) *g=CFDataCreate(kCFAllocatorDefault, decodedParams.g.data, decodedParams.g.length);
375    if(p) *p=CFDataCreate(kCFAllocatorDefault, decodedParams.p.data, decodedParams.p.length);
376    if(l) *l=CFDataCreate(kCFAllocatorDefault, decodedParams.l.data, decodedParams.l.length);
377    if(r) *r=CFDataCreate(kCFAllocatorDefault, decodedParams.recip.data, decodedParams.recip.length);
378
379    return errSecSuccess;
380}
381