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 "SecAsn1Item.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_asn1/secport.h> 47 48#include <Security/SecCertificateInternal.h> 49#include <Security/SecKeyPriv.h> 50 51/* ====== RSA ======================================================================= */ 52 53/* 54 * SecCmsUtilEncryptSymKeyRSA - wrap a symmetric key with RSA 55 * 56 * this function takes a symmetric key and encrypts it using an RSA public key 57 * according to PKCS#1 and RFC2633 (S/MIME) 58 */ 59OSStatus 60SecCmsUtilEncryptSymKeyRSA(PLArenaPool *poolp, SecCertificateRef cert, 61 SecSymmetricKeyRef bulkkey, 62 SecAsn1Item * encKey) 63{ 64 OSStatus rv; 65 SecPublicKeyRef publickey; 66#if USE_CDSA_CRYPTO 67 rv = SecCertificateCopyPublicKey(cert,&publickey); 68#else 69 publickey = SecCertificateCopyPublicKey(cert); 70#endif 71 if (publickey == NULL) 72 return SECFailure; 73 74 rv = SecCmsUtilEncryptSymKeyRSAPubKey(poolp, publickey, bulkkey, encKey); 75 CFRelease(publickey); 76 return rv; 77} 78 79OSStatus 80SecCmsUtilEncryptSymKeyRSAPubKey(PLArenaPool *poolp, 81 SecPublicKeyRef publickey, 82 SecSymmetricKeyRef bulkkey, SecAsn1Item * encKey) 83{ 84 OSStatus rv; 85 size_t data_len; 86 //KeyType keyType; 87 void *mark = NULL; 88 89 mark = PORT_ArenaMark(poolp); 90 if (!mark) 91 goto loser; 92 93#if 0 94 /* sanity check */ 95 keyType = SECKEY_GetPublicKeyType(publickey); 96 PORT_Assert(keyType == rsaKey); 97 if (keyType != rsaKey) { 98 goto loser; 99 } 100#endif 101 /* allocate memory for the encrypted key */ 102#if USE_CDSA_CRYPTO 103 rv = SecKeyGetStrengthInBits(publickey, NULL, &data_len); 104 if (rv) 105 goto loser; 106 // Convert length to bytes; 107 data_len >>= 2; 108#else 109 data_len = SecKeyGetSize(publickey, kSecKeyEncryptedDataSize); 110#endif 111 112 encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len); 113 encKey->Length = data_len; 114 if (encKey->Data == NULL) 115 goto loser; 116 117 /* encrypt the key now */ 118 rv = WRAP_PubWrapSymKey(publickey, bulkkey, encKey); 119 if (rv != SECSuccess) 120 goto loser; 121 122 PORT_ArenaUnmark(poolp, mark); 123 return SECSuccess; 124 125loser: 126 if (mark) { 127 PORT_ArenaRelease(poolp, mark); 128 } 129 return SECFailure; 130} 131 132/* 133 * SecCmsUtilDecryptSymKeyRSA - unwrap a RSA-wrapped symmetric key 134 * 135 * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric 136 * key handle. Please note that the actual unwrapped key data may not be allowed to leave 137 * a hardware token... 138 */ 139SecSymmetricKeyRef 140SecCmsUtilDecryptSymKeyRSA(SecPrivateKeyRef privkey, SecAsn1Item * encKey, SECOidTag bulkalgtag) 141{ 142 /* that's easy */ 143 return WRAP_PubUnwrapSymKey(privkey, encKey, bulkalgtag); 144} 145 146#if 0 147// @@@ Implement Fortezza and Diffie hellman support 148 149/* ====== MISSI (Fortezza) ========================================================== */ 150 151extern const SecAsn1Template NSS_SMIMEKEAParamTemplateAllParams[]; 152 153OSStatus 154SecCmsUtilEncryptSymKeyMISSI(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef bulkkey, 155 SECOidTag symalgtag, SecAsn1Item * encKey, SecAsn1Item * *pparams, void *pwfn_arg) 156{ 157 SECOidTag certalgtag; /* the certificate's encryption algorithm */ 158 SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */ 159 OSStatus rv = SECFailure; 160 SecAsn1Item * params = NULL; 161 OSStatus err; 162 SecSymmetricKeyRef tek; 163 SecCertificateRef ourCert; 164 SecPublicKeyRef ourPubKey, *publickey = NULL; 165 SecPrivateKeyRef ourPrivKey = NULL; 166 SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid; 167 SecCmsSMIMEKEAParameters keaParams; 168 PLArenaPool *arena = NULL; 169 const SECAlgorithmID *algid; 170 171 /* Clear keaParams, since cleanup code checks the lengths */ 172 (void) memset(&keaParams, 0, sizeof(keaParams)); 173 174#if USE_CDSA_CRYPTO 175 SecCertificateGetAlgorithmID(cert,&algid); 176#endif 177 178 certalgtag = SECOID_GetAlgorithmTag(algid); 179 PORT_Assert(certalgtag == SEC_OID_MISSI_KEA_DSS_OLD || 180 certalgtag == SEC_OID_MISSI_KEA_DSS || 181 certalgtag == SEC_OID_MISSI_KEA); 182 183#define SMIME_FORTEZZA_RA_LENGTH 128 184#define SMIME_FORTEZZA_IV_LENGTH 24 185#define SMIME_FORTEZZA_MAX_KEY_SIZE 256 186 187 /* We really want to show our KEA tag as the key exchange algorithm tag. */ 188 encalgtag = SEC_OID_NETSCAPE_SMIME_KEA; 189 190 /* Get the public key of the recipient. */ 191 publickey = CERT_ExtractPublicKey(cert); 192 if (publickey == NULL) goto loser; 193 194 /* Find our own cert, and extract its keys. */ 195 ourCert = PK11_FindBestKEAMatch(cert, pwfn_arg); 196 if (ourCert == NULL) goto loser; 197 198 arena = PORT_NewArena(1024); 199 if (arena == NULL) 200 goto loser; 201 202 ourPubKey = CERT_ExtractPublicKey(ourCert); 203 if (ourPubKey == NULL) { 204 CERT_DestroyCertificate(ourCert); 205 goto loser; 206 } 207 208 /* While we're here, copy the public key into the outgoing 209 * KEA parameters. */ 210 SECITEM_CopyItem(arena, &(keaParams.originatorKEAKey), &(ourPubKey->u.fortezza.KEAKey)); 211 SECKEY_DestroyPublicKey(ourPubKey); 212 ourPubKey = NULL; 213 214 /* Extract our private key in order to derive the KEA key. */ 215 ourPrivKey = PK11_FindKeyByAnyCert(ourCert, pwfn_arg); 216 CERT_DestroyCertificate(ourCert); /* we're done with this */ 217 if (!ourPrivKey) 218 goto loser; 219 220 /* Prepare raItem with 128 bytes (filled with zeros). */ 221 keaParams.originatorRA.Data = (unsigned char *)PORT_ArenaAlloc(arena,SMIME_FORTEZZA_RA_LENGTH); 222 keaParams.originatorRA.Length = SMIME_FORTEZZA_RA_LENGTH; 223 224 /* Generate the TEK (token exchange key) which we use 225 * to wrap the bulk encryption key. (keaparams.originatorRA) will be 226 * filled with a random seed which we need to send to 227 * the recipient. (user keying material in RFC2630/DSA speak) */ 228 tek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE, 229 &keaParams.originatorRA, NULL, 230 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP, 231 CKA_WRAP, 0, pwfn_arg); 232 233 SECKEY_DestroyPublicKey(publickey); 234 SECKEY_DestroyPrivateKey(ourPrivKey); 235 publickey = NULL; 236 ourPrivKey = NULL; 237 238 if (!tek) 239 goto loser; 240 241 /* allocate space for the wrapped key data */ 242 encKey->Data = (unsigned char *)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE); 243 encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE; 244 245 if (encKey->Data == NULL) { 246 CFRelease(tek); 247 goto loser; 248 } 249 250 /* Wrap the bulk key. What we do with the resulting data 251 depends on whether we're using Skipjack to wrap the key. */ 252 switch (PK11_AlgtagToMechanism(symalgtag)) { 253 case CKM_SKIPJACK_CBC64: 254 case CKM_SKIPJACK_ECB64: 255 case CKM_SKIPJACK_OFB64: 256 case CKM_SKIPJACK_CFB64: 257 case CKM_SKIPJACK_CFB32: 258 case CKM_SKIPJACK_CFB16: 259 case CKM_SKIPJACK_CFB8: 260 /* SKIPJACK, we use the wrap mechanism because we can do it on the hardware */ 261 err = PK11_WrapSymKey(CKM_SKIPJACK_WRAP, NULL, tek, bulkkey, encKey); 262 whichKEA = SecCmsKEAUsesSkipjack; 263 break; 264 default: 265 /* Not SKIPJACK, we encrypt the raw key data */ 266 keaParams.nonSkipjackIV.Data = 267 (unsigned char *)PORT_ArenaAlloc(arena, SMIME_FORTEZZA_IV_LENGTH); 268 keaParams.nonSkipjackIV.Length = SMIME_FORTEZZA_IV_LENGTH; 269 err = PK11_WrapSymKey(CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV, tek, bulkkey, encKey); 270 if (err != SECSuccess) 271 goto loser; 272 273 if (encKey->Length != PK11_GetKeyLength(bulkkey)) { 274 /* The size of the encrypted key is not the same as 275 that of the original bulk key, presumably due to 276 padding. Encode and store the real size of the 277 bulk key. */ 278 if (SEC_ASN1EncodeInteger(arena, &keaParams.bulkKeySize, PK11_GetKeyLength(bulkkey)) == NULL) 279 err = (OSStatus)PORT_GetError(); 280 else 281 /* use full template for encoding */ 282 whichKEA = SecCmsKEAUsesNonSkipjackWithPaddedEncKey; 283 } 284 else 285 /* enc key length == bulk key length */ 286 whichKEA = SecCmsKEAUsesNonSkipjack; 287 break; 288 } 289 290 CFRelease(tek); 291 292 if (err != SECSuccess) 293 goto loser; 294 295 PORT_Assert(whichKEA != SecCmsKEAInvalid); 296 297 /* Encode the KEA parameters into the recipient info. */ 298 params = SEC_ASN1EncodeItem(poolp, NULL, &keaParams, nss_cms_get_kea_template(whichKEA)); 299 if (params == NULL) 300 goto loser; 301 302 /* pass back the algorithm params */ 303 *pparams = params; 304 305 rv = SECSuccess; 306 307loser: 308 if (arena) 309 PORT_FreeArena(arena, PR_FALSE); 310 if (publickey) 311 SECKEY_DestroyPublicKey(publickey); 312 if (ourPrivKey) 313 SECKEY_DestroyPrivateKey(ourPrivKey); 314 return rv; 315} 316 317SecSymmetricKeyRef 318SecCmsUtilDecryptSymKeyMISSI(SecPrivateKeyRef privkey, SecAsn1Item * encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg) 319{ 320 /* fortezza: do a key exchange */ 321 OSStatus err; 322 CK_MECHANISM_TYPE bulkType; 323 SecSymmetricKeyRef tek; 324 SecPublicKeyRef originatorPubKey; 325 SecCmsSMIMEKEAParameters keaParams; 326 SecSymmetricKeyRef bulkkey; 327 int bulkLength; 328 329 (void) memset(&keaParams, 0, sizeof(keaParams)); 330 331 /* NOTE: this uses the SMIME v2 recipientinfo for compatibility. 332 All additional KEA parameters are DER-encoded in the encryption algorithm parameters */ 333 334 /* Decode the KEA algorithm parameters. */ 335 err = SEC_ASN1DecodeItem(NULL, &keaParams, NSS_SMIMEKEAParamTemplateAllParams, 336 &(keyEncAlg->parameters)); 337 if (err != SECSuccess) 338 goto loser; 339 340 /* get originator's public key */ 341 originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data, 342 keaParams.originatorKEAKey.Length); 343 if (originatorPubKey == NULL) 344 goto loser; 345 346 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key. 347 The Derive function generates a shared secret and combines it with the originatorRA 348 data to come up with an unique session key */ 349 tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE, 350 &keaParams.originatorRA, NULL, 351 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP, 352 CKA_WRAP, 0, pwfn_arg); 353 SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */ 354 if (tek == NULL) 355 goto loser; 356 357 /* Now that we have the TEK, unwrap the bulk key 358 with which to decrypt the message. We have to 359 do one of two different things depending on 360 whether Skipjack was used for *bulk* encryption 361 of the message. */ 362 bulkType = PK11_AlgtagToMechanism(bulkalgtag); 363 switch (bulkType) { 364 case CKM_SKIPJACK_CBC64: 365 case CKM_SKIPJACK_ECB64: 366 case CKM_SKIPJACK_OFB64: 367 case CKM_SKIPJACK_CFB64: 368 case CKM_SKIPJACK_CFB32: 369 case CKM_SKIPJACK_CFB16: 370 case CKM_SKIPJACK_CFB8: 371 /* Skipjack is being used as the bulk encryption algorithm.*/ 372 /* Unwrap the bulk key. */ 373 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL, 374 encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0); 375 break; 376 default: 377 /* Skipjack was not used for bulk encryption of this 378 message. Use Skipjack CBC64, with the nonSkipjackIV 379 part of the KEA key parameters, to decrypt 380 the bulk key. If the optional parameter bulkKeySize is present, 381 bulk key size is different than the encrypted key size */ 382 if (keaParams.bulkKeySize.Length > 0) { 383 err = SEC_ASN1DecodeItem(NULL, &bulkLength, 384 SEC_ASN1_GET(SEC_IntegerTemplate), 385 &keaParams.bulkKeySize); 386 if (err != SECSuccess) 387 goto loser; 388 } 389 390 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV, 391 encKey, bulkType, CKA_DECRYPT, bulkLength); 392 break; 393 } 394 return bulkkey; 395loser: 396 return NULL; 397} 398 399/* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */ 400 401OSStatus 402SecCmsUtilEncryptSymKeyESDH(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef key, 403 SecAsn1Item * encKey, SecAsn1Item * *ukm, SECAlgorithmID *keyEncAlg, 404 SecAsn1Item * pubKey) 405{ 406#if 0 /* not yet done */ 407 SECOidTag certalgtag; /* the certificate's encryption algorithm */ 408 SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */ 409 OSStatus rv; 410 SecAsn1Item * params = NULL; 411 int data_len; 412 OSStatus err; 413 SecSymmetricKeyRef tek; 414 SecCertificateRef ourCert; 415 SecPublicKeyRef ourPubKey; 416 SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid; 417 418 certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); 419 PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY); 420 421 /* We really want to show our KEA tag as the key exchange algorithm tag. */ 422 encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN; 423 424 /* Get the public key of the recipient. */ 425 publickey = CERT_ExtractPublicKey(cert); 426 if (publickey == NULL) goto loser; 427 428 /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */ 429 /* XXXX */ourCert = PK11_FindBestKEAMatch(cert, wincx); 430 if (ourCert == NULL) goto loser; 431 432 arena = PORT_NewArena(1024); 433 if (arena == NULL) goto loser; 434 435 /* While we're here, extract the key pair's public key data and copy it into */ 436 /* the outgoing parameters. */ 437 /* XXXX */ourPubKey = CERT_ExtractPublicKey(ourCert); 438 if (ourPubKey == NULL) 439 { 440 goto loser; 441 } 442 SECITEM_CopyItem(arena, pubKey, /* XXX */&(ourPubKey->u.fortezza.KEAKey)); 443 SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */ 444 ourPubKey = NULL; 445 446 /* Extract our private key in order to derive the KEA key. */ 447 ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx); 448 CERT_DestroyCertificate(ourCert); /* we're done with this */ 449 if (!ourPrivKey) goto loser; 450 451 /* If ukm desired, prepare it - allocate enough space (filled with zeros). */ 452 if (ukm) { 453 ukm->Data = (unsigned char*)PORT_ArenaZAlloc(arena,/* XXXX */); 454 ukm->Length = /* XXXX */; 455 } 456 457 /* Generate the KEK (key exchange key) according to RFC2631 which we use 458 * to wrap the bulk encryption key. */ 459 kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE, 460 ukm, NULL, 461 /* XXXX */CKM_KEA_KEY_DERIVE, /* XXXX */CKM_SKIPJACK_WRAP, 462 CKA_WRAP, 0, wincx); 463 464 SECKEY_DestroyPublicKey(publickey); 465 SECKEY_DestroyPrivateKey(ourPrivKey); 466 publickey = NULL; 467 ourPrivKey = NULL; 468 469 if (!kek) 470 goto loser; 471 472 /* allocate space for the encrypted CEK (bulk key) */ 473 encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE); 474 encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE; 475 476 if (encKey->Data == NULL) 477 { 478 CFRelease(kek); 479 goto loser; 480 } 481 482 483 /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */ 484 /* bulk encryption algorithm */ 485 switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo->encalg)) 486 { 487 case /* XXXX */CKM_SKIPJACK_CFB8: 488 err = PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey); 489 whichKEA = SecCmsKEAUsesSkipjack; 490 break; 491 case /* XXXX */CKM_SKIPJACK_CFB8: 492 err = PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey); 493 whichKEA = SecCmsKEAUsesSkipjack; 494 break; 495 default: 496 /* XXXX what do we do here? Neither RC2 nor 3DES... */ 497 err = SECFailure; 498 /* set error */ 499 break; 500 } 501 502 CFRelease(kek); /* we do not need the KEK anymore */ 503 if (err != SECSuccess) 504 goto loser; 505 506 PORT_Assert(whichKEA != SecCmsKEAInvalid); 507 508 /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */ 509 /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */ 510 params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA)); 511 if (params == NULL) 512 goto loser; 513 514 /* now set keyEncAlg */ 515 rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params); 516 if (rv != SECSuccess) 517 goto loser; 518 519 /* XXXXXXX this is not right yet */ 520loser: 521 if (arena) { 522 PORT_FreeArena(arena, PR_FALSE); 523 } 524 if (publickey) { 525 SECKEY_DestroyPublicKey(publickey); 526 } 527 if (ourPrivKey) { 528 SECKEY_DestroyPrivateKey(ourPrivKey); 529 } 530#endif 531 return SECFailure; 532} 533 534SecSymmetricKeyRef 535SecCmsUtilDecryptSymKeyESDH(SecPrivateKeyRef privkey, SecAsn1Item * encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg) 536{ 537#if 0 /* not yet done */ 538 OSStatus err; 539 CK_MECHANISM_TYPE bulkType; 540 SecSymmetricKeyRef tek; 541 SecPublicKeyRef originatorPubKey; 542 SecCmsSMIMEKEAParameters keaParams; 543 544 /* XXXX get originator's public key */ 545 originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data, 546 keaParams.originatorKEAKey.Length); 547 if (originatorPubKey == NULL) 548 goto loser; 549 550 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key. 551 The Derive function generates a shared secret and combines it with the originatorRA 552 data to come up with an unique session key */ 553 tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE, 554 &keaParams.originatorRA, NULL, 555 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP, 556 CKA_WRAP, 0, pwfn_arg); 557 SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */ 558 if (tek == NULL) 559 goto loser; 560 561 /* Now that we have the TEK, unwrap the bulk key 562 with which to decrypt the message. */ 563 /* Skipjack is being used as the bulk encryption algorithm.*/ 564 /* Unwrap the bulk key. */ 565 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL, 566 encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0); 567 568 return bulkkey; 569 570loser: 571#endif 572 return NULL; 573} 574 575#endif 576