Coverage Report

Created: 2026-10-28 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/modes/xts/xts.cpp
Line
Count
Source
1
/*
2
* XTS Mode
3
* (C) 2009,2013 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/mem_ops.h>
12
#include <botan/internal/fmt.h>
13
#include <botan/internal/poly_dbl.h>
14
15
namespace Botan {
16
17
XTS_Mode::XTS_Mode(std::unique_ptr<BlockCipher> cipher) :
18
0
      m_cipher(std::move(cipher)),
19
0
      m_cipher_block_size(m_cipher->block_size()),
20
0
      m_cipher_parallelism(m_cipher->parallel_bytes()),
21
0
      m_tweak_blocks(m_cipher_parallelism / m_cipher_block_size) {
22
0
   if(!poly_double_supported_size(m_cipher_block_size)) {
23
0
      throw Invalid_Argument(fmt("Cannot use {} with XTS", m_cipher->name()));
24
0
   }
25
26
0
   m_tweak_cipher = m_cipher->new_object();
27
0
}
28
29
0
void XTS_Mode::clear() {
30
0
   m_cipher->clear();
31
0
   m_tweak_cipher->clear();
32
0
   reset();
33
0
}
34
35
0
size_t XTS_Mode::update_granularity() const {
36
0
   return m_cipher_block_size;
37
0
}
38
39
0
size_t XTS_Mode::ideal_granularity() const {
40
0
   return m_cipher_parallelism;
41
0
}
42
43
0
void XTS_Mode::reset() {
44
0
   m_tweak.clear();
45
0
}
46
47
0
std::string XTS_Mode::name() const {
48
0
   return cipher().name() + "/XTS";
49
0
}
50
51
0
size_t XTS_Mode::minimum_final_size() const {
52
0
   return cipher_block_size();
53
0
}
54
55
0
Key_Length_Specification XTS_Mode::key_spec() const {
56
0
   return cipher().key_spec().multiple(2);
57
0
}
58
59
0
size_t XTS_Mode::default_nonce_length() const {
60
0
   return cipher_block_size();
61
0
}
62
63
0
bool XTS_Mode::valid_nonce_length(size_t n) const {
64
0
   return n <= cipher_block_size();
65
0
}
66
67
0
bool XTS_Mode::has_keying_material() const {
68
0
   return m_cipher->has_keying_material() && m_tweak_cipher->has_keying_material();
69
0
}
70
71
0
void XTS_Mode::key_schedule(std::span<const uint8_t> key) {
72
0
   const size_t key_half = key.size() / 2;
73
74
0
   if(key.size() % 2 == 1 || !m_cipher->valid_keylength(key_half)) {
75
0
      throw Invalid_Key_Length(name(), key.size());
76
0
   }
77
78
0
   m_cipher->set_key(key.first(key_half));
79
0
   m_tweak_cipher->set_key(key.last(key_half));
80
0
}
81
82
0
void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
83
0
   if(!valid_nonce_length(nonce_len)) {
84
0
      throw Invalid_IV_Length(name(), nonce_len);
85
0
   }
86
87
0
   m_tweak.resize(m_cipher_parallelism);
88
0
   clear_mem(m_tweak.data(), m_tweak.size());
89
0
   copy_mem(m_tweak.data(), nonce, nonce_len);
90
0
   m_tweak_cipher->encrypt(m_tweak.data());
91
92
0
   update_tweak(0);
93
0
}
94
95
0
void XTS_Mode::update_tweak(size_t which) {
96
0
   const size_t BS = m_tweak_cipher->block_size();
97
98
0
   if(which > 0) {
99
0
      poly_double_n_le(m_tweak.data(), &m_tweak[(which - 1) * BS], BS);
100
0
   }
101
102
0
   const size_t blocks_in_tweak = tweak_blocks();
103
104
0
   xts_update_tweak_block(m_tweak.data(), BS, blocks_in_tweak);
105
0
}
106
107
0
size_t XTS_Encryption::output_length(size_t input_length) const {
108
0
   return input_length;
109
0
}
110
111
0
size_t XTS_Encryption::process_msg(uint8_t buf[], size_t sz) {
112
0
   BOTAN_STATE_CHECK(tweak_set());
113
0
   const size_t BS = cipher_block_size();
114
115
0
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
116
0
   size_t blocks = sz / BS;
117
118
0
   const size_t blocks_in_tweak = tweak_blocks();
119
120
0
   while(blocks > 0) {
121
0
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
122
0
      const size_t proc_bytes = to_proc * BS;
123
124
0
      xor_buf(buf, tweak(), proc_bytes);
125
0
      cipher().encrypt_n(buf, buf, to_proc);
126
0
      xor_buf(buf, tweak(), proc_bytes);
127
128
0
      buf += proc_bytes;
129
0
      blocks -= to_proc;
130
131
0
      update_tweak(to_proc);
132
0
   }
133
134
0
   return sz;
135
0
}
136
137
0
void XTS_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
138
0
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
139
0
   const size_t sz = buffer.size() - offset;
140
0
   uint8_t* buf = buffer.data() + offset;
141
142
0
   BOTAN_ARG_CHECK(sz >= minimum_final_size(), "missing sufficient final input in XTS encrypt");
143
144
0
   const size_t BS = cipher_block_size();
145
146
0
   if(sz % BS == 0) {
147
0
      update(buffer, offset);
148
0
   } else {
149
      // steal ciphertext
150
0
      const size_t full_blocks = ((sz / BS) - 1) * BS;
151
0
      const size_t final_bytes = sz - full_blocks;
152
0
      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
153
154
0
      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
155
0
      buffer.resize(full_blocks + offset);
156
0
      update(buffer, offset);
157
158
0
      xor_buf(last, tweak(), BS);
159
0
      cipher().encrypt(last);
160
0
      xor_buf(last, tweak(), BS);
161
162
0
      for(size_t i = 0; i != final_bytes - BS; ++i) {
163
0
         last[i] ^= last[i + BS];
164
0
         last[i + BS] ^= last[i];
165
0
         last[i] ^= last[i + BS];
166
0
      }
167
168
0
      xor_buf(last, tweak() + BS, BS);
169
0
      cipher().encrypt(last);
170
0
      xor_buf(last, tweak() + BS, BS);
171
172
0
      buffer += last;
173
0
   }
174
0
}
175
176
0
size_t XTS_Decryption::output_length(size_t input_length) const {
177
0
   return input_length;
178
0
}
179
180
0
size_t XTS_Decryption::process_msg(uint8_t buf[], size_t sz) {
181
0
   BOTAN_STATE_CHECK(tweak_set());
182
0
   const size_t BS = cipher_block_size();
183
184
0
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
185
0
   size_t blocks = sz / BS;
186
187
0
   const size_t blocks_in_tweak = tweak_blocks();
188
189
0
   while(blocks > 0) {
190
0
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
191
0
      const size_t proc_bytes = to_proc * BS;
192
193
0
      xor_buf(buf, tweak(), proc_bytes);
194
0
      cipher().decrypt_n(buf, buf, to_proc);
195
0
      xor_buf(buf, tweak(), proc_bytes);
196
197
0
      buf += proc_bytes;
198
0
      blocks -= to_proc;
199
200
0
      update_tweak(to_proc);
201
0
   }
202
203
0
   return sz;
204
0
}
205
206
0
void XTS_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
207
0
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
208
0
   const size_t sz = buffer.size() - offset;
209
0
   uint8_t* buf = buffer.data() + offset;
210
211
0
   BOTAN_ARG_CHECK(sz >= minimum_final_size(), "missing sufficient final input in XTS decrypt");
212
213
0
   const size_t BS = cipher_block_size();
214
215
0
   if(sz % BS == 0) {
216
0
      update(buffer, offset);
217
0
   } else {
218
      // steal ciphertext
219
0
      const size_t full_blocks = ((sz / BS) - 1) * BS;
220
0
      const size_t final_bytes = sz - full_blocks;
221
0
      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
222
223
0
      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
224
0
      buffer.resize(full_blocks + offset);
225
0
      update(buffer, offset);
226
227
0
      xor_buf(last, tweak() + BS, BS);
228
0
      cipher().decrypt(last);
229
0
      xor_buf(last, tweak() + BS, BS);
230
231
0
      for(size_t i = 0; i != final_bytes - BS; ++i) {
232
0
         last[i] ^= last[i + BS];
233
0
         last[i + BS] ^= last[i];
234
0
         last[i] ^= last[i + BS];
235
0
      }
236
237
0
      xor_buf(last, tweak(), BS);
238
0
      cipher().decrypt(last);
239
0
      xor_buf(last, tweak(), BS);
240
241
0
      buffer += last;
242
0
   }
243
0
}
244
245
}  // namespace Botan