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