1 /**
2 * XTS mode, from IEEE P1619
3 * 
4 * Copyright:
5 * (C) 2009,2013 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.modes.xts;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_MODE_XTS):
15 
16 import botan.modes.cipher_mode;
17 import botan.block.block_cipher;
18 import botan.modes.xts;
19 import botan.utils.loadstor;
20 import botan.utils.xor_buf;
21 import botan.utils.rounding;
22 import botan.utils.mem_ops;
23 import std.algorithm : min;
24 
25 /**
26 * IEEE P1619 XTS Mode
27 */
28 abstract class XTSMode : CipherMode, Transformation
29 {
30 public:
31     override @property string name() const
32     {
33         return cipher().name ~ "/XTS";
34     }
35 
36     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len)
37     {
38         if (!validNonceLength(nonce_len))
39             throw new InvalidIVLength(name, nonce_len);
40         
41         copyMem(m_tweak.ptr, nonce, nonce_len);
42         m_tweak_cipher.encrypt(m_tweak.ptr);
43         
44         updateTweak(0);
45         
46         return SecureVector!ubyte();
47     }
48 
49     override size_t updateGranularity() const
50     {
51         return cipher().parallelBytes();
52     }
53 
54     override size_t minimumFinalSize() const
55     {
56         return cipher().blockSize() + 1;
57     }
58 
59     override KeyLengthSpecification keySpec() const
60     {
61         return cipher().keySpec().multiple(2);
62     }
63 
64     override size_t defaultNonceLength() const
65     {
66         return cipher().blockSize();
67     }
68 
69     override bool validNonceLength(size_t n) const
70     {
71         return cipher().blockSize() == n;
72     }
73 
74     override void clear()
75     {
76         m_cipher.clear();
77         m_tweak_cipher.clear();
78         zeroise(m_tweak);
79     }
80 
81     override bool authenticated() const { return true; }
82 protected:
83     this(BlockCipher cipher) 
84     {
85         m_cipher = cipher;
86         if (m_cipher.blockSize() != 8 && m_cipher.blockSize() != 16)
87             throw new InvalidArgument("Bad cipher for XTS: " ~ m_cipher.name);
88         
89         m_tweak_cipher = m_cipher.clone();
90         m_tweak.resize(updateGranularity());
91     }
92 
93     final ubyte* tweak() const { return m_tweak.ptr; }
94 
95     final BlockCipher cipher() const { return cast()*m_cipher; }
96 
97     final void updateTweak(size_t which)
98     {
99         const size_t BS = m_tweak_cipher.blockSize();
100         
101         if (which > 0)
102             polyDouble(m_tweak.ptr, &m_tweak[(which-1)*BS], BS);
103         
104         const size_t blocks_in_tweak = updateGranularity() / BS;
105         
106         for (size_t i = 1; i < blocks_in_tweak; ++i)
107             polyDouble(&m_tweak[i*BS], &m_tweak[(i-1)*BS], BS);
108     }
109 
110     final override void keySchedule(const(ubyte)* key, size_t length)
111     {
112         const size_t key_half = length / 2;
113         
114         if (length % 2 == 1 || !m_cipher.validKeylength(key_half))
115             throw new InvalidKeyLength(name, length);
116         
117         m_cipher.setKey(key, key_half);
118         m_tweak_cipher.setKey(&key[key_half], key_half);
119     }
120 
121 private:
122     Unique!BlockCipher m_cipher, m_tweak_cipher;
123     SecureVector!ubyte m_tweak;
124 }
125 
126 /**
127 * IEEE P1619 XTS Encryption
128 */
129 final class XTSEncryption : XTSMode, Transformation
130 {
131 public:
132     this(BlockCipher cipher) 
133     {
134         super(cipher);
135     }
136 
137     override void update(ref SecureVector!ubyte buffer, size_t offset = 0)
138     {
139         assert(buffer.length >= offset, "Offset is sane");
140         const size_t sz = buffer.length - offset;
141         ubyte* buf = buffer.ptr + offset;
142         
143         const size_t BS = cipher().blockSize();
144         
145         assert(sz % BS == 0, "Input is full blocks");
146         size_t blocks = sz / BS;
147         
148         const size_t blocks_in_tweak = updateGranularity() / BS;
149         
150         while (blocks)
151         {
152             const size_t to_proc = min(blocks, blocks_in_tweak);
153             const size_t to_proc_bytes = to_proc * BS;
154             
155             xorBuf(buf, tweak(), to_proc_bytes);
156             cipher().encryptN(buf, buf, to_proc);
157             xorBuf(buf, tweak(), to_proc_bytes);
158             
159             buf += to_proc * BS;
160             blocks -= to_proc;
161             
162             updateTweak(to_proc);
163         }
164     }
165 
166     override void finish(ref SecureVector!ubyte buffer, size_t offset = 0)
167     {
168         assert(buffer.length >= offset, "Offset is sane");
169         const size_t sz = buffer.length - offset;
170         ubyte* buf = buffer.ptr + offset;
171         
172         assert(sz >= minimumFinalSize(), "Have sufficient final input");
173         
174         const size_t BS = cipher().blockSize();
175         
176         if (sz % BS == 0)
177         {
178             update(buffer, offset);
179         }
180         else
181         {
182             // steal ciphertext
183             const size_t full_blocks = ((sz / BS) - 1) * BS;
184             const size_t final_bytes = sz - full_blocks;
185             assert(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
186             
187             SecureVector!ubyte last = SecureVector!ubyte(buf[full_blocks .. full_blocks + final_bytes]);
188             buffer.resize(full_blocks + offset);
189             update(buffer, offset);
190             
191             xorBuf(last, tweak(), BS);
192             cipher().encrypt(last);
193             xorBuf(last, tweak(), BS);
194             
195             foreach (size_t i; 0 .. (final_bytes - BS))
196             {
197                 last[i] ^= last[i + BS];
198                 last[i + BS] ^= last[i];
199                 last[i] ^= last[i + BS];
200             }
201             
202             xorBuf(last, tweak() + BS, BS);
203             cipher().encrypt(last);
204             xorBuf(last, tweak() + BS, BS);
205             
206             buffer ~= last;
207         }
208     }
209 
210     override size_t outputLength(size_t input_length) const
211     {
212         return roundUp(input_length, cipher().blockSize());
213     }
214 
215     // Interface fallthrough
216     override string provider() const { return "core"; }
217     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); }
218     override size_t updateGranularity() const { return super.updateGranularity(); }
219     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
220     override @property string name() const { return super.name; }
221     override void clear() { return super.clear(); }
222     override size_t minimumFinalSize() const { return super.minimumFinalSize(); }
223     override bool validNonceLength(size_t n) const {
224         return super.validNonceLength(n);
225     }
226 }
227 
228 /**
229 * IEEE P1619 XTS Decryption
230 */
231 final class XTSDecryption : XTSMode, Transformation
232 {
233 public:
234     this(BlockCipher cipher)
235     {
236         super(cipher);
237     }
238 
239     override void update(ref SecureVector!ubyte buffer, size_t offset = 0)
240     {
241         assert(buffer.length >= offset, "Offset is sane");
242         const size_t sz = buffer.length - offset;
243         ubyte* buf = buffer.ptr + offset;
244         
245         const size_t BS = cipher().blockSize();
246         
247         assert(sz % BS == 0, "Input is full blocks");
248         size_t blocks = sz / BS;
249         
250         const size_t blocks_in_tweak = updateGranularity() / BS;
251         
252         while (blocks)
253         {
254             const size_t to_proc = min(blocks, blocks_in_tweak);
255             const size_t to_proc_bytes = to_proc * BS;
256             
257             xorBuf(buf, tweak(), to_proc_bytes);
258             cipher().decryptN(buf, buf, to_proc);
259             xorBuf(buf, tweak(), to_proc_bytes);
260             
261             buf += to_proc * BS;
262             blocks -= to_proc;
263             
264             updateTweak(to_proc);
265         }
266     }
267 
268     override void finish(ref SecureVector!ubyte buffer, size_t offset = 0)
269     {
270         assert(buffer.length >= offset, "Offset is sane");
271         const size_t sz = buffer.length - offset;
272         ubyte* buf = buffer.ptr + offset;
273         
274         assert(sz >= minimumFinalSize(), "Have sufficient final input");
275         
276         const size_t BS = cipher().blockSize();
277         
278         if (sz % BS == 0)
279         {
280             update(buffer, offset);
281         }
282         else
283         {
284             // steal ciphertext
285             const size_t full_blocks = ((sz / BS) - 1) * BS;
286             const size_t final_bytes = sz - full_blocks;
287             assert(final_bytes > BS && final_bytes < 2*BS, "Left over size in expected range");
288             
289             SecureVector!ubyte last = SecureVector!ubyte(buf[full_blocks .. full_blocks + final_bytes]);
290             buffer.resize(full_blocks + offset);
291             update(buffer, offset);
292             
293             xorBuf(last, tweak() + BS, BS);
294             cipher().decrypt(last);
295             xorBuf(last, tweak() + BS, BS);
296             
297             foreach (size_t i; 0 .. (final_bytes - BS))
298             {
299                 last[i] ^= last[i + BS];
300                 last[i + BS] ^= last[i];
301                 last[i] ^= last[i + BS];
302             }
303             
304             xorBuf(last, tweak(), BS);
305             cipher().decrypt(last);
306             xorBuf(last, tweak(), BS);
307             
308             buffer ~= last;
309         }
310     }
311 
312     override size_t outputLength(size_t input_length) const
313     {
314         // might be less
315         return input_length;
316     }
317 
318     // Interface fallthrough
319     override string provider() const { return "core"; }
320     override SecureVector!ubyte startRaw(const(ubyte)* nonce, size_t nonce_len) { return super.startRaw(nonce, nonce_len); }
321     override size_t updateGranularity() const { return super.updateGranularity(); }
322     override size_t defaultNonceLength() const { return super.defaultNonceLength(); }
323     override @property string name() const { return super.name; }
324     override void clear() { return super.clear(); }
325     override size_t minimumFinalSize() const { return super.minimumFinalSize(); }
326     override bool validNonceLength(size_t n) const {
327         return super.validNonceLength(n);
328     }
329 }
330 
331 
332 private:
333 
334 void polyDouble128(ubyte* output, const(ubyte)* input)
335 {
336     ulong X0 = loadLittleEndian!ulong(input, 0);
337     ulong X1 = loadLittleEndian!ulong(input, 1);
338     
339     const bool carry = (X1 >> 63);
340     
341     X1 = (X1 << 1) | (X0 >> 63);
342     X0 = (X0 << 1);
343     
344     if (carry)
345         X0 ^= 0x87;
346     
347     storeLittleEndian(output, X0, X1);
348 }
349 
350 void polyDouble64(ubyte* output, const(ubyte)* input)
351 {
352     ulong X = loadLittleEndian!ulong(input, 0);
353     const bool carry = (X >> 63);
354     X <<= 1;
355     if (carry)
356         X ^= 0x1B;
357     storeLittleEndian(X, output);
358 }
359 
360 void polyDouble(ubyte* output, const(ubyte)* input, size_t size)
361 {
362     if (size == 8)
363         polyDouble64(output, input);
364     else
365         polyDouble128(output, input);
366 }