1/* 2 * Copyright (c) 2000-2002,2011-2012,2014 Apple Inc. All Rights Reserved. 3 * 4 * The contents of this file constitute Original Code as defined in and are 5 * subject to the Apple Public Source License Version 1.2 (the 'License'). 6 * You may not use this file except in compliance with the License. Please obtain 7 * a copy of the License at http://www.apple.com/publicsource and read it before 8 * using this file. 9 * 10 * This Original Code and all software distributed under the License are 11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS 12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT 13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the 15 * specific language governing rights and limitations under the License. 16 */ 17 18 19/* 20 * DH_keys.cpp - Diffie-Hellman key pair support. 21 */ 22 23#include "DH_keys.h" 24#include "DH_utils.h" 25#include <opensslUtils/opensslUtils.h> 26#include <opensslUtils/opensslAsn1.h> 27#include <security_cdsa_utilities/cssmdata.h> 28#include <AppleCSPSession.h> 29#include <AppleCSPUtils.h> 30#include <assert.h> 31#include <security_utilities/debugging.h> 32#include <Security/oidsalg.h> 33#include <YarrowConnection.h> 34 35#define dhKeyDebug(args...) secdebug("dhKey", ## args) 36 37/* 38 * FIXME - the CDSA Algorithm Guide claims that the incoming params argument 39 * for a GenerateAlgorithmParameters call is ignored for D-H. This means 40 * that there is no way for the caller to specify 'g' (typically 2, 3, or 41 * 5). This seems WAY bogus but we'll code to the spec for now, assuming 42 * a hard-coded default generator. 43 */ 44#define DH_GENERATOR_DEFAULT DH_GENERATOR_2 45 46 47/*** 48 *** Diffie-Hellman-style BinaryKey 49 ***/ 50 51/* constructor with optional existing DSA key */ 52DHBinaryKey::DHBinaryKey(DH *dhKey) 53 : mDhKey(dhKey) 54{ 55} 56 57DHBinaryKey::~DHBinaryKey() 58{ 59 if(mDhKey) { 60 DH_free(mDhKey); 61 mDhKey = NULL; 62 } 63} 64 65void DHBinaryKey::generateKeyBlob( 66 Allocator &allocator, 67 CssmData &blob, 68 CSSM_KEYBLOB_FORMAT &format, 69 AppleCSPSession &session, 70 const CssmKey *paramKey, /* optional, unused here */ 71 CSSM_KEYATTR_FLAGS &attrFlags) /* IN/OUT */ 72{ 73 74 switch(mKeyHeader.KeyClass) { 75 case CSSM_KEYCLASS_PUBLIC_KEY: 76 { 77 switch(format) { 78 case CSSM_KEYBLOB_RAW_FORMAT_NONE: 79 // take default 80 format = DH_PUB_KEY_FORMAT; 81 break; 82 case DH_PUB_KEY_FORMAT: 83 case CSSM_KEYBLOB_RAW_FORMAT_X509: 84 // proceed 85 break; 86 case CSSM_KEYBLOB_RAW_FORMAT_DIGEST: 87 /* use PKCS3 - caller won't care if we change this...right? */ 88 format = DH_PUB_KEY_FORMAT; 89 break; 90 default: 91 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT); 92 } 93 94 assert(mDhKey != NULL); 95 CssmAutoData encodedKey(allocator); 96 CSSM_RETURN crtn = DHPublicKeyEncode(mDhKey, format, 97 encodedKey); 98 if(crtn) { 99 CssmError::throwMe(crtn); 100 } 101 blob = encodedKey.release(); 102 break; 103 } 104 case CSSM_KEYCLASS_PRIVATE_KEY: 105 { 106 switch(format) { 107 case CSSM_KEYBLOB_RAW_FORMAT_NONE: 108 // i.e., use default 109 format = DH_PRIV_KEY_FORMAT; 110 break; 111 case DH_PRIV_KEY_FORMAT: 112 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: 113 // proceed 114 break; 115 116 case CSSM_KEYBLOB_RAW_FORMAT_DIGEST: 117 { 118 /* 119 * Use public blob; calculate it if we 120 * don't already have it. 121 */ 122 assert(mDhKey != NULL); 123 if(mDhKey->pub_key == NULL) { 124 int irtn = DH_generate_key(mDhKey); 125 if(!irtn) { 126 throwRsaDsa("DH_generate_key"); 127 } 128 } 129 assert(mDhKey->pub_key != NULL); 130 setUpData(blob, 131 BN_num_bytes(mDhKey->pub_key), 132 *DH_Factory::privAllocator); 133 BN_bn2bin(mDhKey->pub_key, blob); 134 format = DH_PUB_KEY_FORMAT; 135 return; 136 } 137 138 default: 139 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT); 140 } 141 assert(mDhKey != NULL); 142 CssmAutoData encodedKey(allocator); 143 CSSM_RETURN crtn = DHPrivateKeyEncode(mDhKey, format, 144 encodedKey); 145 if(crtn) { 146 CssmError::throwMe(crtn); 147 } 148 blob = encodedKey.release(); 149 break; 150 } 151 default: 152 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); 153 } 154} 155 156/*** 157 *** Diffie-Hellman style AppleKeyPairGenContext 158 ***/ 159 160/* 161 * This one is specified in, and called from, CSPFullPluginSession. Our 162 * only job is to prepare two subclass-specific BinaryKeys and call up to 163 * AppleKeyPairGenContext. 164 */ 165void DHKeyPairGenContext::generate( 166 const Context &context, 167 CssmKey &pubKey, 168 CssmKey &privKey) 169{ 170 DHBinaryKey *pubBinKey = new DHBinaryKey(); 171 DHBinaryKey *privBinKey = new DHBinaryKey(); 172 173 try { 174 AppleKeyPairGenContext::generate(context, 175 session(), 176 pubKey, 177 pubBinKey, 178 privKey, 179 privBinKey); 180 } 181 catch (...) { 182 delete pubBinKey; 183 delete privBinKey; 184 throw; 185 } 186} 187 188/* 189 * This one is specified in, and called from, AppleKeyPairGenContext 190 */ 191void DHKeyPairGenContext::generate( 192 const Context &context, 193 BinaryKey &pubBinKey, 194 BinaryKey &privBinKey, 195 uint32 &keyBits) 196{ 197 /* 198 * These casts throw exceptions if the keys are of the 199 * wrong classes, which would be a major bogon, since we created 200 * the keys in the above generate() function. 201 */ 202 DHBinaryKey &rPubBinKey = 203 dynamic_cast<DHBinaryKey &>(pubBinKey); 204 DHBinaryKey &rPrivBinKey = 205 dynamic_cast<DHBinaryKey &>(privBinKey); 206 207 /* 208 * Parameters from context: 209 * Key size in bits, required; 210 * {p,g,privKeyLength} from generateParams, optional 211 * NOTE: currently the openssl D-H imnplementation ignores the 212 * privKeyLength field. 213 */ 214 keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH, 215 CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH); 216 CssmData *paramData = context.get<CssmData>(CSSM_ATTRIBUTE_ALG_PARAMS); 217 218 NSS_DHParameterBlock algParamBlock; 219 NSS_DHParameter &algParams = algParamBlock.params; 220 uint32 privValueLen = 0; // only nonzero from externally generated 221 // params 222 SecNssCoder coder; // for temp allocs of decoded parameters 223 224 if(paramData != NULL) { 225 /* this contains the DER encoding of a DHParameterBlock */ 226 CSSM_RETURN crtn; 227 crtn = DHParamBlockDecode(*paramData, algParamBlock, coder); 228 if(crtn) { 229 CssmError::throwMe(crtn); 230 } 231 232 /* snag the optional private key length field */ 233 if(algParams.privateValueLength.Data) { 234 privValueLen = cssmDataToInt(algParams.privateValueLength); 235 } 236 237 /* ensure caller's key size matches the incoming params */ 238 size_t paramKeyBytes; 239 if(privValueLen) { 240 paramKeyBytes = (privValueLen + 7) / 8; 241 } 242 else { 243 paramKeyBytes = algParams.prime.Length; 244 /* trim off possible m.s. byte of zero */ 245 const unsigned char *uo = 246 (const unsigned char *)algParams.prime.Data; 247 if(*uo == 0) { 248 paramKeyBytes--; 249 } 250 } 251 uint32 reqBytes = (keyBits + 7) / 8; 252 if(paramKeyBytes != reqBytes) { 253 dhKeyDebug("DH key size mismatch (req %d param %d)", 254 (int)reqBytes, (int)paramKeyBytes); 255 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE); 256 } 257 } 258 else { 259 /* no alg params specified; generate them now */ 260 dhKeyDebug("DH implicit alg param calculation"); 261 memset(&algParamBlock, 0, sizeof(algParamBlock)); 262 dhGenParams(keyBits, DH_GENERATOR_DEFAULT, 0, algParams, coder); 263 } 264 265 /* create key, stuff params into it */ 266 rPrivBinKey.mDhKey = DH_new(); 267 if(rPrivBinKey.mDhKey == NULL) { 268 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); 269 } 270 DH *dhKey = rPrivBinKey.mDhKey; 271 dhKey->p = cssmDataToBn(algParams.prime); 272 dhKey->g = cssmDataToBn(algParams.base); 273 dhKey->length = privValueLen; 274 cspDhDebug("private DH binary key dhKey %p", dhKey); 275 276 /* generate the key (both public and private capabilities) */ 277 int irtn = DH_generate_key(dhKey); 278 if(!irtn) { 279 throwRsaDsa("DH_generate_key"); 280 } 281 282 /* public key is a subset */ 283 rPubBinKey.mDhKey = DH_new(); 284 if(rPubBinKey.mDhKey == NULL) { 285 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); 286 } 287 DH *pubDhKey = rPubBinKey.mDhKey; 288 pubDhKey->pub_key = BN_dup(dhKey->pub_key); 289 /* these params used for X509 style key blobs */ 290 pubDhKey->p = BN_dup(dhKey->p); 291 pubDhKey->g = BN_dup(dhKey->g); 292 cspDhDebug("public DH binary key pubDhKey %p", pubDhKey); 293} 294 295 296 297/*** 298 *** Diffie-Hellman CSPKeyInfoProvider. 299 ***/ 300DHKeyInfoProvider::DHKeyInfoProvider( 301 const CssmKey &cssmKey, 302 AppleCSPSession &session) : 303 CSPKeyInfoProvider(cssmKey, session) 304{ 305} 306 307CSPKeyInfoProvider *DHKeyInfoProvider::provider( 308 const CssmKey &cssmKey, 309 AppleCSPSession &session) 310{ 311 switch(cssmKey.algorithm()) { 312 case CSSM_ALGID_DH: 313 break; 314 default: 315 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); 316 } 317 switch(cssmKey.keyClass()) { 318 case CSSM_KEYCLASS_PUBLIC_KEY: 319 case CSSM_KEYCLASS_PRIVATE_KEY: 320 break; 321 default: 322 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); 323 } 324 /* OK, we'll handle this one */ 325 return new DHKeyInfoProvider(cssmKey, session); 326} 327 328/* Given a raw key, cook up a Binary key */ 329void DHKeyInfoProvider::CssmKeyToBinary( 330 CssmKey *paramKey, // optional, ignored here 331 CSSM_KEYATTR_FLAGS &attrFlags, // IN/OUT 332 BinaryKey **binKey) 333{ 334 *binKey = NULL; 335 336 assert(mKey.blobType() == CSSM_KEYBLOB_RAW); 337 switch(mKey.keyClass()) { 338 case CSSM_KEYCLASS_PUBLIC_KEY: 339 case CSSM_KEYCLASS_PRIVATE_KEY: 340 break; 341 default: 342 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); 343 } 344 345 /* first cook up an DH key, then drop that into a BinaryKey */ 346 DH *dhKey = rawCssmKeyToDh(mKey); 347 DHBinaryKey *dhBinKey = new DHBinaryKey(dhKey); 348 *binKey = dhBinKey; 349 cspDhDebug("CssmKeyToBinary dhKey %p", dhKey); 350} 351 352/* 353 * Obtain key size in bits. 354 * FIXME - I doubt that this is, or can be, exactly accurate..... 355 */ 356void DHKeyInfoProvider::QueryKeySizeInBits( 357 CSSM_KEY_SIZE &keySize) 358{ 359 uint32 numBits = 0; 360 361 if(mKey.blobType() != CSSM_KEYBLOB_RAW) { 362 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT); 363 } 364 DH *dhKey = rawCssmKeyToDh(mKey); 365 366 /* DH_size requires the p parameter, which some public keys don't have */ 367 if(dhKey->p != NULL) { 368 numBits = DH_size(dhKey) * 8; 369 } 370 else { 371 assert(dhKey->pub_key != NULL); 372 numBits = BN_num_bytes(dhKey->pub_key) * 8; 373 } 374 DH_free(dhKey); 375 keySize.LogicalKeySizeInBits = numBits; 376 keySize.EffectiveKeySizeInBits = numBits; 377} 378 379/* 380 * Obtain blob suitable for hashing in CSSM_APPLECSP_KEYDIGEST 381 * passthrough. 382 */ 383bool DHKeyInfoProvider::getHashableBlob( 384 Allocator &allocator, 385 CssmData &blob) // blob to hash goes here 386{ 387 /* 388 * The optimized case, a raw key in the "proper" format already. 389 */ 390 assert(mKey.blobType() == CSSM_KEYBLOB_RAW); 391 bool useAsIs = false; 392 393 switch(mKey.keyClass()) { 394 case CSSM_KEYCLASS_PUBLIC_KEY: 395 if(mKey.blobFormat() == CSSM_KEYBLOB_RAW_FORMAT_PKCS3) { 396 useAsIs = true; 397 } 398 break; 399 case CSSM_KEYCLASS_PRIVATE_KEY: 400 break; 401 default: 402 /* shouldn't be here */ 403 assert(0); 404 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR); 405 } 406 if(useAsIs) { 407 const CssmData &keyBlob = CssmData::overlay(mKey.KeyData); 408 copyCssmData(keyBlob, blob, allocator); 409 return true; 410 } 411 412 /* caller converts to binary and proceeds */ 413 return false; 414} 415 416/* 417 * Generate keygen parameters, stash them in a context attr array for later use 418 * when actually generating the keys. 419 */ 420 421void DHKeyPairGenContext::generate( 422 const Context &context, 423 uint32 bitSize, 424 CssmData ¶ms, // RETURNED here, 425 uint32 &attrCount, // here, 426 Context::Attr * &attrs) // and here 427{ 428 /* generate the params */ 429 NSS_DHParameterBlock algParamBlock; 430 SecNssCoder coder; 431 NSS_DHParameter &algParams = algParamBlock.params; 432 dhGenParams(bitSize, DH_GENERATOR_DEFAULT, 0, algParams, coder); 433 434 /* drop in the required OID */ 435 algParamBlock.oid = CSSMOID_PKCS3; 436 437 /* 438 * Here comes the fun part. 439 * We "return" the DER encoding of these generated params in two ways: 440 * 1. Copy out to app via the params argument, mallocing if Data ptr is NULL. 441 * The app must free this. 442 * 2. Cook up a 1-element Context::attr array containing one ALG_PARAM attr, 443 * a CSSM_DATA_PTR containing the DER encoding. We have to save a ptr to 444 * this attr array and free it, the CSSM_DATA it points to, and the DER 445 * encoding *that* points to, in our destructor. 446 * 447 * First, DER encode. 448 */ 449 CssmAutoData aDerData(session()); 450 PRErrorCode perr; 451 perr = SecNssEncodeItemOdata(&algParamBlock, kSecAsn1DHParameterBlockTemplate, 452 aDerData); 453 if(perr) { 454 /* only known error... */ 455 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); 456 } 457 458 /* copy/release that into a mallocd CSSM_DATA. */ 459 CSSM_DATA_PTR derData = (CSSM_DATA_PTR)session().malloc(sizeof(CSSM_DATA)); 460 *derData = aDerData.release(); 461 462 /* stuff that into a one-element Attr array which we keep after returning */ 463 freeGenAttrs(); 464 mGenAttrs = (Context::Attr *)session().malloc(sizeof(Context::Attr)); 465 mGenAttrs->AttributeType = CSSM_ATTRIBUTE_ALG_PARAMS; 466 mGenAttrs->AttributeLength = sizeof(CSSM_DATA); 467 mGenAttrs->Attribute.Data = derData; 468 469 /* and "return" this stuff */ 470 copyCssmData(CssmData::overlay(*derData), params, session()); 471 attrCount = 1; 472 attrs = mGenAttrs; 473} 474 475/* free mGenAttrs and its referents if present */ 476void DHKeyPairGenContext::freeGenAttrs() 477{ 478 if(mGenAttrs == NULL) { 479 return; 480 } 481 if(mGenAttrs->Attribute.Data) { 482 if(mGenAttrs->Attribute.Data->Data) { 483 session().free(mGenAttrs->Attribute.Data->Data); 484 } 485 session().free(mGenAttrs->Attribute.Data); 486 } 487 session().free(mGenAttrs); 488} 489 490/* 491 * Generate DSA algorithm parameters returning result 492 * into DHParameter.{prime,base,privateValueLength]. 493 * This is called from both GenerateParameters and from 494 * KeyPairGenerate (if no GenerateParameters has yet been called). 495 * 496 * FIXME - privateValueLength not implemented in openssl, not here 497 * either for now. 498 */ 499 500void DHKeyPairGenContext::dhGenParams( 501 uint32 keySizeInBits, 502 unsigned g, // probably should be BIGNUM 503 int privValueLength, // optional 504 NSS_DHParameter &algParams, 505 SecNssCoder &coder) // temp contents of algParams 506 // mallocd here 507{ 508 /* validate key size */ 509 if((keySizeInBits < DH_MIN_KEY_SIZE) || 510 (keySizeInBits > DH_MAX_KEY_SIZE)) { 511 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH); 512 } 513 514 /* create an openssl-style DH key with minimal setup */ 515 DH *dhKey = DH_generate_parameters(keySizeInBits, g, NULL, NULL); 516 if(dhKey == NULL) { 517 throwRsaDsa("DSA_generate_parameters"); 518 } 519 520 /* stuff dhKey->{p,g,length}] into a caller's NSS_DHParameter */ 521 bnToCssmData(dhKey->p, algParams.prime, coder); 522 bnToCssmData(dhKey->g, algParams.base, coder); 523 CSSM_DATA &privValData = algParams.privateValueLength; 524 if(privValueLength) { 525 intToCssmData(privValueLength, privValData, coder); 526 } 527 else { 528 privValData.Data = NULL; 529 privValData.Length = 0; 530 } 531 DH_free(dhKey); 532} 533 534