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