Coverage Report

Created: 2026-02-05 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/smime/cmspubkey.c
Line
Count
Source
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
0
{
35
0
    SECStatus rv;
36
0
    SECKEYPublicKey *publickey;
37
38
0
    publickey = CERT_ExtractPublicKey(cert);
39
0
    if (publickey == NULL)
40
0
        return SECFailure;
41
42
0
    rv = NSS_CMSUtil_EncryptSymKey_RSAPubKey(poolp, publickey, bulkkey, encKey);
43
0
    SECKEY_DestroyPublicKey(publickey);
44
0
    return rv;
45
0
}
46
47
SECStatus
48
NSS_CMSUtil_EncryptSymKey_RSAPubKey(PLArenaPool *poolp,
49
                                    SECKEYPublicKey *publickey,
50
                                    PK11SymKey *bulkkey, SECItem *encKey)
51
0
{
52
0
    SECStatus rv;
53
0
    int data_len;
54
0
    KeyType keyType;
55
0
    void *mark = NULL;
56
57
0
    mark = PORT_ArenaMark(poolp);
58
0
    if (!mark)
59
0
        goto loser;
60
61
    /* sanity check */
62
0
    keyType = SECKEY_GetPublicKeyType(publickey);
63
0
    PORT_Assert(keyType == rsaKey);
64
0
    if (keyType != rsaKey) {
65
0
        goto loser;
66
0
    }
67
    /* allocate memory for the encrypted key */
68
0
    data_len = SECKEY_PublicKeyStrength(publickey); /* block size (assumed to be > keylen) */
69
0
    encKey->data = (unsigned char *)PORT_ArenaAlloc(poolp, data_len);
70
0
    encKey->len = data_len;
71
0
    if (encKey->data == NULL)
72
0
        goto loser;
73
74
    /* encrypt the key now */
75
0
    rv = PK11_PubWrapSymKey(PK11_AlgtagToMechanism(SEC_OID_PKCS1_RSA_ENCRYPTION),
76
0
                            publickey, bulkkey, encKey);
77
78
0
    if (rv != SECSuccess)
79
0
        goto loser;
80
81
0
    PORT_ArenaUnmark(poolp, mark);
82
0
    return SECSuccess;
83
84
0
loser:
85
0
    if (mark) {
86
0
        PORT_ArenaRelease(poolp, mark);
87
0
    }
88
0
    return SECFailure;
89
0
}
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
0
{
101
    /* that's easy */
102
0
    CK_MECHANISM_TYPE target;
103
0
    PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN);
104
0
    target = PK11_AlgtagToMechanism(bulkalgtag);
105
0
    if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) {
106
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
107
0
        return NULL;
108
0
    }
109
0
    return PK11_PubUnwrapSymKey(privkey, encKey, target, CKA_DECRYPT, 0);
110
0
}
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
0
{
155
0
    CK_RSA_PKCS_OAEP_PARAMS oaep_params;
156
0
    RSA_OAEP_CMS_params encoded_params;
157
0
    SECAlgorithmID mgf1hashAlg;
158
0
    SECOidTag mgfAlgtag, pSourcetag;
159
0
    SECItem encoding_params, params;
160
0
    PK11SymKey *bulkkey = NULL;
161
0
    SECStatus rv;
162
163
0
    CK_MECHANISM_TYPE target;
164
0
    PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN);
165
0
    target = PK11_AlgtagToMechanism(bulkalgtag);
166
0
    if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) {
167
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
168
0
        return NULL;
169
0
    }
170
171
0
    PORT_Memset(&encoded_params, 0, sizeof(RSA_OAEP_CMS_params));
172
0
    PORT_Memset(&mgf1hashAlg, 0, sizeof(SECAlgorithmID));
173
0
    PORT_Memset(&encoding_params, 0, sizeof(SECItem));
174
175
    /* Set default values for the OAEP parameters */
176
0
    oaep_params.hashAlg = CKM_SHA_1;
177
0
    oaep_params.mgf = CKG_MGF1_SHA1;
178
0
    oaep_params.source = CKZ_DATA_SPECIFIED;
179
0
    oaep_params.pSourceData = NULL;
180
0
    oaep_params.ulSourceDataLen = 0;
181
182
0
    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
0
        if ((parameters->data[0] != 0x30) || (parameters->data[1] != 0)) {
187
0
            return NULL;
188
0
        }
189
0
    } else {
190
0
        rv = SEC_ASN1DecodeItem(NULL, &encoded_params, RSA_OAEP_CMS_paramsTemplate, parameters);
191
0
        if (rv != SECSuccess) {
192
0
            goto loser;
193
0
        }
194
0
        if (encoded_params.hashFunc != NULL) {
195
0
            oaep_params.hashAlg = PK11_AlgtagToMechanism(SECOID_GetAlgorithmTag(encoded_params.hashFunc));
196
0
        }
197
0
        if (encoded_params.maskGenFunc != NULL) {
198
0
            mgfAlgtag = SECOID_GetAlgorithmTag(encoded_params.maskGenFunc);
199
0
            if (mgfAlgtag != SEC_OID_PKCS1_MGF1) {
200
                /* MGF1 is the only supported mask generation function */
201
0
                goto loser;
202
0
            }
203
            /* The parameters field contains an AlgorithmIdentifier specifying the
204
             * hash function to use with MGF1.
205
             */
206
0
            rv = SEC_ASN1DecodeItem(NULL, &mgf1hashAlg, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate),
207
0
                                    &encoded_params.maskGenFunc->parameters);
208
0
            if (rv != SECSuccess) {
209
0
                goto loser;
210
0
            }
211
0
            oaep_params.mgf = SEC_GetMgfTypeByOidTag(SECOID_GetAlgorithmTag(&mgf1hashAlg));
212
0
            if (!oaep_params.mgf) {
213
0
                goto loser;
214
0
            }
215
0
        }
216
0
        if (encoded_params.pSourceFunc != NULL) {
217
0
            pSourcetag = SECOID_GetAlgorithmTag(encoded_params.pSourceFunc);
218
0
            if (pSourcetag != SEC_OID_PKCS1_PSPECIFIED) {
219
0
                goto loser;
220
0
            }
221
            /* The encoding parameters (P) are provided as an OCTET STRING in the parameters field. */
222
0
            if (encoded_params.pSourceFunc->parameters.len != 0) {
223
0
                rv = SEC_ASN1DecodeItem(NULL, &encoding_params, SEC_ASN1_GET(SEC_OctetStringTemplate),
224
0
                                        &encoded_params.pSourceFunc->parameters);
225
0
                if (rv != SECSuccess) {
226
0
                    goto loser;
227
0
                }
228
0
                oaep_params.ulSourceDataLen = encoding_params.len;
229
0
                oaep_params.pSourceData = encoding_params.data;
230
0
            }
231
0
        }
232
0
    }
233
0
    params.type = siBuffer;
234
0
    params.data = (unsigned char *)&oaep_params;
235
0
    params.len = sizeof(CK_RSA_PKCS_OAEP_PARAMS);
236
0
    bulkkey = PK11_PubUnwrapSymKeyWithMechanism(privkey, CKM_RSA_PKCS_OAEP, &params,
237
0
                                                encKey, target, CKA_DECRYPT, 0);
238
239
0
loser:
240
0
    PORT_Free(oaep_params.pSourceData);
241
0
    if (encoded_params.hashFunc) {
242
0
        SECOID_DestroyAlgorithmID(encoded_params.hashFunc, PR_TRUE);
243
0
    }
244
0
    if (encoded_params.maskGenFunc) {
245
0
        SECOID_DestroyAlgorithmID(encoded_params.maskGenFunc, PR_TRUE);
246
0
    }
247
0
    if (encoded_params.pSourceFunc) {
248
0
        SECOID_DestroyAlgorithmID(encoded_params.pSourceFunc, PR_TRUE);
249
0
    }
250
0
    SECOID_DestroyAlgorithmID(&mgf1hashAlg, PR_FALSE);
251
0
    return bulkkey;
252
0
}
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
0
{
291
0
    ECC_CMS_SharedInfo SI;
292
0
    unsigned char suppPubInfo[4] = { 0 };
293
294
0
    SI.keyInfo = keyInfo;
295
0
    SI.entityUInfo.type = ukm->type;
296
0
    SI.entityUInfo.data = ukm->data;
297
0
    SI.entityUInfo.len = ukm->len;
298
299
0
    SI.suppPubInfo.type = siBuffer;
300
0
    SI.suppPubInfo.data = suppPubInfo;
301
0
    SI.suppPubInfo.len = sizeof(suppPubInfo);
302
303
0
    if (KEKsize > 0x1FFFFFFF) { // ensure KEKsize * 8 fits in 4 bytes.
304
0
        return NULL;
305
0
    }
306
0
    KEKsize *= 8;
307
0
    suppPubInfo[0] = (KEKsize & 0xFF000000) >> 24;
308
0
    suppPubInfo[1] = (KEKsize & 0xFF0000) >> 16;
309
0
    suppPubInfo[2] = (KEKsize & 0xFF00) >> 8;
310
0
    suppPubInfo[3] = KEKsize & 0xFF;
311
312
0
    return SEC_ASN1EncodeItem(poolp, NULL, &SI, ECC_CMS_SharedInfoTemplate);
313
0
}
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
0
{
329
0
    SECOidTag certalgtag; /* the certificate's encryption algorithm */
330
0
    SECStatus rv;
331
0
    SECStatus err;
332
0
    PK11SymKey *kek;
333
0
    SECKEYPublicKey *publickey = NULL;
334
0
    SECKEYPublicKey *ourPubKey = NULL;
335
0
    SECKEYPrivateKey *ourPrivKey = NULL;
336
0
    unsigned int bulkkey_size, kek_size;
337
0
    SECAlgorithmID keyWrapAlg;
338
0
    SECOidTag keyEncAlgtag;
339
0
    SECItem keyWrapAlg_params, *keyEncAlg_params, *SharedInfo;
340
0
    CK_MECHANISM_TYPE keyDerivationType, keyWrapMech;
341
0
    CK_ULONG kdf;
342
343
0
    if (genUkm && (ukm->len != 0 || ukm->data != NULL)) {
344
0
        PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
345
0
        return SECFailure;
346
0
    }
347
348
0
    certalgtag = SECOID_GetAlgorithmTag(&(cert->subjectPublicKeyInfo.algorithm));
349
0
    PORT_Assert(certalgtag == SEC_OID_ANSIX962_EC_PUBLIC_KEY);
350
0
    if (certalgtag != SEC_OID_ANSIX962_EC_PUBLIC_KEY) {
351
0
        return SECFailure;
352
0
    }
353
354
    /* Get the public key of the recipient. */
355
0
    publickey = CERT_ExtractPublicKey(cert);
356
0
    if (publickey == NULL)
357
0
        goto loser;
358
359
0
    ourPrivKey = SECKEY_CreateECPrivateKey(&publickey->u.ec.DEREncodedParams,
360
0
                                           &ourPubKey, wincx);
361
0
    if (!ourPrivKey || !ourPubKey) {
362
0
        goto loser;
363
0
    }
364
365
0
    rv = SECITEM_CopyItem(poolp, pubKey, &ourPubKey->u.ec.publicValue);
366
0
    if (rv != SECSuccess) {
367
0
        goto loser;
368
0
    }
369
370
    /* pubKey will be encoded as a BIT STRING, so pubKey->len must be length in bits
371
     * rather than bytes */
372
0
    pubKey->len = pubKey->len * 8;
373
374
0
    SECKEY_DestroyPublicKey(ourPubKey); /* we only need the private key from now on */
375
0
    ourPubKey = NULL;
376
377
0
    bulkkey_size = PK11_GetKeyLength(bulkkey);
378
0
    if (bulkkey_size == 0) {
379
0
        goto loser;
380
0
    }
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
0
    keyWrapAlg_params.len = 2;
386
0
    keyWrapAlg_params.data = (unsigned char *)PORT_ArenaAlloc(poolp, keyWrapAlg_params.len);
387
0
    if (keyWrapAlg_params.data == NULL)
388
0
        goto loser;
389
390
0
    keyWrapAlg_params.data[0] = SEC_ASN1_NULL;
391
0
    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
0
    if ((PK11_GetMechanism(bulkkey) == CKM_AES_CBC) && (bulkkey_size > 16)) {
407
0
        rv = SECOID_SetAlgorithmID(poolp, &keyWrapAlg, SEC_OID_AES_256_KEY_WRAP, &keyWrapAlg_params);
408
0
        kek_size = 32;
409
0
        keyWrapMech = CKM_NSS_AES_KEY_WRAP;
410
0
        kdf = CKD_SHA384_KDF;
411
0
        keyEncAlgtag = SEC_OID_DHSINGLEPASS_STDDH_SHA384KDF_SCHEME;
412
0
        keyDerivationType = CKM_ECDH1_DERIVE;
413
0
    } else {
414
0
        rv = SECOID_SetAlgorithmID(poolp, &keyWrapAlg, SEC_OID_AES_128_KEY_WRAP, &keyWrapAlg_params);
415
0
        kek_size = 16;
416
0
        keyWrapMech = CKM_NSS_AES_KEY_WRAP;
417
0
        kdf = CKD_SHA256_KDF;
418
0
        keyEncAlgtag = SEC_OID_DHSINGLEPASS_STDDH_SHA256KDF_SCHEME;
419
0
        keyDerivationType = CKM_ECDH1_DERIVE;
420
0
    }
421
0
    if (rv != SECSuccess) {
422
0
        goto loser;
423
0
    }
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
0
    if (genUkm) {
430
0
        ukm->type = siBuffer;
431
0
        ukm->len = 64;
432
0
        ukm->data = (unsigned char *)PORT_ArenaAlloc(poolp, ukm->len);
433
434
0
        if (ukm->data == NULL) {
435
0
            goto loser;
436
0
        }
437
0
        rv = PK11_GenerateRandom(ukm->data, ukm->len);
438
0
        if (rv != SECSuccess) {
439
0
            goto loser;
440
0
        }
441
0
    }
442
443
0
    SharedInfo = Create_ECC_CMS_SharedInfo(poolp, &keyWrapAlg,
444
0
                                           ukm,
445
0
                                           kek_size);
446
0
    if (!SharedInfo) {
447
0
        goto loser;
448
0
    }
449
450
    /* Generate the KEK (key exchange key) according to RFC5753 which we use
451
     * to wrap the bulk encryption key. */
452
0
    kek = PK11_PubDeriveWithKDF(ourPrivKey, publickey, PR_TRUE,
453
0
                                NULL, NULL,
454
0
                                keyDerivationType, keyWrapMech,
455
0
                                CKA_WRAP, kek_size, kdf, SharedInfo, NULL);
456
0
    if (!kek) {
457
0
        goto loser;
458
0
    }
459
460
0
    SECKEY_DestroyPublicKey(publickey);
461
0
    SECKEY_DestroyPrivateKey(ourPrivKey);
462
0
    publickey = NULL;
463
0
    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
0
    encKey->len = bulkkey_size + 8;
469
0
    encKey->data = (unsigned char *)PORT_ArenaAlloc(poolp, encKey->len);
470
471
0
    if (encKey->data == NULL) {
472
0
        PK11_FreeSymKey(kek);
473
0
        goto loser;
474
0
    }
475
476
0
    err = PK11_WrapSymKey(keyWrapMech, NULL, kek, bulkkey, encKey);
477
478
0
    PK11_FreeSymKey(kek); /* we do not need the KEK anymore */
479
0
    if (err != SECSuccess) {
480
0
        goto loser;
481
0
    }
482
483
0
    keyEncAlg_params = SEC_ASN1EncodeItem(poolp, NULL, &keyWrapAlg, SEC_ASN1_GET(SECOID_AlgorithmIDTemplate));
484
0
    if (keyEncAlg_params == NULL)
485
0
        goto loser;
486
0
    keyEncAlg_params->type = siBuffer;
487
488
    /* now set keyEncAlg */
489
0
    rv = SECOID_SetAlgorithmID(poolp, keyEncAlg, keyEncAlgtag, keyEncAlg_params);
490
0
    if (rv != SECSuccess) {
491
0
        goto loser;
492
0
    }
493
494
0
    return SECSuccess;
495
496
0
loser:
497
0
    if (publickey) {
498
0
        SECKEY_DestroyPublicKey(publickey);
499
0
    }
500
0
    if (ourPubKey) {
501
0
        SECKEY_DestroyPublicKey(ourPubKey);
502
0
    }
503
0
    if (ourPrivKey) {
504
0
        SECKEY_DestroyPrivateKey(ourPrivKey);
505
0
    }
506
0
    return SECFailure;
507
0
}
508
509
/* TODO: Move to pk11wrap and export? */
510
static int
511
cms_GetKekSizeFromKeyWrapAlgTag(SECOidTag keyWrapAlgtag)
512
0
{
513
0
    switch (keyWrapAlgtag) {
514
0
        case SEC_OID_AES_128_KEY_WRAP:
515
0
            return 16;
516
0
        case SEC_OID_AES_192_KEY_WRAP:
517
0
            return 24;
518
0
        case SEC_OID_AES_256_KEY_WRAP:
519
0
            return 32;
520
0
        default:
521
0
            break;
522
0
    }
523
0
    return 0;
524
0
}
525
526
/* TODO: Move to smimeutil and export? */
527
static CK_ULONG
528
cms_GetKdfFromKeyEncAlgTag(SECOidTag keyEncAlgtag)
529
0
{
530
0
    switch (keyEncAlgtag) {
531
0
        case SEC_OID_DHSINGLEPASS_STDDH_SHA1KDF_SCHEME:
532
0
        case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA1KDF_SCHEME:
533
0
            return CKD_SHA1_KDF;
534
0
        case SEC_OID_DHSINGLEPASS_STDDH_SHA224KDF_SCHEME:
535
0
        case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA224KDF_SCHEME:
536
0
            return CKD_SHA224_KDF;
537
0
        case SEC_OID_DHSINGLEPASS_STDDH_SHA256KDF_SCHEME:
538
0
        case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA256KDF_SCHEME:
539
0
            return CKD_SHA256_KDF;
540
0
        case SEC_OID_DHSINGLEPASS_STDDH_SHA384KDF_SCHEME:
541
0
        case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA384KDF_SCHEME:
542
0
            return CKD_SHA384_KDF;
543
0
        case SEC_OID_DHSINGLEPASS_STDDH_SHA512KDF_SCHEME:
544
0
        case SEC_OID_DHSINGLEPASS_COFACTORDH_SHA512KDF_SCHEME:
545
0
            return CKD_SHA512_KDF;
546
0
        default:
547
0
            break;
548
0
    }
549
0
    return 0;
550
0
}
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
0
{
558
0
    SECAlgorithmID keyWrapAlg;
559
0
    SECOidTag keyEncAlgtag, keyWrapAlgtag;
560
0
    CK_MECHANISM_TYPE target, keyDerivationType, keyWrapMech;
561
0
    CK_ULONG kdf;
562
0
    PK11SymKey *kek = NULL, *bulkkey = NULL;
563
0
    int kek_size;
564
0
    SECKEYPublicKey originatorpublickey;
565
0
    SECItem *oiok_publicKey, *SharedInfo = NULL;
566
0
    SECStatus rv;
567
568
0
    PORT_Memset(&keyWrapAlg, 0, sizeof(SECAlgorithmID));
569
570
0
    PORT_Assert(bulkalgtag != SEC_OID_UNKNOWN);
571
0
    target = PK11_AlgtagToMechanism(bulkalgtag);
572
0
    if (bulkalgtag == SEC_OID_UNKNOWN || target == CKM_INVALID_MECHANISM) {
573
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
574
0
        goto loser;
575
0
    }
576
577
0
    keyEncAlgtag = SECOID_GetAlgorithmTag(keyEncAlg);
578
0
    keyDerivationType = PK11_AlgtagToMechanism(keyEncAlgtag);
579
0
    if ((keyEncAlgtag == SEC_OID_UNKNOWN) ||
580
0
        (keyDerivationType == CKM_INVALID_MECHANISM)) {
581
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
582
0
        goto loser;
583
0
    }
584
585
0
    rv = SEC_ASN1DecodeItem(NULL, &keyWrapAlg,
586
0
                            SEC_ASN1_GET(SECOID_AlgorithmIDTemplate), &(keyEncAlg->parameters));
587
0
    if (rv != SECSuccess) {
588
0
        goto loser;
589
0
    }
590
591
0
    keyWrapAlgtag = SECOID_GetAlgorithmTag(&keyWrapAlg);
592
0
    keyWrapMech = PK11_AlgtagToMechanism(keyWrapAlgtag);
593
0
    if ((keyWrapAlgtag == SEC_OID_UNKNOWN) ||
594
0
        (keyWrapMech == CKM_INVALID_MECHANISM)) {
595
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
596
0
        goto loser;
597
0
    }
598
599
0
    kek_size = cms_GetKekSizeFromKeyWrapAlgTag(keyWrapAlgtag);
600
0
    if (!kek_size) {
601
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
602
0
        goto loser;
603
0
    }
604
605
0
    kdf = cms_GetKdfFromKeyEncAlgTag(keyEncAlgtag);
606
0
    if (!kdf) {
607
0
        PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
608
0
        goto loser;
609
0
    }
610
611
    /* Get originator's public key */
612
    /* TODO: Add support for static-static ECDH */
613
0
    if (oiok->identifierType != NSSCMSOriginatorIDOrKey_OriginatorPublicKey) {
614
0
        PORT_SetError(SEC_ERROR_UNSUPPORTED_MESSAGE_TYPE);
615
0
        goto loser;
616
0
    }
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
0
    oiok_publicKey = &(oiok->id.originatorPublicKey.publicKey);
621
0
    originatorpublickey.keyType = ecKey;
622
0
    originatorpublickey.u.ec.publicValue.data = oiok_publicKey->data;
623
0
    originatorpublickey.u.ec.publicValue.len = oiok_publicKey->len / 8;
624
625
0
    SharedInfo = Create_ECC_CMS_SharedInfo(NULL, &keyWrapAlg, ukm, kek_size);
626
0
    if (!SharedInfo) {
627
0
        goto loser;
628
0
    }
629
630
    /* Generate the KEK (key exchange key) according to RFC5753 which we use
631
     * to wrap the bulk encryption key. */
632
0
    kek = PK11_PubDeriveWithKDF(privkey, &originatorpublickey, PR_TRUE,
633
0
                                NULL, NULL,
634
0
                                keyDerivationType, keyWrapMech,
635
0
                                CKA_WRAP, kek_size, kdf, SharedInfo, wincx);
636
637
0
    SECITEM_FreeItem(SharedInfo, PR_TRUE);
638
639
0
    if (kek == NULL) {
640
0
        goto loser;
641
0
    }
642
643
0
    bulkkey = PK11_UnwrapSymKey(kek, keyWrapMech, NULL, encKey, target, CKA_UNWRAP, 0);
644
0
    PK11_FreeSymKey(kek); /* we do not need the KEK anymore */
645
0
loser:
646
0
    SECOID_DestroyAlgorithmID(&keyWrapAlg, PR_FALSE);
647
0
    return bulkkey;
648
0
}