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 //! Provides an implementation of [LDT](https://eprint.iacr.org/2017/841.pdf).
16 
17 #![no_std]
18 
19 #[cfg(feature = "std")]
20 extern crate std;
21 
22 use core::{fmt, marker::PhantomData, ops};
23 use crypto_provider::CryptoProvider;
24 use ldt_tbc::{ConcatenatedKeyArray, TweakableBlockCipher, TweakableBlockCipherKey};
25 use ldt_tbc::{TweakableBlockCipherDecrypter, TweakableBlockCipherEncrypter};
26 
27 /// Common functionality for [LdtEncryptCipher] and [LdtDecryptCipher]
28 pub trait LdtCipher<const B: usize, T: TweakableBlockCipher<B>> {
29     /// The range of input lengths the cipher can operate on
30     const VALID_INPUT_LEN: ops::Range<usize>;
31 
32     /// Create a new cipher with the provided [TweakableBlockCipher] and [Mix] function
new(key: &LdtKey<T::Key>) -> Self33     fn new(key: &LdtKey<T::Key>) -> Self;
34 }
35 
36 /// Implementation of the [LDT](https://eprint.iacr.org/2017/841.pdf) length doubler encryption cipher.
37 ///
38 /// `B` is the block size.
39 /// `T` is the provided implementation of a Tweakable Block Cipher
40 /// `M` is the implementation of a [pure mix function](https://eprint.iacr.org/2017/841.pdf)
41 pub struct LdtEncryptCipher<const B: usize, T: TweakableBlockCipher<B>, M: Mix> {
42     cipher_1: T::EncryptionCipher,
43     cipher_2: T::EncryptionCipher,
44     // marker to use `M`
45     mix_phantom: PhantomData<M>,
46 }
47 
48 impl<const B: usize, T: TweakableBlockCipher<B>, M: Mix> LdtCipher<B, T>
49     for LdtEncryptCipher<B, T, M>
50 {
51     const VALID_INPUT_LEN: ops::Range<usize> = input_len_range::<B>();
52 
new(key: &LdtKey<T::Key>) -> Self53     fn new(key: &LdtKey<T::Key>) -> Self {
54         LdtEncryptCipher {
55             cipher_1: T::EncryptionCipher::new(&key.key_1),
56             cipher_2: T::EncryptionCipher::new(&key.key_2),
57             mix_phantom: PhantomData,
58         }
59     }
60 }
61 
62 impl<const B: usize, T: TweakableBlockCipher<B>, M: Mix> LdtEncryptCipher<B, T, M> {
63     /// Encrypt `data` in place, performing the pad operation with `padder`.
64     ///
65     /// Unless you have particular padding needs, use [DefaultPadder].
66     ///
67     /// # Errors
68     /// - if `data` has a length outside of `[B, B * 2)`.
encrypt<P: Padder<B, T>>(&self, data: &mut [u8], padder: &P) -> Result<(), LdtError>69     pub fn encrypt<P: Padder<B, T>>(&self, data: &mut [u8], padder: &P) -> Result<(), LdtError> {
70         do_ldt::<B, T, _, _, _, P>(
71             data,
72             |cipher, tweak, block| cipher.encrypt(tweak, block),
73             padder,
74             M::forwards,
75             &self.cipher_1,
76             &self.cipher_2,
77         )
78     }
79 }
80 
81 /// Implementation of the [LDT](https://eprint.iacr.org/2017/841.pdf) length doubler decryption cipher.
82 ///
83 /// `B` is the block size.
84 /// `T` is the provided implementation of a Tweakable Block Cipher
85 /// `M` is the implementation of a [pure mix function](https://eprint.iacr.org/2017/841.pdf)
86 #[repr(C)]
87 pub struct LdtDecryptCipher<const B: usize, T: TweakableBlockCipher<B>, M: Mix> {
88     cipher_1: T::DecryptionCipher,
89     cipher_2: T::DecryptionCipher,
90     // marker to use `M`
91     mix_phantom: PhantomData<M>,
92 }
93 
94 impl<const B: usize, T: TweakableBlockCipher<B>, M: Mix> LdtDecryptCipher<B, T, M> {
95     /// Create an [LdtDecryptCipher] with the provided Tweakable block cipher and Mix function
96 
97     /// Decrypt `data` in place, performing the pad operation with `padder`.
98     ///
99     /// Unless you have particular padding needs, use [DefaultPadder].
100     ///
101     /// # Errors
102     /// - if `data` has a length outside of `[B, B * 2)`.
decrypt<P: Padder<B, T>>(&self, data: &mut [u8], padder: &P) -> Result<(), LdtError>103     pub fn decrypt<P: Padder<B, T>>(&self, data: &mut [u8], padder: &P) -> Result<(), LdtError> {
104         do_ldt::<B, T, _, _, _, P>(
105             data,
106             |cipher, tweak, block| cipher.decrypt(tweak, block),
107             padder,
108             M::backwards,
109             // cipher order swapped for decryption
110             &self.cipher_2,
111             &self.cipher_1,
112         )
113     }
114 }
115 
116 impl<const B: usize, T: TweakableBlockCipher<B>, M: Mix> LdtCipher<B, T>
117     for LdtDecryptCipher<B, T, M>
118 {
119     const VALID_INPUT_LEN: ops::Range<usize> = input_len_range::<B>();
120 
new(key: &LdtKey<T::Key>) -> Self121     fn new(key: &LdtKey<T::Key>) -> Self {
122         LdtDecryptCipher {
123             cipher_1: T::DecryptionCipher::new(&key.key_1),
124             cipher_2: T::DecryptionCipher::new(&key.key_2),
125             mix_phantom: PhantomData,
126         }
127     }
128 }
129 
130 /// Returns the range of valid input lengths to encrypt or decrypt with LDT for a given tweakable
131 /// block cipher block size `B`, namely `[B, B * 2)`.
input_len_range<const B: usize>() -> ops::Range<usize>132 const fn input_len_range<const B: usize>() -> ops::Range<usize> {
133     B..B * 2
134 }
135 
136 // internal implementation of ldt cipher operations, re-used by encryption and decryption, by providing
137 // the corresponding cipher_op and mix operation
do_ldt<const B: usize, T, O, C, X, P>( data: &mut [u8], cipher_op: O, padder: &P, mix: X, first_cipher: &C, second_cipher: &C, ) -> Result<(), LdtError> where T: TweakableBlockCipher<B>, O: Fn(&C, T::Tweak, &mut [u8; B]), X: for<'a, 'b> Fn(&'a [u8], &'b [u8]) -> (&'b [u8], &'a [u8]), P: Padder<B, T>,138 fn do_ldt<const B: usize, T, O, C, X, P>(
139     data: &mut [u8],
140     cipher_op: O,
141     padder: &P,
142     mix: X,
143     first_cipher: &C,
144     second_cipher: &C,
145 ) -> Result<(), LdtError>
146 where
147     T: TweakableBlockCipher<B>,
148     // Encrypt or decrypt in place with a tweak
149     O: Fn(&C, T::Tweak, &mut [u8; B]),
150     // Mix a/b into block-sized chunks
151     X: for<'a, 'b> Fn(&'a [u8], &'b [u8]) -> (&'b [u8], &'a [u8]),
152     P: Padder<B, T>,
153 {
154     if !input_len_range::<B>().contains(&data.len()) {
155         return Err(LdtError::InvalidLength(data.len()));
156     }
157     let s = data.len() - B;
158     debug_assert!(s < B);
159 
160     // m1 length B, m2 length s (s < B)
161     let (m1, m2) = data.split_at(B);
162     debug_assert_eq!(s, m2.len());
163     let m1_ciphertext = {
164         let mut m1_plaintext = [0_u8; B];
165         // m1 is of length B, so no panic
166         m1_plaintext[..].copy_from_slice(m1);
167         let tweak = padder.pad_tweak(m2);
168         cipher_op(first_cipher, tweak, &mut m1_plaintext);
169         m1_plaintext
170     };
171     // |z| = B - s, |m3| = s
172     let (z, m3) = m1_ciphertext.split_at(B - s);
173     debug_assert_eq!(s, m3.len());
174 
175     // c3 and c2 are the last s bytes of their size-B arrays, respectively
176     let (c3, c2) = mix(m3, m2);
177 
178     let c1 = {
179         let mut z_c3 = [0; B];
180         z_c3[(B - s)..].copy_from_slice(c3);
181         z_c3[0..(B - s)].copy_from_slice(z);
182 
183         let tweak = padder.pad_tweak(c2);
184         cipher_op(second_cipher, tweak, &mut z_c3);
185         z_c3
186     };
187 
188     let (left, right) = data.split_at_mut(B);
189     left.copy_from_slice(&c1);
190     right.copy_from_slice(c2);
191     Ok(())
192 }
193 
194 /// Errors produced by LDT encryption/decryption.
195 #[derive(Debug, PartialEq, Eq)]
196 pub enum LdtError {
197     /// Data to encrypt/decrypt is the wrong length -- must be in `[B, 2 * B)` for block size `B`
198     /// of the underlying [TweakableBlockCipher].
199     InvalidLength(usize),
200 }
201 
202 impl fmt::Display for LdtError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result203     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204         match self {
205             LdtError::InvalidLength(len) => {
206                 write!(f, "Invalid length ({len}), must be in [block size, 2 * block size)")
207             }
208         }
209     }
210 }
211 
212 /// A key for an LDT cipher.
213 ///
214 /// `T` is the key type used for the underlying tweakable block cipher.
215 pub struct LdtKey<T: TweakableBlockCipherKey> {
216     key_1: T,
217     key_2: T,
218 }
219 
220 impl<T: TweakableBlockCipherKey> LdtKey<T> {
221     /// Build a key from two separate tweakable block cipher keys.
from_separate(key_1: T, key_2: T) -> Self222     pub fn from_separate(key_1: T, key_2: T) -> Self {
223         Self { key_1, key_2 }
224     }
225 
226     /// Build a key from an array representing two concatenated tweakable block cipher keys.
from_concatenated(key: &T::ConcatenatedKeyArray) -> Self227     pub fn from_concatenated(key: &T::ConcatenatedKeyArray) -> Self {
228         let (key_1, key_2) = T::split_from_concatenated(key);
229         Self::from_separate(key_1, key_2)
230     }
231 
232     /// Build a random key from a secure RNG.
from_random<C: CryptoProvider>(rng: &mut C::CryptoRng) -> Self233     pub fn from_random<C: CryptoProvider>(rng: &mut C::CryptoRng) -> Self {
234         Self::from_concatenated(&ConcatenatedKeyArray::from_random::<C>(rng))
235     }
236 
237     /// Returns the key material as a concatenated array with the contents of the two tweakable
238     /// block cipher keys.
as_concatenated(&self) -> T::ConcatenatedKeyArray239     pub fn as_concatenated(&self) -> T::ConcatenatedKeyArray {
240         self.key_1.concatenate_with(&self.key_2)
241     }
242 }
243 
244 /// A [pure mix function](https://eprint.iacr.org/2017/841.pdf).
245 pub trait Mix {
246     /// Mix `a` and `b`, writing into the last `s` bytes of the output arrays.
247     /// `a` and `b` must be the same length `s`, and no longer than the block size `B`.
248     /// Must be the inverse of [Mix::backwards].
forwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8])249     fn forwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8]);
250 
251     /// Mix `a` and `b`, writing into the last `s` bytes of the output arrays.
252     /// `a` and `b` must be the same length, and no longer than the block size `B`.
253     /// Must be the inverse of [Mix::forwards].
backwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8])254     fn backwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8]);
255 }
256 
257 /// Per section 2.4, swapping `a` and `b` is a valid mix function
258 pub struct Swap {}
259 
260 impl Mix for Swap {
forwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8])261     fn forwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8]) {
262         debug_assert_eq!(a.len(), b.len());
263         (b, a)
264     }
265 
backwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8])266     fn backwards<'a, 'b>(a: &'a [u8], b: &'b [u8]) -> (&'b [u8], &'a [u8]) {
267         // backwards is the same as forwards.
268         Self::forwards(a, b)
269     }
270 }
271 
272 /// Pads partial-block input into tweaks.
273 ///
274 /// This is exposed as a separate trait to allow for padding methods beyond the default.
275 pub trait Padder<const B: usize, T: TweakableBlockCipher<B>> {
276     /// Build a tweak for `T` out of `data`.
277     /// `data` must be shorter than the tweak size so that some padding can take place.
278     ///
279     /// # Panics
280     ///
281     /// Panics if the length of `data` >= the tweak size.
pad_tweak(&self, data: &[u8]) -> T::Tweak282     fn pad_tweak(&self, data: &[u8]) -> T::Tweak;
283 }
284 
285 /// The default padding algorithm per section 2 of [LDT paper](https://eprint.iacr.org/2017/841.pdf)
286 #[derive(Default)]
287 pub struct DefaultPadder;
288 
289 impl<const B: usize, T: TweakableBlockCipher<B>> Padder<B, T> for DefaultPadder {
pad_tweak(&self, data: &[u8]) -> T::Tweak290     fn pad_tweak(&self, data: &[u8]) -> T::Tweak {
291         Self::default_padding(data).into()
292     }
293 }
294 
295 impl DefaultPadder {
296     /// Expand `data` to an array of the appropriate size, and append a 1 bit after the original data.
297     ///
298     /// `T` is the tweak size to pad to in bytes.
299     ///
300     /// # Panics
301     ///
302     /// Panics if the length of the data to pad is >= the tweak size.
303     // allow index_slicing, since we are ensuring panic is impossible in assert
304     #[allow(clippy::indexing_slicing)]
default_padding<const N: usize>(data: &[u8]) -> [u8; N]305     fn default_padding<const N: usize>(data: &[u8]) -> [u8; N] {
306         // If this assert fails, our LDT impl is broken - always pads data < block size
307         assert!(data.len() < N);
308         let mut out = [0; N];
309         out[0..data.len()].copy_from_slice(data);
310         // 0b1 followed by zeros for all remaining bits.
311         // Since the array was initialized with 0, nothing left to do.
312         out[data.len()] = 128;
313         out
314     }
315 }
316 
317 /// Pads with the default algorithm to the tweak size, then XORs that with the provided bytes.
318 ///
319 /// This is useful as a means to perturb the cipher output without having to alter the input
320 /// directly.
321 ///
322 /// `T` is the tweak size to pad to in bytes
323 #[derive(Debug, PartialEq, Eq)]
324 pub struct XorPadder<const T: usize> {
325     xor_bytes: [u8; T],
326 }
327 
328 impl<const T: usize> From<[u8; T]> for XorPadder<T> {
from(bytes: [u8; T]) -> Self329     fn from(bytes: [u8; T]) -> Self {
330         XorPadder { xor_bytes: bytes }
331     }
332 }
333 
334 impl<const B: usize, T: TweakableBlockCipher<B>> Padder<B, T> for XorPadder<B> {
pad_tweak(&self, data: &[u8]) -> T::Tweak335     fn pad_tweak(&self, data: &[u8]) -> T::Tweak {
336         let mut out = DefaultPadder::default_padding::<B>(data);
337         debug_assert_eq!(self.xor_bytes.len(), out.len());
338 
339         // xor into the padded data
340         out.iter_mut()
341             .zip(self.xor_bytes.iter())
342             .for_each(|(out_byte, xor_byte): (&mut u8, &u8)| *out_byte ^= xor_byte);
343 
344         out.into()
345     }
346 }
347