1 // Copyright 2022 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Implementation of the XTS-AES tweakable block cipher.
16 //!
17 //! See NIST docs [here](https://luca-giuzzi.unibs.it/corsi/Support/papers-cryptography/1619-2007-NIST-Submission.pdf)
18 //! and [here](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38e.pdf).
19 
20 #![no_std]
21 
22 #[cfg(feature = "std")]
23 extern crate std;
24 
25 use array_ref::{array_mut_ref, array_ref};
26 use core::fmt;
27 use core::marker::PhantomData;
28 
29 use crypto_provider::aes::{Aes, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher};
30 use crypto_provider::{
31     aes::{AesKey, BLOCK_SIZE},
32     CryptoProvider,
33 };
34 
35 use ldt_tbc::{
36     TweakableBlockCipher, TweakableBlockCipherDecrypter, TweakableBlockCipherEncrypter,
37     TweakableBlockCipherKey,
38 };
39 
40 #[cfg(test)]
41 mod tweak_tests;
42 
43 /// XTS-AES as per NIST docs
44 /// [here](https://luca-giuzzi.unibs.it/corsi/Support/papers-cryptography/1619-2007-NIST-Submission.pdf)
45 /// and
46 /// [here](https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38e.pdf).
47 ///
48 /// Data encrypted with XTS is divided into data units, each of which corresponds to one tweak
49 /// (assigned consecutively). If using a numeric tweak (`u128`), the tweak must be converted to
50 /// little endian bytes (e.g. with `u128::to_le_bytes()`).
51 ///
52 /// Inside each data unit, there are one or more 16-byte blocks. The length of a data unit SHOULD
53 /// not to exceed 2^20 blocks (16MB) in order to maintain security properties; see
54 /// [appendix D](https://luca-giuzzi.unibs.it/corsi/Support/papers-cryptography/1619-2007-NIST-Submission.pdf).
55 /// The last block (if there is more than one block) may have fewer than 16 bytes, in which case
56 /// ciphertext stealing will be applied.
57 ///
58 /// Each block in a data unit has its own block number, generated consecutively, incorporated into
59 /// the tweak used to encrypt that block.
60 ///
61 /// There is no support for partial bytes (bit lengths that aren't a multiple of 8).
62 pub struct XtsAes<A: Aes<Key = K::BlockCipherKey>, K: XtsKey + TweakableBlockCipherKey> {
63     _marker: PhantomData<A>,
64     _marker2: PhantomData<K>,
65 }
66 
67 impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey + TweakableBlockCipherKey>
68     TweakableBlockCipher<BLOCK_SIZE> for XtsAes<A, K>
69 {
70     type EncryptionCipher = XtsEncrypter<A, K>;
71     type DecryptionCipher = XtsDecrypter<A, K>;
72     type Tweak = Tweak;
73     type Key = K;
74 }
75 
76 /// The XtsAes128 implementation
77 pub type XtsAes128<C> = XtsAes<<C as CryptoProvider>::Aes128, XtsAes128Key>;
78 
79 /// The XtsAes256 implementation
80 pub type XtsAes256<C> = XtsAes<<C as CryptoProvider>::Aes256, XtsAes256Key>;
81 
82 /// Struct which provides Xts Aes Encrypt operations
83 #[repr(C)]
84 pub struct XtsEncrypter<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> {
85     main_encryption_cipher: A::EncryptCipher,
86     tweak_encryption_cipher: A::EncryptCipher,
87     _marker: PhantomData<K>,
88 }
89 
90 /// Struct which provides Xts Aes Decrypt operations
91 #[repr(C)]
92 pub struct XtsDecrypter<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> {
93     main_decryption_cipher: A::DecryptCipher,
94     tweak_encryption_cipher: A::EncryptCipher,
95     _marker: PhantomData<K>,
96 }
97 
98 #[allow(clippy::expect_used)]
99 impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> XtsEncrypter<A, K> {
100     /// Encrypt a data unit in place, using sequential block numbers for each block.
101     /// `data_unit` must be at least [BLOCK_SIZE] bytes, and fewer than
102     /// `BLOCK_SIZE * 2^20` bytes.
encrypt_data_unit(&self, tweak: Tweak, data_unit: &mut [u8]) -> Result<(), XtsError>103     pub fn encrypt_data_unit(&self, tweak: Tweak, data_unit: &mut [u8]) -> Result<(), XtsError> {
104         let (standalone_blocks, last_complete_block, partial_last_block) =
105             data_unit_parts(data_unit)?;
106 
107         let mut tweaked_xts = self.tweaked(tweak);
108 
109         standalone_blocks.chunks_mut(BLOCK_SIZE).for_each(|block| {
110             // Until array_chunks is stabilized, using a macro to get array refs out of slice chunks
111             // Won't panic because we are only processing complete blocks
112             #[allow(clippy::indexing_slicing)]
113             tweaked_xts.encrypt_block(array_mut_ref!(block, 0, BLOCK_SIZE));
114             tweaked_xts.advance_to_next_block_num();
115         });
116 
117         if partial_last_block.is_empty() {
118             #[allow(clippy::indexing_slicing)]
119             tweaked_xts.encrypt_block(array_mut_ref!(last_complete_block, 0, BLOCK_SIZE));
120             // nothing to do for the last block since it's empty
121         } else {
122             // This implements ciphertext stealing for the last two blocks which allows processing
123             // of a message length not divisible by block size without any expansion or padding.
124             // b is in bytes not bits; we do not consider partial bytes
125             let b = partial_last_block.len();
126 
127             // Per spec: CC = encrypt P_{m-1} (the last complete block)
128             let cc = {
129                 let mut last_complete_block_plaintext: AesBlock =
130                     last_complete_block.try_into().expect("complete block");
131                 tweaked_xts.encrypt_block(&mut last_complete_block_plaintext);
132                 tweaked_xts.advance_to_next_block_num();
133                 last_complete_block_plaintext
134             };
135 
136             // Copy b bytes of P_m before we overwrite them with C_m
137             let mut partial_last_block_plaintext: AesBlock = [0; BLOCK_SIZE];
138             partial_last_block_plaintext
139                 .get_mut(0..b)
140                 .expect("this should never be hit, since a partial block is always smaller than a complete block")
141                 .copy_from_slice(partial_last_block);
142 
143             // C_m = first b bytes of CC
144             partial_last_block.copy_from_slice(
145                 cc.get(0..b)
146                     .expect("b is the len of partial_last_block so it will always be in bounds"),
147             );
148 
149             // PP = P_m || last (16 - b) bytes of CC
150             let mut pp = {
151                 // the first b bytes have already been written as C_m, so it's safe to overwrite
152                 let mut cc = cc;
153                 cc.get_mut(0..b)
154                     .expect("partial block len should always be less than a complete block")
155                     .copy_from_slice(
156                         partial_last_block_plaintext
157                             .get(0..b)
158                             .expect("b is in range of block length"),
159                     );
160                 cc
161             };
162 
163             // C_{m-1} = encrypt PP
164             tweaked_xts.encrypt_block(&mut pp);
165             last_complete_block.copy_from_slice(&pp[..]);
166         }
167 
168         Ok(())
169     }
170 
171     /// Returns an [`XtsEncrypterTweaked`] configured with the specified tweak and a block number of 0.
tweaked(&self, tweak: Tweak) -> XtsEncrypterTweaked<A>172     fn tweaked(&self, tweak: Tweak) -> XtsEncrypterTweaked<A> {
173         let mut bytes = tweak.bytes;
174         self.tweak_encryption_cipher.encrypt(&mut bytes);
175 
176         XtsEncrypterTweaked {
177             tweak_state: TweakState::new(bytes),
178             enc_cipher: &self.main_encryption_cipher,
179         }
180     }
181 }
182 
183 #[allow(clippy::expect_used)]
184 impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey> XtsDecrypter<A, K> {
185     /// Decrypt a data unit in place, using sequential block numbers for each block.
186     /// `data_unit` must be at least [BLOCK_SIZE] bytes, and fewer than
187     /// `BLOCK_SIZE * 2^20` bytes.
decrypt_data_unit(&self, tweak: Tweak, data_unit: &mut [u8]) -> Result<(), XtsError>188     pub fn decrypt_data_unit(&self, tweak: Tweak, data_unit: &mut [u8]) -> Result<(), XtsError> {
189         let (standalone_blocks, last_complete_block, partial_last_block) =
190             data_unit_parts(data_unit)?;
191 
192         let mut tweaked_xts = self.tweaked(tweak);
193 
194         standalone_blocks.chunks_mut(BLOCK_SIZE).for_each(|block| {
195             #[allow(clippy::indexing_slicing)]
196             tweaked_xts.decrypt_block(array_mut_ref!(block, 0, BLOCK_SIZE));
197             tweaked_xts.advance_to_next_block_num();
198         });
199 
200         if partial_last_block.is_empty() {
201             #[allow(clippy::indexing_slicing)]
202             tweaked_xts.decrypt_block(array_mut_ref!(last_complete_block, 0, BLOCK_SIZE));
203         } else {
204             let b = partial_last_block.len();
205 
206             // tweak state is currently at m-1 block, so capture m-1 state for later use
207             let tweak_state_m_1 = tweaked_xts.current_tweak();
208 
209             // Per spec: PP = encrypt C_{m-1} (the last complete block) at block num m
210             let pp = {
211                 tweaked_xts.advance_to_next_block_num();
212                 // Block num is now at m
213                 // We need C_m later, so make a copy to avoid overwriting
214                 let mut last_complete_block_ciphertext: AesBlock =
215                     last_complete_block.try_into().expect("complete block");
216                 tweaked_xts.decrypt_block(&mut last_complete_block_ciphertext);
217                 tweaked_xts.set_tweak(tweak_state_m_1);
218                 last_complete_block_ciphertext
219             };
220 
221             // Copy b bytes of C_m before we overwrite them with P_m
222             let mut partial_last_block_ciphertext: AesBlock = [0; BLOCK_SIZE];
223             partial_last_block_ciphertext
224                 .get_mut(0..b)
225                 .expect("this should never be hit, since a partial block is always smaller than a complete block")
226                 .copy_from_slice(partial_last_block);
227 
228             // P_m = first b bytes of PP
229             partial_last_block.copy_from_slice(
230                 pp.get(0..b)
231                     .expect("b is the len of partial_last_block so it will always be in bounds"),
232             );
233 
234             // CC = C_m | CP (last 16-b bytes of PP)
235             let cc = {
236                 let cp = pp.get(b..).expect(
237                     "b is in bounds since a partial block is always smaller than a complete block",
238                 );
239                 last_complete_block
240                     .get_mut(0..b)
241                     .expect("partial block length within bounds of complete block")
242                     .copy_from_slice(
243                         partial_last_block_ciphertext
244                             .get(0..b)
245                             .expect("partial block length within bounds of complete block"),
246                     );
247                 last_complete_block
248                     .get_mut(b..)
249                     .expect("partial block length within bounds of complete block")
250                     .copy_from_slice(cp);
251                 last_complete_block
252             };
253 
254             // decrypting at block num = m -1
255             #[allow(clippy::indexing_slicing)]
256             tweaked_xts.decrypt_block(array_mut_ref!(cc, 0, BLOCK_SIZE));
257         }
258 
259         Ok(())
260     }
261 
262     /// Returns an [`XtsDecrypterTweaked`] configured with the specified tweak and a block number of 0.
tweaked(&self, tweak: Tweak) -> XtsDecrypterTweaked<A>263     fn tweaked(&self, tweak: Tweak) -> XtsDecrypterTweaked<A> {
264         let mut bytes = tweak.bytes;
265         self.tweak_encryption_cipher.encrypt(&mut bytes);
266 
267         XtsDecrypterTweaked {
268             tweak_state: TweakState::new(bytes),
269             dec_cipher: &self.main_decryption_cipher,
270         }
271     }
272 }
273 
274 type DataUnitPartsResult<'a> = Result<(&'a mut [u8], &'a mut [u8], &'a mut [u8]), XtsError>;
275 
276 /// Returns `(standalone blocks, last complete block, partial last block)`.
data_unit_parts(data_unit: &mut [u8]) -> DataUnitPartsResult277 fn data_unit_parts(data_unit: &mut [u8]) -> DataUnitPartsResult {
278     if data_unit.len() < BLOCK_SIZE {
279         return Err(XtsError::DataTooShort);
280     } else if data_unit.len() > MAX_XTS_SIZE {
281         return Err(XtsError::DataTooLong);
282     }
283     // complete_blocks >= 1
284     let complete_blocks = data_unit.len() / BLOCK_SIZE;
285     // standalone_units >= 0 blocks, suffix = last complete block + possible partial block.
286     let (standalone_blocks, suffix) = data_unit.split_at_mut((complete_blocks - 1) * BLOCK_SIZE);
287     let (last_complete_block, partial_last_block) = suffix.split_at_mut(BLOCK_SIZE);
288     Ok((standalone_blocks, last_complete_block, partial_last_block))
289 }
290 
291 impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey + TweakableBlockCipherKey>
292     TweakableBlockCipherEncrypter<BLOCK_SIZE> for XtsEncrypter<A, K>
293 {
294     type Key = K;
295     type Tweak = Tweak;
296 
297     /// Build an [XtsEncrypter] with the provided [Aes] and the provided key.
new(key: &Self::Key) -> Self298     fn new(key: &Self::Key) -> Self {
299         XtsEncrypter {
300             main_encryption_cipher: A::EncryptCipher::new(key.key_1()),
301             tweak_encryption_cipher: A::EncryptCipher::new(key.key_2()),
302             _marker: Default::default(),
303         }
304     }
305 
306     #[allow(clippy::expect_used)]
encrypt(&self, tweak: Self::Tweak, block: &mut [u8; BLOCK_SIZE])307     fn encrypt(&self, tweak: Self::Tweak, block: &mut [u8; BLOCK_SIZE]) {
308         // We're encrypting precisely one block, so the block number won't advance, and there is no
309         // need to apply ciphertext stealing
310         self.tweaked(tweak).encrypt_block(block)
311     }
312 }
313 
314 impl<A: Aes<Key = K::BlockCipherKey>, K: XtsKey + TweakableBlockCipherKey>
315     TweakableBlockCipherDecrypter<BLOCK_SIZE> for XtsDecrypter<A, K>
316 {
317     type Key = K;
318     type Tweak = Tweak;
319 
new(key: &K) -> Self320     fn new(key: &K) -> Self {
321         XtsDecrypter {
322             main_decryption_cipher: A::DecryptCipher::new(key.key_1()),
323             tweak_encryption_cipher: A::EncryptCipher::new(key.key_2()),
324             _marker: Default::default(),
325         }
326     }
327 
328     #[allow(clippy::expect_used)]
decrypt(&self, tweak: Self::Tweak, block: &mut [u8; BLOCK_SIZE])329     fn decrypt(&self, tweak: Self::Tweak, block: &mut [u8; BLOCK_SIZE]) {
330         // We don't need the ciphertext stealing here since the input size is always exactly one block
331         self.tweaked(tweak).decrypt_block(block)
332     }
333 }
334 
335 /// Errors that can occur during XTS encryption/decryption.
336 #[derive(Debug, PartialEq, Eq)]
337 pub enum XtsError {
338     /// The data is less than one AES block
339     DataTooShort,
340     /// The data is longer than 2^20 blocks, at which point XTS security degrades
341     DataTooLong,
342 }
343 
344 /// XTS spec recommends to not go beyond 2^20 blocks.
345 const MAX_XTS_SIZE: usize = (1 << 20) * BLOCK_SIZE;
346 
347 /// An XTS key comprised of two keys for the underlying block cipher.
348 pub trait XtsKey: for<'a> TryFrom<&'a [u8], Error = Self::TryFromError> {
349     /// The key used by the block cipher underlying XTS
350     type BlockCipherKey;
351     /// The error returned when `TryFrom<&[u8]>` fails.
352     type TryFromError: fmt::Debug;
353 
354     /// Returns the first of the two block cipher keys.
key_1(&self) -> &Self::BlockCipherKey355     fn key_1(&self) -> &Self::BlockCipherKey;
356     /// Returns the second of the two block cipher keys.
key_2(&self) -> &Self::BlockCipherKey357     fn key_2(&self) -> &Self::BlockCipherKey;
358 }
359 
360 const AES_128_KEY_SIZE: usize = 16;
361 
362 /// An XTS-AES-128 key.
363 pub struct XtsAes128Key {
364     key_1: <Self as XtsKey>::BlockCipherKey,
365     key_2: <Self as XtsKey>::BlockCipherKey,
366 }
367 
368 impl XtsKey for XtsAes128Key {
369     type BlockCipherKey = crypto_provider::aes::Aes128Key;
370     type TryFromError = XtsKeyTryFromSliceError;
371 
key_1(&self) -> &Self::BlockCipherKey372     fn key_1(&self) -> &Self::BlockCipherKey {
373         &self.key_1
374     }
375 
key_2(&self) -> &Self::BlockCipherKey376     fn key_2(&self) -> &Self::BlockCipherKey {
377         &self.key_2
378     }
379 }
380 
381 impl TryFrom<&[u8]> for XtsAes128Key {
382     type Error = XtsKeyTryFromSliceError;
383 
try_from(slice: &[u8]) -> Result<Self, Self::Error>384     fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
385         try_split_concat_key::<AES_128_KEY_SIZE>(slice)
386             .map(|(key_1, key_2)| Self { key_1: key_1.into(), key_2: key_2.into() })
387             .ok_or_else(XtsKeyTryFromSliceError::new)
388     }
389 }
390 
391 #[allow(clippy::expect_used)]
392 impl From<&[u8; 32]> for XtsAes128Key {
from(array: &[u8; 32]) -> Self393     fn from(array: &[u8; 32]) -> Self {
394         let arr1: [u8; AES_128_KEY_SIZE] =
395             array[..AES_128_KEY_SIZE].try_into().expect("array is correctly sized");
396         let arr2: [u8; AES_128_KEY_SIZE] =
397             array[AES_128_KEY_SIZE..].try_into().expect("array is correctly sized");
398         Self {
399             key_1: crypto_provider::aes::Aes128Key::from(arr1),
400             key_2: crypto_provider::aes::Aes128Key::from(arr2),
401         }
402     }
403 }
404 
405 impl TweakableBlockCipherKey for XtsAes128Key {
406     type ConcatenatedKeyArray = [u8; 64];
407 
408     // Allow index slicing, since a panic will be impossible to hit
409     #[allow(clippy::indexing_slicing)]
split_from_concatenated(key: &Self::ConcatenatedKeyArray) -> (Self, Self)410     fn split_from_concatenated(key: &Self::ConcatenatedKeyArray) -> (Self, Self) {
411         ((array_ref!(key, 0, 32)).into(), (array_ref!(key, 32, 32)).into())
412     }
413 
concatenate_with(&self, other: &Self) -> Self::ConcatenatedKeyArray414     fn concatenate_with(&self, other: &Self) -> Self::ConcatenatedKeyArray {
415         let mut out = [0; 64];
416         out[..16].copy_from_slice(self.key_1().as_slice());
417         out[16..32].copy_from_slice(self.key_2().as_slice());
418         out[32..48].copy_from_slice(other.key_1().as_slice());
419         out[48..].copy_from_slice(other.key_2().as_slice());
420 
421         out
422     }
423 }
424 
425 const AES_256_KEY_SIZE: usize = 32;
426 
427 /// An XTS-AES-256 key.
428 pub struct XtsAes256Key {
429     key_1: <Self as XtsKey>::BlockCipherKey,
430     key_2: <Self as XtsKey>::BlockCipherKey,
431 }
432 
433 impl XtsKey for XtsAes256Key {
434     type BlockCipherKey = crypto_provider::aes::Aes256Key;
435     type TryFromError = XtsKeyTryFromSliceError;
436 
key_1(&self) -> &Self::BlockCipherKey437     fn key_1(&self) -> &Self::BlockCipherKey {
438         &self.key_1
439     }
440 
key_2(&self) -> &Self::BlockCipherKey441     fn key_2(&self) -> &Self::BlockCipherKey {
442         &self.key_2
443     }
444 }
445 
446 impl TryFrom<&[u8]> for XtsAes256Key {
447     type Error = XtsKeyTryFromSliceError;
448 
try_from(slice: &[u8]) -> Result<Self, Self::Error>449     fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
450         try_split_concat_key::<AES_256_KEY_SIZE>(slice)
451             .map(|(key_1, key_2)| Self { key_1: key_1.into(), key_2: key_2.into() })
452             .ok_or_else(XtsKeyTryFromSliceError::new)
453     }
454 }
455 
456 #[allow(clippy::expect_used)]
457 impl From<&[u8; 64]> for XtsAes256Key {
from(array: &[u8; 64]) -> Self458     fn from(array: &[u8; 64]) -> Self {
459         let arr1: [u8; AES_256_KEY_SIZE] =
460             array[..AES_256_KEY_SIZE].try_into().expect("array is correctly sized");
461         let arr2: [u8; AES_256_KEY_SIZE] =
462             array[AES_256_KEY_SIZE..].try_into().expect("array is correctly sized");
463         Self {
464             key_1: crypto_provider::aes::Aes256Key::from(arr1),
465             key_2: crypto_provider::aes::Aes256Key::from(arr2),
466         }
467     }
468 }
469 
470 impl TweakableBlockCipherKey for XtsAes256Key {
471     type ConcatenatedKeyArray = [u8; 128];
472 
473     // Allow index slicing, since a panic will be impossible to hit
474     #[allow(clippy::indexing_slicing)]
split_from_concatenated(key: &Self::ConcatenatedKeyArray) -> (Self, Self)475     fn split_from_concatenated(key: &Self::ConcatenatedKeyArray) -> (Self, Self) {
476         ((array_ref!(key, 0, 64)).into(), (array_ref!(key, 64, 64)).into())
477     }
478 
concatenate_with(&self, other: &Self) -> Self::ConcatenatedKeyArray479     fn concatenate_with(&self, other: &Self) -> Self::ConcatenatedKeyArray {
480         let mut out = [0; 128];
481         out[..32].copy_from_slice(self.key_1().as_slice());
482         out[32..64].copy_from_slice(self.key_2().as_slice());
483         out[64..96].copy_from_slice(other.key_1().as_slice());
484         out[96..].copy_from_slice(other.key_2().as_slice());
485 
486         out
487     }
488 }
489 
490 /// The error returned when converting from a slice fails.
491 #[derive(Debug)]
492 pub struct XtsKeyTryFromSliceError {
493     _private: (),
494 }
495 
496 impl XtsKeyTryFromSliceError {
new() -> Self497     fn new() -> Self {
498         Self { _private: () }
499     }
500 }
501 
502 /// The tweak for an XTS-AES cipher.
503 #[derive(Clone)]
504 pub struct Tweak {
505     bytes: AesBlock,
506 }
507 
508 impl Tweak {
509     /// Little-endian content of the tweak.
le_bytes(&self) -> AesBlock510     pub fn le_bytes(&self) -> AesBlock {
511         self.bytes
512     }
513 }
514 
515 impl From<AesBlock> for Tweak {
from(bytes: AesBlock) -> Self516     fn from(bytes: AesBlock) -> Self {
517         Self { bytes }
518     }
519 }
520 
521 impl From<u128> for Tweak {
from(n: u128) -> Self522     fn from(n: u128) -> Self {
523         Self { bytes: n.to_le_bytes() }
524     }
525 }
526 
527 /// An XTS tweak advanced to a particular block num.
528 #[derive(Clone)]
529 pub(crate) struct TweakState {
530     /// The block number inside the data unit. Should not exceed 2^20.
531     block_num: u32,
532     /// Original tweak multiplied by the primitive polynomial `block_num` times as per section 5.2
533     tweak: AesBlock,
534 }
535 
536 impl TweakState {
537     /// Create a TweakState from the provided state with block_num = 0.
new(tweak: [u8; BLOCK_SIZE]) -> TweakState538     fn new(tweak: [u8; BLOCK_SIZE]) -> TweakState {
539         TweakState { block_num: 0, tweak }
540     }
541 
542     /// Advance the tweak state in the data unit to the next block without encrypting
543     /// or decrypting the intermediate blocks.
544     #[allow(clippy::indexing_slicing)]
advance_to_next_block(&mut self)545     fn advance_to_next_block(&mut self) {
546         // Conceptual left shift across the bytes.
547         // Most significant byte: if shift would carry, XOR in the coefficients of primitive
548         // polynomial in F_2^128 (x^128 = x^7 + x^2 + x + 1) =>  0b1000_0111 in binary.
549         let mut target = [0_u8; BLOCK_SIZE];
550         target[0] = (self.tweak[0] << 1) ^ ((self.tweak[BLOCK_SIZE - 1] >> 7) * 0b1000_0111);
551         for (j, byte) in target.iter_mut().enumerate().skip(1) {
552             *byte = (self.tweak[j] << 1) ^ (self.tweak[j - 1] >> 7);
553         }
554         self.tweak = target;
555         self.block_num += 1;
556     }
557 
558     /// Advance the tweak state in the data unit to the `block_num`'th block without encrypting
559     /// or decrypting the intermediate blocks.
560     ///
561     /// `block_num` should not exceed 2^20.
562     ///
563     /// # Panics
564     /// - If `block_num` is less than the current block num
565     #[cfg(test)]
advance_to_block(&mut self, block_num: u32)566     fn advance_to_block(&mut self, block_num: u32) {
567         // It's a programmer error; nothing to recover from
568         assert!(self.block_num <= block_num);
569 
570         // Multiply by the primitive polynomial as many times as needed, as per section 5.2
571         // of IEEE spec
572         #[allow(clippy::indexing_slicing)]
573         for _ in 0..(block_num - self.block_num) {
574             self.advance_to_next_block()
575         }
576     }
577 }
578 
579 /// An XTS-AES cipher configured with an initial tweak that can be advanced through the block
580 /// numbers for that tweak's data unit.
581 ///
582 /// Encryption or decryption is per-block only; ciphertext stealing is not implemented at this
583 /// level.
584 struct XtsEncrypterTweaked<'a, A: Aes> {
585     tweak_state: TweakState,
586     enc_cipher: &'a A::EncryptCipher,
587 }
588 
589 impl<'a, A: Aes> XtsEncrypterTweaked<'a, A> {
advance_to_next_block_num(&mut self)590     fn advance_to_next_block_num(&mut self) {
591         self.tweak_state.advance_to_next_block()
592     }
593 
594     /// Encrypt a block in place using the configured tweak and current block number.
encrypt_block(&self, block: &mut AesBlock)595     fn encrypt_block(&self, block: &mut AesBlock) {
596         array_xor(block, &self.tweak_state.tweak);
597         self.enc_cipher.encrypt(block);
598         array_xor(block, &self.tweak_state.tweak);
599     }
600 }
601 
602 /// An XTS-AES cipher configured with an initial tweak that can be advanced through the block
603 /// numbers for that tweak's data unit.
604 ///
605 /// Encryption or decryption is per-block only; ciphertext stealing is not implemented at this
606 /// level.
607 struct XtsDecrypterTweaked<'a, A: Aes> {
608     tweak_state: TweakState,
609     dec_cipher: &'a A::DecryptCipher,
610 }
611 
612 impl<'a, A: Aes> XtsDecrypterTweaked<'a, A> {
advance_to_next_block_num(&mut self)613     fn advance_to_next_block_num(&mut self) {
614         self.tweak_state.advance_to_next_block()
615     }
616 
617     /// Get the current tweak state -- useful if needed to reset to an earlier block num.
current_tweak(&self) -> TweakState618     fn current_tweak(&self) -> TweakState {
619         self.tweak_state.clone()
620     }
621 
622     /// Set the tweak to a state captured via [`current_tweak`](XtsDecrypterTweaked::current_tweak).
set_tweak(&mut self, tweak_state: TweakState)623     fn set_tweak(&mut self, tweak_state: TweakState) {
624         self.tweak_state = tweak_state;
625     }
decrypt_block(&self, block: &mut AesBlock)626     fn decrypt_block(&self, block: &mut AesBlock) {
627         // CC = C ^ T
628         array_xor(block, &self.tweak_state.tweak);
629         // PP = decrypt CC
630         self.dec_cipher.decrypt(block);
631         // P = PP ^ T
632         array_xor(block, &self.tweak_state.tweak);
633     }
634 }
635 
636 /// Calculate `base = base ^ rhs` for each byte.
637 #[allow(clippy::expect_used)]
array_xor(base: &mut AesBlock, rhs: &AesBlock)638 fn array_xor(base: &mut AesBlock, rhs: &AesBlock) {
639     // hopefully this gets done smartly by the compiler (intel pxor, arm veorq, or equivalent).
640     // This seems to happen in practice at opt level 3: https://gcc.godbolt.org/z/qvjE8joMv
641     for i in 0..BLOCK_SIZE {
642         *base.get_mut(i).expect("i is always a valid index for an AesBlock") ^=
643             rhs.get(i).expect("i is always a valid index for an AesBlock");
644     }
645 }
646 
try_split_concat_key<const N: usize>(slice: &[u8]) -> Option<([u8; N], [u8; N])>647 fn try_split_concat_key<const N: usize>(slice: &[u8]) -> Option<([u8; N], [u8; N])> {
648     slice.get(0..N).and_then(|slice| slice.try_into().ok()).and_then(|k1: [u8; N]| {
649         slice.get(N..).and_then(|slice| slice.try_into().ok()).map(|k2: [u8; N]| (k1, k2))
650     })
651 }
652