1/*
2 * Copyright 2008-2016 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#include <string.h>
11#include <openssl/crypto.h>
12#include "crypto/modes.h"
13
14/*
15 * Trouble with Ciphertext Stealing, CTS, mode is that there is no
16 * common official specification, but couple of cipher/application
17 * specific ones: RFC2040 and RFC3962. Then there is 'Proposal to
18 * Extend CBC Mode By "Ciphertext Stealing"' at NIST site, which
19 * deviates from mentioned RFCs. Most notably it allows input to be
20 * of block length and it doesn't flip the order of the last two
21 * blocks. CTS is being discussed even in ECB context, but it's not
22 * adopted for any known application. This implementation provides
23 * two interfaces: one compliant with above mentioned RFCs and one
24 * compliant with the NIST proposal, both extending CBC mode.
25 */
26
27size_t CRYPTO_cts128_encrypt_block(const unsigned char *in,
28 unsigned char *out, size_t len,
29 const void *key, unsigned char ivec[16],
30 block128_f block)
31{
32 size_t residue, n;
33
34 if (len <= 16)
35 return 0;
36
37 if ((residue = len % 16) == 0)
38 residue = 16;
39
40 len -= residue;
41
42 CRYPTO_cbc128_encrypt(in, out, len, key, ivec, block);
43
44 in += len;
45 out += len;
46
47 for (n = 0; n < residue; ++n)
48 ivec[n] ^= in[n];
49 (*block) (ivec, ivec, key);
50 memcpy(out, out - 16, residue);
51 memcpy(out - 16, ivec, 16);
52
53 return len + residue;
54}
55
56size_t CRYPTO_nistcts128_encrypt_block(const unsigned char *in,
57 unsigned char *out, size_t len,
58 const void *key,
59 unsigned char ivec[16],
60 block128_f block)
61{
62 size_t residue, n;
63
64 if (len < 16)
65 return 0;
66
67 residue = len % 16;
68
69 len -= residue;
70
71 CRYPTO_cbc128_encrypt(in, out, len, key, ivec, block);
72
73 if (residue == 0)
74 return len;
75
76 in += len;
77 out += len;
78
79 for (n = 0; n < residue; ++n)
80 ivec[n] ^= in[n];
81 (*block) (ivec, ivec, key);
82 memcpy(out - 16 + residue, ivec, 16);
83
84 return len + residue;
85}
86
87size_t CRYPTO_cts128_encrypt(const unsigned char *in, unsigned char *out,
88 size_t len, const void *key,
89 unsigned char ivec[16], cbc128_f cbc)
90{
91 size_t residue;
92 union {
93 size_t align;
94 unsigned char c[16];
95 } tmp;
96
97 if (len <= 16)
98 return 0;
99
100 if ((residue = len % 16) == 0)
101 residue = 16;
102
103 len -= residue;
104
105 (*cbc) (in, out, len, key, ivec, 1);
106
107 in += len;
108 out += len;
109
110#if defined(CBC_HANDLES_TRUNCATED_IO)
111 memcpy(tmp.c, out - 16, 16);
112 (*cbc) (in, out - 16, residue, key, ivec, 1);
113 memcpy(out, tmp.c, residue);
114#else
115 memset(tmp.c, 0, sizeof(tmp));
116 memcpy(tmp.c, in, residue);
117 memcpy(out, out - 16, residue);
118 (*cbc) (tmp.c, out - 16, 16, key, ivec, 1);
119#endif
120 return len + residue;
121}
122
123size_t CRYPTO_nistcts128_encrypt(const unsigned char *in, unsigned char *out,
124 size_t len, const void *key,
125 unsigned char ivec[16], cbc128_f cbc)
126{
127 size_t residue;
128 union {
129 size_t align;
130 unsigned char c[16];
131 } tmp;
132
133 if (len < 16)
134 return 0;
135
136 residue = len % 16;
137
138 len -= residue;
139
140 (*cbc) (in, out, len, key, ivec, 1);
141
142 if (residue == 0)
143 return len;
144
145 in += len;
146 out += len;
147
148#if defined(CBC_HANDLES_TRUNCATED_IO)
149 (*cbc) (in, out - 16 + residue, residue, key, ivec, 1);
150#else
151 memset(tmp.c, 0, sizeof(tmp));
152 memcpy(tmp.c, in, residue);
153 (*cbc) (tmp.c, out - 16 + residue, 16, key, ivec, 1);
154#endif
155 return len + residue;
156}
157
158size_t CRYPTO_cts128_decrypt_block(const unsigned char *in,
159 unsigned char *out, size_t len,
160 const void *key, unsigned char ivec[16],
161 block128_f block)
162{
163 size_t residue, n;
164 union {
165 size_t align;
166 unsigned char c[32];
167 } tmp;
168
169 if (len <= 16)
170 return 0;
171
172 if ((residue = len % 16) == 0)
173 residue = 16;
174
175 len -= 16 + residue;
176
177 if (len) {
178 CRYPTO_cbc128_decrypt(in, out, len, key, ivec, block);
179 in += len;
180 out += len;
181 }
182
183 (*block) (in, tmp.c + 16, key);
184
185 memcpy(tmp.c, tmp.c + 16, 16);
186 memcpy(tmp.c, in + 16, residue);
187 (*block) (tmp.c, tmp.c, key);
188
189 for (n = 0; n < 16; ++n) {
190 unsigned char c = in[n];
191 out[n] = tmp.c[n] ^ ivec[n];
192 ivec[n] = c;
193 }
194 for (residue += 16; n < residue; ++n)
195 out[n] = tmp.c[n] ^ in[n];
196
197 return 16 + len + residue;
198}
199
200size_t CRYPTO_nistcts128_decrypt_block(const unsigned char *in,
201 unsigned char *out, size_t len,
202 const void *key,
203 unsigned char ivec[16],
204 block128_f block)
205{
206 size_t residue, n;
207 union {
208 size_t align;
209 unsigned char c[32];
210 } tmp;
211
212 if (len < 16)
213 return 0;
214
215 residue = len % 16;
216
217 if (residue == 0) {
218 CRYPTO_cbc128_decrypt(in, out, len, key, ivec, block);
219 return len;
220 }
221
222 len -= 16 + residue;
223
224 if (len) {
225 CRYPTO_cbc128_decrypt(in, out, len, key, ivec, block);
226 in += len;
227 out += len;
228 }
229
230 (*block) (in + residue, tmp.c + 16, key);
231
232 memcpy(tmp.c, tmp.c + 16, 16);
233 memcpy(tmp.c, in, residue);
234 (*block) (tmp.c, tmp.c, key);
235
236 for (n = 0; n < 16; ++n) {
237 unsigned char c = in[n];
238 out[n] = tmp.c[n] ^ ivec[n];
239 ivec[n] = in[n + residue];
240 tmp.c[n] = c;
241 }
242 for (residue += 16; n < residue; ++n)
243 out[n] = tmp.c[n] ^ tmp.c[n - 16];
244
245 return 16 + len + residue;
246}
247
248size_t CRYPTO_cts128_decrypt(const unsigned char *in, unsigned char *out,
249 size_t len, const void *key,
250 unsigned char ivec[16], cbc128_f cbc)
251{
252 size_t residue;
253 union {
254 size_t align;
255 unsigned char c[32];
256 } tmp;
257
258 if (len <= 16)
259 return 0;
260
261 if ((residue = len % 16) == 0)
262 residue = 16;
263
264 len -= 16 + residue;
265
266 if (len) {
267 (*cbc) (in, out, len, key, ivec, 0);
268 in += len;
269 out += len;
270 }
271
272 memset(tmp.c, 0, sizeof(tmp));
273 /*
274 * this places in[16] at &tmp.c[16] and decrypted block at &tmp.c[0]
275 */
276 (*cbc) (in, tmp.c, 16, key, tmp.c + 16, 0);
277
278 memcpy(tmp.c, in + 16, residue);
279#if defined(CBC_HANDLES_TRUNCATED_IO)
280 (*cbc) (tmp.c, out, 16 + residue, key, ivec, 0);
281#else
282 (*cbc) (tmp.c, tmp.c, 32, key, ivec, 0);
283 memcpy(out, tmp.c, 16 + residue);
284#endif
285 return 16 + len + residue;
286}
287
288size_t CRYPTO_nistcts128_decrypt(const unsigned char *in, unsigned char *out,
289 size_t len, const void *key,
290 unsigned char ivec[16], cbc128_f cbc)
291{
292 size_t residue;
293 union {
294 size_t align;
295 unsigned char c[32];
296 } tmp;
297
298 if (len < 16)
299 return 0;
300
301 residue = len % 16;
302
303 if (residue == 0) {
304 (*cbc) (in, out, len, key, ivec, 0);
305 return len;
306 }
307
308 len -= 16 + residue;
309
310 if (len) {
311 (*cbc) (in, out, len, key, ivec, 0);
312 in += len;
313 out += len;
314 }
315
316 memset(tmp.c, 0, sizeof(tmp));
317 /*
318 * this places in[16] at &tmp.c[16] and decrypted block at &tmp.c[0]
319 */
320 (*cbc) (in + residue, tmp.c, 16, key, tmp.c + 16, 0);
321
322 memcpy(tmp.c, in, residue);
323#if defined(CBC_HANDLES_TRUNCATED_IO)
324 (*cbc) (tmp.c, out, 16 + residue, key, ivec, 0);
325#else
326 (*cbc) (tmp.c, tmp.c, 32, key, ivec, 0);
327 memcpy(out, tmp.c, 16 + residue);
328#endif
329 return 16 + len + residue;
330}
331