--- /dev/null
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * CMS recipientInfo methods.
+ */
+
+#include "cmslocal.h"
+
+#include "cert.h"
+#include "secitem.h"
+#include "secoid.h"
+
+#include <security_asn1/secasn1.h>
+#include <security_asn1/secerr.h>
+#include <Security/SecKeyPriv.h>
+#include <Security/SecCertificatePriv.h>
+#include <Security/SecCmsRecipientInfo.h>
+
+static Boolean
+nss_cmsrecipientinfo_usessubjectkeyid(SecCmsRecipientInfoRef ri)
+{
+ if (ri->recipientInfoType == SecCmsRecipientInfoIDKeyTrans) {
+ SecCmsRecipientIdentifier *rid;
+ rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier;
+ if (rid->identifierType == SecCmsRecipientIDSubjectKeyID) {
+ return PR_TRUE;
+ }
+ }
+ return PR_FALSE;
+}
+
+
+static SecCmsRecipientInfoRef
+nss_cmsrecipientinfo_create(SecCmsMessageRef cmsg, SecCmsRecipientIDSelector type,
+ SecCertificateRef cert, SecPublicKeyRef pubKey,
+ CSSM_DATA_PTR subjKeyID)
+{
+ SecCmsRecipientInfoRef ri;
+ void *mark;
+ SECOidTag certalgtag;
+ OSStatus rv = SECSuccess;
+ SecCmsRecipientEncryptedKey *rek;
+ SecCmsOriginatorIdentifierOrKey *oiok;
+ unsigned long version;
+ CSSM_DATA_PTR dummy;
+ PLArenaPool *poolp;
+ const SECAlgorithmID *algid;
+ SecCmsRecipientIdentifier *rid;
+
+ poolp = cmsg->poolp;
+
+ mark = PORT_ArenaMark(poolp);
+
+ ri = (SecCmsRecipientInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsRecipientInfo));
+ if (ri == NULL)
+ goto loser;
+
+ ri->cmsg = cmsg;
+ if (type == SecCmsRecipientIDIssuerSN)
+ {
+ ri->cert = CERT_DupCertificate(cert);
+ if (ri->cert == NULL)
+ goto loser;
+ rv = SecCertificateGetAlgorithmID(cert,&algid);
+ } else {
+ PORT_Assert(pubKey);
+ rv = SecKeyGetAlgorithmID(pubKey,&algid);
+ }
+
+ certalgtag = SECOID_GetAlgorithmTag(algid);
+
+ rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier;
+ switch (certalgtag) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ ri->recipientInfoType = SecCmsRecipientInfoIDKeyTrans;
+ rid->identifierType = type;
+ if (type == SecCmsRecipientIDIssuerSN) {
+ rid->id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
+ if (rid->id.issuerAndSN == NULL) {
+ break;
+ }
+ } else if (type == SecCmsRecipientIDSubjectKeyID){
+ SecCmsKeyTransRecipientInfoEx *riExtra;
+
+ rid->id.subjectKeyID = PORT_ArenaNew(poolp, CSSM_DATA);
+ if (rid->id.subjectKeyID == NULL) {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ break;
+ }
+ SECITEM_CopyItem(poolp, rid->id.subjectKeyID, subjKeyID);
+ if (rid->id.subjectKeyID->Data == NULL) {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ break;
+ }
+ riExtra = &ri->ri.keyTransRecipientInfoEx;
+ riExtra->version = 0;
+ riExtra->pubKey = SECKEY_CopyPublicKey(pubKey);
+ if (riExtra->pubKey == NULL) {
+ rv = SECFailure;
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ break;
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ rv = SECFailure;
+ }
+ break;
+ case SEC_OID_MISSI_KEA_DSS_OLD:
+ case SEC_OID_MISSI_KEA_DSS:
+ case SEC_OID_MISSI_KEA:
+ PORT_Assert(type != SecCmsRecipientIDSubjectKeyID);
+ if (type == SecCmsRecipientIDSubjectKeyID) {
+ rv = SECFailure;
+ break;
+ }
+ /* backward compatibility - this is not really a keytrans operation */
+ ri->recipientInfoType = SecCmsRecipientInfoIDKeyTrans;
+ /* hardcoded issuerSN choice for now */
+ ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType = SecCmsRecipientIDIssuerSN;
+ ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
+ if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
+ rv = SECFailure;
+ break;
+ }
+ break;
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */
+ PORT_Assert(type != SecCmsRecipientIDSubjectKeyID);
+ if (type == SecCmsRecipientIDSubjectKeyID) {
+ rv = SECFailure;
+ break;
+ }
+ /* a key agreement op */
+ ri->recipientInfoType = SecCmsRecipientInfoIDKeyAgree;
+
+ if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
+ rv = SECFailure;
+ break;
+ }
+ /* we do not support the case where multiple recipients
+ * share the same KeyAgreeRecipientInfo and have multiple RecipientEncryptedKeys
+ * in this case, we would need to walk all the recipientInfos, take the
+ * ones that do KeyAgreement algorithms and join them, algorithm by algorithm
+ * Then, we'd generate ONE ukm and OriginatorIdentifierOrKey */
+
+ /* only epheremal-static Diffie-Hellman is supported for now
+ * this is the only form of key agreement that provides potential anonymity
+ * of the sender, plus we do not have to include certs in the message */
+
+ /* force single recipientEncryptedKey for now */
+ if ((rek = SecCmsRecipientEncryptedKeyCreate(poolp)) == NULL) {
+ rv = SECFailure;
+ break;
+ }
+
+ /* hardcoded IssuerSN choice for now */
+ rek->recipientIdentifier.identifierType = SecCmsKeyAgreeRecipientIDIssuerSN;
+ if ((rek->recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) {
+ rv = SECFailure;
+ break;
+ }
+
+ oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
+
+ /* see RFC2630 12.3.1.1 */
+ oiok->identifierType = SecCmsOriginatorIDOrKeyOriginatorPublicKey;
+
+ rv = SecCmsArrayAdd(poolp, (void ***)&ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys,
+ (void *)rek);
+
+ break;
+
+ case SEC_OID_EC_PUBLIC_KEY:
+ /* ephemeral-static ECDH - issuerAndSN, OriginatorPublicKey only */
+ PORT_Assert(type != SecCmsRecipientIDSubjectKeyID);
+ if (type == SecCmsRecipientIDSubjectKeyID) {
+ rv = SECFailure;
+ break;
+ }
+ /* a key agreement op */
+ ri->recipientInfoType = SecCmsRecipientInfoIDKeyAgree;
+ ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
+ if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
+ rv = SECFailure;
+ break;
+ }
+ /* we do not support the case where multiple recipients
+ * share the same KeyAgreeRecipientInfo and have multiple RecipientEncryptedKeys
+ * in this case, we would need to walk all the recipientInfos, take the
+ * ones that do KeyAgreement algorithms and join them, algorithm by algorithm
+ * Then, we'd generate ONE ukm and OriginatorIdentifierOrKey */
+
+ /* force single recipientEncryptedKey for now */
+ if ((rek = SecCmsRecipientEncryptedKeyCreate(poolp)) == NULL) {
+ rv = SECFailure;
+ break;
+ }
+
+ /* hardcoded IssuerSN choice for now */
+ rek->recipientIdentifier.identifierType = SecCmsKeyAgreeRecipientIDIssuerSN;
+ if ((rek->recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) {
+ rv = SECFailure;
+ break;
+ }
+
+ oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
+
+ /* see RFC 3278 3.1.1 */
+ oiok->identifierType = SecCmsOriginatorIDOrKeyOriginatorPublicKey;
+
+ rv = SecCmsArrayAdd(poolp, (void ***)&ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys,
+ (void *)rek);
+
+ break;
+
+ default:
+ /* other algorithms not supported yet */
+ /* NOTE that we do not support any KEK algorithm */
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ rv = SECFailure;
+ break;
+ }
+
+ if (rv == SECFailure)
+ goto loser;
+
+ /* set version */
+ switch (ri->recipientInfoType) {
+ case SecCmsRecipientInfoIDKeyTrans:
+ if (ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType == SecCmsRecipientIDIssuerSN)
+ version = SEC_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_ISSUERSN;
+ else
+ version = SEC_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_SUBJKEY;
+ dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyTransRecipientInfo.version), version);
+ if (dummy == NULL)
+ goto loser;
+ break;
+ case SecCmsRecipientInfoIDKeyAgree:
+ dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyAgreeRecipientInfo.version),
+ SEC_CMS_KEYAGREE_RECIPIENT_INFO_VERSION);
+ if (dummy == NULL)
+ goto loser;
+ break;
+ case SecCmsRecipientInfoIDKEK:
+ /* NOTE: this cannot happen as long as we do not support any KEK algorithm */
+ dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.kekRecipientInfo.version),
+ SEC_CMS_KEK_RECIPIENT_INFO_VERSION);
+ if (dummy == NULL)
+ goto loser;
+ break;
+
+ }
+
+ PORT_ArenaUnmark (poolp, mark);
+#if 0
+ if (freeSpki)
+ SECKEY_DestroySubjectPublicKeyInfo(freeSpki);
+#endif
+ return ri;
+
+loser:
+#if 0
+ if (freeSpki)
+ SECKEY_DestroySubjectPublicKeyInfo(freeSpki);
+#endif
+ PORT_ArenaRelease (poolp, mark);
+ return NULL;
+}
+
+/*
+ * SecCmsRecipientInfoCreate - create a recipientinfo
+ *
+ * we currently do not create KeyAgreement recipientinfos with multiple
+ * recipientEncryptedKeys the certificate is supposed to have been
+ * verified by the caller
+ */
+SecCmsRecipientInfoRef
+SecCmsRecipientInfoCreate(SecCmsMessageRef cmsg, SecCertificateRef cert)
+{
+ return nss_cmsrecipientinfo_create(cmsg, SecCmsRecipientIDIssuerSN, cert,
+ NULL, NULL);
+}
+
+SecCmsRecipientInfoRef
+SecCmsRecipientInfoCreateWithSubjKeyID(SecCmsMessageRef cmsg,
+ CSSM_DATA_PTR subjKeyID,
+ SecPublicKeyRef pubKey)
+{
+ return nss_cmsrecipientinfo_create(cmsg, SecCmsRecipientIDSubjectKeyID,
+ NULL, pubKey, subjKeyID);
+}
+
+SecCmsRecipientInfoRef
+SecCmsRecipientInfoCreateWithSubjKeyIDFromCert(SecCmsMessageRef cmsg,
+ SecCertificateRef cert)
+{
+ SecPublicKeyRef pubKey = NULL;
+ CSSM_DATA subjKeyID = {0, NULL};
+ SecCmsRecipientInfoRef retVal = NULL;
+
+ if (!cmsg || !cert) {
+ return NULL;
+ }
+ pubKey = CERT_ExtractPublicKey(cert);
+ if (!pubKey) {
+ goto done;
+ }
+ if (CERT_FindSubjectKeyIDExtension(cert, &subjKeyID) != SECSuccess ||
+ subjKeyID.Data == NULL) {
+ goto done;
+ }
+ retVal = SecCmsRecipientInfoCreateWithSubjKeyID(cmsg, &subjKeyID, pubKey);
+done:
+ if (pubKey)
+ SECKEY_DestroyPublicKey(pubKey);
+
+ if (subjKeyID.Data)
+ SECITEM_FreeItem(&subjKeyID, PR_FALSE);
+
+ return retVal;
+}
+
+void
+SecCmsRecipientInfoDestroy(SecCmsRecipientInfoRef ri)
+{
+ /* version was allocated on the pool, so no need to destroy it */
+ /* issuerAndSN was allocated on the pool, so no need to destroy it */
+ if (ri->cert != NULL)
+ CERT_DestroyCertificate(ri->cert);
+
+ if (nss_cmsrecipientinfo_usessubjectkeyid(ri)) {
+ SecCmsKeyTransRecipientInfoEx *extra;
+ extra = &ri->ri.keyTransRecipientInfoEx;
+ if (extra->pubKey)
+ SECKEY_DestroyPublicKey(extra->pubKey);
+ }
+
+ /* recipientInfo structure itself was allocated on the pool, so no need to destroy it */
+ /* we're done. */
+}
+
+int
+SecCmsRecipientInfoGetVersion(SecCmsRecipientInfoRef ri)
+{
+ unsigned long version;
+ CSSM_DATA_PTR versionitem = NULL;
+
+ switch (ri->recipientInfoType) {
+ case SecCmsRecipientInfoIDKeyTrans:
+ /* ignore subIndex */
+ versionitem = &(ri->ri.keyTransRecipientInfo.version);
+ break;
+ case SecCmsRecipientInfoIDKEK:
+ /* ignore subIndex */
+ versionitem = &(ri->ri.kekRecipientInfo.version);
+ break;
+ case SecCmsRecipientInfoIDKeyAgree:
+ versionitem = &(ri->ri.keyAgreeRecipientInfo.version);
+ break;
+ }
+
+ PORT_Assert(versionitem);
+ if (versionitem == NULL)
+ return 0;
+
+ /* always take apart the CSSM_DATA */
+ if (SEC_ASN1DecodeInteger(versionitem, &version) != SECSuccess)
+ return 0;
+ else
+ return (int)version;
+}
+
+CSSM_DATA_PTR
+SecCmsRecipientInfoGetEncryptedKey(SecCmsRecipientInfoRef ri, int subIndex)
+{
+ CSSM_DATA_PTR enckey = NULL;
+
+ switch (ri->recipientInfoType) {
+ case SecCmsRecipientInfoIDKeyTrans:
+ /* ignore subIndex */
+ enckey = &(ri->ri.keyTransRecipientInfo.encKey);
+ break;
+ case SecCmsRecipientInfoIDKEK:
+ /* ignore subIndex */
+ enckey = &(ri->ri.kekRecipientInfo.encKey);
+ break;
+ case SecCmsRecipientInfoIDKeyAgree:
+ enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey);
+ break;
+ }
+ return enckey;
+}
+
+
+SECOidTag
+SecCmsRecipientInfoGetKeyEncryptionAlgorithmTag(SecCmsRecipientInfoRef ri)
+{
+ SECOidTag encalgtag = SEC_OID_UNKNOWN; /* an invalid encryption alg */
+
+ switch (ri->recipientInfoType) {
+ case SecCmsRecipientInfoIDKeyTrans:
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg));
+ break;
+ case SecCmsRecipientInfoIDKeyAgree:
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg));
+ break;
+ case SecCmsRecipientInfoIDKEK:
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.kekRecipientInfo.keyEncAlg));
+ break;
+ }
+ return encalgtag;
+}
+
+OSStatus
+SecCmsRecipientInfoWrapBulkKey(SecCmsRecipientInfoRef ri, SecSymmetricKeyRef bulkkey,
+ SECOidTag bulkalgtag)
+{
+ SecCertificateRef cert;
+ SECOidTag certalgtag;
+ OSStatus rv = SECSuccess;
+#if 0
+ CSSM_DATA_PTR params = NULL;
+#endif /* 0 */
+ SecCmsRecipientEncryptedKey *rek;
+ SecCmsOriginatorIdentifierOrKey *oiok;
+ const SECAlgorithmID *algid;
+ PLArenaPool *poolp;
+ SecCmsKeyTransRecipientInfoEx *extra = NULL;
+ Boolean usesSubjKeyID;
+ uint8 nullData[2] = {SEC_ASN1_NULL, 0};
+ SECItem nullItem;
+ SecCmsKeyAgreeRecipientInfo *kari;
+
+ poolp = ri->cmsg->poolp;
+ cert = ri->cert;
+ usesSubjKeyID = nss_cmsrecipientinfo_usessubjectkeyid(ri);
+ if (cert) {
+ rv = SecCertificateGetAlgorithmID(cert,&algid);
+ if (rv)
+ return SECFailure;
+ certalgtag = SECOID_GetAlgorithmTag(algid);
+ } else if (usesSubjKeyID) {
+ extra = &ri->ri.keyTransRecipientInfoEx;
+ /* sanity check */
+ PORT_Assert(extra->pubKey);
+ if (!extra->pubKey) {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+ rv = SecKeyGetAlgorithmID(extra->pubKey,&algid);
+ if (rv)
+ return SECFailure;
+ certalgtag = SECOID_GetAlgorithmTag(algid);
+ } else {
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
+ }
+
+ /* XXX set ri->recipientInfoType to the proper value here */
+ /* or should we look if it's been set already ? */
+
+ certalgtag = SECOID_GetAlgorithmTag(algid);
+ switch (certalgtag) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ /* wrap the symkey */
+ if (cert) {
+ rv = SecCmsUtilEncryptSymKeyRSA(poolp, cert, bulkkey,
+ &ri->ri.keyTransRecipientInfo.encKey);
+ if (rv != SECSuccess)
+ break;
+ } else if (usesSubjKeyID) {
+ PORT_Assert(extra != NULL);
+ rv = SecCmsUtilEncryptSymKeyRSAPubKey(poolp, extra->pubKey,
+ bulkkey, &ri->ri.keyTransRecipientInfo.encKey);
+ if (rv != SECSuccess)
+ break;
+ }
+
+ rv = SECOID_SetAlgorithmID(poolp, &(ri->ri.keyTransRecipientInfo.keyEncAlg), certalgtag, NULL);
+ break;
+#if 0
+ case SEC_OID_MISSI_KEA_DSS_OLD:
+ case SEC_OID_MISSI_KEA_DSS:
+ case SEC_OID_MISSI_KEA:
+ rv = SecCmsUtilEncryptSymKeyMISSI(poolp, cert, bulkkey,
+ bulkalgtag,
+ &ri->ri.keyTransRecipientInfo.encKey,
+ ¶ms, ri->cmsg->pwfn_arg);
+ if (rv != SECSuccess)
+ break;
+
+ /* here, we DO need to pass the params to the wrap function because, with
+ * RSA, there is no funny stuff going on with generation of IV vectors or so */
+ rv = SECOID_SetAlgorithmID(poolp, &(ri->ri.keyTransRecipientInfo.keyEncAlg), certalgtag, params);
+ break;
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */
+ rek = ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[0];
+ if (rek == NULL) {
+ rv = SECFailure;
+ break;
+ }
+
+ oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
+ PORT_Assert(oiok->identifierType == SecCmsOriginatorIDOrKeyOriginatorPublicKey);
+
+ /* see RFC2630 12.3.1.1 */
+ if (SECOID_SetAlgorithmID(poolp, &oiok->id.originatorPublicKey.algorithmIdentifier,
+ SEC_OID_X942_DIFFIE_HELMAN_KEY, NULL) != SECSuccess) {
+ rv = SECFailure;
+ break;
+ }
+
+ /* this will generate a key pair, compute the shared secret, */
+ /* derive a key and ukm for the keyEncAlg out of it, encrypt the bulk key with */
+ /* the keyEncAlg, set encKey, keyEncAlg, publicKey etc. */
+ rv = SecCmsUtilEncryptSymKeyESDH(poolp, cert, bulkkey,
+ &rek->encKey,
+ &ri->ri.keyAgreeRecipientInfo.ukm,
+ &ri->ri.keyAgreeRecipientInfo.keyEncAlg,
+ &oiok->id.originatorPublicKey.publicKey);
+
+ break;
+#endif /* 0 */
+
+ case SEC_OID_EC_PUBLIC_KEY:
+ /* These were set up in nss_cmsrecipientinfo_create() */
+ kari = &ri->ri.keyAgreeRecipientInfo;
+ rek = kari->recipientEncryptedKeys[0];
+ if (rek == NULL) {
+ rv = SECFailure;
+ break;
+ }
+
+ oiok = &(kari->originatorIdentifierOrKey);
+ PORT_Assert(oiok->identifierType == SecCmsOriginatorIDOrKeyOriginatorPublicKey);
+
+ /*
+ * RFC 3278 3.1.1 says this AlgId must contain NULL params which is contrary to
+ * any other use of the SEC_OID_EC_PUBLIC_KEY OID. So we provide one
+ * explicitly instead of mucking up the login in SECOID_SetAlgorithmID().
+ */
+ nullItem.Data = nullData;
+ nullItem.Length = 2;
+ if (SECOID_SetAlgorithmID(poolp, &oiok->id.originatorPublicKey.algorithmIdentifier,
+ SEC_OID_EC_PUBLIC_KEY, &nullItem) != SECSuccess) {
+ rv = SECFailure;
+ break;
+ }
+
+ /* this will generate a key pair, compute the shared secret, */
+ /* derive a key and ukm for the keyEncAlg out of it, encrypt the bulk key with */
+ /* the keyEncAlg, set encKey, keyEncAlg, publicKey etc. */
+ rv = SecCmsUtilEncryptSymKeyECDH(poolp, cert, bulkkey,
+ &rek->encKey,
+ &kari->ukm,
+ &kari->keyEncAlg,
+ &oiok->id.originatorPublicKey.publicKey);
+ /* this is a BIT STRING */
+ oiok->id.originatorPublicKey.publicKey.Length <<= 3;
+ break;
+
+ default:
+ /* other algorithms not supported yet */
+ /* NOTE that we do not support any KEK algorithm */
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ rv = SECFailure;
+ break;
+ }
+#if 0
+ if (freeSpki)
+ SECKEY_DestroySubjectPublicKeyInfo(freeSpki);
+#endif
+
+ return rv;
+}
+
+#ifdef NDEBUG
+#define dprintf(args...)
+#else
+#define dprintf(args...) printf(args)
+#endif
+
+SecSymmetricKeyRef
+SecCmsRecipientInfoUnwrapBulkKey(SecCmsRecipientInfoRef ri, int subIndex,
+ SecCertificateRef cert, SecPrivateKeyRef privkey, SECOidTag bulkalgtag)
+{
+ SecSymmetricKeyRef bulkkey = NULL;
+ SECAlgorithmID *encalg;
+ SECOidTag encalgtag;
+ CSSM_DATA_PTR enckey;
+ int error;
+
+ ri->cert = CERT_DupCertificate(cert);
+ /* mark the recipientInfo so we can find it later */
+
+ switch (ri->recipientInfoType) {
+ case SecCmsRecipientInfoIDKeyTrans:
+ encalg = &(ri->ri.keyTransRecipientInfo.keyEncAlg);
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg));
+ enckey = &(ri->ri.keyTransRecipientInfo.encKey); /* ignore subIndex */
+ switch (encalgtag) {
+ case SEC_OID_PKCS1_RSA_ENCRYPTION:
+ /* RSA encryption algorithm: */
+ /* get the symmetric (bulk) key by unwrapping it using our private key */
+ bulkkey = SecCmsUtilDecryptSymKeyRSA(privkey, enckey, bulkalgtag);
+ break;
+#if 0
+ case SEC_OID_NETSCAPE_SMIME_KEA:
+ /* FORTEZZA key exchange algorithm */
+ /* the supplemental data is in the parameters of encalg */
+ bulkkey = SecCmsUtilDecryptSymKeyMISSI(privkey, enckey, encalg, bulkalgtag, ri->cmsg->pwfn_arg);
+ break;
+#endif /* 0 */
+ default:
+ error = SEC_ERROR_UNSUPPORTED_KEYALG;
+ goto loser;
+ }
+ break;
+ case SecCmsRecipientInfoIDKeyAgree:
+ encalg = &(ri->ri.keyAgreeRecipientInfo.keyEncAlg);
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg));
+ enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey);
+ switch (encalgtag) {
+ case SEC_OID_X942_DIFFIE_HELMAN_KEY:
+ /* Diffie-Helman key exchange */
+ /* XXX not yet implemented */
+ /* XXX problem: SEC_OID_X942_DIFFIE_HELMAN_KEY points to a PKCS3 mechanism! */
+ /* we support ephemeral-static DH only, so if the recipientinfo */
+ /* has originator stuff in it, we punt (or do we? shouldn't be that hard...) */
+ /* first, we derive the KEK (a symkey!) using a Derive operation, then we get the */
+ /* content encryption key using a Unwrap op */
+ /* the derive operation has to generate the key using the algorithm in RFC2631 */
+ error = SEC_ERROR_UNSUPPORTED_KEYALG;
+ break;
+ case SEC_OID_DH_SINGLE_STD_SHA1KDF:
+ {
+ /* ephemeral-static ECDH */
+ SecCmsKeyAgreeRecipientInfo *kari = &ri->ri.keyAgreeRecipientInfo;
+ SecCmsOriginatorIdentifierOrKey *oiok = &kari->originatorIdentifierOrKey;
+ if(oiok->identifierType != SecCmsOriginatorIDOrKeyOriginatorPublicKey) {
+ dprintf("SEC_OID_EC_PUBLIC_KEY unwrap key: bad oiok.id\n");
+ goto loser;
+ }
+ SecCmsOriginatorPublicKey *opk = &oiok->id.originatorPublicKey;
+ /* FIXME - verify opk->algorithmIdentifier here? */
+ CSSM_DATA senderPubKey = opk->publicKey;
+ /* Bit string, convert here */
+ senderPubKey.Length = (senderPubKey.Length + 7) >> 3;
+ CSSM_DATA_PTR ukm = &kari->ukm;
+ bulkkey = SecCmsUtilDecryptSymKeyECDH(privkey, enckey, ukm, encalg, bulkalgtag, &senderPubKey);
+ break;
+ }
+ default:
+ error = SEC_ERROR_UNSUPPORTED_KEYALG;
+ goto loser;
+ }
+ break;
+ case SecCmsRecipientInfoIDKEK:
+ encalg = &(ri->ri.kekRecipientInfo.keyEncAlg);
+ encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.kekRecipientInfo.keyEncAlg));
+ enckey = &(ri->ri.kekRecipientInfo.encKey);
+ /* not supported yet */
+ error = SEC_ERROR_UNSUPPORTED_KEYALG;
+ goto loser;
+ break;
+ }
+ /* XXXX continue here */
+ return bulkkey;
+
+loser:
+ return NULL;
+}