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