Coverage Report

Created: 2026-02-05 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/nss/lib/freebl/ctr.c
Line
Count
Source
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
692
{
30
692
    const CK_AES_CTR_PARAMS *ctrParams = (const CK_AES_CTR_PARAMS *)param;
31
32
692
    if (ctrParams->ulCounterBits == 0 ||
33
692
        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
692
    ctr->checkWrap = PR_FALSE;
40
692
    ctr->bufPtr = AES_BLOCK_SIZE; /* no unused data in the buffer */
41
692
    ctr->cipher = cipher;
42
692
    ctr->context = context;
43
692
    ctr->counterBits = ctrParams->ulCounterBits;
44
692
    if (AES_BLOCK_SIZE > sizeof(ctr->counter) ||
45
0
        AES_BLOCK_SIZE > sizeof(ctrParams->cb)) {
46
0
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
47
0
        return SECFailure;
48
0
    }
49
692
    PORT_Memcpy(ctr->counter, ctrParams->cb, AES_BLOCK_SIZE);
50
692
    if (ctr->counterBits < 64) {
51
0
        PORT_Memcpy(ctr->counterFirst, ctr->counter, AES_BLOCK_SIZE);
52
0
        ctr->checkWrap = PR_TRUE;
53
0
    }
54
692
    return SECSuccess;
55
692
}
56
57
CTRContext *
58
CTR_CreateContext(void *context, freeblCipherFunc cipher,
59
                  const unsigned char *param)
60
692
{
61
692
    CTRContext *ctr;
62
692
    SECStatus rv;
63
64
    /* first fill in the Counter context */
65
692
    ctr = PORT_ZNew(CTRContext);
66
692
    if (ctr == NULL) {
67
0
        return NULL;
68
0
    }
69
692
    rv = CTR_InitContext(ctr, context, cipher, param);
70
692
    if (rv != SECSuccess) {
71
0
        CTR_DestroyContext(ctr, PR_TRUE);
72
0
        ctr = NULL;
73
0
    }
74
692
    return ctr;
75
692
}
76
77
void
78
CTR_DestroyContext(CTRContext *ctr, PRBool freeit)
79
692
{
80
692
    PORT_Memset(ctr, 0, sizeof(CTRContext));
81
692
    if (freeit) {
82
692
        PORT_Free(ctr);
83
692
    }
84
692
}
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
4.08k
{
98
4.08k
    unsigned char *counterPtr = counter + blocksize - 1;
99
4.08k
    unsigned char mask, count;
100
101
4.08k
    PORT_Assert(counterBits <= blocksize * PR_BITS_PER_BYTE);
102
4.78k
    while (counterBits >= PR_BITS_PER_BYTE) {
103
4.78k
        if (++(*(counterPtr--))) {
104
4.08k
            return;
105
4.08k
        }
106
701
        counterBits -= PR_BITS_PER_BYTE;
107
701
    }
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
4.69k
{
122
4.69k
    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
62.7k
    for (i = 0; i < count; i++) {
133
58.0k
        *target++ = *x++ ^ *y++;
134
58.0k
    }
135
4.69k
}
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.33k
{
143
1.33k
    unsigned int tmp;
144
1.33k
    SECStatus rv;
145
146
    // Limit block count to 2^counterBits - 2
147
1.33k
    if (ctr->counterBits < (sizeof(unsigned int) * 8) &&
148
0
        inlen > ((1 << ctr->counterBits) - 2) * AES_BLOCK_SIZE) {
149
0
        PORT_SetError(SEC_ERROR_INPUT_LEN);
150
0
        return SECFailure;
151
0
    }
152
1.33k
    if (maxout < inlen) {
153
0
        *outlen = inlen;
154
0
        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
155
0
        return SECFailure;
156
0
    }
157
1.33k
    *outlen = 0;
158
1.33k
    if (ctr->bufPtr != blocksize) {
159
608
        unsigned int needed = PR_MIN(blocksize - ctr->bufPtr, inlen);
160
608
        ctr_xor(outbuf, inbuf, ctr->buffer + ctr->bufPtr, needed);
161
608
        ctr->bufPtr += needed;
162
608
        outbuf += needed;
163
608
        inbuf += needed;
164
608
        *outlen += needed;
165
608
        inlen -= needed;
166
608
        if (inlen == 0) {
167
311
            return SECSuccess;
168
311
        }
169
297
        PORT_Assert(ctr->bufPtr == blocksize);
170
297
    }
171
172
4.12k
    while (inlen >= blocksize) {
173
3.10k
        rv = (*ctr->cipher)(ctr->context, ctr->buffer, &tmp, blocksize,
174
3.10k
                            ctr->counter, blocksize, blocksize);
175
3.10k
        ctr_GetNextCtr(ctr->counter, ctr->counterBits, blocksize);
176
3.10k
        if (ctr->checkWrap) {
177
0
            if (PORT_Memcmp(ctr->counter, ctr->counterFirst, blocksize) == 0) {
178
0
                PORT_SetError(SEC_ERROR_INVALID_ARGS);
179
0
                return SECFailure;
180
0
            }
181
0
        }
182
3.10k
        if (rv != SECSuccess) {
183
0
            return SECFailure;
184
0
        }
185
3.10k
        ctr_xor(outbuf, inbuf, ctr->buffer, blocksize);
186
3.10k
        outbuf += blocksize;
187
3.10k
        inbuf += blocksize;
188
3.10k
        *outlen += blocksize;
189
3.10k
        inlen -= blocksize;
190
3.10k
    }
191
1.02k
    if (inlen == 0) {
192
38
        return SECSuccess;
193
38
    }
194
985
    rv = (*ctr->cipher)(ctr->context, ctr->buffer, &tmp, blocksize,
195
985
                        ctr->counter, blocksize, blocksize);
196
985
    ctr_GetNextCtr(ctr->counter, ctr->counterBits, blocksize);
197
985
    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
985
    if (rv != SECSuccess) {
204
0
        return SECFailure;
205
0
    }
206
985
    ctr_xor(outbuf, inbuf, ctr->buffer, inlen);
207
985
    ctr->bufPtr = inlen;
208
985
    *outlen += inlen;
209
985
    return SECSuccess;
210
985
}
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