summaryrefslogtreecommitdiff
path: root/security/nss/lib/freebl/cmac.c
blob: 222cef1b4c8555bef036c7a72f21d17683088746 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifdef FREEBL_NO_DEPEND
#include "stubs.h"
#endif

#include "rijndael.h"
#include "blapi.h"
#include "cmac.h"
#include "secerr.h"
#include "nspr.h"

struct CMACContextStr {
    /* Information about the block cipher to use internally. The cipher should
     * be placed in ECB mode so that we can use it to directly encrypt blocks.
     *
     *
     * To add a new cipher, add an entry to CMACCipher, update CMAC_Init,
     * cmac_Encrypt, and CMAC_Destroy methods to handle the new cipher, and
     * add a new Context pointer to the cipher union with the correct type. */
    CMACCipher cipherType;
    union {
        AESContext *aes;
    } cipher;
    unsigned int blockSize;

    /* Internal keys which are conditionally used by the algorithm. Derived
     * from encrypting the NULL block. We leave the storing of (and the
     * cleanup of) the CMAC key to the underlying block cipher. */
    unsigned char k1[MAX_BLOCK_SIZE];
    unsigned char k2[MAX_BLOCK_SIZE];

    /* When Update is called with data which isn't a multiple of the block
     * size, we need a place to put it. HMAC handles this by passing it to
     * the underlying hash function right away; we can't do that as the
     * contract on the cipher object is different. */
    unsigned int partialIndex;
    unsigned char partialBlock[MAX_BLOCK_SIZE];

    /* Last encrypted block. This gets xor-ed with partialBlock prior to
     * encrypting it. NIST defines this to be the empty string to begin. */
    unsigned char lastBlock[MAX_BLOCK_SIZE];
};

static void
cmac_ShiftLeftOne(unsigned char *out, const unsigned char *in, int length)
{
    int i = 0;
    for (; i < length - 1; i++) {
        out[i] = in[i] << 1;
        out[i] |= in[i + 1] >> 7;
    }
    out[i] = in[i] << 1;
}

static SECStatus
cmac_Encrypt(CMACContext *ctx, unsigned char *output,
             const unsigned char *input,
             unsigned int inputLen)
{
    if (ctx->cipherType == CMAC_AES) {
        unsigned int tmpOutputLen;
        SECStatus rv = AES_Encrypt(ctx->cipher.aes, output, &tmpOutputLen,
                                   ctx->blockSize, input, inputLen);

        /* Assumption: AES_Encrypt (when in ECB mode) always returns an
         * output of length equal to blockSize (what was pass as the value
         * of the maxOutputLen parameter). */
        PORT_Assert(tmpOutputLen == ctx->blockSize);
        return rv;
    }

    return SECFailure;
}

/* NIST SP.800-38B, 6.1 Subkey Generation */
static SECStatus
cmac_GenerateSubkeys(CMACContext *ctx)
{
    unsigned char null_block[MAX_BLOCK_SIZE] = { 0 };
    unsigned char L[MAX_BLOCK_SIZE];
    unsigned char v;
    unsigned char i;

    /* Step 1: L = AES(key, null_block) */
    if (cmac_Encrypt(ctx, L, null_block, ctx->blockSize) != SECSuccess) {
        return SECFailure;
    }

    /* In the following, some effort has been made to be constant time. Rather
     * than conditioning on the value of the MSB (of L or K1), we use the loop
     * to build a mask for the conditional constant. */

    /* Step 2: If MSB(L) = 0, K1 = L << 1. Else, K1 = (L << 1) ^ R_b. */
    cmac_ShiftLeftOne(ctx->k1, L, ctx->blockSize);
    v = L[0] >> 7;
    for (i = 1; i <= 7; i <<= 1) {
        v |= (v << i);
    }
    ctx->k1[ctx->blockSize - 1] ^= (0x87 & v);

    /* Step 3: If MSB(K1) = 0, K2 = K1 << 1. Else, K2 = (K1 <, 1) ^ R_b. */
    cmac_ShiftLeftOne(ctx->k2, ctx->k1, ctx->blockSize);
    v = ctx->k1[0] >> 7;
    for (i = 1; i <= 7; i <<= 1) {
        v |= (v << i);
    }
    ctx->k2[ctx->blockSize - 1] ^= (0x87 & v);

    /* Any intermediate value in the computation of the subkey shall be
     * secret. */
    PORT_Memset(null_block, 0, MAX_BLOCK_SIZE);
    PORT_Memset(L, 0, MAX_BLOCK_SIZE);

    /* Step 4: Return the values. */
    return SECSuccess;
}

/* NIST SP.800-38B, 6.2 MAC Generation step 6 */
static SECStatus
cmac_UpdateState(CMACContext *ctx)
{
    if (ctx == NULL || ctx->partialIndex != ctx->blockSize) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    /* Step 6: C_i = CIPHER(key, C_{i-1} ^ M_i)  for 1 <= i <= n, and
     *         C_0 is defined as the empty string. */

    for (unsigned int index = 0; index < ctx->blockSize; index++) {
        ctx->partialBlock[index] ^= ctx->lastBlock[index];
    }

    return cmac_Encrypt(ctx, ctx->lastBlock, ctx->partialBlock, ctx->blockSize);
}

SECStatus
CMAC_Init(CMACContext *ctx, CMACCipher type,
          const unsigned char *key, unsigned int key_len)
{
    if (ctx == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        return SECFailure;
    }

    /* We only currently support AES-CMAC. */
    if (type != CMAC_AES) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    PORT_Memset(ctx, 0, sizeof(*ctx));

    ctx->blockSize = AES_BLOCK_SIZE;
    ctx->cipherType = CMAC_AES;
    ctx->cipher.aes = AES_CreateContext(key, NULL, NSS_AES, 1, key_len,
                                        ctx->blockSize);
    if (ctx->cipher.aes == NULL) {
        return SECFailure;
    }

    return CMAC_Begin(ctx);
}

CMACContext *
CMAC_Create(CMACCipher type, const unsigned char *key,
            unsigned int key_len)
{
    CMACContext *result = PORT_New(CMACContext);

    if (CMAC_Init(result, type, key, key_len) != SECSuccess) {
        CMAC_Destroy(result, PR_TRUE);
        return NULL;
    }

    return result;
}

SECStatus
CMAC_Begin(CMACContext *ctx)
{
    if (ctx == NULL) {
        return SECFailure;
    }

    /* Ensure that our blockSize is less than the maximum. When this fails,
     * a cipher with a larger block size was added and MAX_BLOCK_SIZE needs
     * to be updated accordingly. */
    PORT_Assert(ctx->blockSize <= MAX_BLOCK_SIZE);

    if (cmac_GenerateSubkeys(ctx) != SECSuccess) {
        return SECFailure;
    }

    /* Set the index to write partial blocks at to zero. This saves us from
     * having to clear ctx->partialBlock. */
    ctx->partialIndex = 0;

    /* Step 5: Let C_0 = 0^b. */
    PORT_Memset(ctx->lastBlock, 0, ctx->blockSize);

    return SECSuccess;
}

/* NIST SP.800-38B, 6.2 MAC Generation */
SECStatus
CMAC_Update(CMACContext *ctx, const unsigned char *data,
            unsigned int data_len)
{
    unsigned int data_index = 0;
    if (ctx == NULL) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    if (data == NULL || data_len == 0) {
        return SECSuccess;
    }

    /* Copy as many bytes from data into ctx->partialBlock as we can, up to
     * the maximum of the remaining data and the remaining space in
     * ctx->partialBlock.
     *
     * Note that we swap the order (encrypt *then* copy) because the last
     * block is different from the rest. If we end on an even multiple of
     * the block size, we have to be able to XOR it with K1. But we won't know
     * that it is the last until CMAC_Finish is called (and by then, CMAC_Update
     * has already returned). */
    while (data_index < data_len) {
        if (ctx->partialIndex == ctx->blockSize) {
            if (cmac_UpdateState(ctx) != SECSuccess) {
                return SECFailure;
            }

            ctx->partialIndex = 0;
        }

        unsigned int copy_len = data_len - data_index;
        if (copy_len > (ctx->blockSize - ctx->partialIndex)) {
            copy_len = ctx->blockSize - ctx->partialIndex;
        }

        PORT_Memcpy(ctx->partialBlock + ctx->partialIndex, data + data_index, copy_len);
        data_index += copy_len;
        ctx->partialIndex += copy_len;
    }

    return SECSuccess;
}

/* NIST SP.800-38B, 6.2 MAC Generation */
SECStatus
CMAC_Finish(CMACContext *ctx, unsigned char *result,
            unsigned int *result_len,
            unsigned int max_result_len)
{
    if (ctx == NULL || result == NULL || max_result_len == 0) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    if (max_result_len > ctx->blockSize) {
        /* This is a weird situation. The PKCS #11 soft tokencode passes
         * sizeof(result) here, which is hard-coded as SFTK_MAX_MAC_LENGTH.
         * This later gets truncated to min(SFTK_MAX_MAC_LENGTH, requested). */
        max_result_len = ctx->blockSize;
    }

    /* Step 4: If M_n* is a complete block, M_n = K1 ^ M_n*. Else,
     * M_n = K2 ^ (M_n* || 10^j). */
    if (ctx->partialIndex == ctx->blockSize) {
        /* XOR in K1. */
        for (unsigned int index = 0; index < ctx->blockSize; index++) {
            ctx->partialBlock[index] ^= ctx->k1[index];
        }
    } else {
        /* Use 10* padding on the partial block. */
        ctx->partialBlock[ctx->partialIndex++] = 0x80;
        PORT_Memset(ctx->partialBlock + ctx->partialIndex, 0,
                    ctx->blockSize - ctx->partialIndex);
        ctx->partialIndex = ctx->blockSize;

        /* XOR in K2. */
        for (unsigned int index = 0; index < ctx->blockSize; index++) {
            ctx->partialBlock[index] ^= ctx->k2[index];
        }
    }

    /* Encrypt the block. */
    if (cmac_UpdateState(ctx) != SECSuccess) {
        return SECFailure;
    }

    /* Step 7 & 8: T = MSB_tlen(C_n); return T. */
    PORT_Memcpy(result, ctx->lastBlock, max_result_len);
    if (result_len != NULL) {
        *result_len = max_result_len;
    }
    return SECSuccess;
}

void
CMAC_Destroy(CMACContext *ctx, PRBool free_it)
{
    if (ctx == NULL) {
        return;
    }

    if (ctx->cipherType == CMAC_AES && ctx->cipher.aes != NULL) {
        AES_DestroyContext(ctx->cipher.aes, PR_TRUE);
    }

    /* Destroy everything in the context. This includes sensitive data in
     * K1, K2, and lastBlock. */
    PORT_Memset(ctx, 0, sizeof(*ctx));

    if (free_it == PR_TRUE) {
        PORT_Free(ctx);
    }
}