/src/openssl41/providers/implementations/ciphers/cipher_cts.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2020-2026 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | | * this file except in compliance with the License. You can obtain a copy |
6 | | * in the file LICENSE in the source distribution or at |
7 | | * https://www.openssl.org/source/license.html |
8 | | */ |
9 | | |
10 | | /* |
11 | | * Helper functions for 128 bit CBC CTS ciphers (Currently AES and Camellia). |
12 | | * |
13 | | * The function dispatch tables are embedded into cipher_aes.c |
14 | | * and cipher_camellia.c |
15 | | */ |
16 | | |
17 | | /* |
18 | | * Refer to SP800-38A-Addendum |
19 | | * |
20 | | * Ciphertext stealing encrypts plaintext using a block cipher, without padding |
21 | | * the message to a multiple of the block size, so the ciphertext is the same |
22 | | * size as the plaintext. |
23 | | * It does this by altering processing of the last two blocks of the message. |
24 | | * The processing of all but the last two blocks is unchanged, but a portion of |
25 | | * the second-last block's ciphertext is "stolen" to pad the last plaintext |
26 | | * block. The padded final block is then encrypted as usual. |
27 | | * The final ciphertext for the last two blocks, consists of the partial block |
28 | | * (with the "stolen" portion omitted) plus the full final block, |
29 | | * which are the same size as the original plaintext. |
30 | | * Decryption requires decrypting the final block first, then restoring the |
31 | | * stolen ciphertext to the partial block, which can then be decrypted as usual. |
32 | | |
33 | | * AES_CBC_CTS has 3 variants: |
34 | | * (1) CS1 The NIST variant. |
35 | | * If the length is a multiple of the blocksize it is the same as CBC mode. |
36 | | * otherwise it produces C1||C2||(C(n-1))*||Cn. |
37 | | * Where C(n-1)* is a partial block. |
38 | | * (2) CS2 |
39 | | * If the length is a multiple of the blocksize it is the same as CBC mode. |
40 | | * otherwise it produces C1||C2||Cn||(C(n-1))*. |
41 | | * Where C(n-1)* is a partial block. |
42 | | * (3) CS3 The Kerberos5 variant. |
43 | | * Produces C1||C2||Cn||(C(n-1))* regardless of the length. |
44 | | * If the length is a multiple of the blocksize it looks similar to CBC mode |
45 | | * with the last 2 blocks swapped. |
46 | | * Otherwise it is the same as CS2. |
47 | | */ |
48 | | |
49 | | #include <openssl/core_names.h> |
50 | | #include <openssl/proverr.h> |
51 | | #include "prov/ciphercommon.h" |
52 | | #include "internal/nelem.h" |
53 | | #include "cipher_cts.h" |
54 | | |
55 | | struct cipher_cts_get_ctx_param_list_st { |
56 | | struct ossl_cipher_get_ctx_param_list_st common; |
57 | | OSSL_PARAM *mode; |
58 | | }; |
59 | | |
60 | | struct cipher_cts_set_ctx_param_list_st { |
61 | | OSSL_PARAM *mode; |
62 | | }; |
63 | | |
64 | | #define cipher_cts_get_ctx_params_st cipher_cts_get_ctx_param_list_st |
65 | | #define cipher_cts_set_ctx_params_st cipher_cts_set_ctx_param_list_st |
66 | | |
67 | | #include "providers/implementations/ciphers/cipher_cts.inc" |
68 | | |
69 | | /* The value assigned to 0 is the default */ |
70 | 0 | #define CTS_CS1 0 |
71 | 0 | #define CTS_CS2 1 |
72 | 0 | #define CTS_CS3 2 |
73 | | |
74 | 0 | #define CTS_BLOCK_SIZE 16 |
75 | | |
76 | | typedef union { |
77 | | size_t align; |
78 | | unsigned char c[CTS_BLOCK_SIZE]; |
79 | | } aligned_16bytes; |
80 | | |
81 | | typedef struct cts_mode_name2id_st { |
82 | | unsigned int id; |
83 | | const char *name; |
84 | | } CTS_MODE_NAME2ID; |
85 | | |
86 | | static CTS_MODE_NAME2ID cts_modes[] = { |
87 | | { CTS_CS1, OSSL_CIPHER_CTS_MODE_CS1 }, |
88 | | { CTS_CS2, OSSL_CIPHER_CTS_MODE_CS2 }, |
89 | | { CTS_CS3, OSSL_CIPHER_CTS_MODE_CS3 }, |
90 | | }; |
91 | | |
92 | | const char *ossl_cipher_cbc_cts_mode_id2name(unsigned int id) |
93 | 0 | { |
94 | 0 | size_t i; |
95 | |
|
96 | 0 | for (i = 0; i < OSSL_NELEM(cts_modes); ++i) { |
97 | 0 | if (cts_modes[i].id == id) |
98 | 0 | return cts_modes[i].name; |
99 | 0 | } |
100 | 0 | return NULL; |
101 | 0 | } |
102 | | |
103 | | int ossl_cipher_cbc_cts_mode_name2id(const char *name) |
104 | 9 | { |
105 | 9 | size_t i; |
106 | | |
107 | 36 | for (i = 0; i < OSSL_NELEM(cts_modes); ++i) { |
108 | 27 | if (OPENSSL_strcasecmp(name, cts_modes[i].name) == 0) |
109 | 0 | return (int)cts_modes[i].id; |
110 | 27 | } |
111 | 9 | return -1; |
112 | 9 | } |
113 | | |
114 | | int ossl_cipher_cbc_cts_get_ctx_params(void *vctx, OSSL_PARAM params[]) |
115 | 18 | { |
116 | 18 | PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; |
117 | 18 | struct cipher_cts_get_ctx_param_list_st p; |
118 | | |
119 | 18 | if (ctx == NULL || !cipher_cts_get_ctx_params_decoder(params, &p)) |
120 | 0 | return 0; |
121 | | |
122 | 18 | if (!ossl_cipher_common_get_ctx_params(ctx, &p.common)) |
123 | 0 | return 0; |
124 | | |
125 | 18 | if (p.mode != NULL) { |
126 | 0 | const char *name = ossl_cipher_cbc_cts_mode_id2name(ctx->cts_mode); |
127 | |
|
128 | 0 | if (name == NULL || !OSSL_PARAM_set_utf8_string(p.mode, name)) { |
129 | 0 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); |
130 | 0 | return 0; |
131 | 0 | } |
132 | 0 | } |
133 | 18 | return 1; |
134 | 18 | } |
135 | | |
136 | | const OSSL_PARAM *ossl_cipher_cbc_cts_gettable_ctx_params( |
137 | | ossl_unused void *cctx, ossl_unused void *provctx) |
138 | 102 | { |
139 | 102 | return cipher_cts_get_ctx_params_list; |
140 | 102 | } |
141 | | |
142 | | int ossl_cipher_cbc_cts_set_ctx_params(void *vctx, const OSSL_PARAM params[]) |
143 | 18 | { |
144 | 18 | PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; |
145 | 18 | struct cipher_cts_set_ctx_param_list_st p; |
146 | | |
147 | 18 | if (ctx == NULL || !cipher_cts_set_ctx_params_decoder(params, &p)) |
148 | 0 | return 0; |
149 | | |
150 | 18 | if (p.mode != NULL) { |
151 | 9 | int id; |
152 | | |
153 | 9 | if (p.mode->data_type != OSSL_PARAM_UTF8_STRING |
154 | 9 | || p.mode->data == NULL |
155 | 9 | || (id = ossl_cipher_cbc_cts_mode_name2id(p.mode->data)) < 0) { |
156 | 9 | ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); |
157 | 9 | return 0; |
158 | 9 | } |
159 | 0 | ctx->cts_mode = (unsigned int)id; |
160 | 0 | } |
161 | | |
162 | 9 | return 1; |
163 | 18 | } |
164 | | |
165 | | const OSSL_PARAM *ossl_cipher_cbc_cts_settable_ctx_params( |
166 | | ossl_unused void *cctx, ossl_unused void *provctx) |
167 | 9 | { |
168 | 9 | return cipher_cts_set_ctx_params_list; |
169 | 9 | } |
170 | | |
171 | | static size_t cts128_cs1_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, |
172 | | unsigned char *out, size_t len) |
173 | 0 | { |
174 | 0 | aligned_16bytes tmp_in; |
175 | 0 | size_t residue; |
176 | |
|
177 | 0 | residue = len % CTS_BLOCK_SIZE; |
178 | 0 | len -= residue; |
179 | 0 | if (!ctx->hw->cipher(ctx, out, in, len)) |
180 | 0 | return 0; |
181 | | |
182 | 0 | if (residue == 0) |
183 | 0 | return len; |
184 | | |
185 | 0 | in += len; |
186 | 0 | out += len; |
187 | |
|
188 | 0 | memset(tmp_in.c, 0, sizeof(tmp_in)); |
189 | 0 | memcpy(tmp_in.c, in, residue); |
190 | 0 | if (!ctx->hw->cipher(ctx, out - CTS_BLOCK_SIZE + residue, tmp_in.c, |
191 | 0 | CTS_BLOCK_SIZE)) |
192 | 0 | return 0; |
193 | 0 | return len + residue; |
194 | 0 | } |
195 | | |
196 | | static void do_xor(const unsigned char *in1, const unsigned char *in2, |
197 | | size_t len, unsigned char *out) |
198 | 0 | { |
199 | 0 | size_t i; |
200 | |
|
201 | 0 | for (i = 0; i < len; ++i) |
202 | 0 | out[i] = in1[i] ^ in2[i]; |
203 | 0 | } |
204 | | |
205 | | static size_t cts128_cs1_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, |
206 | | unsigned char *out, size_t len) |
207 | 0 | { |
208 | 0 | aligned_16bytes mid_iv, ct_mid, cn, pt_last; |
209 | 0 | size_t residue; |
210 | |
|
211 | 0 | residue = len % CTS_BLOCK_SIZE; |
212 | 0 | if (residue == 0) { |
213 | | /* If there are no partial blocks then it is the same as CBC mode */ |
214 | 0 | if (!ctx->hw->cipher(ctx, out, in, len)) |
215 | 0 | return 0; |
216 | 0 | return len; |
217 | 0 | } |
218 | | /* Process blocks at the start - but leave the last 2 blocks */ |
219 | 0 | len -= CTS_BLOCK_SIZE + residue; |
220 | 0 | if (len > 0) { |
221 | 0 | if (!ctx->hw->cipher(ctx, out, in, len)) |
222 | 0 | return 0; |
223 | 0 | in += len; |
224 | 0 | out += len; |
225 | 0 | } |
226 | | /* Save the iv that will be used by the second last block */ |
227 | 0 | memcpy(mid_iv.c, ctx->iv, CTS_BLOCK_SIZE); |
228 | | /* Save the C(n) block */ |
229 | 0 | memcpy(cn.c, in + residue, CTS_BLOCK_SIZE); |
230 | | |
231 | | /* Decrypt the last block first using an iv of zero */ |
232 | 0 | memset(ctx->iv, 0, CTS_BLOCK_SIZE); |
233 | 0 | if (!ctx->hw->cipher(ctx, pt_last.c, in + residue, CTS_BLOCK_SIZE)) |
234 | 0 | return 0; |
235 | | |
236 | | /* |
237 | | * Rebuild the ciphertext of the second last block as a combination of |
238 | | * the decrypted last block + replace the start with the ciphertext bytes |
239 | | * of the partial second last block. |
240 | | */ |
241 | 0 | memcpy(ct_mid.c, in, residue); |
242 | 0 | memcpy(ct_mid.c + residue, pt_last.c + residue, CTS_BLOCK_SIZE - residue); |
243 | | /* |
244 | | * Restore the last partial ciphertext block. |
245 | | * Now that we have the cipher text of the second last block, apply |
246 | | * that to the partial plaintext end block. We have already decrypted the |
247 | | * block using an IV of zero. For decryption the IV is just XORed after |
248 | | * doing an Cipher CBC block - so just XOR in the cipher text. |
249 | | */ |
250 | 0 | do_xor(ct_mid.c, pt_last.c, residue, out + CTS_BLOCK_SIZE); |
251 | | |
252 | | /* Restore the iv needed by the second last block */ |
253 | 0 | memcpy(ctx->iv, mid_iv.c, CTS_BLOCK_SIZE); |
254 | | |
255 | | /* |
256 | | * Decrypt the second last plaintext block now that we have rebuilt the |
257 | | * ciphertext. |
258 | | */ |
259 | 0 | if (!ctx->hw->cipher(ctx, out, ct_mid.c, CTS_BLOCK_SIZE)) |
260 | 0 | return 0; |
261 | | |
262 | | /* The returned iv is the C(n) block */ |
263 | 0 | memcpy(ctx->iv, cn.c, CTS_BLOCK_SIZE); |
264 | 0 | return len + CTS_BLOCK_SIZE + residue; |
265 | 0 | } |
266 | | |
267 | | static size_t cts128_cs3_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, |
268 | | unsigned char *out, size_t len) |
269 | 0 | { |
270 | 0 | aligned_16bytes tmp_in; |
271 | 0 | size_t residue; |
272 | |
|
273 | 0 | if (len < CTS_BLOCK_SIZE) /* CS3 requires at least one block */ |
274 | 0 | return 0; |
275 | | |
276 | | /* If we only have one block then just process the aligned block */ |
277 | 0 | if (len == CTS_BLOCK_SIZE) |
278 | 0 | return ctx->hw->cipher(ctx, out, in, len) ? len : 0; |
279 | | |
280 | 0 | residue = len % CTS_BLOCK_SIZE; |
281 | 0 | if (residue == 0) |
282 | 0 | residue = CTS_BLOCK_SIZE; |
283 | 0 | len -= residue; |
284 | |
|
285 | 0 | if (!ctx->hw->cipher(ctx, out, in, len)) |
286 | 0 | return 0; |
287 | | |
288 | 0 | in += len; |
289 | 0 | out += len; |
290 | |
|
291 | 0 | memset(tmp_in.c, 0, sizeof(tmp_in)); |
292 | 0 | memcpy(tmp_in.c, in, residue); |
293 | 0 | memcpy(out, out - CTS_BLOCK_SIZE, residue); |
294 | 0 | if (!ctx->hw->cipher(ctx, out - CTS_BLOCK_SIZE, tmp_in.c, CTS_BLOCK_SIZE)) |
295 | 0 | return 0; |
296 | 0 | return len + residue; |
297 | 0 | } |
298 | | |
299 | | /* |
300 | | * Note: |
301 | | * The cipher text (in) is of the form C(0), C(1), ., C(n), C(n-1)* where |
302 | | * C(n) is a full block and C(n-1)* can be a partial block |
303 | | * (but could be a full block). |
304 | | * This means that the output plaintext (out) needs to swap the plaintext of |
305 | | * the last two decoded ciphertext blocks. |
306 | | */ |
307 | | static size_t cts128_cs3_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, |
308 | | unsigned char *out, size_t len) |
309 | 0 | { |
310 | 0 | aligned_16bytes mid_iv, ct_mid, cn, pt_last; |
311 | 0 | size_t residue; |
312 | |
|
313 | 0 | if (len < CTS_BLOCK_SIZE) /* CS3 requires at least one block */ |
314 | 0 | return 0; |
315 | | |
316 | | /* If we only have one block then just process the aligned block */ |
317 | 0 | if (len == CTS_BLOCK_SIZE) |
318 | 0 | return ctx->hw->cipher(ctx, out, in, len) ? len : 0; |
319 | | |
320 | | /* Process blocks at the start - but leave the last 2 blocks */ |
321 | 0 | residue = len % CTS_BLOCK_SIZE; |
322 | 0 | if (residue == 0) |
323 | 0 | residue = CTS_BLOCK_SIZE; |
324 | 0 | len -= CTS_BLOCK_SIZE + residue; |
325 | |
|
326 | 0 | if (len > 0) { |
327 | 0 | if (!ctx->hw->cipher(ctx, out, in, len)) |
328 | 0 | return 0; |
329 | 0 | in += len; |
330 | 0 | out += len; |
331 | 0 | } |
332 | | /* Save the iv that will be used by the second last block */ |
333 | 0 | memcpy(mid_iv.c, ctx->iv, CTS_BLOCK_SIZE); |
334 | | /* Save the C(n) block : For CS3 it is C(1)||...||C(n-2)||C(n)||C(n-1)* */ |
335 | 0 | memcpy(cn.c, in, CTS_BLOCK_SIZE); |
336 | | |
337 | | /* Decrypt the C(n) block first using an iv of zero */ |
338 | 0 | memset(ctx->iv, 0, CTS_BLOCK_SIZE); |
339 | 0 | if (!ctx->hw->cipher(ctx, pt_last.c, in, CTS_BLOCK_SIZE)) |
340 | 0 | return 0; |
341 | | |
342 | | /* |
343 | | * Rebuild the ciphertext of C(n-1) as a combination of |
344 | | * the decrypted C(n) block + replace the start with the ciphertext bytes |
345 | | * of the partial last block. |
346 | | */ |
347 | 0 | memcpy(ct_mid.c, in + CTS_BLOCK_SIZE, residue); |
348 | 0 | if (residue != CTS_BLOCK_SIZE) |
349 | 0 | memcpy(ct_mid.c + residue, pt_last.c + residue, CTS_BLOCK_SIZE - residue); |
350 | | /* |
351 | | * Restore the last partial ciphertext block. |
352 | | * Now that we have the cipher text of the second last block, apply |
353 | | * that to the partial plaintext end block. We have already decrypted the |
354 | | * block using an IV of zero. For decryption the IV is just XORed after |
355 | | * doing an AES block - so just XOR in the ciphertext. |
356 | | */ |
357 | 0 | do_xor(ct_mid.c, pt_last.c, residue, out + CTS_BLOCK_SIZE); |
358 | | |
359 | | /* Restore the iv needed by the second last block */ |
360 | 0 | memcpy(ctx->iv, mid_iv.c, CTS_BLOCK_SIZE); |
361 | | /* |
362 | | * Decrypt the second last plaintext block now that we have rebuilt the |
363 | | * ciphertext. |
364 | | */ |
365 | 0 | if (!ctx->hw->cipher(ctx, out, ct_mid.c, CTS_BLOCK_SIZE)) |
366 | 0 | return 0; |
367 | | |
368 | | /* The returned iv is the C(n) block */ |
369 | 0 | memcpy(ctx->iv, cn.c, CTS_BLOCK_SIZE); |
370 | 0 | return len + CTS_BLOCK_SIZE + residue; |
371 | 0 | } |
372 | | |
373 | | static size_t cts128_cs2_encrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, |
374 | | unsigned char *out, size_t len) |
375 | 0 | { |
376 | 0 | if (len % CTS_BLOCK_SIZE == 0) { |
377 | | /* If there are no partial blocks then it is the same as CBC mode */ |
378 | 0 | if (!ctx->hw->cipher(ctx, out, in, len)) |
379 | 0 | return 0; |
380 | 0 | return len; |
381 | 0 | } |
382 | | /* For partial blocks CS2 is equivalent to CS3 */ |
383 | 0 | return cts128_cs3_encrypt(ctx, in, out, len); |
384 | 0 | } |
385 | | |
386 | | static size_t cts128_cs2_decrypt(PROV_CIPHER_CTX *ctx, const unsigned char *in, |
387 | | unsigned char *out, size_t len) |
388 | 0 | { |
389 | 0 | if (len % CTS_BLOCK_SIZE == 0) { |
390 | | /* If there are no partial blocks then it is the same as CBC mode */ |
391 | 0 | if (!ctx->hw->cipher(ctx, out, in, len)) |
392 | 0 | return 0; |
393 | 0 | return len; |
394 | 0 | } |
395 | | /* For partial blocks CS2 is equivalent to CS3 */ |
396 | 0 | return cts128_cs3_decrypt(ctx, in, out, len); |
397 | 0 | } |
398 | | |
399 | | int ossl_cipher_cbc_cts_block_update(void *vctx, unsigned char *out, size_t *outl, |
400 | | size_t outsize, const unsigned char *in, |
401 | | size_t inl) |
402 | 0 | { |
403 | 0 | PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; |
404 | 0 | size_t sz = 0; |
405 | |
|
406 | 0 | if (inl < CTS_BLOCK_SIZE) /* There must be at least one block for CTS mode */ |
407 | 0 | return 0; |
408 | 0 | if (outsize < inl) |
409 | 0 | return 0; |
410 | 0 | if (out == NULL) { |
411 | 0 | *outl = inl; |
412 | 0 | return 1; |
413 | 0 | } |
414 | | |
415 | | /* |
416 | | * Return an error if the update is called multiple times, only one shot |
417 | | * is supported. |
418 | | */ |
419 | 0 | if (ctx->updated == 1) |
420 | 0 | return 0; |
421 | | |
422 | 0 | if (ctx->enc) { |
423 | 0 | if (ctx->cts_mode == CTS_CS1) |
424 | 0 | sz = cts128_cs1_encrypt(ctx, in, out, inl); |
425 | 0 | else if (ctx->cts_mode == CTS_CS2) |
426 | 0 | sz = cts128_cs2_encrypt(ctx, in, out, inl); |
427 | 0 | else if (ctx->cts_mode == CTS_CS3) |
428 | 0 | sz = cts128_cs3_encrypt(ctx, in, out, inl); |
429 | 0 | } else { |
430 | 0 | if (ctx->cts_mode == CTS_CS1) |
431 | 0 | sz = cts128_cs1_decrypt(ctx, in, out, inl); |
432 | 0 | else if (ctx->cts_mode == CTS_CS2) |
433 | 0 | sz = cts128_cs2_decrypt(ctx, in, out, inl); |
434 | 0 | else if (ctx->cts_mode == CTS_CS3) |
435 | 0 | sz = cts128_cs3_decrypt(ctx, in, out, inl); |
436 | 0 | } |
437 | 0 | if (sz == 0) |
438 | 0 | return 0; |
439 | 0 | ctx->updated = 1; /* Stop multiple updates being allowed */ |
440 | 0 | *outl = sz; |
441 | 0 | return 1; |
442 | 0 | } |
443 | | |
444 | | int ossl_cipher_cbc_cts_block_final(void *vctx, unsigned char *out, size_t *outl, |
445 | | size_t outsize) |
446 | 0 | { |
447 | 0 | *outl = 0; |
448 | 0 | return 1; |
449 | 0 | } |