/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 |