michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * CMS recipientInfo methods. michael@0: */ michael@0: michael@0: #include "cmslocal.h" michael@0: michael@0: #include "cert.h" michael@0: #include "key.h" michael@0: #include "secasn1.h" michael@0: #include "secitem.h" michael@0: #include "secoid.h" michael@0: #include "pk11func.h" michael@0: #include "secerr.h" michael@0: michael@0: PRBool michael@0: nss_cmsrecipientinfo_usessubjectkeyid(NSSCMSRecipientInfo *ri) michael@0: { michael@0: if (ri->recipientInfoType == NSSCMSRecipientInfoID_KeyTrans) { michael@0: NSSCMSRecipientIdentifier *rid; michael@0: rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier; michael@0: if (rid->identifierType == NSSCMSRecipientID_SubjectKeyID) { michael@0: return PR_TRUE; michael@0: } michael@0: } michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: /* michael@0: * NOTE: fakeContent marks CMSMessage structure which is only used as a carrier michael@0: * of pwfn_arg and arena pools. In an ideal world, NSSCMSMessage would not have michael@0: * been exported, and we would have added an ordinary enum to handle this michael@0: * check. Unfortunatly wo don't have that luxury so we are overloading the michael@0: * contentTypeTag field. NO code should every try to interpret this content tag michael@0: * as a real OID tag, or use any fields other than pwfn_arg or poolp of this michael@0: * CMSMessage for that matter */ michael@0: static const SECOidData fakeContent; michael@0: NSSCMSRecipientInfo * michael@0: nss_cmsrecipientinfo_create(NSSCMSMessage *cmsg, michael@0: NSSCMSRecipientIDSelector type, michael@0: CERTCertificate *cert, michael@0: SECKEYPublicKey *pubKey, michael@0: SECItem *subjKeyID, michael@0: void* pwfn_arg, michael@0: SECItem* DERinput) michael@0: { michael@0: NSSCMSRecipientInfo *ri; michael@0: void *mark; michael@0: SECOidTag certalgtag; michael@0: SECStatus rv = SECSuccess; michael@0: NSSCMSRecipientEncryptedKey *rek; michael@0: NSSCMSOriginatorIdentifierOrKey *oiok; michael@0: unsigned long version; michael@0: SECItem *dummy; michael@0: PLArenaPool *poolp; michael@0: CERTSubjectPublicKeyInfo *spki, *freeSpki = NULL; michael@0: NSSCMSRecipientIdentifier *rid; michael@0: extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[]; michael@0: michael@0: if (!cmsg) { michael@0: /* a CMSMessage wasn't supplied, create a fake one to hold the pwfunc michael@0: * and a private arena pool */ michael@0: cmsg = NSS_CMSMessage_Create(NULL); michael@0: cmsg->pwfn_arg = pwfn_arg; michael@0: /* mark it as a special cms message */ michael@0: cmsg->contentInfo.contentTypeTag = (SECOidData *)&fakeContent; michael@0: } michael@0: michael@0: poolp = cmsg->poolp; michael@0: michael@0: mark = PORT_ArenaMark(poolp); michael@0: michael@0: ri = (NSSCMSRecipientInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSRecipientInfo)); michael@0: if (ri == NULL) michael@0: goto loser; michael@0: michael@0: ri->cmsg = cmsg; michael@0: michael@0: if (DERinput) { michael@0: /* decode everything from DER */ michael@0: SECItem newinput; michael@0: SECStatus rv = SECITEM_CopyItem(poolp, &newinput, DERinput); michael@0: if (SECSuccess != rv) michael@0: goto loser; michael@0: rv = SEC_QuickDERDecodeItem(poolp, ri, NSSCMSRecipientInfoTemplate, &newinput); michael@0: if (SECSuccess != rv) michael@0: goto loser; michael@0: } michael@0: michael@0: switch (type) { michael@0: case NSSCMSRecipientID_IssuerSN: michael@0: { michael@0: ri->cert = CERT_DupCertificate(cert); michael@0: if (NULL == ri->cert) michael@0: goto loser; michael@0: spki = &(cert->subjectPublicKeyInfo); michael@0: break; michael@0: } michael@0: michael@0: case NSSCMSRecipientID_SubjectKeyID: michael@0: { michael@0: PORT_Assert(pubKey); michael@0: spki = freeSpki = SECKEY_CreateSubjectPublicKeyInfo(pubKey); michael@0: break; michael@0: } michael@0: michael@0: case NSSCMSRecipientID_BrandNew: michael@0: goto done; michael@0: break; michael@0: michael@0: default: michael@0: /* unkown type */ michael@0: goto loser; michael@0: break; michael@0: } michael@0: michael@0: certalgtag = SECOID_GetAlgorithmTag(&(spki->algorithm)); michael@0: michael@0: rid = &ri->ri.keyTransRecipientInfo.recipientIdentifier; michael@0: switch (certalgtag) { michael@0: case SEC_OID_PKCS1_RSA_ENCRYPTION: michael@0: ri->recipientInfoType = NSSCMSRecipientInfoID_KeyTrans; michael@0: rid->identifierType = type; michael@0: if (type == NSSCMSRecipientID_IssuerSN) { michael@0: rid->id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert); michael@0: if (rid->id.issuerAndSN == NULL) { michael@0: break; michael@0: } michael@0: } else if (type == NSSCMSRecipientID_SubjectKeyID){ michael@0: NSSCMSKeyTransRecipientInfoEx *riExtra; michael@0: michael@0: rid->id.subjectKeyID = PORT_ArenaNew(poolp, SECItem); michael@0: if (rid->id.subjectKeyID == NULL) { michael@0: rv = SECFailure; michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: break; michael@0: } michael@0: SECITEM_CopyItem(poolp, rid->id.subjectKeyID, subjKeyID); michael@0: if (rid->id.subjectKeyID->data == NULL) { michael@0: rv = SECFailure; michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: break; michael@0: } michael@0: riExtra = &ri->ri.keyTransRecipientInfoEx; michael@0: riExtra->version = 0; michael@0: riExtra->pubKey = SECKEY_CopyPublicKey(pubKey); michael@0: if (riExtra->pubKey == NULL) { michael@0: rv = SECFailure; michael@0: PORT_SetError(SEC_ERROR_NO_MEMORY); michael@0: break; michael@0: } michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: rv = SECFailure; michael@0: } michael@0: break; michael@0: case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */ michael@0: PORT_Assert(type == NSSCMSRecipientID_IssuerSN); michael@0: if (type != NSSCMSRecipientID_IssuerSN) { michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: /* a key agreement op */ michael@0: ri->recipientInfoType = NSSCMSRecipientInfoID_KeyAgree; michael@0: michael@0: if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) { michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: /* we do not support the case where multiple recipients michael@0: * share the same KeyAgreeRecipientInfo and have multiple RecipientEncryptedKeys michael@0: * in this case, we would need to walk all the recipientInfos, take the michael@0: * ones that do KeyAgreement algorithms and join them, algorithm by algorithm michael@0: * Then, we'd generate ONE ukm and OriginatorIdentifierOrKey */ michael@0: michael@0: /* only epheremal-static Diffie-Hellman is supported for now michael@0: * this is the only form of key agreement that provides potential anonymity michael@0: * of the sender, plus we do not have to include certs in the message */ michael@0: michael@0: /* force single recipientEncryptedKey for now */ michael@0: if ((rek = NSS_CMSRecipientEncryptedKey_Create(poolp)) == NULL) { michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: michael@0: /* hardcoded IssuerSN choice for now */ michael@0: rek->recipientIdentifier.identifierType = NSSCMSKeyAgreeRecipientID_IssuerSN; michael@0: if ((rek->recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) { michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: michael@0: oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey); michael@0: michael@0: /* see RFC2630 12.3.1.1 */ michael@0: oiok->identifierType = NSSCMSOriginatorIDOrKey_OriginatorPublicKey; michael@0: michael@0: rv = NSS_CMSArray_Add(poolp, (void ***)&ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys, michael@0: (void *)rek); michael@0: michael@0: break; michael@0: default: michael@0: /* other algorithms not supported yet */ michael@0: /* NOTE that we do not support any KEK algorithm */ michael@0: PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: michael@0: if (rv == SECFailure) michael@0: goto loser; michael@0: michael@0: /* set version */ michael@0: switch (ri->recipientInfoType) { michael@0: case NSSCMSRecipientInfoID_KeyTrans: michael@0: if (ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType == NSSCMSRecipientID_IssuerSN) michael@0: version = NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_ISSUERSN; michael@0: else michael@0: version = NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_SUBJKEY; michael@0: dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyTransRecipientInfo.version), version); michael@0: if (dummy == NULL) michael@0: goto loser; michael@0: break; michael@0: case NSSCMSRecipientInfoID_KeyAgree: michael@0: dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyAgreeRecipientInfo.version), michael@0: NSS_CMS_KEYAGREE_RECIPIENT_INFO_VERSION); michael@0: if (dummy == NULL) michael@0: goto loser; michael@0: break; michael@0: case NSSCMSRecipientInfoID_KEK: michael@0: /* NOTE: this cannot happen as long as we do not support any KEK algorithm */ michael@0: dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.kekRecipientInfo.version), michael@0: NSS_CMS_KEK_RECIPIENT_INFO_VERSION); michael@0: if (dummy == NULL) michael@0: goto loser; michael@0: break; michael@0: michael@0: } michael@0: michael@0: done: michael@0: PORT_ArenaUnmark (poolp, mark); michael@0: if (freeSpki) michael@0: SECKEY_DestroySubjectPublicKeyInfo(freeSpki); michael@0: return ri; michael@0: michael@0: loser: michael@0: if (ri && ri->cert) { michael@0: CERT_DestroyCertificate(ri->cert); michael@0: } michael@0: if (freeSpki) { michael@0: SECKEY_DestroySubjectPublicKeyInfo(freeSpki); michael@0: } michael@0: PORT_ArenaRelease (poolp, mark); michael@0: if (cmsg->contentInfo.contentTypeTag == &fakeContent) { michael@0: NSS_CMSMessage_Destroy(cmsg); michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: /* michael@0: * NSS_CMSRecipientInfo_Create - create a recipientinfo michael@0: * michael@0: * we currently do not create KeyAgreement recipientinfos with multiple michael@0: * recipientEncryptedKeys the certificate is supposed to have been michael@0: * verified by the caller michael@0: */ michael@0: NSSCMSRecipientInfo * michael@0: NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert) michael@0: { michael@0: return nss_cmsrecipientinfo_create(cmsg, NSSCMSRecipientID_IssuerSN, cert, michael@0: NULL, NULL, NULL, NULL); michael@0: } michael@0: michael@0: NSSCMSRecipientInfo * michael@0: NSS_CMSRecipientInfo_CreateNew(void* pwfn_arg) michael@0: { michael@0: return nss_cmsrecipientinfo_create(NULL, NSSCMSRecipientID_BrandNew, NULL, michael@0: NULL, NULL, pwfn_arg, NULL); michael@0: } michael@0: michael@0: NSSCMSRecipientInfo * michael@0: NSS_CMSRecipientInfo_CreateFromDER(SECItem* input, void* pwfn_arg) michael@0: { michael@0: return nss_cmsrecipientinfo_create(NULL, NSSCMSRecipientID_BrandNew, NULL, michael@0: NULL, NULL, pwfn_arg, input); michael@0: } michael@0: michael@0: michael@0: NSSCMSRecipientInfo * michael@0: NSS_CMSRecipientInfo_CreateWithSubjKeyID(NSSCMSMessage *cmsg, michael@0: SECItem *subjKeyID, michael@0: SECKEYPublicKey *pubKey) michael@0: { michael@0: return nss_cmsrecipientinfo_create(cmsg, NSSCMSRecipientID_SubjectKeyID, michael@0: NULL, pubKey, subjKeyID, NULL, NULL); michael@0: } michael@0: michael@0: NSSCMSRecipientInfo * michael@0: NSS_CMSRecipientInfo_CreateWithSubjKeyIDFromCert(NSSCMSMessage *cmsg, michael@0: CERTCertificate *cert) michael@0: { michael@0: SECKEYPublicKey *pubKey = NULL; michael@0: SECItem subjKeyID = {siBuffer, NULL, 0}; michael@0: NSSCMSRecipientInfo *retVal = NULL; michael@0: michael@0: if (!cmsg || !cert) { michael@0: return NULL; michael@0: } michael@0: pubKey = CERT_ExtractPublicKey(cert); michael@0: if (!pubKey) { michael@0: goto done; michael@0: } michael@0: if (CERT_FindSubjectKeyIDExtension(cert, &subjKeyID) != SECSuccess || michael@0: subjKeyID.data == NULL) { michael@0: goto done; michael@0: } michael@0: retVal = NSS_CMSRecipientInfo_CreateWithSubjKeyID(cmsg, &subjKeyID, pubKey); michael@0: done: michael@0: if (pubKey) michael@0: SECKEY_DestroyPublicKey(pubKey); michael@0: michael@0: if (subjKeyID.data) michael@0: SECITEM_FreeItem(&subjKeyID, PR_FALSE); michael@0: michael@0: return retVal; michael@0: } michael@0: michael@0: void michael@0: NSS_CMSRecipientInfo_Destroy(NSSCMSRecipientInfo *ri) michael@0: { michael@0: if (!ri) { michael@0: return; michael@0: } michael@0: /* version was allocated on the pool, so no need to destroy it */ michael@0: /* issuerAndSN was allocated on the pool, so no need to destroy it */ michael@0: if (ri->cert != NULL) michael@0: CERT_DestroyCertificate(ri->cert); michael@0: michael@0: if (nss_cmsrecipientinfo_usessubjectkeyid(ri)) { michael@0: NSSCMSKeyTransRecipientInfoEx *extra; michael@0: extra = &ri->ri.keyTransRecipientInfoEx; michael@0: if (extra->pubKey) michael@0: SECKEY_DestroyPublicKey(extra->pubKey); michael@0: } michael@0: if (ri->cmsg && ri->cmsg->contentInfo.contentTypeTag == &fakeContent) { michael@0: NSS_CMSMessage_Destroy(ri->cmsg); michael@0: } michael@0: michael@0: /* we're done. */ michael@0: } michael@0: michael@0: int michael@0: NSS_CMSRecipientInfo_GetVersion(NSSCMSRecipientInfo *ri) michael@0: { michael@0: unsigned long version; michael@0: SECItem *versionitem = NULL; michael@0: michael@0: switch (ri->recipientInfoType) { michael@0: case NSSCMSRecipientInfoID_KeyTrans: michael@0: /* ignore subIndex */ michael@0: versionitem = &(ri->ri.keyTransRecipientInfo.version); michael@0: break; michael@0: case NSSCMSRecipientInfoID_KEK: michael@0: /* ignore subIndex */ michael@0: versionitem = &(ri->ri.kekRecipientInfo.version); michael@0: break; michael@0: case NSSCMSRecipientInfoID_KeyAgree: michael@0: versionitem = &(ri->ri.keyAgreeRecipientInfo.version); michael@0: break; michael@0: } michael@0: michael@0: PORT_Assert(versionitem); michael@0: if (versionitem == NULL) michael@0: return 0; michael@0: michael@0: /* always take apart the SECItem */ michael@0: if (SEC_ASN1DecodeInteger(versionitem, &version) != SECSuccess) michael@0: return 0; michael@0: else michael@0: return (int)version; michael@0: } michael@0: michael@0: SECItem * michael@0: NSS_CMSRecipientInfo_GetEncryptedKey(NSSCMSRecipientInfo *ri, int subIndex) michael@0: { michael@0: SECItem *enckey = NULL; michael@0: michael@0: switch (ri->recipientInfoType) { michael@0: case NSSCMSRecipientInfoID_KeyTrans: michael@0: /* ignore subIndex */ michael@0: enckey = &(ri->ri.keyTransRecipientInfo.encKey); michael@0: break; michael@0: case NSSCMSRecipientInfoID_KEK: michael@0: /* ignore subIndex */ michael@0: enckey = &(ri->ri.kekRecipientInfo.encKey); michael@0: break; michael@0: case NSSCMSRecipientInfoID_KeyAgree: michael@0: enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey); michael@0: break; michael@0: } michael@0: return enckey; michael@0: } michael@0: michael@0: michael@0: SECOidTag michael@0: NSS_CMSRecipientInfo_GetKeyEncryptionAlgorithmTag(NSSCMSRecipientInfo *ri) michael@0: { michael@0: SECOidTag encalgtag = SEC_OID_UNKNOWN; /* an invalid encryption alg */ michael@0: michael@0: switch (ri->recipientInfoType) { michael@0: case NSSCMSRecipientInfoID_KeyTrans: michael@0: encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg)); michael@0: break; michael@0: case NSSCMSRecipientInfoID_KeyAgree: michael@0: encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg)); michael@0: break; michael@0: case NSSCMSRecipientInfoID_KEK: michael@0: encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.kekRecipientInfo.keyEncAlg)); michael@0: break; michael@0: } michael@0: return encalgtag; michael@0: } michael@0: michael@0: SECStatus michael@0: NSS_CMSRecipientInfo_WrapBulkKey(NSSCMSRecipientInfo *ri, PK11SymKey *bulkkey, michael@0: SECOidTag bulkalgtag) michael@0: { michael@0: CERTCertificate *cert; michael@0: SECOidTag certalgtag; michael@0: SECStatus rv = SECSuccess; michael@0: NSSCMSRecipientEncryptedKey *rek; michael@0: NSSCMSOriginatorIdentifierOrKey *oiok; michael@0: CERTSubjectPublicKeyInfo *spki, *freeSpki = NULL; michael@0: PLArenaPool *poolp; michael@0: NSSCMSKeyTransRecipientInfoEx *extra = NULL; michael@0: PRBool usesSubjKeyID; michael@0: michael@0: poolp = ri->cmsg->poolp; michael@0: cert = ri->cert; michael@0: usesSubjKeyID = nss_cmsrecipientinfo_usessubjectkeyid(ri); michael@0: if (cert) { michael@0: spki = &cert->subjectPublicKeyInfo; michael@0: certalgtag = SECOID_GetAlgorithmTag(&(spki->algorithm)); michael@0: } else if (usesSubjKeyID) { michael@0: extra = &ri->ri.keyTransRecipientInfoEx; michael@0: /* sanity check */ michael@0: PORT_Assert(extra->pubKey); michael@0: if (!extra->pubKey) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: spki = freeSpki = SECKEY_CreateSubjectPublicKeyInfo(extra->pubKey); michael@0: certalgtag = SECOID_GetAlgorithmTag(&spki->algorithm); michael@0: } else { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: return SECFailure; michael@0: } michael@0: michael@0: /* XXX set ri->recipientInfoType to the proper value here */ michael@0: /* or should we look if it's been set already ? */ michael@0: michael@0: certalgtag = SECOID_GetAlgorithmTag(&spki->algorithm); michael@0: switch (certalgtag) { michael@0: case SEC_OID_PKCS1_RSA_ENCRYPTION: michael@0: /* wrap the symkey */ michael@0: if (cert) { michael@0: rv = NSS_CMSUtil_EncryptSymKey_RSA(poolp, cert, bulkkey, michael@0: &ri->ri.keyTransRecipientInfo.encKey); michael@0: if (rv != SECSuccess) michael@0: break; michael@0: } else if (usesSubjKeyID) { michael@0: PORT_Assert(extra != NULL); michael@0: rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, extra->pubKey, michael@0: bulkkey, &ri->ri.keyTransRecipientInfo.encKey); michael@0: if (rv != SECSuccess) michael@0: break; michael@0: } michael@0: michael@0: rv = SECOID_SetAlgorithmID(poolp, &(ri->ri.keyTransRecipientInfo.keyEncAlg), certalgtag, NULL); michael@0: break; michael@0: case SEC_OID_X942_DIFFIE_HELMAN_KEY: /* dh-public-number */ michael@0: rek = ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[0]; michael@0: if (rek == NULL) { michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: michael@0: oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey); michael@0: PORT_Assert(oiok->identifierType == NSSCMSOriginatorIDOrKey_OriginatorPublicKey); michael@0: michael@0: /* see RFC2630 12.3.1.1 */ michael@0: if (SECOID_SetAlgorithmID(poolp, &oiok->id.originatorPublicKey.algorithmIdentifier, michael@0: SEC_OID_X942_DIFFIE_HELMAN_KEY, NULL) != SECSuccess) { michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: michael@0: /* this will generate a key pair, compute the shared secret, */ michael@0: /* derive a key and ukm for the keyEncAlg out of it, encrypt the bulk key with */ michael@0: /* the keyEncAlg, set encKey, keyEncAlg, publicKey etc. */ michael@0: rv = NSS_CMSUtil_EncryptSymKey_ESDH(poolp, cert, bulkkey, michael@0: &rek->encKey, michael@0: &ri->ri.keyAgreeRecipientInfo.ukm, michael@0: &ri->ri.keyAgreeRecipientInfo.keyEncAlg, michael@0: &oiok->id.originatorPublicKey.publicKey); michael@0: michael@0: break; michael@0: default: michael@0: /* other algorithms not supported yet */ michael@0: /* NOTE that we do not support any KEK algorithm */ michael@0: PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); michael@0: rv = SECFailure; michael@0: break; michael@0: } michael@0: if (freeSpki) michael@0: SECKEY_DestroySubjectPublicKeyInfo(freeSpki); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: PK11SymKey * michael@0: NSS_CMSRecipientInfo_UnwrapBulkKey(NSSCMSRecipientInfo *ri, int subIndex, michael@0: CERTCertificate *cert, SECKEYPrivateKey *privkey, SECOidTag bulkalgtag) michael@0: { michael@0: PK11SymKey *bulkkey = NULL; michael@0: SECAlgorithmID *encalg; michael@0: SECOidTag encalgtag; michael@0: SECItem *enckey; michael@0: int error; michael@0: michael@0: ri->cert = CERT_DupCertificate(cert); michael@0: /* mark the recipientInfo so we can find it later */ michael@0: michael@0: switch (ri->recipientInfoType) { michael@0: case NSSCMSRecipientInfoID_KeyTrans: michael@0: encalg = &(ri->ri.keyTransRecipientInfo.keyEncAlg); michael@0: encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg)); michael@0: enckey = &(ri->ri.keyTransRecipientInfo.encKey); /* ignore subIndex */ michael@0: switch (encalgtag) { michael@0: case SEC_OID_PKCS1_RSA_ENCRYPTION: michael@0: /* RSA encryption algorithm: */ michael@0: /* get the symmetric (bulk) key by unwrapping it using our private key */ michael@0: bulkkey = NSS_CMSUtil_DecryptSymKey_RSA(privkey, enckey, bulkalgtag); michael@0: break; michael@0: default: michael@0: error = SEC_ERROR_UNSUPPORTED_KEYALG; michael@0: goto loser; michael@0: } michael@0: break; michael@0: case NSSCMSRecipientInfoID_KeyAgree: michael@0: encalg = &(ri->ri.keyAgreeRecipientInfo.keyEncAlg); michael@0: encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg)); michael@0: enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey); michael@0: switch (encalgtag) { michael@0: case SEC_OID_X942_DIFFIE_HELMAN_KEY: michael@0: /* Diffie-Helman key exchange */ michael@0: /* XXX not yet implemented */ michael@0: /* XXX problem: SEC_OID_X942_DIFFIE_HELMAN_KEY points to a PKCS3 mechanism! */ michael@0: /* we support ephemeral-static DH only, so if the recipientinfo */ michael@0: /* has originator stuff in it, we punt (or do we? shouldn't be that hard...) */ michael@0: /* first, we derive the KEK (a symkey!) using a Derive operation, then we get the */ michael@0: /* content encryption key using a Unwrap op */ michael@0: /* the derive operation has to generate the key using the algorithm in RFC2631 */ michael@0: error = SEC_ERROR_UNSUPPORTED_KEYALG; michael@0: goto loser; michael@0: break; michael@0: default: michael@0: error = SEC_ERROR_UNSUPPORTED_KEYALG; michael@0: goto loser; michael@0: } michael@0: break; michael@0: case NSSCMSRecipientInfoID_KEK: michael@0: encalg = &(ri->ri.kekRecipientInfo.keyEncAlg); michael@0: encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.kekRecipientInfo.keyEncAlg)); michael@0: enckey = &(ri->ri.kekRecipientInfo.encKey); michael@0: /* not supported yet */ michael@0: error = SEC_ERROR_UNSUPPORTED_KEYALG; michael@0: goto loser; michael@0: break; michael@0: } michael@0: /* XXXX continue here */ michael@0: return bulkkey; michael@0: michael@0: loser: michael@0: PORT_SetError(error); michael@0: return NULL; michael@0: } michael@0: michael@0: SECStatus NSS_CMSRecipientInfo_GetCertAndKey(NSSCMSRecipientInfo *ri, michael@0: CERTCertificate** retcert, michael@0: SECKEYPrivateKey** retkey) michael@0: { michael@0: CERTCertificate* cert = NULL; michael@0: NSSCMSRecipient** recipients = NULL; michael@0: NSSCMSRecipientInfo* recipientInfos[2]; michael@0: SECStatus rv = SECSuccess; michael@0: SECKEYPrivateKey* key = NULL; michael@0: michael@0: if (!ri) michael@0: return SECFailure; michael@0: michael@0: if (!retcert && !retkey) { michael@0: /* nothing requested, nothing found, success */ michael@0: return SECSuccess; michael@0: } michael@0: michael@0: if (retcert) { michael@0: *retcert = NULL; michael@0: } michael@0: if (retkey) { michael@0: *retkey = NULL; michael@0: } michael@0: michael@0: if (ri->cert) { michael@0: cert = CERT_DupCertificate(ri->cert); michael@0: if (!cert) { michael@0: rv = SECFailure; michael@0: } michael@0: } michael@0: if (SECSuccess == rv && !cert) { michael@0: /* we don't have the cert, we have to look for it */ michael@0: /* first build an NSS_CMSRecipient */ michael@0: recipientInfos[0] = ri; michael@0: recipientInfos[1] = NULL; michael@0: michael@0: recipients = nss_cms_recipient_list_create(recipientInfos); michael@0: if (recipients) { michael@0: /* now look for the cert and key */ michael@0: if (0 == PK11_FindCertAndKeyByRecipientListNew(recipients, michael@0: ri->cmsg->pwfn_arg)) { michael@0: cert = CERT_DupCertificate(recipients[0]->cert); michael@0: key = SECKEY_CopyPrivateKey(recipients[0]->privkey); michael@0: } else { michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: nss_cms_recipient_list_destroy(recipients); michael@0: } michael@0: else { michael@0: rv = SECFailure; michael@0: } michael@0: } else if (SECSuccess == rv && cert && retkey) { michael@0: /* we have the cert, we just need the key now */ michael@0: key = PK11_FindPrivateKeyFromCert(cert->slot, cert, ri->cmsg->pwfn_arg); michael@0: } michael@0: if (retcert) { michael@0: *retcert = cert; michael@0: } else { michael@0: if (cert) { michael@0: CERT_DestroyCertificate(cert); michael@0: } michael@0: } michael@0: if (retkey) { michael@0: *retkey = key; michael@0: } else { michael@0: if (key) { michael@0: SECKEY_DestroyPrivateKey(key); michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: SECStatus NSS_CMSRecipientInfo_Encode(PLArenaPool* poolp, michael@0: const NSSCMSRecipientInfo *src, michael@0: SECItem* returned) michael@0: { michael@0: extern const SEC_ASN1Template NSSCMSRecipientInfoTemplate[]; michael@0: SECStatus rv = SECFailure; michael@0: if (!src || !returned) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ARGS); michael@0: } else if (SEC_ASN1EncodeItem(poolp, returned, src, michael@0: NSSCMSRecipientInfoTemplate)) { michael@0: rv = SECSuccess; michael@0: } michael@0: return rv; michael@0: }