]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
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 | */ | |
63 | OSStatus | |
64 | SecCmsUtilEncryptSymKeyRSA(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 | ||
80 | OSStatus | |
81 | SecCmsUtilEncryptSymKeyRSAPubKey(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 | ||
122 | loser: | |
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 | */ | |
136 | SecSymmetricKeyRef | |
137 | SecCmsUtilDecryptSymKeyRSA(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 | ||
148 | extern const SecAsn1Template NSS_SMIMEKEAParamTemplateAllParams[]; | |
149 | ||
150 | OSStatus | |
151 | SecCmsUtilEncryptSymKeyMISSI(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 | ||
302 | loser: | |
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 | ||
312 | SecSymmetricKeyRef | |
313 | SecCmsUtilDecryptSymKeyMISSI(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; | |
390 | loser: | |
391 | return NULL; | |
392 | } | |
393 | ||
394 | /* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */ | |
395 | ||
396 | OSStatus | |
397 | SecCmsUtilEncryptSymKeyESDH(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 */ | |
515 | loser: | |
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 | ||
529 | SecSymmetricKeyRef | |
530 | SecCmsUtilDecryptSymKeyESDH(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 | ||
565 | loser: | |
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 | ||
599 | static 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 | */ | |
623 | typedef 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 | ||
629 | static 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 */ | |
646 | typedef enum { | |
647 | CAT_Uint32, | |
648 | CAT_Ptr | |
649 | } ContextAttrType; | |
650 | ||
651 | static 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 | ||
679 | static 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 */ | |
705 | static 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 | */ | |
719 | static 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 | */ | |
427c49bc | 766 | static void cmsFreeCssmMemory( |
b1ab9ed8 A |
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 | */ | |
784 | static 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 | */ | |
850 | OSStatus | |
851 | SecCmsUtilEncryptSymKeyECDH( | |
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 | ||
1194 | loser: | |
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 | ||
1227 | SecSymmetricKeyRef | |
1228 | SecCmsUtilDecryptSymKeyECDH( | |
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); | |
427c49bc | 1279 | if(pool == NULL) { |
b1ab9ed8 A |
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 | ||
1435 | loser: | |
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 |