tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cmspubkey.c (23033B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /*
      6 * CMS public key crypto
      7 */
      8 
      9 #include "cmslocal.h"
     10 
     11 #include "cert.h"
     12 #include "keyhi.h"
     13 #include "secasn1.h"
     14 #include "secitem.h"
     15 #include "secoid.h"
     16 #include "pk11func.h"
     17 #include "secerr.h"
     18 #include "secder.h"
     19 #include "prerr.h"
     20 #include "sechash.h"
     21 
     22 /* ====== RSA ======================================================================= */
     23 
     24 /*
     25 * NSS_CMSUtil_EncryptSymKey_RSA - wrap a symmetric key with RSA
     26 *
     27 * this function takes a symmetric key and encrypts it using an RSA public key
     28 * according to PKCS#1 and RFC2633 (S/MIME)
     29 */
     30 SECStatus
     31 NSS_CMSUtil_EncryptSymKey_RSA(PLArenaPool *poolp, CERTCertificate *cert,
     32                              PK11SymKey *bulkkey,
     33                              SECItem *encKey)
     34 {
     35    SECStatus rv;
     36    SECKEYPublicKey *publickey;
     37 
     38    publickey = CERT_ExtractPublicKey(cert);
     39    if (publickey == NULL)
     40        return SECFailure;
     41 
     42    rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, publickey, bulkkey, encKey);
     43    SECKEY_DestroyPublicKey(publickey);
     44    return rv;
     45 }
     46 
     47 SECStatus
     48 NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp,
     49                                    SECKEYPublicKey *publickey,
     50                                    PK11SymKey *bulkkey, SECItem *encKey)
     51 {
     52    SECStatus rv;
     53    int data_len;
     54    KeyType keyType;
     55    void *mark = NULL;
     56 
     57    mark = PORT_ArenaMark(poolp);
     58    if (!mark)
     59        goto loser;
     60 
     61    /* sanity check */
     62    keyType = SECKEY_GetPublicKeyType(publickey);
     63    PORT_Assert(keyType == rsaKey);
     64    if (keyType != rsaKey) {
     65        goto loser;
     66    }
     67    /* allocate memory for the encrypted key */
     68    data_len = SECKEY_PublicKeyStrength(publickey); /* block size (assumed to be > keylen) */
     69    encKey->data = (unsigned char *)PORT_ArenaAlloc(poolp, data_len);
     70    encKey->len = data_len;
     71    if (encKey->data == NULL)
     72        goto loser;
     73 
     74    /* encrypt the key now */
     75    rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION),
     76                            publickey, bulkkey, encKey);
     77 
     78    if (rv != SECSuccess)
     79        goto loser;
     80 
     81    PORT_ArenaUnmark(poolp, mark);
     82    return SECSuccess;
     83 
     84 loser:
     85    if (mark) {
     86        PORT_ArenaRelease(poolp, mark);
     87    }
     88    return SECFailure;
     89 }
     90 
     91 /*
     92 * NSS_CMSUtil_DecryptSymKey_RSA - unwrap a RSA-wrapped symmetric key
     93 *
     94 * this function takes an RSA-wrapped symmetric key and unwraps it, returning a symmetric
     95 * key handle. Please note that the actual unwrapped key data may not be allowed to leave
     96 * a hardware token...
     97 */
     98 PK11SymKey *
     99 NSS_CMSUtil_DecryptSymKey_RSA(SECKEYPrivateKey *privkey, SECItem *encKey, SECOidTag bulkalgtag)
    100 {
    101    /* that's easy */
    102    CK_MECHANISM_TYPE target;
    103    PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN);
    104    target = PK11_AlgtagToMechanism(bulkalgtag);
    105    if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) {
    106        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    107        return NULL;
    108    }
    109    return PK11_PubUnwrapSymKey(privkey, encKey, target, CKA_DECRYPT, 0);
    110 }
    111 
    112 typedef struct RSA_OAEP_CMS_paramsStr RSA_OAEP_CMS_params;
    113 struct RSA_OAEP_CMS_paramsStr {
    114    SECAlgorithmID *hashFunc;
    115    SECAlgorithmID *maskGenFunc;
    116    SECAlgorithmID *pSourceFunc;
    117 };
    118 
    119 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
    120 SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
    121 
    122 static const SEC_ASN1Template seckey_PointerToAlgorithmIDTemplate[] = {
    123    { SEC_ASN1_POINTER | SEC_ASN1_XTRN, 0,
    124      SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }
    125 };
    126 
    127 static const SEC_ASN1Template RSA_OAEP_CMS_paramsTemplate[] = {
    128    { SEC_ASN1_SEQUENCE,
    129      0, NULL, sizeof(RSA_OAEP_CMS_params) },
    130    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
    131          SEC_ASN1_CONTEXT_SPECIFIC | 0,
    132      offsetof(RSA_OAEP_CMS_params, hashFunc),
    133      seckey_PointerToAlgorithmIDTemplate },
    134    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
    135          SEC_ASN1_CONTEXT_SPECIFIC | 1,
    136      offsetof(RSA_OAEP_CMS_params, maskGenFunc),
    137      seckey_PointerToAlgorithmIDTemplate },
    138    { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_EXPLICIT |
    139          SEC_ASN1_CONTEXT_SPECIFIC | 2,
    140      offsetof(RSA_OAEP_CMS_params, pSourceFunc),
    141      seckey_PointerToAlgorithmIDTemplate },
    142    { 0 }
    143 };
    144 
    145 /*
    146 * NSS_CMSUtil_DecryptSymKey_RSA_OAEP - unwrap a RSA-wrapped symmetric key
    147 *
    148 * this function takes an RSA-OAEP-wrapped symmetric key and unwraps it, returning a symmetric
    149 * key handle. Please note that the actual unwrapped key data may not be allowed to leave
    150 * a hardware token...
    151 */
    152 PK11SymKey *
    153 NSS_CMSUtil_DecryptSymKey_RSA_OAEP(SECKEYPrivateKey *privkey, SECItem *parameters, SECItem *encKey, SECOidTag bulkalgtag)
    154 {
    155    CK_RSA_PKCS_OAEP_PARAMS oaep_params;
    156    RSA_OAEP_CMS_params encoded_params;
    157    SECAlgorithmID mgf1hashAlg;
    158    SECOidTag mgfAlgtag, pSourcetag;
    159    SECItem encoding_params, params;
    160    PK11SymKey *bulkkey = NULL;
    161    SECStatus rv;
    162 
    163    CK_MECHANISM_TYPE target;
    164    PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN);
    165    target = PK11_AlgtagToMechanism(bulkalgtag);
    166    if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) {
    167        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    168        return NULL;
    169    }
    170 
    171    PORT_Memset(&encoded_params, 0, sizeof(RSA_OAEP_CMS_params));
    172    PORT_Memset(&mgf1hashAlg, 0, sizeof(SECAlgorithmID));
    173    PORT_Memset(&encoding_params, 0, sizeof(SECItem));
    174 
    175    /* Set default values for the OAEP parameters */
    176    oaep_params.hashAlg = CKM_SHA_1;
    177    oaep_params.mgf = CKG_MGF1_SHA1;
    178    oaep_params.source = CKZ_DATA_SPECIFIED;
    179    oaep_params.pSourceData = NULL;
    180    oaep_params.ulSourceDataLen = 0;
    181 
    182    if (parameters->len == 2) {
    183        /* For some reason SEC_ASN1DecodeItem cannot process parameters if it is an emtpy SEQUENCE */
    184        /* Just check that this is a properly encoded empty SEQUENCE */
    185        /* TODO: Investigate if there a better way to handle this as part of decoding. */
    186        if ((parameters->data[0] != 0x30) || (parameters->data[1] != 0)) {
    187            return NULL;
    188        }
    189    } else {
    190        rv = SEC_ASN1DecodeItem(NULL, &encoded_params, RSA_OAEP_CMS_paramsTemplate, parameters);
    191        if (rv != SECSuccess) {
    192            goto loser;
    193        }
    194        if (encoded_params.hashFunc != NULL) {
    195            oaep_params.hashAlg = PK11_AlgtagToMechanism(SECOID_GetAlgorithmTag(encoded_params.hashFunc));
    196        }
    197        if (encoded_params.maskGenFunc != NULL) {
    198            mgfAlgtag = SECOID_GetAlgorithmTag(encoded_params.maskGenFunc);
    199            if (mgfAlgtag != SEC_OID_PKCS1_MGF1) {
    200                /* MGF1 is the only supported mask generation function */
    201                goto loser;
    202            }
    203            /* The parameters field contains an AlgorithmIdentifier specifying the
    204             * hash function to use with MGF1.
    205             */
    206            rv = SEC_ASN1DecodeItem(NULL, &mgf1hashAlg, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
    207                                    &encoded_params.maskGenFunc->parameters);
    208            if (rv != SECSuccess) {
    209                goto loser;
    210            }
    211            oaep_params.mgf = SEC_GetMgfTypeByOidTag(SECOID_GetAlgorithmTag(&mgf1hashAlg));
    212            if (!oaep_params.mgf) {
    213                goto loser;
    214            }
    215        }
    216        if (encoded_params.pSourceFunc != NULL) {
    217            pSourcetag = SECOID_GetAlgorithmTag(encoded_params.pSourceFunc);
    218            if (pSourcetag != SEC_OID_PKCS1_PSPECIFIED) {
    219                goto loser;
    220            }
    221            /* The encoding parameters (P) are provided as an OCTET STRING in the parameters field. */
    222            if (encoded_params.pSourceFunc->parameters.len != 0) {
    223                rv = SEC_ASN1DecodeItem(NULL, &encoding_params, SEC_ASN1_GET(SEC_OctetStringTemplate),
    224                                        &encoded_params.pSourceFunc->parameters);
    225                if (rv != SECSuccess) {
    226                    goto loser;
    227                }
    228                oaep_params.ulSourceDataLen = encoding_params.len;
    229                oaep_params.pSourceData = encoding_params.data;
    230            }
    231        }
    232    }
    233    params.type = siBuffer;
    234    params.data = (unsigned char *)&oaep_params;
    235    params.len = sizeof(CK_RSA_PKCS_OAEP_PARAMS);
    236    bulkkey = PK11_PubUnwrapSymKeyWithMechanism(privkey, CKM_RSA_PKCS_OAEP, &params,
    237                                                encKey, target, CKA_DECRYPT, 0);
    238 
    239 loser:
    240    PORT_Free(oaep_params.pSourceData);
    241    if (encoded_params.hashFunc) {
    242        SECOID_DestroyAlgorithmID(encoded_params.hashFunc, PR_TRUE);
    243    }
    244    if (encoded_params.maskGenFunc) {
    245        SECOID_DestroyAlgorithmID(encoded_params.maskGenFunc, PR_TRUE);
    246    }
    247    if (encoded_params.pSourceFunc) {
    248        SECOID_DestroyAlgorithmID(encoded_params.pSourceFunc, PR_TRUE);
    249    }
    250    SECOID_DestroyAlgorithmID(&mgf1hashAlg, PR_FALSE);
    251    return bulkkey;
    252 }
    253 
    254 /* ====== ESECDH (Ephemeral-Static Elliptic Curve Diffie-Hellman) =========== */
    255 
    256 typedef struct ECC_CMS_SharedInfoStr ECC_CMS_SharedInfo;
    257 struct ECC_CMS_SharedInfoStr {
    258    SECAlgorithmID *keyInfo; /* key-encryption algorithm */
    259    SECItem entityUInfo;     /* ukm */
    260    SECItem suppPubInfo;     /* length of KEK in bits as 32-bit number */
    261 };
    262 
    263 static const SEC_ASN1Template ECC_CMS_SharedInfoTemplate[] = {
    264    { SEC_ASN1_SEQUENCE,
    265      0, NULL, sizeof(ECC_CMS_SharedInfo) },
    266    { SEC_ASN1_XTRN | SEC_ASN1_POINTER,
    267      offsetof(ECC_CMS_SharedInfo, keyInfo),
    268      SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
    269    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
    270          SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
    271      offsetof(ECC_CMS_SharedInfo, entityUInfo),
    272      SEC_ASN1_SUB(SEC_OctetStringTemplate) },
    273    { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED |
    274          SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2,
    275      offsetof(ECC_CMS_SharedInfo, suppPubInfo),
    276      SEC_ASN1_SUB(SEC_OctetStringTemplate) },
    277    { 0 }
    278 };
    279 
    280 /* Inputs:
    281 *       keyInfo: key-encryption algorithm
    282 *       ukm: ukm field of KeyAgreeRecipientInfo
    283 *       KEKsize: length of KEK in bytes
    284 *
    285 * Output: DER encoded ECC-CMS-SharedInfo
    286 */
    287 static SECItem *
    288 Create_ECC_CMS_SharedInfo(PLArenaPool *poolp,
    289                          SECAlgorithmID *keyInfo, SECItem *ukm, unsigned int KEKsize)
    290 {
    291    ECC_CMS_SharedInfo SI;
    292    unsigned char suppPubInfo[4] = { 0 };
    293 
    294    SI.keyInfo = keyInfo;
    295    SI.entityUInfo.type = ukm->type;
    296    SI.entityUInfo.data = ukm->data;
    297    SI.entityUInfo.len = ukm->len;
    298 
    299    SI.suppPubInfo.type = siBuffer;
    300    SI.suppPubInfo.data = suppPubInfo;
    301    SI.suppPubInfo.len = sizeof(suppPubInfo);
    302 
    303    if (KEKsize > 0x1FFFFFFF) { // ensure KEKsize * 8 fits in 4 bytes.
    304        return NULL;
    305    }
    306    KEKsize *= 8;
    307    suppPubInfo[0] = (KEKsize & 0xFF000000) >> 24;
    308    suppPubInfo[1] = (KEKsize & 0xFF0000) >> 16;
    309    suppPubInfo[2] = (KEKsize & 0xFF00) >> 8;
    310    suppPubInfo[3] = KEKsize & 0xFF;
    311 
    312    return SEC_ASN1EncodeItem(poolp, NULL, &SI, ECC_CMS_SharedInfoTemplate);
    313 }
    314 
    315 /* this will generate a key pair, compute the shared secret, */
    316 /* derive a key and ukm for the keyEncAlg out of it, encrypt the bulk key with */
    317 /* the keyEncAlg, set encKey, keyEncAlg, publicKey etc.
    318 * The ukm is optional per RFC 5753. Pass a NULL value to request an empty ukm.
    319 * Pass a SECItem with the size set to zero, to request allocating a random
    320 * ukm of a default size. Or provide an explicit ukm that was defined by the caller.
    321 */
    322 SECStatus
    323 NSS_CMSUtil_EncryptSymKey_ESECDH(PLArenaPool *poolp, CERTCertificate *cert,
    324                                 PK11SymKey *bulkkey, SECItem *encKey,
    325                                 PRBool genUkm, SECItem *ukm,
    326                                 SECAlgorithmID *keyEncAlg, SECItem *pubKey,
    327                                 void *wincx)
    328 {
    329    SECOidTag certalgtag; /* the certificate's encryption algorithm */
    330    SECStatus rv;
    331    SECStatus err;
    332    PK11SymKey *kek;
    333    SECKEYPublicKey *publickey = NULL;
    334    SECKEYPublicKey *ourPubKey = NULL;
    335    SECKEYPrivateKey *ourPrivKey = NULL;
    336    unsigned int bulkkey_size, kek_size;
    337    SECAlgorithmID keyWrapAlg;
    338    SECOidTag keyEncAlgtag;
    339    SECItem keyWrapAlg_params, *keyEncAlg_params, *SharedInfo;
    340    CK_MECHANISM_TYPE keyDerivationType, keyWrapMech;
    341    CK_ULONG kdf;
    342 
    343    if (genUkm && (ukm->len != 0 || ukm->data != NULL)) {
    344        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
    345        return SECFailure;
    346    }
    347 
    348    certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
    349    PORT_Assert(certalgtag == SEC_OID_ANSIX962_EC_PUBLIC_KEY);
    350    if (certalgtag != SEC_OID_ANSIX962_EC_PUBLIC_KEY) {
    351        return SECFailure;
    352    }
    353 
    354    /* Get the public key of the recipient. */
    355    publickey = CERT_ExtractPublicKey(cert);
    356    if (publickey == NULL)
    357        goto loser;
    358 
    359    ourPrivKey = SECKEY_CreateECPrivateKey(&publickey->u.ec.DEREncodedParams,
    360                                           &ourPubKey, wincx);
    361    if (!ourPrivKey || !ourPubKey) {
    362        goto loser;
    363    }
    364 
    365    rv = SECITEM_CopyItem(poolp, pubKey, &ourPubKey->u.ec.publicValue);
    366    if (rv != SECSuccess) {
    367        goto loser;
    368    }
    369 
    370    /* pubKey will be encoded as a BIT STRING, so pubKey->len must be length in bits
    371     * rather than bytes */
    372    pubKey->len = pubKey->len * 8;
    373 
    374    SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */
    375    ourPubKey = NULL;
    376 
    377    bulkkey_size = PK11_GetKeyLength(bulkkey);
    378    if (bulkkey_size == 0) {
    379        goto loser;
    380    }
    381 
    382    /* Parameters are supposed to be absent for AES key wrap algorithms.
    383     * However, Microsoft Outlook cannot decrypt message unless
    384     * parameters field is NULL. */
    385    keyWrapAlg_params.len = 2;
    386    keyWrapAlg_params.data = (unsigned char *)PORT_ArenaAlloc(poolp, keyWrapAlg_params.len);
    387    if (keyWrapAlg_params.data == NULL)
    388        goto loser;
    389 
    390    keyWrapAlg_params.data[0] = SEC_ASN1_NULL;
    391    keyWrapAlg_params.data[1] = 0;
    392 
    393    /* RFC5753 specifies id-aes128-wrap as the mandatory to support algorithm.
    394     * So, use id-aes128-wrap unless bulkkey provides more than 128 bits of
    395     * security. If bulkkey provides more than 128 bits of security, use
    396     * the algorithms from KE Set 2 in RFC 6318. */
    397 
    398    /* TODO: NIST Special Publication SP 800-56A requires the use of
    399     * Cofactor ECDH. However, RFC 6318 specifies the use of
    400     * dhSinglePass-stdDH-sha256kdf-scheme or dhSinglePass-stdDH-sha384kdf-scheme
    401     * for Suite B. The reason for this is that all of the NIST recommended
    402     * prime curves in FIPS 186-3 (including the Suite B curves) have a cofactor
    403     * of 1, and so for these curves standard and cofactor ECDH are the same.
    404     * This code should really look at the key's parameters and choose cofactor
    405     * ECDH if the curve has a cofactor other than 1. */
    406    if ((PK11_GetMechanism(bulkkey) == CKM_AES_CBC) && (bulkkey_size > 16)) {
    407        rv = SECOID_SetAlgorithmID(poolp, &keyWrapAlg, SEC_OID_AES_256_KEY_WRAP, &keyWrapAlg_params);
    408        kek_size = 32;
    409        keyWrapMech = CKM_NSS_AES_KEY_WRAP;
    410        kdf = CKD_SHA384_KDF;
    411        keyEncAlgtag = SEC_OID_DHSINGLEPASS_STDDH_SHA384KDF_SCHEME;
    412        keyDerivationType = CKM_ECDH1_DERIVE;
    413    } else {
    414        rv = SECOID_SetAlgorithmID(poolp, &keyWrapAlg, SEC_OID_AES_128_KEY_WRAP, &keyWrapAlg_params);
    415        kek_size = 16;
    416        keyWrapMech = CKM_NSS_AES_KEY_WRAP;
    417        kdf = CKD_SHA256_KDF;
    418        keyEncAlgtag = SEC_OID_DHSINGLEPASS_STDDH_SHA256KDF_SCHEME;
    419        keyDerivationType = CKM_ECDH1_DERIVE;
    420    }
    421    if (rv != SECSuccess) {
    422        goto loser;
    423    }
    424 
    425    /* ukm is optional, but RFC 5753 says that originators SHOULD include the ukm.
    426     * I made ukm 64 bytes, since RFC 2631 states that UserKeyingMaterial must
    427     * contain 512 bits for Diffie-Hellman key agreement. */
    428 
    429    if (genUkm) {
    430        ukm->type = siBuffer;
    431        ukm->len = 64;
    432        ukm->data = (unsigned char *)PORT_ArenaAlloc(poolp, ukm->len);
    433 
    434        if (ukm->data == NULL) {
    435            goto loser;
    436        }
    437        rv = PK11_GenerateRandom(ukm->data, ukm->len);
    438        if (rv != SECSuccess) {
    439            goto loser;
    440        }
    441    }
    442 
    443    SharedInfo = Create_ECC_CMS_SharedInfo(poolp, &keyWrapAlg,
    444                                           ukm,
    445                                           kek_size);
    446    if (!SharedInfo) {
    447        goto loser;
    448    }
    449 
    450    /* Generate the KEK (key exchange key) according to RFC5753 which we use
    451     * to wrap the bulk encryption key. */
    452    kek = PK11_PubDeriveWithKDF(ourPrivKey, publickey, PR_TRUE,
    453                                NULL, NULL,
    454                                keyDerivationType, keyWrapMech,
    455                                CKA_WRAP, kek_size, kdf, SharedInfo, NULL);
    456    if (!kek) {
    457        goto loser;
    458    }
    459 
    460    SECKEY_DestroyPublicKey(publickey);
    461    SECKEY_DestroyPrivateKey(ourPrivKey);
    462    publickey = NULL;
    463    ourPrivKey = NULL;
    464 
    465    /* allocate space for the encrypted CEK (bulk key) */
    466    /* AES key wrap produces an output 64-bits longer than
    467     * the input AES CEK (RFC 3565, Section 2.3.2) */
    468    encKey->len = bulkkey_size + 8;
    469    encKey->data = (unsigned char *)PORT_ArenaAlloc(poolp, encKey->len);
    470 
    471    if (encKey->data == NULL) {
    472        PK11_FreeSymKey(kek);
    473        goto loser;
    474    }
    475 
    476    err = PK11_WrapSymKey(keyWrapMech, NULL, kek, bulkkey, encKey);
    477 
    478    PK11_FreeSymKey(kek); /* we do not need the KEK anymore */
    479    if (err != SECSuccess) {
    480        goto loser;
    481    }
    482 
    483    keyEncAlg_params = SEC_ASN1EncodeItem(poolp, NULL, &keyWrapAlg, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
    484    if (keyEncAlg_params == NULL)
    485        goto loser;
    486    keyEncAlg_params->type = siBuffer;
    487 
    488    /* now set keyEncAlg */
    489    rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, keyEncAlgtag, keyEncAlg_params);
    490    if (rv != SECSuccess) {
    491        goto loser;
    492    }
    493 
    494    return SECSuccess;
    495 
    496 loser:
    497    if (publickey) {
    498        SECKEY_DestroyPublicKey(publickey);
    499    }
    500    if (ourPubKey) {
    501        SECKEY_DestroyPublicKey(ourPubKey);
    502    }
    503    if (ourPrivKey) {
    504        SECKEY_DestroyPrivateKey(ourPrivKey);
    505    }
    506    return SECFailure;
    507 }
    508 
    509 /* TODO: Move to pk11wrap and export? */
    510 static int
    511 cms_GetKekSizeFromKeyWrapAlgTag(SECOidTag keyWrapAlgtag)
    512 {
    513    switch (keyWrapAlgtag) {
    514        case SEC_OID_AES_128_KEY_WRAP:
    515            return 16;
    516        case SEC_OID_AES_192_KEY_WRAP:
    517            return 24;
    518        case SEC_OID_AES_256_KEY_WRAP:
    519            return 32;
    520        default:
    521            break;
    522    }
    523    return 0;
    524 }
    525 
    526 /* TODO: Move to smimeutil and export? */
    527 static CK_ULONG
    528 cms_GetKdfFromKeyEncAlgTag(SECOidTag keyEncAlgtag)
    529 {
    530    switch (keyEncAlgtag) {
    531        case SEC_OID_DHSINGLEPASS_STDDH_SHA1KDF_SCHEME:
    532        case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA1KDF_SCHEME:
    533            return CKD_SHA1_KDF;
    534        case SEC_OID_DHSINGLEPASS_STDDH_SHA224KDF_SCHEME:
    535        case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA224KDF_SCHEME:
    536            return CKD_SHA224_KDF;
    537        case SEC_OID_DHSINGLEPASS_STDDH_SHA256KDF_SCHEME:
    538        case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA256KDF_SCHEME:
    539            return CKD_SHA256_KDF;
    540        case SEC_OID_DHSINGLEPASS_STDDH_SHA384KDF_SCHEME:
    541        case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA384KDF_SCHEME:
    542            return CKD_SHA384_KDF;
    543        case SEC_OID_DHSINGLEPASS_STDDH_SHA512KDF_SCHEME:
    544        case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA512KDF_SCHEME:
    545            return CKD_SHA512_KDF;
    546        default:
    547            break;
    548    }
    549    return 0;
    550 }
    551 
    552 PK11SymKey *
    553 NSS_CMSUtil_DecryptSymKey_ECDH(SECKEYPrivateKey *privkey, SECItem *encKey,
    554                               SECAlgorithmID *keyEncAlg, SECOidTag bulkalgtag,
    555                               SECItem *ukm, NSSCMSOriginatorIdentifierOrKey *oiok,
    556                               void *wincx)
    557 {
    558    SECAlgorithmID keyWrapAlg;
    559    SECOidTag keyEncAlgtag, keyWrapAlgtag;
    560    CK_MECHANISM_TYPE target, keyDerivationType, keyWrapMech;
    561    CK_ULONG kdf;
    562    PK11SymKey *kek = NULL, *bulkkey = NULL;
    563    int kek_size;
    564    SECKEYPublicKey originatorpublickey;
    565    SECItem *oiok_publicKey, *SharedInfo = NULL;
    566    SECStatus rv;
    567 
    568    PORT_Memset(&keyWrapAlg, 0, sizeof(SECAlgorithmID));
    569 
    570    PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN);
    571    target = PK11_AlgtagToMechanism(bulkalgtag);
    572    if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) {
    573        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    574        goto loser;
    575    }
    576 
    577    keyEncAlgtag = SECOID_GetAlgorithmTag(keyEncAlg);
    578    keyDerivationType = PK11_AlgtagToMechanism(keyEncAlgtag);
    579    if ((keyEncAlgtag == SEC_OID_UNKNOWN) ||
    580        (keyDerivationType == CKM_INVALID_MECHANISM)) {
    581        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    582        goto loser;
    583    }
    584 
    585    rv = SEC_ASN1DecodeItem(NULL, &keyWrapAlg,
    586                            SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), &(keyEncAlg->parameters));
    587    if (rv != SECSuccess) {
    588        goto loser;
    589    }
    590 
    591    keyWrapAlgtag = SECOID_GetAlgorithmTag(&keyWrapAlg);
    592    keyWrapMech = PK11_AlgtagToMechanism(keyWrapAlgtag);
    593    if ((keyWrapAlgtag == SEC_OID_UNKNOWN) ||
    594        (keyWrapMech == CKM_INVALID_MECHANISM)) {
    595        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    596        goto loser;
    597    }
    598 
    599    kek_size = cms_GetKekSizeFromKeyWrapAlgTag(keyWrapAlgtag);
    600    if (!kek_size) {
    601        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    602        goto loser;
    603    }
    604 
    605    kdf = cms_GetKdfFromKeyEncAlgTag(keyEncAlgtag);
    606    if (!kdf) {
    607        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
    608        goto loser;
    609    }
    610 
    611    /* Get originator's public key */
    612    /* TODO: Add support for static-static ECDH */
    613    if (oiok->identifierType != NSSCMSOriginatorIDOrKey_OriginatorPublicKey) {
    614        PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE);
    615        goto loser;
    616    }
    617 
    618    /* PK11_PubDeriveWithKDF only uses the keyType u.ec.publicValue.data
    619     * and u.ec.publicValue.len from the originator's public key. */
    620    oiok_publicKey = &(oiok->id.originatorPublicKey.publicKey);
    621    originatorpublickey.keyType = ecKey;
    622    originatorpublickey.u.ec.publicValue.data = oiok_publicKey->data;
    623    originatorpublickey.u.ec.publicValue.len = oiok_publicKey->len / 8;
    624 
    625    SharedInfo = Create_ECC_CMS_SharedInfo(NULL, &keyWrapAlg, ukm, kek_size);
    626    if (!SharedInfo) {
    627        goto loser;
    628    }
    629 
    630    /* Generate the KEK (key exchange key) according to RFC5753 which we use
    631     * to wrap the bulk encryption key. */
    632    kek = PK11_PubDeriveWithKDF(privkey, &originatorpublickey, PR_TRUE,
    633                                NULL, NULL,
    634                                keyDerivationType, keyWrapMech,
    635                                CKA_WRAP, kek_size, kdf, SharedInfo, wincx);
    636 
    637    SECITEM_FreeItem(SharedInfo, PR_TRUE);
    638 
    639    if (kek == NULL) {
    640        goto loser;
    641    }
    642 
    643    bulkkey = PK11_UnwrapSymKey(kek, keyWrapMech, NULL, encKey, target, CKA_UNWRAP, 0);
    644    PK11_FreeSymKey(kek); /* we do not need the KEK anymore */
    645 loser:
    646    SECOID_DestroyAlgorithmID(&keyWrapAlg, PR_FALSE);
    647    return bulkkey;
    648 }