/src/BearSSL/src/aead/ccm.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org> |
3 | | * |
4 | | * Permission is hereby granted, free of charge, to any person obtaining |
5 | | * a copy of this software and associated documentation files (the |
6 | | * "Software"), to deal in the Software without restriction, including |
7 | | * without limitation the rights to use, copy, modify, merge, publish, |
8 | | * distribute, sublicense, and/or sell copies of the Software, and to |
9 | | * permit persons to whom the Software is furnished to do so, subject to |
10 | | * the following conditions: |
11 | | * |
12 | | * The above copyright notice and this permission notice shall be |
13 | | * included in all copies or substantial portions of the Software. |
14 | | * |
15 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
16 | | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
17 | | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
18 | | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
19 | | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
20 | | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
21 | | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
22 | | * SOFTWARE. |
23 | | */ |
24 | | |
25 | | #include "inner.h" |
26 | | |
27 | | /* |
28 | | * Implementation Notes |
29 | | * ==================== |
30 | | * |
31 | | * The combined CTR + CBC-MAC functions can only handle full blocks, |
32 | | * so some buffering is necessary. |
33 | | * |
34 | | * - 'ptr' contains a value from 0 to 15, which is the number of bytes |
35 | | * accumulated in buf[] that still needs to be processed with the |
36 | | * current CBC-MAC computation. |
37 | | * |
38 | | * - When processing the message itself, CTR encryption/decryption is |
39 | | * also done at the same time. The first 'ptr' bytes of buf[] then |
40 | | * contains the plaintext bytes, while the last '16 - ptr' bytes of |
41 | | * buf[] are the remnants of the stream block, to be used against |
42 | | * the next input bytes, when available. When 'ptr' is 0, the |
43 | | * contents of buf[] are to be ignored. |
44 | | * |
45 | | * - The current counter and running CBC-MAC values are kept in 'ctr' |
46 | | * and 'cbcmac', respectively. |
47 | | */ |
48 | | |
49 | | /* see bearssl_block.h */ |
50 | | void |
51 | | br_ccm_init(br_ccm_context *ctx, const br_block_ctrcbc_class **bctx) |
52 | 4.07k | { |
53 | 4.07k | ctx->bctx = bctx; |
54 | 4.07k | } |
55 | | |
56 | | /* see bearssl_block.h */ |
57 | | int |
58 | | br_ccm_reset(br_ccm_context *ctx, const void *nonce, size_t nonce_len, |
59 | | uint64_t aad_len, uint64_t data_len, size_t tag_len) |
60 | 4.07k | { |
61 | 4.07k | unsigned char tmp[16]; |
62 | 4.07k | unsigned u, q; |
63 | | |
64 | 4.07k | if (nonce_len < 7 || nonce_len > 13) { |
65 | 246 | return 0; |
66 | 246 | } |
67 | 3.82k | if (tag_len < 4 || tag_len > 16 || (tag_len & 1) != 0) { |
68 | 207 | return 0; |
69 | 207 | } |
70 | 3.61k | q = 15 - (unsigned)nonce_len; |
71 | 3.61k | ctx->tag_len = tag_len; |
72 | | |
73 | | /* |
74 | | * Block B0, to start CBC-MAC. |
75 | | */ |
76 | 3.61k | tmp[0] = (aad_len > 0 ? 0x40 : 0x00) |
77 | 3.61k | | (((unsigned)tag_len - 2) << 2) |
78 | 3.61k | | (q - 1); |
79 | 3.61k | memcpy(tmp + 1, nonce, nonce_len); |
80 | 21.5k | for (u = 0; u < q; u ++) { |
81 | 17.8k | tmp[15 - u] = (unsigned char)data_len; |
82 | 17.8k | data_len >>= 8; |
83 | 17.8k | } |
84 | 3.61k | if (data_len != 0) { |
85 | | /* |
86 | | * If the data length was not entirely consumed in the |
87 | | * loop above, then it exceeds the maximum limit of |
88 | | * q bytes (when encoded). |
89 | | */ |
90 | 0 | return 0; |
91 | 0 | } |
92 | | |
93 | | /* |
94 | | * Start CBC-MAC. |
95 | | */ |
96 | 3.61k | memset(ctx->cbcmac, 0, sizeof ctx->cbcmac); |
97 | 3.61k | (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, tmp, sizeof tmp); |
98 | | |
99 | | /* |
100 | | * Assemble AAD length header. |
101 | | */ |
102 | 3.61k | if ((aad_len >> 32) != 0) { |
103 | 0 | ctx->buf[0] = 0xFF; |
104 | 0 | ctx->buf[1] = 0xFF; |
105 | 0 | br_enc64be(ctx->buf + 2, aad_len); |
106 | 0 | ctx->ptr = 10; |
107 | 3.61k | } else if (aad_len >= 0xFF00) { |
108 | 31 | ctx->buf[0] = 0xFF; |
109 | 31 | ctx->buf[1] = 0xFE; |
110 | 31 | br_enc32be(ctx->buf + 2, (uint32_t)aad_len); |
111 | 31 | ctx->ptr = 6; |
112 | 3.58k | } else if (aad_len > 0) { |
113 | 687 | br_enc16be(ctx->buf, (unsigned)aad_len); |
114 | 687 | ctx->ptr = 2; |
115 | 2.90k | } else { |
116 | 2.90k | ctx->ptr = 0; |
117 | 2.90k | } |
118 | | |
119 | | /* |
120 | | * Make initial counter value and compute tag mask. |
121 | | */ |
122 | 3.61k | ctx->ctr[0] = q - 1; |
123 | 3.61k | memcpy(ctx->ctr + 1, nonce, nonce_len); |
124 | 3.61k | memset(ctx->ctr + 1 + nonce_len, 0, q); |
125 | 3.61k | memset(ctx->tagmask, 0, sizeof ctx->tagmask); |
126 | 3.61k | (*ctx->bctx)->ctr(ctx->bctx, ctx->ctr, |
127 | 3.61k | ctx->tagmask, sizeof ctx->tagmask); |
128 | | |
129 | 3.61k | return 1; |
130 | 3.61k | } |
131 | | |
132 | | /* see bearssl_block.h */ |
133 | | void |
134 | | br_ccm_aad_inject(br_ccm_context *ctx, const void *data, size_t len) |
135 | 9.55k | { |
136 | 9.55k | const unsigned char *dbuf; |
137 | 9.55k | size_t ptr; |
138 | | |
139 | 9.55k | dbuf = data; |
140 | | |
141 | | /* |
142 | | * Complete partial block, if needed. |
143 | | */ |
144 | 9.55k | ptr = ctx->ptr; |
145 | 9.55k | if (ptr != 0) { |
146 | 6.21k | size_t clen; |
147 | | |
148 | 6.21k | clen = (sizeof ctx->buf) - ptr; |
149 | 6.21k | if (clen > len) { |
150 | 5.45k | memcpy(ctx->buf + ptr, dbuf, len); |
151 | 5.45k | ctx->ptr = ptr + len; |
152 | 5.45k | return; |
153 | 5.45k | } |
154 | 761 | memcpy(ctx->buf + ptr, dbuf, clen); |
155 | 761 | dbuf += clen; |
156 | 761 | len -= clen; |
157 | 761 | (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, |
158 | 761 | ctx->buf, sizeof ctx->buf); |
159 | 761 | } |
160 | | |
161 | | /* |
162 | | * Process complete blocks. |
163 | | */ |
164 | 4.10k | ptr = len & 15; |
165 | 4.10k | len -= ptr; |
166 | 4.10k | (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, dbuf, len); |
167 | 4.10k | dbuf += len; |
168 | | |
169 | | /* |
170 | | * Copy last partial block in the context buffer. |
171 | | */ |
172 | 4.10k | memcpy(ctx->buf, dbuf, ptr); |
173 | 4.10k | ctx->ptr = ptr; |
174 | 4.10k | } |
175 | | |
176 | | /* see bearssl_block.h */ |
177 | | void |
178 | | br_ccm_flip(br_ccm_context *ctx) |
179 | 3.61k | { |
180 | 3.61k | size_t ptr; |
181 | | |
182 | | /* |
183 | | * Complete AAD partial block with zeros, if necessary. |
184 | | */ |
185 | 3.61k | ptr = ctx->ptr; |
186 | 3.61k | if (ptr != 0) { |
187 | 718 | memset(ctx->buf + ptr, 0, (sizeof ctx->buf) - ptr); |
188 | 718 | (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, |
189 | 718 | ctx->buf, sizeof ctx->buf); |
190 | 718 | ctx->ptr = 0; |
191 | 718 | } |
192 | | |
193 | | /* |
194 | | * Counter was already set by br_ccm_reset(). |
195 | | */ |
196 | 3.61k | } |
197 | | |
198 | | /* see bearssl_block.h */ |
199 | | void |
200 | | br_ccm_run(br_ccm_context *ctx, int encrypt, void *data, size_t len) |
201 | 31.8k | { |
202 | 31.8k | unsigned char *dbuf; |
203 | 31.8k | size_t ptr; |
204 | | |
205 | 31.8k | dbuf = data; |
206 | | |
207 | | /* |
208 | | * Complete a partial block, if any: ctx->buf[] contains |
209 | | * ctx->ptr plaintext bytes (already reported), and the other |
210 | | * bytes are CTR stream output. |
211 | | */ |
212 | 31.8k | ptr = ctx->ptr; |
213 | 31.8k | if (ptr != 0) { |
214 | 23.4k | size_t clen; |
215 | 23.4k | size_t u; |
216 | | |
217 | 23.4k | clen = (sizeof ctx->buf) - ptr; |
218 | 23.4k | if (clen > len) { |
219 | 20.0k | clen = len; |
220 | 20.0k | } |
221 | 23.4k | if (encrypt) { |
222 | 29.2k | for (u = 0; u < clen; u ++) { |
223 | 11.8k | unsigned w, x; |
224 | | |
225 | 11.8k | w = ctx->buf[ptr + u]; |
226 | 11.8k | x = dbuf[u]; |
227 | 11.8k | ctx->buf[ptr + u] = x; |
228 | 11.8k | dbuf[u] = w ^ x; |
229 | 11.8k | } |
230 | 17.3k | } else { |
231 | 24.3k | for (u = 0; u < clen; u ++) { |
232 | 18.2k | unsigned w; |
233 | | |
234 | 18.2k | w = ctx->buf[ptr + u] ^ dbuf[u]; |
235 | 18.2k | dbuf[u] = w; |
236 | 18.2k | ctx->buf[ptr + u] = w; |
237 | 18.2k | } |
238 | 6.09k | } |
239 | 23.4k | dbuf += clen; |
240 | 23.4k | len -= clen; |
241 | 23.4k | ptr += clen; |
242 | 23.4k | if (ptr < sizeof ctx->buf) { |
243 | 20.0k | ctx->ptr = ptr; |
244 | 20.0k | return; |
245 | 20.0k | } |
246 | 3.38k | (*ctx->bctx)->mac(ctx->bctx, |
247 | 3.38k | ctx->cbcmac, ctx->buf, sizeof ctx->buf); |
248 | 3.38k | } |
249 | | |
250 | | /* |
251 | | * Process all complete blocks. Note that the ctrcbc API is for |
252 | | * encrypt-then-MAC (CBC-MAC is computed over the encrypted |
253 | | * blocks) while CCM uses MAC-and-encrypt (CBC-MAC is computed |
254 | | * over the plaintext blocks). Therefore, we need to use the |
255 | | * _decryption_ function for encryption, and the encryption |
256 | | * function for decryption (this works because CTR encryption |
257 | | * and decryption are identical, so the choice really is about |
258 | | * computing the CBC-MAC before or after XORing with the CTR |
259 | | * stream). |
260 | | */ |
261 | 11.8k | ptr = len & 15; |
262 | 11.8k | len -= ptr; |
263 | 11.8k | if (encrypt) { |
264 | 6.26k | (*ctx->bctx)->decrypt(ctx->bctx, ctx->ctr, ctx->cbcmac, |
265 | 6.26k | dbuf, len); |
266 | 6.26k | } else { |
267 | 5.55k | (*ctx->bctx)->encrypt(ctx->bctx, ctx->ctr, ctx->cbcmac, |
268 | 5.55k | dbuf, len); |
269 | 5.55k | } |
270 | 11.8k | dbuf += len; |
271 | | |
272 | | /* |
273 | | * If there is some remaining data, then we need to compute an |
274 | | * extra block of CTR stream. |
275 | | */ |
276 | 11.8k | if (ptr != 0) { |
277 | 5.90k | size_t u; |
278 | | |
279 | 5.90k | memset(ctx->buf, 0, sizeof ctx->buf); |
280 | 5.90k | (*ctx->bctx)->ctr(ctx->bctx, ctx->ctr, |
281 | 5.90k | ctx->buf, sizeof ctx->buf); |
282 | 5.90k | if (encrypt) { |
283 | 17.5k | for (u = 0; u < ptr; u ++) { |
284 | 15.4k | unsigned w, x; |
285 | | |
286 | 15.4k | w = ctx->buf[u]; |
287 | 15.4k | x = dbuf[u]; |
288 | 15.4k | ctx->buf[u] = x; |
289 | 15.4k | dbuf[u] = w ^ x; |
290 | 15.4k | } |
291 | 3.72k | } else { |
292 | 31.4k | for (u = 0; u < ptr; u ++) { |
293 | 27.7k | unsigned w; |
294 | | |
295 | 27.7k | w = ctx->buf[u] ^ dbuf[u]; |
296 | 27.7k | dbuf[u] = w; |
297 | 27.7k | ctx->buf[u] = w; |
298 | 27.7k | } |
299 | 3.72k | } |
300 | 5.90k | } |
301 | 11.8k | ctx->ptr = ptr; |
302 | 11.8k | } |
303 | | |
304 | | /* see bearssl_block.h */ |
305 | | size_t |
306 | | br_ccm_get_tag(br_ccm_context *ctx, void *tag) |
307 | 3.61k | { |
308 | 3.61k | size_t ptr; |
309 | 3.61k | size_t u; |
310 | | |
311 | | /* |
312 | | * If there is some buffered data, then we need to pad it with |
313 | | * zeros and finish up CBC-MAC. |
314 | | */ |
315 | 3.61k | ptr = ctx->ptr; |
316 | 3.61k | if (ptr != 0) { |
317 | 2.51k | memset(ctx->buf + ptr, 0, (sizeof ctx->buf) - ptr); |
318 | 2.51k | (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, |
319 | 2.51k | ctx->buf, sizeof ctx->buf); |
320 | 2.51k | } |
321 | | |
322 | | /* |
323 | | * XOR the tag mask into the CBC-MAC output. |
324 | | */ |
325 | 37.9k | for (u = 0; u < ctx->tag_len; u ++) { |
326 | 34.3k | ctx->cbcmac[u] ^= ctx->tagmask[u]; |
327 | 34.3k | } |
328 | 3.61k | memcpy(tag, ctx->cbcmac, ctx->tag_len); |
329 | 3.61k | return ctx->tag_len; |
330 | 3.61k | } |
331 | | |
332 | | /* see bearssl_block.h */ |
333 | | uint32_t |
334 | | br_ccm_check_tag(br_ccm_context *ctx, const void *tag) |
335 | 2.39k | { |
336 | 2.39k | unsigned char tmp[16]; |
337 | 2.39k | size_t u, tag_len; |
338 | 2.39k | uint32_t z; |
339 | | |
340 | 2.39k | tag_len = br_ccm_get_tag(ctx, tmp); |
341 | 2.39k | z = 0; |
342 | 24.0k | for (u = 0; u < tag_len; u ++) { |
343 | 21.6k | z |= tmp[u] ^ ((const unsigned char *)tag)[u]; |
344 | 21.6k | } |
345 | 2.39k | return EQ0(z); |
346 | 2.39k | } |