ocb3/
lib.rs

1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
6    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
7)]
8#![deny(unsafe_code)]
9#![warn(missing_docs, rust_2018_idioms)]
10
11/// Constants used, reexported for convenience.
12pub mod consts {
13    pub use cipher::consts::{U0, U12, U15, U16, U6};
14}
15
16mod util;
17
18pub use aead::{
19    self, generic_array::GenericArray, AeadCore, AeadInPlace, Error, KeyInit, KeySizeUser,
20};
21
22use crate::util::{double, inplace_xor, ntz, Block};
23use cipher::{
24    consts::{U0, U12, U16},
25    BlockDecrypt, BlockEncrypt, BlockSizeUser,
26};
27use core::marker::PhantomData;
28use subtle::ConstantTimeEq;
29
30/// Number of L values to be precomputed. Precomputing m values, allows
31/// processing inputs of length up to 2^m blocks (2^m * 16 bytes) without
32/// needing to calculate L values at runtime.
33///
34/// By setting this to 32, we can process inputs of length up to 1 terabyte.
35#[cfg(target_pointer_width = "64")]
36const L_TABLE_SIZE: usize = 32;
37
38/// Number of L values to be precomputed. Precomputing m values, allows
39/// processing inputs of length up to 2^m blocks (2^m * 16 bytes) without
40/// needing to calculate L values at runtime.
41#[cfg(target_pointer_width = "32")]
42const L_TABLE_SIZE: usize = 16;
43
44/// Max associated data.
45pub const A_MAX: usize = 1 << (L_TABLE_SIZE + 4);
46/// Max plaintext.
47pub const P_MAX: usize = 1 << (L_TABLE_SIZE + 4);
48/// Max ciphertext.
49pub const C_MAX: usize = 1 << (L_TABLE_SIZE + 4);
50
51/// OCB3 nonce
52pub type Nonce<NonceSize> = GenericArray<u8, NonceSize>;
53
54/// OCB3 tag
55pub type Tag<TagSize> = GenericArray<u8, TagSize>;
56
57mod sealed {
58    use aead::generic_array::{
59        typenum::{GrEq, IsGreaterOrEqual, IsLessOrEqual, LeEq, NonZero, U15, U16, U6},
60        ArrayLength,
61    };
62
63    /// Sealed trait for nonce sizes in the range of `6..=15` bytes.
64    pub trait NonceSizes: ArrayLength<u8> {}
65
66    impl<T> NonceSizes for T
67    where
68        T: ArrayLength<u8> + IsGreaterOrEqual<U6> + IsLessOrEqual<U15>,
69        GrEq<T, U6>: NonZero,
70        LeEq<T, U15>: NonZero,
71    {
72    }
73
74    /// Sealed trait for tag sizes in the range of `1..=16` bytes.
75    pub trait TagSizes: ArrayLength<u8> {}
76
77    impl<T> TagSizes for T
78    where
79        T: ArrayLength<u8> + NonZero + IsLessOrEqual<U16>,
80        LeEq<T, U16>: NonZero,
81    {
82    }
83}
84
85/// OCB3: generic over a block cipher implementation, nonce size, and tag size.
86///
87/// - `NonceSize`: max of 15-bytes, default and recommended size of 12-bytes (96-bits).
88///   We further restrict the minimum nonce size to 6-bytes to prevent an attack described in
89///   the following paper: <https://eprint.iacr.org/2023/326.pdf>.
90/// - `TagSize`: non-zero, max of 16-bytes, default and recommended size of 16-bytes.
91///
92/// Compilation will fail if the size conditions are not satisfied:
93///
94/// ```rust,compile_fail
95/// # use aes::Aes128;
96/// # use ocb3::{aead::{consts::U5, KeyInit}, Ocb3};
97/// # let key = [42; 16].into();
98/// // Invalid nonce size equal to 5 bytes
99/// let cipher = Ocb3::<Aes128, U5>::new(&key);
100/// ```
101///
102/// ```rust,compile_fail
103/// # use aes::Aes128;
104/// # use ocb3::{aead::{consts::U16, KeyInit}, Ocb3};
105/// # let key = [42; 16].into();
106/// // Invalid nonce size equal to 16 bytes
107/// let cipher = Ocb3::<Aes128, U16>::new(&key);
108/// ```
109///
110/// ```rust,compile_fail
111/// # use aes::Aes128;
112/// # use ocb3::{aead::{consts::{U12, U0}, KeyInit}, Ocb3};
113/// # let key = [42; 16].into();
114/// // Invalid tag size equal to 0 bytes
115/// let cipher = Ocb3::<Aes128, U12, U0>::new(&key);
116/// ```
117///
118/// ```rust,compile_fail
119/// # use aes::Aes128;
120/// # use ocb3::{aead::{consts::{U12, U20}, KeyInit}, Ocb3};
121/// # let key = [42; 16].into();
122/// // Invalid tag size equal to 20 bytes
123/// let cipher = Ocb3::<Aes128, U12, U20>::new(&key);
124/// ```
125#[derive(Clone)]
126pub struct Ocb3<Cipher, NonceSize = U12, TagSize = U16>
127where
128    NonceSize: sealed::NonceSizes,
129    TagSize: sealed::TagSizes,
130{
131    cipher: Cipher,
132    nonce_size: PhantomData<NonceSize>,
133    tag_size: PhantomData<TagSize>,
134    // precomputed key-dependent variables
135    ll_star: Block,
136    ll_dollar: Block,
137    // list of pre-computed L values
138    ll: [Block; L_TABLE_SIZE],
139}
140
141/// Output of the HASH function defined in https://www.rfc-editor.org/rfc/rfc7253.html#section-4.1
142type SumSize = U16;
143type Sum = GenericArray<u8, SumSize>;
144
145impl<Cipher, NonceSize, TagSize> KeySizeUser for Ocb3<Cipher, NonceSize, TagSize>
146where
147    Cipher: KeySizeUser,
148    NonceSize: sealed::NonceSizes,
149    TagSize: sealed::TagSizes,
150{
151    type KeySize = Cipher::KeySize;
152}
153
154impl<Cipher, NonceSize, TagSize> KeyInit for Ocb3<Cipher, NonceSize, TagSize>
155where
156    Cipher: BlockSizeUser<BlockSize = U16> + BlockEncrypt + KeyInit + BlockDecrypt,
157    NonceSize: sealed::NonceSizes,
158    TagSize: sealed::TagSizes,
159{
160    fn new(key: &aead::Key<Self>) -> Self {
161        Cipher::new(key).into()
162    }
163}
164
165impl<Cipher, NonceSize, TagSize> AeadCore for Ocb3<Cipher, NonceSize, TagSize>
166where
167    NonceSize: sealed::NonceSizes,
168    TagSize: sealed::TagSizes,
169{
170    type NonceSize = NonceSize;
171    type TagSize = TagSize;
172    type CiphertextOverhead = U0;
173}
174
175impl<Cipher, NonceSize, TagSize> From<Cipher> for Ocb3<Cipher, NonceSize, TagSize>
176where
177    Cipher: BlockSizeUser<BlockSize = U16> + BlockEncrypt + BlockDecrypt,
178    NonceSize: sealed::NonceSizes,
179    TagSize: sealed::TagSizes,
180{
181    fn from(cipher: Cipher) -> Self {
182        let (ll_star, ll_dollar, ll) = key_dependent_variables(&cipher);
183
184        Self {
185            cipher,
186            nonce_size: PhantomData,
187            tag_size: PhantomData,
188            ll_star,
189            ll_dollar,
190            ll,
191        }
192    }
193}
194
195/// Computes key-dependent variables defined in
196/// https://www.rfc-editor.org/rfc/rfc7253.html#section-4.1
197fn key_dependent_variables<Cipher: BlockSizeUser<BlockSize = U16> + BlockEncrypt>(
198    cipher: &Cipher,
199) -> (Block, Block, [Block; L_TABLE_SIZE]) {
200    let mut zeros = [0u8; 16];
201    let ll_star = Block::from_mut_slice(&mut zeros);
202    cipher.encrypt_block(ll_star);
203    let ll_dollar = double(ll_star);
204
205    let mut ll = [Block::default(); L_TABLE_SIZE];
206    let mut ll_i = ll_dollar;
207    #[allow(clippy::needless_range_loop)]
208    for i in 0..L_TABLE_SIZE {
209        ll_i = double(&ll_i);
210        ll[i] = ll_i
211    }
212    (*ll_star, ll_dollar, ll)
213}
214
215impl<Cipher, NonceSize, TagSize> AeadInPlace for Ocb3<Cipher, NonceSize, TagSize>
216where
217    Cipher: BlockSizeUser<BlockSize = U16> + BlockEncrypt + BlockDecrypt,
218    NonceSize: sealed::NonceSizes,
219    TagSize: sealed::TagSizes,
220{
221    fn encrypt_in_place_detached(
222        &self,
223        nonce: &Nonce<NonceSize>,
224        associated_data: &[u8],
225        buffer: &mut [u8],
226    ) -> aead::Result<aead::Tag<Self>> {
227        if (buffer.len() > P_MAX) || (associated_data.len() > A_MAX) {
228            unimplemented!()
229        }
230
231        // First, try to process many blocks at once.
232        let (processed_bytes, mut offset_i, mut checksum_i) = self.wide_encrypt(nonce, buffer);
233
234        let mut i = (processed_bytes / 16) + 1;
235
236        // Then, process the remaining blocks.
237        for p_i in buffer[processed_bytes..].chunks_exact_mut(16) {
238            let p_i = Block::from_mut_slice(p_i);
239            // offset_i = offset_{i-1} xor L_{ntz(i)}
240            inplace_xor(&mut offset_i, &self.ll[ntz(i)]);
241            // checksum_i = checksum_{i-1} xor p_i
242            inplace_xor(&mut checksum_i, p_i);
243            // c_i = offset_i xor ENCIPHER(K, p_i xor offset_i)
244            let c_i = p_i;
245            inplace_xor(c_i, &offset_i);
246            self.cipher.encrypt_block(c_i);
247            inplace_xor(c_i, &offset_i);
248
249            i += 1;
250        }
251
252        // Process any partial blocks.
253        if (buffer.len() % 16) != 0 {
254            let processed_bytes = (i - 1) * 16;
255            let remaining_bytes = buffer.len() - processed_bytes;
256
257            // offset_* = offset_m xor L_*
258            inplace_xor(&mut offset_i, &self.ll_star);
259            // Pad = ENCIPHER(K, offset_*)
260            let mut pad = Block::default();
261            inplace_xor(&mut pad, &offset_i);
262            self.cipher.encrypt_block(&mut pad);
263            // checksum_* = checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*)))
264            let checksum_rhs = &mut [0u8; 16];
265            checksum_rhs[..remaining_bytes].copy_from_slice(&buffer[processed_bytes..]);
266            checksum_rhs[remaining_bytes] = 0b1000_0000;
267            inplace_xor(&mut checksum_i, Block::from_slice(checksum_rhs));
268            // C_* = P_* xor Pad[1..bitlen(P_*)]
269            let p_star = &mut buffer[processed_bytes..];
270            let pad = &mut pad[..p_star.len()];
271            for (aa, bb) in p_star.iter_mut().zip(pad) {
272                *aa ^= *bb;
273            }
274        }
275
276        let tag = self.compute_tag(associated_data, &mut checksum_i, &offset_i);
277
278        Ok(tag)
279    }
280
281    fn decrypt_in_place_detached(
282        &self,
283        nonce: &Nonce<NonceSize>,
284        associated_data: &[u8],
285        buffer: &mut [u8],
286        tag: &aead::Tag<Self>,
287    ) -> aead::Result<()> {
288        let expected_tag = self.decrypt_in_place_return_tag(nonce, associated_data, buffer);
289        if expected_tag.ct_eq(tag).into() {
290            Ok(())
291        } else {
292            Err(Error)
293        }
294    }
295}
296
297impl<Cipher, NonceSize, TagSize> Ocb3<Cipher, NonceSize, TagSize>
298where
299    Cipher: BlockSizeUser<BlockSize = U16> + BlockEncrypt + BlockDecrypt,
300    NonceSize: sealed::NonceSizes,
301    TagSize: sealed::TagSizes,
302{
303    /// Decrypts in place and returns expected tag.
304    pub(crate) fn decrypt_in_place_return_tag(
305        &self,
306        nonce: &Nonce<NonceSize>,
307        associated_data: &[u8],
308        buffer: &mut [u8],
309    ) -> aead::Tag<Self> {
310        if (buffer.len() > C_MAX) || (associated_data.len() > A_MAX) {
311            unimplemented!()
312        }
313
314        // First, try to process many blocks at once.
315        let (processed_bytes, mut offset_i, mut checksum_i) = self.wide_decrypt(nonce, buffer);
316
317        let mut i = (processed_bytes / 16) + 1;
318
319        // Then, process the remaining blocks.
320        for c_i in buffer[processed_bytes..].chunks_exact_mut(16) {
321            let c_i = Block::from_mut_slice(c_i);
322            // offset_i = offset_{i-1} xor L_{ntz(i)}
323            inplace_xor(&mut offset_i, &self.ll[ntz(i)]);
324            // p_i = offset_i xor DECIPHER(K, c_i xor offset_i)
325            let p_i = c_i;
326            inplace_xor(p_i, &offset_i);
327            self.cipher.decrypt_block(p_i);
328            inplace_xor(p_i, &offset_i);
329            // checksum_i = checksum_{i-1} xor p_i
330            inplace_xor(&mut checksum_i, p_i);
331
332            i += 1;
333        }
334
335        // Process any partial blocks.
336        if (buffer.len() % 16) != 0 {
337            let processed_bytes = (i - 1) * 16;
338            let remaining_bytes = buffer.len() - processed_bytes;
339
340            // offset_* = offset_m xor L_*
341            inplace_xor(&mut offset_i, &self.ll_star);
342            // Pad = ENCIPHER(K, offset_*)
343            let mut pad = Block::default();
344            inplace_xor(&mut pad, &offset_i);
345            self.cipher.encrypt_block(&mut pad);
346            // P_* = C_* xor Pad[1..bitlen(C_*)]
347            let c_star = &mut buffer[processed_bytes..];
348            let pad = &mut pad[..c_star.len()];
349            for (aa, bb) in c_star.iter_mut().zip(pad) {
350                *aa ^= *bb;
351            }
352            // checksum_* = checksum_m xor (P_* || 1 || zeros(127-bitlen(P_*)))
353            let checksum_rhs = &mut [0u8; 16];
354            checksum_rhs[..remaining_bytes].copy_from_slice(&buffer[processed_bytes..]);
355            checksum_rhs[remaining_bytes] = 0b1000_0000;
356            inplace_xor(&mut checksum_i, Block::from_slice(checksum_rhs));
357        }
358
359        self.compute_tag(associated_data, &mut checksum_i, &offset_i)
360    }
361
362    /// Encrypts plaintext in groups of two.
363    ///
364    /// Adapted from https://www.cs.ucdavis.edu/~rogaway/ocb/news/code/ocb.c
365    fn wide_encrypt(&self, nonce: &Nonce<NonceSize>, buffer: &mut [u8]) -> (usize, Block, Block) {
366        const WIDTH: usize = 2;
367        let split_into_blocks = crate::util::split_into_two_blocks;
368
369        let mut i = 1;
370
371        let mut offset_i = [Block::default(); WIDTH];
372        offset_i[offset_i.len() - 1] = initial_offset(&self.cipher, nonce, TagSize::to_u32());
373        let mut checksum_i = Block::default();
374        for wide_blocks in buffer.chunks_exact_mut(16 * WIDTH) {
375            let p_i = split_into_blocks(wide_blocks);
376
377            // checksum_i = checksum_{i-1} xor p_i
378            for p_ij in &p_i {
379                inplace_xor(&mut checksum_i, p_ij);
380            }
381
382            // offset_i = offset_{i-1} xor L_{ntz(i)}
383            offset_i[0] = offset_i[offset_i.len() - 1];
384            inplace_xor(&mut offset_i[0], &self.ll[ntz(i)]);
385            for j in 1..p_i.len() {
386                offset_i[j] = offset_i[j - 1];
387                inplace_xor(&mut offset_i[j], &self.ll[ntz(i + j)]);
388            }
389
390            // c_i = offset_i xor ENCIPHER(K, p_i xor offset_i)
391            for j in 0..p_i.len() {
392                inplace_xor(p_i[j], &offset_i[j]);
393                self.cipher.encrypt_block(p_i[j]);
394                inplace_xor(p_i[j], &offset_i[j])
395            }
396
397            i += WIDTH;
398        }
399
400        let processed_bytes = (buffer.len() / (WIDTH * 16)) * (WIDTH * 16);
401
402        (processed_bytes, offset_i[offset_i.len() - 1], checksum_i)
403    }
404
405    /// Decrypts plaintext in groups of two.
406    ///
407    /// Adapted from https://www.cs.ucdavis.edu/~rogaway/ocb/news/code/ocb.c
408    fn wide_decrypt(&self, nonce: &Nonce<NonceSize>, buffer: &mut [u8]) -> (usize, Block, Block) {
409        const WIDTH: usize = 2;
410        let split_into_blocks = crate::util::split_into_two_blocks;
411
412        let mut i = 1;
413
414        let mut offset_i = [Block::default(); WIDTH];
415        offset_i[offset_i.len() - 1] = initial_offset(&self.cipher, nonce, TagSize::to_u32());
416        let mut checksum_i = Block::default();
417        for wide_blocks in buffer.chunks_exact_mut(16 * WIDTH) {
418            let c_i = split_into_blocks(wide_blocks);
419
420            // offset_i = offset_{i-1} xor L_{ntz(i)}
421            offset_i[0] = offset_i[offset_i.len() - 1];
422            inplace_xor(&mut offset_i[0], &self.ll[ntz(i)]);
423            for j in 1..c_i.len() {
424                offset_i[j] = offset_i[j - 1];
425                inplace_xor(&mut offset_i[j], &self.ll[ntz(i + j)]);
426            }
427
428            // p_i = offset_i xor DECIPHER(K, c_i xor offset_i)
429            // checksum_i = checksum_{i-1} xor p_i
430            for j in 0..c_i.len() {
431                inplace_xor(c_i[j], &offset_i[j]);
432                self.cipher.decrypt_block(c_i[j]);
433                inplace_xor(c_i[j], &offset_i[j]);
434                inplace_xor(&mut checksum_i, c_i[j]);
435            }
436
437            i += WIDTH;
438        }
439
440        let processed_bytes = (buffer.len() / (WIDTH * 16)) * (WIDTH * 16);
441
442        (processed_bytes, offset_i[offset_i.len() - 1], checksum_i)
443    }
444}
445
446/// Computes nonce-dependent variables as defined
447/// in https://www.rfc-editor.org/rfc/rfc7253.html#section-4.2
448fn nonce_dependent_variables<
449    Cipher: BlockSizeUser<BlockSize = U16> + BlockEncrypt,
450    NonceSize: sealed::NonceSizes,
451>(
452    cipher: &Cipher,
453    nn: &Nonce<NonceSize>,
454    tag_len: u32,
455) -> (usize, [u8; 24]) {
456    // Nonce = num2str(TAGLEN mod 128,7) || zeros(120-bitlen(N)) || 1 || N
457    let mut nonce = [0u8; 16];
458    nonce[0] = (((tag_len * 8) % 128) << 1) as u8;
459
460    let start = 16 - NonceSize::to_usize();
461    nonce[start..16].copy_from_slice(nn.as_slice());
462    nonce[16 - NonceSize::to_usize() - 1] |= 1;
463
464    // Separate the last 6 bits into `bottom`, and the rest into `top`.
465    let bottom = nonce[15] & 0b111111;
466
467    let nonce = u128::from_be_bytes(nonce);
468    let top = nonce & !0b111111;
469
470    let mut ktop = Block::from(top.to_be_bytes());
471    cipher.encrypt_block(&mut ktop);
472    let ktop = ktop.as_mut_slice();
473
474    // stretch = Ktop || (Ktop[1..64] xor Ktop[9..72])
475    let mut stretch = [0u8; 24];
476    stretch[..16].copy_from_slice(ktop);
477    for i in 0..8 {
478        ktop[i] ^= ktop[i + 1];
479    }
480    stretch[16..].copy_from_slice(&ktop[..8]);
481
482    (bottom as usize, stretch)
483}
484
485/// Computes the initial offset as defined
486/// in https://www.rfc-editor.org/rfc/rfc7253.html#section-4.2
487fn initial_offset<
488    Cipher: BlockSizeUser<BlockSize = U16> + BlockEncrypt,
489    NonceSize: sealed::NonceSizes,
490>(
491    cipher: &Cipher,
492    nn: &Nonce<NonceSize>,
493    tag_size: u32,
494) -> Block {
495    let (bottom, stretch) = nonce_dependent_variables(cipher, nn, tag_size);
496    let stretch_low = u128::from_be_bytes((&stretch[..16]).try_into().unwrap());
497    let stretch_hi = u64::from_be_bytes((&stretch[16..24]).try_into().unwrap());
498    let stretch_hi = u128::from(stretch_hi);
499
500    // offset_0 = stretch[1+bottom..128+bottom]
501    let offset = (stretch_low << bottom) | (stretch_hi >> (64 - bottom));
502    offset.to_be_bytes().into()
503}
504
505impl<Cipher, NonceSize, TagSize> Ocb3<Cipher, NonceSize, TagSize>
506where
507    Cipher: BlockSizeUser<BlockSize = U16> + BlockEncrypt,
508    NonceSize: sealed::NonceSizes,
509    TagSize: sealed::TagSizes,
510{
511    /// Computes HASH function defined in https://www.rfc-editor.org/rfc/rfc7253.html#section-4.1
512    fn hash(&self, associated_data: &[u8]) -> Sum {
513        let mut offset_i = Block::default();
514        let mut sum_i = Block::default();
515
516        let mut i = 1;
517        for a_i in associated_data.chunks_exact(16) {
518            // offset_i = offset_{i-1} xor L_{ntz(i)}
519            inplace_xor(&mut offset_i, &self.ll[ntz(i)]);
520            // Sum_i = Sum_{i-1} xor ENCIPHER(K, A_i xor offset_i)
521            let mut a_i = *Block::from_slice(a_i);
522            inplace_xor(&mut a_i, &offset_i);
523            self.cipher.encrypt_block(&mut a_i);
524            inplace_xor(&mut sum_i, &a_i);
525
526            i += 1;
527        }
528
529        // Process any partial blocks.
530        if (associated_data.len() % 16) != 0 {
531            let processed_bytes = (i - 1) * 16;
532            let remaining_bytes = associated_data.len() - processed_bytes;
533
534            // offset_* = offset_m xor L_*
535            inplace_xor(&mut offset_i, &self.ll_star);
536            // CipherInput = (A_* || 1 || zeros(127-bitlen(A_*))) xor offset_*
537            let cipher_input = &mut [0u8; 16];
538            cipher_input[..remaining_bytes].copy_from_slice(&associated_data[processed_bytes..]);
539            cipher_input[remaining_bytes] = 0b1000_0000;
540            let cipher_input = Block::from_mut_slice(cipher_input);
541            inplace_xor(cipher_input, &offset_i);
542            // Sum = Sum_m xor ENCIPHER(K, CipherInput)
543            self.cipher.encrypt_block(cipher_input);
544            inplace_xor(&mut sum_i, cipher_input);
545        }
546
547        sum_i
548    }
549
550    fn compute_tag(
551        &self,
552        associated_data: &[u8],
553        checksum_m: &mut Block,
554        offset_m: &Block,
555    ) -> Tag<TagSize> {
556        // Tag = ENCIPHER(K, checksum_m xor offset_m xor L_$) xor HASH(K,A)
557        let full_tag = checksum_m;
558        inplace_xor(full_tag, offset_m);
559        inplace_xor(full_tag, &self.ll_dollar);
560        self.cipher.encrypt_block(full_tag);
561        inplace_xor(full_tag, &self.hash(associated_data));
562
563        // truncate the tag to the required length
564        Tag::clone_from_slice(&full_tag[..TagSize::to_usize()])
565    }
566}
567
568#[cfg(test)]
569mod tests {
570    use super::*;
571    use hex_literal::hex;
572
573    #[test]
574    fn double_basic_test() {
575        let zero = Block::from(hex!("00000000000000000000000000000000"));
576        assert_eq!(zero, double(&zero));
577        let one = Block::from(hex!("00000000000000000000000000000001"));
578        let two = Block::from(hex!("00000000000000000000000000000002"));
579        assert_eq!(two, double(&one));
580    }
581
582    #[test]
583    fn rfc7253_key_dependent_constants() {
584        // Test vector from page 17 of https://www.rfc-editor.org/rfc/rfc7253.html
585        let key = hex!("000102030405060708090A0B0C0D0E0F");
586        let expected_ll_star = Block::from(hex!("C6A13B37878F5B826F4F8162A1C8D879"));
587        let expected_ll_dollar = Block::from(hex!("8D42766F0F1EB704DE9F02C54391B075"));
588        let expected_ll0 = Block::from(hex!("1A84ECDE1E3D6E09BD3E058A8723606D"));
589        let expected_ll1 = Block::from(hex!("3509D9BC3C7ADC137A7C0B150E46C0DA"));
590
591        let cipher = aes::Aes128::new(GenericArray::from_slice(&key));
592        let (ll_star, ll_dollar, ll) = key_dependent_variables(&cipher);
593
594        assert_eq!(ll_star, expected_ll_star);
595        assert_eq!(ll_dollar, expected_ll_dollar);
596        assert_eq!(ll[0], expected_ll0);
597        assert_eq!(ll[1], expected_ll1);
598    }
599
600    #[test]
601    fn rfc7253_nonce_dependent_constants() {
602        // Test vector from page 17 of https://www.rfc-editor.org/rfc/rfc7253.html
603        let key = hex!("000102030405060708090A0B0C0D0E0F");
604        let nonce = hex!("BBAA9988776655443322110F");
605        let expected_bottom = usize::try_from(15).unwrap();
606        let expected_stretch = hex!("9862B0FDEE4E2DD56DBA6433F0125AA2FAD24D13A063F8B8");
607        let expected_offset_0 = Block::from(hex!("587EF72716EAB6DD3219F8092D517D69"));
608
609        const TAGLEN: u32 = 16;
610
611        let cipher = aes::Aes128::new(GenericArray::from_slice(&key));
612        let (bottom, stretch) = nonce_dependent_variables(&cipher, &Nonce::from(nonce), TAGLEN);
613        let offset_0 = initial_offset(&cipher, &Nonce::from(nonce), TAGLEN);
614
615        assert_eq!(bottom, expected_bottom, "bottom");
616        assert_eq!(stretch, expected_stretch, "stretch");
617        assert_eq!(offset_0, expected_offset_0, "offset");
618    }
619}