hs1_siv/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3#![warn(missing_docs, rust_2018_idioms)]
4
5mod hash;
6
7pub use aead;
8
9use aead::{
10    array::{
11        typenum::{Gr, IsGreater, Prod, Quot, Sub1, Sum, Unsigned},
12        Array, ArraySize,
13    },
14    consts::{False, True, B1, U0, U12, U16, U2, U32, U4, U6, U8},
15    AeadCore, AeadInPlace, KeyInit, KeySizeUser,
16};
17use chacha20::{
18    cipher::{StreamCipher, StreamCipherSeek},
19    ChaCha12, ChaCha20, ChaCha8, KeyIvInit,
20};
21use core::{
22    marker::PhantomData,
23    mem,
24    ops::{Add, Div, Mul, Sub},
25};
26use hash::Hasher;
27
28/// Implementation of HS1-SIV.
29///
30/// While HS1-SIV takes a key between 1 and 32 bytes,
31/// this structure instead stores the derived key,
32/// which is substantially larger:
33///
34/// - `Hs1SivLo`: 128 bytes.
35/// - `Hs1SivMe`: 176 bytes.
36/// - `Hs1SivHi`: 368 bytes.
37#[derive(Clone)]
38pub struct Hs1Siv<P>
39where
40    P: Hs1Params,
41{
42    key: Hs1Key<P>,
43    _marker: PhantomData<P>,
44}
45
46/// | `B` | `T` | `C`        | `L` |
47/// |-----|-----|------------|-----|
48/// |   4 |   2 | `ChaCha8`  |   8 |
49///
50/// | Key search  | SIV collision                   |
51/// |-------------|---------------------------------|
52/// | `n/(2^256)` | `(n^2)/(2^56)  + (n^2)/(2^64) ` |
53pub type Hs1SivLo = Hs1Siv<params::Hs1SivLo>;
54
55/// | `B` | `T` | `C`        | `L` |
56/// |-----|-----|------------|-----|
57/// |   4 |   4 | `ChaCha12` |  16 |
58///
59/// | Key search  | SIV collision                   |
60/// |-------------|---------------------------------|
61/// | `n/(2^256)` | `(n^2)/(2^112) + (n^2)/(2^128)` |
62pub type Hs1SivMe = Hs1Siv<params::Hs1SivMe>;
63
64/// | `B` | `T` | `C`        | `L` |
65/// |-----|-----|------------|-----|
66/// |   4 |   6 | `ChaCha20` |  32 |
67///
68/// | Key search  | SIV collision                   |
69/// |-------------|---------------------------------|
70/// | `n/(2^256)` | `(n^2)/(2^168) + (n^2)/(2^256)` |
71pub type Hs1SivHi = Hs1Siv<params::Hs1SivHi>;
72
73impl<P> AeadCore for Hs1Siv<P>
74where
75    P: Hs1Params,
76{
77    type TagSize = P::L;
78    type NonceSize = U12;
79    type CiphertextOverhead = U0;
80}
81
82impl<P> AeadInPlace for Hs1Siv<P>
83where
84    P: Hs1Params,
85{
86    fn encrypt_in_place_detached(
87        &self,
88        nonce: &aead::Nonce<Self>,
89        associated_data: &[u8],
90        buffer: &mut [u8],
91    ) -> aead::Result<aead::Tag<Self>> {
92        hs1_siv_encrypt::<P>(&self.key, nonce, associated_data, buffer)
93    }
94
95    fn decrypt_in_place_detached(
96        &self,
97        nonce: &aead::Nonce<Self>,
98        associated_data: &[u8],
99        buffer: &mut [u8],
100        tag: &aead::Tag<Self>,
101    ) -> aead::Result<()> {
102        hs1_siv_decrypt::<P>(&self.key, nonce, associated_data, buffer, tag)
103    }
104}
105
106impl<P> KeySizeUser for Hs1Siv<P>
107where
108    P: Hs1Params,
109{
110    type KeySize = U32;
111}
112
113impl<P> KeyInit for Hs1Siv<P>
114where
115    P: Hs1Params,
116{
117    fn new(key: &aead::Key<Self>) -> Self {
118        assert!((1..=32).contains(&key.len()));
119        let key = hs1_subkeygen::<P>(key);
120        Self {
121            key,
122            _marker: PhantomData,
123        }
124    }
125}
126
127/// Definitions of standard parameters for use with HS1-SIV.
128///
129/// Prefer using the type aliases at the root of the crate instead.
130pub mod params {
131    use super::*;
132
133    /// | `B` | `T` | `C`        | `L` |
134    /// |-----|-----|------------|-----|
135    /// |   4 |   2 | `ChaCha8`  |   8 |
136    ///
137    /// | Key search  | SIV collision                   |
138    /// |-------------|---------------------------------|
139    /// | `n/(2^256)` | `(n^2)/(2^56)  + (n^2)/(2^64) ` |
140    #[derive(Clone, Copy)]
141    pub struct Hs1SivLo;
142
143    /// | `B` | `T` | `C`        | `L` |
144    /// |-----|-----|------------|-----|
145    /// |   4 |   4 | `ChaCha12` |  16 |
146    ///
147    /// | Key search  | SIV collision                   |
148    /// |-------------|---------------------------------|
149    /// | `n/(2^256)` | `(n^2)/(2^112) + (n^2)/(2^128)` |
150    #[derive(Clone, Copy)]
151    pub struct Hs1SivMe;
152
153    /// | `B` | `T` | `C`        | `L` |
154    /// |-----|-----|------------|-----|
155    /// |   4 |   6 | `ChaCha20` |  32 |
156    ///
157    /// | Key search  | SIV collision                   |
158    /// |-------------|---------------------------------|
159    /// | `n/(2^256)` | `(n^2)/(2^168) + (n^2)/(2^256)` |
160    #[derive(Clone, Copy)]
161    pub struct Hs1SivHi;
162
163    impl Hs1Params for Hs1SivLo {
164        type B = U4;
165        type T = U2;
166        type C = ChaCha8;
167        type L = U8;
168    }
169
170    impl Hs1Params for Hs1SivMe {
171        type B = U4;
172        type T = U4;
173        type C = ChaCha12;
174        type L = U16;
175    }
176
177    impl Hs1Params for Hs1SivHi {
178        type B = U4;
179        type T = U6;
180        type C = ChaCha20;
181        type L = U32;
182    }
183}
184
185#[derive(Clone)]
186#[repr(C, align(16))]
187struct Hs1Key<P: Hs1Params> {
188    chacha: Array<u8, U32>,
189    hash: Hs1HashKey<P>,
190}
191
192#[derive(Clone)]
193#[repr(C, align(16))]
194struct Hs1HashKey<P: Hs1Params> {
195    nh: Array<u32, NhLen<P>>,
196    poly: Array<u64, P::T>,
197    asu: Array<hash::Asu<P>, P::T>,
198}
199
200impl<P: Hs1Params> Hs1Key<P> {
201    fn as_bytes_mut(&mut self) -> &mut [u8] {
202        // Ensure that all fields have a size which is a multiple of 16.
203        // This trivializes the safety proof, since padding is impossible if the check passes.
204        const {
205            const fn chk<T, L: ArraySize>() {
206                assert!(mem::size_of::<Array<T, L>>() % 16 == 0);
207            }
208            chk::<u8, U32>();
209            chk::<u32, NhLen<P>>();
210            chk::<u64, P::T>();
211            chk::<hash::Asu<P>, P::T>();
212        }
213        // SAFETY:
214        // - There are no padding bytes
215        // - There are no invalid bit patterns
216        unsafe {
217            let len = mem::size_of_val(self);
218            let ptr = self as *mut Self as *mut u8;
219            core::slice::from_raw_parts_mut(ptr, len)
220        }
221    }
222}
223
224type B16<P> = Prod<<P as Hs1Params>::B, U16>;
225type NhLen<P> = Sum<Quot<B16<P>, U4>, Prod<Sub1<<P as Hs1Params>::T>, U4>>;
226
227/// HS1 parameters.
228// hey, as long as it works!
229pub trait Hs1Params: Copy + Sync + Send
230where
231    Self::B: Mul<U16> + 'static,
232    B16<Self>: ArraySize,
233    Self::T: ArraySize,
234    Self::L: ArraySize,
235    Quot<B16<Self>, U4>: ArraySize,
236    // Hs1Key
237    Self::T: Sub<B1>,
238    Sub1<Self::T>: Mul<U4>,
239    B16<Self>: Div<U4>,
240    Quot<B16<Self>, U4>: Add<Prod<Sub1<Self::T>, U4>>,
241    NhLen<Self>: ArraySize,
242    // hs1_hash
243    Self::T: IsGreater<U4>,
244    Gr<Self::T, U4>: hash::sealed::Hs1HashFinal,
245    hash::Output<Self>: Default + AsRef<[u8]>,
246{
247    /// Block size, in terms of 16 bytes.
248    type B;
249    /// "collision level" (higher is more secure).
250    type T;
251    /// ChaCha implementation.
252    type C: KeyIvInit<KeySize = U32, IvSize = U12>
253        + StreamCipher
254        + StreamCipherSeek
255        + sealed::ChaChaImpl;
256    /// Tag length in bytes.
257    type L;
258}
259
260mod sealed {
261    // Necessary for subkeygen
262    pub trait ChaChaImpl {
263        const ROUNDS: u8;
264    }
265}
266
267impl sealed::ChaChaImpl for ChaCha8 {
268    const ROUNDS: u8 = 8;
269}
270impl sealed::ChaChaImpl for ChaCha12 {
271    const ROUNDS: u8 = 12;
272}
273impl sealed::ChaChaImpl for ChaCha20 {
274    const ROUNDS: u8 = 20;
275}
276
277/// # Note
278///
279/// `m.len()` may not exceed `2**38`.
280fn hs1_siv_encrypt<P: Hs1Params>(
281    k: &Hs1Key<P>,
282    n: &Array<u8, U12>,
283    a: &[u8],
284    m: &mut [u8],
285) -> Result<Array<u8, P::L>, aead::Error> {
286    if m.len() as u128 > 1 << 38 {
287        return Err(aead::Error);
288    }
289    let t = hs1_tag::<P>(k, a, n, &*m);
290    hs1::<P>(k, &[&*t], n, 64, m);
291    Ok(t)
292}
293
294fn hs1_siv_decrypt<P: Hs1Params>(
295    k: &Hs1Key<P>,
296    n: &Array<u8, U12>,
297    a: &[u8],
298    m: &mut [u8],
299    t: &Array<u8, P::L>,
300) -> Result<(), aead::Error> {
301    if m.len() as u128 > 1 << 38 {
302        return Err(aead::Error);
303    }
304    hs1::<P>(k, &[t], n, 64, m);
305    let t2 = hs1_tag::<P>(k, a, n, m);
306    let diff = t.iter().zip(t2.iter()).fold(0, |s, (x, y)| s | (x ^ y));
307    (diff == 0).then_some(()).ok_or_else(|| {
308        // Apparently keeping the plaintext is CVE-worthy (CVE-2023-42811)
309        // No way in hell I'm running the cipher again - just zero out the buffer
310        m.fill(0);
311        aead::Error
312    })
313}
314
315fn hs1_tag<P: Hs1Params>(k: &Hs1Key<P>, a: &[u8], n: &Array<u8, U12>, m: &[u8]) -> Array<u8, P::L> {
316    let a_m_len = &mut [0; 16];
317    a_m_len[..8].copy_from_slice(&(a.len() as u64).to_le_bytes());
318    a_m_len[8..].copy_from_slice(&(m.len() as u64).to_le_bytes());
319    let m2 = &[a, m, a_m_len];
320    let mut t = Array::<u8, P::L>::default();
321    hs1::<P>(k, m2, n, 0, &mut t);
322    t
323}
324
325#[inline(always)]
326fn hs1<P: Hs1Params>(k: &Hs1Key<P>, m: &[&[u8]], n: &Array<u8, U12>, y_offset: u32, y: &mut [u8]) {
327    let mut key = k.chacha;
328
329    let mut hasher = Hasher::<P>::new(&k.hash);
330    for (i, b) in m.iter().enumerate() {
331        if i > 0 {
332            hasher.pad_to(4);
333        }
334        hasher.update(b);
335    }
336    let input = hasher.finalize();
337
338    key.iter_mut()
339        .zip(input.iter().flat_map(|x| x.as_ref()))
340        .for_each(|(w, r)| *w ^= *r);
341    let mut cipher = P::C::new(&key, n);
342    cipher.seek(y_offset);
343    cipher.apply_keystream(y)
344}
345
346fn hs1_subkeygen<P: Hs1Params>(k: &[u8]) -> Hs1Key<P> {
347    assert!((1..=32).contains(&k.len()));
348
349    let k2 = &mut Array::<u8, U32>::default();
350    k2.iter_mut()
351        .zip(k.iter().cycle())
352        .for_each(|(w, r)| *w = *r);
353
354    let n = &mut Array::<u8, U12>::default();
355    debug_assert!(k.len() < 256);
356    debug_assert!(P::L::U64 < 256);
357    debug_assert!(P::T::U64 < 256);
358    debug_assert!(B16::<P>::U64 < 256);
359    n[0] = k.len() as u8;
360    n[2] = P::L::to_u8();
361    n[4] = <P::C as sealed::ChaChaImpl>::ROUNDS;
362    n[5] = P::T::to_u8();
363    n[6] = B16::<P>::to_u8();
364
365    let mut k = Hs1Key {
366        chacha: Array::default(),
367        hash: Hs1HashKey {
368            nh: Array::default(),
369            poly: Array::default(),
370            asu: Array::default(),
371        },
372    };
373
374    <P::C as KeyIvInit>::new(k2, n).apply_keystream(k.as_bytes_mut());
375    k.hash.poly.iter_mut().for_each(|p| *p &= mask(60));
376
377    k
378}
379
380#[inline(always)]
381const fn mask(bits: u8) -> u64 {
382    (1u64 << bits).wrapping_sub(1)
383}
384
385#[cfg(test)]
386mod test {
387    use super::*;
388    use aead::{Aead, KeyInit};
389
390    const MSG: &[u8] = b"Hello to the entire wide, round, global globe!";
391    const MSG_LONG: &[u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
392    const KEY: &[u8; 32] = b"Short keys? Use long for testing";
393    const NONCE: &[u8; 12] = b"Quack quack!";
394
395    fn hs1siv<P: Hs1Params>() {
396        let hs1 = Hs1Siv::<P>::new(KEY.into());
397        let cph = hs1.encrypt(NONCE.into(), MSG).unwrap();
398        let msg = hs1.decrypt(NONCE.into(), &*cph).unwrap();
399        assert_eq!(&msg, MSG);
400    }
401
402    #[test]
403    fn hs1siv_me() {
404        hs1siv::<params::Hs1SivMe>();
405    }
406
407    #[test]
408    fn hs1siv_lo() {
409        hs1siv::<params::Hs1SivLo>();
410    }
411
412    #[test]
413    fn hs1siv_hi() {
414        hs1siv::<params::Hs1SivHi>();
415    }
416
417    /// Custom generated vectors using (reference implementation)[0].
418    ///
419    /// [0]: https://bench.cr.yp.to/supercop.html
420    mod test_vectors {
421        use super::*;
422
423        #[test]
424        fn subkeygen_me() {
425            let k = hs1_subkeygen::<params::Hs1SivMe>(KEY);
426            assert_eq!(
427                k.chacha,
428                [
429                    0x02, 0xea, 0xb5, 0x34, 0x85, 0x3e, 0xf7, 0xf4, 0x81, 0x3f, 0x87, 0xd8, 0xd2,
430                    0x63, 0x1e, 0x05, 0xf9, 0x68, 0x91, 0xd0, 0x8a, 0x03, 0x34, 0xfc, 0x64, 0xbe,
431                    0x6b, 0x3a, 0x89, 0xfe, 0x20, 0x8d,
432                ]
433            );
434            assert_eq!(
435                k.hash.nh,
436                [
437                    0x74e7102f, 0x374603b7, 0xf470c90c, 0x8c829c82, 0x07d6f293, 0xf9e7e569,
438                    0xcd590406, 0xe6bdc9ad, 0xa2687cda, 0xfc1a8b80, 0x501efbee, 0x0df51d32,
439                    0x7fd3f594, 0xc3d1520b, 0x1b83db2f, 0x0791c054, 0x66583c46, 0xcb096241,
440                    0x7afc8085, 0x4b37d47a, 0x540287e0, 0xe1ace58b, 0x4f125f3b, 0xb69b5935,
441                    0x6cb2cf06, 0xbf86407b, 0x18a6a2e5, 0xe1eaa248,
442                ]
443            );
444            assert_eq!(
445                k.hash.poly,
446                [
447                    0x09aad6627602f656,
448                    0x07f2089068131f87,
449                    0x0a982e724caf2722,
450                    0x004f2d42b1092d0a,
451                ]
452            );
453            assert_eq!(k.hash.asu, [[]; 4]);
454        }
455
456        #[test]
457        fn subkeygen_lo() {
458            let k = hs1_subkeygen::<params::Hs1SivLo>(KEY);
459            assert_eq!(
460                k.chacha,
461                [
462                    0xab, 0x1b, 0x65, 0x62, 0xe5, 0x4c, 0x79, 0x27, 0x30, 0xa3, 0x4c, 0xa6, 0x7e,
463                    0x79, 0x0f, 0xb9, 0xa9, 0x85, 0x62, 0xb2, 0x17, 0x2e, 0x47, 0x99, 0xe3, 0x7a,
464                    0x0c, 0x63, 0x77, 0xc4, 0x85, 0xca,
465                ]
466            );
467            assert_eq!(
468                k.hash.nh,
469                [
470                    0xd743ff76, 0x64b9e928, 0x0effa5ae, 0xf850ec1d, 0xda1249a9, 0x29afefcf,
471                    0x18bb4916, 0x35d0b524, 0x2036f9c4, 0x0ae224a6, 0x98f18f97, 0x3aad32e2,
472                    0x85256859, 0x30e4ad2e, 0x63b08461, 0x13c97c7d, 0xe4d45609, 0x0ca44ba2,
473                    0x6c4b356e, 0x9b960e6b,
474                ]
475            );
476            assert_eq!(k.hash.poly, [0x0ef85bac983cb194, 0x0a584b5179c75231]);
477            assert_eq!(k.hash.asu, [[]; 2]);
478        }
479
480        #[test]
481        fn hash_me() {
482            let k = hs1_subkeygen::<params::Hs1SivMe>(KEY);
483            let h = Hasher::new(&k.hash).update(MSG).finalize();
484            assert_eq!(
485                h,
486                [
487                    0x1808a23d991ae22c,
488                    0x08f96bf01b438f3b,
489                    0x194ee1ffd24b84a0,
490                    0x0b25578352a73e9d,
491                ]
492                .map(u64::to_le_bytes)
493            );
494        }
495
496        #[test]
497        fn hash_me_64() {
498            const MSG_64: &[u8; 64] =
499                b"Hello to the entire wide, round, global globe!!! okookokokokokok";
500            let k = hs1_subkeygen::<params::Hs1SivMe>(KEY);
501            let h = Hasher::new(&k.hash).update(MSG_64).finalize();
502            assert_eq!(
503                h,
504                [
505                    0x0f128a7f7b601324,
506                    0x0dc82e748a2a1395,
507                    0x106966138221d2ba,
508                    0x09f86f41d6677d4d,
509                ]
510                .map(u64::to_le_bytes)
511            );
512        }
513
514        #[test]
515        fn hash_me_65() {
516            const MSG_65: &[u8; 65] =
517                b"Hello to the entire wide, round, global globe!!! okookokokokokok?";
518            let k = hs1_subkeygen::<params::Hs1SivMe>(KEY);
519            let h = Hasher::new(&k.hash).update(MSG_65).finalize();
520            assert_eq!(
521                h,
522                [
523                    0x10619b1a23127759,
524                    0x160f2049c69ee554,
525                    0x1de3d0b0f4d56bec,
526                    0x03e8ec8fdef39c71,
527                ]
528                .map(u64::to_le_bytes)
529            );
530        }
531
532        #[test]
533        fn hash_me_128() {
534            const MSG_128: &[u8; 128] =
535                b"Hello to the entire wide, round, global globe!!! okookokokokokokHello to the entire wide, round, global globe!!! okookokokokokok";
536            let k = hs1_subkeygen::<params::Hs1SivMe>(KEY);
537            let h = Hasher::new(&k.hash).update(MSG_128).finalize();
538            assert_eq!(
539                h,
540                [
541                    0x07d3154786d50a10,
542                    0x145bceb11f846780,
543                    0x0321fdeb01118846,
544                    0x0a0ac6ce29b11e5a,
545                ]
546                .map(u64::to_le_bytes)
547            );
548        }
549
550        #[test]
551        fn hash_lo() {
552            let k = hs1_subkeygen::<params::Hs1SivLo>(KEY);
553            let h = Hasher::new(&k.hash).update(MSG).finalize();
554            assert_eq!(
555                h,
556                [0x1afa0c19eba9a66b, 0x15ceb31a087f2657,].map(u64::to_le_bytes)
557            );
558        }
559
560        #[test]
561        fn hash_hi() {
562            let k = hs1_subkeygen::<params::Hs1SivHi>(KEY);
563            let h = Hasher::new(&k.hash).update(MSG).finalize();
564            assert_eq!(
565                h,
566                [0xcf452c22, 0x452317a2, 0x7fa1f1d6, 0x100d9702, 0xcf1defb0, 0x4c73da69,]
567                    .map(u32::to_le_bytes)
568            );
569        }
570
571        #[test]
572        fn hash_lo_long() {
573            let k = hs1_subkeygen::<params::Hs1SivLo>(KEY);
574            let h = Hasher::new(&k.hash).update(MSG_LONG).finalize();
575            assert_eq!(
576                h,
577                [0x0b65743a2f4c73aa, 0x1863d3ec1873cd72,].map(u64::to_le_bytes)
578            );
579        }
580
581        #[test]
582        fn hash_me_long() {
583            let k = hs1_subkeygen::<params::Hs1SivMe>(KEY);
584            let h = Hasher::new(&k.hash).update(MSG_LONG).finalize();
585            assert_eq!(
586                h,
587                [
588                    0x1f8e6282cbc4455f,
589                    0x0e6ade357355de7b,
590                    0x1a5834576032c7b0,
591                    0x1bd063cb8b70044a,
592                ]
593                .map(u64::to_le_bytes)
594            );
595        }
596
597        #[test]
598        fn hash_hi_long() {
599            let k = hs1_subkeygen::<params::Hs1SivHi>(KEY);
600            let h = Hasher::new(&k.hash).update(MSG_LONG).finalize();
601            assert_eq!(
602                h,
603                [0x52645829, 0x8f0c0687, 0x01f33121, 0xc94264e3, 0x85dc8143, 0xc8fd435e,]
604                    .map(u32::to_le_bytes)
605            );
606        }
607
608        // TODO I'm 99% sure this is wrong according to the paper,
609        // but it shouldn't be an issue as long as we don't expose the hasher
610        // to the public...
611        #[test]
612        fn hash_me_empty() {
613            let k = hs1_subkeygen::<params::Hs1SivMe>(KEY);
614            let h = Hasher::new(&k.hash).finalize();
615            assert_eq!(
616                h,
617                [
618                    0x0000000000000001,
619                    0x0000000000000001,
620                    0x0000000000000001,
621                    0x0000000000000001,
622                ]
623                .map(u64::to_le_bytes)
624            );
625        }
626
627        #[test]
628        fn hs1siv_me() {
629            let hs1 = Hs1SivMe::new(KEY.into());
630            let cph = hs1.encrypt(NONCE.into(), MSG).unwrap();
631            assert_eq!(
632                &cph,
633                &[
634                    0x1b, 0x26, 0x40, 0x4d, 0xe3, 0x46, 0xb3, 0x65, 0x07, 0xa7, 0x93, 0xf3, 0x6e,
635                    0xab, 0xb5, 0xcb, 0x1a, 0x99, 0x7c, 0xbf, 0xdf, 0x6c, 0xed, 0x15, 0xd9, 0xd0,
636                    0x26, 0x37, 0xf7, 0xcc, 0xd4, 0xb1, 0x20, 0xee, 0x02, 0x52, 0x3c, 0xee, 0xcc,
637                    0x41, 0x04, 0xbf, 0x42, 0xa9, 0xfc, 0x2e, 0x45, 0x06, 0x67, 0xa6, 0xfe, 0x07,
638                    0x2f, 0x00, 0x81, 0x72, 0x52, 0xa8, 0xb0, 0xb1, 0x2e, 0xd6,
639                ]
640            );
641        }
642
643        #[test]
644        fn hs1siv_lo() {
645            let hs1 = Hs1SivLo::new(KEY.into());
646            let cph = hs1.encrypt(NONCE.into(), MSG).unwrap();
647            assert_eq!(
648                &cph,
649                &[
650                    0xa8, 0xac, 0xcd, 0x91, 0x09, 0x39, 0xac, 0x6a, 0x13, 0x81, 0xa3, 0xa4, 0xbe,
651                    0xa1, 0xc9, 0x97, 0xa7, 0xda, 0xe6, 0x5e, 0x73, 0xd6, 0x0f, 0x2e, 0x87, 0xcf,
652                    0xe7, 0x20, 0xaf, 0x0d, 0x94, 0x45, 0xaa, 0x9b, 0x91, 0xf2, 0x11, 0x33, 0x48,
653                    0xc5, 0x7d, 0x0f, 0xd8, 0xda, 0xd7, 0x9a, 0x3d, 0xcf, 0x63, 0xea, 0xda, 0x32,
654                    0x7c, 0xa6,
655                ]
656            );
657        }
658
659        #[test]
660        fn hs1siv_hi() {
661            let hs1 = Hs1SivHi::new(KEY.into());
662            let cph = hs1.encrypt(NONCE.into(), MSG).unwrap();
663            assert_eq!(
664                &cph,
665                &[
666                    0xbc, 0x5d, 0xbb, 0x49, 0x52, 0x97, 0xb8, 0xb0, 0xab, 0x3a, 0x0b, 0x69, 0xb0,
667                    0x60, 0xd2, 0x75, 0xd1, 0x4e, 0x14, 0x73, 0x8f, 0xe3, 0xb6, 0x14, 0xb0, 0x06,
668                    0x01, 0x96, 0x4f, 0x90, 0x6e, 0x6a, 0x67, 0x71, 0xd0, 0x71, 0xf0, 0x4b, 0xc9,
669                    0xf8, 0x14, 0x54, 0x30, 0xe3, 0x33, 0xb0, 0x09, 0x97, 0x47, 0xf4, 0x8c, 0xd0,
670                    0x60, 0xae, 0x68, 0x40, 0xcb, 0x58, 0x64, 0x6b, 0xf9, 0x66, 0x5f, 0x58, 0xfa,
671                    0xdf, 0xd0, 0x50, 0xa7, 0x00, 0x43, 0x55, 0x5e, 0x63, 0xe9, 0x89, 0x31, 0x29,
672                ]
673            );
674        }
675    }
676}