#include "cmslocal.h"
#include "cert.h"
#include "SecAsn1Item.h"
#include "secoid.h"
#include <security_asn1/secasn1.h>
#include <security_asn1/secerr.h>
#include <security_asn1/secport.h>
#include <Security/SecKeyPriv.h>
#include <Security/SecCertificatePriv.h>
#include <Security/SecCertificateInternal.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(SecCmsEnvelopedDataRef envd, SecCmsRecipientIDSelector type,
SecCertificateRef cert, SecPublicKeyRef pubKey,
const SecAsn1Item *subjKeyID)
{
SecCmsRecipientInfoRef ri;
void *mark;
SECOidTag certalgtag;
OSStatus rv = SECSuccess;
SecCmsRecipientEncryptedKey *rek;
SecCmsOriginatorIdentifierOrKey *oiok;
unsigned long version;
SecAsn1Item * dummy;
PLArenaPool *poolp;
const SECAlgorithmID *algid;
SECAlgorithmID freeAlgID;
SecCmsRecipientIdentifier *rid;
poolp = envd->contentInfo.cmsg->poolp;
mark = PORT_ArenaMark(poolp);
ri = (SecCmsRecipientInfoRef)PORT_ArenaZAlloc(poolp, sizeof(SecCmsRecipientInfo));
if (ri == NULL)
goto loser;
ri->envelopedData = envd;
#if USE_CDSA_CRYPTO
if (type == SecCmsRecipientIDIssuerSN)
{
rv = SecCertificateGetAlgorithmID(cert,&algid);
} else {
PORT_Assert(pubKey);
rv = SecKeyGetAlgorithmID(pubKey,&algid);
}
#else
ri->cert = CERT_DupCertificate(cert);
if (ri->cert == NULL)
goto loser;
const SecAsn1AlgId *length_data_swapped = (const SecAsn1AlgId *)SecCertificateGetPublicKeyAlgorithm(cert);
freeAlgID.algorithm.Length = (size_t)length_data_swapped->algorithm.Data;
freeAlgID.algorithm.Data = (uint8_t *)length_data_swapped->algorithm.Length;
freeAlgID.parameters.Length = (size_t)length_data_swapped->parameters.Data;
freeAlgID.parameters.Data = (uint8_t *)length_data_swapped->parameters.Length;
algid = &freeAlgID;
#endif
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){
rid->id.subjectKeyID = PORT_ArenaNew(poolp, SecAsn1Item);
if (rid->id.subjectKeyID == NULL) {
rv = SECFailure;
PORT_SetError(SEC_ERROR_NO_MEMORY);
break;
}
if (SECITEM_CopyItem(poolp, rid->id.subjectKeyID, subjKeyID)) {
rv = SECFailure;
PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
break;
}
if (rid->id.subjectKeyID->Data == 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;
}
ri->recipientInfoType = SecCmsRecipientInfoIDKeyTrans;
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:
PORT_Assert(type != SecCmsRecipientIDSubjectKeyID);
if (type == SecCmsRecipientIDSubjectKeyID) {
rv = SECFailure;
break;
}
ri->recipientInfoType = SecCmsRecipientInfoIDKeyAgree;
if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
rv = SECFailure;
break;
}
if ((rek = SecCmsRecipientEncryptedKeyCreate(poolp)) == NULL) {
rv = SECFailure;
break;
}
rek->recipientIdentifier.identifierType = SecCmsKeyAgreeRecipientIDIssuerSN;
if ((rek->recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) {
rv = SECFailure;
break;
}
oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
oiok->identifierType = SecCmsOriginatorIDOrKeyOriginatorPublicKey;
rv = SecCmsArrayAdd(poolp, (void ***)&ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys,
(void *)rek);
break;
case SEC_OID_EC_PUBLIC_KEY:
PORT_Assert(type != SecCmsRecipientIDSubjectKeyID);
if (type == SecCmsRecipientIDSubjectKeyID) {
rv = SECFailure;
break;
}
ri->recipientInfoType = SecCmsRecipientInfoIDKeyAgree;
ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert);
if (ri->ri.keyTransRecipientInfo.recipientIdentifier.id.issuerAndSN == NULL) {
rv = SECFailure;
break;
}
if ((rek = SecCmsRecipientEncryptedKeyCreate(poolp)) == NULL) {
rv = SECFailure;
break;
}
rek->recipientIdentifier.identifierType = SecCmsKeyAgreeRecipientIDIssuerSN;
if ((rek->recipientIdentifier.id.issuerAndSN = CERT_GetCertIssuerAndSN(poolp, cert)) == NULL) {
rv = SECFailure;
break;
}
oiok = &(ri->ri.keyAgreeRecipientInfo.originatorIdentifierOrKey);
oiok->identifierType = SecCmsOriginatorIDOrKeyOriginatorPublicKey;
rv = SecCmsArrayAdd(poolp, (void ***)&ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys,
(void *)rek);
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
rv = SECFailure;
break;
}
if (rv == SECFailure)
goto loser;
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:
dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.kekRecipientInfo.version),
SEC_CMS_KEK_RECIPIENT_INFO_VERSION);
if (dummy == NULL)
goto loser;
break;
}
if (SecCmsEnvelopedDataAddRecipient(envd, ri))
goto loser;
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;
}
SecCmsRecipientInfoRef
SecCmsRecipientInfoCreate(SecCmsEnvelopedDataRef envd, SecCertificateRef cert)
{
#if 0
SecCmsRecipientInfoRef info = SecCmsRecipientInfoCreateWithSubjKeyIDFromCert(envd, cert);
if (info)
return info;
else
#endif
return nss_cmsrecipientinfo_create(envd, SecCmsRecipientIDIssuerSN, cert,
NULL, NULL);
}
SecCmsRecipientInfoRef
SecCmsRecipientInfoCreateWithSubjKeyID(SecCmsEnvelopedDataRef envd,
const SecAsn1Item * subjKeyID,
SecPublicKeyRef pubKey)
{
return nss_cmsrecipientinfo_create(envd, SecCmsRecipientIDSubjectKeyID,
NULL, pubKey, subjKeyID);
}
SecCmsRecipientInfoRef
SecCmsRecipientInfoCreateWithSubjKeyIDFromCert(SecCmsEnvelopedDataRef envd,
SecCertificateRef cert)
{
SecPublicKeyRef pubKey = NULL;
SecAsn1Item subjKeyID = {0, NULL};
SecCmsRecipientInfoRef retVal = NULL;
CFDataRef subjectKeyIDData = NULL;
if (!envd || !cert) {
return NULL;
}
subjectKeyIDData = SecCertificateGetSubjectKeyID(cert);
if (!subjectKeyIDData)
goto done;
subjKeyID.Length =
CFDataGetLength(subjectKeyIDData);
subjKeyID.Data = (uint8_t *)CFDataGetBytePtr(subjectKeyIDData);
retVal = nss_cmsrecipientinfo_create(envd, SecCmsRecipientIDSubjectKeyID,
cert, pubKey, &subjKeyID);
done:
return retVal;
}
void
SecCmsRecipientInfoDestroy(SecCmsRecipientInfoRef ri)
{
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);
}
}
int
SecCmsRecipientInfoGetVersion(SecCmsRecipientInfoRef ri)
{
unsigned long version;
SecAsn1Item * versionitem = NULL;
switch (ri->recipientInfoType) {
case SecCmsRecipientInfoIDKeyTrans:
versionitem = &(ri->ri.keyTransRecipientInfo.version);
break;
case SecCmsRecipientInfoIDKEK:
versionitem = &(ri->ri.kekRecipientInfo.version);
break;
case SecCmsRecipientInfoIDKeyAgree:
versionitem = &(ri->ri.keyAgreeRecipientInfo.version);
break;
}
PORT_Assert(versionitem);
if (versionitem == NULL)
return 0;
if (SEC_ASN1DecodeInteger(versionitem, &version) != SECSuccess)
return 0;
else
return (int)version;
}
SecAsn1Item *
SecCmsRecipientInfoGetEncryptedKey(SecCmsRecipientInfoRef ri, int subIndex)
{
SecAsn1Item * enckey = NULL;
switch (ri->recipientInfoType) {
case SecCmsRecipientInfoIDKeyTrans:
enckey = &(ri->ri.keyTransRecipientInfo.encKey);
break;
case SecCmsRecipientInfoIDKEK:
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;
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;
SecCmsRecipientEncryptedKey *rek;
SecCmsOriginatorIdentifierOrKey *oiok;
const SECAlgorithmID *algid;
SECAlgorithmID freeAlgID;
PLArenaPool *poolp;
uint8_t nullData[2] = {SEC_ASN1_NULL, 0};
SECItem nullItem;
SecCmsKeyAgreeRecipientInfo *kari;
poolp = ri->envelopedData->contentInfo.cmsg->poolp;
cert = ri->cert;
if (cert) {
const SecAsn1AlgId *length_data_swapped = (const SecAsn1AlgId *)SecCertificateGetPublicKeyAlgorithm(cert);
freeAlgID.algorithm.Length = (size_t)length_data_swapped->algorithm.Data;
freeAlgID.algorithm.Data = (uint8_t *)length_data_swapped->algorithm.Length;
freeAlgID.parameters.Length = (size_t)length_data_swapped->parameters.Data;
freeAlgID.parameters.Data = (uint8_t *)length_data_swapped->parameters.Length;
algid = &freeAlgID;
} else {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
certalgtag = SECOID_GetAlgorithmTag(algid);
switch (certalgtag) {
case SEC_OID_PKCS1_RSA_ENCRYPTION:
if (cert) {
rv = SecCmsUtilEncryptSymKeyRSA(poolp, cert, bulkkey,
&ri->ri.keyTransRecipientInfo.encKey);
if (rv != SECSuccess)
break;
}
rv = SECOID_SetAlgorithmID(poolp, &(ri->ri.keyTransRecipientInfo.keyEncAlg), certalgtag, NULL);
break;
case SEC_OID_EC_PUBLIC_KEY:
kari = &ri->ri.keyAgreeRecipientInfo;
rek = kari->recipientEncryptedKeys[0];
if (rek == NULL) {
rv = SECFailure;
break;
}
oiok = &(kari->originatorIdentifierOrKey);
PORT_Assert(oiok->identifierType == SecCmsOriginatorIDOrKeyOriginatorPublicKey);
nullItem.Data = nullData;
nullItem.Length = 2;
if (SECOID_SetAlgorithmID(poolp, &oiok->id.originatorPublicKey.algorithmIdentifier,
SEC_OID_EC_PUBLIC_KEY, &nullItem) != SECSuccess) {
rv = SECFailure;
break;
}
rv = SecCmsUtilEncryptSymKeyECDH(poolp, cert, bulkkey,
&rek->encKey,
&kari->ukm,
&kari->keyEncAlg,
&oiok->id.originatorPublicKey.publicKey);
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
rv = SECFailure;
break;
}
return rv;
}
#ifdef NDEBUG
#define dprintf(args...)
#else
#define dprintf(args...) fprintf(stderr, args)
#endif
SecSymmetricKeyRef
SecCmsRecipientInfoUnwrapBulkKey(SecCmsRecipientInfoRef ri, int subIndex,
SecCertificateRef cert, SecPrivateKeyRef privkey, SECOidTag bulkalgtag)
{
SecSymmetricKeyRef bulkkey = NULL;
SECAlgorithmID *encalg;
SECOidTag encalgtag;
SecAsn1Item * enckey;
int error;
ri->cert = CERT_DupCertificate(cert);
switch (ri->recipientInfoType) {
case SecCmsRecipientInfoIDKeyTrans:
encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg));
enckey = &(ri->ri.keyTransRecipientInfo.encKey);
switch (encalgtag) {
case SEC_OID_PKCS1_RSA_ENCRYPTION:
bulkkey = SecCmsUtilDecryptSymKeyRSA(privkey, enckey, bulkalgtag);
break;
default:
error = SEC_ERROR_UNSUPPORTED_KEYALG;
goto loser;
}
break;
case SecCmsRecipientInfoIDKeyAgree:
encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg));
switch (encalgtag) {
case SEC_OID_X942_DIFFIE_HELMAN_KEY:
error = SEC_ERROR_UNSUPPORTED_KEYALG;
goto loser;
case SEC_OID_DH_SINGLE_STD_SHA1KDF:
{
enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey);
encalg = &(ri->ri.keyAgreeRecipientInfo.keyEncAlg);
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");
error = SEC_ERROR_LIBRARY_FAILURE;
goto loser;
}
SecCmsOriginatorPublicKey *opk = &oiok->id.originatorPublicKey;
SecAsn1Item senderPubKey = opk->publicKey;
SecAsn1Item *ukm = &kari->ukm;
bulkkey = SecCmsUtilDecryptSymKeyECDH(privkey, enckey, ukm, encalg, bulkalgtag, &senderPubKey);
break;
}
default:
error = SEC_ERROR_UNSUPPORTED_KEYALG;
goto loser;
}
break;
case SecCmsRecipientInfoIDKEK:
error = SEC_ERROR_UNSUPPORTED_KEYALG;
goto loser;
}
return bulkkey;
loser:
PORT_SetError(error);
return NULL;
}