]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_smime/lib/cmspubkey.c
Security-57336.1.9.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/Security.h>
49 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
50 #include <Security/SecCmsBase.h>
51 #include <Security/secasn1t.h>
52 #include <security_asn1/plarenas.h>
53 #include <Security/keyTemplates.h>
54
55 /* ====== RSA ======================================================================= */
56
57 /*
58 * SecCmsUtilEncryptSymKeyRSA - wrap a symmetric key with RSA
59 *
60 * this function takes a symmetric key and encrypts it using an RSA public key
61 * according to PKCS#1 and RFC2633 (S/MIME)
62 */
63 OSStatus
64 SecCmsUtilEncryptSymKeyRSA(PLArenaPool *poolp, SecCertificateRef cert,
65 SecSymmetricKeyRef bulkkey,
66 CSSM_DATA_PTR encKey)
67 {
68 OSStatus rv;
69 SecPublicKeyRef publickey;
70
71 rv = SecCertificateCopyPublicKey(cert,&publickey);
72 if (publickey == NULL)
73 return SECFailure;
74
75 rv = SecCmsUtilEncryptSymKeyRSAPubKey(poolp, publickey, bulkkey, encKey);
76 CFRelease(publickey);
77 return rv;
78 }
79
80 OSStatus
81 SecCmsUtilEncryptSymKeyRSAPubKey(PLArenaPool *poolp,
82 SecPublicKeyRef publickey,
83 SecSymmetricKeyRef bulkkey, CSSM_DATA_PTR encKey)
84 {
85 OSStatus rv;
86 unsigned int data_len;
87 //KeyType keyType;
88 void *mark = NULL;
89
90 mark = PORT_ArenaMark(poolp);
91 if (!mark)
92 goto loser;
93
94 #if 0
95 /* sanity check */
96 keyType = SECKEY_GetPublicKeyType(publickey);
97 PORT_Assert(keyType == rsaKey);
98 if (keyType != rsaKey) {
99 goto loser;
100 }
101 #endif
102 /* allocate memory for the encrypted key */
103 rv = SecKeyGetStrengthInBits(publickey, NULL, &data_len);
104 if (rv)
105 goto loser;
106
107 // Convert length to bytes;
108 data_len >>= 2;
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 (mark) {
124 PORT_ArenaRelease(poolp, mark);
125 }
126 return SECFailure;
127 }
128
129 /*
130 * SecCmsUtilDecryptSymKeyRSA - unwrap a RSA-wrapped symmetric key
131 *
132 * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
133 * key handle. Please note that the actual unwrapped key data may not be allowed to leave
134 * a hardware token...
135 */
136 SecSymmetricKeyRef
137 SecCmsUtilDecryptSymKeyRSA(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECOidTag bulkalgtag)
138 {
139 /* that's easy */
140 return WRAP_PubUnwrapSymKey(privkey, encKey, bulkalgtag);
141 }
142
143 #if 0
144 // @@@ Implement Fortezza and Diffie hellman support
145
146 /* ====== MISSI (Fortezza) ========================================================== */
147
148 extern const SecAsn1Template NSS_SMIMEKEAParamTemplateAllParams[];
149
150 OSStatus
151 SecCmsUtilEncryptSymKeyMISSI(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef bulkkey,
152 SECOidTag symalgtag, CSSM_DATA_PTR encKey, CSSM_DATA_PTR *pparams, void *pwfn_arg)
153 {
154 SECOidTag certalgtag; /* the certificate's encryption algorithm */
155 SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */
156 OSStatus rv = SECFailure;
157 CSSM_DATA_PTR params = NULL;
158 OSStatus err;
159 SecSymmetricKeyRef tek;
160 SecCertificateRef ourCert;
161 SecPublicKeyRef ourPubKey, *publickey = NULL;
162 SecPrivateKeyRef ourPrivKey = NULL;
163 SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid;
164 SecCmsSMIMEKEAParameters keaParams;
165 PLArenaPool *arena = NULL;
166 extern const SecAsn1Template *nss_cms_get_kea_template(SecCmsKEATemplateSelector whichTemplate);
167 const SECAlgorithmID *algid;
168
169 /* Clear keaParams, since cleanup code checks the lengths */
170 (void) memset(&keaParams, 0, sizeof(keaParams));
171
172 SecCertificateGetAlgorithmID(cert,&algid);
173 certalgtag = SECOID_GetAlgorithmTag(algid);
174 PORT_Assert(certalgtag == SEC_OID_MISSI_KEA_DSS_OLD ||
175 certalgtag == SEC_OID_MISSI_KEA_DSS ||
176 certalgtag == SEC_OID_MISSI_KEA);
177
178 #define SMIME_FORTEZZA_RA_LENGTH 128
179 #define SMIME_FORTEZZA_IV_LENGTH 24
180 #define SMIME_FORTEZZA_MAX_KEY_SIZE 256
181
182 /* We really want to show our KEA tag as the key exchange algorithm tag. */
183 encalgtag = SEC_OID_NETSCAPE_SMIME_KEA;
184
185 /* Get the public key of the recipient. */
186 publickey = CERT_ExtractPublicKey(cert);
187 if (publickey == NULL) goto loser;
188
189 /* Find our own cert, and extract its keys. */
190 ourCert = PK11_FindBestKEAMatch(cert, pwfn_arg);
191 if (ourCert == NULL) goto loser;
192
193 arena = PORT_NewArena(1024);
194 if (arena == NULL)
195 goto loser;
196
197 ourPubKey = CERT_ExtractPublicKey(ourCert);
198 if (ourPubKey == NULL) {
199 CERT_DestroyCertificate(ourCert);
200 goto loser;
201 }
202
203 /* While we're here, copy the public key into the outgoing
204 * KEA parameters. */
205 SECITEM_CopyItem(arena, &(keaParams.originatorKEAKey), &(ourPubKey->u.fortezza.KEAKey));
206 SECKEY_DestroyPublicKey(ourPubKey);
207 ourPubKey = NULL;
208
209 /* Extract our private key in order to derive the KEA key. */
210 ourPrivKey = PK11_FindKeyByAnyCert(ourCert, pwfn_arg);
211 CERT_DestroyCertificate(ourCert); /* we're done with this */
212 if (!ourPrivKey)
213 goto loser;
214
215 /* Prepare raItem with 128 bytes (filled with zeros). */
216 keaParams.originatorRA.Data = (unsigned char *)PORT_ArenaAlloc(arena,SMIME_FORTEZZA_RA_LENGTH);
217 keaParams.originatorRA.Length = SMIME_FORTEZZA_RA_LENGTH;
218
219 /* Generate the TEK (token exchange key) which we use
220 * to wrap the bulk encryption key. (keaparams.originatorRA) will be
221 * filled with a random seed which we need to send to
222 * the recipient. (user keying material in RFC2630/DSA speak) */
223 tek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
224 &keaParams.originatorRA, NULL,
225 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
226 CKA_WRAP, 0, pwfn_arg);
227
228 SECKEY_DestroyPublicKey(publickey);
229 SECKEY_DestroyPrivateKey(ourPrivKey);
230 publickey = NULL;
231 ourPrivKey = NULL;
232
233 if (!tek)
234 goto loser;
235
236 /* allocate space for the wrapped key data */
237 encKey->Data = (unsigned char *)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
238 encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE;
239
240 if (encKey->Data == NULL) {
241 CFRelease(tek);
242 goto loser;
243 }
244
245 /* Wrap the bulk key. What we do with the resulting data
246 depends on whether we're using Skipjack to wrap the key. */
247 switch (PK11_AlgtagToMechanism(symalgtag)) {
248 case CKM_SKIPJACK_CBC64:
249 case CKM_SKIPJACK_ECB64:
250 case CKM_SKIPJACK_OFB64:
251 case CKM_SKIPJACK_CFB64:
252 case CKM_SKIPJACK_CFB32:
253 case CKM_SKIPJACK_CFB16:
254 case CKM_SKIPJACK_CFB8:
255 /* SKIPJACK, we use the wrap mechanism because we can do it on the hardware */
256 err = PK11_WrapSymKey(CKM_SKIPJACK_WRAP, NULL, tek, bulkkey, encKey);
257 whichKEA = SecCmsKEAUsesSkipjack;
258 break;
259 default:
260 /* Not SKIPJACK, we encrypt the raw key data */
261 keaParams.nonSkipjackIV.Data =
262 (unsigned char *)PORT_ArenaAlloc(arena, SMIME_FORTEZZA_IV_LENGTH);
263 keaParams.nonSkipjackIV.Length = SMIME_FORTEZZA_IV_LENGTH;
264 err = PK11_WrapSymKey(CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV, tek, bulkkey, encKey);
265 if (err != SECSuccess)
266 goto loser;
267
268 if (encKey->Length != PK11_GetKeyLength(bulkkey)) {
269 /* The size of the encrypted key is not the same as
270 that of the original bulk key, presumably due to
271 padding. Encode and store the real size of the
272 bulk key. */
273 if (SEC_ASN1EncodeInteger(arena, &keaParams.bulkKeySize, PK11_GetKeyLength(bulkkey)) == NULL)
274 err = (OSStatus)PORT_GetError();
275 else
276 /* use full template for encoding */
277 whichKEA = SecCmsKEAUsesNonSkipjackWithPaddedEncKey;
278 }
279 else
280 /* enc key length == bulk key length */
281 whichKEA = SecCmsKEAUsesNonSkipjack;
282 break;
283 }
284
285 CFRelease(tek);
286
287 if (err != SECSuccess)
288 goto loser;
289
290 PORT_Assert(whichKEA != SecCmsKEAInvalid);
291
292 /* Encode the KEA parameters into the recipient info. */
293 params = SEC_ASN1EncodeItem(poolp, NULL, &keaParams, nss_cms_get_kea_template(whichKEA));
294 if (params == NULL)
295 goto loser;
296
297 /* pass back the algorithm params */
298 *pparams = params;
299
300 rv = SECSuccess;
301
302 loser:
303 if (arena)
304 PORT_FreeArena(arena, PR_FALSE);
305 if (publickey)
306 SECKEY_DestroyPublicKey(publickey);
307 if (ourPrivKey)
308 SECKEY_DestroyPrivateKey(ourPrivKey);
309 return rv;
310 }
311
312 SecSymmetricKeyRef
313 SecCmsUtilDecryptSymKeyMISSI(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
314 {
315 /* fortezza: do a key exchange */
316 OSStatus err;
317 CK_MECHANISM_TYPE bulkType;
318 SecSymmetricKeyRef tek;
319 SecPublicKeyRef originatorPubKey;
320 SecCmsSMIMEKEAParameters keaParams;
321 SecSymmetricKeyRef bulkkey;
322 int bulkLength;
323
324 (void) memset(&keaParams, 0, sizeof(keaParams));
325
326 /* NOTE: this uses the SMIME v2 recipientinfo for compatibility.
327 All additional KEA parameters are DER-encoded in the encryption algorithm parameters */
328
329 /* Decode the KEA algorithm parameters. */
330 err = SEC_ASN1DecodeItem(NULL, &keaParams, NSS_SMIMEKEAParamTemplateAllParams,
331 &(keyEncAlg->parameters));
332 if (err != SECSuccess)
333 goto loser;
334
335 /* get originator's public key */
336 originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data,
337 keaParams.originatorKEAKey.Length);
338 if (originatorPubKey == NULL)
339 goto loser;
340
341 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
342 The Derive function generates a shared secret and combines it with the originatorRA
343 data to come up with an unique session key */
344 tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
345 &keaParams.originatorRA, NULL,
346 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
347 CKA_WRAP, 0, pwfn_arg);
348 SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */
349 if (tek == NULL)
350 goto loser;
351
352 /* Now that we have the TEK, unwrap the bulk key
353 with which to decrypt the message. We have to
354 do one of two different things depending on
355 whether Skipjack was used for *bulk* encryption
356 of the message. */
357 bulkType = PK11_AlgtagToMechanism(bulkalgtag);
358 switch (bulkType) {
359 case CKM_SKIPJACK_CBC64:
360 case CKM_SKIPJACK_ECB64:
361 case CKM_SKIPJACK_OFB64:
362 case CKM_SKIPJACK_CFB64:
363 case CKM_SKIPJACK_CFB32:
364 case CKM_SKIPJACK_CFB16:
365 case CKM_SKIPJACK_CFB8:
366 /* Skipjack is being used as the bulk encryption algorithm.*/
367 /* Unwrap the bulk key. */
368 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
369 encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
370 break;
371 default:
372 /* Skipjack was not used for bulk encryption of this
373 message. Use Skipjack CBC64, with the nonSkipjackIV
374 part of the KEA key parameters, to decrypt
375 the bulk key. If the optional parameter bulkKeySize is present,
376 bulk key size is different than the encrypted key size */
377 if (keaParams.bulkKeySize.Length > 0) {
378 err = SEC_ASN1DecodeItem(NULL, &bulkLength,
379 SEC_ASN1_GET(SEC_IntegerTemplate),
380 &keaParams.bulkKeySize);
381 if (err != SECSuccess)
382 goto loser;
383 }
384
385 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_CBC64, &keaParams.nonSkipjackIV,
386 encKey, bulkType, CKA_DECRYPT, bulkLength);
387 break;
388 }
389 return bulkkey;
390 loser:
391 return NULL;
392 }
393
394 /* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */
395
396 OSStatus
397 SecCmsUtilEncryptSymKeyESDH(PLArenaPool *poolp, SecCertificateRef cert, SecSymmetricKeyRef key,
398 CSSM_DATA_PTR encKey, CSSM_DATA_PTR ukm, SECAlgorithmID *keyEncAlg,
399 CSSM_DATA_PTR pubKey)
400 {
401 #if 0 /* not yet done */
402 SECOidTag certalgtag; /* the certificate's encryption algorithm */
403 SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */
404 OSStatus rv;
405 CSSM_DATA_PTR params = NULL;
406 int data_len;
407 OSStatus err;
408 SecSymmetricKeyRef tek;
409 SecCertificateRef ourCert;
410 SecPublicKeyRef ourPubKey;
411 SecCmsKEATemplateSelector whichKEA = SecCmsKEAInvalid;
412
413 certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
414 PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY);
415
416 /* We really want to show our KEA tag as the key exchange algorithm tag. */
417 encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN;
418
419 /* Get the public key of the recipient. */
420 publickey = CERT_ExtractPublicKey(cert);
421 if (publickey == NULL) goto loser;
422
423 /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */
424 /* XXXX */ourCert = PK11_FindBestKEAMatch(cert, wincx);
425 if (ourCert == NULL) goto loser;
426
427 arena = PORT_NewArena(1024);
428 if (arena == NULL) goto loser;
429
430 /* While we're here, extract the key pair's public key data and copy it into */
431 /* the outgoing parameters. */
432 /* XXXX */ourPubKey = CERT_ExtractPublicKey(ourCert);
433 if (ourPubKey == NULL)
434 {
435 goto loser;
436 }
437 SECITEM_CopyItem(arena, pubKey, /* XXX */&(ourPubKey->u.fortezza.KEAKey));
438 SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */
439 ourPubKey = NULL;
440
441 /* Extract our private key in order to derive the KEA key. */
442 ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx);
443 CERT_DestroyCertificate(ourCert); /* we're done with this */
444 if (!ourPrivKey) goto loser;
445
446 /* If ukm desired, prepare it - allocate enough space (filled with zeros). */
447 if (ukm) {
448 ukm->Data = (unsigned char*)PORT_ArenaZAlloc(arena,/* XXXX */);
449 ukm->Length = /* XXXX */;
450 }
451
452 /* Generate the KEK (key exchange key) according to RFC2631 which we use
453 * to wrap the bulk encryption key. */
454 kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
455 ukm, NULL,
456 /* XXXX */CKM_KEA_KEY_DERIVE, /* XXXX */CKM_SKIPJACK_WRAP,
457 CKA_WRAP, 0, wincx);
458
459 SECKEY_DestroyPublicKey(publickey);
460 SECKEY_DestroyPrivateKey(ourPrivKey);
461 publickey = NULL;
462 ourPrivKey = NULL;
463
464 if (!kek)
465 goto loser;
466
467 /* allocate space for the encrypted CEK (bulk key) */
468 encKey->Data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
469 encKey->Length = SMIME_FORTEZZA_MAX_KEY_SIZE;
470
471 if (encKey->Data == NULL)
472 {
473 CFRelease(kek);
474 goto loser;
475 }
476
477
478 /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */
479 /* bulk encryption algorithm */
480 switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo->encalg))
481 {
482 case /* XXXX */CKM_SKIPJACK_CFB8:
483 err = PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey);
484 whichKEA = SecCmsKEAUsesSkipjack;
485 break;
486 case /* XXXX */CKM_SKIPJACK_CFB8:
487 err = PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey);
488 whichKEA = SecCmsKEAUsesSkipjack;
489 break;
490 default:
491 /* XXXX what do we do here? Neither RC2 nor 3DES... */
492 err = SECFailure;
493 /* set error */
494 break;
495 }
496
497 CFRelease(kek); /* we do not need the KEK anymore */
498 if (err != SECSuccess)
499 goto loser;
500
501 PORT_Assert(whichKEA != SecCmsKEAInvalid);
502
503 /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */
504 /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */
505 params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA));
506 if (params == NULL)
507 goto loser;
508
509 /* now set keyEncAlg */
510 rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params);
511 if (rv != SECSuccess)
512 goto loser;
513
514 /* XXXXXXX this is not right yet */
515 loser:
516 if (arena) {
517 PORT_FreeArena(arena, PR_FALSE);
518 }
519 if (publickey) {
520 SECKEY_DestroyPublicKey(publickey);
521 }
522 if (ourPrivKey) {
523 SECKEY_DestroyPrivateKey(ourPrivKey);
524 }
525 #endif
526 return SECFailure;
527 }
528
529 SecSymmetricKeyRef
530 SecCmsUtilDecryptSymKeyESDH(SecPrivateKeyRef privkey, CSSM_DATA_PTR encKey, SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag, void *pwfn_arg)
531 {
532 #if 0 /* not yet done */
533 OSStatus err;
534 CK_MECHANISM_TYPE bulkType;
535 SecSymmetricKeyRef tek;
536 SecPublicKeyRef originatorPubKey;
537 SecCmsSMIMEKEAParameters keaParams;
538
539 /* XXXX get originator's public key */
540 originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.Data,
541 keaParams.originatorKEAKey.Length);
542 if (originatorPubKey == NULL)
543 goto loser;
544
545 /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
546 The Derive function generates a shared secret and combines it with the originatorRA
547 data to come up with an unique session key */
548 tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
549 &keaParams.originatorRA, NULL,
550 CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
551 CKA_WRAP, 0, pwfn_arg);
552 SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */
553 if (tek == NULL)
554 goto loser;
555
556 /* Now that we have the TEK, unwrap the bulk key
557 with which to decrypt the message. */
558 /* Skipjack is being used as the bulk encryption algorithm.*/
559 /* Unwrap the bulk key. */
560 bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
561 encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
562
563 return bulkkey;
564
565 loser:
566 #endif
567 return NULL;
568 }
569
570 #endif /* Fortezza, DIffie-Hellman */
571
572 #define CFRELEASE(cf) if(cf != NULL) { CFRelease(cf); }
573
574 /* ====== ECDH (Ephemeral-Static Diffie-Hellman) ==================================== */
575
576 #pragma mark ---- ECDH support functions ----
577
578 #ifdef NDEBUG
579 #define CSSM_PERROR(f, r)
580 #define dprintf(args...)
581 #else
582 #define CSSM_PERROR(f, r) cssmPerror(f, r)
583 #define dprintf(args...) fprintf(stderr, args)
584 #endif
585
586 /* Length of KeyAgreeRecipientInfo.ukm we create */
587 #define UKM_LENGTH 8
588
589 /* KEK algorithm info we generate */
590 #define ECDH_KEK_ALG_TAG SEC_OID_DES_EDE3_CBC
591 #define ECDH_KEK_KEY_CSSM_ALGID CSSM_ALGID_3DES_3KEY
592 #define ECDH_KEK_ENCR_CSSM_ALGID CSSM_ALGID_3DES_3KEY_EDE
593 #define ECDH_KEK_KEY_LEN_BYTES 24
594 #define ECDH_KEK_IV_LEN_BYTES 8
595
596 #define CMS_DUMP_BUFS 0
597 #if CMS_DUMP_BUFS
598
599 static void dumpBuf(
600 const char *label,
601 const CSSM_DATA *cd)
602 {
603 unsigned dex;
604
605 printf("%s:\n ", label);
606 for(dex=0; dex<cd->Length; dex++) {
607 printf("%02X ", cd->Data[dex]);
608 if(((dex % 16) == 15) && (dex != (cd->Length - 1))) {
609 printf("\n ");
610 }
611 }
612 putchar('\n');
613 }
614
615 #else
616 #define dumpBuf(l, d)
617 #endif /* CMS_DUMP_BUFS */
618
619 /*
620 * The ECC-CMS-SharedInfo struct, as defined in RFC 3278 8.2, and the
621 * template for DER encoding and decoding it.
622 */
623 typedef struct {
624 SECAlgorithmID algId; /* KEK alg, NULL params */
625 CSSM_DATA entityUInfo; /* optional, ukm */
626 CSSM_DATA suppPubInfo; /* length of KEK in bits as 4-byte integer */
627 } ECC_CMS_SharedInfo;
628
629 static const SecAsn1Template ECC_CMS_SharedInfoTemplate[] = {
630 { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(ECC_CMS_SharedInfo) },
631 { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 0,
632 offsetof(ECC_CMS_SharedInfo,entityUInfo),
633 kSecAsn1OctetStringTemplate },
634 { SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT | SEC_ASN1_CONTEXT_SPECIFIC | 2,
635 offsetof(ECC_CMS_SharedInfo,suppPubInfo),
636 kSecAsn1OctetStringTemplate },
637 { 0 }
638 };
639
640 /*
641 * Given a context specified via a CSSM_CC_HANDLE, add a new
642 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
643 * AttributeLength, and an untyped pointer.
644 */
645 /* specify either 32-bit integer or a pointer as an added attribute value */
646 typedef enum {
647 CAT_Uint32,
648 CAT_Ptr
649 } ContextAttrType;
650
651 static CSSM_RETURN cmsAddContextAttribute(
652 CSSM_CC_HANDLE CCHandle,
653 uint32 AttributeType,
654 uint32 AttributeLength,
655 ContextAttrType attrType,
656 /* specify exactly one of these */
657 const void *AttributePtr,
658 uint32 attributeInt)
659 {
660 CSSM_CONTEXT_ATTRIBUTE newAttr;
661 CSSM_RETURN crtn;
662
663 newAttr.AttributeType = AttributeType;
664 newAttr.AttributeLength = AttributeLength;
665 if(attrType == CAT_Uint32) {
666 newAttr.Attribute.Uint32 = attributeInt;
667 }
668 else {
669 /* this is a union of a bunch of different pointers...*/
670 newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr;
671 }
672 crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
673 if(crtn) {
674 CSSM_PERROR("CSSM_UpdateContextAttributes", crtn);
675 }
676 return crtn;
677 }
678
679 static CSSM_RETURN cmsGenRand(
680 CSSM_CSP_HANDLE cspHand,
681 CSSM_SIZE len,
682 uint8 *randOut)
683 {
684 CSSM_CC_HANDLE ccHand = 0;
685 CSSM_DATA randData = {len, randOut};
686
687 CSSM_RETURN crtn = CSSM_CSP_CreateRandomGenContext(cspHand,
688 CSSM_ALGID_APPLE_YARROW,
689 NULL, /* seed*/
690 len,
691 &ccHand);
692 if(crtn) {
693 CSSM_PERROR("CSSM_CSP_CreateRandomGenContext", crtn);
694 return crtn;
695 }
696 crtn = CSSM_GenerateRandom(ccHand, &randData);
697 CSSM_DeleteContext(ccHand);
698 if(crtn) {
699 CSSM_PERROR("CSSM_GenerateRandom", crtn);
700 }
701 return crtn;
702 }
703
704 /* convert uint32 to big-endian 4 bytes */
705 static void int32ToBytes(
706 uint32_t i,
707 unsigned char *b)
708 {
709 int dex;
710 for(dex=3; dex>=0; dex--) {
711 b[dex] = i;
712 i >>= 8;
713 }
714 }
715
716 /*
717 * NULL wrap a ref key to raw key in default format.
718 */
719 static OSStatus cmsNullWrapKey(
720 CSSM_CSP_HANDLE cspHand,
721 const CSSM_KEY *refKey,
722 CSSM_KEY_PTR rawKey)
723 {
724 CSSM_DATA descData = {0, 0};
725 CSSM_RETURN crtn;
726 CSSM_CC_HANDLE ccHand;
727 CSSM_ACCESS_CREDENTIALS creds;
728 uint32 keyAttr;
729
730 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
731 memset(rawKey, 0, sizeof(CSSM_KEY));
732
733 crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
734 CSSM_ALGID_NONE,
735 CSSM_ALGMODE_NONE,
736 &creds,
737 NULL, // unwrappingKey
738 NULL, // initVector
739 CSSM_PADDING_NONE,
740 0, // Params
741 &ccHand);
742 if(crtn) {
743 CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", crtn);
744 return crtn;
745 }
746
747 keyAttr = rawKey->KeyHeader.KeyAttr;
748 keyAttr &= ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE |
749 CSSM_KEYATTR_MODIFIABLE);
750 keyAttr |= CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE;
751 crtn = CSSM_WrapKey(ccHand,
752 &creds,
753 refKey,
754 &descData,
755 rawKey);
756 if(crtn != CSSM_OK) {
757 CSSM_PERROR("CSSM_WrapKey", crtn);
758 }
759 CSSM_DeleteContext(ccHand);
760 return crtn;
761 }
762
763 /*
764 * Free memory via specified plugin's app-level allocator
765 */
766 static void cmsFreeCssmMemory(
767 CSSM_HANDLE hand,
768 void *p)
769 {
770 CSSM_API_MEMORY_FUNCS memFuncs;
771 CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
772 if(crtn) {
773 return;
774 }
775 memFuncs.free_func(p, memFuncs.AllocRef);
776 }
777
778 /*
779 * Given an OID tag, return key size and mode.
780 * NOTE: ciphers with variable key sizes, like RC2, RC4, and RC5 cannot
781 * be used here because the message does not contain a key size
782 * indication.
783 */
784 static OSStatus encrAlgInfo(
785 SECOidTag oidTag,
786 uint32 *keySizeBits, /* RETURNED */
787 CSSM_ENCRYPT_MODE *mode) /* RETURNED */
788 {
789 *keySizeBits = 64; /* default */
790 *mode = CSSM_ALGMODE_CBCPadIV8; /* default */
791
792 switch(oidTag) {
793 case SEC_OID_RC2_CBC:
794 case SEC_OID_RC4:
795 case SEC_OID_RC5_CBC_PAD:
796 dprintf("encrAlgInfo: key size unknowable\n");
797 return errSecDataNotAvailable;
798
799 case SEC_OID_DES_EDE3_CBC:
800 *keySizeBits = 192;
801 break;
802 case SEC_OID_DES_EDE:
803 /* Not sure about this; SecCmsCipherContextStart() treats this
804 * like SEC_OID_DES_EDE3_CBC... */
805 case SEC_OID_DES_ECB:
806 *mode = CSSM_ALGMODE_ECB;
807 break;
808 case SEC_OID_DES_CBC:
809 *mode = CSSM_ALGMODE_CBC;
810 break;
811 case SEC_OID_AES_128_CBC:
812 *keySizeBits = 128;
813 break;
814 case SEC_OID_AES_192_CBC:
815 *keySizeBits = 192;
816 break;
817 case SEC_OID_AES_256_CBC:
818 *keySizeBits = 256;
819 break;
820 case SEC_OID_AES_128_ECB:
821 *keySizeBits = 128;
822 *mode = CSSM_ALGMODE_ECB;
823 break;
824 case SEC_OID_AES_192_ECB:
825 *keySizeBits = 192;
826 *mode = CSSM_ALGMODE_ECB;
827 break;
828 case SEC_OID_AES_256_ECB:
829 *keySizeBits = 256;
830 *mode = CSSM_ALGMODE_ECB;
831 break;
832 case SEC_OID_DES_OFB:
833 *mode = CSSM_ALGMODE_OFB;
834 break;
835 case SEC_OID_DES_CFB:
836 *mode = CSSM_ALGMODE_CFB;
837 break;
838 default:
839 dprintf("encrAlgInfo: unknown alg tag (%d)\n", (int)oidTag);
840 return errSecDataNotAvailable;
841 }
842 return noErr;
843 }
844
845 #pragma mark ---- ECDH CEK key wrap ----
846
847 /*
848 * Encrypt bulk encryption key (a.k.a. content encryption key, CEK) using ECDH
849 */
850 OSStatus
851 SecCmsUtilEncryptSymKeyECDH(
852 PLArenaPool *poolp,
853 SecCertificateRef cert, /* recipient's cert */
854 SecSymmetricKeyRef key, /* bulk key */
855 /* remaining fields RETURNED */
856 CSSM_DATA_PTR encKey, /* encrypted key --> recipientEncryptedKeys[0].EncryptedKey */
857 CSSM_DATA_PTR ukm, /* random UKM --> KeyAgreeRecipientInfo.ukm */
858 SECAlgorithmID *keyEncAlg, /* alg := dhSinglePass-stdDH-sha1kdf-scheme
859 * params := another encoded AlgId, with the KEK alg and IV */
860 CSSM_DATA_PTR pubKey) /* our pub key as ECPoint -->
861 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
862 {
863 OSStatus rv = noErr;
864 CSSM_KEY ourPrivKeyCssm;
865 CSSM_KEY ourPubKeyCssm;
866 SecKeyRef theirPubKeyRef = NULL;
867 CSSM_KEY_PTR theirPubKeyCssm = NULL;
868 const CSSM_KEY *cekCssmRef = NULL;
869 uint32 ecdhKeySizeBits;
870 CSSM_CSP_HANDLE rawCspHand = SecCspHandleForAlgorithm(CSSM_ALGID_ECDH);
871 CSSM_CC_HANDLE ccHand = 0;
872 CSSM_RETURN crtn;
873 CSSM_DATA keyLabel = {8, (uint8 *)"tempKey"};
874 SECAlgorithmID kekAlgId;
875 uint8 iv[ECDH_KEK_IV_LEN_BYTES];
876 CSSM_DATA ivData = {ECDH_KEK_IV_LEN_BYTES, iv};
877 SECOidData *kekOid;
878 ECC_CMS_SharedInfo sharedInfo;
879 CSSM_DATA sharedInfoEnc = {0, NULL};
880 uint8 nullData[2] = {SEC_ASN1_NULL, 0};
881 uint8 keyLenAsBytes[4];
882 CSSM_KEY kekDerive;
883 CSSM_DATA certData;
884 CSSM_CL_HANDLE clHand;
885 CSSM_ACCESS_CREDENTIALS creds;
886 CSSM_DATA paramData = {0, NULL};
887 CSSM_KEY cekCssm;
888 CSSM_CSP_HANDLE refCspHand;
889 CSSM_SIZE bytesEncrypted;
890 CSSM_DATA remData = {0, NULL};
891 CSSM_DATA ctext = {0, NULL};
892 CSSM_X509_SUBJECT_PUBLIC_KEY_INFO subjPubKey;
893
894 if(rawCspHand == 0) {
895 return internalComponentErr;
896 }
897
898 memset(&ourPrivKeyCssm, 0, sizeof(CSSM_KEY));
899 memset(&ourPubKeyCssm, 0, sizeof(CSSM_KEY));
900 memset(&cekCssm, 0, sizeof(CSSM_KEY));
901 memset(&kekDerive, 0, sizeof(kekDerive));
902
903 encKey->Data = NULL;
904 encKey->Length = 0;
905
906 /*
907 * Create our ECDH key pair matching the recipient's key.
908 * Get the public key in "read-only" OCTET_STRING format, which
909 * is the ECPoint we put in
910 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey.
911 */
912 rv = SecCertificateGetData(cert, &certData);
913 if(rv) {
914 CSSM_PERROR("SecCertificateGetData", rv);
915 return rv;
916 }
917 rv = SecCertificateGetCLHandle(cert, &clHand);
918 if(rv) {
919 CSSM_PERROR("SecCertificateGetCLHandle", rv);
920 return rv;
921 }
922 rv = CSSM_CL_CertGetKeyInfo(clHand, &certData, &theirPubKeyCssm);
923 if(rv) {
924 CSSM_PERROR("CSSM_CL_CertGetKeyInfo", rv);
925 return rv;
926 }
927
928 /*
929 * Verify the EC curve of the recipient's public key. It's in the
930 * public key's AlgId.parameters as an OID. The key we were
931 * given is in CSSM_X509_SUBJECT_PUBLIC_KEY_INFO form.
932 */
933 memset(&subjPubKey, 0, sizeof(subjPubKey));
934 if(SEC_ASN1DecodeItem(poolp, &subjPubKey, kSecAsn1SubjectPublicKeyInfoTemplate,
935 &theirPubKeyCssm->KeyData)) {
936 dprintf("SecCmsUtilEncryptSymKeyECDH: error decoding SubjPubKey\n");
937 /* oh well, keep going */
938 }
939 else {
940 if(subjPubKey.algorithm.parameters.Data != NULL) {
941 CSSM_DATA curveOid;
942 if(SEC_ASN1DecodeItem(poolp, &curveOid, kSecAsn1ObjectIDTemplate,
943 &subjPubKey.algorithm.parameters)) {
944 dprintf("SecCmsUtilEncryptSymKeyECDH: error decoding curveOid\n");
945 /* oh well, keep going */
946 }
947 else {
948 /* We have the curve OID. Any other errors are fatal. */
949 SECOidTag oidTag = SECOID_FindOIDTag(&curveOid);
950 switch(oidTag) {
951 case SEC_OID_SECP_256_R1:
952 case SEC_OID_SECP_384_R1:
953 case SEC_OID_SECP_521_R1:
954 break;
955 default:
956 dprintf("SecCmsUtilEncryptSymKeyECDH: unsupported curveOid\n");
957 rv = CSSMERR_CSP_INVALID_KEY;
958 goto loser;
959 }
960 }
961 }
962 }
963
964 ecdhKeySizeBits = theirPubKeyCssm->KeyHeader.LogicalKeySizeInBits;
965 crtn = CSSM_CSP_CreateKeyGenContext(rawCspHand,
966 CSSM_ALGID_ECDSA,
967 ecdhKeySizeBits,
968 NULL, // Seed
969 NULL, // Salt
970 NULL, // StartDate
971 NULL, // EndDate
972 NULL, // Params
973 &ccHand);
974 if(crtn) {
975 CSSM_PERROR("CSSM_CSP_CreateKeyGenContext", crtn);
976 rv = crtn;
977 goto loser;
978 }
979 crtn = cmsAddContextAttribute(ccHand,
980 CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT,
981 sizeof(uint32),
982 CAT_Uint32,
983 NULL,
984 CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING);
985 if(crtn) {
986 CSSM_PERROR("AddContextAttribute(CSSM_ATTRIBUTE_PUBLIC_KEY_FORMAT)", crtn);
987 rv = crtn;
988 goto loser;
989 }
990
991 crtn = CSSM_GenerateKeyPair(ccHand,
992 CSSM_KEYUSE_DERIVE,
993 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
994 &keyLabel,
995 &ourPubKeyCssm,
996 CSSM_KEYUSE_DERIVE,
997 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE,
998 &keyLabel,
999 NULL, // CredAndAclEntry
1000 &ourPrivKeyCssm);
1001 CSSM_DeleteContext(ccHand);
1002 ccHand = 0;
1003 if(crtn) {
1004 CSSM_PERROR("CSSM_GenerateKeyPair", crtn);
1005 rv = crtn;
1006 goto loser;
1007 }
1008 pubKey->Length = ourPubKeyCssm.KeyData.Length;
1009 pubKey->Data = (uint8 *)PORT_ArenaAlloc(poolp, pubKey->Length);
1010 memmove(pubKey->Data, ourPubKeyCssm.KeyData.Data, pubKey->Length);
1011 dumpBuf("sender's public key", pubKey);
1012
1013 /*
1014 * Cook up random UKM
1015 */
1016 ukm->Data = (uint8 *)PORT_ArenaAlloc(poolp, UKM_LENGTH);
1017 ukm->Length = UKM_LENGTH;
1018 crtn = cmsGenRand(rawCspHand, UKM_LENGTH, ukm->Data);
1019 if(crtn) {
1020 goto loser;
1021 }
1022 dumpBuf("sender UKM", ukm);
1023
1024 /*
1025 * OK, we have to set up a weird SECAlgorithmID.
1026 * algorithm = dhSinglePass-stdDH-sha1kdf-scheme
1027 * params = an encoded SECAlgorithmID representing the KEK algorithm, with
1028 * algorithm = whatever we pick
1029 * parameters = IV as octet string (though I haven't seen that specified
1030 * anywhere; it's how the CEK IV is encoded)
1031 *
1032 * First, the 8-byte random IV, encoded as octet string
1033 */
1034 crtn = cmsGenRand(rawCspHand, ECDH_KEK_IV_LEN_BYTES, iv);
1035 if(crtn) {
1036 goto loser;
1037 }
1038 dumpBuf("sender IV", &ivData);
1039
1040 memset(&kekAlgId, 0, sizeof(kekAlgId));
1041 if (!SEC_ASN1EncodeItem(poolp, &kekAlgId.parameters,
1042 &ivData, kSecAsn1OctetStringTemplate)) {
1043 rv = internalComponentErr;
1044 goto loser;
1045 }
1046
1047 /* Drop in the KEK OID and encode the whole thing */
1048 kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG);
1049 if(kekOid == NULL) {
1050 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
1051 rv = internalComponentErr;
1052 goto loser;
1053 }
1054 kekAlgId.algorithm = kekOid->oid;
1055 memset(keyEncAlg, 0, sizeof(*keyEncAlg));
1056 if (!SEC_ASN1EncodeItem(poolp, &keyEncAlg->parameters,
1057 &kekAlgId, SECOID_AlgorithmIDTemplate)) {
1058 rv = internalComponentErr;
1059 goto loser;
1060 }
1061 kekOid = SECOID_FindOIDByTag(SEC_OID_DH_SINGLE_STD_SHA1KDF);
1062 if(kekOid == NULL) {
1063 dprintf("SecCmsUtilEncryptSymKeyECDH: OID screwup\n");
1064 rv = internalComponentErr;
1065 goto loser;
1066 }
1067 keyEncAlg->algorithm = kekOid->oid;
1068
1069 /*
1070 * Now in order to derive the KEK proper, we have to create a
1071 * ECC-CMS-SharedInfo, which does not appear in the message, and DER
1072 * encode that struct, the result of which is used as the
1073 * SharedInfo value in the KEK key derive.
1074 */
1075 memset(&sharedInfo, 0, sizeof(sharedInfo));
1076 kekOid = SECOID_FindOIDByTag(ECDH_KEK_ALG_TAG);
1077 sharedInfo.algId.algorithm = kekOid->oid;
1078 sharedInfo.algId.parameters.Data = nullData;
1079 sharedInfo.algId.parameters.Length = 2;
1080 sharedInfo.entityUInfo = *ukm;
1081 int32ToBytes(ECDH_KEK_KEY_LEN_BYTES << 3, keyLenAsBytes);
1082 sharedInfo.suppPubInfo.Length = 4;
1083 sharedInfo.suppPubInfo.Data = keyLenAsBytes;
1084 if (!SEC_ASN1EncodeItem(poolp, &sharedInfoEnc,
1085 &sharedInfo, ECC_CMS_SharedInfoTemplate)) {
1086 rv = internalComponentErr;
1087 goto loser;
1088 }
1089 dumpBuf("sender encoded SharedInfo", &sharedInfoEnc);
1090
1091 /*
1092 * Since we're using the raw CSP here, we can provide the "other" public
1093 * key as an actual CSSM_KEY. When unwrapping, we won't be able to do that
1094 * since we'll be using our private key obtained from a SecIdentityRef.
1095 */
1096 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
1097 crtn = CSSM_CSP_CreateDeriveKeyContext(rawCspHand,
1098 CSSM_ALGID_ECDH_X963_KDF,
1099 ECDH_KEK_KEY_CSSM_ALGID, // algorithm of the KEK
1100 ECDH_KEK_KEY_LEN_BYTES * 8,
1101 &creds,
1102 &ourPrivKeyCssm, // BaseKey
1103 0, // IterationCount
1104 &sharedInfoEnc, // Salt
1105 0, // Seed
1106 &ccHand);
1107 if(crtn) {
1108 CSSM_PERROR("CSSM_CSP_CreateDeriveKeyContext", crtn);
1109 rv = crtn;
1110 goto loser;
1111 }
1112
1113 /* add recipient's pub key as a context attr */
1114 crtn = cmsAddContextAttribute(ccHand,
1115 CSSM_ATTRIBUTE_PUBLIC_KEY,
1116 sizeof(CSSM_KEY),
1117 CAT_Ptr,
1118 (void *)theirPubKeyCssm,
1119 0);
1120 if(crtn) {
1121 rv = crtn;
1122 goto loser;
1123 }
1124
1125 /* Derive the KEK */
1126 crtn = CSSM_DeriveKey(ccHand,
1127 &paramData,
1128 CSSM_KEYUSE_ANY,
1129 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE,
1130 &keyLabel,
1131 NULL, // cread/acl
1132 &kekDerive);
1133 if(crtn) {
1134 CSSM_PERROR("CSSM_DeriveKey", crtn);
1135 rv = crtn;
1136 goto loser;
1137 }
1138 CSSM_DeleteContext(ccHand);
1139 ccHand = 0;
1140
1141 /*
1142 * Obtain the raw CEK bits.
1143 */
1144 rv = SecKeyGetCSSMKey(key, &cekCssmRef);
1145 if(rv) {
1146 CSSM_PERROR("SecKeyGetCSSMKey", rv);
1147 goto loser;
1148 }
1149 rv = SecKeyGetCSPHandle(key, &refCspHand);
1150 if(rv) {
1151 CSSM_PERROR("SecKeyGetCSPHandle", rv);
1152 goto loser;
1153 }
1154 rv = cmsNullWrapKey(refCspHand, cekCssmRef, &cekCssm);
1155 if(rv) {
1156 goto loser;
1157 }
1158
1159 /*
1160 * Finally, encrypt the raw CEK bits with the KEK we just derived
1161 */
1162 crtn = CSSM_CSP_CreateSymmetricContext(rawCspHand,
1163 ECDH_KEK_ENCR_CSSM_ALGID,
1164 CSSM_ALGMODE_CBCPadIV8,
1165 NULL, // access cred
1166 &kekDerive,
1167 &ivData, // InitVector
1168 CSSM_PADDING_PKCS7,
1169 NULL, // Params
1170 &ccHand);
1171 if(rv) {
1172 CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", rv);
1173 goto loser;
1174 }
1175 rv = CSSM_EncryptData(ccHand,
1176 &cekCssm.KeyData,
1177 1,
1178 &ctext,
1179 1,
1180 &bytesEncrypted,
1181 &remData);
1182 if(rv) {
1183 CSSM_PERROR("CSSM_EncryptData", rv);
1184 goto loser;
1185 }
1186 encKey->Data = PORT_ArenaAlloc(poolp, bytesEncrypted);
1187 encKey->Length = bytesEncrypted;
1188 memmove(encKey->Data, ctext.Data, ctext.Length);
1189 if(bytesEncrypted != ctext.Length) {
1190 memmove(encKey->Data + ctext.Length, remData.Data, remData.Length);
1191 }
1192 dumpBuf("sender encKey", encKey);
1193
1194 loser:
1195 if(ccHand) {
1196 CSSM_DeleteContext(ccHand);
1197 }
1198 CFRELEASE(theirPubKeyRef);
1199 if(ourPubKeyCssm.KeyData.Data) {
1200 CSSM_FreeKey(rawCspHand, NULL, &ourPubKeyCssm, CSSM_FALSE);
1201 }
1202 if(ourPrivKeyCssm.KeyData.Data) {
1203 CSSM_FreeKey(rawCspHand, NULL, &ourPrivKeyCssm, CSSM_FALSE);
1204 }
1205 if(ctext.Data) {
1206 cmsFreeCssmMemory(rawCspHand, ctext.Data);
1207 }
1208 if(remData.Data) {
1209 cmsFreeCssmMemory(rawCspHand, remData.Data);
1210 }
1211 if(cekCssm.KeyData.Data) {
1212 CSSM_FreeKey(refCspHand, NULL, &cekCssm, CSSM_FALSE);
1213 }
1214 if(kekDerive.KeyData.Data) {
1215 CSSM_FreeKey(rawCspHand, NULL, &kekDerive, CSSM_FALSE);
1216 }
1217 if(theirPubKeyCssm) {
1218 /* Allocated by CL */
1219 cmsFreeCssmMemory(clHand, theirPubKeyCssm->KeyData.Data);
1220 cmsFreeCssmMemory(clHand, theirPubKeyCssm);
1221 }
1222 return rv;
1223 }
1224
1225 #pragma mark ---- ECDH CEK key unwrap ----
1226
1227 SecSymmetricKeyRef
1228 SecCmsUtilDecryptSymKeyECDH(
1229 SecPrivateKeyRef privkey, /* our private key */
1230 CSSM_DATA_PTR encKey, /* encrypted CEK */
1231 CSSM_DATA_PTR ukm, /* random UKM from KeyAgreeRecipientInfo.ukm */
1232 SECAlgorithmID *keyEncAlg, /* alg := dhSinglePass-stdDH-sha1kdf-scheme
1233 * params := another encoded AlgId, with the KEK alg and IV */
1234 SECOidTag bulkalgtag, /* algorithm of returned key */
1235 CSSM_DATA_PTR pubKey) /* sender's pub key as ECPoint from
1236 * KeyAgreeRecipientInfo.originator.OriginatorPublicKey */
1237
1238 {
1239 SecSymmetricKeyRef outKey = NULL;
1240 OSStatus rv = noErr;
1241 const CSSM_KEY *ourPrivKeyCssm;
1242 PLArenaPool *pool = NULL;
1243 SECAlgorithmID keyAlgParam;
1244 SECOidData *kekOid = NULL;
1245 CSSM_DATA iv = {0, NULL};
1246 ECC_CMS_SharedInfo sharedInfo;
1247 CSSM_DATA sharedInfoEnc = {0, NULL};
1248 uint8 nullData[2] = {SEC_ASN1_NULL, 0};
1249 uint8 keyLenAsBytes[4];
1250 CSSM_ENCRYPT_MODE kekMode;
1251 uint32 kekSizeBits;
1252 CSSM_KEY kekDerive;
1253 CSSM_RETURN crtn;
1254 CSSM_ACCESS_CREDENTIALS creds;
1255 CSSM_CSP_HANDLE refCspHand;
1256 CSSM_CC_HANDLE ccHand = 0;
1257 CSSM_DATA keyLabel = {8, (uint8 *)"tempKey"};
1258 const CSSM_ACCESS_CREDENTIALS *accessCred;
1259 CSSM_KEY wrappedKey;
1260 CSSM_KEY unwrappedKey;
1261 CSSM_ALGORITHMS bulkAlg;
1262 CSSM_DATA descriptiveData = {0, NULL};
1263
1264 dumpBuf("receiver encKey", encKey);
1265
1266 memset(&kekDerive, 0, sizeof(kekDerive));
1267
1268 /* our private key in CSSM form */
1269 rv = SecKeyGetCSSMKey(privkey, &ourPrivKeyCssm);
1270 if(rv) {
1271 CSSM_PERROR("SecKeyGetCSSMKey", rv);
1272 goto loser;
1273 }
1274
1275 /*
1276 * Decode keyEncAlg.params to get KEK algorithm and IV
1277 */
1278 pool = PORT_NewArena(1024);
1279 if(pool == NULL) {
1280 goto loser;
1281 }
1282 memset(&keyAlgParam, 0, sizeof(keyAlgParam));
1283 if(SEC_ASN1DecodeItem(pool, &keyAlgParam, SECOID_AlgorithmIDTemplate,
1284 &keyEncAlg->parameters)) {
1285 dprintf("SecCmsUtilDecryptSymKeyECDH: error decoding keyAlgParams\n");
1286 goto loser;
1287 }
1288 kekOid = SECOID_FindOID(&keyAlgParam.algorithm);
1289 if(kekOid == NULL) {
1290 dprintf("SecCmsUtilDecryptSymKeyECDH: unknown KEK enc OID\n");
1291 goto loser;
1292 }
1293 rv = encrAlgInfo(kekOid->offset, &kekSizeBits, &kekMode);
1294 if(rv) {
1295 goto loser;
1296 }
1297 /* IV is OCTET STRING in the alg params */
1298 if(SEC_ASN1DecodeItem(pool, &iv, kSecAsn1OctetStringTemplate,
1299 &keyAlgParam.parameters)) {
1300 /*
1301 * Not sure here - is it legal to have no IV? I haven't seen this
1302 * addressed in any spec. Maybe we should condition the behavior
1303 * here on the KEK algorithm.
1304 */
1305 dprintf("SecCmsUtilDecryptSymKeyECDH: no KEK IV\n");
1306 goto loser;
1307 }
1308
1309 /*
1310 * Now in order to derive the KEK proper, we have to create a
1311 * ECC-CMS-SharedInfo, which does not appear in the message, and DER
1312 * encode that struct, the result of which is used as the
1313 * SharedInfo value in the KEK key derive.
1314 */
1315 memset(&sharedInfo, 0, sizeof(sharedInfo));
1316 sharedInfo.algId.algorithm = kekOid->oid;
1317 sharedInfo.algId.parameters.Data = nullData;
1318 sharedInfo.algId.parameters.Length = 2;
1319 sharedInfo.entityUInfo = *ukm;
1320 int32ToBytes(kekSizeBits, keyLenAsBytes);
1321 sharedInfo.suppPubInfo.Length = 4;
1322 sharedInfo.suppPubInfo.Data = keyLenAsBytes;
1323 if (!SEC_ASN1EncodeItem(pool, &sharedInfoEnc,
1324 &sharedInfo, ECC_CMS_SharedInfoTemplate)) {
1325 rv = internalComponentErr;
1326 goto loser;
1327 }
1328 dumpBuf("receiver encoded SharedInfo", &sharedInfoEnc);
1329 dumpBuf("receiver IV", &iv);
1330 dumpBuf("receiver UKM", ukm);
1331 dumpBuf("sender's public key", pubKey);
1332
1333 /*
1334 * Using the Sec-layer CSPDL, "other's" public key specified as ECPOint param. Which
1335 * is fortunate because that's what we have...
1336 */
1337 memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
1338 rv = SecKeyGetCSPHandle(privkey, &refCspHand);
1339 if(rv) {
1340 CSSM_PERROR("SecKeyGetCSPHandle", rv);
1341 goto loser;
1342 }
1343 rv = SecKeyGetCredentials(privkey,
1344 CSSM_ACL_AUTHORIZATION_DERIVE,
1345 kSecCredentialTypeDefault,
1346 &accessCred);
1347 if (rv) {
1348 CSSM_PERROR("SecKeyGetCredentials", rv);
1349 goto loser;
1350 }
1351 crtn = CSSM_CSP_CreateDeriveKeyContext(refCspHand,
1352 CSSM_ALGID_ECDH_X963_KDF,
1353 kekOid->cssmAlgorithm, // algorithm of the KEK
1354 kekSizeBits,
1355 &creds,
1356 ourPrivKeyCssm, // BaseKey
1357 0, // IterationCount
1358 &sharedInfoEnc, // Salt
1359 0, // Seed
1360 &ccHand);
1361 if(crtn) {
1362 CSSM_PERROR("CSSM_CSP_CreateDeriveKeyContext", crtn);
1363 goto loser;
1364 }
1365 crtn = CSSM_DeriveKey(ccHand,
1366 pubKey, // param
1367 CSSM_KEYUSE_ANY,
1368 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_EXTRACTABLE,
1369 &keyLabel,
1370 NULL, // cred/acl
1371 &kekDerive);
1372 CSSM_DeleteContext(ccHand);
1373 ccHand = 0;
1374 if(crtn) {
1375 CSSM_PERROR("CSSM_DeriveKey", crtn);
1376 goto loser;
1377 }
1378
1379 /*
1380 * Decrypt the encrypted key bits with the KEK key.
1381 */
1382 crtn = CSSM_CSP_CreateSymmetricContext(refCspHand,
1383 kekOid->cssmAlgorithm,
1384 kekMode,
1385 NULL, // access cred
1386 &kekDerive,
1387 &iv, // InitVector
1388 /* FIXME is this variable too? */
1389 CSSM_PADDING_PKCS7,
1390 NULL, // Params
1391 &ccHand);
1392 if(rv) {
1393 CSSM_PERROR("CSSM_CSP_CreateSymmetricContext", rv);
1394 goto loser;
1395 }
1396
1397 memset(&wrappedKey, 0, sizeof(CSSM_KEY));
1398 memset(&unwrappedKey, 0, sizeof(CSSM_KEY));
1399
1400 bulkAlg = SECOID_FindyCssmAlgorithmByTag(bulkalgtag);
1401 if(bulkAlg == CSSM_ALGID_NONE) {
1402 dprintf("SecCmsUtilDecryptSymKeyECDH: unknown bulk alg\n");
1403 goto loser;
1404 }
1405
1406 wrappedKey.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION;
1407 wrappedKey.KeyHeader.BlobType = CSSM_KEYBLOB_WRAPPED;
1408 wrappedKey.KeyHeader.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7;
1409 wrappedKey.KeyHeader.AlgorithmId = bulkAlg;
1410 wrappedKey.KeyHeader.KeyClass = CSSM_KEYCLASS_SESSION_KEY;
1411 wrappedKey.KeyHeader.WrapAlgorithmId = kekOid->cssmAlgorithm;
1412 wrappedKey.KeyHeader.WrapMode = CSSM_ALGMODE_NONE;
1413 wrappedKey.KeyData = *encKey;
1414
1415 crtn = CSSM_UnwrapKey(ccHand,
1416 NULL, /* publicKey */
1417 &wrappedKey,
1418 CSSM_KEYUSE_DECRYPT,
1419 CSSM_KEYATTR_EXTRACTABLE,
1420 &keyLabel,
1421 NULL, /* rcc */
1422 &unwrappedKey,
1423 &descriptiveData);
1424 CSSM_DeleteContext(ccHand);
1425 ccHand = 0;
1426 if(crtn) {
1427 CSSM_PERROR("CSSM_UnwrapKey", crtn);
1428 goto loser;
1429 }
1430 rv = SecKeyCreateWithCSSMKey(&unwrappedKey, &outKey);
1431 if (rv) {
1432 CSSM_PERROR("SecKeyCreateWithCSSMKey", rv);
1433 }
1434
1435 loser:
1436 if(pool != NULL) {
1437 PORT_FreeArena(pool, PR_FALSE);
1438 }
1439 if(kekDerive.KeyData.Data) {
1440 CSSM_FreeKey(refCspHand, NULL, &kekDerive, CSSM_FALSE);
1441 }
1442 if(outKey == NULL) {
1443 PORT_SetError(SEC_ERROR_NO_KEY);
1444 }
1445 return outKey;
1446 }
1447
1448
1449