1/*
2 * Copyright 2011-2020 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the OpenSSL license (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 <openssl/crypto.h>
11#include "modes_local.h"
12#include <string.h>
13
14#ifndef STRICT_ALIGNMENT
15# ifdef __GNUC__
16typedef u64 u64_a1 __attribute((__aligned__(1)));
17# else
18typedef u64 u64_a1;
19# endif
20#endif
21
22int CRYPTO_xts128_encrypt(const XTS128_CONTEXT *ctx,
23                          const unsigned char iv[16],
24                          const unsigned char *inp, unsigned char *out,
25                          size_t len, int enc)
26{
27    const union {
28        long one;
29        char little;
30    } is_endian = {
31        1
32    };
33    union {
34        u64 u[2];
35        u32 d[4];
36        u8 c[16];
37    } tweak, scratch;
38    unsigned int i;
39
40    if (len < 16)
41        return -1;
42
43    memcpy(tweak.c, iv, 16);
44
45    (*ctx->block2) (tweak.c, tweak.c, ctx->key2);
46
47    if (!enc && (len % 16))
48        len -= 16;
49
50    while (len >= 16) {
51#if defined(STRICT_ALIGNMENT)
52        memcpy(scratch.c, inp, 16);
53        scratch.u[0] ^= tweak.u[0];
54        scratch.u[1] ^= tweak.u[1];
55#else
56        scratch.u[0] = ((u64_a1 *)inp)[0] ^ tweak.u[0];
57        scratch.u[1] = ((u64_a1 *)inp)[1] ^ tweak.u[1];
58#endif
59        (*ctx->block1) (scratch.c, scratch.c, ctx->key1);
60#if defined(STRICT_ALIGNMENT)
61        scratch.u[0] ^= tweak.u[0];
62        scratch.u[1] ^= tweak.u[1];
63        memcpy(out, scratch.c, 16);
64#else
65        ((u64_a1 *)out)[0] = scratch.u[0] ^= tweak.u[0];
66        ((u64_a1 *)out)[1] = scratch.u[1] ^= tweak.u[1];
67#endif
68        inp += 16;
69        out += 16;
70        len -= 16;
71
72        if (len == 0)
73            return 0;
74
75        if (is_endian.little) {
76            unsigned int carry, res;
77
78            res = 0x87 & (((int)tweak.d[3]) >> 31);
79            carry = (unsigned int)(tweak.u[0] >> 63);
80            tweak.u[0] = (tweak.u[0] << 1) ^ res;
81            tweak.u[1] = (tweak.u[1] << 1) | carry;
82        } else {
83            size_t c;
84
85            for (c = 0, i = 0; i < 16; ++i) {
86                /*
87                 * + substitutes for |, because c is 1 bit
88                 */
89                c += ((size_t)tweak.c[i]) << 1;
90                tweak.c[i] = (u8)c;
91                c = c >> 8;
92            }
93            tweak.c[0] ^= (u8)(0x87 & (0 - c));
94        }
95    }
96    if (enc) {
97        for (i = 0; i < len; ++i) {
98            u8 c = inp[i];
99            out[i] = scratch.c[i];
100            scratch.c[i] = c;
101        }
102        scratch.u[0] ^= tweak.u[0];
103        scratch.u[1] ^= tweak.u[1];
104        (*ctx->block1) (scratch.c, scratch.c, ctx->key1);
105        scratch.u[0] ^= tweak.u[0];
106        scratch.u[1] ^= tweak.u[1];
107        memcpy(out - 16, scratch.c, 16);
108    } else {
109        union {
110            u64 u[2];
111            u8 c[16];
112        } tweak1;
113
114        if (is_endian.little) {
115            unsigned int carry, res;
116
117            res = 0x87 & (((int)tweak.d[3]) >> 31);
118            carry = (unsigned int)(tweak.u[0] >> 63);
119            tweak1.u[0] = (tweak.u[0] << 1) ^ res;
120            tweak1.u[1] = (tweak.u[1] << 1) | carry;
121        } else {
122            size_t c;
123
124            for (c = 0, i = 0; i < 16; ++i) {
125                /*
126                 * + substitutes for |, because c is 1 bit
127                 */
128                c += ((size_t)tweak.c[i]) << 1;
129                tweak1.c[i] = (u8)c;
130                c = c >> 8;
131            }
132            tweak1.c[0] ^= (u8)(0x87 & (0 - c));
133        }
134#if defined(STRICT_ALIGNMENT)
135        memcpy(scratch.c, inp, 16);
136        scratch.u[0] ^= tweak1.u[0];
137        scratch.u[1] ^= tweak1.u[1];
138#else
139        scratch.u[0] = ((u64_a1 *)inp)[0] ^ tweak1.u[0];
140        scratch.u[1] = ((u64_a1 *)inp)[1] ^ tweak1.u[1];
141#endif
142        (*ctx->block1) (scratch.c, scratch.c, ctx->key1);
143        scratch.u[0] ^= tweak1.u[0];
144        scratch.u[1] ^= tweak1.u[1];
145
146        for (i = 0; i < len; ++i) {
147            u8 c = inp[16 + i];
148            out[16 + i] = scratch.c[i];
149            scratch.c[i] = c;
150        }
151        scratch.u[0] ^= tweak.u[0];
152        scratch.u[1] ^= tweak.u[1];
153        (*ctx->block1) (scratch.c, scratch.c, ctx->key1);
154#if defined(STRICT_ALIGNMENT)
155        scratch.u[0] ^= tweak.u[0];
156        scratch.u[1] ^= tweak.u[1];
157        memcpy(out, scratch.c, 16);
158#else
159        ((u64_a1 *)out)[0] = scratch.u[0] ^ tweak.u[0];
160        ((u64_a1 *)out)[1] = scratch.u[1] ^ tweak.u[1];
161#endif
162    }
163
164    return 0;
165}
166