diff options
Diffstat (limited to 'security/nss/lib/smime/cmsrecinfo.c')
-rw-r--r-- | security/nss/lib/smime/cmsrecinfo.c | 428 |
1 files changed, 428 insertions, 0 deletions
diff --git a/security/nss/lib/smime/cmsrecinfo.c b/security/nss/lib/smime/cmsrecinfo.c new file mode 100644 index 000000000..dfe05a07a --- /dev/null +++ b/security/nss/lib/smime/cmsrecinfo.c @@ -0,0 +1,428 @@ +/* + * 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. + * + * $Id$ + */ + +#include "cmslocal.h" + +#include "cert.h" +#include "key.h" +#include "secasn1.h" +#include "secitem.h" +#include "secoid.h" +#include "pk11func.h" +#include "secerr.h" + +/* + * NSS_CMSRecipientInfo_Create - create a recipientinfo + * + * we currently do not create KeyAgreement recipientinfos with multiple recipientEncryptedKeys + * the certificate is supposed to have been verified by the caller + */ +NSSCMSRecipientInfo * +NSS_CMSRecipientInfo_Create(NSSCMSMessage *cmsg, CERTCertificate *cert) +{ + NSSCMSRecipientInfo *ri; + void *mark; + SECOidTag certalgtag; + SECStatus rv = SECSuccess; + NSSCMSRecipientEncryptedKey *rek; + NSSCMSOriginatorIdentifierOrKey *oiok; + unsigned long version; + SECItem *dummy; + PLArenaPool *poolp; + + poolp = cmsg->poolp; + + mark = PORT_ArenaMark(poolp); + + ri = (NSSCMSRecipientInfo *)PORT_ArenaZAlloc(poolp, sizeof(NSSCMSRecipientInfo)); + if (ri == NULL) + goto loser; + + ri->cmsg = cmsg; + ri->cert = CERT_DupCertificate(cert); + if (ri->cert == NULL) + goto loser; + + certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); + + switch (certalgtag) { + case SEC_OID_PKCS1_RSA_ENCRYPTION: + ri->recipientInfoType = NSSCMSRecipientInfoID_KeyTrans; + /* hardcoded issuerSN choice for now */ + ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType = NSSCMSRecipientID_IssuerSN; + 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_MISSI_KEA_DSS_OLD: + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_KEA: + /* backward compatibility - this is not really a keytrans operation */ + ri->recipientInfoType = NSSCMSRecipientInfoID_KeyTrans; + /* hardcoded issuerSN choice for now */ + ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType = NSSCMSRecipientID_IssuerSN; + 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 */ + /* a key agreement op */ + ri->recipientInfoType = NSSCMSRecipientInfoID_KeyAgree; + + 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 = NSS_CMSRecipientEncryptedKey_Create(poolp)) == NULL) { + rv = SECFailure; + break; + } + + /* hardcoded IssuerSN choice for now */ + rek->recipientIdentifier.identifierType = NSSCMSKeyAgreeRecipientID_IssuerSN; + 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 = NSSCMSOriginatorIDOrKey_OriginatorPublicKey; + + rv = NSS_CMSArray_Add(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 NSSCMSRecipientInfoID_KeyTrans: + if (ri->ri.keyTransRecipientInfo.recipientIdentifier.identifierType == NSSCMSRecipientID_IssuerSN) + version = NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_ISSUERSN; + else + version = NSS_CMS_KEYTRANS_RECIPIENT_INFO_VERSION_SUBJKEY; + dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyTransRecipientInfo.version), version); + if (dummy == NULL) + goto loser; + break; + case NSSCMSRecipientInfoID_KeyAgree: + dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.keyAgreeRecipientInfo.version), + NSS_CMS_KEYAGREE_RECIPIENT_INFO_VERSION); + if (dummy == NULL) + goto loser; + break; + case NSSCMSRecipientInfoID_KEK: + /* NOTE: this cannot happen as long as we do not support any KEK algorithm */ + dummy = SEC_ASN1EncodeInteger(poolp, &(ri->ri.kekRecipientInfo.version), + NSS_CMS_KEK_RECIPIENT_INFO_VERSION); + if (dummy == NULL) + goto loser; + break; + + } + + PORT_ArenaUnmark (poolp, mark); + return ri; + +loser: + PORT_ArenaRelease (poolp, mark); + return NULL; +} + +void +NSS_CMSRecipientInfo_Destroy(NSSCMSRecipientInfo *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); + /* recipientInfo structure itself was allocated on the pool, so no need to destroy it */ + /* we're done. */ +} + +int +NSS_CMSRecipientInfo_GetVersion(NSSCMSRecipientInfo *ri) +{ + unsigned long version; + SECItem *versionitem = NULL; + + switch (ri->recipientInfoType) { + case NSSCMSRecipientInfoID_KeyTrans: + /* ignore subIndex */ + versionitem = &(ri->ri.keyTransRecipientInfo.version); + break; + case NSSCMSRecipientInfoID_KEK: + /* ignore subIndex */ + versionitem = &(ri->ri.kekRecipientInfo.version); + break; + case NSSCMSRecipientInfoID_KeyAgree: + versionitem = &(ri->ri.keyAgreeRecipientInfo.version); + break; + } + + PORT_Assert(versionitem); + if (versionitem == NULL) + return 0; + + /* always take apart the SECItem */ + if (SEC_ASN1DecodeInteger(versionitem, &version) != SECSuccess) + return 0; + else + return (int)version; +} + +SECItem * +NSS_CMSRecipientInfo_GetEncryptedKey(NSSCMSRecipientInfo *ri, int subIndex) +{ + SECItem *enckey = NULL; + + switch (ri->recipientInfoType) { + case NSSCMSRecipientInfoID_KeyTrans: + /* ignore subIndex */ + enckey = &(ri->ri.keyTransRecipientInfo.encKey); + break; + case NSSCMSRecipientInfoID_KEK: + /* ignore subIndex */ + enckey = &(ri->ri.kekRecipientInfo.encKey); + break; + case NSSCMSRecipientInfoID_KeyAgree: + enckey = &(ri->ri.keyAgreeRecipientInfo.recipientEncryptedKeys[subIndex]->encKey); + break; + } + return enckey; +} + + +SECOidTag +NSS_CMSRecipientInfo_GetKeyEncryptionAlgorithmTag(NSSCMSRecipientInfo *ri) +{ + SECOidTag encalgtag = SEC_OID_SHA1; /* set to not a valid encryption alg */ + + switch (ri->recipientInfoType) { + case NSSCMSRecipientInfoID_KeyTrans: + encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyTransRecipientInfo.keyEncAlg)); + break; + case NSSCMSRecipientInfoID_KeyAgree: + encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.keyAgreeRecipientInfo.keyEncAlg)); + break; + case NSSCMSRecipientInfoID_KEK: + encalgtag = SECOID_GetAlgorithmTag(&(ri->ri.kekRecipientInfo.keyEncAlg)); + break; + } + return encalgtag; +} + +SECStatus +NSS_CMSRecipientInfo_WrapBulkKey(NSSCMSRecipientInfo *ri, PK11SymKey *bulkkey, SECOidTag bulkalgtag) +{ + CERTCertificate *cert; + SECOidTag certalgtag; + SECStatus rv = SECSuccess; + SECItem *params = NULL; + NSSCMSRecipientEncryptedKey *rek; + NSSCMSOriginatorIdentifierOrKey *oiok; + PLArenaPool *poolp; + + poolp = ri->cmsg->poolp; + cert = ri->cert; + PORT_Assert (cert != NULL); + if (cert == NULL) + return SECFailure; + + /* XXX set ri->recipientInfoType to the proper value here */ + /* or should we look if it's been set already ? */ + + certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm)); + switch (certalgtag) { + case SEC_OID_PKCS1_RSA_ENCRYPTION: + /* wrap the symkey */ + if (NSS_CMSUtil_EncryptSymKey_RSA(poolp, cert, bulkkey, &ri->ri.keyTransRecipientInfo.encKey) != SECSuccess) { + rv = SECFailure; + break; + } + + rv = SECOID_SetAlgorithmID(poolp, &(ri->ri.keyTransRecipientInfo.keyEncAlg), certalgtag, NULL); + break; + case SEC_OID_MISSI_KEA_DSS_OLD: + case SEC_OID_MISSI_KEA_DSS: + case SEC_OID_MISSI_KEA: + rv = NSS_CMSUtil_EncryptSymKey_MISSI(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 == NSSCMSOriginatorIDOrKey_OriginatorPublicKey); + + /* 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 = NSS_CMSUtil_EncryptSymKey_ESDH(poolp, cert, bulkkey, + &rek->encKey, + &ri->ri.keyAgreeRecipientInfo.ukm, + &ri->ri.keyAgreeRecipientInfo.keyEncAlg, + &oiok->id.originatorPublicKey.publicKey); + + 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; + } + return rv; +} + +PK11SymKey * +NSS_CMSRecipientInfo_UnwrapBulkKey(NSSCMSRecipientInfo *ri, int subIndex, + CERTCertificate *cert, SECKEYPrivateKey *privkey, SECOidTag bulkalgtag) +{ + PK11SymKey *bulkkey = NULL; + SECAlgorithmID *encalg; + SECOidTag encalgtag; + SECItem *enckey; + int error; + + ri->cert = CERT_DupCertificate(cert); + /* mark the recipientInfo so we can find it later */ + + switch (ri->recipientInfoType) { + case NSSCMSRecipientInfoID_KeyTrans: + 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 = NSS_CMSUtil_DecryptSymKey_RSA(privkey, enckey, bulkalgtag); + break; + case SEC_OID_NETSCAPE_SMIME_KEA: + /* FORTEZZA key exchange algorithm */ + /* the supplemental data is in the parameters of encalg */ + bulkkey = NSS_CMSUtil_DecryptSymKey_MISSI(privkey, enckey, encalg, bulkalgtag, ri->cmsg->pwfn_arg); + break; + default: + error = SEC_ERROR_UNSUPPORTED_KEYALG; + goto loser; + } + break; + case NSSCMSRecipientInfoID_KeyAgree: + 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; + default: + error = SEC_ERROR_UNSUPPORTED_KEYALG; + goto loser; + } + break; + case NSSCMSRecipientInfoID_KEK: + 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; +} |