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