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