1/*
2 * Copyright 2008-2021 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#if !defined(STRICT_ALIGNMENT) && !defined(PEDANTIC)
15# define STRICT_ALIGNMENT 0
16#endif
17
18#if defined(__GNUC__) && !STRICT_ALIGNMENT
19typedef size_t size_t_aX __attribute((__aligned__(1)));
20#else
21typedef size_t size_t_aX;
22#endif
23
24void CRYPTO_cbc128_encrypt(const unsigned char *in, unsigned char *out,
25                           size_t len, const void *key,
26                           unsigned char ivec[16], block128_f block)
27{
28    size_t n;
29    const unsigned char *iv = ivec;
30
31    if (len == 0)
32        return;
33
34#if !defined(OPENSSL_SMALL_FOOTPRINT)
35    if (STRICT_ALIGNMENT &&
36        ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
37        while (len >= 16) {
38            for (n = 0; n < 16; ++n)
39                out[n] = in[n] ^ iv[n];
40            (*block) (out, out, key);
41            iv = out;
42            len -= 16;
43            in += 16;
44            out += 16;
45        }
46    } else {
47        while (len >= 16) {
48            for (n = 0; n < 16; n += sizeof(size_t))
49                *(size_t_aX *)(out + n) =
50                    *(size_t_aX *)(in + n) ^ *(size_t_aX *)(iv + n);
51            (*block) (out, out, key);
52            iv = out;
53            len -= 16;
54            in += 16;
55            out += 16;
56        }
57    }
58#endif
59    while (len) {
60        for (n = 0; n < 16 && n < len; ++n)
61            out[n] = in[n] ^ iv[n];
62        for (; n < 16; ++n)
63            out[n] = iv[n];
64        (*block) (out, out, key);
65        iv = out;
66        if (len <= 16)
67            break;
68        len -= 16;
69        in += 16;
70        out += 16;
71    }
72    if (ivec != iv)
73        memcpy(ivec, iv, 16);
74}
75
76void CRYPTO_cbc128_decrypt(const unsigned char *in, unsigned char *out,
77                           size_t len, const void *key,
78                           unsigned char ivec[16], block128_f block)
79{
80    size_t n;
81    union {
82        size_t t[16 / sizeof(size_t)];
83        unsigned char c[16];
84    } tmp;
85
86    if (len == 0)
87        return;
88
89#if !defined(OPENSSL_SMALL_FOOTPRINT)
90    if (in != out) {
91        const unsigned char *iv = ivec;
92
93        if (STRICT_ALIGNMENT &&
94            ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
95            while (len >= 16) {
96                (*block) (in, out, key);
97                for (n = 0; n < 16; ++n)
98                    out[n] ^= iv[n];
99                iv = in;
100                len -= 16;
101                in += 16;
102                out += 16;
103            }
104        } else if (16 % sizeof(size_t) == 0) { /* always true */
105            while (len >= 16) {
106                size_t_aX *out_t = (size_t_aX *)out;
107                size_t_aX *iv_t = (size_t_aX *)iv;
108
109                (*block) (in, out, key);
110                for (n = 0; n < 16 / sizeof(size_t); n++)
111                    out_t[n] ^= iv_t[n];
112                iv = in;
113                len -= 16;
114                in += 16;
115                out += 16;
116            }
117        }
118        if (ivec != iv)
119            memcpy(ivec, iv, 16);
120    } else {
121        if (STRICT_ALIGNMENT &&
122            ((size_t)in | (size_t)out | (size_t)ivec) % sizeof(size_t) != 0) {
123            unsigned char c;
124            while (len >= 16) {
125                (*block) (in, tmp.c, key);
126                for (n = 0; n < 16; ++n) {
127                    c = in[n];
128                    out[n] = tmp.c[n] ^ ivec[n];
129                    ivec[n] = c;
130                }
131                len -= 16;
132                in += 16;
133                out += 16;
134            }
135        } else if (16 % sizeof(size_t) == 0) { /* always true */
136            while (len >= 16) {
137                size_t c;
138                size_t_aX *out_t = (size_t_aX *)out;
139                size_t_aX *ivec_t = (size_t_aX *)ivec;
140                const size_t_aX *in_t = (const size_t_aX *)in;
141
142                (*block) (in, tmp.c, key);
143                for (n = 0; n < 16 / sizeof(size_t); n++) {
144                    c = in_t[n];
145                    out_t[n] = tmp.t[n] ^ ivec_t[n];
146                    ivec_t[n] = c;
147                }
148                len -= 16;
149                in += 16;
150                out += 16;
151            }
152        }
153    }
154#endif
155    while (len) {
156        unsigned char c;
157        (*block) (in, tmp.c, key);
158        for (n = 0; n < 16 && n < len; ++n) {
159            c = in[n];
160            out[n] = tmp.c[n] ^ ivec[n];
161            ivec[n] = c;
162        }
163        if (len <= 16) {
164            for (; n < 16; ++n)
165                ivec[n] = in[n];
166            break;
167        }
168        len -= 16;
169        in += 16;
170        out += 16;
171    }
172}
173