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 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 const KMAC256_SAMPLE_4: &str = "20c570c31346f703c9ac36c61c03cb64c3970d0cfc787e9b79599d273a68d2f7f69d4cc3de9d104a351689f27cf6f5951f0103f33f4f24871024d9c27773a8dd";
73 const KMAC256_SAMPLE_5: &str = "75358cf39e41494e949707927cee0af20a3ff553904c86b08f21cc414bcfd691589d27cf5e15369cbbff8b9a4c2eb17800855d0235ff635da82533ec6b759b69";
75 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}