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