]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_smime/lib/cmspubkey.c
Security-59306.11.20.tar.gz
[apple/security.git] / OSX / libsecurity_smime / lib / cmspubkey.c
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/SecItemPriv.h>
49 #include <Security/Security.h>
50 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
51 #include <Security/SecCmsBase.h>
52 #include <Security/secasn1t.h>
53 #include <security_asn1/plarenas.h>
54 #include <Security/keyTemplates.h>
55 #include <utilities/SecCFWrappers.h>
56 #include <CommonCrypto/CommonCryptor.h>
57 #include <CommonCrypto/CommonRandomSPI.h>
58 #include <CommonCrypto/CommonRandom.h>
59
60 /* ====== RSA ======================================================================= */
61
62 /*
63 * SecCmsUtilEncryptSymKeyRSA - wrap a symmetric key with RSA
64 *
65 * this function takes a symmetric key and encrypts it using an RSA public key
66 * according to PKCS#1 and RFC2633 (S/MIME)
67 */
68 OSStatus
69 SecCmsUtilEncryptSymKeyRSA(PLArenaPool *poolp, SecCertificateRef cert,
70 SecSymmetricKeyRef bulkkey,
71 CSSM_DATA_PTR encKey)
72 {
73 OSStatus rv;
74 SecPublicKeyRef publickey = SecCertificateCopyKey(cert);
75 if (publickey == NULL)
76 return SECFailure;
77
78 rv = SecCmsUtilEncryptSymKeyRSAPubKey(poolp, publickey, bulkkey, encKey);
79 CFRelease(publickey);
80 return rv;
81 }
82
83 OSStatus
84 SecCmsUtilEncryptSymKeyRSAPubKey(PLArenaPool *poolp,
85 SecPublicKeyRef publickey,
86 SecSymmetricKeyRef bulkkey, CSSM_DATA_PTR encKey)
87 {
88 OSStatus rv;
89 unsigned int data_len;
90 //KeyType keyType;
91 void *mark = NULL;
92 CFDictionaryRef theirKeyAttrs = NULL;
93
94 mark = PORT_ArenaMark(poolp);
95 if (!mark)
96 goto loser;
97 /* allocate memory for the encrypted key */
98 theirKeyAttrs = SecKeyCopyAttributes(publickey);
99 if (!theirKeyAttrs) {
100 goto loser;
101 }
102
103 CFNumberRef keySizeNum = CFDictionaryGetValue(theirKeyAttrs, kSecAttrKeySizeInBits);
104 if (!CFNumberGetValue(keySizeNum, kCFNumberIntType, &data_len)) {
105 goto loser;
106 }
107 // Convert length to bytes;
108 data_len /= 8;
109
110 encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len);
111 encKey->Length = data_len;
112 if (encKey->Data == NULL)
113 goto loser;
114
115 /* encrypt the key now */
116 rv = WRAP_PubWrapSymKey(publickey, bulkkey, encKey);
117 if (rv != SECSuccess)
118 goto loser;
119
120 PORT_ArenaUnmark(poolp, mark);
121 CFReleaseNull(theirKeyAttrs);
122 return SECSuccess;
123
124 loser:
125 if (theirKeyAttrs) {
126 CFRelease(theirKeyAttrs);
127 }
128 if (mark) {
129 PORT_ArenaRelease(poolp, mark);
130 }
131 return SECFailure;
132 }
133
134 /*
135 * SecCmsUtilDecryptSymKeyRSA - unwrap a RSA-wrapped symmetric key
136 *
137 * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
138 * key handle. Please note that the actual unwrapped key data may not be allowed to leave
139 * a hardware token...
140 */
141 SecSymmetricKeyRef
142 SecCmsUtilDecryptSymKeyRSA(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECOidTag bulkalgtag)
143 {
144 /* that's easy */
145 return WRAP_PubUnwrapSymKey(privkey, encKey, bulkalgtag);
146 }
147
148 #if 0
149 // @@@ Implement Fortezza and Diffie hellman support
150
151 /* ====== MISSI (Fortezza) ========================================================== */
152
153 extern const SecAsn1Template NSS_SMIMEKEAParamTemplateAllParams[];
154
155 OSStatus
156 SecCmsUtilEncryptSymKeyMISSI(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef bulkkey,
157 SECOidTag symalgtag, CSSM_DATA_PTR encKey, CSSM_DATA_PTR *pparams, void *pwfn_arg)
158 {
159 SECOidTag certalgtag; /* the certificate's encryption algorithm */
160 SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */
161 OSStatus rv = SECFailure;
162 CSSM_DATA_PTR params = NULL;
163 OSStatus err;
164 SecSymmetricKeyRef tek;
165 SecCertificateRef ourCert;
166 SecPublicKeyRef ourPubKey, *publickey = NULL;
167 SecPrivateKeyRef ourPrivKey = NULL;
168 SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid;
169 SecCmsSMIMEKEAParameters keaParams;
170 PLArenaPool *arena = NULL;
171 extern const SecAsn1Template *nss_cms_get_kea_template(SecCmsKEATemplateSelector whichTemplate);
172 const SECAlgorithmID *algid;
173
174 /* Clear keaParams, since cleanup code checks the lengths */
175 (void) memset(&keaParams, 0, sizeof(keaParams));
176
177 SecCertificateGetAlgorithmID(cert,&algid);
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
307 loser:
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
317 SecSymmetricKeyRef
318 SecCmsUtilDecryptSymKeyMISSI(SecPrivateKeyRef privkey, CSSM_DATA_PTR 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;
395 loser:
396 return NULL;
397 }
398
399 /* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */
400
401 OSStatus
402 SecCmsUtilEncryptSymKeyESDH(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef key,
403 CSSM_DATA_PTR encKey, CSSM_DATA_PTR ukm, SECAlgorithmID *keyEncAlg,
404 CSSM_DATA_PTR 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 CSSM_DATA_PTR 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 */
520 loser:
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
534 SecSymmetricKeyRef
535 SecCmsUtilDecryptSymKeyESDH(SecPrivateKeyRef privkey, CSSM_DATA_PTR 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
570 loser:
571 #endif
572 return NULL;
573 }
574
575 #endif /* Fortezza, DIffie-Hellman */
576
577 #define CFRELEASE(cf) if(cf != NULL) { CFRelease(cf); }
578
579 /* ====== ECDH (Ephemeral-Static Diffie-Hellman) ==================================== */
580
581 #pragma mark ---- ECDH support functions ----
582
583 #ifdef NDEBUG
584 #define CSSM_PERROR(f, r)
585 #define dprintf(args...)
586 #else
587 #define CSSM_PERROR(f, r) cssmPerror(f, r)
588 #define dprintf(args...) fprintf(stderr, args)
589 #endif
590
591 /* Length of KeyAgreeRecipientInfo.ukm we create */
592 #define UKM_LENGTH 8
593
594 /* KEK algorithm info we generate */
595 #define ECDH_KEK_ALG_TAG SEC_OID_DES_EDE3_CBC
596 #define ECDH_KEK_KEY_CSSM_ALGID CSSM_ALGID_3DES_3KEY
597 #define ECDH_KEK_ENCR_CSSM_ALGID CSSM_ALGID_3DES_3KEY_EDE
598 #define ECDH_KEK_KEY_LEN_BYTES 24
599 #define ECDH_KEK_IV_LEN_BYTES 8
600
601 #define CMS_DUMP_BUFS 0
602 #if CMS_DUMP_BUFS
603
604 static void dumpBuf(
605 const char *label,
606 const CSSM_DATA *cd)
607 {
608 unsigned dex;
609
610 printf("%s:\n ", label);
611 for(dex=0; dex<cd->Length; dex++) {
612 printf("%02X ", cd->Data[dex]);
613 if(((dex % 16) == 15) && (dex != (cd->Length - 1))) {
614 printf("\n ");
615 }
616 }
617 putchar('\n');
618 }
619
620 #else
621 #define dumpBuf(l, d)
622 #endif /* CMS_DUMP_BUFS */
623
624 /*
625 * The ECC-CMS-SharedInfo struct, as defined in RFC 3278 8.2, and the
626 * template for DER encoding and decoding it.
627 */
628 typedef struct {
629 SECAlgorithmID algId; /* KEK alg, NULL params */
630 CSSM_DATA entityUInfo; /* optional, ukm */
631 CSSM_DATA suppPubInfo; /* length of KEK in bits as 4-byte integer */
632 } ECC_CMS_SharedInfo;
633
634 static const SecAsn1Template ECC_CMS_SharedInfoTemplate[] = {
635 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ECC_CMS_SharedInfo) },
636 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0,
637 offsetof(ECC_CMS_SharedInfo,entityUInfo),
638 kSecAsn1OctetStringTemplate },
639 { SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 2,
640 offsetof(ECC_CMS_SharedInfo,suppPubInfo),
641 kSecAsn1OctetStringTemplate },
642 { 0 }
643 };
644
645 /*
646 * Given a context specified via a CSSM_CC_HANDLE, add a new
647 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
648 * AttributeLength, and an untyped pointer.
649 */
650 /* specify either 32-bit integer or a pointer as an added attribute value */
651 typedef enum {
652 CAT_Uint32,
653 CAT_Ptr
654 } ContextAttrType;
655
656 /* convert uint32 to big-endian 4 bytes */
657 static void int32ToBytes(
658 uint32_t i,
659 unsigned char *b)
660 {
661 int dex;
662 for(dex=3; dex>=0; dex--) {
663 b[dex] = i;
664 i >>= 8;
665 }
666 }
667
668 /*
669 * Given an OID tag, return key size and mode.
670 * NOTE: ciphers with variable key sizes, like RC2, RC4, and RC5 cannot
671 * be used here because the message does not contain a key size
672 * indication.
673 */
674 static OSStatus encrAlgInfo(
675 SECOidTag oidTag,
676 uint32 *keySizeBits, /* RETURNED */
677 CCAlgorithm *algorithm, /* RETURNED */
678 CCOptions *options) /* RETURNED */
679 {
680 *keySizeBits = 64; /* default */
681 *options = kCCOptionPKCS7Padding; /* default */
682
683 switch(oidTag) {
684 case SEC_OID_RC2_CBC:
685 case SEC_OID_RC4:
686 case SEC_OID_RC5_CBC_PAD:
687 dprintf("encrAlgInfo: key size unknowable\n");
688 return errSecDataNotAvailable;
689 case SEC_OID_DES_EDE:
690 /* Not sure about this; SecCmsCipherContextStart() treats this
691 * like SEC_OID_DES_EDE3_CBC... */
692 *options = kCCOptionECBMode;
693 // fall through
694 case SEC_OID_DES_EDE3_CBC:
695 *keySizeBits = 192;
696 *algorithm = kCCAlgorithm3DES;
697 break;
698 case SEC_OID_DES_ECB:
699 *options = kCCOptionECBMode;
700 // fall through
701 case SEC_OID_DES_CBC:
702 *algorithm = kCCAlgorithmDES;
703 break;
704 case SEC_OID_AES_128_CBC:
705 *keySizeBits = 128;
706 *algorithm = kCCAlgorithmAES;
707 break;
708 case SEC_OID_AES_192_CBC:
709 *keySizeBits = 192;
710 *algorithm = kCCAlgorithmAES;
711 break;
712 case SEC_OID_AES_256_CBC:
713 *keySizeBits = 256;
714 *algorithm = kCCAlgorithmAES;
715 break;
716 case SEC_OID_AES_128_ECB:
717 *keySizeBits = 128;
718 *algorithm = kCCAlgorithmAES;
719 *options = kCCOptionECBMode;
720 break;
721 case SEC_OID_AES_192_ECB:
722 *keySizeBits = 192;
723 *algorithm = kCCAlgorithmAES;
724 *options = kCCOptionECBMode;
725 break;
726 case SEC_OID_AES_256_ECB:
727 *keySizeBits = 256;
728 *algorithm = kCCAlgorithmAES;
729 *options = kCCOptionECBMode;
730 break;
731 default:
732 dprintf("encrAlgInfo: unknown alg tag (%d)\n", (int)oidTag);
733 return errSecDataNotAvailable;
734 }
735 return noErr;
736 }
737
738 #pragma mark ---- ECDH CEK key wrap ----
739
740 /*
741 * Encrypt bulk encryption key (a.k.a. content encryption key, CEK) using ECDH
742 */
743 OSStatus
744 SecCmsUtilEncryptSymKeyECDH(
745 PLArenaPool *poolp,
746 SecCertificateRef cert, /* recipient's cert */
747 SecSymmetricKeyRef key, /* bulk key */
748 /* remaining fields RETURNED */
749 CSSM_DATA_PTR encKey, /* encrypted key --> recipientEncryptedKeys[0].EncryptedKey */
750 CSSM_DATA_PTR ukm, /* random UKM --> KeyAgreeRecipientInfo.ukm */
751 SECAlgorithmID *keyEncAlg, /* alg := dhSinglePass-stdDH-sha1kdf-scheme
752 * params := another encoded AlgId, with the KEK alg and IV */
753 CSSM_DATA_PTR pubKey) /* our pub key as ECPoint -->
754 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
755 {
756 OSStatus rv = noErr;
757 SecKeyRef theirPubKey = NULL, ourPubKey = NULL, ourPrivKey = NULL;
758 CFDictionaryRef theirKeyAttrs = NULL, ourKeyParams = NULL, kekParams = NULL;
759 uint8_t iv[ECDH_KEK_IV_LEN_BYTES];
760 CSSM_DATA ivData = { ECDH_KEK_IV_LEN_BYTES, iv };
761 SECAlgorithmID kekAlgId;
762 SECOidData *kekOid;
763 ECC_CMS_SharedInfo sharedInfo;
764 CSSM_DATA sharedInfoEnc = {0, NULL};
765 uint8 nullData[2] = {SEC_ASN1_NULL, 0};
766 uint8 keyLenAsBytes[4];
767 CFDataRef sharedInfoData = NULL, kekData = NULL, ourPubData = NULL;
768 CFNumberRef kekLen = NULL;
769 CFErrorRef error = NULL;
770 CCCryptorRef ciphercc = NULL;
771
772 encKey->Data = NULL;
773 encKey->Length = 0;
774
775 /* Copy the recipient's static public ECDH key */
776 theirPubKey = SecCertificateCopyKey(cert);
777 if (rv || !theirPubKey) {
778 dprintf("SecCmsUtilEncryptSymKeyECDH: failed to get public key from cert, %d\n", (int)rv);
779 goto out;
780 }
781
782 theirKeyAttrs = SecKeyCopyAttributes(theirPubKey);
783 if (!theirKeyAttrs) {
784 dprintf("SecCmsUtilEncryptSymKeyECDH: failed to get key attributes\n");
785 goto out;
786 }
787
788 CFStringRef keyType = NULL;
789 CFNumberRef keySizeNum = NULL;
790 keyType = CFDictionaryGetValue(theirKeyAttrs, kSecAttrKeyType);
791 keySizeNum = CFDictionaryGetValue(theirKeyAttrs, kSecAttrKeySizeInBits);
792
793 if (!CFEqual(kSecAttrKeyTypeECSECPrimeRandom, keyType)) {
794 dprintf("SecCmsUtilEncryptSymKeyECDH: unsupported key type\n");
795 rv = CSSMERR_CSP_INVALID_KEY;
796 goto out;
797 }
798
799 /* Generate ephemeral ECDH key */
800 const void *keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits, kSecUseDataProtectionKeychain};
801 const void *values[] = { keyType, keySizeNum, kCFBooleanTrue };
802 ourKeyParams = CFDictionaryCreate(NULL, keys, values, 3,
803 &kCFTypeDictionaryKeyCallBacks,
804 &kCFTypeDictionaryValueCallBacks);
805 rv = SecKeyGeneratePair(ourKeyParams, &ourPubKey, &ourPrivKey);
806 if (rv || !ourPubKey || !ourPrivKey) {
807 dprintf("SecKeyGeneratePair: unable to generate ECDH key pair, %d\n", (int)rv);
808 goto out;
809 }
810
811 /* Generate UKM */
812 ukm->Data = PORT_Alloc(UKM_LENGTH);
813 ukm->Length = UKM_LENGTH;
814 rv = CCRandomCopyBytes(kCCRandomDefault, ukm->Data, UKM_LENGTH);
815 if (rv || !ukm->Data) {
816 dprintf("CCRandomGenerateBytes failed, %d", (int)rv);
817 goto out;
818 }
819
820 /*
821 * OK, we have to set up a weird SECAlgorithmID.
822 * algorithm = dhSinglePass-stdDH-sha1kdf-scheme
823 * params = an encoded SECAlgorithmID representing the KEK algorithm, with
824 * algorithm = whatever we pick
825 * parameters = IV as octet string (though I haven't seen that specified
826 * anywhere; it's how the CEK IV is encoded)
827 */
828
829 /* Generate 8-byte IV */
830 rv = CCRandomCopyBytes(kCCRandomDefault, iv, ECDH_KEK_IV_LEN_BYTES);
831 if (rv) {
832 dprintf("CCRandomGenerateBytes failed, %d", (int)rv);
833 goto out;
834 }
835 dumpBuf("sender IV", &ivData);
836
837 memset(&kekAlgId, 0, sizeof(kekAlgId));
838 if (!SEC_ASN1EncodeItem(poolp, &kekAlgId.parameters,
839 &ivData, kSecAsn1OctetStringTemplate)) {
840 rv = internalComponentErr;
841 goto out;
842 }
843
844 /* Drop in the KEK OID and encode the whole thing */
845 kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG);
846 if(kekOid == NULL) {
847 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
848 rv = internalComponentErr;
849 goto out;
850 }
851 kekAlgId.algorithm = kekOid->oid;
852 memset(keyEncAlg, 0, sizeof(*keyEncAlg));
853 if (!SEC_ASN1EncodeItem(poolp, &keyEncAlg->parameters,
854 &kekAlgId, SECOID_AlgorithmIDTemplate)) {
855 rv = internalComponentErr;
856 goto out;
857 }
858 kekOid = SECOID_FindOIDByTag(SEC_OID_DH_SINGLE_STD_SHA1KDF);
859 if(kekOid == NULL) {
860 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
861 rv = internalComponentErr;
862 goto out;
863 }
864 keyEncAlg->algorithm = kekOid->oid;
865
866 /*
867 * Now in order to derive the KEK proper, we have to create a
868 * ECC-CMS-SharedInfo, which does not appear in the message, and DER
869 * encode that struct, the result of which is used as the
870 * SharedInfo value in the KEK key derive.
871 */
872 memset(&sharedInfo, 0, sizeof(sharedInfo));
873 kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG);
874 sharedInfo.algId.algorithm = kekOid->oid;
875 sharedInfo.algId.parameters.Data = nullData;
876 sharedInfo.algId.parameters.Length = 2;
877 sharedInfo.entityUInfo = *ukm;
878 int32ToBytes(ECDH_KEK_KEY_LEN_BYTES << 3, keyLenAsBytes);
879 sharedInfo.suppPubInfo.Length = 4;
880 sharedInfo.suppPubInfo.Data = keyLenAsBytes;
881 if (!SEC_ASN1EncodeItem(poolp, &sharedInfoEnc,
882 &sharedInfo, ECC_CMS_SharedInfoTemplate)) {
883 rv = internalComponentErr;
884 goto out;
885 }
886 dumpBuf("sender encoded SharedInfo", &sharedInfoEnc);
887
888 /* Derive KEK */
889 sharedInfoData = CFDataCreate(NULL, sharedInfoEnc.Data, sharedInfoEnc.Length);
890 int32_t ecdh_key_key_len = ECDH_KEK_KEY_LEN_BYTES;
891 kekLen = CFNumberCreate(NULL, kCFNumberSInt32Type, &ecdh_key_key_len);
892 const void *kekKeys[] = { kSecKeyKeyExchangeParameterRequestedSize, kSecKeyKeyExchangeParameterSharedInfo };
893 const void *kekValues[] = { kekLen, sharedInfoData };
894 kekParams = CFDictionaryCreate(NULL, kekKeys, kekValues, 2,
895 &kCFTypeDictionaryKeyCallBacks,
896 &kCFTypeDictionaryValueCallBacks);
897 kekData = SecKeyCopyKeyExchangeResult(ourPrivKey, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1,
898 theirPubKey, kekParams, &error);
899 if (error) {
900 dprintf("SecKeyCopyKeyExchangeResult: failed\n");
901 goto out;
902 }
903
904 /*
905 * Encrypt the raw CEK bits with the KEK we just derived
906 */
907 rv = CCCryptorCreate(kCCEncrypt, kCCAlgorithm3DES, kCCOptionPKCS7Padding,
908 CFDataGetBytePtr(kekData), CFDataGetLength(kekData), iv, &ciphercc);
909 if (rv) {
910 dprintf("CCCryptorCreate failed: %d\n", (int)rv);
911 goto out;
912 }
913 CSSM_KEY cek;
914 rv = cmsNullWrapKey(key, &cek);
915 if (rv) {
916 dprintf("SecKeyGetCSSMKey failed: %d\n", (int)rv);
917 goto out;
918 }
919 size_t expectedEncKeyLength = CCCryptorGetOutputLength(ciphercc, cek.KeyData.Length, true);
920 encKey->Data = PORT_ArenaAlloc(poolp, expectedEncKeyLength);
921 size_t bytes_output = 0;
922 rv = CCCryptorUpdate(ciphercc, cek.KeyData.Data, cek.KeyData.Length, encKey->Data, expectedEncKeyLength, &bytes_output);
923 if (rv) {
924 dprintf("CCCryptorUpdate failed: %d\n", (int)rv);
925 goto out;
926 }
927 size_t final_bytes_output = 0;
928 rv = CCCryptorFinal(ciphercc, encKey->Data+bytes_output, expectedEncKeyLength - bytes_output, &final_bytes_output);
929 if (rv) {
930 dprintf("CCCryptorFinal failed: %d\n", (int)rv);
931 goto out;
932 }
933 encKey->Length = bytes_output + final_bytes_output;
934
935 /* Provide our ephemeral public key to the caller */
936 ourPubData = SecKeyCopyExternalRepresentation(ourPubKey, &error);
937 if (error) {
938 dprintf("SecKeyCopyExternalRepresentation failed\n");
939 goto out;
940 }
941 pubKey->Length = CFDataGetLength(ourPubData);
942 pubKey->Data = malloc(pubKey->Length);
943 if (pubKey->Data) {
944 memcpy(pubKey->Data, CFDataGetBytePtr(ourPubData), pubKey->Length);
945 } else {
946 rv = errSecAllocate;
947 }
948 /* pubKey is bit string, convert here */
949 pubKey->Length <<= 3;
950
951 out:
952 if (theirPubKey) { CFRelease(theirPubKey); }
953 if (theirKeyAttrs) { CFRelease(theirKeyAttrs); }
954 if (ourKeyParams) { CFRelease(ourKeyParams); }
955 if (ourPubKey) { CFRelease(ourPubKey); }
956 if (ourPrivKey) { CFRelease(ourPrivKey); }
957 if (sharedInfoData) { CFRelease(sharedInfoData); }
958 if (kekLen != NULL) { CFRelease(kekLen); }
959 if (kekParams) { CFRelease(kekParams); }
960 if (kekData) { CFRelease(kekData); }
961 if (error) { CFRelease(error); }
962 if (ciphercc) { CCCryptorRelease(ciphercc); }
963 if (ourPubData) { CFRelease(ourPubData); }
964 if (rv && encKey->Data) {
965 PORT_Free(encKey->Data);
966 encKey->Data = NULL;
967 encKey->Length = 0;
968 }
969 if (rv && ukm->Data) {
970 PORT_Free(ukm->Data);
971 ukm->Data = NULL;
972 ukm->Length = 0;
973 }
974 return rv;
975 }
976
977
978 #pragma mark ---- ECDH CEK key unwrap ----
979
980 SecSymmetricKeyRef
981 SecCmsUtilDecryptSymKeyECDH(
982 SecPrivateKeyRef privkey, /* our private key */
983 CSSM_DATA_PTR encKey, /* encrypted CEK */
984 CSSM_DATA_PTR ukm, /* random UKM from KeyAgreeRecipientInfo.ukm */
985 SECAlgorithmID *keyEncAlg, /* alg := dhSinglePass-stdDH-sha1kdf-scheme
986 * params := another encoded AlgId, with the KEK alg and IV */
987 SECOidTag bulkalgtag, /* algorithm of returned key */
988 CSSM_DATA_PTR pubKey) /* sender's pub key as ECPoint from
989 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
990 {
991 SecSymmetricKeyRef outKey = NULL;
992 OSStatus rv = noErr;
993 PLArenaPool *pool = NULL;
994 SECAlgorithmID keyAlgParam;
995 SECOidData *kekOid = NULL;
996 CSSM_DATA iv = {0, NULL};
997 ECC_CMS_SharedInfo sharedInfo;
998 CSSM_DATA sharedInfoEnc = {0, NULL};
999 uint8 nullData[2] = {SEC_ASN1_NULL, 0};
1000 uint8 keyLenAsBytes[4];
1001 uint32 kekSizeBits;
1002 SecKeyRef theirPubKey = NULL;
1003 CFStringRef keyType = NULL;
1004 CFDictionaryRef theirKeyAttrs = NULL, kekParams = NULL;
1005 CFMutableDictionaryRef cekParams = NULL;
1006 CFDataRef sharedInfoData = NULL, theirPubData= NULL, kekData = NULL, cekData = NULL;
1007 CFNumberRef kekLen = NULL, theirKeyLen = NULL;
1008 CFErrorRef error = NULL;
1009 CCAlgorithm alg;
1010 CCOptions options = 0;
1011 CCCryptorRef ciphercc = NULL;
1012 size_t theirKeySizeInBits = 0;
1013
1014 /*
1015 * Decode keyEncAlg.params to get KEK algorithm and IV
1016 */
1017 pool = PORT_NewArena(1024);
1018 if(pool == NULL) {
1019 goto out;
1020 }
1021 memset(&keyAlgParam, 0, sizeof(keyAlgParam));
1022 if(SEC_ASN1DecodeItem(pool, &keyAlgParam, SECOID_AlgorithmIDTemplate,
1023 &keyEncAlg->parameters)) {
1024 dprintf("SecCmsUtilDecryptSymKeyECDH: error decoding keyAlgParams\n");
1025 goto out;
1026 }
1027 kekOid = SECOID_FindOID(&keyAlgParam.algorithm);
1028 if(kekOid == NULL) {
1029 dprintf("SecCmsUtilDecryptSymKeyECDH: unknown KEK enc OID\n");
1030 goto out;
1031 }
1032 rv = encrAlgInfo(kekOid->offset, &kekSizeBits, &alg, &options);
1033 if(rv) {
1034 goto out;
1035 }
1036 /* IV is OCTET STRING in the alg params */
1037 if(SEC_ASN1DecodeItem(pool, &iv, kSecAsn1OctetStringTemplate,
1038 &keyAlgParam.parameters)) {
1039 /*
1040 * Not sure here - is it legal to have no IV? I haven't seen this
1041 * addressed in any spec. Maybe we should condition the behavior
1042 * here on the KEK algorithm.
1043 */
1044 dprintf("SecCmsUtilDecryptSymKeyECDH: no KEK IV\n");
1045 goto out;
1046 }
1047
1048 /*
1049 * Now in order to derive the KEK proper, we have to create a
1050 * ECC-CMS-SharedInfo, which does not appear in the message, and DER
1051 * encode that struct, the result of which is used as the
1052 * SharedInfo value in the KEK key derive.
1053 */
1054 memset(&sharedInfo, 0, sizeof(sharedInfo));
1055 sharedInfo.algId.algorithm = kekOid->oid;
1056 sharedInfo.algId.parameters.Data = nullData;
1057 sharedInfo.algId.parameters.Length = 2;
1058 sharedInfo.entityUInfo = *ukm;
1059 int32ToBytes(kekSizeBits, keyLenAsBytes);
1060 sharedInfo.suppPubInfo.Length = 4;
1061 sharedInfo.suppPubInfo.Data = keyLenAsBytes;
1062 if (!SEC_ASN1EncodeItem(pool, &sharedInfoEnc,
1063 &sharedInfo, ECC_CMS_SharedInfoTemplate)) {
1064 rv = internalComponentErr;
1065 goto out;
1066 }
1067 dumpBuf("receiver encoded SharedInfo", &sharedInfoEnc);
1068 dumpBuf("receiver IV", &iv);
1069 dumpBuf("receiver UKM", ukm);
1070 dumpBuf("sender's public key", pubKey);
1071
1072 /* pubKey is bit string, convert here */
1073 theirKeySizeInBits = pubKey->Length;
1074 pubKey->Length = (theirKeySizeInBits + 7) >> 3;
1075 theirPubData = CFDataCreate(NULL, pubKey->Data, pubKey->Length);
1076 theirKeyLen = CFNumberCreate(NULL, kCFNumberSInt64Type, &theirKeySizeInBits);
1077 const void *keys[] = { kSecAttrKeyType, kSecAttrKeyClass, kSecAttrKeySizeInBits };
1078 const void *values[] = { kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClassPublic, theirKeyLen};
1079 theirKeyAttrs = CFDictionaryCreate(NULL, keys, values, 3,
1080 &kCFTypeDictionaryKeyCallBacks,
1081 &kCFTypeDictionaryValueCallBacks);
1082 theirPubKey = SecKeyCreateWithData(theirPubData, theirKeyAttrs, &error);
1083 if (error) {
1084 dprintf("SecKeyCreateWithData: failed\n");
1085 goto out;
1086 }
1087
1088 /* Derive KEK */
1089 sharedInfoData = CFDataCreate(NULL, sharedInfoEnc.Data, sharedInfoEnc.Length);
1090 int32_t ecdh_key_key_len = (kekSizeBits + 7) >> 3;
1091 kekLen = CFNumberCreate(NULL, kCFNumberSInt32Type, &ecdh_key_key_len);
1092 const void *kekKeys[] = { kSecKeyKeyExchangeParameterRequestedSize, kSecKeyKeyExchangeParameterSharedInfo };
1093 const void *kekValues[] = { kekLen, sharedInfoData };
1094 kekParams = CFDictionaryCreate(NULL, kekKeys, kekValues, 2,
1095 &kCFTypeDictionaryKeyCallBacks,
1096 &kCFTypeDictionaryValueCallBacks);
1097 kekData = SecKeyCopyKeyExchangeResult(privkey, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1,
1098 theirPubKey, kekParams, &error);
1099 if (error) {
1100 dprintf("SecKeyCopyKeyExchangeResult: failed\n");
1101 goto out;
1102 }
1103
1104 /*
1105 * Decrypt the raw CEK bits with the KEK we just derived
1106 */
1107 CSSM_DATA cek = { 0, NULL };
1108 rv = CCCryptorCreate(kCCDecrypt, alg, options,
1109 CFDataGetBytePtr(kekData), CFDataGetLength(kekData), iv.Data, &ciphercc);
1110 if (rv) {
1111 dprintf("CCCryptorCreate failed: %d\n", (int)rv);
1112 goto out;
1113 }
1114 size_t expectedKeyLength = CCCryptorGetOutputLength(ciphercc, encKey->Length, true);
1115 cek.Data = PORT_ArenaAlloc(pool, expectedKeyLength);
1116 size_t bytes_output = 0;
1117 rv = CCCryptorUpdate(ciphercc, encKey->Data, encKey->Length, cek.Data, expectedKeyLength, &bytes_output);
1118 if (rv) {
1119 dprintf("CCCryptorUpdate failed: %d\n", (int)rv);
1120 goto out;
1121 }
1122 size_t final_bytes_output = 0;
1123 rv = CCCryptorFinal(ciphercc, cek.Data+bytes_output, expectedKeyLength - bytes_output, &final_bytes_output);
1124 if (rv) {
1125 dprintf("CCCryptorFinal failed: %d\n", (int)rv);
1126 goto out;
1127 }
1128 cek.Length = bytes_output + final_bytes_output;
1129
1130 /* create the SecSymmetricKeyRef */
1131 cekData = CFDataCreate(NULL, cek.Data, cek.Length);
1132 keyType = SECOID_CopyKeyTypeByTag(bulkalgtag);
1133 if (!keyType) {
1134 goto out;
1135 }
1136 cekParams = CFDictionaryCreateMutable(NULL, 1,
1137 &kCFTypeDictionaryKeyCallBacks,
1138 &kCFTypeDictionaryValueCallBacks);
1139 if (!cekParams) {
1140 goto out;
1141 }
1142 CFDictionaryAddValue(cekParams, kSecAttrKeyType, keyType);
1143 outKey = SecKeyCreateFromData(cekParams, cekData, NULL);
1144
1145 out:
1146 if(pool != NULL) {
1147 PORT_FreeArena(pool, PR_FALSE);
1148 }
1149 if (theirPubData) { CFRelease(theirPubData); }
1150 if (theirKeyLen != NULL) { CFRelease(theirKeyLen); }
1151 if (theirPubKey) { CFRelease(theirPubKey); }
1152 if (theirKeyAttrs) { CFRelease(theirKeyAttrs); }
1153 if (sharedInfoData) { CFRelease(sharedInfoData); }
1154 if (kekLen != NULL) { CFRelease(kekLen); }
1155 if (kekParams) { CFRelease(kekParams); }
1156 if (kekData) { CFRelease(kekData); }
1157 if (error) { CFRelease(error); }
1158 if (ciphercc) { CCCryptorRelease(ciphercc); }
1159 if (cekData) { CFRelease(cekData); }
1160 if (keyType) { CFRelease(keyType); }
1161 if (cekParams) { CFRelease(cekParams); }
1162 if(outKey == NULL) {
1163 PORT_SetError(SEC_ERROR_NO_KEY);
1164 }
1165 return outKey;
1166 }