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