Coverage Report

Created: 2024-11-21 07:03

/src/nss-nspr/nss/lib/freebl/cmac.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
#ifdef FREEBL_NO_DEPEND
6
#include "stubs.h"
7
#endif
8
9
#include "rijndael.h"
10
#include "blapi.h"
11
#include "cmac.h"
12
#include "secerr.h"
13
#include "nspr.h"
14
15
struct CMACContextStr {
16
    /* Information about the block cipher to use internally. The cipher should
17
     * be placed in ECB mode so that we can use it to directly encrypt blocks.
18
     *
19
     *
20
     * To add a new cipher, add an entry to CMACCipher, update CMAC_Init,
21
     * cmac_Encrypt, and CMAC_Destroy methods to handle the new cipher, and
22
     * add a new Context pointer to the cipher union with the correct type. */
23
    CMACCipher cipherType;
24
    union {
25
        AESContext *aes;
26
    } cipher;
27
    unsigned int blockSize;
28
29
    /* Internal keys which are conditionally used by the algorithm. Derived
30
     * from encrypting the NULL block. We leave the storing of (and the
31
     * cleanup of) the CMAC key to the underlying block cipher. */
32
    unsigned char k1[MAX_BLOCK_SIZE];
33
    unsigned char k2[MAX_BLOCK_SIZE];
34
35
    /* When Update is called with data which isn't a multiple of the block
36
     * size, we need a place to put it. HMAC handles this by passing it to
37
     * the underlying hash function right away; we can't do that as the
38
     * contract on the cipher object is different. */
39
    unsigned int partialIndex;
40
    unsigned char partialBlock[MAX_BLOCK_SIZE];
41
42
    /* Last encrypted block. This gets xor-ed with partialBlock prior to
43
     * encrypting it. NIST defines this to be the empty string to begin. */
44
    unsigned char lastBlock[MAX_BLOCK_SIZE];
45
};
46
47
static void
48
cmac_ShiftLeftOne(unsigned char *out, const unsigned char *in, int length)
49
2
{
50
2
    int i = 0;
51
32
    for (; i < length - 1; i++) {
52
30
        out[i] = in[i] << 1;
53
30
        out[i] |= in[i + 1] >> 7;
54
30
    }
55
2
    out[i] = in[i] << 1;
56
2
}
57
58
static SECStatus
59
cmac_Encrypt(CMACContext *ctx, unsigned char *output,
60
             const unsigned char *input,
61
             unsigned int inputLen)
62
757
{
63
757
    if (ctx->cipherType == CMAC_AES) {
64
757
        unsigned int tmpOutputLen;
65
757
        SECStatus rv = AES_Encrypt(ctx->cipher.aes, output, &tmpOutputLen,
66
757
                                   ctx->blockSize, input, inputLen);
67
68
        /* Assumption: AES_Encrypt (when in ECB mode) always returns an
69
         * output of length equal to blockSize (what was pass as the value
70
         * of the maxOutputLen parameter). */
71
757
        PORT_Assert(tmpOutputLen == ctx->blockSize);
72
757
        return rv;
73
757
    }
74
75
0
    return SECFailure;
76
757
}
77
78
/* NIST SP.800-38B, 6.1 Subkey Generation */
79
static SECStatus
80
cmac_GenerateSubkeys(CMACContext *ctx)
81
1
{
82
1
    unsigned char null_block[MAX_BLOCK_SIZE] = { 0 };
83
1
    unsigned char L[MAX_BLOCK_SIZE];
84
1
    unsigned char v;
85
1
    unsigned char i;
86
87
    /* Step 1: L = AES(key, null_block) */
88
1
    if (cmac_Encrypt(ctx, L, null_block, ctx->blockSize) != SECSuccess) {
89
0
        return SECFailure;
90
0
    }
91
92
    /* In the following, some effort has been made to be constant time. Rather
93
     * than conditioning on the value of the MSB (of L or K1), we use the loop
94
     * to build a mask for the conditional constant. */
95
96
    /* Step 2: If MSB(L) = 0, K1 = L << 1. Else, K1 = (L << 1) ^ R_b. */
97
1
    cmac_ShiftLeftOne(ctx->k1, L, ctx->blockSize);
98
1
    v = L[0] >> 7;
99
4
    for (i = 1; i <= 7; i <<= 1) {
100
3
        v |= (v << i);
101
3
    }
102
1
    ctx->k1[ctx->blockSize - 1] ^= (0x87 & v);
103
104
    /* Step 3: If MSB(K1) = 0, K2 = K1 << 1. Else, K2 = (K1 <, 1) ^ R_b. */
105
1
    cmac_ShiftLeftOne(ctx->k2, ctx->k1, ctx->blockSize);
106
1
    v = ctx->k1[0] >> 7;
107
4
    for (i = 1; i <= 7; i <<= 1) {
108
3
        v |= (v << i);
109
3
    }
110
1
    ctx->k2[ctx->blockSize - 1] ^= (0x87 & v);
111
112
    /* Any intermediate value in the computation of the subkey shall be
113
     * secret. */
114
1
    PORT_Memset(null_block, 0, MAX_BLOCK_SIZE);
115
1
    PORT_Memset(L, 0, MAX_BLOCK_SIZE);
116
117
    /* Step 4: Return the values. */
118
1
    return SECSuccess;
119
1
}
120
121
/* NIST SP.800-38B, 6.2 MAC Generation step 6 */
122
static SECStatus
123
cmac_UpdateState(CMACContext *ctx)
124
756
{
125
756
    if (ctx == NULL || ctx->partialIndex != ctx->blockSize) {
126
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
127
0
        return SECFailure;
128
0
    }
129
130
    /* Step 6: C_i = CIPHER(key, C_{i-1} ^ M_i)  for 1 <= i <= n, and
131
     *         C_0 is defined as the empty string. */
132
133
12.8k
    for (unsigned int index = 0; index < ctx->blockSize; index++) {
134
12.0k
        ctx->partialBlock[index] ^= ctx->lastBlock[index];
135
12.0k
    }
136
137
756
    return cmac_Encrypt(ctx, ctx->lastBlock, ctx->partialBlock, ctx->blockSize);
138
756
}
139
140
SECStatus
141
CMAC_Init(CMACContext *ctx, CMACCipher type,
142
          const unsigned char *key, unsigned int key_len)
143
1
{
144
1
    if (ctx == NULL) {
145
0
        PORT_SetError(SEC_ERROR_NO_MEMORY);
146
0
        return SECFailure;
147
0
    }
148
149
    /* We only currently support AES-CMAC. */
150
1
    if (type != CMAC_AES) {
151
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
152
0
        return SECFailure;
153
0
    }
154
155
1
    PORT_Memset(ctx, 0, sizeof(*ctx));
156
157
1
    ctx->blockSize = AES_BLOCK_SIZE;
158
1
    ctx->cipherType = CMAC_AES;
159
1
    ctx->cipher.aes = AES_CreateContext(key, NULL, NSS_AES, 1, key_len,
160
1
                                        ctx->blockSize);
161
1
    if (ctx->cipher.aes == NULL) {
162
0
        return SECFailure;
163
0
    }
164
165
1
    return CMAC_Begin(ctx);
166
1
}
167
168
CMACContext *
169
CMAC_Create(CMACCipher type, const unsigned char *key,
170
            unsigned int key_len)
171
1
{
172
1
    CMACContext *result = PORT_New(CMACContext);
173
174
1
    if (CMAC_Init(result, type, key, key_len) != SECSuccess) {
175
0
        CMAC_Destroy(result, PR_TRUE);
176
0
        return NULL;
177
0
    }
178
179
1
    return result;
180
1
}
181
182
SECStatus
183
CMAC_Begin(CMACContext *ctx)
184
1
{
185
1
    if (ctx == NULL) {
186
0
        return SECFailure;
187
0
    }
188
189
    /* Ensure that our blockSize is less than the maximum. When this fails,
190
     * a cipher with a larger block size was added and MAX_BLOCK_SIZE needs
191
     * to be updated accordingly. */
192
1
    PORT_Assert(ctx->blockSize <= MAX_BLOCK_SIZE);
193
194
1
    if (cmac_GenerateSubkeys(ctx) != SECSuccess) {
195
0
        return SECFailure;
196
0
    }
197
198
    /* Set the index to write partial blocks at to zero. This saves us from
199
     * having to clear ctx->partialBlock. */
200
1
    ctx->partialIndex = 0;
201
202
    /* Step 5: Let C_0 = 0^b. */
203
1
    PORT_Memset(ctx->lastBlock, 0, ctx->blockSize);
204
205
1
    return SECSuccess;
206
1
}
207
208
/* NIST SP.800-38B, 6.2 MAC Generation */
209
SECStatus
210
CMAC_Update(CMACContext *ctx, const unsigned char *data,
211
            unsigned int data_len)
212
1
{
213
1
    unsigned int data_index = 0;
214
1
    if (ctx == NULL) {
215
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
216
0
        return SECFailure;
217
0
    }
218
219
1
    if (data == NULL || data_len == 0) {
220
0
        return SECSuccess;
221
0
    }
222
223
    /* Copy as many bytes from data into ctx->partialBlock as we can, up to
224
     * the maximum of the remaining data and the remaining space in
225
     * ctx->partialBlock.
226
     *
227
     * Note that we swap the order (encrypt *then* copy) because the last
228
     * block is different from the rest. If we end on an even multiple of
229
     * the block size, we have to be able to XOR it with K1. But we won't know
230
     * that it is the last until CMAC_Finish is called (and by then, CMAC_Update
231
     * has already returned). */
232
757
    while (data_index < data_len) {
233
756
        if (ctx->partialIndex == ctx->blockSize) {
234
755
            if (cmac_UpdateState(ctx) != SECSuccess) {
235
0
                return SECFailure;
236
0
            }
237
238
755
            ctx->partialIndex = 0;
239
755
        }
240
241
756
        unsigned int copy_len = data_len - data_index;
242
756
        if (copy_len > (ctx->blockSize - ctx->partialIndex)) {
243
755
            copy_len = ctx->blockSize - ctx->partialIndex;
244
755
        }
245
246
756
        PORT_Memcpy(ctx->partialBlock + ctx->partialIndex, data + data_index, copy_len);
247
756
        data_index += copy_len;
248
756
        ctx->partialIndex += copy_len;
249
756
    }
250
251
1
    return SECSuccess;
252
1
}
253
254
/* NIST SP.800-38B, 6.2 MAC Generation */
255
SECStatus
256
CMAC_Finish(CMACContext *ctx, unsigned char *result,
257
            unsigned int *result_len,
258
            unsigned int max_result_len)
259
1
{
260
1
    if (ctx == NULL || result == NULL || max_result_len == 0) {
261
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
262
0
        return SECFailure;
263
0
    }
264
265
1
    if (max_result_len > ctx->blockSize) {
266
        /* This is a weird situation. The PKCS #11 soft tokencode passes
267
         * sizeof(result) here, which is hard-coded as SFTK_MAX_MAC_LENGTH.
268
         * This later gets truncated to min(SFTK_MAX_MAC_LENGTH, requested). */
269
1
        max_result_len = ctx->blockSize;
270
1
    }
271
272
    /* Step 4: If M_n* is a complete block, M_n = K1 ^ M_n*. Else,
273
     * M_n = K2 ^ (M_n* || 10^j). */
274
1
    if (ctx->partialIndex == ctx->blockSize) {
275
        /* XOR in K1. */
276
0
        for (unsigned int index = 0; index < ctx->blockSize; index++) {
277
0
            ctx->partialBlock[index] ^= ctx->k1[index];
278
0
        }
279
1
    } else {
280
        /* Use 10* padding on the partial block. */
281
1
        ctx->partialBlock[ctx->partialIndex++] = 0x80;
282
1
        PORT_Memset(ctx->partialBlock + ctx->partialIndex, 0,
283
1
                    ctx->blockSize - ctx->partialIndex);
284
1
        ctx->partialIndex = ctx->blockSize;
285
286
        /* XOR in K2. */
287
17
        for (unsigned int index = 0; index < ctx->blockSize; index++) {
288
16
            ctx->partialBlock[index] ^= ctx->k2[index];
289
16
        }
290
1
    }
291
292
    /* Encrypt the block. */
293
1
    if (cmac_UpdateState(ctx) != SECSuccess) {
294
0
        return SECFailure;
295
0
    }
296
297
    /* Step 7 & 8: T = MSB_tlen(C_n); return T. */
298
1
    PORT_Memcpy(result, ctx->lastBlock, max_result_len);
299
1
    if (result_len != NULL) {
300
1
        *result_len = max_result_len;
301
1
    }
302
1
    return SECSuccess;
303
1
}
304
305
void
306
CMAC_Destroy(CMACContext *ctx, PRBool free_it)
307
1
{
308
1
    if (ctx == NULL) {
309
0
        return;
310
0
    }
311
312
1
    if (ctx->cipherType == CMAC_AES && ctx->cipher.aes != NULL) {
313
1
        AES_DestroyContext(ctx->cipher.aes, PR_TRUE);
314
1
    }
315
316
    /* Destroy everything in the context. This includes sensitive data in
317
     * K1, K2, and lastBlock. */
318
1
    PORT_Memset(ctx, 0, sizeof(*ctx));
319
320
1
    if (free_it == PR_TRUE) {
321
1
        PORT_Free(ctx);
322
1
    }
323
1
}