]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_smime/lib/cmspubkey.c
Security-58286.51.6.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 <CommonCrypto/CommonCryptor.h>
56 #include <CommonCrypto/CommonRandomSPI.h>
57 #include <CommonCrypto/CommonRandom.h>
58
59 /* ====== RSA ======================================================================= */
60
61 /*
62 * SecCmsUtilEncryptSymKeyRSA - wrap a symmetric key with RSA
63 *
64 * this function takes a symmetric key and encrypts it using an RSA public key
65 * according to PKCS#1 and RFC2633 (S/MIME)
66 */
67 OSStatus
68 SecCmsUtilEncryptSymKeyRSA(PLArenaPool *poolp, SecCertificateRef cert,
69 SecSymmetricKeyRef bulkkey,
70 CSSM_DATA_PTR encKey)
71 {
72 SecPublicKeyRef publickey = SecCertificateCopyPublicKey_ios(cert);
73 if (publickey == NULL)
74 return SECFailure;
75
76 OSStatus rv = SecCmsUtilEncryptSymKeyRSAPubKey(poolp, publickey, bulkkey, encKey);
77 CFRelease(publickey);
78 return rv;
79 }
80
81 OSStatus
82 SecCmsUtilEncryptSymKeyRSAPubKey(PLArenaPool *poolp,
83 SecPublicKeyRef publickey,
84 SecSymmetricKeyRef bulkkey, CSSM_DATA_PTR encKey)
85 {
86 OSStatus rv;
87 unsigned int data_len;
88 //KeyType keyType;
89 void *mark = NULL;
90 CFDictionaryRef theirKeyAttrs = NULL;
91
92 mark = PORT_ArenaMark(poolp);
93 if (!mark)
94 goto loser;
95
96 #if 0
97 /* sanity check */
98 keyType = SECKEY_GetPublicKeyType(publickey);
99 PORT_Assert(keyType == rsaKey);
100 if (keyType != rsaKey) {
101 goto loser;
102 }
103 #endif
104 /* allocate memory for the encrypted key */
105 theirKeyAttrs = SecKeyCopyAttributes(publickey);
106 if (!theirKeyAttrs) {
107 goto loser;
108 }
109
110 CFNumberRef keySizeNum = CFDictionaryGetValue(theirKeyAttrs, kSecAttrKeySizeInBits);
111 if (!CFNumberGetValue(keySizeNum, kCFNumberIntType, &data_len)) {
112 goto loser;
113 }
114 // Convert length to bytes;
115 data_len /= 8;
116
117 encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, data_len);
118 encKey->Length = data_len;
119 if (encKey->Data == NULL)
120 goto loser;
121
122 /* encrypt the key now */
123 rv = WRAP_PubWrapSymKey(publickey, bulkkey, encKey);
124 if (rv != SECSuccess)
125 goto loser;
126
127 PORT_ArenaUnmark(poolp, mark);
128 return SECSuccess;
129
130 loser:
131 if (theirKeyAttrs) {
132 CFRelease(theirKeyAttrs);
133 }
134 if (mark) {
135 PORT_ArenaRelease(poolp, mark);
136 }
137 return SECFailure;
138 }
139
140 /*
141 * SecCmsUtilDecryptSymKeyRSA - unwrap a RSA-wrapped symmetric key
142 *
143 * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
144 * key handle. Please note that the actual unwrapped key data may not be allowed to leave
145 * a hardware token...
146 */
147 SecSymmetricKeyRef
148 SecCmsUtilDecryptSymKeyRSA(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECOidTag bulkalgtag)
149 {
150 /* that's easy */
151 return WRAP_PubUnwrapSymKey(privkey, encKey, bulkalgtag);
152 }
153
154 #if 0
155 // @@@ Implement Fortezza and Diffie hellman support
156
157 /* ====== MISSI (Fortezza) ========================================================== */
158
159 extern const SecAsn1Template NSS_SMIMEKEAParamTemplateAllParams[];
160
161 OSStatus
162 SecCmsUtilEncryptSymKeyMISSI(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef bulkkey,
163 SECOidTag symalgtag, CSSM_DATA_PTR encKey, CSSM_DATA_PTR *pparams, void *pwfn_arg)
164 {
165 SECOidTag certalgtag; /* the certificate's encryption algorithm */
166 SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */
167 OSStatus rv = SECFailure;
168 CSSM_DATA_PTR params = NULL;
169 OSStatus err;
170 SecSymmetricKeyRef tek;
171 SecCertificateRef ourCert;
172 SecPublicKeyRef ourPubKey, *publickey = NULL;
173 SecPrivateKeyRef ourPrivKey = NULL;
174 SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid;
175 SecCmsSMIMEKEAParameters keaParams;
176 PLArenaPool *arena = NULL;
177 extern const SecAsn1Template *nss_cms_get_kea_template(SecCmsKEATemplateSelector whichTemplate);
178 const SECAlgorithmID *algid;
179
180 /* Clear keaParams, since cleanup code checks the lengths */
181 (void) memset(&keaParams, 0, sizeof(keaParams));
182
183 SecCertificateGetAlgorithmID(cert,&algid);
184 certalgtag = SECOID_GetAlgorithmTag(algid);
185 PORT_Assert(certalgtag == SEC_OID_MISSI_KEA_DSS_OLD ||
186 certalgtag == SEC_OID_MISSI_KEA_DSS ||
187 certalgtag == SEC_OID_MISSI_KEA);
188
189 #define SMIME_FORTEZZA_RA_LENGTH 128
190 #define SMIME_FORTEZZA_IV_LENGTH 24
191 #define SMIME_FORTEZZA_MAX_KEY_SIZE 256
192
193 /* We really want to show our KEA tag as the key exchange algorithm tag. */
194 encalgtag = SEC_OID_NETSCAPE_SMIME_KEA;
195
196 /* Get the public key of the recipient. */
197 publickey = CERT_ExtractPublicKey(cert);
198 if (publickey == NULL) goto loser;
199
200 /* Find our own cert, and extract its keys. */
201 ourCert = PK11_FindBestKEAMatch(cert, pwfn_arg);
202 if (ourCert == NULL) goto loser;
203
204 arena = PORT_NewArena(1024);
205 if (arena == NULL)
206 goto loser;
207
208 ourPubKey = CERT_ExtractPublicKey(ourCert);
209 if (ourPubKey == NULL) {
210 CERT_DestroyCertificate(ourCert);
211 goto loser;
212 }
213
214 /* While we're here, copy the public key into the outgoing
215 * KEA parameters. */
216 SECITEM_CopyItem(arena, &(keaParams.originatorKEAKey), &(ourPubKey->u.fortezza.KEAKey));
217 SECKEY_DestroyPublicKey(ourPubKey);
218 ourPubKey = NULL;
219
220 /* Extract our private key in order to derive the KEA key. */
221 ourPrivKey = PK11_FindKeyByAnyCert(ourCert, pwfn_arg);
222 CERT_DestroyCertificate(ourCert); /* we're done with this */
223 if (!ourPrivKey)
224 goto loser;
225
226 /* Prepare raItem with 128 bytes (filled with zeros). */
227 keaParams.originatorRA.Data = (unsigned char *)PORT_ArenaAlloc(arena,SMIME_FORTEZZA_RA_LENGTH);
228 keaParams.originatorRA.Length = SMIME_FORTEZZA_RA_LENGTH;
229
230 /* Generate the TEK (token exchange key) which we use
231 * to wrap the bulk encryption key. (keaparams.originatorRA) will be
232 * filled with a random seed which we need to send to
233 * the recipient. (user keying material in RFC2630/DSA speak) */
234 tek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
235 &keaParams.originatorRA, NULL,
236 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
237 CKA_WRAP, 0, pwfn_arg);
238
239 SECKEY_DestroyPublicKey(publickey);
240 SECKEY_DestroyPrivateKey(ourPrivKey);
241 publickey = NULL;
242 ourPrivKey = NULL;
243
244 if (!tek)
245 goto loser;
246
247 /* allocate space for the wrapped key data */
248 encKey->Data = (unsigned char *)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
249 encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE;
250
251 if (encKey->Data == NULL) {
252 CFRelease(tek);
253 goto loser;
254 }
255
256 /* Wrap the bulk key. What we do with the resulting data
257 depends on whether we're using Skipjack to wrap the key. */
258 switch (PK11_AlgtagToMechanism(symalgtag)) {
259 case CKM_SKIPJACK_CBC64:
260 case CKM_SKIPJACK_ECB64:
261 case CKM_SKIPJACK_OFB64:
262 case CKM_SKIPJACK_CFB64:
263 case CKM_SKIPJACK_CFB32:
264 case CKM_SKIPJACK_CFB16:
265 case CKM_SKIPJACK_CFB8:
266 /* SKIPJACK, we use the wrap mechanism because we can do it on the hardware */
267 err = PK11_WrapSymKey(CKM_SKIPJACK_WRAP, NULL, tek, bulkkey, encKey);
268 whichKEA = SecCmsKEAUsesSkipjack;
269 break;
270 default:
271 /* Not SKIPJACK, we encrypt the raw key data */
272 keaParams.nonSkipjackIV.Data =
273 (unsigned char *)PORT_ArenaAlloc(arena, SMIME_FORTEZZA_IV_LENGTH);
274 keaParams.nonSkipjackIV.Length = SMIME_FORTEZZA_IV_LENGTH;
275 err = PK11_WrapSymKey(CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV, tek, bulkkey, encKey);
276 if (err != SECSuccess)
277 goto loser;
278
279 if (encKey->Length != PK11_GetKeyLength(bulkkey)) {
280 /* The size of the encrypted key is not the same as
281 that of the original bulk key, presumably due to
282 padding. Encode and store the real size of the
283 bulk key. */
284 if (SEC_ASN1EncodeInteger(arena, &keaParams.bulkKeySize, PK11_GetKeyLength(bulkkey)) == NULL)
285 err = (OSStatus)PORT_GetError();
286 else
287 /* use full template for encoding */
288 whichKEA = SecCmsKEAUsesNonSkipjackWithPaddedEncKey;
289 }
290 else
291 /* enc key length == bulk key length */
292 whichKEA = SecCmsKEAUsesNonSkipjack;
293 break;
294 }
295
296 CFRelease(tek);
297
298 if (err != SECSuccess)
299 goto loser;
300
301 PORT_Assert(whichKEA != SecCmsKEAInvalid);
302
303 /* Encode the KEA parameters into the recipient info. */
304 params = SEC_ASN1EncodeItem(poolp, NULL, &keaParams, nss_cms_get_kea_template(whichKEA));
305 if (params == NULL)
306 goto loser;
307
308 /* pass back the algorithm params */
309 *pparams = params;
310
311 rv = SECSuccess;
312
313 loser:
314 if (arena)
315 PORT_FreeArena(arena, PR_FALSE);
316 if (publickey)
317 SECKEY_DestroyPublicKey(publickey);
318 if (ourPrivKey)
319 SECKEY_DestroyPrivateKey(ourPrivKey);
320 return rv;
321 }
322
323 SecSymmetricKeyRef
324 SecCmsUtilDecryptSymKeyMISSI(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
325 {
326 /* fortezza: do a key exchange */
327 OSStatus err;
328 CK_MECHANISM_TYPE bulkType;
329 SecSymmetricKeyRef tek;
330 SecPublicKeyRef originatorPubKey;
331 SecCmsSMIMEKEAParameters keaParams;
332 SecSymmetricKeyRef bulkkey;
333 int bulkLength;
334
335 (void) memset(&keaParams, 0, sizeof(keaParams));
336
337 /* NOTE: this uses the SMIME v2 recipientinfo for compatibility.
338 All additional KEA parameters are DER-encoded in the encryption algorithm parameters */
339
340 /* Decode the KEA algorithm parameters. */
341 err = SEC_ASN1DecodeItem(NULL, &keaParams, NSS_SMIMEKEAParamTemplateAllParams,
342 &(keyEncAlg->parameters));
343 if (err != SECSuccess)
344 goto loser;
345
346 /* get originator's public key */
347 originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data,
348 keaParams.originatorKEAKey.Length);
349 if (originatorPubKey == NULL)
350 goto loser;
351
352 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
353 The Derive function generates a shared secret and combines it with the originatorRA
354 data to come up with an unique session key */
355 tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
356 &keaParams.originatorRA, NULL,
357 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
358 CKA_WRAP, 0, pwfn_arg);
359 SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */
360 if (tek == NULL)
361 goto loser;
362
363 /* Now that we have the TEK, unwrap the bulk key
364 with which to decrypt the message. We have to
365 do one of two different things depending on
366 whether Skipjack was used for *bulk* encryption
367 of the message. */
368 bulkType = PK11_AlgtagToMechanism(bulkalgtag);
369 switch (bulkType) {
370 case CKM_SKIPJACK_CBC64:
371 case CKM_SKIPJACK_ECB64:
372 case CKM_SKIPJACK_OFB64:
373 case CKM_SKIPJACK_CFB64:
374 case CKM_SKIPJACK_CFB32:
375 case CKM_SKIPJACK_CFB16:
376 case CKM_SKIPJACK_CFB8:
377 /* Skipjack is being used as the bulk encryption algorithm.*/
378 /* Unwrap the bulk key. */
379 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
380 encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
381 break;
382 default:
383 /* Skipjack was not used for bulk encryption of this
384 message. Use Skipjack CBC64, with the nonSkipjackIV
385 part of the KEA key parameters, to decrypt
386 the bulk key. If the optional parameter bulkKeySize is present,
387 bulk key size is different than the encrypted key size */
388 if (keaParams.bulkKeySize.Length > 0) {
389 err = SEC_ASN1DecodeItem(NULL, &bulkLength,
390 SEC_ASN1_GET(SEC_IntegerTemplate),
391 &keaParams.bulkKeySize);
392 if (err != SECSuccess)
393 goto loser;
394 }
395
396 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV,
397 encKey, bulkType, CKA_DECRYPT, bulkLength);
398 break;
399 }
400 return bulkkey;
401 loser:
402 return NULL;
403 }
404
405 /* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */
406
407 OSStatus
408 SecCmsUtilEncryptSymKeyESDH(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef key,
409 CSSM_DATA_PTR encKey, CSSM_DATA_PTR ukm, SECAlgorithmID *keyEncAlg,
410 CSSM_DATA_PTR pubKey)
411 {
412 #if 0 /* not yet done */
413 SECOidTag certalgtag; /* the certificate's encryption algorithm */
414 SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */
415 OSStatus rv;
416 CSSM_DATA_PTR params = NULL;
417 int data_len;
418 OSStatus err;
419 SecSymmetricKeyRef tek;
420 SecCertificateRef ourCert;
421 SecPublicKeyRef ourPubKey;
422 SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid;
423
424 certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
425 PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY);
426
427 /* We really want to show our KEA tag as the key exchange algorithm tag. */
428 encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN;
429
430 /* Get the public key of the recipient. */
431 publickey = CERT_ExtractPublicKey(cert);
432 if (publickey == NULL) goto loser;
433
434 /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */
435 /* XXXX */ourCert = PK11_FindBestKEAMatch(cert, wincx);
436 if (ourCert == NULL) goto loser;
437
438 arena = PORT_NewArena(1024);
439 if (arena == NULL) goto loser;
440
441 /* While we're here, extract the key pair's public key data and copy it into */
442 /* the outgoing parameters. */
443 /* XXXX */ourPubKey = CERT_ExtractPublicKey(ourCert);
444 if (ourPubKey == NULL)
445 {
446 goto loser;
447 }
448 SECITEM_CopyItem(arena, pubKey, /* XXX */&(ourPubKey->u.fortezza.KEAKey));
449 SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */
450 ourPubKey = NULL;
451
452 /* Extract our private key in order to derive the KEA key. */
453 ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx);
454 CERT_DestroyCertificate(ourCert); /* we're done with this */
455 if (!ourPrivKey) goto loser;
456
457 /* If ukm desired, prepare it - allocate enough space (filled with zeros). */
458 if (ukm) {
459 ukm->Data = (unsigned char*)PORT_ArenaZAlloc(arena,/* XXXX */);
460 ukm->Length = /* XXXX */;
461 }
462
463 /* Generate the KEK (key exchange key) according to RFC2631 which we use
464 * to wrap the bulk encryption key. */
465 kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
466 ukm, NULL,
467 /* XXXX */CKM_KEA_KEY_DERIVE, /* XXXX */CKM_SKIPJACK_WRAP,
468 CKA_WRAP, 0, wincx);
469
470 SECKEY_DestroyPublicKey(publickey);
471 SECKEY_DestroyPrivateKey(ourPrivKey);
472 publickey = NULL;
473 ourPrivKey = NULL;
474
475 if (!kek)
476 goto loser;
477
478 /* allocate space for the encrypted CEK (bulk key) */
479 encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
480 encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE;
481
482 if (encKey->Data == NULL)
483 {
484 CFRelease(kek);
485 goto loser;
486 }
487
488
489 /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */
490 /* bulk encryption algorithm */
491 switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo->encalg))
492 {
493 case /* XXXX */CKM_SKIPJACK_CFB8:
494 err = PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey);
495 whichKEA = SecCmsKEAUsesSkipjack;
496 break;
497 case /* XXXX */CKM_SKIPJACK_CFB8:
498 err = PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey);
499 whichKEA = SecCmsKEAUsesSkipjack;
500 break;
501 default:
502 /* XXXX what do we do here? Neither RC2 nor 3DES... */
503 err = SECFailure;
504 /* set error */
505 break;
506 }
507
508 CFRelease(kek); /* we do not need the KEK anymore */
509 if (err != SECSuccess)
510 goto loser;
511
512 PORT_Assert(whichKEA != SecCmsKEAInvalid);
513
514 /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */
515 /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */
516 params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA));
517 if (params == NULL)
518 goto loser;
519
520 /* now set keyEncAlg */
521 rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params);
522 if (rv != SECSuccess)
523 goto loser;
524
525 /* XXXXXXX this is not right yet */
526 loser:
527 if (arena) {
528 PORT_FreeArena(arena, PR_FALSE);
529 }
530 if (publickey) {
531 SECKEY_DestroyPublicKey(publickey);
532 }
533 if (ourPrivKey) {
534 SECKEY_DestroyPrivateKey(ourPrivKey);
535 }
536 #endif
537 return SECFailure;
538 }
539
540 SecSymmetricKeyRef
541 SecCmsUtilDecryptSymKeyESDH(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
542 {
543 #if 0 /* not yet done */
544 OSStatus err;
545 CK_MECHANISM_TYPE bulkType;
546 SecSymmetricKeyRef tek;
547 SecPublicKeyRef originatorPubKey;
548 SecCmsSMIMEKEAParameters keaParams;
549
550 /* XXXX get originator's public key */
551 originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data,
552 keaParams.originatorKEAKey.Length);
553 if (originatorPubKey == NULL)
554 goto loser;
555
556 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
557 The Derive function generates a shared secret and combines it with the originatorRA
558 data to come up with an unique session key */
559 tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
560 &keaParams.originatorRA, NULL,
561 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
562 CKA_WRAP, 0, pwfn_arg);
563 SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */
564 if (tek == NULL)
565 goto loser;
566
567 /* Now that we have the TEK, unwrap the bulk key
568 with which to decrypt the message. */
569 /* Skipjack is being used as the bulk encryption algorithm.*/
570 /* Unwrap the bulk key. */
571 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
572 encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
573
574 return bulkkey;
575
576 loser:
577 #endif
578 return NULL;
579 }
580
581 #endif /* Fortezza, DIffie-Hellman */
582
583 #define CFRELEASE(cf) if(cf != NULL) { CFRelease(cf); }
584
585 /* ====== ECDH (Ephemeral-Static Diffie-Hellman) ==================================== */
586
587 #pragma mark ---- ECDH support functions ----
588
589 #ifdef NDEBUG
590 #define CSSM_PERROR(f, r)
591 #define dprintf(args...)
592 #else
593 #define CSSM_PERROR(f, r) cssmPerror(f, r)
594 #define dprintf(args...) fprintf(stderr, args)
595 #endif
596
597 /* Length of KeyAgreeRecipientInfo.ukm we create */
598 #define UKM_LENGTH 8
599
600 /* KEK algorithm info we generate */
601 #define ECDH_KEK_ALG_TAG SEC_OID_DES_EDE3_CBC
602 #define ECDH_KEK_KEY_CSSM_ALGID CSSM_ALGID_3DES_3KEY
603 #define ECDH_KEK_ENCR_CSSM_ALGID CSSM_ALGID_3DES_3KEY_EDE
604 #define ECDH_KEK_KEY_LEN_BYTES 24
605 #define ECDH_KEK_IV_LEN_BYTES 8
606
607 #define CMS_DUMP_BUFS 0
608 #if CMS_DUMP_BUFS
609
610 static void dumpBuf(
611 const char *label,
612 const CSSM_DATA *cd)
613 {
614 unsigned dex;
615
616 printf("%s:\n ", label);
617 for(dex=0; dex<cd->Length; dex++) {
618 printf("%02X ", cd->Data[dex]);
619 if(((dex % 16) == 15) && (dex != (cd->Length - 1))) {
620 printf("\n ");
621 }
622 }
623 putchar('\n');
624 }
625
626 #else
627 #define dumpBuf(l, d)
628 #endif /* CMS_DUMP_BUFS */
629
630 /*
631 * The ECC-CMS-SharedInfo struct, as defined in RFC 3278 8.2, and the
632 * template for DER encoding and decoding it.
633 */
634 typedef struct {
635 SECAlgorithmID algId; /* KEK alg, NULL params */
636 CSSM_DATA entityUInfo; /* optional, ukm */
637 CSSM_DATA suppPubInfo; /* length of KEK in bits as 4-byte integer */
638 } ECC_CMS_SharedInfo;
639
640 static const SecAsn1Template ECC_CMS_SharedInfoTemplate[] = {
641 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ECC_CMS_SharedInfo) },
642 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0,
643 offsetof(ECC_CMS_SharedInfo,entityUInfo),
644 kSecAsn1OctetStringTemplate },
645 { SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 2,
646 offsetof(ECC_CMS_SharedInfo,suppPubInfo),
647 kSecAsn1OctetStringTemplate },
648 { 0 }
649 };
650
651 /*
652 * Given a context specified via a CSSM_CC_HANDLE, add a new
653 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
654 * AttributeLength, and an untyped pointer.
655 */
656 /* specify either 32-bit integer or a pointer as an added attribute value */
657 typedef enum {
658 CAT_Uint32,
659 CAT_Ptr
660 } ContextAttrType;
661
662 /* convert uint32 to big-endian 4 bytes */
663 static void int32ToBytes(
664 uint32_t i,
665 unsigned char *b)
666 {
667 int dex;
668 for(dex=3; dex>=0; dex--) {
669 b[dex] = i;
670 i >>= 8;
671 }
672 }
673
674 /*
675 * Given an OID tag, return key size and mode.
676 * NOTE: ciphers with variable key sizes, like RC2, RC4, and RC5 cannot
677 * be used here because the message does not contain a key size
678 * indication.
679 */
680 static OSStatus encrAlgInfo(
681 SECOidTag oidTag,
682 uint32 *keySizeBits, /* RETURNED */
683 CCAlgorithm *algorithm, /* RETURNED */
684 CCOptions *options) /* RETURNED */
685 {
686 *keySizeBits = 64; /* default */
687 *options = kCCOptionPKCS7Padding; /* default */
688
689 switch(oidTag) {
690 case SEC_OID_RC2_CBC:
691 case SEC_OID_RC4:
692 case SEC_OID_RC5_CBC_PAD:
693 dprintf("encrAlgInfo: key size unknowable\n");
694 return errSecDataNotAvailable;
695 case SEC_OID_DES_EDE:
696 /* Not sure about this; SecCmsCipherContextStart() treats this
697 * like SEC_OID_DES_EDE3_CBC... */
698 *options = kCCOptionECBMode;
699 // fall through
700 case SEC_OID_DES_EDE3_CBC:
701 *keySizeBits = 192;
702 *algorithm = kCCAlgorithm3DES;
703 break;
704 case SEC_OID_DES_ECB:
705 *options = kCCOptionECBMode;
706 // fall through
707 case SEC_OID_DES_CBC:
708 *algorithm = kCCAlgorithmDES;
709 break;
710 case SEC_OID_AES_128_CBC:
711 *keySizeBits = 128;
712 *algorithm = kCCAlgorithmAES;
713 break;
714 case SEC_OID_AES_192_CBC:
715 *keySizeBits = 192;
716 *algorithm = kCCAlgorithmAES;
717 break;
718 case SEC_OID_AES_256_CBC:
719 *keySizeBits = 256;
720 *algorithm = kCCAlgorithmAES;
721 break;
722 case SEC_OID_AES_128_ECB:
723 *keySizeBits = 128;
724 *algorithm = kCCAlgorithmAES;
725 *options = kCCOptionECBMode;
726 break;
727 case SEC_OID_AES_192_ECB:
728 *keySizeBits = 192;
729 *algorithm = kCCAlgorithmAES;
730 *options = kCCOptionECBMode;
731 break;
732 case SEC_OID_AES_256_ECB:
733 *keySizeBits = 256;
734 *algorithm = kCCAlgorithmAES;
735 *options = kCCOptionECBMode;
736 break;
737 default:
738 dprintf("encrAlgInfo: unknown alg tag (%d)\n", (int)oidTag);
739 return errSecDataNotAvailable;
740 }
741 return noErr;
742 }
743
744 #pragma mark ---- ECDH CEK key wrap ----
745
746 /*
747 * Encrypt bulk encryption key (a.k.a. content encryption key, CEK) using ECDH
748 */
749 OSStatus
750 SecCmsUtilEncryptSymKeyECDH(
751 PLArenaPool *poolp,
752 SecCertificateRef cert, /* recipient's cert */
753 SecSymmetricKeyRef key, /* bulk key */
754 /* remaining fields RETURNED */
755 CSSM_DATA_PTR encKey, /* encrypted key --> recipientEncryptedKeys[0].EncryptedKey */
756 CSSM_DATA_PTR ukm, /* random UKM --> KeyAgreeRecipientInfo.ukm */
757 SECAlgorithmID *keyEncAlg, /* alg := dhSinglePass-stdDH-sha1kdf-scheme
758 * params := another encoded AlgId, with the KEK alg and IV */
759 CSSM_DATA_PTR pubKey) /* our pub key as ECPoint -->
760 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
761 {
762 OSStatus rv = noErr;
763 SecKeyRef theirPubKey = NULL, ourPubKey = NULL, ourPrivKey = NULL;
764 CFDictionaryRef theirKeyAttrs = NULL, ourKeyParams = NULL, kekParams = NULL;
765 uint8_t iv[ECDH_KEK_IV_LEN_BYTES];
766 CSSM_DATA ivData = { ECDH_KEK_IV_LEN_BYTES, iv };
767 SECAlgorithmID kekAlgId;
768 SECOidData *kekOid;
769 ECC_CMS_SharedInfo sharedInfo;
770 CSSM_DATA sharedInfoEnc = {0, NULL};
771 uint8 nullData[2] = {SEC_ASN1_NULL, 0};
772 uint8 keyLenAsBytes[4];
773 CFDataRef sharedInfoData = NULL, kekData = NULL, ourPubData = NULL;
774 CFNumberRef kekLen = NULL;
775 CFErrorRef error = NULL;
776 CCCryptorRef ciphercc = NULL;
777
778 encKey->Data = NULL;
779 encKey->Length = 0;
780
781 /* Copy the recipient's static public ECDH key */
782 #if TARGET_OS_IPHONE
783 theirPubKey = SecCertificateCopyPublicKey(cert);
784 #else
785 rv = SecCertificateCopyPublicKey(cert, &theirPubKey);
786 #endif
787 if (rv || !theirPubKey) {
788 dprintf("SecCmsUtilEncryptSymKeyECDH: failed to get public key from cert, %d\n", (int)rv);
789 goto out;
790 }
791
792 theirKeyAttrs = SecKeyCopyAttributes(theirPubKey);
793 if (!theirKeyAttrs) {
794 dprintf("SecCmsUtilEncryptSymKeyECDH: failed to get key attributes\n");
795 goto out;
796 }
797
798 CFStringRef keyType = NULL;
799 CFNumberRef keySizeNum = NULL;
800 keyType = CFDictionaryGetValue(theirKeyAttrs, kSecAttrKeyType);
801 keySizeNum = CFDictionaryGetValue(theirKeyAttrs, kSecAttrKeySizeInBits);
802
803 if (!CFEqual(kSecAttrKeyTypeECSECPrimeRandom, keyType)) {
804 dprintf("SecCmsUtilEncryptSymKeyECDH: unsupported key type\n");
805 rv = CSSMERR_CSP_INVALID_KEY;
806 goto out;
807 }
808
809 /* Generate ephemeral ECDH key */
810 const void *keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits, kSecAttrNoLegacy};
811 const void *values[] = { keyType, keySizeNum, kCFBooleanTrue };
812 ourKeyParams = CFDictionaryCreate(NULL, keys, values, 3,
813 &kCFTypeDictionaryKeyCallBacks,
814 &kCFTypeDictionaryValueCallBacks);
815 rv = SecKeyGeneratePair(ourKeyParams, &ourPubKey, &ourPrivKey);
816 if (rv || !ourPubKey || !ourPrivKey) {
817 dprintf("SecKeyGeneratePair: unable to generate ECDH key pair, %d\n", (int)rv);
818 goto out;
819 }
820
821 /* Generate UKM */
822 ukm->Data = PORT_Alloc(UKM_LENGTH);
823 ukm->Length = UKM_LENGTH;
824 rv = CCRandomCopyBytes(kCCRandomDefault, ukm->Data, UKM_LENGTH);
825 if (rv || !ukm->Data) {
826 dprintf("CCRandomGenerateBytes failed, %d", (int)rv);
827 goto out;
828 }
829
830 /*
831 * OK, we have to set up a weird SECAlgorithmID.
832 * algorithm = dhSinglePass-stdDH-sha1kdf-scheme
833 * params = an encoded SECAlgorithmID representing the KEK algorithm, with
834 * algorithm = whatever we pick
835 * parameters = IV as octet string (though I haven't seen that specified
836 * anywhere; it's how the CEK IV is encoded)
837 */
838
839 /* Generate 8-byte IV */
840 rv = CCRandomCopyBytes(kCCRandomDefault, iv, ECDH_KEK_IV_LEN_BYTES);
841 if (rv) {
842 dprintf("CCRandomGenerateBytes failed, %d", (int)rv);
843 goto out;
844 }
845 dumpBuf("sender IV", &ivData);
846
847 memset(&kekAlgId, 0, sizeof(kekAlgId));
848 if (!SEC_ASN1EncodeItem(poolp, &kekAlgId.parameters,
849 &ivData, kSecAsn1OctetStringTemplate)) {
850 rv = internalComponentErr;
851 goto out;
852 }
853
854 /* Drop in the KEK OID and encode the whole thing */
855 kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG);
856 if(kekOid == NULL) {
857 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
858 rv = internalComponentErr;
859 goto out;
860 }
861 kekAlgId.algorithm = kekOid->oid;
862 memset(keyEncAlg, 0, sizeof(*keyEncAlg));
863 if (!SEC_ASN1EncodeItem(poolp, &keyEncAlg->parameters,
864 &kekAlgId, SECOID_AlgorithmIDTemplate)) {
865 rv = internalComponentErr;
866 goto out;
867 }
868 kekOid = SECOID_FindOIDByTag(SEC_OID_DH_SINGLE_STD_SHA1KDF);
869 if(kekOid == NULL) {
870 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
871 rv = internalComponentErr;
872 goto out;
873 }
874 keyEncAlg->algorithm = kekOid->oid;
875
876 /*
877 * Now in order to derive the KEK proper, we have to create a
878 * ECC-CMS-SharedInfo, which does not appear in the message, and DER
879 * encode that struct, the result of which is used as the
880 * SharedInfo value in the KEK key derive.
881 */
882 memset(&sharedInfo, 0, sizeof(sharedInfo));
883 kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG);
884 sharedInfo.algId.algorithm = kekOid->oid;
885 sharedInfo.algId.parameters.Data = nullData;
886 sharedInfo.algId.parameters.Length = 2;
887 sharedInfo.entityUInfo = *ukm;
888 int32ToBytes(ECDH_KEK_KEY_LEN_BYTES << 3, keyLenAsBytes);
889 sharedInfo.suppPubInfo.Length = 4;
890 sharedInfo.suppPubInfo.Data = keyLenAsBytes;
891 if (!SEC_ASN1EncodeItem(poolp, &sharedInfoEnc,
892 &sharedInfo, ECC_CMS_SharedInfoTemplate)) {
893 rv = internalComponentErr;
894 goto out;
895 }
896 dumpBuf("sender encoded SharedInfo", &sharedInfoEnc);
897
898 /* Derive KEK */
899 sharedInfoData = CFDataCreate(NULL, sharedInfoEnc.Data, sharedInfoEnc.Length);
900 int32_t ecdh_key_key_len = ECDH_KEK_KEY_LEN_BYTES;
901 kekLen = CFNumberCreate(NULL, kCFNumberSInt32Type, &ecdh_key_key_len);
902 const void *kekKeys[] = { kSecKeyKeyExchangeParameterRequestedSize, kSecKeyKeyExchangeParameterSharedInfo };
903 const void *kekValues[] = { kekLen, sharedInfoData };
904 kekParams = CFDictionaryCreate(NULL, kekKeys, kekValues, 2,
905 &kCFTypeDictionaryKeyCallBacks,
906 &kCFTypeDictionaryValueCallBacks);
907 kekData = SecKeyCopyKeyExchangeResult(ourPrivKey, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1,
908 theirPubKey, kekParams, &error);
909 if (error) {
910 dprintf("SecKeyCopyKeyExchangeResult: failed\n");
911 goto out;
912 }
913
914 /*
915 * Encrypt the raw CEK bits with the KEK we just derived
916 */
917 rv = CCCryptorCreate(kCCEncrypt, kCCAlgorithm3DES, kCCOptionPKCS7Padding,
918 CFDataGetBytePtr(kekData), CFDataGetLength(kekData), iv, &ciphercc);
919 if (rv) {
920 dprintf("CCCryptorCreate failed: %d\n", (int)rv);
921 goto out;
922 }
923 CSSM_KEY cek;
924 rv = cmsNullWrapKey(key, &cek);
925 if (rv) {
926 dprintf("SecKeyGetCSSMKey failed: %d\n", (int)rv);
927 goto out;
928 }
929 size_t expectedEncKeyLength = CCCryptorGetOutputLength(ciphercc, cek.KeyData.Length, true);
930 encKey->Data = PORT_ArenaAlloc(poolp, expectedEncKeyLength);
931 size_t bytes_output = 0;
932 rv = CCCryptorUpdate(ciphercc, cek.KeyData.Data, cek.KeyData.Length, encKey->Data, expectedEncKeyLength, &bytes_output);
933 if (rv) {
934 dprintf("CCCryptorUpdate failed: %d\n", (int)rv);
935 goto out;
936 }
937 size_t final_bytes_output = 0;
938 rv = CCCryptorFinal(ciphercc, encKey->Data+bytes_output, expectedEncKeyLength - bytes_output, &final_bytes_output);
939 if (rv) {
940 dprintf("CCCryptorFinal failed: %d\n", (int)rv);
941 goto out;
942 }
943 encKey->Length = bytes_output + final_bytes_output;
944
945 /* Provide our ephemeral public key to the caller */
946 ourPubData = SecKeyCopyExternalRepresentation(ourPubKey, &error);
947 if (error) {
948 dprintf("SecKeyCopyExternalRepresentation failed\n");
949 goto out;
950 }
951 pubKey->Length = CFDataGetLength(ourPubData);
952 pubKey->Data = malloc(pubKey->Length);
953 if (pubKey->Data) {
954 memcpy(pubKey->Data, CFDataGetBytePtr(ourPubData), pubKey->Length);
955 } else {
956 rv = errSecAllocate;
957 }
958 /* pubKey is bit string, convert here */
959 pubKey->Length <<= 3;
960
961 out:
962 if (theirPubKey) { CFRelease(theirPubKey); }
963 if (theirKeyAttrs) { CFRelease(theirKeyAttrs); }
964 if (ourKeyParams) { CFRelease(ourKeyParams); }
965 if (ourPubKey) { CFRelease(ourPubKey); }
966 if (ourPrivKey) { CFRelease(ourPrivKey); }
967 if (sharedInfoData) { CFRelease(sharedInfoData); }
968 if (kekLen) { CFRelease(kekLen); }
969 if (kekParams) { CFRelease(kekParams); }
970 if (kekData) { CFRelease(kekData); }
971 if (error) { CFRelease(error); }
972 if (ciphercc) { CCCryptorRelease(ciphercc); }
973 if (ourPubData) { CFRelease(ourPubData); }
974 if (rv && encKey->Data) {
975 PORT_Free(encKey->Data);
976 encKey->Data = NULL;
977 encKey->Length = 0;
978 }
979 if (rv && ukm->Data) {
980 PORT_Free(ukm->Data);
981 ukm->Data = NULL;
982 ukm->Length = 0;
983 }
984 return rv;
985 }
986
987
988 #pragma mark ---- ECDH CEK key unwrap ----
989
990 SecSymmetricKeyRef
991 SecCmsUtilDecryptSymKeyECDH(
992 SecPrivateKeyRef privkey, /* our private key */
993 CSSM_DATA_PTR encKey, /* encrypted CEK */
994 CSSM_DATA_PTR ukm, /* random UKM from KeyAgreeRecipientInfo.ukm */
995 SECAlgorithmID *keyEncAlg, /* alg := dhSinglePass-stdDH-sha1kdf-scheme
996 * params := another encoded AlgId, with the KEK alg and IV */
997 SECOidTag bulkalgtag, /* algorithm of returned key */
998 CSSM_DATA_PTR pubKey) /* sender's pub key as ECPoint from
999 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
1000 {
1001 SecSymmetricKeyRef outKey = NULL;
1002 OSStatus rv = noErr;
1003 PLArenaPool *pool = NULL;
1004 SECAlgorithmID keyAlgParam;
1005 SECOidData *kekOid = NULL;
1006 CSSM_DATA iv = {0, NULL};
1007 ECC_CMS_SharedInfo sharedInfo;
1008 CSSM_DATA sharedInfoEnc = {0, NULL};
1009 uint8 nullData[2] = {SEC_ASN1_NULL, 0};
1010 uint8 keyLenAsBytes[4];
1011 uint32 kekSizeBits;
1012 SecKeyRef theirPubKey = NULL;
1013 CFStringRef keyType = NULL;
1014 CFDictionaryRef theirKeyAttrs = NULL, kekParams = NULL;
1015 CFMutableDictionaryRef cekParams = NULL;
1016 CFDataRef sharedInfoData = NULL, theirPubData= NULL, kekData = NULL, cekData = NULL;
1017 CFNumberRef kekLen = NULL, theirKeyLen = NULL;
1018 CFErrorRef error = NULL;
1019 CCAlgorithm alg;
1020 CCOptions options = 0;
1021 CCCryptorRef ciphercc = NULL;
1022 size_t theirKeySizeInBits = 0;
1023
1024 /*
1025 * Decode keyEncAlg.params to get KEK algorithm and IV
1026 */
1027 pool = PORT_NewArena(1024);
1028 if(pool == NULL) {
1029 goto out;
1030 }
1031 memset(&keyAlgParam, 0, sizeof(keyAlgParam));
1032 if(SEC_ASN1DecodeItem(pool, &keyAlgParam, SECOID_AlgorithmIDTemplate,
1033 &keyEncAlg->parameters)) {
1034 dprintf("SecCmsUtilDecryptSymKeyECDH: error decoding keyAlgParams\n");
1035 goto out;
1036 }
1037 kekOid = SECOID_FindOID(&keyAlgParam.algorithm);
1038 if(kekOid == NULL) {
1039 dprintf("SecCmsUtilDecryptSymKeyECDH: unknown KEK enc OID\n");
1040 goto out;
1041 }
1042 rv = encrAlgInfo(kekOid->offset, &kekSizeBits, &alg, &options);
1043 if(rv) {
1044 goto out;
1045 }
1046 /* IV is OCTET STRING in the alg params */
1047 if(SEC_ASN1DecodeItem(pool, &iv, kSecAsn1OctetStringTemplate,
1048 &keyAlgParam.parameters)) {
1049 /*
1050 * Not sure here - is it legal to have no IV? I haven't seen this
1051 * addressed in any spec. Maybe we should condition the behavior
1052 * here on the KEK algorithm.
1053 */
1054 dprintf("SecCmsUtilDecryptSymKeyECDH: no KEK IV\n");
1055 goto out;
1056 }
1057
1058 /*
1059 * Now in order to derive the KEK proper, we have to create a
1060 * ECC-CMS-SharedInfo, which does not appear in the message, and DER
1061 * encode that struct, the result of which is used as the
1062 * SharedInfo value in the KEK key derive.
1063 */
1064 memset(&sharedInfo, 0, sizeof(sharedInfo));
1065 sharedInfo.algId.algorithm = kekOid->oid;
1066 sharedInfo.algId.parameters.Data = nullData;
1067 sharedInfo.algId.parameters.Length = 2;
1068 sharedInfo.entityUInfo = *ukm;
1069 int32ToBytes(kekSizeBits, keyLenAsBytes);
1070 sharedInfo.suppPubInfo.Length = 4;
1071 sharedInfo.suppPubInfo.Data = keyLenAsBytes;
1072 if (!SEC_ASN1EncodeItem(pool, &sharedInfoEnc,
1073 &sharedInfo, ECC_CMS_SharedInfoTemplate)) {
1074 rv = internalComponentErr;
1075 goto out;
1076 }
1077 dumpBuf("receiver encoded SharedInfo", &sharedInfoEnc);
1078 dumpBuf("receiver IV", &iv);
1079 dumpBuf("receiver UKM", ukm);
1080 dumpBuf("sender's public key", pubKey);
1081
1082 /* pubKey is bit string, convert here */
1083 theirKeySizeInBits = pubKey->Length;
1084 pubKey->Length = (theirKeySizeInBits + 7) >> 3;
1085 theirPubData = CFDataCreate(NULL, pubKey->Data, pubKey->Length);
1086 theirKeyLen = CFNumberCreate(NULL, kCFNumberSInt32Type, &theirKeySizeInBits);
1087 const void *keys[] = { kSecAttrKeyType, kSecAttrKeyClass, kSecAttrKeySizeInBits };
1088 const void *values[] = { kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClassPublic, theirKeyLen};
1089 theirKeyAttrs = CFDictionaryCreate(NULL, keys, values, 3,
1090 &kCFTypeDictionaryKeyCallBacks,
1091 &kCFTypeDictionaryValueCallBacks);
1092 theirPubKey = SecKeyCreateWithData(theirPubData, theirKeyAttrs, &error);
1093 if (error) {
1094 dprintf("SecKeyCreateWithData: failed\n");
1095 goto out;
1096 }
1097
1098 /* Derive KEK */
1099 sharedInfoData = CFDataCreate(NULL, sharedInfoEnc.Data, sharedInfoEnc.Length);
1100 int32_t ecdh_key_key_len = (kekSizeBits + 7) >> 3;
1101 kekLen = CFNumberCreate(NULL, kCFNumberSInt32Type, &ecdh_key_key_len);
1102 const void *kekKeys[] = { kSecKeyKeyExchangeParameterRequestedSize, kSecKeyKeyExchangeParameterSharedInfo };
1103 const void *kekValues[] = { kekLen, sharedInfoData };
1104 kekParams = CFDictionaryCreate(NULL, kekKeys, kekValues, 2,
1105 &kCFTypeDictionaryKeyCallBacks,
1106 &kCFTypeDictionaryValueCallBacks);
1107 kekData = SecKeyCopyKeyExchangeResult(privkey, kSecKeyAlgorithmECDHKeyExchangeStandardX963SHA1,
1108 theirPubKey, kekParams, &error);
1109 if (error) {
1110 dprintf("SecKeyCopyKeyExchangeResult: failed\n");
1111 goto out;
1112 }
1113
1114 /*
1115 * Decrypt the raw CEK bits with the KEK we just derived
1116 */
1117 CSSM_DATA cek = { 0, NULL };
1118 rv = CCCryptorCreate(kCCDecrypt, alg, options,
1119 CFDataGetBytePtr(kekData), CFDataGetLength(kekData), iv.Data, &ciphercc);
1120 if (rv) {
1121 dprintf("CCCryptorCreate failed: %d\n", (int)rv);
1122 goto out;
1123 }
1124 size_t expectedKeyLength = CCCryptorGetOutputLength(ciphercc, encKey->Length, true);
1125 cek.Data = PORT_ArenaAlloc(pool, expectedKeyLength);
1126 size_t bytes_output = 0;
1127 rv = CCCryptorUpdate(ciphercc, encKey->Data, encKey->Length, cek.Data, expectedKeyLength, &bytes_output);
1128 if (rv) {
1129 dprintf("CCCryptorUpdate failed: %d\n", (int)rv);
1130 goto out;
1131 }
1132 size_t final_bytes_output = 0;
1133 rv = CCCryptorFinal(ciphercc, cek.Data+bytes_output, expectedKeyLength - bytes_output, &final_bytes_output);
1134 if (rv) {
1135 dprintf("CCCryptorFinal failed: %d\n", (int)rv);
1136 goto out;
1137 }
1138 cek.Length = bytes_output + final_bytes_output;
1139
1140 /* create the SecSymmetricKeyRef */
1141 cekData = CFDataCreate(NULL, cek.Data, cek.Length);
1142 keyType = SECOID_CopyKeyTypeByTag(bulkalgtag);
1143 if (!keyType) {
1144 goto out;
1145 }
1146 cekParams = CFDictionaryCreateMutable(NULL, 1,
1147 &kCFTypeDictionaryKeyCallBacks,
1148 &kCFTypeDictionaryValueCallBacks);
1149 if (!cekParams) {
1150 goto out;
1151 }
1152 CFDictionaryAddValue(cekParams, kSecAttrKeyType, keyType);
1153 outKey = SecKeyCreateFromData(cekParams, cekData, NULL);
1154
1155 out:
1156 if(pool != NULL) {
1157 PORT_FreeArena(pool, PR_FALSE);
1158 }
1159 if (theirPubData) { CFRelease(theirPubData); }
1160 if (theirKeyLen) { CFRelease(theirKeyLen); }
1161 if (theirPubKey) { CFRelease(theirPubKey); }
1162 if (theirKeyAttrs) { CFRelease(theirKeyAttrs); }
1163 if (sharedInfoData) { CFRelease(sharedInfoData); }
1164 if (kekLen) { CFRelease(kekLen); }
1165 if (kekParams) { CFRelease(kekParams); }
1166 if (kekData) { CFRelease(kekData); }
1167 if (error) { CFRelease(error); }
1168 if (ciphercc) { CCCryptorRelease(ciphercc); }
1169 if (cekData) { CFRelease(cekData); }
1170 if (keyType) { CFRelease(keyType); }
1171 if (cekParams) { CFRelease(cekParams); }
1172 if(outKey == NULL) {
1173 PORT_SetError(SEC_ERROR_NO_KEY);
1174 }
1175 return outKey;
1176 }