Coverage Report

Created: 2021-10-13 08:49

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