• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 21753596263

06 Feb 2026 02:13PM UTC coverage: 90.063% (-0.01%) from 90.073%
21753596263

Pull #5289

github

web-flow
Merge 587099284 into 8ea0ca252
Pull Request #5289: Further misc header reductions, forward declarations, etc

102237 of 113517 relevant lines covered (90.06%)

11402137.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.04
/src/lib/modes/xts/xts.cpp
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

20
namespace Botan {
21

22
XTS_Mode::XTS_Mode(std::unique_ptr<BlockCipher> cipher) :
2,822✔
23
      m_cipher(std::move(cipher)),
2,822✔
24
      m_cipher_block_size(m_cipher->block_size()),
2,822✔
25
      m_cipher_parallelism(m_cipher->parallel_bytes()),
2,822✔
26
      m_tweak_blocks(m_cipher_parallelism / m_cipher_block_size) {
2,822✔
27
   if(!poly_double_supported_size(m_cipher_block_size)) {
2,822✔
28
      throw Invalid_Argument(fmt("Cannot use {} with XTS", m_cipher->name()));
×
29
   }
30

31
   m_tweak_cipher = m_cipher->new_object();
2,822✔
32
}
2,822✔
33

34
void XTS_Mode::clear() {
1,672✔
35
   m_cipher->clear();
1,672✔
36
   m_tweak_cipher->clear();
1,672✔
37
   reset();
3,344✔
38
}
1,672✔
39

40
size_t XTS_Mode::update_granularity() const {
5,852✔
41
   return m_cipher_block_size;
5,852✔
42
}
43

44
size_t XTS_Mode::ideal_granularity() const {
5,016✔
45
   return m_cipher_parallelism;
5,016✔
46
}
47

48
void XTS_Mode::reset() {
5,016✔
49
   m_tweak.clear();
5,016✔
50
}
3,344✔
51

52
std::string XTS_Mode::name() const {
3,344✔
53
   return cipher().name() + "/XTS";
6,688✔
54
}
55

56
size_t XTS_Mode::minimum_final_size() const {
12,918✔
57
   return cipher_block_size();
12,918✔
58
}
59

60
Key_Length_Specification XTS_Mode::key_spec() const {
3,658✔
61
   return cipher().key_spec().multiple(2);
3,658✔
62
}
63

64
size_t XTS_Mode::default_nonce_length() const {
1,672✔
65
   return cipher_block_size();
1,672✔
66
}
67

68
bool XTS_Mode::valid_nonce_length(size_t n) const {
14,590✔
69
   return n <= cipher_block_size();
14,590✔
70
}
71

72
bool XTS_Mode::has_keying_material() const {
5,016✔
73
   return m_cipher->has_keying_material() && m_tweak_cipher->has_keying_material();
5,016✔
74
}
75

76
void XTS_Mode::key_schedule(std::span<const uint8_t> key) {
3,658✔
77
   const size_t key_half = key.size() / 2;
3,658✔
78

79
   if(key.size() % 2 == 1 || !m_cipher->valid_keylength(key_half)) {
3,658✔
80
      throw Invalid_Key_Length(name(), key.size());
×
81
   }
82

83
   m_cipher->set_key(key.first(key_half));
3,658✔
84
   m_tweak_cipher->set_key(key.last(key_half));
3,658✔
85
}
3,658✔
86

87
void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
11,246✔
88
   if(!valid_nonce_length(nonce_len)) {
11,246✔
89
      throw Invalid_IV_Length(name(), nonce_len);
3,344✔
90
   }
91

92
   m_tweak.resize(m_cipher_parallelism);
9,574✔
93
   clear_mem(m_tweak.data(), m_tweak.size());
9,574✔
94
   copy_mem(m_tweak.data(), nonce, nonce_len);
9,574✔
95
   m_tweak_cipher->encrypt(m_tweak.data());
9,574✔
96

97
   // Just repeated doubling from first, remaining contents are junk...
98
   xts_compute_tweak_block(m_tweak.data(), m_tweak_cipher->block_size(), tweak_blocks());
9,574✔
99
}
9,574✔
100

101
//static
102
void XTS_Mode::update_tweak_block(uint8_t tweak[], size_t BS, size_t blocks_in_tweak) {
3,906✔
103
#if defined(BOTAN_HAS_MODE_XTS_AVX512_CLMUL)
104
   if(BS == 16 && blocks_in_tweak % 8 == 0 && CPUID::has(CPUID::Feature::AVX512_CLMUL)) {
3,906✔
105
      return update_tweak_block_avx512_clmul(tweak, BS, blocks_in_tweak);
×
106
   }
107
#endif
108

109
   /*
110
   * If we don't have a fast method available, just set the first tweak block to
111
   * the doubling of the last tweak block, and recompute all the rest via
112
   * successive doublings.
113
   */
114
   poly_double_n_le(tweak, &tweak[(blocks_in_tweak - 1) * BS], BS);
3,906✔
115
   xts_compute_tweak_block(tweak, BS, blocks_in_tweak);
3,906✔
116
}
117

118
void XTS_Mode::update_tweak(size_t consumed) {
21,546✔
119
   const size_t BS = m_tweak_cipher->block_size();
21,546✔
120
   const size_t blocks_in_tweak = tweak_blocks();
21,546✔
121

122
   BOTAN_ASSERT_NOMSG(consumed > 0 && consumed <= blocks_in_tweak);
21,546✔
123

124
   if(consumed == blocks_in_tweak) {
21,546✔
125
      // Update all in parallel
126
      update_tweak_block(m_tweak.data(), BS, blocks_in_tweak);
3,906✔
127
   } else {
128
      /*
129
      The last remaining tweaks can just be shifted over
130

131
      This could be a lot better though! We can copy all of the remaining tweaks
132
      and just recompute the last few
133
      */
134
      copy_mem(m_tweak.data(), &m_tweak[(consumed * BS)], BS);
17,640✔
135
      xts_compute_tweak_block(m_tweak.data(), BS, blocks_in_tweak);
17,640✔
136
   }
137
}
21,546✔
138

139
size_t XTS_Encryption::output_length(size_t input_length) const {
836✔
140
   return input_length;
836✔
141
}
142

143
size_t XTS_Encryption::process_msg(uint8_t buf[], size_t sz) {
16,140✔
144
   BOTAN_STATE_CHECK(tweak_set());
16,140✔
145
   const size_t BS = cipher_block_size();
11,124✔
146

147
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
11,124✔
148
   size_t blocks = sz / BS;
11,124✔
149

150
   const size_t blocks_in_tweak = tweak_blocks();
11,124✔
151

152
   while(blocks > 0) {
21,897✔
153
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
10,773✔
154
      const size_t proc_bytes = to_proc * BS;
10,773✔
155

156
      xor_buf(buf, tweak(), proc_bytes);
10,773✔
157
      cipher().encrypt_n(buf, buf, to_proc);
10,773✔
158
      xor_buf(buf, tweak(), proc_bytes);
10,773✔
159

160
      buf += proc_bytes;
10,773✔
161
      blocks -= to_proc;
10,773✔
162

163
      update_tweak(to_proc);
10,773✔
164
   }
165

166
   return sz;
11,124✔
167
}
168

169
void XTS_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
5,623✔
170
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
5,623✔
171
   const size_t sz = buffer.size() - offset;
5,623✔
172
   uint8_t* buf = buffer.data() + offset;
5,623✔
173

174
   BOTAN_ARG_CHECK(sz >= minimum_final_size(), "missing sufficient final input in XTS encrypt");
5,623✔
175

176
   const size_t BS = cipher_block_size();
5,623✔
177

178
   if(sz % BS == 0) {
5,623✔
179
      update(buffer, offset);
2,814✔
180
   } else {
181
      // steal ciphertext
182
      const size_t full_blocks = ((sz / BS) - 1) * BS;
2,809✔
183
      const size_t final_bytes = sz - full_blocks;
2,809✔
184
      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
2,809✔
185

186
      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
2,809✔
187
      buffer.resize(full_blocks + offset);
2,809✔
188
      update(buffer, offset);
2,809✔
189

190
      xor_buf(last, tweak(), BS);
2,809✔
191
      cipher().encrypt(last);
2,809✔
192
      xor_buf(last, tweak(), BS);
2,809✔
193

194
      for(size_t i = 0; i != final_bytes - BS; ++i) {
22,436✔
195
         last[i] ^= last[i + BS];
19,627✔
196
         last[i + BS] ^= last[i];
19,627✔
197
         last[i] ^= last[i + BS];
19,627✔
198
      }
199

200
      xor_buf(last, tweak() + BS, BS);
2,809✔
201
      cipher().encrypt(last);
2,809✔
202
      xor_buf(last, tweak() + BS, BS);
2,809✔
203

204
      buffer += last;
2,809✔
205
   }
2,809✔
206
}
3,115✔
207

208
size_t XTS_Decryption::output_length(size_t input_length) const {
836✔
209
   return input_length;
836✔
210
}
211

212
size_t XTS_Decryption::process_msg(uint8_t buf[], size_t sz) {
16,140✔
213
   BOTAN_STATE_CHECK(tweak_set());
16,140✔
214
   const size_t BS = cipher_block_size();
11,124✔
215

216
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
11,124✔
217
   size_t blocks = sz / BS;
11,124✔
218

219
   const size_t blocks_in_tweak = tweak_blocks();
11,124✔
220

221
   while(blocks > 0) {
21,897✔
222
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
10,773✔
223
      const size_t proc_bytes = to_proc * BS;
10,773✔
224

225
      xor_buf(buf, tweak(), proc_bytes);
10,773✔
226
      cipher().decrypt_n(buf, buf, to_proc);
10,773✔
227
      xor_buf(buf, tweak(), proc_bytes);
10,773✔
228

229
      buf += proc_bytes;
10,773✔
230
      blocks -= to_proc;
10,773✔
231

232
      update_tweak(to_proc);
10,773✔
233
   }
234

235
   return sz;
11,124✔
236
}
237

238
void XTS_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
5,623✔
239
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
5,623✔
240
   const size_t sz = buffer.size() - offset;
5,623✔
241
   uint8_t* buf = buffer.data() + offset;
5,623✔
242

243
   BOTAN_ARG_CHECK(sz >= minimum_final_size(), "missing sufficient final input in XTS decrypt");
5,623✔
244

245
   const size_t BS = cipher_block_size();
5,623✔
246

247
   if(sz % BS == 0) {
5,623✔
248
      update(buffer, offset);
2,814✔
249
   } else {
250
      // steal ciphertext
251
      const size_t full_blocks = ((sz / BS) - 1) * BS;
2,809✔
252
      const size_t final_bytes = sz - full_blocks;
2,809✔
253
      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
2,809✔
254

255
      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
2,809✔
256
      buffer.resize(full_blocks + offset);
2,809✔
257
      update(buffer, offset);
2,809✔
258

259
      xor_buf(last, tweak() + BS, BS);
2,809✔
260
      cipher().decrypt(last);
2,809✔
261
      xor_buf(last, tweak() + BS, BS);
2,809✔
262

263
      for(size_t i = 0; i != final_bytes - BS; ++i) {
22,436✔
264
         last[i] ^= last[i + BS];
19,627✔
265
         last[i + BS] ^= last[i];
19,627✔
266
         last[i] ^= last[i + BS];
19,627✔
267
      }
268

269
      xor_buf(last, tweak(), BS);
2,809✔
270
      cipher().decrypt(last);
2,809✔
271
      xor_buf(last, tweak(), BS);
2,809✔
272

273
      buffer += last;
2,809✔
274
   }
2,809✔
275
}
3,115✔
276

277
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc