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