Coverage Report

Created: 2024-11-21 07:03

/src/nss-nspr/nss/lib/softoken/sftkhmac.c
Line
Count
Source (jump to first uncovered line)
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
#include "seccomon.h"
6
#include "secerr.h"
7
#include "blapi.h"
8
#include "pkcs11i.h"
9
#include "softoken.h"
10
#include "hmacct.h"
11
12
/* Wrappers to avoid undefined behavior calling functions through a pointer of incorrect type. */
13
static void
14
SFTKMAC_CMAC_Destroy(void *ctx, PRBool freeit)
15
1
{
16
1
    CMACContext *cctx = ctx;
17
1
    CMAC_Destroy(cctx, freeit);
18
1
}
19
20
static void
21
SFTKMAC_HMAC_Destroy(void *ctx, PRBool freeit)
22
68
{
23
68
    HMACContext *hctx = ctx;
24
68
    HMAC_Destroy(hctx, freeit);
25
68
}
26
27
/* sftk_HMACMechanismToHash converts a PKCS#11 MAC mechanism into a freebl hash
28
 * type. */
29
HASH_HashType
30
sftk_HMACMechanismToHash(CK_MECHANISM_TYPE mech)
31
68
{
32
68
    switch (mech) {
33
0
        case CKM_MD2_HMAC:
34
0
            return HASH_AlgMD2;
35
12
        case CKM_MD5_HMAC:
36
12
        case CKM_SSL3_MD5_MAC:
37
12
            return HASH_AlgMD5;
38
14
        case CKM_SHA_1_HMAC:
39
14
        case CKM_SSL3_SHA1_MAC:
40
14
            return HASH_AlgSHA1;
41
11
        case CKM_SHA224_HMAC:
42
11
            return HASH_AlgSHA224;
43
8
        case CKM_SHA256_HMAC:
44
8
            return HASH_AlgSHA256;
45
8
        case CKM_SHA384_HMAC:
46
8
            return HASH_AlgSHA384;
47
15
        case CKM_SHA512_HMAC:
48
15
            return HASH_AlgSHA512;
49
0
        case CKM_SHA3_224_HMAC:
50
0
            return HASH_AlgSHA3_224;
51
0
        case CKM_SHA3_256_HMAC:
52
0
            return HASH_AlgSHA3_256;
53
0
        case CKM_SHA3_384_HMAC:
54
0
            return HASH_AlgSHA3_384;
55
0
        case CKM_SHA3_512_HMAC:
56
0
            return HASH_AlgSHA3_512;
57
68
    }
58
0
    return HASH_AlgNULL;
59
68
}
60
61
static sftk_MACConstantTimeCtx *
62
SetupMAC(CK_MECHANISM_PTR mech, SFTKObject *key)
63
0
{
64
0
    CK_NSS_MAC_CONSTANT_TIME_PARAMS *params =
65
0
        (CK_NSS_MAC_CONSTANT_TIME_PARAMS *)mech->pParameter;
66
0
    sftk_MACConstantTimeCtx *ctx;
67
0
    HASH_HashType alg;
68
0
    SFTKAttribute *keyval;
69
0
    unsigned char secret[sizeof(ctx->secret)];
70
0
    unsigned int secretLength;
71
72
0
    if (mech->ulParameterLen != sizeof(CK_NSS_MAC_CONSTANT_TIME_PARAMS)) {
73
0
        return NULL;
74
0
    }
75
76
0
    alg = sftk_HMACMechanismToHash(params->macAlg);
77
0
    if (alg == HASH_AlgNULL) {
78
0
        return NULL;
79
0
    }
80
81
0
    keyval = sftk_FindAttribute(key, CKA_VALUE);
82
0
    if (keyval == NULL) {
83
0
        return NULL;
84
0
    }
85
0
    secretLength = keyval->attrib.ulValueLen;
86
0
    if (secretLength > sizeof(secret)) {
87
0
        sftk_FreeAttribute(keyval);
88
0
        return NULL;
89
0
    }
90
0
    memcpy(secret, keyval->attrib.pValue, secretLength);
91
0
    sftk_FreeAttribute(keyval);
92
93
0
    ctx = PORT_Alloc(sizeof(sftk_MACConstantTimeCtx));
94
0
    if (!ctx) {
95
0
        PORT_Memset(secret, 0, secretLength);
96
0
        return NULL;
97
0
    }
98
99
0
    memcpy(ctx->secret, secret, secretLength);
100
0
    ctx->secretLength = secretLength;
101
0
    ctx->hash = HASH_GetRawHashObject(alg);
102
0
    ctx->totalLength = params->ulBodyTotalLen;
103
0
    PORT_Memset(secret, 0, secretLength);
104
105
0
    return ctx;
106
0
}
107
108
sftk_MACConstantTimeCtx *
109
sftk_HMACConstantTime_New(CK_MECHANISM_PTR mech, SFTKObject *key)
110
0
{
111
0
    CK_NSS_MAC_CONSTANT_TIME_PARAMS *params =
112
0
        (CK_NSS_MAC_CONSTANT_TIME_PARAMS *)mech->pParameter;
113
0
    sftk_MACConstantTimeCtx *ctx;
114
115
0
    if (params->ulHeaderLen > sizeof(ctx->header)) {
116
0
        return NULL;
117
0
    }
118
0
    ctx = SetupMAC(mech, key);
119
0
    if (!ctx) {
120
0
        return NULL;
121
0
    }
122
123
0
    ctx->headerLength = params->ulHeaderLen;
124
0
    memcpy(ctx->header, params->pHeader, params->ulHeaderLen);
125
0
    return ctx;
126
0
}
127
128
sftk_MACConstantTimeCtx *
129
sftk_SSLv3MACConstantTime_New(CK_MECHANISM_PTR mech, SFTKObject *key)
130
0
{
131
0
    CK_NSS_MAC_CONSTANT_TIME_PARAMS *params =
132
0
        (CK_NSS_MAC_CONSTANT_TIME_PARAMS *)mech->pParameter;
133
0
    unsigned int padLength = 40, j;
134
0
    sftk_MACConstantTimeCtx *ctx;
135
136
0
    if (params->macAlg != CKM_SSL3_MD5_MAC &&
137
0
        params->macAlg != CKM_SSL3_SHA1_MAC) {
138
0
        return NULL;
139
0
    }
140
0
    ctx = SetupMAC(mech, key);
141
0
    if (!ctx) {
142
0
        return NULL;
143
0
    }
144
145
0
    if (params->macAlg == CKM_SSL3_MD5_MAC) {
146
0
        padLength = 48;
147
0
    }
148
149
0
    ctx->headerLength =
150
0
        ctx->secretLength +
151
0
        padLength +
152
0
        params->ulHeaderLen;
153
154
0
    if (ctx->headerLength > sizeof(ctx->header)) {
155
0
        goto loser;
156
0
    }
157
158
0
    j = 0;
159
0
    memcpy(&ctx->header[j], ctx->secret, ctx->secretLength);
160
0
    j += ctx->secretLength;
161
0
    memset(&ctx->header[j], 0x36, padLength);
162
0
    j += padLength;
163
0
    memcpy(&ctx->header[j], params->pHeader, params->ulHeaderLen);
164
165
0
    return ctx;
166
167
0
loser:
168
0
    PORT_Free(ctx);
169
0
    return NULL;
170
0
}
171
172
void
173
sftk_HMACConstantTime_Update(void *pctx, const unsigned char *data, unsigned int len)
174
0
{
175
0
    sftk_MACConstantTimeCtx *ctx = (sftk_MACConstantTimeCtx *)pctx;
176
0
    PORT_CheckSuccess(HMAC_ConstantTime(
177
0
        ctx->mac, NULL, sizeof(ctx->mac),
178
0
        ctx->hash,
179
0
        ctx->secret, ctx->secretLength,
180
0
        ctx->header, ctx->headerLength,
181
0
        data, len,
182
0
        ctx->totalLength));
183
0
}
184
185
void
186
sftk_SSLv3MACConstantTime_Update(void *pctx, const unsigned char *data, unsigned int len)
187
0
{
188
0
    sftk_MACConstantTimeCtx *ctx = (sftk_MACConstantTimeCtx *)pctx;
189
0
    PORT_CheckSuccess(SSLv3_MAC_ConstantTime(
190
0
        ctx->mac, NULL, sizeof(ctx->mac),
191
0
        ctx->hash,
192
0
        ctx->secret, ctx->secretLength,
193
0
        ctx->header, ctx->headerLength,
194
0
        data, len,
195
0
        ctx->totalLength));
196
0
}
197
198
void
199
sftk_MACConstantTime_EndHash(void *pctx, unsigned char *out, unsigned int *outLength,
200
                             unsigned int maxLength)
201
0
{
202
0
    const sftk_MACConstantTimeCtx *ctx = (sftk_MACConstantTimeCtx *)pctx;
203
0
    unsigned int toCopy = ctx->hash->length;
204
0
    if (toCopy > maxLength) {
205
0
        toCopy = maxLength;
206
0
    }
207
0
    memcpy(out, ctx->mac, toCopy);
208
0
    if (outLength) {
209
0
        *outLength = toCopy;
210
0
    }
211
0
}
212
213
void
214
sftk_MACConstantTime_DestroyContext(void *pctx, PRBool free)
215
0
{
216
0
    PORT_ZFree(pctx, sizeof(sftk_MACConstantTimeCtx));
217
0
}
218
219
CK_RV
220
sftk_MAC_Create(CK_MECHANISM_TYPE mech, SFTKObject *key, sftk_MACCtx **ret_ctx)
221
69
{
222
69
    CK_RV ret;
223
224
69
    if (ret_ctx == NULL || key == NULL) {
225
0
        return CKR_HOST_MEMORY;
226
0
    }
227
228
69
    *ret_ctx = PORT_New(sftk_MACCtx);
229
69
    if (*ret_ctx == NULL) {
230
0
        return CKR_HOST_MEMORY;
231
0
    }
232
233
69
    ret = sftk_MAC_Init(*ret_ctx, mech, key);
234
69
    if (ret != CKR_OK) {
235
0
        sftk_MAC_DestroyContext(*ret_ctx, PR_TRUE);
236
0
    }
237
238
69
    return ret;
239
69
}
240
241
CK_RV
242
sftk_MAC_Init(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, SFTKObject *key)
243
69
{
244
69
    SFTKAttribute *keyval = NULL;
245
69
    PRBool isFIPS = sftk_isFIPS(key->slot->slotID);
246
69
    CK_RV ret = CKR_OK;
247
248
    /* Find the actual value of the key. */
249
69
    keyval = sftk_FindAttribute(key, CKA_VALUE);
250
69
    if (keyval == NULL) {
251
0
        ret = CKR_KEY_SIZE_RANGE;
252
0
        goto done;
253
0
    }
254
255
69
    ret = sftk_MAC_InitRaw(ctx, mech,
256
69
                           (const unsigned char *)keyval->attrib.pValue,
257
69
                           keyval->attrib.ulValueLen, isFIPS);
258
259
69
done:
260
69
    if (keyval) {
261
69
        sftk_FreeAttribute(keyval);
262
69
    }
263
69
    return ret;
264
69
}
265
266
CK_RV
267
sftk_MAC_InitRaw(sftk_MACCtx *ctx, CK_MECHANISM_TYPE mech, const unsigned char *key, unsigned int key_len, PRBool isFIPS)
268
69
{
269
69
    const SECHashObject *hashObj = NULL;
270
69
    CK_RV ret = CKR_OK;
271
272
69
    if (ctx == NULL) {
273
0
        return CKR_HOST_MEMORY;
274
0
    }
275
276
    /* Clear the context before use. */
277
69
    PORT_Memset(ctx, 0, sizeof(*ctx));
278
279
    /* Save the mech. */
280
69
    ctx->mech = mech;
281
282
    /* Initialize the correct MAC context. */
283
69
    switch (mech) {
284
0
        case CKM_MD2_HMAC:
285
12
        case CKM_MD5_HMAC:
286
26
        case CKM_SHA_1_HMAC:
287
37
        case CKM_SHA224_HMAC:
288
45
        case CKM_SHA256_HMAC:
289
53
        case CKM_SHA384_HMAC:
290
68
        case CKM_SHA512_HMAC:
291
68
        case CKM_SHA3_224_HMAC:
292
68
        case CKM_SHA3_256_HMAC:
293
68
        case CKM_SHA3_384_HMAC:
294
68
        case CKM_SHA3_512_HMAC:
295
68
            hashObj = HASH_GetRawHashObject(sftk_HMACMechanismToHash(mech));
296
297
            /* Because we condition above only on hashes we know to be valid,
298
             * hashObj should never be NULL. This assert is only useful when
299
             * adding a new hash function (for which only partial support has
300
             * been added); thus there is no need to turn it into an if and
301
             * avoid the NULL dereference on the following line. */
302
68
            PR_ASSERT(hashObj != NULL);
303
68
            ctx->mac_size = hashObj->length;
304
305
68
            goto hmac;
306
1
        case CKM_AES_CMAC:
307
1
            ctx->mac.cmac = CMAC_Create(CMAC_AES, key, key_len);
308
1
            ctx->destroy_func = SFTKMAC_CMAC_Destroy;
309
310
            /* Copy the behavior of sftk_doCMACInit here. */
311
1
            if (ctx->mac.cmac == NULL) {
312
0
                if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) {
313
0
                    ret = CKR_KEY_SIZE_RANGE;
314
0
                    goto done;
315
0
                }
316
317
0
                ret = CKR_HOST_MEMORY;
318
0
                goto done;
319
0
            }
320
321
1
            ctx->mac_size = AES_BLOCK_SIZE;
322
323
1
            goto done;
324
0
        default:
325
0
            ret = CKR_MECHANISM_PARAM_INVALID;
326
0
            goto done;
327
69
    }
328
329
68
hmac:
330
68
    ctx->mac.hmac = HMAC_Create(hashObj, key, key_len, isFIPS);
331
68
    ctx->destroy_func = SFTKMAC_HMAC_Destroy;
332
333
    /* Copy the behavior of sftk_doHMACInit here. */
334
68
    if (ctx->mac.hmac == NULL) {
335
0
        if (PORT_GetError() == SEC_ERROR_INVALID_ARGS) {
336
0
            ret = CKR_KEY_SIZE_RANGE;
337
0
            goto done;
338
0
        }
339
0
        ret = CKR_HOST_MEMORY;
340
0
        goto done;
341
0
    }
342
343
    /* Semantics: HMAC and CMAC should behave the same. Begin HMAC now. */
344
68
    HMAC_Begin(ctx->mac.hmac);
345
346
69
done:
347
    /* Handle a failure: ctx->mac.raw should be NULL, but make sure
348
     * destroy_func isn't set. */
349
69
    if (ret != CKR_OK) {
350
0
        ctx->destroy_func = NULL;
351
0
    }
352
353
69
    return ret;
354
68
}
355
356
CK_RV
357
sftk_MAC_Reset(sftk_MACCtx *ctx)
358
0
{
359
    /* Useful for resetting the state of MAC prior to calling update again
360
     *
361
     * This lets the caller keep a single MAC instance and re-use it as long
362
     * as the key stays the same. */
363
0
    switch (ctx->mech) {
364
0
        case CKM_MD2_HMAC:
365
0
        case CKM_MD5_HMAC:
366
0
        case CKM_SHA_1_HMAC:
367
0
        case CKM_SHA224_HMAC:
368
0
        case CKM_SHA256_HMAC:
369
0
        case CKM_SHA384_HMAC:
370
0
        case CKM_SHA512_HMAC:
371
0
        case CKM_SHA3_224_HMAC:
372
0
        case CKM_SHA3_256_HMAC:
373
0
        case CKM_SHA3_384_HMAC:
374
0
        case CKM_SHA3_512_HMAC:
375
0
            HMAC_Begin(ctx->mac.hmac);
376
0
            break;
377
0
        case CKM_AES_CMAC:
378
0
            if (CMAC_Begin(ctx->mac.cmac) != SECSuccess) {
379
0
                return CKR_FUNCTION_FAILED;
380
0
            }
381
0
            break;
382
0
        default:
383
            /* This shouldn't happen -- asserting indicates partial support
384
             * for a new MAC type. */
385
0
            PR_ASSERT(PR_FALSE);
386
0
            return CKR_FUNCTION_FAILED;
387
0
    }
388
389
0
    return CKR_OK;
390
0
}
391
392
CK_RV
393
sftk_MAC_Update(sftk_MACCtx *ctx, const CK_BYTE *data, unsigned int data_len)
394
524
{
395
524
    switch (ctx->mech) {
396
0
        case CKM_MD2_HMAC:
397
98
        case CKM_MD5_HMAC:
398
192
        case CKM_SHA_1_HMAC:
399
240
        case CKM_SHA224_HMAC:
400
324
        case CKM_SHA256_HMAC:
401
392
        case CKM_SHA384_HMAC:
402
523
        case CKM_SHA512_HMAC:
403
523
        case CKM_SHA3_224_HMAC:
404
523
        case CKM_SHA3_256_HMAC:
405
523
        case CKM_SHA3_384_HMAC:
406
523
        case CKM_SHA3_512_HMAC:
407
            /* HMAC doesn't indicate failure in the return code. */
408
523
            HMAC_Update(ctx->mac.hmac, data, data_len);
409
523
            break;
410
1
        case CKM_AES_CMAC:
411
            /* CMAC indicates failure in the return code, however this is
412
             * unlikely to occur. */
413
1
            if (CMAC_Update(ctx->mac.cmac, data, data_len) != SECSuccess) {
414
0
                return CKR_FUNCTION_FAILED;
415
0
            }
416
1
            break;
417
1
        default:
418
            /* This shouldn't happen -- asserting indicates partial support
419
             * for a new MAC type. */
420
0
            PR_ASSERT(PR_FALSE);
421
0
            return CKR_FUNCTION_FAILED;
422
524
    }
423
524
    return CKR_OK;
424
524
}
425
426
CK_RV
427
sftk_MAC_End(sftk_MACCtx *ctx, CK_BYTE_PTR result, unsigned int *result_len, unsigned int max_result_len)
428
69
{
429
69
    unsigned int actual_result_len;
430
431
69
    switch (ctx->mech) {
432
0
        case CKM_MD2_HMAC:
433
12
        case CKM_MD5_HMAC:
434
26
        case CKM_SHA_1_HMAC:
435
37
        case CKM_SHA224_HMAC:
436
45
        case CKM_SHA256_HMAC:
437
53
        case CKM_SHA384_HMAC:
438
68
        case CKM_SHA512_HMAC:
439
68
        case CKM_SHA3_224_HMAC:
440
68
        case CKM_SHA3_256_HMAC:
441
68
        case CKM_SHA3_384_HMAC:
442
68
        case CKM_SHA3_512_HMAC:
443
            /* HMAC doesn't indicate failure in the return code. Additionally,
444
             * unlike CMAC, it doesn't support partial results. This means that we
445
             * need to allocate a buffer if max_result_len < ctx->mac_size. */
446
68
            if (max_result_len >= ctx->mac_size) {
447
                /* Split this into two calls to avoid an unnecessary stack
448
                 * allocation and memcpy when possible. */
449
68
                HMAC_Finish(ctx->mac.hmac, result, &actual_result_len, max_result_len);
450
68
            } else {
451
0
                uint8_t tmp_buffer[SFTK_MAX_MAC_LENGTH];
452
453
                /* Assumption: buffer is large enough to hold this HMAC's
454
                 * output. */
455
0
                PR_ASSERT(SFTK_MAX_MAC_LENGTH >= ctx->mac_size);
456
457
0
                HMAC_Finish(ctx->mac.hmac, tmp_buffer, &actual_result_len, SFTK_MAX_MAC_LENGTH);
458
459
0
                if (actual_result_len > max_result_len) {
460
                    /* This should always be true since:
461
                     *
462
                     *   (SFTK_MAX_MAC_LENGTH >= ctx->mac_size =
463
                     *       actual_result_len) > max_result_len,
464
                     *
465
                     * but guard this truncation just in case. */
466
0
                    actual_result_len = max_result_len;
467
0
                }
468
469
0
                PORT_Memcpy(result, tmp_buffer, actual_result_len);
470
0
            }
471
68
            break;
472
1
        case CKM_AES_CMAC:
473
            /* CMAC indicates failure in the return code, however this is
474
             * unlikely to occur. */
475
1
            if (CMAC_Finish(ctx->mac.cmac, result, &actual_result_len, max_result_len) != SECSuccess) {
476
0
                return CKR_FUNCTION_FAILED;
477
0
            }
478
1
            break;
479
1
        default:
480
            /* This shouldn't happen -- asserting indicates partial support
481
             * for a new MAC type. */
482
0
            PR_ASSERT(PR_FALSE);
483
0
            return CKR_FUNCTION_FAILED;
484
69
    }
485
486
69
    if (result_len) {
487
        /* When result length is passed, inform the caller of its value. */
488
69
        *result_len = actual_result_len;
489
69
    } else if (max_result_len == ctx->mac_size) {
490
        /* Validate that the amount requested was what was actually given; the
491
         * caller assumes that what they passed was the output size of the
492
         * underlying MAC and that they got all the bytes the asked for. */
493
0
        PR_ASSERT(actual_result_len == max_result_len);
494
0
    }
495
496
69
    return CKR_OK;
497
69
}
498
499
void
500
sftk_MAC_DestroyContext(sftk_MACCtx *ctx, PRBool free_it)
501
69
{
502
69
    if (ctx == NULL) {
503
0
        return;
504
0
    }
505
506
69
    if (ctx->mac.raw != NULL && ctx->destroy_func != NULL) {
507
69
        ctx->destroy_func(ctx->mac.raw, PR_TRUE);
508
69
    }
509
510
    /* Clean up the struct so we don't double free accidentally. */
511
69
    PORT_Memset(ctx, 0, sizeof(sftk_MACCtx));
512
513
69
    if (free_it == PR_TRUE) {
514
69
        PORT_Free(ctx);
515
69
    }
516
69
}