Skip to main content

crypto/sha3/
kmac.rs

1use super::shake256::{CShake256, left_encode, right_encode};
2use crate::Xof;
3
4const KMAC256_RATE: usize = 136;
5
6#[derive(Clone)]
7pub struct Kmac256 {
8    cshake: CShake256,
9}
10
11impl Kmac256 {
12    #[inline]
13    pub fn mac(key: &[u8], data: &[u8], customization: &[u8], output: &mut [u8]) {
14        let mut kmac = Kmac256::new(key, customization);
15        kmac.update(data);
16        kmac.finalize_into(output);
17    }
18
19    #[inline]
20    pub fn new(key: &[u8], customization: &[u8]) -> Self {
21        let mut cshake = CShake256::new(b"KMAC", customization);
22
23        // absorb bytepad(encode_string(key), KMAC256_RATE)
24
25        // bytepad(encode_string(key), w)
26        let enc_w = left_encode(KMAC256_RATE);
27        cshake.absorb(enc_w.as_ref());
28
29        let enc_key = left_encode(key.len() * 8);
30        cshake.absorb(enc_key.as_ref());
31        cshake.absorb(key);
32
33        let total = enc_w.len() + enc_key.len() + key.len();
34        let pad = (KMAC256_RATE - (total % KMAC256_RATE)) % KMAC256_RATE;
35        if pad > 0 {
36            let zeros = [0u8; KMAC256_RATE];
37            cshake.absorb(&zeros[..pad]);
38        }
39
40        return Kmac256 {
41            cshake,
42        };
43    }
44
45    #[inline]
46    pub fn update(&mut self, data: &[u8]) {
47        self.cshake.absorb(data);
48    }
49
50    #[inline]
51    pub fn finalize_into(mut self, output: &mut [u8]) {
52        let output_bits = output.len().checked_mul(8).expect("output size too large for KMAC");
53        let encoded_output_len = right_encode(output_bits);
54        self.cshake.absorb(encoded_output_len.as_ref());
55        self.cshake.squeeze(output);
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use hex;
62
63    use super::Kmac256;
64
65    const KEY: [u8; 32] = [
66        0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51,
67        0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
68    ];
69    const TAGGED_APP: &[u8] = b"My Tagged Application";
70
71    // NIST SP 800-185 KMAC_samples.pdf sample #4
72    const KMAC256_SAMPLE_4: &str = "20c570c31346f703c9ac36c61c03cb64c3970d0cfc787e9b79599d273a68d2f7f69d4cc3de9d104a351689f27cf6f5951f0103f33f4f24871024d9c27773a8dd";
73    // NIST SP 800-185 KMAC_samples.pdf sample #5
74    const KMAC256_SAMPLE_5: &str = "75358cf39e41494e949707927cee0af20a3ff553904c86b08f21cc414bcfd691589d27cf5e15369cbbff8b9a4c2eb17800855d0235ff635da82533ec6b759b69";
75    // NIST SP 800-185 KMAC_samples.pdf sample #6 (streaming-friendly)
76    const KMAC256_SAMPLE_6: &str = "b58618f71f92e1d56c1b8c55ddd7cd188b97b4ca4d99831eb2699a837da2e4d970fbacfde50033aea585f1a2708510c32d07880801bd182898fe476876fc8965";
77
78    #[test]
79    fn kmac256_nist_sample_4() {
80        let mut out = [0u8; 64];
81        Kmac256::mac(&KEY, &[0x00, 0x01, 0x02, 0x03], TAGGED_APP, &mut out);
82        assert_eq!(hex::encode(out), KMAC256_SAMPLE_4);
83    }
84
85    #[test]
86    fn kmac256_nist_sample_5() {
87        let input: Vec<u8> = (0u8..200).collect();
88        let mut out = [0u8; 64];
89        Kmac256::mac(&KEY, &input, b"", &mut out);
90        assert_eq!(hex::encode(out), KMAC256_SAMPLE_5);
91    }
92
93    #[test]
94    fn kmac256_nist_sample_6_incremental() {
95        let input: Vec<u8> = (0u8..200).collect();
96        let mut kmac = Kmac256::new(&KEY, TAGGED_APP);
97        for chunk in input.chunks(1) {
98            kmac.update(chunk);
99        }
100        let mut out = [0u8; 64];
101        kmac.finalize_into(&mut out);
102        assert_eq!(hex::encode(out), KMAC256_SAMPLE_6);
103    }
104
105    #[test]
106    fn kmac256_incremental_matches_one_shot() {
107        let input: Vec<u8> = (0u8..200).collect();
108
109        let mut one_shot = [0u8; 64];
110        Kmac256::mac(&KEY, &input, TAGGED_APP, &mut one_shot);
111
112        let mut kmac = Kmac256::new(&KEY, TAGGED_APP);
113        for chunk in input.chunks(7) {
114            kmac.update(chunk);
115        }
116        let mut incremental = [0u8; 64];
117        kmac.finalize_into(&mut incremental);
118        assert_eq!(incremental, one_shot);
119    }
120
121    #[test]
122    fn wycheproof_kmac256() {
123        let data: serde_json::Value = serde_json::from_str(include_str!(
124            "../../testdata/wycheproof/testvectors_v1/kmac256_no_customization_test.json"
125        ))
126        .unwrap();
127        let mut valid_tested = 0u64;
128        for group in data["testGroups"].as_array().unwrap() {
129            for test in group["tests"].as_array().unwrap() {
130                let key_hex = test["key"].as_str().unwrap();
131                let msg_hex = test["msg"].as_str().unwrap();
132                let tag_hex = test["tag"].as_str().unwrap();
133                let result = test["result"].as_str().unwrap();
134
135                let key = hex::decode(key_hex).unwrap();
136                let msg = hex::decode(msg_hex).unwrap();
137                let expected_tag = hex::decode(tag_hex).unwrap();
138
139                let mut out = vec![0u8; expected_tag.len()];
140                Kmac256::mac(&key, &msg, b"", &mut out);
141
142                if result == "valid" {
143                    assert_eq!(
144                        out.as_slice(),
145                        expected_tag.as_slice(),
146                        "wycheproof KMAC256 tcId={}",
147                        test["tcId"]
148                    );
149                    valid_tested += 1;
150                } else if result == "acceptable" {
151                    if out.as_slice() != expected_tag.as_slice() {
152                        continue;
153                    }
154                    valid_tested += 1;
155                }
156            }
157        }
158        assert!(valid_tested > 0, "no valid KMAC256 wycheproof tests were run");
159    }
160}