1/* 2 * The contents of this file are subject to the Mozilla Public 3 * License Version 1.1 (the "License"); you may not use this file 4 * except in compliance with the License. You may obtain a copy of 5 * the License at http://www.mozilla.org/MPL/ 6 * 7 * Software distributed under the License is distributed on an "AS 8 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or 9 * implied. See the License for the specific language governing 10 * rights and limitations under the License. 11 * 12 * The Original Code is the Netscape security libraries. 13 * 14 * The Initial Developer of the Original Code is Netscape 15 * Communications Corporation. Portions created by Netscape are 16 * Copyright (C) 1994-2000 Netscape Communications Corporation. All 17 * Rights Reserved. 18 * 19 * Contributor(s): 20 * 21 * Alternatively, the contents of this file may be used under the 22 * terms of the GNU General Public License Version 2 or later (the 23 * "GPL"), in which case the provisions of the GPL are applicable 24 * instead of those above. If you wish to allow use of your 25 * version of this file only under the terms of the GPL and not to 26 * allow others to use your version of this file under the MPL, 27 * indicate your decision by deleting the provisions above and 28 * replace them with the notice and other provisions required by 29 * the GPL. If you do not delete the provisions above, a recipient 30 * may use your version of this file under either the MPL or the 31 * GPL. 32 */ 33 34/* 35 * CMS public key crypto 36 */ 37 38#include "cmslocal.h" 39 40#include "secitem.h" 41#include "secoid.h" 42#include "cryptohi.h" 43 44#include <security_asn1/secasn1.h> 45#include <security_asn1/secerr.h> 46#include <Security/SecCertificatePriv.h> 47#include <Security/SecKeyPriv.h> 48#include <Security/Security.h> 49#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> 50#include <Security/SecCmsBase.h> 51#include <Security/secasn1t.h> 52#include <security_asn1/plarenas.h> 53#include <Security/keyTemplates.h> 54 55/* ====== RSA ======================================================================= */ 56 57/* 58 * SecCmsUtilEncryptSymKeyRSA - wrap a symmetric key with RSA 59 * 60 * this function takes a symmetric key and encrypts it using an RSA public key 61 * according to PKCS#1 and RFC2633 (S/MIME) 62 */ 63OSStatus 64SecCmsUtilEncryptSymKeyRSA(PLArenaPool *poolp, SecCertificateRef cert, 65 SecSymmetricKeyRef bulkkey, 66 CSSM_DATA_PTR encKey) 67{ 68 OSStatus rv; 69 SecPublicKeyRef publickey; 70 71 rv = SecCertificateCopyPublicKey(cert,&publickey); 72 if (publickey == NULL) 73 return SECFailure; 74 75 rv = SecCmsUtilEncryptSymKeyRSAPubKey(poolp, publickey, bulkkey, encKey); 76 CFRelease(publickey); 77 return rv; 78} 79 80OSStatus 81SecCmsUtilEncryptSymKeyRSAPubKey(PLArenaPool *poolp, 82 SecPublicKeyRef publickey, 83 SecSymmetricKeyRef bulkkey, CSSM_DATA_PTR encKey) 84{ 85 OSStatus rv; 86 unsigned int data_len; 87 //KeyType keyType; 88 void *mark = NULL; 89 90 mark = PORT_ArenaMark(poolp); 91 if (!mark) 92 goto loser; 93 94#if 0 95 /* sanity check */ 96 keyType = SECKEY_GetPublicKeyType(publickey); 97 PORT_Assert(keyType == rsaKey); 98 if (keyType != rsaKey) { 99 goto loser; 100 } 101#endif 102 /* allocate memory for the encrypted key */ 103 rv = SecKeyGetStrengthInBits(publickey, NULL, &data_len); 104 if (rv) 105 goto loser; 106 107 // Convert length to bytes; 108 data_len >>= 2; 109 encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len); 110 encKey->Length = data_len; 111 if (encKey->Data == NULL) 112 goto loser; 113 114 /* encrypt the key now */ 115 rv = WRAP_PubWrapSymKey(publickey, bulkkey, encKey); 116 if (rv != SECSuccess) 117 goto loser; 118 119 PORT_ArenaUnmark(poolp, mark); 120 return SECSuccess; 121 122loser: 123 if (mark) { 124 PORT_ArenaRelease(poolp, mark); 125 } 126 return SECFailure; 127} 128 129/* 130 * SecCmsUtilDecryptSymKeyRSA - unwrap a RSA-wrapped symmetric key 131 * 132 * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric 133 * key handle. Please note that the actual unwrapped key data may not be allowed to leave 134 * a hardware token... 135 */ 136SecSymmetricKeyRef 137SecCmsUtilDecryptSymKeyRSA(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECOidTag bulkalgtag) 138{ 139 /* that's easy */ 140 return WRAP_PubUnwrapSymKey(privkey, encKey, bulkalgtag); 141} 142 143#if 0 144// @@@ Implement Fortezza and Diffie hellman support 145 146/* ====== MISSI (Fortezza) ========================================================== */ 147 148extern const SecAsn1Template NSS_SMIMEKEAParamTemplateAllParams[]; 149 150OSStatus 151SecCmsUtilEncryptSymKeyMISSI(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef bulkkey, 152 SECOidTag symalgtag, CSSM_DATA_PTR encKey, CSSM_DATA_PTR *pparams, void *pwfn_arg) 153{ 154 SECOidTag certalgtag; /* the certificate's encryption algorithm */ 155 SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */ 156 OSStatus rv = SECFailure; 157 CSSM_DATA_PTR params = NULL; 158 OSStatus err; 159 SecSymmetricKeyRef tek; 160 SecCertificateRef ourCert; 161 SecPublicKeyRef ourPubKey, *publickey = NULL; 162 SecPrivateKeyRef ourPrivKey = NULL; 163 SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid; 164 SecCmsSMIMEKEAParameters keaParams; 165 PLArenaPool *arena = NULL; 166 extern const SecAsn1Template *nss_cms_get_kea_template(SecCmsKEATemplateSelector whichTemplate); 167 const SECAlgorithmID *algid; 168 169 /* Clear keaParams, since cleanup code checks the lengths */ 170 (void) memset(&keaParams, 0, sizeof(keaParams)); 171 172 SecCertificateGetAlgorithmID(cert,&algid); 173 certalgtag = SECOID_GetAlgorithmTag(algid); 174 PORT_Assert(certalgtag == SEC_OID_MISSI_KEA_DSS_OLD || 175 certalgtag == SEC_OID_MISSI_KEA_DSS || 176 certalgtag == SEC_OID_MISSI_KEA); 177 178#define SMIME_FORTEZZA_RA_LENGTH 128 179#define SMIME_FORTEZZA_IV_LENGTH 24 180#define SMIME_FORTEZZA_MAX_KEY_SIZE 256 181 182 /* We really want to show our KEA tag as the key exchange algorithm tag. */ 183 encalgtag = SEC_OID_NETSCAPE_SMIME_KEA; 184 185 /* Get the public key of the recipient. */ 186 publickey = CERT_ExtractPublicKey(cert); 187 if (publickey == NULL) goto loser; 188 189 /* Find our own cert, and extract its keys. */ 190 ourCert = PK11_FindBestKEAMatch(cert, pwfn_arg); 191 if (ourCert == NULL) goto loser; 192 193 arena = PORT_NewArena(1024); 194 if (arena == NULL) 195 goto loser; 196 197 ourPubKey = CERT_ExtractPublicKey(ourCert); 198 if (ourPubKey == NULL) { 199 CERT_DestroyCertificate(ourCert); 200 goto loser; 201 } 202 203 /* While we're here, copy the public key into the outgoing 204 * KEA parameters. */ 205 SECITEM_CopyItem(arena, &(keaParams.originatorKEAKey), &(ourPubKey->u.fortezza.KEAKey)); 206 SECKEY_DestroyPublicKey(ourPubKey); 207 ourPubKey = NULL; 208 209 /* Extract our private key in order to derive the KEA key. */ 210 ourPrivKey = PK11_FindKeyByAnyCert(ourCert, pwfn_arg); 211 CERT_DestroyCertificate(ourCert); /* we're done with this */ 212 if (!ourPrivKey) 213 goto loser; 214 215 /* Prepare raItem with 128 bytes (filled with zeros). */ 216 keaParams.originatorRA.Data = (unsigned char *)PORT_ArenaAlloc(arena,SMIME_FORTEZZA_RA_LENGTH); 217 keaParams.originatorRA.Length = SMIME_FORTEZZA_RA_LENGTH; 218 219 /* Generate the TEK (token exchange key) which we use 220 * to wrap the bulk encryption key. (keaparams.originatorRA) will be 221 * filled with a random seed which we need to send to 222 * the recipient. (user keying material in RFC2630/DSA speak) */ 223 tek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE, 224 &keaParams.originatorRA, NULL, 225 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP, 226 CKA_WRAP, 0, pwfn_arg); 227 228 SECKEY_DestroyPublicKey(publickey); 229 SECKEY_DestroyPrivateKey(ourPrivKey); 230 publickey = NULL; 231 ourPrivKey = NULL; 232 233 if (!tek) 234 goto loser; 235 236 /* allocate space for the wrapped key data */ 237 encKey->Data = (unsigned char *)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE); 238 encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE; 239 240 if (encKey->Data == NULL) { 241 CFRelease(tek); 242 goto loser; 243 } 244 245 /* Wrap the bulk key. What we do with the resulting data 246 depends on whether we're using Skipjack to wrap the key. */ 247 switch (PK11_AlgtagToMechanism(symalgtag)) { 248 case CKM_SKIPJACK_CBC64: 249 case CKM_SKIPJACK_ECB64: 250 case CKM_SKIPJACK_OFB64: 251 case CKM_SKIPJACK_CFB64: 252 case CKM_SKIPJACK_CFB32: 253 case CKM_SKIPJACK_CFB16: 254 case CKM_SKIPJACK_CFB8: 255 /* SKIPJACK, we use the wrap mechanism because we can do it on the hardware */ 256 err = PK11_WrapSymKey(CKM_SKIPJACK_WRAP, NULL, tek, bulkkey, encKey); 257 whichKEA = SecCmsKEAUsesSkipjack; 258 break; 259 default: 260 /* Not SKIPJACK, we encrypt the raw key data */ 261 keaParams.nonSkipjackIV.Data = 262 (unsigned char *)PORT_ArenaAlloc(arena, SMIME_FORTEZZA_IV_LENGTH); 263 keaParams.nonSkipjackIV.Length = SMIME_FORTEZZA_IV_LENGTH; 264 err = PK11_WrapSymKey(CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV, tek, bulkkey, encKey); 265 if (err != SECSuccess) 266 goto loser; 267 268 if (encKey->Length != PK11_GetKeyLength(bulkkey)) { 269 /* The size of the encrypted key is not the same as 270 that of the original bulk key, presumably due to 271 padding. Encode and store the real size of the 272 bulk key. */ 273 if (SEC_ASN1EncodeInteger(arena, &keaParams.bulkKeySize, PK11_GetKeyLength(bulkkey)) == NULL) 274 err = (OSStatus)PORT_GetError(); 275 else 276 /* use full template for encoding */ 277 whichKEA = SecCmsKEAUsesNonSkipjackWithPaddedEncKey; 278 } 279 else 280 /* enc key length == bulk key length */ 281 whichKEA = SecCmsKEAUsesNonSkipjack; 282 break; 283 } 284 285 CFRelease(tek); 286 287 if (err != SECSuccess) 288 goto loser; 289 290 PORT_Assert(whichKEA != SecCmsKEAInvalid); 291 292 /* Encode the KEA parameters into the recipient info. */ 293 params = SEC_ASN1EncodeItem(poolp, NULL, &keaParams, nss_cms_get_kea_template(whichKEA)); 294 if (params == NULL) 295 goto loser; 296 297 /* pass back the algorithm params */ 298 *pparams = params; 299 300 rv = SECSuccess; 301 302loser: 303 if (arena) 304 PORT_FreeArena(arena, PR_FALSE); 305 if (publickey) 306 SECKEY_DestroyPublicKey(publickey); 307 if (ourPrivKey) 308 SECKEY_DestroyPrivateKey(ourPrivKey); 309 return rv; 310} 311 312SecSymmetricKeyRef 313SecCmsUtilDecryptSymKeyMISSI(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg) 314{ 315 /* fortezza: do a key exchange */ 316 OSStatus err; 317 CK_MECHANISM_TYPE bulkType; 318 SecSymmetricKeyRef tek; 319 SecPublicKeyRef originatorPubKey; 320 SecCmsSMIMEKEAParameters keaParams; 321 SecSymmetricKeyRef bulkkey; 322 int bulkLength; 323 324 (void) memset(&keaParams, 0, sizeof(keaParams)); 325 326 /* NOTE: this uses the SMIME v2 recipientinfo for compatibility. 327 All additional KEA parameters are DER-encoded in the encryption algorithm parameters */ 328 329 /* Decode the KEA algorithm parameters. */ 330 err = SEC_ASN1DecodeItem(NULL, &keaParams, NSS_SMIMEKEAParamTemplateAllParams, 331 &(keyEncAlg->parameters)); 332 if (err != SECSuccess) 333 goto loser; 334 335 /* get originator's public key */ 336 originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data, 337 keaParams.originatorKEAKey.Length); 338 if (originatorPubKey == NULL) 339 goto loser; 340 341 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key. 342 The Derive function generates a shared secret and combines it with the originatorRA 343 data to come up with an unique session key */ 344 tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE, 345 &keaParams.originatorRA, NULL, 346 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP, 347 CKA_WRAP, 0, pwfn_arg); 348 SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */ 349 if (tek == NULL) 350 goto loser; 351 352 /* Now that we have the TEK, unwrap the bulk key 353 with which to decrypt the message. We have to 354 do one of two different things depending on 355 whether Skipjack was used for *bulk* encryption 356 of the message. */ 357 bulkType = PK11_AlgtagToMechanism(bulkalgtag); 358 switch (bulkType) { 359 case CKM_SKIPJACK_CBC64: 360 case CKM_SKIPJACK_ECB64: 361 case CKM_SKIPJACK_OFB64: 362 case CKM_SKIPJACK_CFB64: 363 case CKM_SKIPJACK_CFB32: 364 case CKM_SKIPJACK_CFB16: 365 case CKM_SKIPJACK_CFB8: 366 /* Skipjack is being used as the bulk encryption algorithm.*/ 367 /* Unwrap the bulk key. */ 368 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL, 369 encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0); 370 break; 371 default: 372 /* Skipjack was not used for bulk encryption of this 373 message. Use Skipjack CBC64, with the nonSkipjackIV 374 part of the KEA key parameters, to decrypt 375 the bulk key. If the optional parameter bulkKeySize is present, 376 bulk key size is different than the encrypted key size */ 377 if (keaParams.bulkKeySize.Length > 0) { 378 err = SEC_ASN1DecodeItem(NULL, &bulkLength, 379 SEC_ASN1_GET(SEC_IntegerTemplate), 380 &keaParams.bulkKeySize); 381 if (err != SECSuccess) 382 goto loser; 383 } 384 385 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV, 386 encKey, bulkType, CKA_DECRYPT, bulkLength); 387 break; 388 } 389 return bulkkey; 390loser: 391 return NULL; 392} 393 394/* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */ 395 396OSStatus 397SecCmsUtilEncryptSymKeyESDH(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef key, 398 CSSM_DATA_PTR encKey, CSSM_DATA_PTR ukm, SECAlgorithmID *keyEncAlg, 399 CSSM_DATA_PTR pubKey) 400{ 401#if 0 /* not yet done */ 402 SECOidTag certalgtag; /* the certificate's encryption algorithm */ 403 SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */ 404 OSStatus rv; 405 CSSM_DATA_PTR params = NULL; 406 int data_len; 407 OSStatus err; 408 SecSymmetricKeyRef tek; 409 SecCertificateRef ourCert; 410 SecPublicKeyRef ourPubKey; 411 SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid; 412 413 certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); 414 PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY); 415 416 /* We really want to show our KEA tag as the key exchange algorithm tag. */ 417 encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN; 418 419 /* Get the public key of the recipient. */ 420 publickey = CERT_ExtractPublicKey(cert); 421 if (publickey == NULL) goto loser; 422 423 /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */ 424 /* XXXX */ourCert = PK11_FindBestKEAMatch(cert, wincx); 425 if (ourCert == NULL) goto loser; 426 427 arena = PORT_NewArena(1024); 428 if (arena == NULL) goto loser; 429 430 /* While we're here, extract the key pair's public key data and copy it into */ 431 /* the outgoing parameters. */ 432 /* XXXX */ourPubKey = CERT_ExtractPublicKey(ourCert); 433 if (ourPubKey == NULL) 434 { 435 goto loser; 436 } 437 SECITEM_CopyItem(arena, pubKey, /* XXX */&(ourPubKey->u.fortezza.KEAKey)); 438 SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */ 439 ourPubKey = NULL; 440 441 /* Extract our private key in order to derive the KEA key. */ 442 ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx); 443 CERT_DestroyCertificate(ourCert); /* we're done with this */ 444 if (!ourPrivKey) goto loser; 445 446 /* If ukm desired, prepare it - allocate enough space (filled with zeros). */ 447 if (ukm) { 448 ukm->Data = (unsigned char*)PORT_ArenaZAlloc(arena,/* XXXX */); 449 ukm->Length = /* XXXX */; 450 } 451 452 /* Generate the KEK (key exchange key) according to RFC2631 which we use 453 * to wrap the bulk encryption key. */ 454 kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE, 455 ukm, NULL, 456 /* XXXX */CKM_KEA_KEY_DERIVE, /* XXXX */CKM_SKIPJACK_WRAP, 457 CKA_WRAP, 0, wincx); 458 459 SECKEY_DestroyPublicKey(publickey); 460 SECKEY_DestroyPrivateKey(ourPrivKey); 461 publickey = NULL; 462 ourPrivKey = NULL; 463 464 if (!kek) 465 goto loser; 466 467 /* allocate space for the encrypted CEK (bulk key) */ 468 encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE); 469 encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE; 470 471 if (encKey->Data == NULL) 472 { 473 CFRelease(kek); 474 goto loser; 475 } 476 477 478 /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */ 479 /* bulk encryption algorithm */ 480 switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo->encalg)) 481 { 482 case /* XXXX */CKM_SKIPJACK_CFB8: 483 err = PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey); 484 whichKEA = SecCmsKEAUsesSkipjack; 485 break; 486 case /* XXXX */CKM_SKIPJACK_CFB8: 487 err = PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey); 488 whichKEA = SecCmsKEAUsesSkipjack; 489 break; 490 default: 491 /* XXXX what do we do here? Neither RC2 nor 3DES... */ 492 err = SECFailure; 493 /* set error */ 494 break; 495 } 496 497 CFRelease(kek); /* we do not need the KEK anymore */ 498 if (err != SECSuccess) 499 goto loser; 500 501 PORT_Assert(whichKEA != SecCmsKEAInvalid); 502 503 /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */ 504 /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */ 505 params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA)); 506 if (params == NULL) 507 goto loser; 508 509 /* now set keyEncAlg */ 510 rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params); 511 if (rv != SECSuccess) 512 goto loser; 513 514 /* XXXXXXX this is not right yet */ 515loser: 516 if (arena) { 517 PORT_FreeArena(arena, PR_FALSE); 518 } 519 if (publickey) { 520 SECKEY_DestroyPublicKey(publickey); 521 } 522 if (ourPrivKey) { 523 SECKEY_DestroyPrivateKey(ourPrivKey); 524 } 525#endif 526 return SECFailure; 527} 528 529SecSymmetricKeyRef 530SecCmsUtilDecryptSymKeyESDH(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg) 531{ 532#if 0 /* not yet done */ 533 OSStatus err; 534 CK_MECHANISM_TYPE bulkType; 535 SecSymmetricKeyRef tek; 536 SecPublicKeyRef originatorPubKey; 537 SecCmsSMIMEKEAParameters keaParams; 538 539 /* XXXX get originator's public key */ 540 originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data, 541 keaParams.originatorKEAKey.Length); 542 if (originatorPubKey == NULL) 543 goto loser; 544 545 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key. 546 The Derive function generates a shared secret and combines it with the originatorRA 547 data to come up with an unique session key */ 548 tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE, 549 &keaParams.originatorRA, NULL, 550 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP, 551 CKA_WRAP, 0, pwfn_arg); 552 SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */ 553 if (tek == NULL) 554 goto loser; 555 556 /* Now that we have the TEK, unwrap the bulk key 557 with which to decrypt the message. */ 558 /* Skipjack is being used as the bulk encryption algorithm.*/ 559 /* Unwrap the bulk key. */ 560 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL, 561 encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0); 562 563 return bulkkey; 564 565loser: 566#endif 567 return NULL; 568} 569 570#endif /* Fortezza, DIffie-Hellman */ 571 572#define CFRELEASE(cf) if(cf != NULL) { CFRelease(cf); } 573 574/* ====== ECDH (Ephemeral-Static Diffie-Hellman) ==================================== */ 575 576#pragma mark ---- ECDH support functions ---- 577 578#ifdef NDEBUG 579#define CSSM_PERROR(f, r) 580#define dprintf(args...) 581#else 582#define CSSM_PERROR(f, r) cssmPerror(f, r) 583#define dprintf(args...) printf(args) 584#endif 585 586/* Length of KeyAgreeRecipientInfo.ukm we create */ 587#define UKM_LENGTH 8 588 589/* KEK algorithm info we generate */ 590#define ECDH_KEK_ALG_TAG SEC_OID_DES_EDE3_CBC 591#define ECDH_KEK_KEY_CSSM_ALGID CSSM_ALGID_3DES_3KEY 592#define ECDH_KEK_ENCR_CSSM_ALGID CSSM_ALGID_3DES_3KEY_EDE 593#define ECDH_KEK_KEY_LEN_BYTES 24 594#define ECDH_KEK_IV_LEN_BYTES 8 595 596#define CMS_DUMP_BUFS 0 597#if CMS_DUMP_BUFS 598 599static void dumpBuf( 600 const char *label, 601 const CSSM_DATA *cd) 602{ 603 unsigned dex; 604 605 printf("%s:\n ", label); 606 for(dex=0; dex<cd->Length; dex++) { 607 printf("%02X ", cd->Data[dex]); 608 if(((dex % 16) == 15) && (dex != (cd->Length - 1))) { 609 printf("\n "); 610 } 611 } 612 putchar('\n'); 613} 614 615#else 616#define dumpBuf(l, d) 617#endif /* CMS_DUMP_BUFS */ 618 619/* 620 * The ECC-CMS-SharedInfo struct, as defined in RFC 3278 8.2, and the 621 * template for DER encoding and decoding it. 622 */ 623typedef struct { 624 SECAlgorithmID algId; /* KEK alg, NULL params */ 625 CSSM_DATA entityUInfo; /* optional, ukm */ 626 CSSM_DATA suppPubInfo; /* length of KEK in bits as 4-byte integer */ 627} ECC_CMS_SharedInfo; 628 629static const SecAsn1Template ECC_CMS_SharedInfoTemplate[] = { 630 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ECC_CMS_SharedInfo) }, 631 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0, 632 offsetof(ECC_CMS_SharedInfo,entityUInfo), 633 kSecAsn1OctetStringTemplate }, 634 { SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 2, 635 offsetof(ECC_CMS_SharedInfo,suppPubInfo), 636 kSecAsn1OctetStringTemplate }, 637 { 0 } 638}; 639 640/* 641 * Given a context specified via a CSSM_CC_HANDLE, add a new 642 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType, 643 * AttributeLength, and an untyped pointer. 644 */ 645/* specify either 32-bit integer or a pointer as an added attribute value */ 646typedef enum { 647 CAT_Uint32, 648 CAT_Ptr 649} ContextAttrType; 650 651static CSSM_RETURN cmsAddContextAttribute( 652 CSSM_CC_HANDLE CCHandle, 653 uint32 AttributeType, 654 uint32 AttributeLength, 655 ContextAttrType attrType, 656 /* specify exactly one of these */ 657 const void *AttributePtr, 658 uint32 attributeInt) 659{ 660 CSSM_CONTEXT_ATTRIBUTE newAttr; 661 CSSM_RETURN crtn; 662 663 newAttr.AttributeType = AttributeType; 664 newAttr.AttributeLength = AttributeLength; 665 if(attrType == CAT_Uint32) { 666 newAttr.Attribute.Uint32 = attributeInt; 667 } 668 else { 669 /* this is a union of a bunch of different pointers...*/ 670 newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr; 671 } 672 crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr); 673 if(crtn) { 674 CSSM_PERROR("CSSM_UpdateContextAttributes", crtn); 675 } 676 return crtn; 677} 678 679static CSSM_RETURN cmsGenRand( 680 CSSM_CSP_HANDLE cspHand, 681 CSSM_SIZE len, 682 uint8 *randOut) 683{ 684 CSSM_CC_HANDLE ccHand = 0; 685 CSSM_DATA randData = {len, randOut}; 686 687 CSSM_RETURN crtn = CSSM_CSP_CreateRandomGenContext(cspHand, 688 CSSM_ALGID_APPLE_YARROW, 689 NULL, /* seed*/ 690 len, 691 &ccHand); 692 if(crtn) { 693 CSSM_PERROR("CSSM_CSP_CreateRandomGenContext", crtn); 694 return crtn; 695 } 696 crtn = CSSM_GenerateRandom(ccHand, &randData); 697 CSSM_DeleteContext(ccHand); 698 if(crtn) { 699 CSSM_PERROR("CSSM_GenerateRandom", crtn); 700 } 701 return crtn; 702} 703 704/* convert uint32 to big-endian 4 bytes */ 705static void int32ToBytes( 706 uint32_t i, 707 unsigned char *b) 708{ 709 int dex; 710 for(dex=3; dex>=0; dex--) { 711 b[dex] = i; 712 i >>= 8; 713 } 714} 715 716/* 717 * NULL wrap a ref key to raw key in default format. 718 */ 719static OSStatus cmsNullWrapKey( 720 CSSM_CSP_HANDLE cspHand, 721 const CSSM_KEY *refKey, 722 CSSM_KEY_PTR rawKey) 723{ 724 CSSM_DATA descData = {0, 0}; 725 CSSM_RETURN crtn; 726 CSSM_CC_HANDLE ccHand; 727 CSSM_ACCESS_CREDENTIALS creds; 728 uint32 keyAttr; 729 730 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); 731 memset(rawKey, 0, sizeof(CSSM_KEY)); 732 733 crtn = CSSM_CSP_CreateSymmetricContext(cspHand, 734 CSSM_ALGID_NONE, 735 CSSM_ALGMODE_NONE, 736 &creds, 737 NULL, // unwrappingKey 738 NULL, // initVector 739 CSSM_PADDING_NONE, 740 0, // Params 741 &ccHand); 742 if(crtn) { 743 CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", crtn); 744 return crtn; 745 } 746 747 keyAttr = rawKey->KeyHeader.KeyAttr; 748 keyAttr &= ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE | 749 CSSM_KEYATTR_MODIFIABLE); 750 keyAttr |= CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE; 751 crtn = CSSM_WrapKey(ccHand, 752 &creds, 753 refKey, 754 &descData, 755 rawKey); 756 if(crtn != CSSM_OK) { 757 CSSM_PERROR("CSSM_WrapKey", crtn); 758 } 759 CSSM_DeleteContext(ccHand); 760 return crtn; 761} 762 763/* 764 * Free memory via specified plugin's app-level allocator 765 */ 766static void cmsFreeCssmMemory( 767 CSSM_HANDLE hand, 768 void *p) 769{ 770 CSSM_API_MEMORY_FUNCS memFuncs; 771 CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs); 772 if(crtn) { 773 return; 774 } 775 memFuncs.free_func(p, memFuncs.AllocRef); 776} 777 778/* 779 * Given an OID tag, return key size and mode. 780 * NOTE: ciphers with variable key sizes, like RC2, RC4, and RC5 cannot 781 * be used here because the message does not contain a key size 782 * indication. 783 */ 784static OSStatus encrAlgInfo( 785 SECOidTag oidTag, 786 uint32 *keySizeBits, /* RETURNED */ 787 CSSM_ENCRYPT_MODE *mode) /* RETURNED */ 788{ 789 *keySizeBits = 64; /* default */ 790 *mode = CSSM_ALGMODE_CBCPadIV8; /* default */ 791 792 switch(oidTag) { 793 case SEC_OID_RC2_CBC: 794 case SEC_OID_RC4: 795 case SEC_OID_RC5_CBC_PAD: 796 dprintf("encrAlgInfo: key size unknowable\n"); 797 return errSecDataNotAvailable; 798 799 case SEC_OID_DES_EDE3_CBC: 800 *keySizeBits = 192; 801 break; 802 case SEC_OID_DES_EDE: 803 /* Not sure about this; SecCmsCipherContextStart() treats this 804 * like SEC_OID_DES_EDE3_CBC... */ 805 case SEC_OID_DES_ECB: 806 *mode = CSSM_ALGMODE_ECB; 807 break; 808 case SEC_OID_DES_CBC: 809 *mode = CSSM_ALGMODE_CBC; 810 break; 811 case SEC_OID_AES_128_CBC: 812 *keySizeBits = 128; 813 break; 814 case SEC_OID_AES_192_CBC: 815 *keySizeBits = 192; 816 break; 817 case SEC_OID_AES_256_CBC: 818 *keySizeBits = 256; 819 break; 820 case SEC_OID_AES_128_ECB: 821 *keySizeBits = 128; 822 *mode = CSSM_ALGMODE_ECB; 823 break; 824 case SEC_OID_AES_192_ECB: 825 *keySizeBits = 192; 826 *mode = CSSM_ALGMODE_ECB; 827 break; 828 case SEC_OID_AES_256_ECB: 829 *keySizeBits = 256; 830 *mode = CSSM_ALGMODE_ECB; 831 break; 832 case SEC_OID_DES_OFB: 833 *mode = CSSM_ALGMODE_OFB; 834 break; 835 case SEC_OID_DES_CFB: 836 *mode = CSSM_ALGMODE_CFB; 837 break; 838 default: 839 dprintf("encrAlgInfo: unknown alg tag (%d)\n", (int)oidTag); 840 return errSecDataNotAvailable; 841 } 842 return noErr; 843} 844 845#pragma mark ---- ECDH CEK key wrap ---- 846 847/* 848 * Encrypt bulk encryption key (a.k.a. content encryption key, CEK) using ECDH 849 */ 850OSStatus 851SecCmsUtilEncryptSymKeyECDH( 852 PLArenaPool *poolp, 853 SecCertificateRef cert, /* recipient's cert */ 854 SecSymmetricKeyRef key, /* bulk key */ 855 /* remaining fields RETURNED */ 856 CSSM_DATA_PTR encKey, /* encrypted key --> recipientEncryptedKeys[0].EncryptedKey */ 857 CSSM_DATA_PTR ukm, /* random UKM --> KeyAgreeRecipientInfo.ukm */ 858 SECAlgorithmID *keyEncAlg, /* alg := dhSinglePass-stdDH-sha1kdf-scheme 859 * params := another encoded AlgId, with the KEK alg and IV */ 860 CSSM_DATA_PTR pubKey) /* our pub key as ECPoint --> 861 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */ 862{ 863 OSStatus rv = noErr; 864 CSSM_KEY ourPrivKeyCssm; 865 CSSM_KEY ourPubKeyCssm; 866 SecKeyRef theirPubKeyRef = NULL; 867 CSSM_KEY_PTR theirPubKeyCssm = NULL; 868 const CSSM_KEY *cekCssmRef = NULL; 869 uint32 ecdhKeySizeBits; 870 CSSM_CSP_HANDLE rawCspHand = SecCspHandleForAlgorithm(CSSM_ALGID_ECDH); 871 CSSM_CC_HANDLE ccHand = 0; 872 CSSM_RETURN crtn; 873 CSSM_DATA keyLabel = {8, (uint8 *)"tempKey"}; 874 SECAlgorithmID kekAlgId; 875 uint8 iv[ECDH_KEK_IV_LEN_BYTES]; 876 CSSM_DATA ivData = {ECDH_KEK_IV_LEN_BYTES, iv}; 877 SECOidData *kekOid; 878 ECC_CMS_SharedInfo sharedInfo; 879 CSSM_DATA sharedInfoEnc = {0, NULL}; 880 uint8 nullData[2] = {SEC_ASN1_NULL, 0}; 881 uint8 keyLenAsBytes[4]; 882 CSSM_KEY kekDerive; 883 CSSM_DATA certData; 884 CSSM_CL_HANDLE clHand; 885 CSSM_ACCESS_CREDENTIALS creds; 886 CSSM_DATA paramData = {0, NULL}; 887 CSSM_KEY cekCssm; 888 CSSM_CSP_HANDLE refCspHand; 889 CSSM_SIZE bytesEncrypted; 890 CSSM_DATA remData = {0, NULL}; 891 CSSM_DATA ctext = {0, NULL}; 892 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO subjPubKey; 893 894 if(rawCspHand == 0) { 895 return internalComponentErr; 896 } 897 898 memset(&ourPrivKeyCssm, 0, sizeof(CSSM_KEY)); 899 memset(&ourPubKeyCssm, 0, sizeof(CSSM_KEY)); 900 memset(&cekCssm, 0, sizeof(CSSM_KEY)); 901 memset(&kekDerive, 0, sizeof(kekDerive)); 902 903 encKey->Data = NULL; 904 encKey->Length = 0; 905 906 /* 907 * Create our ECDH key pair matching the recipient's key. 908 * Get the public key in "read-only" OCTET_STRING format, which 909 * is the ECPoint we put in 910 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey. 911 */ 912 rv = SecCertificateGetData(cert, &certData); 913 if(rv) { 914 CSSM_PERROR("SecCertificateGetData", rv); 915 return rv; 916 } 917 rv = SecCertificateGetCLHandle(cert, &clHand); 918 if(rv) { 919 CSSM_PERROR("SecCertificateGetCLHandle", rv); 920 return rv; 921 } 922 rv = CSSM_CL_CertGetKeyInfo(clHand, &certData, &theirPubKeyCssm); 923 if(rv) { 924 CSSM_PERROR("CSSM_CL_CertGetKeyInfo", rv); 925 return rv; 926 } 927 928 /* 929 * Verify the EC curve of the recipient's public key. It's in the 930 * public key's AlgId.parameters as an OID. The key we were 931 * given is in CSSM_X509_SUBJECT_PUBLIC_KEY_INFO form. 932 */ 933 memset(&subjPubKey, 0, sizeof(subjPubKey)); 934 if(SEC_ASN1DecodeItem(poolp, &subjPubKey, kSecAsn1SubjectPublicKeyInfoTemplate, 935 &theirPubKeyCssm->KeyData)) { 936 dprintf("SecCmsUtilEncryptSymKeyECDH: error decoding SubjPubKey\n"); 937 /* oh well, keep going */ 938 } 939 else { 940 if(subjPubKey.algorithm.parameters.Data != NULL) { 941 CSSM_DATA curveOid; 942 if(SEC_ASN1DecodeItem(poolp, &curveOid, kSecAsn1ObjectIDTemplate, 943 &subjPubKey.algorithm.parameters)) { 944 dprintf("SecCmsUtilEncryptSymKeyECDH: error decoding curveOid\n"); 945 /* oh well, keep going */ 946 } 947 else { 948 /* We have the curve OID. Any other errors are fatal. */ 949 SECOidTag oidTag = SECOID_FindOIDTag(&curveOid); 950 switch(oidTag) { 951 case SEC_OID_SECP_256_R1: 952 case SEC_OID_SECP_384_R1: 953 case SEC_OID_SECP_521_R1: 954 break; 955 default: 956 dprintf("SecCmsUtilEncryptSymKeyECDH: unsupported curveOid\n"); 957 rv = CSSMERR_CSP_INVALID_KEY; 958 goto loser; 959 } 960 } 961 } 962 } 963 964 ecdhKeySizeBits = theirPubKeyCssm->KeyHeader.LogicalKeySizeInBits; 965 crtn = CSSM_CSP_CreateKeyGenContext(rawCspHand, 966 CSSM_ALGID_ECDSA, 967 ecdhKeySizeBits, 968 NULL, // Seed 969 NULL, // Salt 970 NULL, // StartDate 971 NULL, // EndDate 972 NULL, // Params 973 &ccHand); 974 if(crtn) { 975 CSSM_PERROR("CSSM_CSP_CreateKeyGenContext", crtn); 976 rv = crtn; 977 goto loser; 978 } 979 crtn = cmsAddContextAttribute(ccHand, 980 CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT, 981 sizeof(uint32), 982 CAT_Uint32, 983 NULL, 984 CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING); 985 if(crtn) { 986 CSSM_PERROR("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT)", crtn); 987 rv = crtn; 988 goto loser; 989 } 990 991 crtn = CSSM_GenerateKeyPair(ccHand, 992 CSSM_KEYUSE_DERIVE, 993 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, 994 &keyLabel, 995 &ourPubKeyCssm, 996 CSSM_KEYUSE_DERIVE, 997 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE, 998 &keyLabel, 999 NULL, // CredAndAclEntry 1000 &ourPrivKeyCssm); 1001 CSSM_DeleteContext(ccHand); 1002 ccHand = 0; 1003 if(crtn) { 1004 CSSM_PERROR("CSSM_GenerateKeyPair", crtn); 1005 rv = crtn; 1006 goto loser; 1007 } 1008 pubKey->Length = ourPubKeyCssm.KeyData.Length; 1009 pubKey->Data = (uint8 *)PORT_ArenaAlloc(poolp, pubKey->Length); 1010 memmove(pubKey->Data, ourPubKeyCssm.KeyData.Data, pubKey->Length); 1011 dumpBuf("sender's public key", pubKey); 1012 1013 /* 1014 * Cook up random UKM 1015 */ 1016 ukm->Data = (uint8 *)PORT_ArenaAlloc(poolp, UKM_LENGTH); 1017 ukm->Length = UKM_LENGTH; 1018 crtn = cmsGenRand(rawCspHand, UKM_LENGTH, ukm->Data); 1019 if(crtn) { 1020 goto loser; 1021 } 1022 dumpBuf("sender UKM", ukm); 1023 1024 /* 1025 * OK, we have to set up a weird SECAlgorithmID. 1026 * algorithm = dhSinglePass-stdDH-sha1kdf-scheme 1027 * params = an encoded SECAlgorithmID representing the KEK algorithm, with 1028 * algorithm = whatever we pick 1029 * parameters = IV as octet string (though I haven't seen that specified 1030 * anywhere; it's how the CEK IV is encoded) 1031 * 1032 * First, the 8-byte random IV, encoded as octet string 1033 */ 1034 crtn = cmsGenRand(rawCspHand, ECDH_KEK_IV_LEN_BYTES, iv); 1035 if(crtn) { 1036 goto loser; 1037 } 1038 dumpBuf("sender IV", &ivData); 1039 1040 memset(&kekAlgId, 0, sizeof(kekAlgId)); 1041 if (!SEC_ASN1EncodeItem(poolp, &kekAlgId.parameters, 1042 &ivData, kSecAsn1OctetStringTemplate)) { 1043 rv = internalComponentErr; 1044 goto loser; 1045 } 1046 1047 /* Drop in the KEK OID and encode the whole thing */ 1048 kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG); 1049 if(kekOid == NULL) { 1050 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n"); 1051 rv = internalComponentErr; 1052 goto loser; 1053 } 1054 kekAlgId.algorithm = kekOid->oid; 1055 memset(keyEncAlg, 0, sizeof(*keyEncAlg)); 1056 if (!SEC_ASN1EncodeItem(poolp, &keyEncAlg->parameters, 1057 &kekAlgId, SECOID_AlgorithmIDTemplate)) { 1058 rv = internalComponentErr; 1059 goto loser; 1060 } 1061 kekOid = SECOID_FindOIDByTag(SEC_OID_DH_SINGLE_STD_SHA1KDF); 1062 if(kekOid == NULL) { 1063 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n"); 1064 rv = internalComponentErr; 1065 goto loser; 1066 } 1067 keyEncAlg->algorithm = kekOid->oid; 1068 1069 /* 1070 * Now in order to derive the KEK proper, we have to create a 1071 * ECC-CMS-SharedInfo, which does not appear in the message, and DER 1072 * encode that struct, the result of which is used as the 1073 * SharedInfo value in the KEK key derive. 1074 */ 1075 memset(&sharedInfo, 0, sizeof(sharedInfo)); 1076 kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG); 1077 sharedInfo.algId.algorithm = kekOid->oid; 1078 sharedInfo.algId.parameters.Data = nullData; 1079 sharedInfo.algId.parameters.Length = 2; 1080 sharedInfo.entityUInfo = *ukm; 1081 int32ToBytes(ECDH_KEK_KEY_LEN_BYTES << 3, keyLenAsBytes); 1082 sharedInfo.suppPubInfo.Length = 4; 1083 sharedInfo.suppPubInfo.Data = keyLenAsBytes; 1084 if (!SEC_ASN1EncodeItem(poolp, &sharedInfoEnc, 1085 &sharedInfo, ECC_CMS_SharedInfoTemplate)) { 1086 rv = internalComponentErr; 1087 goto loser; 1088 } 1089 dumpBuf("sender encoded SharedInfo", &sharedInfoEnc); 1090 1091 /* 1092 * Since we're using the raw CSP here, we can provide the "other" public 1093 * key as an actual CSSM_KEY. When unwrapping, we won't be able to do that 1094 * since we'll be using our private key obtained from a SecIdentityRef. 1095 */ 1096 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); 1097 crtn = CSSM_CSP_CreateDeriveKeyContext(rawCspHand, 1098 CSSM_ALGID_ECDH_X963_KDF, 1099 ECDH_KEK_KEY_CSSM_ALGID, // algorithm of the KEK 1100 ECDH_KEK_KEY_LEN_BYTES * 8, 1101 &creds, 1102 &ourPrivKeyCssm, // BaseKey 1103 0, // IterationCount 1104 &sharedInfoEnc, // Salt 1105 0, // Seed 1106 &ccHand); 1107 if(crtn) { 1108 CSSM_PERROR("CSSM_CSP_CreateDeriveKeyContext", crtn); 1109 rv = crtn; 1110 goto loser; 1111 } 1112 1113 /* add recipient's pub key as a context attr */ 1114 crtn = cmsAddContextAttribute(ccHand, 1115 CSSM_ATTRIBUTE_PUBLIC_KEY, 1116 sizeof(CSSM_KEY), 1117 CAT_Ptr, 1118 (void *)theirPubKeyCssm, 1119 0); 1120 if(crtn) { 1121 rv = crtn; 1122 goto loser; 1123 } 1124 1125 /* Derive the KEK */ 1126 crtn = CSSM_DeriveKey(ccHand, 1127 ¶mData, 1128 CSSM_KEYUSE_ANY, 1129 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE, 1130 &keyLabel, 1131 NULL, // cread/acl 1132 &kekDerive); 1133 if(crtn) { 1134 CSSM_PERROR("CSSM_DeriveKey", crtn); 1135 rv = crtn; 1136 goto loser; 1137 } 1138 CSSM_DeleteContext(ccHand); 1139 ccHand = 0; 1140 1141 /* 1142 * Obtain the raw CEK bits. 1143 */ 1144 rv = SecKeyGetCSSMKey(key, &cekCssmRef); 1145 if(rv) { 1146 CSSM_PERROR("SecKeyGetCSSMKey", rv); 1147 goto loser; 1148 } 1149 rv = SecKeyGetCSPHandle(key, &refCspHand); 1150 if(rv) { 1151 CSSM_PERROR("SecKeyGetCSPHandle", rv); 1152 goto loser; 1153 } 1154 rv = cmsNullWrapKey(refCspHand, cekCssmRef, &cekCssm); 1155 if(rv) { 1156 goto loser; 1157 } 1158 1159 /* 1160 * Finally, encrypt the raw CEK bits with the KEK we just derived 1161 */ 1162 crtn = CSSM_CSP_CreateSymmetricContext(rawCspHand, 1163 ECDH_KEK_ENCR_CSSM_ALGID, 1164 CSSM_ALGMODE_CBCPadIV8, 1165 NULL, // access cred 1166 &kekDerive, 1167 &ivData, // InitVector 1168 CSSM_PADDING_PKCS7, 1169 NULL, // Params 1170 &ccHand); 1171 if(rv) { 1172 CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", rv); 1173 goto loser; 1174 } 1175 rv = CSSM_EncryptData(ccHand, 1176 &cekCssm.KeyData, 1177 1, 1178 &ctext, 1179 1, 1180 &bytesEncrypted, 1181 &remData); 1182 if(rv) { 1183 CSSM_PERROR("CSSM_EncryptData", rv); 1184 goto loser; 1185 } 1186 encKey->Data = PORT_ArenaAlloc(poolp, bytesEncrypted); 1187 encKey->Length = bytesEncrypted; 1188 memmove(encKey->Data, ctext.Data, ctext.Length); 1189 if(bytesEncrypted != ctext.Length) { 1190 memmove(encKey->Data + ctext.Length, remData.Data, remData.Length); 1191 } 1192 dumpBuf("sender encKey", encKey); 1193 1194loser: 1195 if(ccHand) { 1196 CSSM_DeleteContext(ccHand); 1197 } 1198 CFRELEASE(theirPubKeyRef); 1199 if(ourPubKeyCssm.KeyData.Data) { 1200 CSSM_FreeKey(rawCspHand, NULL, &ourPubKeyCssm, CSSM_FALSE); 1201 } 1202 if(ourPrivKeyCssm.KeyData.Data) { 1203 CSSM_FreeKey(rawCspHand, NULL, &ourPrivKeyCssm, CSSM_FALSE); 1204 } 1205 if(ctext.Data) { 1206 cmsFreeCssmMemory(rawCspHand, ctext.Data); 1207 } 1208 if(remData.Data) { 1209 cmsFreeCssmMemory(rawCspHand, remData.Data); 1210 } 1211 if(cekCssm.KeyData.Data) { 1212 CSSM_FreeKey(refCspHand, NULL, &cekCssm, CSSM_FALSE); 1213 } 1214 if(kekDerive.KeyData.Data) { 1215 CSSM_FreeKey(rawCspHand, NULL, &kekDerive, CSSM_FALSE); 1216 } 1217 if(theirPubKeyCssm) { 1218 /* Allocated by CL */ 1219 cmsFreeCssmMemory(clHand, theirPubKeyCssm->KeyData.Data); 1220 cmsFreeCssmMemory(clHand, theirPubKeyCssm); 1221 } 1222 return rv; 1223} 1224 1225#pragma mark ---- ECDH CEK key unwrap ---- 1226 1227SecSymmetricKeyRef 1228SecCmsUtilDecryptSymKeyECDH( 1229 SecPrivateKeyRef privkey, /* our private key */ 1230 CSSM_DATA_PTR encKey, /* encrypted CEK */ 1231 CSSM_DATA_PTR ukm, /* random UKM from KeyAgreeRecipientInfo.ukm */ 1232 SECAlgorithmID *keyEncAlg, /* alg := dhSinglePass-stdDH-sha1kdf-scheme 1233 * params := another encoded AlgId, with the KEK alg and IV */ 1234 SECOidTag bulkalgtag, /* algorithm of returned key */ 1235 CSSM_DATA_PTR pubKey) /* sender's pub key as ECPoint from 1236 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */ 1237 1238{ 1239 SecSymmetricKeyRef outKey = NULL; 1240 OSStatus rv = noErr; 1241 const CSSM_KEY *ourPrivKeyCssm; 1242 PLArenaPool *pool = NULL; 1243 SECAlgorithmID keyAlgParam; 1244 SECOidData *kekOid = NULL; 1245 CSSM_DATA iv = {0, NULL}; 1246 ECC_CMS_SharedInfo sharedInfo; 1247 CSSM_DATA sharedInfoEnc = {0, NULL}; 1248 uint8 nullData[2] = {SEC_ASN1_NULL, 0}; 1249 uint8 keyLenAsBytes[4]; 1250 CSSM_ENCRYPT_MODE kekMode; 1251 uint32 kekSizeBits; 1252 CSSM_KEY kekDerive; 1253 CSSM_RETURN crtn; 1254 CSSM_ACCESS_CREDENTIALS creds; 1255 CSSM_CSP_HANDLE refCspHand; 1256 CSSM_CC_HANDLE ccHand = 0; 1257 CSSM_DATA keyLabel = {8, (uint8 *)"tempKey"}; 1258 const CSSM_ACCESS_CREDENTIALS *accessCred; 1259 CSSM_KEY wrappedKey; 1260 CSSM_KEY unwrappedKey; 1261 CSSM_ALGORITHMS bulkAlg; 1262 CSSM_DATA descriptiveData = {0, NULL}; 1263 1264 dumpBuf("receiver encKey", encKey); 1265 1266 memset(&kekDerive, 0, sizeof(kekDerive)); 1267 1268 /* our private key in CSSM form */ 1269 rv = SecKeyGetCSSMKey(privkey, &ourPrivKeyCssm); 1270 if(rv) { 1271 CSSM_PERROR("SecKeyGetCSSMKey", rv); 1272 goto loser; 1273 } 1274 1275 /* 1276 * Decode keyEncAlg.params to get KEK algorithm and IV 1277 */ 1278 pool = PORT_NewArena(1024); 1279 if(pool == NULL) { 1280 goto loser; 1281 } 1282 memset(&keyAlgParam, 0, sizeof(keyAlgParam)); 1283 if(SEC_ASN1DecodeItem(pool, &keyAlgParam, SECOID_AlgorithmIDTemplate, 1284 &keyEncAlg->parameters)) { 1285 dprintf("SecCmsUtilDecryptSymKeyECDH: error decoding keyAlgParams\n"); 1286 goto loser; 1287 } 1288 kekOid = SECOID_FindOID(&keyAlgParam.algorithm); 1289 if(kekOid == NULL) { 1290 dprintf("SecCmsUtilDecryptSymKeyECDH: unknown KEK enc OID\n"); 1291 goto loser; 1292 } 1293 rv = encrAlgInfo(kekOid->offset, &kekSizeBits, &kekMode); 1294 if(rv) { 1295 goto loser; 1296 } 1297 /* IV is OCTET STRING in the alg params */ 1298 if(SEC_ASN1DecodeItem(pool, &iv, kSecAsn1OctetStringTemplate, 1299 &keyAlgParam.parameters)) { 1300 /* 1301 * Not sure here - is it legal to have no IV? I haven't seen this 1302 * addressed in any spec. Maybe we should condition the behavior 1303 * here on the KEK algorithm. 1304 */ 1305 dprintf("SecCmsUtilDecryptSymKeyECDH: no KEK IV\n"); 1306 goto loser; 1307 } 1308 1309 /* 1310 * Now in order to derive the KEK proper, we have to create a 1311 * ECC-CMS-SharedInfo, which does not appear in the message, and DER 1312 * encode that struct, the result of which is used as the 1313 * SharedInfo value in the KEK key derive. 1314 */ 1315 memset(&sharedInfo, 0, sizeof(sharedInfo)); 1316 sharedInfo.algId.algorithm = kekOid->oid; 1317 sharedInfo.algId.parameters.Data = nullData; 1318 sharedInfo.algId.parameters.Length = 2; 1319 sharedInfo.entityUInfo = *ukm; 1320 int32ToBytes(kekSizeBits, keyLenAsBytes); 1321 sharedInfo.suppPubInfo.Length = 4; 1322 sharedInfo.suppPubInfo.Data = keyLenAsBytes; 1323 if (!SEC_ASN1EncodeItem(pool, &sharedInfoEnc, 1324 &sharedInfo, ECC_CMS_SharedInfoTemplate)) { 1325 rv = internalComponentErr; 1326 goto loser; 1327 } 1328 dumpBuf("receiver encoded SharedInfo", &sharedInfoEnc); 1329 dumpBuf("receiver IV", &iv); 1330 dumpBuf("receiver UKM", ukm); 1331 dumpBuf("sender's public key", pubKey); 1332 1333 /* 1334 * Using the Sec-layer CSPDL, "other's" public key specified as ECPOint param. Which 1335 * is fortunate because that's what we have... 1336 */ 1337 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); 1338 rv = SecKeyGetCSPHandle(privkey, &refCspHand); 1339 if(rv) { 1340 CSSM_PERROR("SecKeyGetCSPHandle", rv); 1341 goto loser; 1342 } 1343 rv = SecKeyGetCredentials(privkey, 1344 CSSM_ACL_AUTHORIZATION_DERIVE, 1345 kSecCredentialTypeDefault, 1346 &accessCred); 1347 if (rv) { 1348 CSSM_PERROR("SecKeyGetCredentials", rv); 1349 goto loser; 1350 } 1351 crtn = CSSM_CSP_CreateDeriveKeyContext(refCspHand, 1352 CSSM_ALGID_ECDH_X963_KDF, 1353 kekOid->cssmAlgorithm, // algorithm of the KEK 1354 kekSizeBits, 1355 &creds, 1356 ourPrivKeyCssm, // BaseKey 1357 0, // IterationCount 1358 &sharedInfoEnc, // Salt 1359 0, // Seed 1360 &ccHand); 1361 if(crtn) { 1362 CSSM_PERROR("CSSM_CSP_CreateDeriveKeyContext", crtn); 1363 goto loser; 1364 } 1365 crtn = CSSM_DeriveKey(ccHand, 1366 pubKey, // param 1367 CSSM_KEYUSE_ANY, 1368 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE, 1369 &keyLabel, 1370 NULL, // cred/acl 1371 &kekDerive); 1372 CSSM_DeleteContext(ccHand); 1373 ccHand = 0; 1374 if(crtn) { 1375 CSSM_PERROR("CSSM_DeriveKey", crtn); 1376 goto loser; 1377 } 1378 1379 /* 1380 * Decrypt the encrypted key bits with the KEK key. 1381 */ 1382 crtn = CSSM_CSP_CreateSymmetricContext(refCspHand, 1383 kekOid->cssmAlgorithm, 1384 kekMode, 1385 NULL, // access cred 1386 &kekDerive, 1387 &iv, // InitVector 1388 /* FIXME is this variable too? */ 1389 CSSM_PADDING_PKCS7, 1390 NULL, // Params 1391 &ccHand); 1392 if(rv) { 1393 CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", rv); 1394 goto loser; 1395 } 1396 1397 memset(&wrappedKey, 0, sizeof(CSSM_KEY)); 1398 memset(&unwrappedKey, 0, sizeof(CSSM_KEY)); 1399 1400 bulkAlg = SECOID_FindyCssmAlgorithmByTag(bulkalgtag); 1401 if(bulkAlg == CSSM_ALGID_NONE) { 1402 dprintf("SecCmsUtilDecryptSymKeyECDH: unknown bulk alg\n"); 1403 goto loser; 1404 } 1405 1406 wrappedKey.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; 1407 wrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_WRAPPED; 1408 wrappedKey.KeyHeader.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7; 1409 wrappedKey.KeyHeader.AlgorithmId = bulkAlg; 1410 wrappedKey.KeyHeader.KeyClass = CSSM_KEYCLASS_SESSION_KEY; 1411 wrappedKey.KeyHeader.WrapAlgorithmId = kekOid->cssmAlgorithm; 1412 wrappedKey.KeyHeader.WrapMode = CSSM_ALGMODE_NONE; 1413 wrappedKey.KeyData = *encKey; 1414 1415 crtn = CSSM_UnwrapKey(ccHand, 1416 NULL, /* publicKey */ 1417 &wrappedKey, 1418 CSSM_KEYUSE_DECRYPT, 1419 CSSM_KEYATTR_EXTRACTABLE, 1420 &keyLabel, 1421 NULL, /* rcc */ 1422 &unwrappedKey, 1423 &descriptiveData); 1424 CSSM_DeleteContext(ccHand); 1425 ccHand = 0; 1426 if(crtn) { 1427 CSSM_PERROR("CSSM_UnwrapKey", crtn); 1428 goto loser; 1429 } 1430 rv = SecKeyCreateWithCSSMKey(&unwrappedKey, &outKey); 1431 if (rv) { 1432 CSSM_PERROR("SecKeyCreateWithCSSMKey", rv); 1433 } 1434 1435loser: 1436 if(pool != NULL) { 1437 PORT_FreeArena(pool, PR_FALSE); 1438 } 1439 if(kekDerive.KeyData.Data) { 1440 CSSM_FreeKey(refCspHand, NULL, &kekDerive, CSSM_FALSE); 1441 } 1442 if(outKey == NULL) { 1443 PORT_SetError(SEC_ERROR_NO_KEY); 1444 } 1445 return outKey; 1446} 1447 1448 1449 1450