1/* 2 * Copyright (c) 2011-12 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#include "ossl-config.h" 25 26#include <stdio.h> 27#include <stdlib.h> 28 29#include "rfc2459_asn1.h" 30 31#include "ossl-dh.h" 32#include "ossl-common.h" 33 34#ifdef HAVE_COMMONCRYPTO_COMMONDH_H 35 36 37/* XXX <rdar://problem/10771188> is blocking this code from working */ 38#ifdef PR_10771188_FIXED 39#include <CommonCrypto/CommonDH.h> 40#include <CommonCrypto/CommonBigNum.h> 41 42/* 43 * XXX we need the following structs from CommonDHPriv.h 44 * given the missing bits in the CommonDH SPI. See the 45 * other XXX's below for the details. 46 */ 47typedef struct DHParms_ { 48 CCBigNumRef p; 49 CCBigNumRef g; 50 uint32_t l; 51} DHParmSet; 52 53typedef struct DH_ { 54 DHParmSet parms; 55 CCBigNumRef pub_key; 56 CCBigNumRef priv_key; 57} *CCDH; 58 59/* 60 * 61 */ 62static /* CCHRef */ CCDH 63loadkeys(DH *dh) 64{ 65 void *p = NULL, *g = NULL, *priv_key = NULL, *pub_key = NULL; 66 int plen, glen, priv_keylen, pub_keylen; 67 /* CCHRef */ CCDH ccdh = NULL; 68 CCDHParameters dhparams = NULL; 69 70 plen = BN_num_bytes(dh->p); 71 glen = BN_num_bytes(dh->g); 72 73 p = malloc(plen); 74 g = malloc(glen); 75 if ((NULL == p) || (NULL == g)) { 76 goto err; 77 } 78 79 if (!BN_bn2bin(dh->p, (unsigned char *)p)) { 80 goto err; 81 } 82 if (!BN_bn2bin(dh->g, (unsigned char *)g)) { 83 goto err; 84 } 85 86 dhparams = CCDHCreateParametersFromData(p, plen, g, glen); 87 if (NULL == dhparams) { 88 goto err; 89 } 90 91 ccdh = (CCDH)CCDHCreate(dhparams); 92 if (NULL == ccdh) { 93 goto err; 94 } 95 96 /* 97 * XXX doesn't properly initialize priv_key and pub_key. 98 * See <rdar://problem/10771223> for more information. 99 * The following is the workaround for now. 100 */ 101 ccdh->pub_key = NULL; 102 ccdh->priv_key = NULL; 103 104 /* XXX there is no SPI call to use a provided private key 105 * See <rdar://problem/10771283> for more info. 106 */ 107 if (NULL != dh->priv_key) { 108 CCStatus bnstatus; 109 110 /* we have a private key provided */ 111 priv_keylen = BN_num_bytes(dh->priv_key); 112 priv_key = malloc(priv_keylen); 113 if (NULL == priv_key) { 114 goto err; 115 } 116 if (!BN_bn2bin(dh->priv_key, (unsigned char *)priv_key)) { 117 goto err; 118 } 119 if ((ccdh->priv_key = CCBigNumFromData(&bnstatus, 120 priv_key, priv_keylen)) == NULL) { 121 goto err; 122 } 123 } 124 125 /* XXX there is no SPI call to use a provided public key 126 * See <rdar://problem/10771283> for more info. 127 */ 128 if (NULL != dh->pub_key) { 129 CCStatus bnstatus; 130 131 /* we have a private key provided */ 132 pub_keylen = BN_num_bytes(dh->pub_key); 133 pub_key = malloc(pub_keylen); 134 if (NULL == pub_key) { 135 goto err; 136 } 137 if (!BN_bn2bin(dh->pub_key, (unsigned char *)pub_key)) { 138 goto err; 139 } 140 if ((ccdh->pub_key = CCBigNumFromData(&bnstatus, 141 pub_key, pub_keylen)) == NULL) { 142 goto err; 143 } 144 } 145 146err: 147 if (p) { 148 memset(p, 0, plen); 149 free(p); 150 } 151 if (g) { 152 memset(g, 0, glen); 153 free(g); 154 } 155 if (priv_key) { 156 memset(g, 0, priv_keylen); 157 free(priv_key); 158 } 159 if (pub_key) { 160 memset(g, 0, pub_keylen); 161 free(pub_key); 162 } 163 if (dhparams) { 164 CCDHParametersRelease(dhparams); 165 } 166 167 return (ccdh); 168} 169 170 171static int 172cc_dh_generate_key(DH *dh) 173{ 174 /* CCDHRef */ 175 CCDH ccdh = NULL; 176 size_t pub_keylen; 177 void *pub_key; 178 int rv = 0; 179 180 if ((NULL == dh->p) || (NULL == dh->g)) { 181 return (0); 182 } 183 184 if ((ccdh = loadkeys(dh)) == NULL) { 185 goto err; 186 } 187 188 /* XXX header doc for CCDHGenerateKey is wrong. 189 * output is bin data not BigNUm. 190 * *outputLength = CCBigNumToData(&bnRetval, dh->pub_key, output); 191 * See <rdar://problem/10771260> 192 */ 193 if (CCDHGenerateKey(ccdh, pub_key, &pub_keylen) != 0) { 194 goto err; 195 } 196 197 if ((dh->pub_key = BN_bin2bn(pub_key, (int)pub_keylen, dh->pub_key)) 198 == NULL) { 199 goto err; 200 } 201 202 rv = 1; 203 204err: 205 if (ccdh) { 206 /* 207 * XXX CCDHRelease() doesn't free pub/priv key. 208 * See <rdar://problem/10771265> for more info. 209 */ 210 if (ccdh->priv_key) { 211 CCBigNumFree(ccdh->priv_key); 212 } 213 if (ccdh->pub_key) { 214 CCBigNumFree(ccdh->pub_key); 215 } 216 217 CCDHRelease((CCDHRef)ccdh); 218 } 219 220 return (rv); 221} 222 223 224static int 225cc_dh_compute_key(unsigned char *shared, const BIGNUM *peer, DH *dh) 226{ 227 /* CCDHRef */ 228 CCDH ccdh = NULL; 229 void *peer_key = NULL; 230 int rv = -1, peer_keylen; 231 232 233 if ((NULL == dh->priv_key) || (NULL == dh->pub_key)) { 234 return (-1); 235 } 236 237 if ((ccdh = loadkeys(dh)) == NULL) { 238 goto err; 239 } 240 241 peer_keylen = BN_num_bytes(peer); 242 if ((peer_key = malloc(peer_keylen)) == NULL) { 243 goto err; 244 } 245 if (!BN_bn2bin(peer, (unsigned char *)peer_key)) { 246 goto err; 247 } 248 249 if ((rv = CCDHComputeKey(shared, peer_key, (size_t)peer_keylen, 250 (CCDHRef)ccdh)) == -1) { 251 goto err; 252 } 253 254err: 255 if (ccdh) { 256 /* XXX CCDHRelease() doesn't free pub/priv key */ 257 if (ccdh->priv_key) { 258 CCBigNumFree(ccdh->priv_key); 259 } 260 if (ccdh->pub_key) { 261 CCBigNumFree(ccdh->pub_key); 262 } 263 264 CCDHRelease((CCDHRef)ccdh); 265 } 266 267 return (rv); 268} 269 270 271#else /* ! PR_10771188_FIXED */ 272 273/* 274 * Given that CC's implementation of DH doesn't work we use libtommath. 275 * Note that this requires the libtommath lib to be linked in. 276 */ 277#include <tommath.h> 278 279static void 280BN2mpz(mp_int *s, const BIGNUM *bn) 281{ 282 size_t len; 283 void *p; 284 285 len = BN_num_bytes(bn); 286 p = malloc(len); 287 BN_bn2bin(bn, p); 288 mp_read_unsigned_bin(s, p, len); 289 /* s = (mp_int *)CCBigNumFromData(&status, p, len); */ 290 free(p); 291} 292 293 294static BIGNUM * 295mpz2BN(mp_int *s) 296{ 297 size_t size; 298 BIGNUM *bn; 299 void *p; 300 301 size = mp_unsigned_bin_size(s); 302 p = malloc(size); 303 if ((p == NULL) && (size != 0)) { 304 return (NULL); 305 } 306 mp_to_unsigned_bin(s, p); 307 /* (void)CCBigNumToData(&status, (CCBigNumRef)s, p); */ 308 309 bn = BN_bin2bn(p, size, NULL); 310 free(p); 311 return (bn); 312} 313 314 315/* 316 * 317 */ 318 319#define DH_NUM_TRIES 10 320 321static int 322cc_dh_generate_key(DH *dh) 323{ 324 mp_int pub, priv_key, g, p; 325 int have_private_key = (dh->priv_key != NULL); 326 int codes, times = 0; 327 int res; 328 329 if ((dh->p == NULL) || (dh->g == NULL)) { 330 return (0); 331 } 332 333 mp_init_multi(&pub, &priv_key, &g, &p, NULL); 334 335 while (times++ < DH_NUM_TRIES) { 336 if (!have_private_key) { 337 size_t bits = BN_num_bits(dh->p); 338 339 if (dh->priv_key) { 340 BN_free(dh->priv_key); 341 } 342 343 dh->priv_key = BN_new(); 344 if (dh->priv_key == NULL) { 345 return (0); 346 } 347 if (!BN_rand(dh->priv_key, bits /* - 1 */, 0, 0)) { 348 BN_clear_free(dh->priv_key); 349 dh->priv_key = NULL; 350 return (0); 351 } 352 } 353 if (dh->pub_key) { 354 BN_free(dh->pub_key); 355 } 356 357 /* mp_init_multi(&pub, &priv_key, &g, &p, NULL); */ 358 359 BN2mpz(&g, dh->g); 360 BN2mpz(&priv_key, dh->priv_key); 361 BN2mpz(&p, dh->p); 362 363 res = mp_exptmod(&g, &priv_key, &p, &pub); 364 365 mp_clear_multi(&priv_key, &g, &p, NULL); 366 if (res != 0) { 367 continue; 368 } 369 370 dh->pub_key = mpz2BN(&pub); 371 mp_clear(&pub); 372 if (dh->pub_key == NULL) { 373 return (0); 374 } 375 376 if (DH_check_pubkey(dh, dh->pub_key, &codes) && (codes == 0)) { 377 break; 378 } 379 if (have_private_key) { 380 return (0); 381 } 382 } 383 384 if (times >= DH_NUM_TRIES) { 385 if (!have_private_key && dh->priv_key) { 386 BN_free(dh->priv_key); 387 dh->priv_key = NULL; 388 } 389 if (dh->pub_key) { 390 BN_free(dh->pub_key); 391 dh->pub_key = NULL; 392 } 393 return (0); 394 } 395 396 return (1); 397} 398 399 400#define mp_isneg(a) (((a)->sign) ? 1 : 0) 401 402static int 403cc_dh_compute_key(unsigned char *shared, const BIGNUM *pub, DH *dh) 404{ 405 mp_int s, priv_key, p, peer_pub; 406 int ret; 407 408 if ((dh->pub_key == NULL) || (dh->g == NULL) || (dh->priv_key == NULL)) { 409 return (-1); 410 } 411 412 mp_init_multi(&s, &priv_key, &p, &peer_pub, NULL); 413 414 BN2mpz(&p, dh->p); 415 BN2mpz(&peer_pub, pub); 416 417 /* check if peers pubkey is reasonable */ 418 if (mp_isneg(&peer_pub) || 419 (mp_cmp(&peer_pub, &p) >= 0) || 420 (mp_cmp_d(&peer_pub, 1) <= 0)) { 421 ret = -1; 422 goto out; 423 } 424 425 BN2mpz(&priv_key, dh->priv_key); 426 427 ret = mp_exptmod(&peer_pub, &priv_key, &p, &s); 428 if (ret != 0) { 429 ret = -1; 430 goto out; 431 } 432 433 ret = mp_unsigned_bin_size(&s); 434 mp_to_unsigned_bin(&s, shared); 435 436out: 437 mp_clear_multi(&s, &priv_key, &p, &peer_pub, NULL); 438 439 return (ret); 440} 441 442 443#endif /* ! PR_10771188_FIXED */ 444 445static int 446cc_dh_generate_params(DH *dh, int a, int b, BN_GENCB *callback) 447{ 448 /* groups should already be known, we don't care about this */ 449 return (0); 450} 451 452 453static int 454cc_dh_init(DH *dh) 455{ 456 /* set flags */ 457 return (1); 458} 459 460 461static int 462cc_dh_finish(DH *dh) 463{ 464 /* free resources */ 465 return (1); 466} 467 468 469/* 470 * 471 */ 472 473const DH_METHOD _ossl_dh_cc_method = 474{ 475 .name = "CommonCrypto DH", 476 .generate_key = cc_dh_generate_key, 477 .compute_key = cc_dh_compute_key, 478 .bn_mod_exp = NULL, 479 .init = cc_dh_init, 480 .finish = cc_dh_finish, 481 .flags = 0, 482 .app_data = NULL, 483 .generate_params = cc_dh_generate_params 484}; 485 486#endif /* HAVE_COMMONCRYPTO_COMMONDH_H */ 487 488/** 489 * DH implementation using cdsa. 490 * 491 * @return the DH_METHOD for the DH implementation using CommonCrypto. 492 */ 493const DH_METHOD * 494DH_cc_method(void) 495{ 496#ifdef HAVE_COMMONCRYPTO_COMMONDH_H 497 return (&_ossl_dh_cc_method); 498 499#else 500 return (NULL); 501#endif /* HAVE_COMMONCRYPTO_COMMONDH_H */ 502} 503