Coverage Report

Created: 2025-11-05 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/ssl/sslprimitive.c
Line
Count
Source
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * SSL Primitives: Public HKDF and AEAD Functions
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8
9
#include "blapit.h"
10
#include "keyhi.h"
11
#include "pk11pub.h"
12
#include "sechash.h"
13
#include "ssl.h"
14
#include "sslexp.h"
15
#include "sslerr.h"
16
#include "sslproto.h"
17
18
#include "sslimpl.h"
19
#include "tls13con.h"
20
#include "tls13hkdf.h"
21
22
struct SSLAeadContextStr {
23
    /* sigh, the API creates a single context, but then uses either encrypt
24
     * and decrypt on that context. We should take an encrypt/decrypt
25
     * variable here, but for now create two contexts. */
26
    PK11Context *encryptContext;
27
    PK11Context *decryptContext;
28
    int tagLen;
29
    int ivLen;
30
    unsigned char iv[MAX_IV_LENGTH];
31
};
32
33
SECStatus
34
SSLExp_MakeVariantAead(PRUint16 version, PRUint16 cipherSuite, SSLProtocolVariant variant,
35
                       PK11SymKey *secret, const char *labelPrefix,
36
                       unsigned int labelPrefixLen, SSLAeadContext **ctx)
37
0
{
38
0
    SSLAeadContext *out = NULL;
39
0
    char label[255]; // Maximum length label.
40
0
    static const char *const keySuffix = "key";
41
0
    static const char *const ivSuffix = "iv";
42
0
    CK_MECHANISM_TYPE mech;
43
0
    SECItem nullParams = { siBuffer, NULL, 0 };
44
0
    PK11SymKey *key = NULL;
45
46
0
    PORT_Assert(strlen(keySuffix) >= strlen(ivSuffix));
47
0
    if (secret == NULL || ctx == NULL ||
48
0
        (labelPrefix == NULL && labelPrefixLen > 0) ||
49
0
        labelPrefixLen + strlen(keySuffix) > sizeof(label)) {
50
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
51
0
        goto loser;
52
0
    }
53
54
0
    SSLHashType hash;
55
0
    const ssl3BulkCipherDef *cipher;
56
0
    SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite,
57
0
                                          &hash, &cipher);
58
0
    if (rv != SECSuccess) {
59
0
        goto loser; /* Code already set. */
60
0
    }
61
62
0
    out = PORT_ZNew(SSLAeadContext);
63
0
    if (out == NULL) {
64
0
        goto loser;
65
0
    }
66
0
    mech = ssl3_Alg2Mech(cipher->calg);
67
0
    out->ivLen = cipher->iv_size + cipher->explicit_nonce_size;
68
0
    out->tagLen = cipher->tag_size;
69
70
0
    if (labelPrefixLen > 0) {
71
0
        memcpy(label, labelPrefix, labelPrefixLen);
72
0
    }
73
0
    memcpy(label + labelPrefixLen, ivSuffix, strlen(ivSuffix));
74
0
    unsigned int labelLen = labelPrefixLen + strlen(ivSuffix);
75
0
    unsigned int ivLen = cipher->iv_size + cipher->explicit_nonce_size;
76
0
    rv = tls13_HkdfExpandLabelRaw(secret, hash,
77
0
                                  NULL, 0, // Handshake hash.
78
0
                                  label, labelLen, variant,
79
0
                                  out->iv, ivLen);
80
0
    if (rv != SECSuccess) {
81
0
        goto loser;
82
0
    }
83
84
0
    memcpy(label + labelPrefixLen, keySuffix, strlen(keySuffix));
85
0
    labelLen = labelPrefixLen + strlen(keySuffix);
86
0
    rv = tls13_HkdfExpandLabel(secret, hash,
87
0
                               NULL, 0, // Handshake hash.
88
0
                               label, labelLen, mech, cipher->key_size,
89
0
                               variant, &key);
90
0
    if (rv != SECSuccess) {
91
0
        goto loser;
92
0
    }
93
94
    /* We really need to change the API to Create a context for each
95
     * encrypt and decrypt rather than a single call that does both. it's
96
     * almost certain that the underlying application tries to use the same
97
     * context for both. */
98
0
    out->encryptContext = PK11_CreateContextBySymKey(mech,
99
0
                                                     CKA_NSS_MESSAGE | CKA_ENCRYPT,
100
0
                                                     key, &nullParams);
101
0
    if (out->encryptContext == NULL) {
102
0
        goto loser;
103
0
    }
104
105
0
    out->decryptContext = PK11_CreateContextBySymKey(mech,
106
0
                                                     CKA_NSS_MESSAGE | CKA_DECRYPT,
107
0
                                                     key, &nullParams);
108
0
    if (out->decryptContext == NULL) {
109
0
        goto loser;
110
0
    }
111
112
0
    PK11_FreeSymKey(key);
113
0
    *ctx = out;
114
0
    return SECSuccess;
115
116
0
loser:
117
0
    PK11_FreeSymKey(key);
118
0
    SSLExp_DestroyAead(out);
119
0
    return SECFailure;
120
0
}
121
122
SECStatus
123
SSLExp_MakeAead(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *secret,
124
                const char *labelPrefix, unsigned int labelPrefixLen, SSLAeadContext **ctx)
125
0
{
126
0
    return SSLExp_MakeVariantAead(version, cipherSuite, ssl_variant_stream, secret,
127
0
                                  labelPrefix, labelPrefixLen, ctx);
128
0
}
129
130
SECStatus
131
SSLExp_DestroyAead(SSLAeadContext *ctx)
132
0
{
133
0
    if (!ctx) {
134
0
        return SECSuccess;
135
0
    }
136
0
    if (ctx->encryptContext) {
137
0
        PK11_DestroyContext(ctx->encryptContext, PR_TRUE);
138
0
    }
139
0
    if (ctx->decryptContext) {
140
0
        PK11_DestroyContext(ctx->decryptContext, PR_TRUE);
141
0
    }
142
143
0
    PORT_ZFree(ctx, sizeof(*ctx));
144
0
    return SECSuccess;
145
0
}
146
147
/* Bug 1529440 exists to refactor this and the other AEAD uses. */
148
static SECStatus
149
ssl_AeadInner(const SSLAeadContext *ctx, PK11Context *context,
150
              PRBool decrypt, PRUint64 counter,
151
              const PRUint8 *aad, unsigned int aadLen,
152
              const PRUint8 *in, unsigned int inLen,
153
              PRUint8 *out, unsigned int *outLen, unsigned int maxOut)
154
0
{
155
0
    if (ctx == NULL || (aad == NULL && aadLen > 0) || in == NULL ||
156
0
        out == NULL || outLen == NULL) {
157
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
158
0
        return SECFailure;
159
0
    }
160
161
    // Setup the nonce.
162
0
    PRUint8 nonce[sizeof(counter)] = { 0 };
163
0
    sslBuffer nonceBuf = SSL_BUFFER_FIXED(nonce, sizeof(counter));
164
0
    SECStatus rv = sslBuffer_AppendNumber(&nonceBuf, counter, sizeof(counter));
165
0
    if (rv != SECSuccess) {
166
0
        PORT_Assert(0);
167
0
        return SECFailure;
168
0
    }
169
    /* at least on encrypt, we should not be using CKG_NO_GENERATE, but
170
     * the current experimental API has the application tracking the counter
171
     * rather than token. We should look at the QUIC code and see if the
172
     * counter can be moved internally where it belongs. That would
173
     * also get rid of the  formatting code above and have the API
174
     * call tls13_AEAD directly in SSLExp_Aead* */
175
0
    return tls13_AEAD(context, decrypt, CKG_NO_GENERATE, 0, ctx->iv, NULL,
176
0
                      ctx->ivLen, nonce, sizeof(counter), aad, aadLen,
177
0
                      out, outLen, maxOut, ctx->tagLen, in, inLen);
178
0
}
179
180
SECStatus
181
SSLExp_AeadEncrypt(const SSLAeadContext *ctx, PRUint64 counter,
182
                   const PRUint8 *aad, unsigned int aadLen,
183
                   const PRUint8 *plaintext, unsigned int plaintextLen,
184
                   PRUint8 *out, unsigned int *outLen, unsigned int maxOut)
185
0
{
186
    // false == encrypt
187
0
    return ssl_AeadInner(ctx, ctx->encryptContext, PR_FALSE, counter,
188
0
                         aad, aadLen, plaintext, plaintextLen,
189
0
                         out, outLen, maxOut);
190
0
}
191
192
SECStatus
193
SSLExp_AeadDecrypt(const SSLAeadContext *ctx, PRUint64 counter,
194
                   const PRUint8 *aad, unsigned int aadLen,
195
                   const PRUint8 *ciphertext, unsigned int ciphertextLen,
196
                   PRUint8 *out, unsigned int *outLen, unsigned int maxOut)
197
0
{
198
    // true == decrypt
199
0
    return ssl_AeadInner(ctx, ctx->decryptContext, PR_TRUE, counter,
200
0
                         aad, aadLen, ciphertext, ciphertextLen,
201
0
                         out, outLen, maxOut);
202
0
}
203
204
SECStatus
205
SSLExp_HkdfExtract(PRUint16 version, PRUint16 cipherSuite,
206
                   PK11SymKey *salt, PK11SymKey *ikm, PK11SymKey **keyp)
207
0
{
208
0
    if (keyp == NULL) {
209
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
210
0
        return SECFailure;
211
0
    }
212
213
0
    SSLHashType hash;
214
0
    SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite,
215
0
                                          &hash, NULL);
216
0
    if (rv != SECSuccess) {
217
0
        return SECFailure; /* Code already set. */
218
0
    }
219
0
    return tls13_HkdfExtract(salt, ikm, hash, keyp);
220
0
}
221
222
SECStatus
223
SSLExp_HkdfExpandLabel(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *prk,
224
                       const PRUint8 *hsHash, unsigned int hsHashLen,
225
                       const char *label, unsigned int labelLen, PK11SymKey **keyp)
226
0
{
227
0
    return SSLExp_HkdfVariantExpandLabel(version, cipherSuite, prk, hsHash, hsHashLen,
228
0
                                         label, labelLen, ssl_variant_stream, keyp);
229
0
}
230
231
SECStatus
232
SSLExp_HkdfVariantExpandLabel(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *prk,
233
                              const PRUint8 *hsHash, unsigned int hsHashLen,
234
                              const char *label, unsigned int labelLen,
235
                              SSLProtocolVariant variant, PK11SymKey **keyp)
236
0
{
237
0
    if (prk == NULL || keyp == NULL ||
238
0
        label == NULL || labelLen == 0) {
239
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
240
0
        return SECFailure;
241
0
    }
242
243
0
    SSLHashType hash;
244
0
    SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite,
245
0
                                          &hash, NULL);
246
0
    if (rv != SECSuccess) {
247
0
        return SECFailure; /* Code already set. */
248
0
    }
249
0
    return tls13_HkdfExpandLabel(prk, hash, hsHash, hsHashLen, label, labelLen,
250
0
                                 CKM_HKDF_DERIVE,
251
0
                                 tls13_GetHashSizeForHash(hash), variant, keyp);
252
0
}
253
254
SECStatus
255
SSLExp_HkdfExpandLabelWithMech(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *prk,
256
                               const PRUint8 *hsHash, unsigned int hsHashLen,
257
                               const char *label, unsigned int labelLen,
258
                               CK_MECHANISM_TYPE mech, unsigned int keySize,
259
                               PK11SymKey **keyp)
260
0
{
261
0
    return SSLExp_HkdfVariantExpandLabelWithMech(version, cipherSuite, prk, hsHash, hsHashLen,
262
0
                                                 label, labelLen, mech, keySize,
263
0
                                                 ssl_variant_stream, keyp);
264
0
}
265
266
SECStatus
267
SSLExp_HkdfVariantExpandLabelWithMech(PRUint16 version, PRUint16 cipherSuite, PK11SymKey *prk,
268
                                      const PRUint8 *hsHash, unsigned int hsHashLen,
269
                                      const char *label, unsigned int labelLen,
270
                                      CK_MECHANISM_TYPE mech, unsigned int keySize,
271
                                      SSLProtocolVariant variant, PK11SymKey **keyp)
272
0
{
273
0
    if (prk == NULL || keyp == NULL ||
274
0
        label == NULL || labelLen == 0 ||
275
0
        mech == CKM_INVALID_MECHANISM || keySize == 0) {
276
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
277
0
        return SECFailure;
278
0
    }
279
280
0
    SSLHashType hash;
281
0
    SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite,
282
0
                                          &hash, NULL);
283
0
    if (rv != SECSuccess) {
284
0
        return SECFailure; /* Code already set. */
285
0
    }
286
0
    return tls13_HkdfExpandLabel(prk, hash, hsHash, hsHashLen, label, labelLen,
287
0
                                 mech, keySize, variant, keyp);
288
0
}
289
290
SECStatus
291
ssl_CreateMaskingContextInner(PRUint16 version, PRUint16 cipherSuite,
292
                              SSLProtocolVariant variant,
293
                              PK11SymKey *secret,
294
                              const char *label,
295
                              unsigned int labelLen,
296
                              SSLMaskingContext **ctx)
297
5.50k
{
298
5.50k
    if (!secret || !ctx || (!label && labelLen)) {
299
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
300
0
        return SECFailure;
301
0
    }
302
303
5.50k
    SSLMaskingContext *out = PORT_ZNew(SSLMaskingContext);
304
5.50k
    if (out == NULL) {
305
0
        goto loser;
306
0
    }
307
308
5.50k
    SSLHashType hash;
309
5.50k
    const ssl3BulkCipherDef *cipher;
310
5.50k
    SECStatus rv = tls13_GetHashAndCipher(version, cipherSuite,
311
5.50k
                                          &hash, &cipher);
312
5.50k
    if (rv != SECSuccess) {
313
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
314
0
        goto loser; /* Code already set. */
315
0
    }
316
317
5.50k
    out->mech = tls13_SequenceNumberEncryptionMechanism(cipher->calg);
318
5.50k
    if (out->mech == CKM_INVALID_MECHANISM) {
319
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
320
0
        goto loser;
321
0
    }
322
323
    // Derive the masking key
324
5.50k
    rv = tls13_HkdfExpandLabel(secret, hash,
325
5.50k
                               NULL, 0, // Handshake hash.
326
5.50k
                               label, labelLen,
327
5.50k
                               out->mech,
328
5.50k
                               cipher->key_size, variant,
329
5.50k
                               &out->secret);
330
5.50k
    if (rv != SECSuccess) {
331
0
        goto loser;
332
0
    }
333
334
5.50k
    out->version = version;
335
5.50k
    out->cipherSuite = cipherSuite;
336
337
5.50k
    *ctx = out;
338
5.50k
    return SECSuccess;
339
0
loser:
340
0
    SSLExp_DestroyMaskingContext(out);
341
0
    return SECFailure;
342
5.50k
}
343
344
SECStatus
345
ssl_CreateMaskInner(SSLMaskingContext *ctx, const PRUint8 *sample,
346
                    unsigned int sampleLen, PRUint8 *outMask,
347
                    unsigned int maskLen)
348
39.4k
{
349
39.4k
    if (!ctx || !sample || !sampleLen || !outMask || !maskLen) {
350
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
351
0
        return SECFailure;
352
0
    }
353
354
39.4k
    if (ctx->secret == NULL) {
355
0
        PORT_SetError(SEC_ERROR_NO_KEY);
356
0
        return SECFailure;
357
0
    }
358
359
39.4k
    SECStatus rv = SECFailure;
360
39.4k
    unsigned int outMaskLen = 0;
361
39.4k
    int paramLen = 0;
362
363
    /* Internal output len/buf, for use if the caller allocated and requested
364
     * less than one block of output. |oneBlock| should have size equal to the
365
     * largest block size supported below. */
366
39.4k
    PRUint8 oneBlock[AES_BLOCK_SIZE];
367
39.4k
    PRUint8 *outMask_ = outMask;
368
39.4k
    unsigned int maskLen_ = maskLen;
369
370
39.4k
    switch (ctx->mech) {
371
27.5k
        case CKM_AES_ECB:
372
27.5k
            if (sampleLen < AES_BLOCK_SIZE) {
373
0
                PORT_SetError(SEC_ERROR_INVALID_ARGS);
374
0
                return SECFailure;
375
0
            }
376
27.5k
            if (maskLen_ < AES_BLOCK_SIZE) {
377
27.5k
                outMask_ = oneBlock;
378
27.5k
                maskLen_ = sizeof(oneBlock);
379
27.5k
            }
380
27.5k
            rv = PK11_Encrypt(ctx->secret,
381
27.5k
                              ctx->mech,
382
27.5k
                              NULL,
383
27.5k
                              outMask_, &outMaskLen, maskLen_,
384
27.5k
                              sample, AES_BLOCK_SIZE);
385
27.5k
            if (rv == SECSuccess &&
386
27.5k
                maskLen < AES_BLOCK_SIZE) {
387
27.5k
                memcpy(outMask, outMask_, maskLen);
388
27.5k
            }
389
27.5k
            break;
390
11.9k
        case CKM_NSS_CHACHA20_CTR:
391
11.9k
            paramLen = 16;
392
        /* fall through */
393
11.9k
        case CKM_CHACHA20:
394
11.9k
            paramLen = (paramLen) ? paramLen : sizeof(CK_CHACHA20_PARAMS);
395
11.9k
            if (sampleLen < paramLen) {
396
0
                PORT_SetError(SEC_ERROR_INVALID_ARGS);
397
0
                return SECFailure;
398
0
            }
399
400
11.9k
            SECItem param;
401
11.9k
            param.type = siBuffer;
402
11.9k
            param.len = paramLen;
403
11.9k
            param.data = (PRUint8 *)sample; // const-cast :(
404
11.9k
            unsigned char zeros[128] = { 0 };
405
406
11.9k
            if (maskLen > sizeof(zeros)) {
407
0
                PORT_SetError(SEC_ERROR_OUTPUT_LEN);
408
0
                return SECFailure;
409
0
            }
410
411
11.9k
            rv = PK11_Encrypt(ctx->secret,
412
11.9k
                              ctx->mech,
413
11.9k
                              &param,
414
11.9k
                              outMask, &outMaskLen,
415
11.9k
                              maskLen,
416
11.9k
                              zeros, maskLen);
417
11.9k
            break;
418
0
        default:
419
0
            PORT_SetError(SEC_ERROR_INVALID_ARGS);
420
0
            return SECFailure;
421
39.4k
    }
422
423
39.4k
    if (rv != SECSuccess) {
424
0
        PORT_SetError(SEC_ERROR_PKCS11_FUNCTION_FAILED);
425
0
        return SECFailure;
426
0
    }
427
428
    // Ensure we produced at least as much material as requested.
429
39.4k
    if (outMaskLen < maskLen) {
430
0
        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
431
0
        return SECFailure;
432
0
    }
433
434
39.4k
    return SECSuccess;
435
39.4k
}
436
437
SECStatus
438
ssl_DestroyMaskingContextInner(SSLMaskingContext *ctx)
439
535k
{
440
535k
    if (!ctx) {
441
530k
        return SECSuccess;
442
530k
    }
443
444
5.50k
    PK11_FreeSymKey(ctx->secret);
445
5.50k
    PORT_ZFree(ctx, sizeof(*ctx));
446
5.50k
    return SECSuccess;
447
535k
}
448
449
SECStatus
450
SSLExp_CreateMask(SSLMaskingContext *ctx, const PRUint8 *sample,
451
                  unsigned int sampleLen, PRUint8 *outMask,
452
                  unsigned int maskLen)
453
0
{
454
0
    return ssl_CreateMaskInner(ctx, sample, sampleLen, outMask, maskLen);
455
0
}
456
457
SECStatus
458
SSLExp_CreateMaskingContext(PRUint16 version, PRUint16 cipherSuite,
459
                            PK11SymKey *secret,
460
                            const char *label,
461
                            unsigned int labelLen,
462
                            SSLMaskingContext **ctx)
463
0
{
464
0
    return ssl_CreateMaskingContextInner(version, cipherSuite, ssl_variant_stream, secret,
465
0
                                         label, labelLen, ctx);
466
0
}
467
468
SECStatus
469
SSLExp_CreateVariantMaskingContext(PRUint16 version, PRUint16 cipherSuite,
470
                                   SSLProtocolVariant variant,
471
                                   PK11SymKey *secret,
472
                                   const char *label,
473
                                   unsigned int labelLen,
474
                                   SSLMaskingContext **ctx)
475
0
{
476
0
    return ssl_CreateMaskingContextInner(version, cipherSuite, variant, secret,
477
0
                                         label, labelLen, ctx);
478
0
}
479
480
SECStatus
481
SSLExp_DestroyMaskingContext(SSLMaskingContext *ctx)
482
0
{
483
0
    return ssl_DestroyMaskingContextInner(ctx);
484
0
}