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