]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_smime/lib/cmsrecinfo.c
Security-59306.101.1.tar.gz
[apple/security.git] / OSX / libsecurity_smime / lib / cmsrecinfo.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 recipientInfo methods.
36 */
37
38 #include "cmslocal.h"
39
40 #include "cert.h"
41 #include "secitem.h"
42 #include "secoid.h"
43
44 #include <security_asn1/secasn1.h>
45 #include <security_asn1/secerr.h>
46 #include <Security/SecKeyPriv.h>
47 #include <Security/SecCertificatePriv.h>
48 #include <Security/SecCmsRecipientInfo.h>
49
50 static Boolean
51 nss_cmsrecipientinfo_usessubjectkeyid(SecCmsRecipientInfoRef ri)
52 {
53 if (ri->recipientInfoType == SecCmsRecipientInfoIDKeyTrans) {
54 SecCmsRecipientIdentifier *rid;
55 rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier;
56 if (rid->identifierType == SecCmsRecipientIDSubjectKeyID) {
57 return PR_TRUE;
58 }
59 }
60 return PR_FALSE;
61 }
62
63
64 static SecCmsRecipientInfoRef
65 nss_cmsrecipientinfo_create(SecCmsMessageRef cmsg, SecCmsRecipientIDSelector type,
66 SecCertificateRef cert, SecPublicKeyRef pubKey,
67 CSSM_DATA_PTR subjKeyID)
68 {
69 SecCmsRecipientInfoRef ri;
70 void *mark;
71 SECOidTag certalgtag;
72 OSStatus rv = SECSuccess;
73 SecCmsRecipientEncryptedKey *rek;
74 SecCmsOriginatorIdentifierOrKey *oiok;
75 unsigned long version;
76 CSSM_DATA_PTR dummy;
77 PLArenaPool *poolp;
78 const SECAlgorithmID *algid;
79 SecCmsRecipientIdentifier *rid;
80
81 poolp = cmsg->poolp;
82
83 mark = PORT_ArenaMark(poolp);
84
85 ri = (SecCmsRecipientInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsRecipientInfo));
86 if (ri == NULL)
87 goto loser;
88
89 ri->cmsg = cmsg;
90 if (type == SecCmsRecipientIDIssuerSN)
91 {
92 ri->cert = CERT_DupCertificate(cert);
93 if (ri->cert == NULL)
94 goto loser;
95 rv = SecCertificateGetAlgorithmID(cert,&algid);
96 } else {
97 PORT_Assert(pubKey);
98 #if TARGET_OS_OSX
99 rv = SecKeyGetAlgorithmID(pubKey,&algid);
100 #else
101 /* TBD: Unify this code. Currently, iOS has an incompatible
102 * SecKeyGetAlgorithmID implementation. */
103 goto loser;
104 #endif
105 }
106
107 certalgtag = SECOID_GetAlgorithmTag(algid);
108
109 rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier;
110 switch (certalgtag) {
111 case SEC_OID_PKCS1_RSA_ENCRYPTION:
112 ri->recipientInfoType = SecCmsRecipientInfoIDKeyTrans;
113 rid->identifierType = type;
114 if (type == SecCmsRecipientIDIssuerSN) {
115 rid->id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
116 if (rid->id.issuerAndSN == NULL) {
117 break;
118 }
119 } else if (type == SecCmsRecipientIDSubjectKeyID){
120
121 rid->id.subjectKeyID = PORT_ArenaNew(poolp, CSSM_DATA);
122 if (rid->id.subjectKeyID == NULL) {
123 rv = SECFailure;
124 PORT_SetError(SEC_ERROR_NO_MEMORY);
125 break;
126 }
127 if (SECITEM_CopyItem(poolp, rid->id.subjectKeyID, subjKeyID)) {
128 rv = SECFailure;
129 PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
130 break;
131 }
132 if (rid->id.subjectKeyID->Data == NULL) {
133 rv = SECFailure;
134 PORT_SetError(SEC_ERROR_NO_MEMORY);
135 break;
136 }
137 } else {
138 PORT_SetError(SEC_ERROR_INVALID_ARGS);
139 rv = SECFailure;
140 }
141 break;
142 case SEC_OID_MISSI_KEA_DSS_OLD:
143 case SEC_OID_MISSI_KEA_DSS:
144 case SEC_OID_MISSI_KEA:
145 PORT_Assert(type != SecCmsRecipientIDSubjectKeyID);
146 if (type == SecCmsRecipientIDSubjectKeyID) {
147 rv = SECFailure;
148 break;
149 }
150 /* backward compatibility - this is not really a keytrans operation */
151 ri->recipientInfoType = SecCmsRecipientInfoIDKeyTrans;
152 /* hardcoded issuerSN choice for now */
153 ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType = SecCmsRecipientIDIssuerSN;
154 ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
155 if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
156 rv = SECFailure;
157 break;
158 }
159 break;
160 case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */
161 PORT_Assert(type != SecCmsRecipientIDSubjectKeyID);
162 if (type == SecCmsRecipientIDSubjectKeyID) {
163 rv = SECFailure;
164 break;
165 }
166 /* a key agreement op */
167 ri->recipientInfoType = SecCmsRecipientInfoIDKeyAgree;
168
169 if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
170 rv = SECFailure;
171 break;
172 }
173 /* we do not support the case where multiple recipients
174 * share the same KeyAgreeRecipientInfo and have multiple RecipientEncryptedKeys
175 * in this case, we would need to walk all the recipientInfos, take the
176 * ones that do KeyAgreement algorithms and join them, algorithm by algorithm
177 * Then, we'd generate ONE ukm and OriginatorIdentifierOrKey */
178
179 /* only epheremal-static Diffie-Hellman is supported for now
180 * this is the only form of key agreement that provides potential anonymity
181 * of the sender, plus we do not have to include certs in the message */
182
183 /* force single recipientEncryptedKey for now */
184 if ((rek = SecCmsRecipientEncryptedKeyCreate(poolp)) == NULL) {
185 rv = SECFailure;
186 break;
187 }
188
189 /* hardcoded IssuerSN choice for now */
190 rek->recipientIdentifier.identifierType = SecCmsKeyAgreeRecipientIDIssuerSN;
191 if ((rek->recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) {
192 rv = SECFailure;
193 break;
194 }
195
196 oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
197
198 /* see RFC2630 12.3.1.1 */
199 oiok->identifierType = SecCmsOriginatorIDOrKeyOriginatorPublicKey;
200
201 rv = SecCmsArrayAdd(poolp, (void ***)&ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys,
202 (void *)rek);
203
204 break;
205
206 case SEC_OID_EC_PUBLIC_KEY:
207 /* ephemeral-static ECDH - issuerAndSN, OriginatorPublicKey only */
208 PORT_Assert(type != SecCmsRecipientIDSubjectKeyID);
209 if (type == SecCmsRecipientIDSubjectKeyID) {
210 rv = SECFailure;
211 break;
212 }
213 /* a key agreement op */
214 ri->recipientInfoType = SecCmsRecipientInfoIDKeyAgree;
215 ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
216 if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
217 rv = SECFailure;
218 break;
219 }
220 /* we do not support the case where multiple recipients
221 * share the same KeyAgreeRecipientInfo and have multiple RecipientEncryptedKeys
222 * in this case, we would need to walk all the recipientInfos, take the
223 * ones that do KeyAgreement algorithms and join them, algorithm by algorithm
224 * Then, we'd generate ONE ukm and OriginatorIdentifierOrKey */
225
226 /* force single recipientEncryptedKey for now */
227 if ((rek = SecCmsRecipientEncryptedKeyCreate(poolp)) == NULL) {
228 rv = SECFailure;
229 break;
230 }
231
232 /* hardcoded IssuerSN choice for now */
233 rek->recipientIdentifier.identifierType = SecCmsKeyAgreeRecipientIDIssuerSN;
234 if ((rek->recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) {
235 rv = SECFailure;
236 break;
237 }
238
239 oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
240
241 /* see RFC 3278 3.1.1 */
242 oiok->identifierType = SecCmsOriginatorIDOrKeyOriginatorPublicKey;
243
244 rv = SecCmsArrayAdd(poolp, (void ***)&ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys,
245 (void *)rek);
246
247 break;
248
249 default:
250 /* other algorithms not supported yet */
251 /* NOTE that we do not support any KEK algorithm */
252 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
253 rv = SECFailure;
254 break;
255 }
256
257 if (rv == SECFailure)
258 goto loser;
259
260 /* set version */
261 switch (ri->recipientInfoType) {
262 case SecCmsRecipientInfoIDKeyTrans:
263 if (ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType == SecCmsRecipientIDIssuerSN)
264 version = SEC_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_ISSUERSN;
265 else
266 version = SEC_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_SUBJKEY;
267 dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyTransRecipientInfo.version), version);
268 if (dummy == NULL)
269 goto loser;
270 break;
271 case SecCmsRecipientInfoIDKeyAgree:
272 dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyAgreeRecipientInfo.version),
273 SEC_CMS_KEYAGREE_RECIPIENT_INFO_VERSION);
274 if (dummy == NULL)
275 goto loser;
276 break;
277 case SecCmsRecipientInfoIDKEK:
278 /* NOTE: this cannot happen as long as we do not support any KEK algorithm */
279 dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.kekRecipientInfo.version),
280 SEC_CMS_KEK_RECIPIENT_INFO_VERSION);
281 if (dummy == NULL)
282 goto loser;
283 break;
284
285 }
286
287 PORT_ArenaUnmark (poolp, mark);
288 #if 0
289 if (freeSpki)
290 SECKEY_DestroySubjectPublicKeyInfo(freeSpki);
291 #endif
292 return ri;
293
294 loser:
295 #if 0
296 if (freeSpki)
297 SECKEY_DestroySubjectPublicKeyInfo(freeSpki);
298 #endif
299 PORT_ArenaRelease (poolp, mark);
300 return NULL;
301 }
302
303 /*
304 * SecCmsRecipientInfoCreate - create a recipientinfo
305 *
306 * we currently do not create KeyAgreement recipientinfos with multiple
307 * recipientEncryptedKeys the certificate is supposed to have been
308 * verified by the caller
309 */
310 SecCmsRecipientInfoRef
311 SecCmsRecipientInfoCreate(SecCmsMessageRef cmsg, SecCertificateRef cert)
312 {
313 return nss_cmsrecipientinfo_create(cmsg, SecCmsRecipientIDIssuerSN, cert,
314 NULL, NULL);
315 }
316
317 SecCmsRecipientInfoRef
318 SecCmsRecipientInfoCreateWithSubjKeyID(SecCmsMessageRef cmsg,
319 CSSM_DATA_PTR subjKeyID,
320 SecPublicKeyRef pubKey)
321 {
322 return nss_cmsrecipientinfo_create(cmsg, SecCmsRecipientIDSubjectKeyID,
323 NULL, pubKey, subjKeyID);
324 }
325
326 /* This is exported out of the Security framework, but it's in
327 * SecCertificateInternal.h, which we don't have access to from
328 * the libsecurity_smime project. */
329 CFDataRef SecCertificateGetSubjectKeyID(SecCertificateRef certificate);
330
331 SecCmsRecipientInfoRef
332 SecCmsRecipientInfoCreateWithSubjKeyIDFromCert(SecCmsMessageRef cmsg,
333 SecCertificateRef cert)
334 {
335 SecKeyRef pubKey = NULL;
336 CSSM_DATA subjKeyID = {0, NULL};
337 SecCmsRecipientInfoRef retVal = NULL;
338 CFDataRef subjectKeyIDData = NULL;
339
340
341 if (!cmsg || !cert) {
342 return NULL;
343 }
344
345 subjectKeyIDData = SecCertificateGetSubjectKeyID(cert);
346 if (!subjectKeyIDData)
347 goto done;
348 subjKeyID.Length =
349 CFDataGetLength(subjectKeyIDData);
350 subjKeyID.Data = (uint8_t *)CFDataGetBytePtr(subjectKeyIDData);
351
352 retVal = SecCmsRecipientInfoCreateWithSubjKeyID(cmsg, &subjKeyID, pubKey);
353 done:
354 if (pubKey)
355 SECKEY_DestroyPublicKey(pubKey);
356
357 if (subjKeyID.Data)
358 SECITEM_FreeItem(&subjKeyID, PR_FALSE);
359
360 return retVal;
361 }
362
363 void
364 SecCmsRecipientInfoDestroy(SecCmsRecipientInfoRef ri)
365 {
366 /* version was allocated on the pool, so no need to destroy it */
367 /* issuerAndSN was allocated on the pool, so no need to destroy it */
368 if (ri->cert != NULL)
369 CERT_DestroyCertificate(ri->cert);
370
371 if (nss_cmsrecipientinfo_usessubjectkeyid(ri)) {
372 SecCmsKeyTransRecipientInfoEx *extra;
373 extra = &ri->ri.keyTransRecipientInfoEx;
374 if (extra->pubKey)
375 SECKEY_DestroyPublicKey(extra->pubKey);
376 }
377
378 /* recipientInfo structure itself was allocated on the pool, so no need to destroy it */
379 /* we're done. */
380 }
381
382 int
383 SecCmsRecipientInfoGetVersion(SecCmsRecipientInfoRef ri)
384 {
385 unsigned long version;
386 CSSM_DATA_PTR versionitem = NULL;
387
388 switch (ri->recipientInfoType) {
389 case SecCmsRecipientInfoIDKeyTrans:
390 /* ignore subIndex */
391 versionitem = &(ri->ri.keyTransRecipientInfo.version);
392 break;
393 case SecCmsRecipientInfoIDKEK:
394 /* ignore subIndex */
395 versionitem = &(ri->ri.kekRecipientInfo.version);
396 break;
397 case SecCmsRecipientInfoIDKeyAgree:
398 versionitem = &(ri->ri.keyAgreeRecipientInfo.version);
399 break;
400 }
401
402 PORT_Assert(versionitem);
403 if (versionitem == NULL)
404 return 0;
405
406 /* always take apart the CSSM_DATA */
407 if (SEC_ASN1DecodeInteger(versionitem, &version) != SECSuccess)
408 return 0;
409 else
410 return (int)version;
411 }
412
413 CSSM_DATA_PTR
414 SecCmsRecipientInfoGetEncryptedKey(SecCmsRecipientInfoRef ri, int subIndex)
415 {
416 CSSM_DATA_PTR enckey = NULL;
417
418 switch (ri->recipientInfoType) {
419 case SecCmsRecipientInfoIDKeyTrans:
420 /* ignore subIndex */
421 enckey = &(ri->ri.keyTransRecipientInfo.encKey);
422 break;
423 case SecCmsRecipientInfoIDKEK:
424 /* ignore subIndex */
425 enckey = &(ri->ri.kekRecipientInfo.encKey);
426 break;
427 case SecCmsRecipientInfoIDKeyAgree:
428 enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey);
429 break;
430 }
431 return enckey;
432 }
433
434
435 SECOidTag
436 SecCmsRecipientInfoGetKeyEncryptionAlgorithmTag(SecCmsRecipientInfoRef ri)
437 {
438 SECOidTag encalgtag = SEC_OID_UNKNOWN; /* an invalid encryption alg */
439
440 switch (ri->recipientInfoType) {
441 case SecCmsRecipientInfoIDKeyTrans:
442 encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg));
443 break;
444 case SecCmsRecipientInfoIDKeyAgree:
445 encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg));
446 break;
447 case SecCmsRecipientInfoIDKEK:
448 encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.kekRecipientInfo.keyEncAlg));
449 break;
450 }
451 return encalgtag;
452 }
453
454 OSStatus
455 SecCmsRecipientInfoWrapBulkKey(SecCmsRecipientInfoRef ri, SecSymmetricKeyRef bulkkey,
456 SECOidTag bulkalgtag)
457 {
458 SecCertificateRef cert;
459 SECOidTag certalgtag;
460 OSStatus rv = SECSuccess;
461 #if 0
462 CSSM_DATA_PTR params = NULL;
463 #endif /* 0 */
464 SecCmsRecipientEncryptedKey *rek;
465 SecCmsOriginatorIdentifierOrKey *oiok;
466 const SECAlgorithmID *algid;
467 PLArenaPool *poolp;
468 SecCmsKeyTransRecipientInfoEx *extra = NULL;
469 Boolean usesSubjKeyID;
470 uint8 nullData[2] = {SEC_ASN1_NULL, 0};
471 SECItem nullItem;
472 SecCmsKeyAgreeRecipientInfo *kari;
473
474 poolp = ri->cmsg->poolp;
475 cert = ri->cert;
476 usesSubjKeyID = nss_cmsrecipientinfo_usessubjectkeyid(ri);
477 if (cert) {
478 rv = SecCertificateGetAlgorithmID(cert,&algid);
479 if (rv)
480 return SECFailure;
481 certalgtag = SECOID_GetAlgorithmTag(algid);
482 } else if (usesSubjKeyID) {
483 extra = &ri->ri.keyTransRecipientInfoEx;
484 /* sanity check */
485 PORT_Assert(extra->pubKey);
486 if (!extra->pubKey) {
487 PORT_SetError(SEC_ERROR_INVALID_ARGS);
488 return SECFailure;
489 }
490 #if TARGET_OS_OSX
491 rv = SecKeyGetAlgorithmID(extra->pubKey,&algid);
492 #else
493 /* TBD: Unify this code. Currently, iOS has an incompatible
494 * SecKeyGetAlgorithmID implementation. */
495 return SECFailure;
496 #endif
497 if (rv)
498 return SECFailure;
499 certalgtag = SECOID_GetAlgorithmTag(algid);
500 } else {
501 PORT_SetError(SEC_ERROR_INVALID_ARGS);
502 return SECFailure;
503 }
504
505 /* XXX set ri->recipientInfoType to the proper value here */
506 /* or should we look if it's been set already ? */
507
508 certalgtag = SECOID_GetAlgorithmTag(algid);
509 switch (certalgtag) {
510 case SEC_OID_PKCS1_RSA_ENCRYPTION:
511 /* wrap the symkey */
512 if (cert) {
513 rv = SecCmsUtilEncryptSymKeyRSA(poolp, cert, bulkkey,
514 &ri->ri.keyTransRecipientInfo.encKey);
515 if (rv != SECSuccess)
516 break;
517 } else if (usesSubjKeyID) {
518 PORT_Assert(extra != NULL);
519 rv = SecCmsUtilEncryptSymKeyRSAPubKey(poolp, extra->pubKey,
520 bulkkey, &ri->ri.keyTransRecipientInfo.encKey);
521 if (rv != SECSuccess)
522 break;
523 }
524
525 rv = SECOID_SetAlgorithmID(poolp, &(ri->ri.keyTransRecipientInfo.keyEncAlg), certalgtag, NULL);
526 break;
527 #if 0
528 case SEC_OID_MISSI_KEA_DSS_OLD:
529 case SEC_OID_MISSI_KEA_DSS:
530 case SEC_OID_MISSI_KEA:
531 rv = SecCmsUtilEncryptSymKeyMISSI(poolp, cert, bulkkey,
532 bulkalgtag,
533 &ri->ri.keyTransRecipientInfo.encKey,
534 &params, ri->cmsg->pwfn_arg);
535 if (rv != SECSuccess)
536 break;
537
538 /* here, we DO need to pass the params to the wrap function because, with
539 * RSA, there is no funny stuff going on with generation of IV vectors or so */
540 rv = SECOID_SetAlgorithmID(poolp, &(ri->ri.keyTransRecipientInfo.keyEncAlg), certalgtag, params);
541 break;
542 case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */
543 rek = ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[0];
544 if (rek == NULL) {
545 rv = SECFailure;
546 break;
547 }
548
549 oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
550 PORT_Assert(oiok->identifierType == SecCmsOriginatorIDOrKeyOriginatorPublicKey);
551
552 /* see RFC2630 12.3.1.1 */
553 if (SECOID_SetAlgorithmID(poolp, &oiok->id.originatorPublicKey.algorithmIdentifier,
554 SEC_OID_X942_DIFFIE_HELMAN_KEY, NULL) != SECSuccess) {
555 rv = SECFailure;
556 break;
557 }
558
559 /* this will generate a key pair, compute the shared secret, */
560 /* derive a key and ukm for the keyEncAlg out of it, encrypt the bulk key with */
561 /* the keyEncAlg, set encKey, keyEncAlg, publicKey etc. */
562 rv = SecCmsUtilEncryptSymKeyESDH(poolp, cert, bulkkey,
563 &rek->encKey,
564 &ri->ri.keyAgreeRecipientInfo.ukm,
565 &ri->ri.keyAgreeRecipientInfo.keyEncAlg,
566 &oiok->id.originatorPublicKey.publicKey);
567
568 break;
569 #endif /* 0 */
570
571 case SEC_OID_EC_PUBLIC_KEY:
572 /* These were set up in nss_cmsrecipientinfo_create() */
573 kari = &ri->ri.keyAgreeRecipientInfo;
574 rek = kari->recipientEncryptedKeys[0];
575 if (rek == NULL) {
576 rv = SECFailure;
577 break;
578 }
579
580 oiok = &(kari->originatorIdentifierOrKey);
581 PORT_Assert(oiok->identifierType == SecCmsOriginatorIDOrKeyOriginatorPublicKey);
582
583 /*
584 * RFC 3278 3.1.1 says this AlgId must contain NULL params which is contrary to
585 * any other use of the SEC_OID_EC_PUBLIC_KEY OID. So we provide one
586 * explicitly instead of mucking up the login in SECOID_SetAlgorithmID().
587 */
588 nullItem.Data = nullData;
589 nullItem.Length = 2;
590 if (SECOID_SetAlgorithmID(poolp, &oiok->id.originatorPublicKey.algorithmIdentifier,
591 SEC_OID_EC_PUBLIC_KEY, &nullItem) != SECSuccess) {
592 rv = SECFailure;
593 break;
594 }
595
596 /* this will generate a key pair, compute the shared secret, */
597 /* derive a key and ukm for the keyEncAlg out of it, encrypt the bulk key with */
598 /* the keyEncAlg, set encKey, keyEncAlg, publicKey etc. */
599 rv = SecCmsUtilEncryptSymKeyECDH(poolp, cert, bulkkey,
600 &rek->encKey,
601 &kari->ukm,
602 &kari->keyEncAlg,
603 &oiok->id.originatorPublicKey.publicKey);
604 break;
605
606 default:
607 /* other algorithms not supported yet */
608 /* NOTE that we do not support any KEK algorithm */
609 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
610 rv = SECFailure;
611 break;
612 }
613 #if 0
614 if (freeSpki)
615 SECKEY_DestroySubjectPublicKeyInfo(freeSpki);
616 #endif
617
618 return rv;
619 }
620
621 #ifdef NDEBUG
622 #define dprintf(args...)
623 #else
624 #define dprintf(args...) fprintf(stderr, args)
625 #endif
626
627 SecSymmetricKeyRef
628 SecCmsRecipientInfoUnwrapBulkKey(SecCmsRecipientInfoRef ri, int subIndex,
629 SecCertificateRef cert, SecPrivateKeyRef privkey, SECOidTag bulkalgtag)
630 {
631 SecSymmetricKeyRef bulkkey = NULL;
632 SECAlgorithmID *encalg;
633 SECOidTag encalgtag;
634 CSSM_DATA_PTR enckey;
635 int error;
636
637 ri->cert = CERT_DupCertificate(cert);
638 /* mark the recipientInfo so we can find it later */
639
640 switch (ri->recipientInfoType) {
641 case SecCmsRecipientInfoIDKeyTrans:
642 encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg));
643 enckey = &(ri->ri.keyTransRecipientInfo.encKey); /* ignore subIndex */
644 switch (encalgtag) {
645 case SEC_OID_PKCS1_RSA_ENCRYPTION:
646 /* RSA encryption algorithm: */
647 /* get the symmetric (bulk) key by unwrapping it using our private key */
648 bulkkey = SecCmsUtilDecryptSymKeyRSA(privkey, enckey, bulkalgtag);
649 break;
650 #if 0
651 case SEC_OID_NETSCAPE_SMIME_KEA:
652 /* FORTEZZA key exchange algorithm */
653 /* the supplemental data is in the parameters of encalg */
654 encalg = &(ri->ri.keyTransRecipientInfo.keyEncAlg);
655 bulkkey = SecCmsUtilDecryptSymKeyMISSI(privkey, enckey, encalg, bulkalgtag, ri->cmsg->pwfn_arg);
656 break;
657 #endif /* 0 */
658 default:
659 error = SEC_ERROR_UNSUPPORTED_KEYALG;
660 goto loser;
661 }
662 break;
663 case SecCmsRecipientInfoIDKeyAgree:
664 encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg));
665 switch (encalgtag) {
666 case SEC_OID_X942_DIFFIE_HELMAN_KEY:
667 /* Diffie-Helman key exchange */
668 /* XXX not yet implemented */
669 /* XXX problem: SEC_OID_X942_DIFFIE_HELMAN_KEY points to a PKCS3 mechanism! */
670 /* we support ephemeral-static DH only, so if the recipientinfo */
671 /* has originator stuff in it, we punt (or do we? shouldn't be that hard...) */
672 /* first, we derive the KEK (a symkey!) using a Derive operation, then we get the */
673 /* content encryption key using a Unwrap op */
674 /* the derive operation has to generate the key using the algorithm in RFC2631 */
675 error = SEC_ERROR_UNSUPPORTED_KEYALG;
676 break;
677 case SEC_OID_DH_SINGLE_STD_SHA1KDF:
678 {
679 /* ephemeral-static ECDH */
680 enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey);
681 encalg = &(ri->ri.keyAgreeRecipientInfo.keyEncAlg);
682 SecCmsKeyAgreeRecipientInfo *kari = &ri->ri.keyAgreeRecipientInfo;
683 SecCmsOriginatorIdentifierOrKey *oiok = &kari->originatorIdentifierOrKey;
684 if(oiok->identifierType != SecCmsOriginatorIDOrKeyOriginatorPublicKey) {
685 dprintf("SEC_OID_EC_PUBLIC_KEY unwrap key: bad oiok.id\n");
686 error = SEC_ERROR_LIBRARY_FAILURE;
687 goto loser;
688 }
689 SecCmsOriginatorPublicKey *opk = &oiok->id.originatorPublicKey;
690 /* FIXME - verify opk->algorithmIdentifier here? */
691 CSSM_DATA senderPubKey = opk->publicKey;
692 CSSM_DATA_PTR ukm = &kari->ukm;
693 bulkkey = SecCmsUtilDecryptSymKeyECDH(privkey, enckey, ukm, encalg, bulkalgtag, &senderPubKey);
694 break;
695 }
696 default:
697 error = SEC_ERROR_UNSUPPORTED_KEYALG;
698 goto loser;
699 }
700 break;
701 case SecCmsRecipientInfoIDKEK:
702 /* not supported yet */
703 error = SEC_ERROR_UNSUPPORTED_KEYALG;
704 goto loser;
705 break;
706 }
707 /* XXXX continue here */
708 return bulkkey;
709
710 loser:
711 PORT_SetError(error);
712 return NULL;
713 }