summaryrefslogtreecommitdiffstats
path: root/nss/lib/smime/cmspubkey.c
diff options
context:
space:
mode:
Diffstat (limited to 'nss/lib/smime/cmspubkey.c')
-rw-r--r--nss/lib/smime/cmspubkey.c597
1 files changed, 480 insertions, 117 deletions
diff --git a/nss/lib/smime/cmspubkey.c b/nss/lib/smime/cmspubkey.c
index 8f18f60..5ff8306 100644
--- a/nss/lib/smime/cmspubkey.c
+++ b/nss/lib/smime/cmspubkey.c
@@ -15,6 +15,9 @@
#include "secoid.h"
#include "pk11func.h"
#include "secerr.h"
+#include "secder.h"
+#include "prerr.h"
+#include "sechash.h"
/* ====== RSA ======================================================================= */
@@ -106,180 +109,540 @@ NSS_CMSUtil_DecryptSymKey_RSA(SECKEYPrivateKey *privkey, SECItem *encKey, SECOid
return PK11_PubUnwrapSymKey(privkey, encKey, target, CKA_DECRYPT, 0);
}
-/* ====== ESDH (Ephemeral-Static Diffie-Hellman) ==================================== */
+typedef struct RSA_OAEP_CMS_paramsStr RSA_OAEP_CMS_params;
+struct RSA_OAEP_CMS_paramsStr {
+ SECAlgorithmID *hashFunc;
+ SECAlgorithmID *maskGenFunc;
+ SECAlgorithmID *pSourceFunc;
+};
+
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
+
+static const SEC_ASN1Template seckey_PointerToAlgorithmIDTemplate[] = {
+ { SEC_ASN1_POINTER | SEC_ASN1_XTRN, 0,
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }
+};
+
+static const SEC_ASN1Template RSA_OAEP_CMS_paramsTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(RSA_OAEP_CMS_params) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONTEXT_SPECIFIC | 0,
+ offsetof(RSA_OAEP_CMS_params, hashFunc),
+ seckey_PointerToAlgorithmIDTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONTEXT_SPECIFIC | 1,
+ offsetof(RSA_OAEP_CMS_params, maskGenFunc),
+ seckey_PointerToAlgorithmIDTemplate },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
+ SEC_ASN1_CONTEXT_SPECIFIC | 2,
+ offsetof(RSA_OAEP_CMS_params, pSourceFunc),
+ seckey_PointerToAlgorithmIDTemplate },
+ { 0 }
+};
+/*
+ * NSS_CMSUtil_DecryptSymKey_RSA_OAEP - unwrap a RSA-wrapped symmetric key
+ *
+ * this function takes an RSA-OAEP-wrapped symmetric key and unwraps it, returning a symmetric
+ * key handle. Please note that the actual unwrapped key data may not be allowed to leave
+ * a hardware token...
+ */
+PK11SymKey *
+NSS_CMSUtil_DecryptSymKey_RSA_OAEP(SECKEYPrivateKey *privkey, SECItem *parameters, SECItem *encKey, SECOidTag bulkalgtag)
+{
+ CK_RSA_PKCS_OAEP_PARAMS oaep_params;
+ RSA_OAEP_CMS_params encoded_params;
+ SECAlgorithmID mgf1hashAlg;
+ SECOidTag mgfAlgtag, pSourcetag;
+ SECItem encoding_params, params;
+ PK11SymKey *bulkkey = NULL;
+ SECStatus rv;
+
+ CK_MECHANISM_TYPE target;
+ PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN);
+ target = PK11_AlgtagToMechanism(bulkalgtag);
+ if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ return NULL;
+ }
+
+ PORT_Memset(&encoded_params, 0, sizeof(RSA_OAEP_CMS_params));
+ PORT_Memset(&mgf1hashAlg, 0, sizeof(SECAlgorithmID));
+ PORT_Memset(&encoding_params, 0, sizeof(SECItem));
+
+ /* Set default values for the OAEP parameters */
+ oaep_params.hashAlg = CKM_SHA_1;
+ oaep_params.mgf = CKG_MGF1_SHA1;
+ oaep_params.source = CKZ_DATA_SPECIFIED;
+ oaep_params.pSourceData = NULL;
+ oaep_params.ulSourceDataLen = 0;
+
+ if (parameters->len == 2) {
+ /* For some reason SEC_ASN1DecodeItem cannot process parameters if it is an emtpy SEQUENCE */
+ /* Just check that this is a properly encoded empty SEQUENCE */
+ /* TODO: Investigate if there a better way to handle this as part of decoding. */
+ if ((parameters->data[0] != 0x30) || (parameters->data[1] != 0)) {
+ return NULL;
+ }
+ } else {
+ rv = SEC_ASN1DecodeItem(NULL, &encoded_params, RSA_OAEP_CMS_paramsTemplate, parameters);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ if (encoded_params.hashFunc != NULL) {
+ oaep_params.hashAlg = PK11_AlgtagToMechanism(SECOID_GetAlgorithmTag(encoded_params.hashFunc));
+ }
+ if (encoded_params.maskGenFunc != NULL) {
+ mgfAlgtag = SECOID_GetAlgorithmTag(encoded_params.maskGenFunc);
+ if (mgfAlgtag != SEC_OID_PKCS1_MGF1) {
+ /* MGF1 is the only supported mask generation function */
+ goto loser;
+ }
+ /* The parameters field contains an AlgorithmIdentifier specifying the
+ * hash function to use with MGF1.
+ */
+ rv = SEC_ASN1DecodeItem(NULL, &mgf1hashAlg, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
+ &encoded_params.maskGenFunc->parameters);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ oaep_params.mgf = SEC_GetMgfTypeByOidTag(SECOID_GetAlgorithmTag(&mgf1hashAlg));
+ if (!oaep_params.mgf) {
+ goto loser;
+ }
+ }
+ if (encoded_params.pSourceFunc != NULL) {
+ pSourcetag = SECOID_GetAlgorithmTag(encoded_params.pSourceFunc);
+ if (pSourcetag != SEC_OID_PKCS1_PSPECIFIED) {
+ goto loser;
+ }
+ /* The encoding parameters (P) are provided as an OCTET STRING in the parameters field. */
+ if (encoded_params.pSourceFunc->parameters.len != 0) {
+ rv = SEC_ASN1DecodeItem(NULL, &encoding_params, SEC_ASN1_GET(SEC_OctetStringTemplate),
+ &encoded_params.pSourceFunc->parameters);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ oaep_params.ulSourceDataLen = encoding_params.len;
+ oaep_params.pSourceData = encoding_params.data;
+ }
+ }
+ }
+ params.type = siBuffer;
+ params.data = (unsigned char *)&oaep_params;
+ params.len = sizeof(CK_RSA_PKCS_OAEP_PARAMS);
+ bulkkey = PK11_PubUnwrapSymKeyWithMechanism(privkey, CKM_RSA_PKCS_OAEP, &params,
+ encKey, target, CKA_DECRYPT, 0);
+
+loser:
+ PORT_Free(oaep_params.pSourceData);
+ if (encoded_params.hashFunc) {
+ SECOID_DestroyAlgorithmID(encoded_params.hashFunc, PR_TRUE);
+ }
+ if (encoded_params.maskGenFunc) {
+ SECOID_DestroyAlgorithmID(encoded_params.maskGenFunc, PR_TRUE);
+ }
+ if (encoded_params.pSourceFunc) {
+ SECOID_DestroyAlgorithmID(encoded_params.pSourceFunc, PR_TRUE);
+ }
+ SECOID_DestroyAlgorithmID(&mgf1hashAlg, PR_FALSE);
+ return bulkkey;
+}
+
+/* ====== ESECDH (Ephemeral-Static Elliptic Curve Diffie-Hellman) =========== */
+
+typedef struct ECC_CMS_SharedInfoStr ECC_CMS_SharedInfo;
+struct ECC_CMS_SharedInfoStr {
+ SECAlgorithmID *keyInfo; /* key-encryption algorithm */
+ SECItem entityUInfo; /* ukm */
+ SECItem suppPubInfo; /* length of KEK in bits as 32-bit number */
+};
+
+static const SEC_ASN1Template ECC_CMS_SharedInfoTemplate[] = {
+ { SEC_ASN1_SEQUENCE,
+ 0, NULL, sizeof(ECC_CMS_SharedInfo) },
+ { SEC_ASN1_XTRN | SEC_ASN1_POINTER,
+ offsetof(ECC_CMS_SharedInfo, keyInfo),
+ SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+ { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+ offsetof(ECC_CMS_SharedInfo, entityUInfo),
+ SEC_ASN1_SUB(SEC_OctetStringTemplate) },
+ { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
+ SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2,
+ offsetof(ECC_CMS_SharedInfo, suppPubInfo),
+ SEC_ASN1_SUB(SEC_OctetStringTemplate) },
+ { 0 }
+};
+
+/* Inputs:
+ * keyInfo: key-encryption algorithm
+ * ukm: ukm field of KeyAgreeRecipientInfo
+ * KEKsize: length of KEK in bytes
+ *
+ * Output: DER encoded ECC-CMS-SharedInfo
+ */
+static SECItem *
+Create_ECC_CMS_SharedInfo(PLArenaPool *poolp,
+ SECAlgorithmID *keyInfo, SECItem *ukm, unsigned int KEKsize)
+{
+ ECC_CMS_SharedInfo SI;
+ unsigned char suppPubInfo[4] = { 0 };
+
+ SI.keyInfo = keyInfo;
+ SI.entityUInfo.type = ukm->type;
+ SI.entityUInfo.data = ukm->data;
+ SI.entityUInfo.len = ukm->len;
+
+ SI.suppPubInfo.type = siBuffer;
+ SI.suppPubInfo.data = suppPubInfo;
+ SI.suppPubInfo.len = sizeof(suppPubInfo);
+
+ if (KEKsize > 0x1FFFFFFF) { // ensure KEKsize * 8 fits in 4 bytes.
+ return NULL;
+ }
+ KEKsize *= 8;
+ suppPubInfo[0] = (KEKsize & 0xFF000000) >> 24;
+ suppPubInfo[1] = (KEKsize & 0xFF0000) >> 16;
+ suppPubInfo[2] = (KEKsize & 0xFF00) >> 8;
+ suppPubInfo[3] = KEKsize & 0xFF;
+
+ return SEC_ASN1EncodeItem(poolp, NULL, &SI, ECC_CMS_SharedInfoTemplate);
+}
+
+/* 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.
+ * The ukm is optional per RFC 5753. Pass a NULL value to request an empty ukm.
+ * Pass a SECItem with the size set to zero, to request allocating a random
+ * ukm of a default size. Or provide an explicit ukm that was defined by the caller.
+ */
SECStatus
-NSS_CMSUtil_EncryptSymKey_ESDH(PLArenaPool *poolp, CERTCertificate *cert, PK11SymKey *key,
- SECItem *encKey, SECItem **ukm, SECAlgorithmID *keyEncAlg,
- SECItem *pubKey)
+NSS_CMSUtil_EncryptSymKey_ESECDH(PLArenaPool *poolp, CERTCertificate *cert,
+ PK11SymKey *bulkkey, SECItem *encKey,
+ PRBool genUkm, SECItem *ukm,
+ SECAlgorithmID *keyEncAlg, SECItem *pubKey,
+ void *wincx)
{
-#if 0 /* not yet done */
- SECOidTag certalgtag; /* the certificate's encryption algorithm */
- SECOidTag encalgtag; /* the algorithm used for key exchange/agreement */
+ SECOidTag certalgtag; /* the certificate's encryption algorithm */
SECStatus rv;
- SECItem *params = NULL;
- int data_len;
SECStatus err;
- PK11SymKey *tek;
- CERTCertificate *ourCert;
- SECKEYPublicKey *ourPubKey;
- NSSCMSKEATemplateSelector whichKEA = NSSCMSKEAInvalid;
+ PK11SymKey *kek;
+ SECKEYPublicKey *publickey = NULL;
+ SECKEYPublicKey *ourPubKey = NULL;
+ SECKEYPrivateKey *ourPrivKey = NULL;
+ unsigned int bulkkey_size, kek_size;
+ SECAlgorithmID keyWrapAlg;
+ SECOidTag keyEncAlgtag;
+ SECItem keyWrapAlg_params, *keyEncAlg_params, *SharedInfo;
+ CK_MECHANISM_TYPE keyDerivationType, keyWrapMech;
+ CK_ULONG kdf;
+
+ if (genUkm && (ukm->len != 0 || ukm->data != NULL)) {
+ PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
+ return SECFailure;
+ }
certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
- PORT_Assert(certalgtag == SEC_OID_X942_DIFFIE_HELMAN_KEY);
-
- /* We really want to show our KEA tag as the key exchange algorithm tag. */
- encalgtag = SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN;
+ PORT_Assert(certalgtag == SEC_OID_ANSIX962_EC_PUBLIC_KEY);
+ if (certalgtag != SEC_OID_ANSIX962_EC_PUBLIC_KEY) {
+ return SECFailure;
+ }
/* Get the public key of the recipient. */
publickey = CERT_ExtractPublicKey(cert);
- if (publickey == NULL) goto loser;
-
- /* XXXX generate a DH key pair on a PKCS11 module (XXX which parameters?) */
- /* XXXX */ourCert = PK11_FindBestKEAMatch(cert, wincx);
- if (ourCert == NULL) goto loser;
+ if (publickey == NULL)
+ goto loser;
- arena = PORT_NewArena(1024);
- if (arena == NULL) goto loser;
+ ourPrivKey = SECKEY_CreateECPrivateKey(&publickey->u.ec.DEREncodedParams,
+ &ourPubKey, wincx);
+ if (!ourPrivKey || !ourPubKey) {
+ goto loser;
+ }
- /* While we're here, extract the key pair's public key data and copy it into */
- /* the outgoing parameters. */
- /* XXXX */ourPubKey = CERT_ExtractPublicKey(ourCert);
- if (ourPubKey == NULL)
- {
+ rv = SECITEM_CopyItem(poolp, pubKey, &ourPubKey->u.ec.publicValue);
+ if (rv != SECSuccess) {
goto loser;
}
- SECITEM_CopyItem(arena, pubKey, /* XXX */&(ourPubKey->u.fortezza.KEAKey));
+
+ /* pubKey will be encoded as a BIT STRING, so pubKey->len must be length in bits
+ * rather than bytes */
+ pubKey->len = pubKey->len * 8;
+
SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */
ourPubKey = NULL;
- /* Extract our private key in order to derive the KEA key. */
- ourPrivKey = PK11_FindKeyByAnyCert(ourCert,wincx);
- CERT_DestroyCertificate(ourCert); /* we're done with this */
- if (!ourPrivKey) goto loser;
+ bulkkey_size = PK11_GetKeyLength(bulkkey);
+ if (bulkkey_size == 0) {
+ goto loser;
+ }
+
+ /* Parameters are supposed to be absent for AES key wrap algorithms.
+ * However, Microsoft Outlook cannot decrypt message unless
+ * parameters field is NULL. */
+ keyWrapAlg_params.len = 2;
+ keyWrapAlg_params.data = (unsigned char *)PORT_ArenaAlloc(poolp, keyWrapAlg_params.len);
+ if (keyWrapAlg_params.data == NULL)
+ goto loser;
- /* If ukm desired, prepare it - allocate enough space (filled with zeros). */
- if (ukm) {
- ukm->data = (unsigned char*)PORT_ArenaZAlloc(arena,/* XXXX */);
- ukm->len = /* XXXX */;
+ keyWrapAlg_params.data[0] = SEC_ASN1_NULL;
+ keyWrapAlg_params.data[1] = 0;
+
+ /* RFC5753 specifies id-aes128-wrap as the mandatory to support algorithm.
+ * So, use id-aes128-wrap unless bulkkey provides more than 128 bits of
+ * security. If bulkkey provides more than 128 bits of security, use
+ * the algorithms from KE Set 2 in RFC 6318. */
+
+ /* TODO: NIST Special Publication SP 800-56A requires the use of
+ * Cofactor ECDH. However, RFC 6318 specifies the use of
+ * dhSinglePass-stdDH-sha256kdf-scheme or dhSinglePass-stdDH-sha384kdf-scheme
+ * for Suite B. The reason for this is that all of the NIST recommended
+ * prime curves in FIPS 186-3 (including the Suite B curves) have a cofactor
+ * of 1, and so for these curves standard and cofactor ECDH are the same.
+ * This code should really look at the key's parameters and choose cofactor
+ * ECDH if the curve has a cofactor other than 1. */
+ if ((PK11_GetMechanism(bulkkey) == CKM_AES_CBC) && (bulkkey_size > 16)) {
+ rv = SECOID_SetAlgorithmID(poolp, &keyWrapAlg, SEC_OID_AES_256_KEY_WRAP, &keyWrapAlg_params);
+ kek_size = 32;
+ keyWrapMech = CKM_NSS_AES_KEY_WRAP;
+ kdf = CKD_SHA384_KDF;
+ keyEncAlgtag = SEC_OID_DHSINGLEPASS_STDDH_SHA384KDF_SCHEME;
+ keyDerivationType = CKM_ECDH1_DERIVE;
+ } else {
+ rv = SECOID_SetAlgorithmID(poolp, &keyWrapAlg, SEC_OID_AES_128_KEY_WRAP, &keyWrapAlg_params);
+ kek_size = 16;
+ keyWrapMech = CKM_NSS_AES_KEY_WRAP;
+ kdf = CKD_SHA256_KDF;
+ keyEncAlgtag = SEC_OID_DHSINGLEPASS_STDDH_SHA256KDF_SCHEME;
+ keyDerivationType = CKM_ECDH1_DERIVE;
+ }
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* ukm is optional, but RFC 5753 says that originators SHOULD include the ukm.
+ * I made ukm 64 bytes, since RFC 2631 states that UserKeyingMaterial must
+ * contain 512 bits for Diffie-Hellman key agreement. */
+
+ if (genUkm) {
+ ukm->type = siBuffer;
+ ukm->len = 64;
+ ukm->data = (unsigned char *)PORT_ArenaAlloc(poolp, ukm->len);
+
+ if (ukm->data == NULL) {
+ goto loser;
+ }
+ rv = PK11_GenerateRandom(ukm->data, ukm->len);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ }
+
+ SharedInfo = Create_ECC_CMS_SharedInfo(poolp, &keyWrapAlg,
+ ukm,
+ kek_size);
+ if (!SharedInfo) {
+ goto loser;
}
- /* Generate the KEK (key exchange key) according to RFC2631 which we use
+ /* Generate the KEK (key exchange key) according to RFC5753 which we use
* to wrap the bulk encryption key. */
- kek = PK11_PubDerive(ourPrivKey, publickey, PR_TRUE,
- ukm, NULL,
- /* XXXX */CKM_KEA_KEY_DERIVE, /* XXXX */CKM_SKIPJACK_WRAP,
- CKA_WRAP, 0, wincx);
+ kek = PK11_PubDeriveWithKDF(ourPrivKey, publickey, PR_TRUE,
+ NULL, NULL,
+ keyDerivationType, keyWrapMech,
+ CKA_WRAP, kek_size, kdf, SharedInfo, NULL);
+ if (!kek) {
+ goto loser;
+ }
SECKEY_DestroyPublicKey(publickey);
SECKEY_DestroyPrivateKey(ourPrivKey);
publickey = NULL;
ourPrivKey = NULL;
- if (!kek)
- goto loser;
-
/* allocate space for the encrypted CEK (bulk key) */
- encKey->data = (unsigned char*)PORT_ArenaAlloc(poolp, SMIME_FORTEZZA_MAX_KEY_SIZE);
- encKey->len = SMIME_FORTEZZA_MAX_KEY_SIZE;
+ /* AES key wrap produces an output 64-bits longer than
+ * the input AES CEK (RFC 3565, Section 2.3.2) */
+ encKey->len = bulkkey_size + 8;
+ encKey->data = (unsigned char *)PORT_ArenaAlloc(poolp, encKey->len);
- if (encKey->data == NULL)
- {
+ if (encKey->data == NULL) {
PK11_FreeSymKey(kek);
goto loser;
}
+ err = PK11_WrapSymKey(keyWrapMech, NULL, kek, bulkkey, encKey);
- /* Wrap the bulk key using CMSRC2WRAP or CMS3DESWRAP, depending on the */
- /* bulk encryption algorithm */
- switch (/* XXXX */PK11_AlgtagToMechanism(enccinfo->encalg))
- {
- case /* XXXX */CKM_SKIPJACK_CFB8:
- err = PK11_WrapSymKey(/* XXXX */CKM_CMS3DES_WRAP, NULL, kek, bulkkey, encKey);
- whichKEA = NSSCMSKEAUsesSkipjack;
- break;
- case /* XXXX */CKM_SKIPJACK_CFB8:
- err = PK11_WrapSymKey(/* XXXX */CKM_CMSRC2_WRAP, NULL, kek, bulkkey, encKey);
- whichKEA = NSSCMSKEAUsesSkipjack;
- break;
- default:
- /* XXXX what do we do here? Neither RC2 nor 3DES... */
- err = SECFailure;
- /* set error */
- break;
- }
-
- PK11_FreeSymKey(kek); /* we do not need the KEK anymore */
- if (err != SECSuccess)
+ PK11_FreeSymKey(kek); /* we do not need the KEK anymore */
+ if (err != SECSuccess) {
goto loser;
+ }
- PORT_Assert(whichKEA != NSSCMSKEAInvalid);
-
- /* see RFC2630 12.3.1.1 "keyEncryptionAlgorithm must be ..." */
- /* params is the DER encoded key wrap algorithm (with parameters!) (XXX) */
- params = SEC_ASN1EncodeItem(arena, NULL, &keaParams, sec_pkcs7_get_kea_template(whichKEA));
- if (params == NULL)
+ keyEncAlg_params = SEC_ASN1EncodeItem(poolp, NULL, &keyWrapAlg, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
+ if (keyEncAlg_params == NULL)
goto loser;
+ keyEncAlg_params->type = siBuffer;
/* now set keyEncAlg */
- rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, SEC_OID_CMS_EPHEMERAL_STATIC_DIFFIE_HELLMAN, params);
- if (rv != SECSuccess)
+ rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, keyEncAlgtag, keyEncAlg_params);
+ if (rv != SECSuccess) {
goto loser;
+ }
+
+ return SECSuccess;
- /* XXXXXXX this is not right yet */
loser:
- if (arena) {
- PORT_FreeArena(arena, PR_FALSE);
- }
if (publickey) {
SECKEY_DestroyPublicKey(publickey);
}
+ if (ourPubKey) {
+ SECKEY_DestroyPublicKey(ourPubKey);
+ }
if (ourPrivKey) {
SECKEY_DestroyPrivateKey(ourPrivKey);
}
-#endif
return SECFailure;
}
+/* TODO: Move to pk11wrap and export? */
+static int
+cms_GetKekSizeFromKeyWrapAlgTag(SECOidTag keyWrapAlgtag)
+{
+ switch (keyWrapAlgtag) {
+ case SEC_OID_AES_128_KEY_WRAP:
+ return 16;
+ case SEC_OID_AES_192_KEY_WRAP:
+ return 24;
+ case SEC_OID_AES_256_KEY_WRAP:
+ return 32;
+ default:
+ break;
+ }
+ return 0;
+}
+
+/* TODO: Move to smimeutil and export? */
+static CK_ULONG
+cms_GetKdfFromKeyEncAlgTag(SECOidTag keyEncAlgtag)
+{
+ switch (keyEncAlgtag) {
+ case SEC_OID_DHSINGLEPASS_STDDH_SHA1KDF_SCHEME:
+ case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA1KDF_SCHEME:
+ return CKD_SHA1_KDF;
+ case SEC_OID_DHSINGLEPASS_STDDH_SHA224KDF_SCHEME:
+ case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA224KDF_SCHEME:
+ return CKD_SHA224_KDF;
+ case SEC_OID_DHSINGLEPASS_STDDH_SHA256KDF_SCHEME:
+ case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA256KDF_SCHEME:
+ return CKD_SHA256_KDF;
+ case SEC_OID_DHSINGLEPASS_STDDH_SHA384KDF_SCHEME:
+ case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA384KDF_SCHEME:
+ return CKD_SHA384_KDF;
+ case SEC_OID_DHSINGLEPASS_STDDH_SHA512KDF_SCHEME:
+ case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA512KDF_SCHEME:
+ return CKD_SHA512_KDF;
+ default:
+ break;
+ }
+ return 0;
+}
+
PK11SymKey *
-NSS_CMSUtil_DecryptSymKey_ESDH(SECKEYPrivateKey *privkey, SECItem *encKey,
+NSS_CMSUtil_DecryptSymKey_ECDH(SECKEYPrivateKey *privkey, SECItem *encKey,
SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag,
- void *pwfn_arg)
+ SECItem *ukm, NSSCMSOriginatorIdentifierOrKey *oiok,
+ void *wincx)
{
-#if 0 /* not yet done */
- SECStatus err;
- CK_MECHANISM_TYPE bulkType;
- PK11SymKey *tek;
- SECKEYPublicKey *originatorPubKey;
- NSSCMSSMIMEKEAParameters keaParams;
-
- /* XXXX get originator's public key */
- originatorPubKey = PK11_MakeKEAPubKey(keaParams.originatorKEAKey.data,
- keaParams.originatorKEAKey.len);
- if (originatorPubKey == NULL)
- goto loser;
-
- /* Generate the TEK (token exchange key) which we use to unwrap the bulk encryption key.
- The Derive function generates a shared secret and combines it with the originatorRA
- data to come up with an unique session key */
- tek = PK11_PubDerive(privkey, originatorPubKey, PR_FALSE,
- &keaParams.originatorRA, NULL,
- CKM_KEA_KEY_DERIVE, CKM_SKIPJACK_WRAP,
- CKA_WRAP, 0, pwfn_arg);
- SECKEY_DestroyPublicKey(originatorPubKey); /* not needed anymore */
- if (tek == NULL)
+ SECAlgorithmID keyWrapAlg;
+ SECOidTag keyEncAlgtag, keyWrapAlgtag;
+ CK_MECHANISM_TYPE target, keyDerivationType, keyWrapMech;
+ CK_ULONG kdf;
+ PK11SymKey *kek = NULL, *bulkkey = NULL;
+ int kek_size;
+ SECKEYPublicKey originatorpublickey;
+ SECItem *oiok_publicKey, *SharedInfo = NULL;
+ SECStatus rv;
+
+ PORT_Memset(&keyWrapAlg, 0, sizeof(SECAlgorithmID));
+
+ PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN);
+ target = PK11_AlgtagToMechanism(bulkalgtag);
+ if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+
+ keyEncAlgtag = SECOID_GetAlgorithmTag(keyEncAlg);
+ keyDerivationType = PK11_AlgtagToMechanism(keyEncAlgtag);
+ if ((keyEncAlgtag == SEC_OID_UNKNOWN) ||
+ (keyDerivationType == CKM_INVALID_MECHANISM)) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
goto loser;
+ }
- /* Now that we have the TEK, unwrap the bulk key
- with which to decrypt the message. */
- /* Skipjack is being used as the bulk encryption algorithm.*/
- /* Unwrap the bulk key. */
- bulkkey = PK11_UnwrapSymKey(tek, CKM_SKIPJACK_WRAP, NULL,
- encKey, CKM_SKIPJACK_CBC64, CKA_DECRYPT, 0);
+ rv = SEC_ASN1DecodeItem(NULL, &keyWrapAlg,
+ SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), &(keyEncAlg->parameters));
+ if (rv != SECSuccess) {
+ goto loser;
+ }
- return bulkkey;
+ keyWrapAlgtag = SECOID_GetAlgorithmTag(&keyWrapAlg);
+ keyWrapMech = PK11_AlgtagToMechanism(keyWrapAlgtag);
+ if ((keyWrapAlgtag == SEC_OID_UNKNOWN) ||
+ (keyWrapMech == CKM_INVALID_MECHANISM)) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+
+ kek_size = cms_GetKekSizeFromKeyWrapAlgTag(keyWrapAlgtag);
+ if (!kek_size) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+ kdf = cms_GetKdfFromKeyEncAlgTag(keyEncAlgtag);
+ if (!kdf) {
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ goto loser;
+ }
+
+ /* Get originator's public key */
+ /* TODO: Add support for static-static ECDH */
+ if (oiok->identifierType != NSSCMSOriginatorIDOrKey_OriginatorPublicKey) {
+ PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE);
+ goto loser;
+ }
+
+ /* PK11_PubDeriveWithKDF only uses the keyType u.ec.publicValue.data
+ * and u.ec.publicValue.len from the originator's public key. */
+ oiok_publicKey = &(oiok->id.originatorPublicKey.publicKey);
+ originatorpublickey.keyType = ecKey;
+ originatorpublickey.u.ec.publicValue.data = oiok_publicKey->data;
+ originatorpublickey.u.ec.publicValue.len = oiok_publicKey->len / 8;
+
+ SharedInfo = Create_ECC_CMS_SharedInfo(NULL, &keyWrapAlg, ukm, kek_size);
+ if (!SharedInfo) {
+ goto loser;
+ }
+
+ /* Generate the KEK (key exchange key) according to RFC5753 which we use
+ * to wrap the bulk encryption key. */
+ kek = PK11_PubDeriveWithKDF(privkey, &originatorpublickey, PR_TRUE,
+ NULL, NULL,
+ keyDerivationType, keyWrapMech,
+ CKA_WRAP, kek_size, kdf, SharedInfo, wincx);
+
+ SECITEM_FreeItem(SharedInfo, PR_TRUE);
+
+ if (kek == NULL) {
+ goto loser;
+ }
+
+ bulkkey = PK11_UnwrapSymKey(kek, keyWrapMech, NULL, encKey, target, CKA_UNWRAP, 0);
+ PK11_FreeSymKey(kek); /* we do not need the KEK anymore */
loser:
-#endif
- return NULL;
+ SECOID_DestroyAlgorithmID(&keyWrapAlg, PR_FALSE);
+ return bulkkey;
}