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