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(¶mItem, 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(¶mItem, 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