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

randombit / botan / 16239649803

12 Jul 2025 03:54PM UTC coverage: 90.588% (+0.02%) from 90.572%
16239649803

Pull #4880

github

web-flow
Merge 79ae69b5e into e3b3452ec
Pull Request #4880: Refactor: Cipher_Mode::finish_msg() using std::span

99178 of 109483 relevant lines covered (90.59%)

12365337.5 hits per line

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

98.61
/src/lib/modes/xts/xts.cpp
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
namespace {
18

19
constexpr void swap_bytes_via_xor(std::span<uint8_t> a, std::span<uint8_t> b) {
6,864✔
20
   BOTAN_DEBUG_ASSERT(a.size() == b.size());
6,864✔
21

22
   // TODO: use std::views::zip once we shed some older compilers, like:
23
   //       for(auto& [front, back] : std::views::zip(a, b))
24
   for(auto front = a.begin(), back = b.begin(); front != a.end() && back != b.end(); ++front, ++back) {
55,120✔
25
      *front ^= *back;
48,256✔
26
      *back ^= *front;
48,256✔
27
      *front ^= *back;
48,256✔
28
   }
29
}
6,864✔
30

31
}  // namespace
32

33
XTS_Mode::XTS_Mode(std::unique_ptr<BlockCipher> cipher) :
4,175✔
34
      m_cipher(std::move(cipher)),
4,175✔
35
      m_cipher_block_size(m_cipher->block_size()),
4,175✔
36
      m_cipher_parallelism(m_cipher->parallel_bytes()),
4,175✔
37
      m_tweak_blocks(m_cipher_parallelism / m_cipher_block_size) {
8,350✔
38
   if(poly_double_supported_size(m_cipher_block_size) == false) {
4,175✔
39
      throw Invalid_Argument(fmt("Cannot use {} with XTS", m_cipher->name()));
×
40
   }
41

42
   m_tweak_cipher = m_cipher->new_object();
4,175✔
43
}
4,175✔
44

45
void XTS_Mode::clear() {
1,670✔
46
   m_cipher->clear();
1,670✔
47
   m_tweak_cipher->clear();
1,670✔
48
   reset();
3,340✔
49
}
1,670✔
50

51
size_t XTS_Mode::update_granularity() const {
5,845✔
52
   return m_cipher_block_size;
5,845✔
53
}
54

55
size_t XTS_Mode::ideal_granularity() const {
5,010✔
56
   return m_cipher_parallelism;
5,010✔
57
}
58

59
void XTS_Mode::reset() {
5,010✔
60
   m_tweak.clear();
5,010✔
61
}
3,340✔
62

63
std::string XTS_Mode::name() const {
3,340✔
64
   return cipher().name() + "/XTS";
6,680✔
65
}
66

67
size_t XTS_Mode::minimum_final_size() const {
26,846✔
68
   return cipher_block_size();
12,588✔
69
}
70

71
Key_Length_Specification XTS_Mode::key_spec() const {
5,010✔
72
   return cipher().key_spec().multiple(2);
5,010✔
73
}
74

75
size_t XTS_Mode::output_length(size_t input_length) const {
1,670✔
76
   return input_length;
1,670✔
77
}
78

79
size_t XTS_Mode::bytes_needed_for_finalization(size_t final_input_length) const {
12,588✔
80
   BOTAN_ARG_CHECK(final_input_length >= minimum_final_size(), "Sufficient input");
12,588✔
81
   return final_input_length;
12,588✔
82
}
83

84
size_t XTS_Mode::default_nonce_length() const {
1,670✔
85
   return cipher_block_size();
1,670✔
86
}
87

88
bool XTS_Mode::valid_nonce_length(size_t n) const {
15,928✔
89
   return n <= cipher_block_size();
15,928✔
90
}
91

92
bool XTS_Mode::has_keying_material() const {
5,010✔
93
   return m_cipher->has_keying_material() && m_tweak_cipher->has_keying_material();
5,010✔
94
}
95

96
void XTS_Mode::key_schedule(std::span<const uint8_t> key) {
5,010✔
97
   const size_t key_half = key.size() / 2;
5,010✔
98

99
   if(key.size() % 2 == 1 || !m_cipher->valid_keylength(key_half)) {
5,010✔
100
      throw Invalid_Key_Length(name(), key.size());
×
101
   }
102

103
   m_cipher->set_key(key.first(key_half));
5,010✔
104
   m_tweak_cipher->set_key(key.last(key_half));
5,010✔
105
}
5,010✔
106

107
void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
12,588✔
108
   if(!valid_nonce_length(nonce_len)) {
12,588✔
109
      throw Invalid_IV_Length(name(), nonce_len);
3,340✔
110
   }
111

112
   m_tweak.resize(m_cipher_parallelism);
10,918✔
113
   clear_mem(m_tweak.data(), m_tweak.size());
10,918✔
114
   copy_mem(m_tweak.data(), nonce, nonce_len);
10,918✔
115
   m_tweak_cipher->encrypt(m_tweak.data());
10,918✔
116

117
   update_tweak(0);
10,918✔
118
}
10,918✔
119

120
void XTS_Mode::update_tweak(size_t which) {
34,700✔
121
   const size_t BS = m_tweak_cipher->block_size();
34,700✔
122

123
   if(which > 0) {
34,700✔
124
      poly_double_n_le(m_tweak.data(), &m_tweak[(which - 1) * BS], BS);
23,782✔
125
   }
126

127
   const size_t blocks_in_tweak = tweak_blocks();
34,700✔
128

129
   xts_update_tweak_block(m_tweak.data(), BS, blocks_in_tweak);
34,700✔
130
}
34,700✔
131

132
size_t XTS_Encryption::process_msg(uint8_t buf[], size_t sz) {
16,715✔
133
   BOTAN_STATE_CHECK(tweak_set());
16,715✔
134
   const size_t BS = cipher_block_size();
11,705✔
135

136
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
11,705✔
137
   size_t blocks = sz / BS;
11,705✔
138

139
   const size_t blocks_in_tweak = tweak_blocks();
11,705✔
140

141
   while(blocks > 0) {
23,596✔
142
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
11,891✔
143
      const size_t proc_bytes = to_proc * BS;
11,891✔
144

145
      xor_buf(buf, tweak(), proc_bytes);
11,891✔
146
      cipher().encrypt_n(buf, buf, to_proc);
11,891✔
147
      xor_buf(buf, tweak(), proc_bytes);
11,891✔
148

149
      buf += proc_bytes;
11,891✔
150
      blocks -= to_proc;
11,891✔
151

152
      update_tweak(to_proc);
11,891✔
153
   }
154

155
   return sz;
11,705✔
156
}
157

158
size_t XTS_Encryption::finish_msg(std::span<uint8_t> buffer, [[maybe_unused]] size_t final_input) {
6,294✔
159
   BOTAN_ASSERT_NOMSG(buffer.size() >= minimum_final_size());
6,294✔
160
   BOTAN_DEBUG_ASSERT(buffer.size() == bytes_needed_for_finalization(final_input));
6,294✔
161

162
   const size_t BS = cipher_block_size();
6,294✔
163

164
   if(buffer.size() % BS == 0) {
6,294✔
165
      process(buffer);
2,862✔
166
   } else {
167
      // steal ciphertext
168
      const auto full_blocks = buffer.first(((buffer.size() / BS) - 1) * BS);
3,432✔
169
      const auto tail = buffer.subspan(full_blocks.size());
3,432✔
170
      BOTAN_ASSERT(tail.size() > BS && tail.size() < 2 * BS, "Left over size in expected range");
3,432✔
171

172
      const auto full_tweak = std::span{tweak(), tweak_blocks() * BS};
3,432✔
173
      const auto first_block_of_tail = tail.first(BS);
3,432✔
174
      const auto final_bytes_of_tail = tail.subspan(BS);
3,432✔
175
      const auto first_bytes_of_tail = tail.first(final_bytes_of_tail.size());
3,432✔
176

177
      process(full_blocks);
3,432✔
178

179
      xor_buf(first_block_of_tail, full_tweak.first(BS));
3,432✔
180
      cipher().encrypt(first_block_of_tail);
3,432✔
181
      xor_buf(first_block_of_tail, full_tweak.first(BS));
3,432✔
182

183
      swap_bytes_via_xor(first_bytes_of_tail, final_bytes_of_tail);
3,432✔
184

185
      xor_buf(first_block_of_tail, full_tweak.subspan(BS, BS));
3,432✔
186
      cipher().encrypt(first_block_of_tail);
3,432✔
187
      xor_buf(first_block_of_tail, full_tweak.subspan(BS, BS));
3,432✔
188
   }
189

190
   return buffer.size();
3,789✔
191
}
192

193
size_t XTS_Decryption::process_msg(uint8_t buf[], size_t sz) {
16,715✔
194
   BOTAN_STATE_CHECK(tweak_set());
16,715✔
195
   const size_t BS = cipher_block_size();
11,705✔
196

197
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
11,705✔
198
   size_t blocks = sz / BS;
11,705✔
199

200
   const size_t blocks_in_tweak = tweak_blocks();
11,705✔
201

202
   while(blocks > 0) {
23,596✔
203
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
11,891✔
204
      const size_t proc_bytes = to_proc * BS;
11,891✔
205

206
      xor_buf(buf, tweak(), proc_bytes);
11,891✔
207
      cipher().decrypt_n(buf, buf, to_proc);
11,891✔
208
      xor_buf(buf, tweak(), proc_bytes);
11,891✔
209

210
      buf += proc_bytes;
11,891✔
211
      blocks -= to_proc;
11,891✔
212

213
      update_tweak(to_proc);
11,891✔
214
   }
215

216
   return sz;
11,705✔
217
}
218

219
size_t XTS_Decryption::finish_msg(std::span<uint8_t> buffer, [[maybe_unused]] size_t final_input) {
6,294✔
220
   BOTAN_ASSERT_NOMSG(buffer.size() >= minimum_final_size());
6,294✔
221
   BOTAN_DEBUG_ASSERT(buffer.size() == bytes_needed_for_finalization(final_input));
6,294✔
222

223
   const size_t BS = cipher_block_size();
6,294✔
224

225
   if(buffer.size() % BS == 0) {
6,294✔
226
      process(buffer);
2,862✔
227
   } else {
228
      // steal ciphertext
229
      const auto full_blocks = buffer.first(((buffer.size() / BS) - 1) * BS);
3,432✔
230
      const auto tail = buffer.subspan(full_blocks.size());
3,432✔
231
      BOTAN_ASSERT(tail.size() > BS && tail.size() < 2 * BS, "Left over size in expected range");
3,432✔
232

233
      const auto full_tweak = std::span{tweak(), tweak_blocks() * BS};
3,432✔
234
      const auto first_block_of_tail = tail.first(BS);
3,432✔
235
      const auto final_bytes_of_tail = tail.subspan(BS);
3,432✔
236
      const auto first_bytes_of_tail = tail.first(final_bytes_of_tail.size());
3,432✔
237

238
      process(full_blocks);
3,432✔
239

240
      xor_buf(first_block_of_tail, full_tweak.subspan(BS, BS));
3,432✔
241
      cipher().decrypt(first_block_of_tail);
3,432✔
242
      xor_buf(first_block_of_tail, full_tweak.subspan(BS, BS));
3,432✔
243

244
      swap_bytes_via_xor(final_bytes_of_tail, first_bytes_of_tail);
3,432✔
245

246
      xor_buf(first_block_of_tail, full_tweak.first(BS));
3,432✔
247
      cipher().decrypt(first_block_of_tail);
3,432✔
248
      xor_buf(first_block_of_tail, full_tweak.first(BS));
3,432✔
249
   }
250

251
   return buffer.size();
3,789✔
252
}
253

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