polyhash/
poly.rs

1use crate::{
2    backend::{Big, Small, POLYVAL},
3    impl_hash, impl_state,
4};
5
6impl_state!(PolyvalState, LE);
7
8#[cfg(feature = "experimental")]
9type State = PolyvalState;
10
11impl_hash! {
12    /// An implementation of POLYVAL.
13    ///
14    /// POLYVAL is similar to GHASH. It operates in `GF(2¹²⁸)`
15    /// defined by the irreducible polynomial
16    ///
17    /// ```text
18    /// x^128 + x^127 + x^126 + x^121 + 1
19    /// ```
20    ///
21    /// The field has characteristic 2, so addition is performed
22    /// with XOR. Multiplication is polynomial multiplication
23    /// reduced modulo the polynomial.
24    ///
25    /// For more information on POLYVAL, see [RFC 8452].
26    ///
27    /// [RFC 8452]: https://datatracker.ietf.org/doc/html/rfc8452
28    pub struct Polyval(Big<POLYVAL>);
29}
30
31impl_hash! {
32    /// The same thing as [`Polyval`], except it only processes
33    /// one block at a time.
34    ///
35    /// This saves space, but can be slower if the input is more
36    /// than a couple blocks long.
37    pub struct PolyvalLite(Small<POLYVAL>);
38}
39
40#[cfg(test)]
41mod tests {
42    use serde::Deserialize;
43
44    use super::*;
45    use crate::{as_blocks, KEY_SIZE};
46
47    macro_rules! hex {
48        ($($s:literal)*) => {{
49            const LEN: usize = hex_literal::hex!($($s)*).len();
50            const OUTPUT: [u8; LEN] = hex_literal::hex!($($s)*);
51            &OUTPUT
52        }};
53    }
54
55    #[test]
56    fn test_rfc_vectors() {
57        type TestCase = (&'static [u8; KEY_SIZE], &'static [u8], &'static [u8; 16]);
58        let cases: &[TestCase] = &[
59            (
60                hex!("25629347589242761d31f826ba4b757b"),
61                hex!("4f4f95668c83dfb6401762bb2d01a262"),
62                hex!("cedac64537ff50989c16011551086d77"),
63            ),
64            (
65                hex!("25629347589242761d31f826ba4b757b"),
66                hex!(
67                    "4f4f95668c83dfb6401762bb2d01a262"
68                    "d1a24ddd2721d006bbe45f20d3c9f362"
69                ),
70                hex!("f7a3b47b846119fae5b7866cf5e5b77e"),
71            ),
72            (
73                hex!("d9b360279694941ac5dbc6987ada7377"),
74                hex!("00000000000000000000000000000000"),
75                hex!("00000000000000000000000000000000"),
76            ),
77            (
78                hex!("d9b360279694941ac5dbc6987ada7377"),
79                hex!("01000000000000000000000000000000"
80                    "000000000000000040"),
81                hex!("eb93b7740962c5e49d2a90a7dc5cec74"),
82            ),
83            (
84                hex!("d9b360279694941ac5dbc6987ada7377"),
85                hex!("01000000000000000000000000000000"
86                    "000000000000000060"),
87                hex!("48eb6c6c5a2dbe4a1dde508fee06361b"),
88            ),
89            (
90                hex!("d9b360279694941ac5dbc6987ada7377"),
91                hex!("01000000000000000000000000000000"
92                    "000000000000000080"),
93                hex!("20806c26e3c1de019e111255708031d6"),
94            ),
95            (
96                hex!("d9b360279694941ac5dbc6987ada7377"),
97                hex!(
98                    "01000000000000000000000000000000"
99                    "02000000000000000000000000000000"
100                    "00000000000000000001"
101                ),
102                hex!("ce6edc9a50b36d9a98986bbf6a261c3b"),
103            ),
104            (
105                hex!("0533fd71f4119257361a3ff1469dd4e5"),
106                hex!(
107                    "489c8fde2be2cf97e74e932d4ed87d00"
108                    "c9882e5386fd9f92ec00000000000000"
109                    "780000000000000048"
110                ),
111                hex!("bf160bc9ded8c63057d2c38aae552fb4"),
112            ),
113            (
114                hex!("64779ab10ee8a280272f14cc8851b727"),
115                hex!(
116                    "0da55210cc1c1b0abde3b2f204d1e9f8"
117                    "b06bc47f000000000000000000000000"
118                    "1db2316fd568378da107b52b00000000"
119                    "a00000000000000060"
120                ),
121                hex!("cc86ee22c861e1fd474c84676b42739c"),
122            ),
123            (
124                hex!("27c2959ed4daea3b1f52e849478de376"),
125                hex!(
126                    "f37de21c7ff901cfe8a69615a93fdf7a"
127                    "98cad481796245709f00000000000000"
128                    "21702de0de18baa9c9596291b0846600"
129                    "c80000000000000078"
130                ),
131                hex!("c4fa5e5b713853703bcf8e6424505fa5"),
132            ),
133            (
134                hex!("670b98154076ddb59b7a9137d0dcc0f0"),
135                hex!(
136                    "9c2159058b1f0fe91433a5bdc20e214e"
137                    "ab7fecef4454a10ef0657df21ac70000"
138                    "b202b370ef9768ec6561c4fe6b7e7296"
139                    "fa850000000000000000000000000000"
140                    "f00000000000000090"
141                ),
142                hex!("4e4108f09f41d797dc9256f8da8d58c7"),
143            ),
144            (
145                hex!("cb8c3aa3f8dbaeb4b28a3e86ff6625f8"),
146                hex!(
147                    "734320ccc9d9bbbb19cb81b2af4ecbc3"
148                    "e72834321f7aa0f70b7282b4f33df23f"
149                    "16754100000000000000000000000000"
150                    "ced532ce4159b035277d4dfbb7db6296"
151                    "8b13cd4eec0000000000000000000000"
152                    "1801000000000000a8"
153                ),
154                hex!("ffd503c7dd712eb3791b7114b17bb0cf"),
155            ),
156        ];
157
158        for (i, &(h, x, r)) in cases.iter().enumerate() {
159            let mut p = Polyval::new_unchecked(h);
160            p.update_padded(x);
161            assert_eq!(&p.tag().0, r, "#{i} (precomp)");
162
163            let mut p = PolyvalLite::new_unchecked(h);
164            p.update_padded(x);
165            assert_eq!(&p.tag().0, r, "#{i} (lite)");
166        }
167    }
168
169    #[test]
170    fn test_vectors() {
171        #[derive(Deserialize)]
172        #[allow(dead_code)]
173        struct Lengths {
174            block: usize,
175            key: usize,
176            nonce: usize,
177        }
178
179        #[derive(Deserialize)]
180        #[allow(dead_code)]
181        struct BlockCipher {
182            cipher: String,
183            lengths: Lengths,
184        }
185
186        #[derive(Deserialize)]
187        #[allow(dead_code)]
188        struct Input {
189            #[serde(with = "hex::serde")]
190            key_hex: Vec<u8>,
191            #[serde(default, with = "hex::serde")]
192            tweak_hex: Vec<u8>,
193            #[serde(with = "hex::serde")]
194            message_hex: Vec<u8>,
195            #[serde(default, with = "hex::serde")]
196            nonce_hex: Vec<u8>,
197        }
198
199        #[derive(Deserialize)]
200        #[allow(dead_code)]
201        struct Cipher {
202            cipher: String,
203            block_cipher: Option<BlockCipher>,
204        }
205
206        #[derive(Deserialize)]
207        #[allow(dead_code)]
208        struct TestVector {
209            cipher: Cipher,
210            description: String,
211            input: Input,
212            #[serde(default, with = "hex::serde")]
213            plaintext_hex: Vec<u8>,
214            #[serde(default, with = "hex::serde")]
215            ciphertext_hex: Vec<u8>,
216            #[serde(with = "hex::serde")]
217            hash_hex: Vec<u8>,
218        }
219
220        const DATA: &str = include_str!("testdata/polyval.json");
221        let tests: Vec<TestVector> = serde_json::from_str(DATA).expect("should be valid JSON");
222        for (i, tc) in tests.iter().enumerate() {
223            let h: [u8; KEY_SIZE] = (&*tc.input.key_hex).try_into().unwrap_or_else(|_| {
224                panic!(
225                    "#{i}: {} should be `BLOCK_SIZE` all non-zero bytes",
226                    tc.description
227                )
228            });
229            let mut p = Polyval::new_unchecked(&h);
230            let (blocks, []) = as_blocks(&tc.input.message_hex) else {
231                panic!("#{i}: {} should block sized", tc.description);
232            };
233            p.update_blocks(blocks);
234            let got: [u8; 16] = p.clone().tag().into();
235            let want = &tc.hash_hex[..];
236            assert_eq!(got, want, "#{i}: (precomp) {}", tc.description);
237
238            let mut p = PolyvalLite::new_unchecked(&h);
239            let (blocks, []) = as_blocks(&tc.input.message_hex) else {
240                panic!("#{i}: {} should block sized", tc.description);
241            };
242            p.update_blocks(blocks);
243            let got: [u8; 16] = p.clone().tag().into();
244            let want = &tc.hash_hex[..];
245            assert_eq!(got, want, "#{i}: (lite) {}", tc.description);
246        }
247    }
248}