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