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