Coverage Report

Created: 2023-01-25 06:35

/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
void XTS_Mode::key_schedule(const uint8_t key[], size_t length)
76
0
   {
77
0
   const size_t key_half = length / 2;
78
79
0
   if(length % 2 == 1 || !m_cipher->valid_keylength(key_half))
80
0
      throw Invalid_Key_Length(name(), length);
81
82
0
   m_cipher->set_key(key, key_half);
83
0
   m_tweak_cipher->set_key(&key[key_half], key_half);
84
0
   }
85
86
void XTS_Mode::start_msg(const uint8_t nonce[], size_t nonce_len)
87
0
   {
88
0
   if(!valid_nonce_length(nonce_len))
89
0
      throw Invalid_IV_Length(name(), nonce_len);
90
91
0
   m_tweak.resize(m_cipher_parallelism);
92
0
   copy_mem(m_tweak.data(), nonce, nonce_len);
93
0
   m_tweak_cipher->encrypt(m_tweak.data());
94
95
0
   update_tweak(0);
96
0
   }
97
98
void XTS_Mode::update_tweak(size_t which)
99
0
   {
100
0
   const size_t BS = m_tweak_cipher->block_size();
101
102
0
   if(which > 0)
103
0
      poly_double_n_le(m_tweak.data(), &m_tweak[(which-1)*BS], BS);
104
105
0
   const size_t blocks_in_tweak = tweak_blocks();
106
107
0
   for(size_t i = 1; i < blocks_in_tweak; ++i)
108
0
      poly_double_n_le(&m_tweak[i*BS], &m_tweak[(i-1)*BS], BS);
109
0
   }
110
111
size_t XTS_Encryption::output_length(size_t input_length) const
112
0
   {
113
0
   return input_length;
114
0
   }
115
116
size_t XTS_Encryption::process(uint8_t buf[], size_t sz)
117
0
   {
118
0
   BOTAN_STATE_CHECK(tweak_set());
119
0
   const size_t BS = cipher_block_size();
120
121
0
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
122
0
   size_t blocks = sz / BS;
123
124
0
   const size_t blocks_in_tweak = tweak_blocks();
125
126
0
   while(blocks)
127
0
      {
128
0
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
129
130
0
      cipher().encrypt_n_xex(buf, tweak(), to_proc);
131
132
0
      buf += to_proc * BS;
133
0
      blocks -= to_proc;
134
135
0
      update_tweak(to_proc);
136
0
      }
137
138
0
   return sz;
139
0
   }
140
141
void XTS_Encryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
142
0
   {
143
0
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
144
0
   const size_t sz = buffer.size() - offset;
145
0
   uint8_t* buf = buffer.data() + offset;
146
147
0
   BOTAN_ARG_CHECK(sz >= minimum_final_size(),
148
0
                   "missing sufficient final input in XTS encrypt");
149
150
0
   const size_t BS = cipher_block_size();
151
152
0
   if(sz % BS == 0)
153
0
      {
154
0
      update(buffer, offset);
155
0
      }
156
0
   else
157
0
      {
158
      // steal ciphertext
159
0
      const size_t full_blocks = ((sz / BS) - 1) * BS;
160
0
      const size_t final_bytes = sz - full_blocks;
161
0
      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
162
163
0
      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
164
0
      buffer.resize(full_blocks + offset);
165
0
      update(buffer, offset);
166
167
0
      xor_buf(last, tweak(), BS);
168
0
      cipher().encrypt(last);
169
0
      xor_buf(last, tweak(), BS);
170
171
0
      for(size_t i = 0; i != final_bytes - BS; ++i)
172
0
         {
173
0
         last[i] ^= last[i + BS];
174
0
         last[i + BS] ^= last[i];
175
0
         last[i] ^= last[i + BS];
176
0
         }
177
178
0
      xor_buf(last, tweak() + BS, BS);
179
0
      cipher().encrypt(last);
180
0
      xor_buf(last, tweak() + BS, BS);
181
182
0
      buffer += last;
183
0
      }
184
0
   }
185
186
size_t XTS_Decryption::output_length(size_t input_length) const
187
0
   {
188
0
   return input_length;
189
0
   }
190
191
size_t XTS_Decryption::process(uint8_t buf[], size_t sz)
192
0
   {
193
0
   BOTAN_STATE_CHECK(tweak_set());
194
0
   const size_t BS = cipher_block_size();
195
196
0
   BOTAN_ARG_CHECK(sz % BS == 0, "Input is not full blocks");
197
0
   size_t blocks = sz / BS;
198
199
0
   const size_t blocks_in_tweak = tweak_blocks();
200
201
0
   while(blocks)
202
0
      {
203
0
      const size_t to_proc = std::min(blocks, blocks_in_tweak);
204
205
0
      cipher().decrypt_n_xex(buf, tweak(), to_proc);
206
207
0
      buf += to_proc * BS;
208
0
      blocks -= to_proc;
209
210
0
      update_tweak(to_proc);
211
0
      }
212
213
0
   return sz;
214
0
   }
215
216
void XTS_Decryption::finish(secure_vector<uint8_t>& buffer, size_t offset)
217
0
   {
218
0
   BOTAN_ARG_CHECK(buffer.size() >= offset, "Offset is out of range");
219
0
   const size_t sz = buffer.size() - offset;
220
0
   uint8_t* buf = buffer.data() + offset;
221
222
0
   BOTAN_ARG_CHECK(sz >= minimum_final_size(),
223
0
                   "missing sufficient final input in XTS decrypt");
224
225
0
   const size_t BS = cipher_block_size();
226
227
0
   if(sz % BS == 0)
228
0
      {
229
0
      update(buffer, offset);
230
0
      }
231
0
   else
232
0
      {
233
      // steal ciphertext
234
0
      const size_t full_blocks = ((sz / BS) - 1) * BS;
235
0
      const size_t final_bytes = sz - full_blocks;
236
0
      BOTAN_ASSERT(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
237
238
0
      secure_vector<uint8_t> last(buf + full_blocks, buf + full_blocks + final_bytes);
239
0
      buffer.resize(full_blocks + offset);
240
0
      update(buffer, offset);
241
242
0
      xor_buf(last, tweak() + BS, BS);
243
0
      cipher().decrypt(last);
244
0
      xor_buf(last, tweak() + BS, BS);
245
246
0
      for(size_t i = 0; i != final_bytes - BS; ++i)
247
0
         {
248
0
         last[i] ^= last[i + BS];
249
0
         last[i + BS] ^= last[i];
250
0
         last[i] ^= last[i + BS];
251
0
         }
252
253
0
      xor_buf(last, tweak(), BS);
254
0
      cipher().decrypt(last);
255
0
      xor_buf(last, tweak(), BS);
256
257
0
      buffer += last;
258
0
      }
259
0
   }
260
261
}