Botan 3.13.0
Crypto and TLS for C&
xts.cpp
Go to the documentation of this file.
1/*
2* XTS Mode
3* (C) 2009,2013,2026 Jack Lloyd
4* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/internal/xts.h>
10
11#include <botan/exceptn.h>
12#include <botan/mem_ops.h>
13#include <botan/internal/fmt.h>
14#include <botan/internal/poly_dbl.h>
15
16#if defined(BOTAN_HAS_MODE_XTS_AVX512_CLMUL)
17 #include <botan/internal/cpuid.h>
18#endif
19
20namespace Botan {
21
22XTS_Mode::XTS_Mode(std::unique_ptr<BlockCipher> cipher) :
23 m_cipher(std::move(cipher)),
24 m_cipher_block_size(m_cipher->block_size()),
25 m_cipher_parallelism(m_cipher->parallel_bytes()),
26 m_tweak_blocks(m_cipher_parallelism / m_cipher_block_size) {
27 if(!poly_double_supported_size(m_cipher_block_size)) {
28 throw Invalid_Argument(fmt("Cannot use {} with XTS", m_cipher->name()));
29 }
30
31 m_tweak_cipher = m_cipher->new_object();
32}
33
35 m_cipher->clear();
36 m_tweak_cipher->clear();
37 reset();
38}
39
41 return m_cipher_block_size;
42}
43
45 return m_cipher_parallelism;
46}
47
49 m_tweak.clear();
50}
51
52std::string XTS_Mode::name() const {
53 return cipher().name() + "/XTS";
54}
55
57 return cipher_block_size();
58}
59
63
65 return cipher_block_size();
66}
67
68bool XTS_Mode::valid_nonce_length(size_t n) const {
69 return n <= cipher_block_size();
70}
71
73 return m_cipher->has_keying_material() && m_tweak_cipher->has_keying_material();
74}
75
76void XTS_Mode::key_schedule(std::span<const uint8_t> key) {
77 const size_t key_half = key.size() / 2;
78
79 if(key.size() % 2 == 1 || !m_cipher->valid_keylength(key_half)) {
80 throw Invalid_Key_Length(name(), key.size());
81 }
82
83 m_cipher->set_key(key.first(key_half));
84 m_tweak_cipher->set_key(key.last(key_half));
85
86 // Drop the tweak: it was computed under the previous tweak-cipher key
87 // and the previous nonce; the user must call start_msg again before
88 // any further processing.
89 reset();
90}
91
92void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
93 if(!valid_nonce_length(nonce_len)) {
94 throw Invalid_IV_Length(name(), nonce_len);
95 }
96
97 m_tweak.resize(m_cipher_parallelism);
98 clear_mem(m_tweak.data(), m_tweak.size());
99 copy_mem(m_tweak.data(), nonce, nonce_len);
100 m_tweak_cipher->encrypt(m_tweak.data());
101
102 // Just repeated doubling from first, remaining contents are junk...
103 xts_compute_tweak_block(m_tweak.data(), m_tweak_cipher->block_size(), tweak_blocks());
104}
105
106//static
107void XTS_Mode::update_tweak_block(uint8_t tweak[], size_t BS, size_t blocks_in_tweak) {
108#if defined(BOTAN_HAS_MODE_XTS_AVX512_CLMUL)
109 if(BS == 16 && blocks_in_tweak % 8 == 0 && CPUID::has(CPUID::Feature::AVX512_CLMUL)) {
110 return update_tweak_block_avx512_clmul(tweak, BS, blocks_in_tweak);
111 }
112#endif
113
114 /*
115 * If we don't have a fast method available, just set the first tweak block to
116 * the doubling of the last tweak block, and recompute all the rest via
117 * successive doublings.
118 */
119 poly_double_n_le(tweak, &tweak[(blocks_in_tweak - 1) * BS], BS);
120 xts_compute_tweak_block(tweak, BS, blocks_in_tweak);
121}
122
123void XTS_Mode::update_tweak(size_t consumed) {
124 const size_t BS = m_tweak_cipher->block_size();
125 const size_t blocks_in_tweak = tweak_blocks();
126
127 BOTAN_ASSERT_NOMSG(consumed > 0 && consumed <= blocks_in_tweak);
128
129 if(consumed == blocks_in_tweak) {
130 // Update all in parallel
131 update_tweak_block(m_tweak.data(), BS, blocks_in_tweak);
132 } else {
133 /*
134 The last remaining tweaks can just be shifted over
135
136 This could be a lot better though! We can copy all of the remaining tweaks
137 and just recompute the last few
138 */
139 copy_mem(m_tweak.data(), &m_tweak[(consumed * BS)], BS);
140 xts_compute_tweak_block(m_tweak.data(), BS, blocks_in_tweak);
141 }
142}
143
144size_t XTS_Encryption::output_length(size_t input_length) const {
145 return input_length;
146}
147
148size_t XTS_Encryption::process_msg(uint8_t buf[], size_t sz) {
150 const size_t BS = cipher_block_size();
151
152 BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
153 size_t blocks = sz / BS;
154
155 const size_t blocks_in_tweak = tweak_blocks();
156
157 while(blocks > 0) {
158 const size_t to_proc = std::min(blocks, blocks_in_tweak);
159 const size_t proc_bytes = to_proc * BS;
160
161 xor_buf(buf, tweak(), proc_bytes);
162 cipher().encrypt_n(buf, buf, to_proc);
163 xor_buf(buf, tweak(), proc_bytes);
164
165 buf += proc_bytes;
166 blocks -= to_proc;
167
168 update_tweak(to_proc);
169 }
170
171 return sz;
172}
173
174void XTS_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
176 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
177 const size_t sz = buffer.size() - offset;
178 uint8_t* buf = buffer.data() + offset;
179
180 BOTAN_ARG_CHECK(sz >= minimum_final_size(), "missing sufficient final input in XTS encrypt");
181
182 const size_t BS = cipher_block_size();
183
184 if(sz % BS == 0) {
185 update(buffer, offset);
186 } else {
187 // steal ciphertext
188 const size_t full_blocks = ((sz / BS) - 1) * BS;
189 const size_t final_bytes = sz - full_blocks;
190 BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
191
192 secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
193 buffer.resize(full_blocks + offset);
194 update(buffer, offset);
195
196 xor_buf(last, tweak(), BS);
197 cipher().encrypt(last);
198 xor_buf(last, tweak(), BS);
199
200 for(size_t i = 0; i != final_bytes - BS; ++i) {
201 last[i] ^= last[i + BS];
202 last[i + BS] ^= last[i];
203 last[i] ^= last[i + BS];
204 }
205
206 xor_buf(last, tweak() + BS, BS);
207 cipher().encrypt(last);
208 xor_buf(last, tweak() + BS, BS);
209
210 buffer += last;
211 }
212}
213
214size_t XTS_Decryption::output_length(size_t input_length) const {
215 return input_length;
216}
217
218size_t XTS_Decryption::process_msg(uint8_t buf[], size_t sz) {
220 const size_t BS = cipher_block_size();
221
222 BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
223 size_t blocks = sz / BS;
224
225 const size_t blocks_in_tweak = tweak_blocks();
226
227 while(blocks > 0) {
228 const size_t to_proc = std::min(blocks, blocks_in_tweak);
229 const size_t proc_bytes = to_proc * BS;
230
231 xor_buf(buf, tweak(), proc_bytes);
232 cipher().decrypt_n(buf, buf, to_proc);
233 xor_buf(buf, tweak(), proc_bytes);
234
235 buf += proc_bytes;
236 blocks -= to_proc;
237
238 update_tweak(to_proc);
239 }
240
241 return sz;
242}
243
244void XTS_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
246 BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
247 const size_t sz = buffer.size() - offset;
248 uint8_t* buf = buffer.data() + offset;
249
250 BOTAN_ARG_CHECK(sz >= minimum_final_size(), "missing sufficient final input in XTS decrypt");
251
252 const size_t BS = cipher_block_size();
253
254 if(sz % BS == 0) {
255 update(buffer, offset);
256 } else {
257 // steal ciphertext
258 const size_t full_blocks = ((sz / BS) - 1) * BS;
259 const size_t final_bytes = sz - full_blocks;
260 BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
261
262 secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
263 buffer.resize(full_blocks + offset);
264 update(buffer, offset);
265
266 xor_buf(last, tweak() + BS, BS);
267 cipher().decrypt(last);
268 xor_buf(last, tweak() + BS, BS);
269
270 for(size_t i = 0; i != final_bytes - BS; ++i) {
271 last[i] ^= last[i + BS];
272 last[i + BS] ^= last[i];
273 last[i] ^= last[i + BS];
274 }
275
276 xor_buf(last, tweak(), BS);
277 cipher().decrypt(last);
278 xor_buf(last, tweak(), BS);
279
280 buffer += last;
281 }
282}
283
284} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:62
void encrypt(const uint8_t in[], uint8_t out[]) const
void decrypt(const uint8_t in[], uint8_t out[]) const
virtual void decrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const =0
virtual void encrypt_n(const uint8_t in[], uint8_t out[], size_t blocks) const =0
static bool has(CPUID::Feature feat)
Definition cpuid.h:94
void update(T &buffer, size_t offset=0)
Key_Length_Specification multiple(size_t n) const
Definition sym_algo.h:72
virtual std::string name() const =0
virtual Key_Length_Specification key_spec() const =0
size_t output_length(size_t input_length) const override
Definition xts.cpp:214
size_t output_length(size_t input_length) const override
Definition xts.cpp:144
void reset() final
Definition xts.cpp:48
const uint8_t * tweak() const
Definition xts.h:45
size_t ideal_granularity() const final
Definition xts.cpp:44
std::string name() const final
Definition xts.cpp:52
void update_tweak(size_t consumed)
Definition xts.cpp:123
size_t default_nonce_length() const final
Definition xts.cpp:64
size_t cipher_block_size() const
Definition xts.h:55
size_t update_granularity() const final
Definition xts.cpp:40
bool has_keying_material() const final
Definition xts.cpp:72
void clear() final
Definition xts.cpp:34
XTS_Mode(std::unique_ptr< BlockCipher > cipher)
Definition xts.cpp:22
const BlockCipher & cipher() const
Definition xts.h:51
size_t tweak_blocks() const
Definition xts.h:49
Key_Length_Specification key_spec() const final
Definition xts.cpp:60
bool tweak_set() const
Definition xts.h:47
bool valid_nonce_length(size_t n) const final
Definition xts.cpp:68
size_t minimum_final_size() const final
Definition xts.cpp:56
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
void poly_double_n_le(uint8_t out[], const uint8_t in[], size_t n)
Definition poly_dbl.cpp:100
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:403
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
void xts_compute_tweak_block(uint8_t tweak[], size_t BS, size_t blocks_in_tweak)
Definition poly_dbl.cpp:119
bool poly_double_supported_size(size_t n)
Definition poly_dbl.h:22
constexpr void clear_mem(T *ptr, size_t n)
Definition mem_ops.h:118