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

randombit / botan / 24124820251

07 Apr 2026 10:30PM UTC coverage: 89.442% (-2.4%) from 91.821%
24124820251

push

github

randombit
Fix a warning when building for Emscripten

105964 of 118472 relevant lines covered (89.44%)

11407761.44 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 {
16,084✔
57
   return cipher_block_size();
16,084✔
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 {
17,756✔
69
   return n <= cipher_block_size();
17,756✔
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) {
14,412✔
88
   if(!valid_nonce_length(nonce_len)) {
14,412✔
89
      throw Invalid_IV_Length(name(), nonce_len);
3,344✔
90
   }
91

92
   m_tweak.resize(m_cipher_parallelism);
12,740✔
93
   clear_mem(m_tweak.data(), m_tweak.size());
12,740✔
94
   copy_mem(m_tweak.data(), nonce, nonce_len);
12,740✔
95
   m_tweak_cipher->encrypt(m_tweak.data());
12,740✔
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());
12,740✔
99
}
12,740✔
100

101
//static
102
void XTS_Mode::update_tweak_block(uint8_t tweak[], size_t BS, size_t blocks_in_tweak) {
3,576✔
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,576✔
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,576✔
115
   xts_compute_tweak_block(tweak, BS, blocks_in_tweak);
3,576✔
116
}
117

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

122
   BOTAN_ASSERT_NOMSG(consumed > 0 && consumed <= blocks_in_tweak);
24,280✔
123

124
   if(consumed == blocks_in_tweak) {
24,280✔
125
      // Update all in parallel
126
      update_tweak_block(m_tweak.data(), BS, blocks_in_tweak);
3,576✔
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);
20,704✔
135
      xts_compute_tweak_block(m_tweak.data(), BS, blocks_in_tweak);
20,704✔
136
   }
137
}
24,280✔
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) {
18,470✔
144
   BOTAN_STATE_CHECK(tweak_set());
18,470✔
145
   const size_t BS = cipher_block_size();
13,454✔
146

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

150
   const size_t blocks_in_tweak = tweak_blocks();
13,454✔
151

152
   while(blocks > 0) {
25,594✔
153
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
12,140✔
154
      const size_t proc_bytes = to_proc * BS;
12,140✔
155

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

160
      buf += proc_bytes;
12,140✔
161
      blocks -= to_proc;
12,140✔
162

163
      update_tweak(to_proc);
12,140✔
164
   }
165

166
   return sz;
13,454✔
167
}
168

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

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

176
   const size_t BS = cipher_block_size();
7,206✔
177

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

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

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

194
      for(size_t i = 0; i != final_bytes - BS; ++i) {
33,878✔
195
         last[i] ^= last[i + BS];
29,633✔
196
         last[i + BS] ^= last[i];
29,633✔
197
         last[i] ^= last[i + BS];
29,633✔
198
      }
199

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

204
      buffer += last;
4,245✔
205
   }
4,245✔
206
}
4,698✔
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) {
18,470✔
213
   BOTAN_STATE_CHECK(tweak_set());
18,470✔
214
   const size_t BS = cipher_block_size();
13,454✔
215

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

219
   const size_t blocks_in_tweak = tweak_blocks();
13,454✔
220

221
   while(blocks > 0) {
25,594✔
222
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
12,140✔
223
      const size_t proc_bytes = to_proc * BS;
12,140✔
224

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

229
      buf += proc_bytes;
12,140✔
230
      blocks -= to_proc;
12,140✔
231

232
      update_tweak(to_proc);
12,140✔
233
   }
234

235
   return sz;
13,454✔
236
}
237

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

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

245
   const size_t BS = cipher_block_size();
7,206✔
246

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

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

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

263
      for(size_t i = 0; i != final_bytes - BS; ++i) {
33,878✔
264
         last[i] ^= last[i + BS];
29,633✔
265
         last[i + BS] ^= last[i];
29,633✔
266
         last[i] ^= last[i + BS];
29,633✔
267
      }
268

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

273
      buffer += last;
4,245✔
274
   }
4,245✔
275
}
4,698✔
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