Coverage Report

Created: 2023-02-22 06:39

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