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

randombit / botan / 21228005733

21 Jan 2026 10:28PM UTC coverage: 90.071% (-0.02%) from 90.091%
21228005733

push

github

web-flow
Merge pull request #5251 from randombit/jack/xts-avx512-clmul

Add AVX512+CLMUL optimized XTS tweak computation

102088 of 113342 relevant lines covered (90.07%)

11473779.29 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/mem_ops.h>
12
#include <botan/internal/fmt.h>
13
#include <botan/internal/poly_dbl.h>
14

15
#if defined(BOTAN_HAS_MODE_XTS_AVX512_CLMUL)
16
   #include <botan/internal/cpuid.h>
17
#endif
18

19
namespace Botan {
20

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

165
   return sz;
11,124✔
166
}
167

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

234
   return sz;
11,124✔
235
}
236

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

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

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

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

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

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

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

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

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

276
}  // 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