Coverage Report

Created: 2024-06-28 06:08

/src/botan/src/lib/modes/xts/xts.cpp
Line
Count
Source (jump to first uncovered line)
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/internal/fmt.h>
12
#include <botan/internal/poly_dbl.h>
13
14
namespace Botan {
15
16
XTS_Mode::XTS_Mode(std::unique_ptr<BlockCipher> cipher) :
17
      m_cipher(std::move(cipher)),
18
      m_cipher_block_size(m_cipher->block_size()),
19
      m_cipher_parallelism(m_cipher->parallel_bytes()),
20
0
      m_tweak_blocks(m_cipher_parallelism / m_cipher_block_size) {
21
0
   if(poly_double_supported_size(m_cipher_block_size) == false) {
22
0
      throw Invalid_Argument(fmt("Cannot use {} with XTS", m_cipher->name()));
23
0
   }
24
25
0
   m_tweak_cipher = m_cipher->new_object();
26
0
}
27
28
0
void XTS_Mode::clear() {
29
0
   m_cipher->clear();
30
0
   m_tweak_cipher->clear();
31
0
   reset();
32
0
}
33
34
0
size_t XTS_Mode::update_granularity() const {
35
0
   return m_cipher_block_size;
36
0
}
37
38
0
size_t XTS_Mode::ideal_granularity() const {
39
0
   return m_cipher_parallelism;
40
0
}
41
42
0
void XTS_Mode::reset() {
43
0
   m_tweak.clear();
44
0
}
45
46
0
std::string XTS_Mode::name() const {
47
0
   return cipher().name() + "/XTS";
48
0
}
49
50
0
size_t XTS_Mode::minimum_final_size() const {
51
0
   return cipher_block_size();
52
0
}
53
54
0
Key_Length_Specification XTS_Mode::key_spec() const {
55
0
   return cipher().key_spec().multiple(2);
56
0
}
57
58
0
size_t XTS_Mode::default_nonce_length() const {
59
0
   return cipher_block_size();
60
0
}
61
62
0
bool XTS_Mode::valid_nonce_length(size_t n) const {
63
0
   return n <= cipher_block_size();
64
0
}
65
66
0
bool XTS_Mode::has_keying_material() const {
67
0
   return m_cipher->has_keying_material() && m_tweak_cipher->has_keying_material();
68
0
}
69
70
0
void XTS_Mode::key_schedule(std::span<const uint8_t> key) {
71
0
   const size_t key_half = key.size() / 2;
72
73
0
   if(key.size() % 2 == 1 || !m_cipher->valid_keylength(key_half)) {
74
0
      throw Invalid_Key_Length(name(), key.size());
75
0
   }
76
77
0
   m_cipher->set_key(key.first(key_half));
78
0
   m_tweak_cipher->set_key(key.last(key_half));
79
0
}
80
81
0
void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
82
0
   if(!valid_nonce_length(nonce_len)) {
83
0
      throw Invalid_IV_Length(name(), nonce_len);
84
0
   }
85
86
0
   m_tweak.resize(m_cipher_parallelism);
87
0
   clear_mem(m_tweak.data(), m_tweak.size());
88
0
   copy_mem(m_tweak.data(), nonce, nonce_len);
89
0
   m_tweak_cipher->encrypt(m_tweak.data());
90
91
0
   update_tweak(0);
92
0
}
93
94
0
void XTS_Mode::update_tweak(size_t which) {
95
0
   const size_t BS = m_tweak_cipher->block_size();
96
97
0
   if(which > 0) {
98
0
      poly_double_n_le(m_tweak.data(), &m_tweak[(which - 1) * BS], BS);
99
0
   }
100
101
0
   const size_t blocks_in_tweak = tweak_blocks();
102
103
0
   xts_update_tweak_block(m_tweak.data(), BS, blocks_in_tweak);
104
0
}
105
106
0
size_t XTS_Encryption::output_length(size_t input_length) const {
107
0
   return input_length;
108
0
}
109
110
0
size_t XTS_Encryption::process_msg(uint8_t buf[], size_t sz) {
111
0
   BOTAN_STATE_CHECK(tweak_set());
112
0
   const size_t BS = cipher_block_size();
113
114
0
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
115
0
   size_t blocks = sz / BS;
116
117
0
   const size_t blocks_in_tweak = tweak_blocks();
118
119
0
   while(blocks) {
120
0
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
121
122
0
      cipher().encrypt_n_xex(buf, tweak(), to_proc);
123
124
0
      buf += to_proc * BS;
125
0
      blocks -= to_proc;
126
127
0
      update_tweak(to_proc);
128
0
   }
129
130
0
   return sz;
131
0
}
132
133
0
void XTS_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
134
0
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
135
0
   const size_t sz = buffer.size() - offset;
136
0
   uint8_t* buf = buffer.data() + offset;
137
138
0
   BOTAN_ARG_CHECK(sz >= minimum_final_size(), "missing sufficient final input in XTS encrypt");
139
140
0
   const size_t BS = cipher_block_size();
141
142
0
   if(sz % BS == 0) {
143
0
      update(buffer, offset);
144
0
   } else {
145
      // steal ciphertext
146
0
      const size_t full_blocks = ((sz / BS) - 1) * BS;
147
0
      const size_t final_bytes = sz - full_blocks;
148
0
      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
149
150
0
      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
151
0
      buffer.resize(full_blocks + offset);
152
0
      update(buffer, offset);
153
154
0
      xor_buf(last, tweak(), BS);
155
0
      cipher().encrypt(last);
156
0
      xor_buf(last, tweak(), BS);
157
158
0
      for(size_t i = 0; i != final_bytes - BS; ++i) {
159
0
         last[i] ^= last[i + BS];
160
0
         last[i + BS] ^= last[i];
161
0
         last[i] ^= last[i + BS];
162
0
      }
163
164
0
      xor_buf(last, tweak() + BS, BS);
165
0
      cipher().encrypt(last);
166
0
      xor_buf(last, tweak() + BS, BS);
167
168
0
      buffer += last;
169
0
   }
170
0
}
171
172
0
size_t XTS_Decryption::output_length(size_t input_length) const {
173
0
   return input_length;
174
0
}
175
176
0
size_t XTS_Decryption::process_msg(uint8_t buf[], size_t sz) {
177
0
   BOTAN_STATE_CHECK(tweak_set());
178
0
   const size_t BS = cipher_block_size();
179
180
0
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
181
0
   size_t blocks = sz / BS;
182
183
0
   const size_t blocks_in_tweak = tweak_blocks();
184
185
0
   while(blocks) {
186
0
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
187
188
0
      cipher().decrypt_n_xex(buf, tweak(), to_proc);
189
190
0
      buf += to_proc * BS;
191
0
      blocks -= to_proc;
192
193
0
      update_tweak(to_proc);
194
0
   }
195
196
0
   return sz;
197
0
}
198
199
0
void XTS_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
200
0
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
201
0
   const size_t sz = buffer.size() - offset;
202
0
   uint8_t* buf = buffer.data() + offset;
203
204
0
   BOTAN_ARG_CHECK(sz >= minimum_final_size(), "missing sufficient final input in XTS decrypt");
205
206
0
   const size_t BS = cipher_block_size();
207
208
0
   if(sz % BS == 0) {
209
0
      update(buffer, offset);
210
0
   } else {
211
      // steal ciphertext
212
0
      const size_t full_blocks = ((sz / BS) - 1) * BS;
213
0
      const size_t final_bytes = sz - full_blocks;
214
0
      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2 * BS, "Left over size in expected range");
215
216
0
      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
217
0
      buffer.resize(full_blocks + offset);
218
0
      update(buffer, offset);
219
220
0
      xor_buf(last, tweak() + BS, BS);
221
0
      cipher().decrypt(last);
222
0
      xor_buf(last, tweak() + BS, BS);
223
224
0
      for(size_t i = 0; i != final_bytes - BS; ++i) {
225
0
         last[i] ^= last[i + BS];
226
0
         last[i + BS] ^= last[i];
227
0
         last[i] ^= last[i + BS];
228
0
      }
229
230
0
      xor_buf(last, tweak(), BS);
231
0
      cipher().decrypt(last);
232
0
      xor_buf(last, tweak(), BS);
233
234
0
      buffer += last;
235
0
   }
236
0
}
237
238
}  // namespace Botan