Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* CMS public key crypto
*/
#include "cmslocal.h"
#include "cert.h"
#include "keyhi.h"
#include "secasn1.h"
#include "secitem.h"
#include "secoid.h"
#include "pk11func.h"
#include "secerr.h"
#include "secder.h"
#include "prerr.h"
#include "sechash.h"
/* ====== RSA ======================================================================= */
/*
* NSS_CMSUtil_EncryptSymKey_RSA - wrap a symmetric key with RSA
*
* this function takes a symmetric key and encrypts it using an RSA public key
* according to PKCS#1 and RFC2633 (S/MIME)
*/
SECStatus
NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert,
PK11SymKey *bulkkey,
SECItem *encKey)
{
SECStatus rv;
SECKEYPublicKey *publickey;
publickey = CERT_ExtractPublicKey(cert);
if (publickey == NULL)
return SECFailure;
rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, publickey, bulkkey, encKey);
SECKEY_DestroyPublicKey(publickey);
return rv;
}
SECStatus
NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp,
SECKEYPublicKey *publickey,
PK11SymKey *bulkkey, SECItem *encKey)
{
SECStatus rv;
int data_len;
KeyType keyType;
void *mark = NULL;
mark = PORT_ArenaMark(poolp);
if (!mark)
goto loser;
/* sanity check */
keyType = SECKEY_GetPublicKeyType(publickey);
PORT_Assert(keyType == rsaKey);
if (keyType != rsaKey) {
goto loser;
}
/* allocate memory for the encrypted key */
data_len = SECKEY_PublicKeyStrength(publickey); /* block size (assumed to be > keylen) */
encKey->data = (unsigned char *)PORT_ArenaAlloc(poolp, data_len);
encKey->len = data_len;
if (encKey->data == NULL)
goto loser;
/* encrypt the key now */
rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION),
publickey, bulkkey, encKey);
if (rv != SECSuccess)
goto loser;
PORT_ArenaUnmark(poolp, mark);
return SECSuccess;
loser:
if (mark) {
PORT_ArenaRelease(poolp, mark);
}
return SECFailure;
}
/*
* NSS_CMSUtil_DecryptSymKey_RSA - unwrap a RSA-wrapped symmetric key
*
* this function takes an RSA-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(SECKEYPrivateKey *privkey, SECItem *encKey, SECOidTag bulkalgtag)
{
/* that's easy */
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;
}
return PK11_PubUnwrapSymKey(privkey, encKey, target, CKA_DECRYPT, 0);
}
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_ESECDH(PLArenaPool *poolp, CERTCertificate *cert,
PK11SymKey *bulkkey, SECItem *encKey,
PRBool genUkm, SECItem *ukm,
SECAlgorithmID *keyEncAlg, SECItem *pubKey,
void *wincx)
{
SECOidTag certalgtag; /* the certificate's encryption algorithm */
SECStatus rv;
SECStatus err;
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_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;
ourPrivKey = SECKEY_CreateECPrivateKey(&publickey->u.ec.DEREncodedParams,
&ourPubKey, wincx);
if (!ourPrivKey || !ourPubKey) {
goto loser;
}
rv = SECITEM_CopyItem(poolp, pubKey, &ourPubKey->u.ec.publicValue);
if (rv != SECSuccess) {
goto loser;
}
/* 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;
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;
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 RFC5753 which we use
* to wrap the bulk encryption key. */
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;
/* allocate space for the encrypted CEK (bulk key) */
/* 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) {
PK11_FreeSymKey(kek);
goto loser;
}
err = PK11_WrapSymKey(keyWrapMech, NULL, kek, bulkkey, encKey);
PK11_FreeSymKey(kek); /* we do not need the KEK anymore */
if (err != SECSuccess) {
goto loser;
}
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, keyEncAlgtag, keyEncAlg_params);
if (rv != SECSuccess) {
goto loser;
}
return SECSuccess;
loser:
if (publickey) {
SECKEY_DestroyPublicKey(publickey);
}
if (ourPubKey) {
SECKEY_DestroyPublicKey(ourPubKey);
}
if (ourPrivKey) {
SECKEY_DestroyPrivateKey(ourPrivKey);
}
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_ECDH(SECKEYPrivateKey *privkey, SECItem *encKey,
SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag,
SECItem *ukm, NSSCMSOriginatorIdentifierOrKey *oiok,
void *wincx)
{
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;
}
rv = SEC_ASN1DecodeItem(NULL, &keyWrapAlg,
SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), &(keyEncAlg->parameters));
if (rv != SECSuccess) {
goto loser;
}
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:
SECOID_DestroyAlgorithmID(&keyWrapAlg, PR_FALSE);
return bulkkey;
}