Coverage Report

Created: 2024-11-21 07:03

/src/nss-nspr/nss/lib/freebl/ctr.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
#include "prtypes.h"
9
#include "blapit.h"
10
#include "blapii.h"
11
#include "ctr.h"
12
#include "pkcs11t.h"
13
#include "secerr.h"
14
15
#ifdef USE_HW_AES
16
#ifdef NSS_X86_OR_X64
17
#include "intel-aes.h"
18
#endif
19
#include "rijndael.h"
20
#endif
21
22
#if defined(__ARM_NEON) || defined(__ARM_NEON__)
23
#include <arm_neon.h>
24
#endif
25
26
SECStatus
27
CTR_InitContext(CTRContext *ctr, void *context, freeblCipherFunc cipher,
28
                const unsigned char *param)
29
1
{
30
1
    const CK_AES_CTR_PARAMS *ctrParams = (const CK_AES_CTR_PARAMS *)param;
31
32
1
    if (ctrParams->ulCounterBits == 0 ||
33
1
        ctrParams->ulCounterBits > AES_BLOCK_SIZE * PR_BITS_PER_BYTE) {
34
0
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
35
0
        return SECFailure;
36
0
    }
37
38
    /* Invariant: 0 < ctr->bufPtr <= AES_BLOCK_SIZE */
39
1
    ctr->checkWrap = PR_FALSE;
40
1
    ctr->bufPtr = AES_BLOCK_SIZE; /* no unused data in the buffer */
41
1
    ctr->cipher = cipher;
42
1
    ctr->context = context;
43
1
    ctr->counterBits = ctrParams->ulCounterBits;
44
1
    if (AES_BLOCK_SIZE > sizeof(ctr->counter) ||
45
1
        AES_BLOCK_SIZE > sizeof(ctrParams->cb)) {
46
0
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
47
0
        return SECFailure;
48
0
    }
49
1
    PORT_Memcpy(ctr->counter, ctrParams->cb, AES_BLOCK_SIZE);
50
1
    if (ctr->counterBits < 64) {
51
1
        PORT_Memcpy(ctr->counterFirst, ctr->counter, AES_BLOCK_SIZE);
52
1
        ctr->checkWrap = PR_TRUE;
53
1
    }
54
1
    return SECSuccess;
55
1
}
56
57
CTRContext *
58
CTR_CreateContext(void *context, freeblCipherFunc cipher,
59
                  const unsigned char *param)
60
0
{
61
0
    CTRContext *ctr;
62
0
    SECStatus rv;
63
64
    /* first fill in the Counter context */
65
0
    ctr = PORT_ZNew(CTRContext);
66
0
    if (ctr == NULL) {
67
0
        return NULL;
68
0
    }
69
0
    rv = CTR_InitContext(ctr, context, cipher, param);
70
0
    if (rv != SECSuccess) {
71
0
        CTR_DestroyContext(ctr, PR_TRUE);
72
0
        ctr = NULL;
73
0
    }
74
0
    return ctr;
75
0
}
76
77
void
78
CTR_DestroyContext(CTRContext *ctr, PRBool freeit)
79
1
{
80
1
    PORT_Memset(ctr, 0, sizeof(CTRContext));
81
1
    if (freeit) {
82
0
        PORT_Free(ctr);
83
0
    }
84
1
}
85
86
/*
87
 * Used by counter mode. Increment the counter block. Not all bits in the
88
 * counter block are part of the counter, counterBits tells how many bits
89
 * are part of the counter. The counter block is blocksize long. It's a
90
 * big endian value.
91
 *
92
 * XXX Does not handle counter rollover.
93
 */
94
static void
95
ctr_GetNextCtr(unsigned char *counter, unsigned int counterBits,
96
               unsigned int blocksize)
97
1
{
98
1
    unsigned char *counterPtr = counter + blocksize - 1;
99
1
    unsigned char mask, count;
100
101
1
    PORT_Assert(counterBits <= blocksize * PR_BITS_PER_BYTE);
102
1
    while (counterBits >= PR_BITS_PER_BYTE) {
103
1
        if (++(*(counterPtr--))) {
104
1
            return;
105
1
        }
106
0
        counterBits -= PR_BITS_PER_BYTE;
107
0
    }
108
0
    if (counterBits == 0) {
109
0
        return;
110
0
    }
111
    /* increment the final partial byte */
112
0
    mask = (1 << counterBits) - 1;
113
0
    count = ++(*counterPtr) & mask;
114
0
    *counterPtr = ((*counterPtr) & ~mask) | count;
115
0
    return;
116
0
}
117
118
static void
119
ctr_xor(unsigned char *target, const unsigned char *x,
120
        const unsigned char *y, unsigned int count)
121
1
{
122
1
    unsigned int i;
123
#if defined(__ARM_NEON) || defined(__ARM_NEON__)
124
    while (count >= 16) {
125
        vst1q_u8(target, veorq_u8(vld1q_u8(x), vld1q_u8(y)));
126
        target += 16;
127
        x += 16;
128
        y += 16;
129
        count -= 16;
130
    }
131
#endif
132
17
    for (i = 0; i < count; i++) {
133
16
        *target++ = *x++ ^ *y++;
134
16
    }
135
1
}
136
137
SECStatus
138
CTR_Update(CTRContext *ctr, unsigned char *outbuf,
139
           unsigned int *outlen, unsigned int maxout,
140
           const unsigned char *inbuf, unsigned int inlen,
141
           unsigned int blocksize)
142
1
{
143
1
    unsigned int tmp;
144
1
    SECStatus rv;
145
146
    // Limit block count to 2^counterBits - 2
147
1
    if (ctr->counterBits < (sizeof(unsigned int) * 8) &&
148
1
        inlen > ((1 << ctr->counterBits) - 2) * AES_BLOCK_SIZE) {
149
0
        PORT_SetError(SEC_ERROR_INPUT_LEN);
150
0
        return SECFailure;
151
0
    }
152
1
    if (maxout < inlen) {
153
0
        *outlen = inlen;
154
0
        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
155
0
        return SECFailure;
156
0
    }
157
1
    *outlen = 0;
158
1
    if (ctr->bufPtr != blocksize) {
159
0
        unsigned int needed = PR_MIN(blocksize - ctr->bufPtr, inlen);
160
0
        ctr_xor(outbuf, inbuf, ctr->buffer + ctr->bufPtr, needed);
161
0
        ctr->bufPtr += needed;
162
0
        outbuf += needed;
163
0
        inbuf += needed;
164
0
        *outlen += needed;
165
0
        inlen -= needed;
166
0
        if (inlen == 0) {
167
0
            return SECSuccess;
168
0
        }
169
0
        PORT_Assert(ctr->bufPtr == blocksize);
170
0
    }
171
172
2
    while (inlen >= blocksize) {
173
1
        rv = (*ctr->cipher)(ctr->context, ctr->buffer, &tmp, blocksize,
174
1
                            ctr->counter, blocksize, blocksize);
175
1
        ctr_GetNextCtr(ctr->counter, ctr->counterBits, blocksize);
176
1
        if (ctr->checkWrap) {
177
1
            if (PORT_Memcmp(ctr->counter, ctr->counterFirst, blocksize) == 0) {
178
0
                PORT_SetError(SEC_ERROR_INVALID_ARGS);
179
0
                return SECFailure;
180
0
            }
181
1
        }
182
1
        if (rv != SECSuccess) {
183
0
            return SECFailure;
184
0
        }
185
1
        ctr_xor(outbuf, inbuf, ctr->buffer, blocksize);
186
1
        outbuf += blocksize;
187
1
        inbuf += blocksize;
188
1
        *outlen += blocksize;
189
1
        inlen -= blocksize;
190
1
    }
191
1
    if (inlen == 0) {
192
1
        return SECSuccess;
193
1
    }
194
0
    rv = (*ctr->cipher)(ctr->context, ctr->buffer, &tmp, blocksize,
195
0
                        ctr->counter, blocksize, blocksize);
196
0
    ctr_GetNextCtr(ctr->counter, ctr->counterBits, blocksize);
197
0
    if (ctr->checkWrap) {
198
0
        if (PORT_Memcmp(ctr->counter, ctr->counterFirst, blocksize) == 0) {
199
0
            PORT_SetError(SEC_ERROR_INVALID_ARGS);
200
0
            return SECFailure;
201
0
        }
202
0
    }
203
0
    if (rv != SECSuccess) {
204
0
        return SECFailure;
205
0
    }
206
0
    ctr_xor(outbuf, inbuf, ctr->buffer, inlen);
207
0
    ctr->bufPtr = inlen;
208
0
    *outlen += inlen;
209
0
    return SECSuccess;
210
0
}
211
212
#if defined(USE_HW_AES) && defined(_MSC_VER) && defined(NSS_X86_OR_X64)
213
SECStatus
214
CTR_Update_HW_AES(CTRContext *ctr, unsigned char *outbuf,
215
                  unsigned int *outlen, unsigned int maxout,
216
                  const unsigned char *inbuf, unsigned int inlen,
217
                  unsigned int blocksize)
218
{
219
    unsigned int fullblocks;
220
    unsigned int tmp;
221
    SECStatus rv;
222
223
    // Limit block count to 2^counterBits - 2
224
    if (ctr->counterBits < (sizeof(unsigned int) * 8) &&
225
        inlen > ((1 << ctr->counterBits) - 2) * AES_BLOCK_SIZE) {
226
        PORT_SetError(SEC_ERROR_INPUT_LEN);
227
        return SECFailure;
228
    }
229
    if (maxout < inlen) {
230
        *outlen = inlen;
231
        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
232
        return SECFailure;
233
    }
234
    *outlen = 0;
235
    if (ctr->bufPtr != blocksize) {
236
        unsigned int needed = PR_MIN(blocksize - ctr->bufPtr, inlen);
237
        ctr_xor(outbuf, inbuf, ctr->buffer + ctr->bufPtr, needed);
238
        ctr->bufPtr += needed;
239
        outbuf += needed;
240
        inbuf += needed;
241
        *outlen += needed;
242
        inlen -= needed;
243
        if (inlen == 0) {
244
            return SECSuccess;
245
        }
246
        PORT_Assert(ctr->bufPtr == blocksize);
247
    }
248
249
    if (inlen >= blocksize) {
250
        rv = intel_aes_ctr_worker(((AESContext *)(ctr->context))->Nr)(
251
            ctr, outbuf, outlen, maxout, inbuf, inlen, blocksize);
252
        if (rv != SECSuccess) {
253
            return SECFailure;
254
        }
255
        fullblocks = (inlen / blocksize) * blocksize;
256
        *outlen += fullblocks;
257
        outbuf += fullblocks;
258
        inbuf += fullblocks;
259
        inlen -= fullblocks;
260
    }
261
262
    if (inlen == 0) {
263
        return SECSuccess;
264
    }
265
    rv = (*ctr->cipher)(ctr->context, ctr->buffer, &tmp, blocksize,
266
                        ctr->counter, blocksize, blocksize);
267
    ctr_GetNextCtr(ctr->counter, ctr->counterBits, blocksize);
268
    if (rv != SECSuccess) {
269
        return SECFailure;
270
    }
271
    ctr_xor(outbuf, inbuf, ctr->buffer, inlen);
272
    ctr->bufPtr = inlen;
273
    *outlen += inlen;
274
    return SECSuccess;
275
}
276
#endif