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

randombit / botan / 26323460081

23 May 2026 12:00AM UTC coverage: 89.383% (+0.03%) from 89.349%
26323460081

push

github

web-flow
Merge pull request #5621 from randombit/jack/cli-port-shift

Move the cli test TCP/UDP port range downward

109787 of 122827 relevant lines covered (89.38%)

11032030.9 hits per line

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

98.08
/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() {
12,018✔
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 {
11,068✔
57
   return cipher_block_size();
11,068✔
58
}
59

60
Key_Length_Specification XTS_Mode::key_spec() const {
7,002✔
61
   return cipher().key_spec().multiple(2);
7,002✔
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 {
19,428✔
69
   return n <= cipher_block_size();
19,428✔
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) {
7,002✔
77
   const size_t key_half = key.size() / 2;
7,002✔
78

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

83
   m_cipher->set_key(key.first(key_half));
7,002✔
84
   m_tweak_cipher->set_key(key.last(key_half));
7,002✔
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();
7,002✔
90
}
7,002✔
91

92
void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
16,084✔
93
   if(!valid_nonce_length(nonce_len)) {
16,084✔
94
      throw Invalid_IV_Length(name(), nonce_len);
3,344✔
95
   }
96

97
   m_tweak.resize(m_cipher_parallelism);
14,412✔
98
   clear_mem(m_tweak.data(), m_tweak.size());
14,412✔
99
   copy_mem(m_tweak.data(), nonce, nonce_len);
14,412✔
100
   m_tweak_cipher->encrypt(m_tweak.data());
14,412✔
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());
14,412✔
104
}
14,412✔
105

106
//static
107
void XTS_Mode::update_tweak_block(uint8_t tweak[], size_t BS, size_t blocks_in_tweak) {
3,576✔
108
#if defined(BOTAN_HAS_MODE_XTS_AVX512_CLMUL)
109
   if(BS == 16 && blocks_in_tweak % 8 == 0 && CPUID::has(CPUID::Feature::AVX512_CLMUL)) {
3,576✔
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);
3,576✔
120
   xts_compute_tweak_block(tweak, BS, blocks_in_tweak);
3,576✔
121
}
122

123
void XTS_Mode::update_tweak(size_t consumed) {
25,952✔
124
   const size_t BS = m_tweak_cipher->block_size();
25,952✔
125
   const size_t blocks_in_tweak = tweak_blocks();
25,952✔
126

127
   BOTAN_ASSERT_NOMSG(consumed > 0 && consumed <= blocks_in_tweak);
25,952✔
128

129
   if(consumed == blocks_in_tweak) {
25,952✔
130
      // Update all in parallel
131
      update_tweak_block(m_tweak.data(), BS, blocks_in_tweak);
3,576✔
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);
22,376✔
140
      xts_compute_tweak_block(m_tweak.data(), BS, blocks_in_tweak);
22,376✔
141
   }
142
}
25,952✔
143

144
size_t XTS_Encryption::output_length(size_t input_length) const {
836✔
145
   return input_length;
836✔
146
}
147

148
size_t XTS_Encryption::process_msg(uint8_t buf[], size_t sz) {
17,634✔
149
   BOTAN_STATE_CHECK(tweak_set());
17,634✔
150
   const size_t BS = cipher_block_size();
14,290✔
151

152
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
14,290✔
153
   size_t blocks = sz / BS;
14,290✔
154

155
   const size_t blocks_in_tweak = tweak_blocks();
14,290✔
156

157
   while(blocks > 0) {
27,266✔
158
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
12,976✔
159
      const size_t proc_bytes = to_proc * BS;
12,976✔
160

161
      xor_buf(buf, tweak(), proc_bytes);
12,976✔
162
      cipher().encrypt_n(buf, buf, to_proc);
12,976✔
163
      xor_buf(buf, tweak(), proc_bytes);
12,976✔
164

165
      buf += proc_bytes;
12,976✔
166
      blocks -= to_proc;
12,976✔
167

168
      update_tweak(to_proc);
12,976✔
169
   }
170

171
   return sz;
14,290✔
172
}
173

174
void XTS_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
8,042✔
175
   BOTAN_STATE_CHECK(tweak_set());
8,042✔
176
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
4,698✔
177
   const size_t sz = buffer.size() - offset;
4,698✔
178
   uint8_t* buf = buffer.data() + offset;
4,698✔
179

180
   BOTAN_ARG_CHECK(sz >= minimum_final_size(), "missing sufficient final input in XTS encrypt");
4,698✔
181

182
   const size_t BS = cipher_block_size();
4,698✔
183

184
   if(sz % BS == 0) {
4,698✔
185
      update(buffer, offset);
453✔
186
   } else {
187
      // steal ciphertext
188
      const size_t full_blocks = ((sz / BS) - 1) * BS;
4,245✔
189
      const size_t final_bytes = sz - full_blocks;
4,245✔
190
      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
4,245✔
191

192
      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
4,245✔
193
      buffer.resize(full_blocks + offset);
4,245✔
194
      update(buffer, offset);
4,245✔
195

196
      xor_buf(last, tweak(), BS);
4,245✔
197
      cipher().encrypt(last);
4,245✔
198
      xor_buf(last, tweak(), BS);
4,245✔
199

200
      for(size_t i = 0; i != final_bytes - BS; ++i) {
33,878✔
201
         last[i] ^= last[i + BS];
29,633✔
202
         last[i + BS] ^= last[i];
29,633✔
203
         last[i] ^= last[i + BS];
29,633✔
204
      }
205

206
      xor_buf(last, tweak() + BS, BS);
4,245✔
207
      cipher().encrypt(last);
4,245✔
208
      xor_buf(last, tweak() + BS, BS);
4,245✔
209

210
      buffer += last;
4,245✔
211
   }
4,245✔
212
}
4,698✔
213

214
size_t XTS_Decryption::output_length(size_t input_length) const {
836✔
215
   return input_length;
836✔
216
}
217

218
size_t XTS_Decryption::process_msg(uint8_t buf[], size_t sz) {
17,634✔
219
   BOTAN_STATE_CHECK(tweak_set());
17,634✔
220
   const size_t BS = cipher_block_size();
14,290✔
221

222
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
14,290✔
223
   size_t blocks = sz / BS;
14,290✔
224

225
   const size_t blocks_in_tweak = tweak_blocks();
14,290✔
226

227
   while(blocks > 0) {
27,266✔
228
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
12,976✔
229
      const size_t proc_bytes = to_proc * BS;
12,976✔
230

231
      xor_buf(buf, tweak(), proc_bytes);
12,976✔
232
      cipher().decrypt_n(buf, buf, to_proc);
12,976✔
233
      xor_buf(buf, tweak(), proc_bytes);
12,976✔
234

235
      buf += proc_bytes;
12,976✔
236
      blocks -= to_proc;
12,976✔
237

238
      update_tweak(to_proc);
12,976✔
239
   }
240

241
   return sz;
14,290✔
242
}
243

244
void XTS_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
8,042✔
245
   BOTAN_STATE_CHECK(tweak_set());
8,042✔
246
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
4,698✔
247
   const size_t sz = buffer.size() - offset;
4,698✔
248
   uint8_t* buf = buffer.data() + offset;
4,698✔
249

250
   BOTAN_ARG_CHECK(sz >= minimum_final_size(), "missing sufficient final input in XTS decrypt");
4,698✔
251

252
   const size_t BS = cipher_block_size();
4,698✔
253

254
   if(sz % BS == 0) {
4,698✔
255
      update(buffer, offset);
453✔
256
   } else {
257
      // steal ciphertext
258
      const size_t full_blocks = ((sz / BS) - 1) * BS;
4,245✔
259
      const size_t final_bytes = sz - full_blocks;
4,245✔
260
      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
4,245✔
261

262
      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
4,245✔
263
      buffer.resize(full_blocks + offset);
4,245✔
264
      update(buffer, offset);
4,245✔
265

266
      xor_buf(last, tweak() + BS, BS);
4,245✔
267
      cipher().decrypt(last);
4,245✔
268
      xor_buf(last, tweak() + BS, BS);
4,245✔
269

270
      for(size_t i = 0; i != final_bytes - BS; ++i) {
33,878✔
271
         last[i] ^= last[i + BS];
29,633✔
272
         last[i + BS] ^= last[i];
29,633✔
273
         last[i] ^= last[i + BS];
29,633✔
274
      }
275

276
      xor_buf(last, tweak(), BS);
4,245✔
277
      cipher().decrypt(last);
4,245✔
278
      xor_buf(last, tweak(), BS);
4,245✔
279

280
      buffer += last;
4,245✔
281
   }
4,245✔
282
}
4,698✔
283

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