1use super::shake256::{CShake256, left_encode, right_encode};
2use crate::Xof;
3
4const KMAC256_RATE: usize = 136;
5
6#[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 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 const KMAC256_SAMPLE_4: &str = "20c570c31346f703c9ac36c61c03cb64c3970d0cfc787e9b79599d273a68d2f7f69d4cc3de9d104a351689f27cf6f5951f0103f33f4f24871024d9c27773a8dd";
97 const KMAC256_SAMPLE_5: &str = "75358cf39e41494e949707927cee0af20a3ff553904c86b08f21cc414bcfd691589d27cf5e15369cbbff8b9a4c2eb17800855d0235ff635da82533ec6b759b69";
99 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}