1use alloc::vec::Vec;
23use tessera::{
24 Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Sha224, Sha256, Sha384, Sha512, Sha512_224, Sha512_256, Shake128,
25 Shake256, Xof,
26};
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq)]
32pub enum PreHash {
33 Sha256,
35 Sha384,
37 Sha512,
39 Sha224,
41 Sha512_224,
43 Sha512_256,
45 Sha3_224,
47 Sha3_256,
49 Sha3_384,
51 Sha3_512,
53 Shake128,
55 Shake256,
57}
58
59impl PreHash {
60 pub(crate) fn oid(self) -> &'static [u8] {
65 match self {
68 PreHash::Sha256 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01],
69 PreHash::Sha384 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02],
70 PreHash::Sha512 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03],
71 PreHash::Sha224 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04],
72 PreHash::Sha512_224 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x05],
73 PreHash::Sha512_256 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x06],
74 PreHash::Sha3_224 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x07],
75 PreHash::Sha3_256 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08],
76 PreHash::Sha3_384 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09],
77 PreHash::Sha3_512 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0A],
78 PreHash::Shake128 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0B],
79 PreHash::Shake256 => &[0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0C],
80 }
81 }
82
83 pub(crate) fn hash(self, m: &[u8]) -> Vec<u8> {
86 fn fixed<H: Digest>(m: &[u8]) -> Vec<u8> {
87 let mut out = alloc::vec![0u8; H::OUTPUT_LEN];
88 H::digest(m, &mut out);
89 out
90 }
91 fn xof<X: Xof>(m: &[u8], out_len: usize) -> Vec<u8> {
92 let mut x = X::new();
93 x.update(m);
94 let mut out = alloc::vec![0u8; out_len];
95 x.squeeze(&mut out);
96 out
97 }
98 match self {
99 PreHash::Sha256 => fixed::<Sha256>(m),
100 PreHash::Sha384 => fixed::<Sha384>(m),
101 PreHash::Sha512 => fixed::<Sha512>(m),
102 PreHash::Sha224 => fixed::<Sha224>(m),
103 PreHash::Sha512_224 => fixed::<Sha512_224>(m),
104 PreHash::Sha512_256 => fixed::<Sha512_256>(m),
105 PreHash::Sha3_224 => fixed::<Sha3_224>(m),
106 PreHash::Sha3_256 => fixed::<Sha3_256>(m),
107 PreHash::Sha3_384 => fixed::<Sha3_384>(m),
108 PreHash::Sha3_512 => fixed::<Sha3_512>(m),
109 PreHash::Shake128 => xof::<Shake128>(m, 32),
110 PreHash::Shake256 => xof::<Shake256>(m, 64),
111 }
112 }
113}
114
115pub(crate) fn m_prime_pure(ctx: &[u8], msg: &[u8]) -> Vec<u8> {
121 let mut out = Vec::with_capacity(2 + ctx.len() + msg.len());
122 out.push(0x00);
123 out.push(ctx.len() as u8);
124 out.extend_from_slice(ctx);
125 out.extend_from_slice(msg);
126 out
127}
128
129pub(crate) fn m_prime_prehash(ctx: &[u8], ph: PreHash, msg: &[u8]) -> Vec<u8> {
134 let oid = ph.oid();
135 let digest = ph.hash(msg);
136 let mut out = Vec::with_capacity(2 + ctx.len() + oid.len() + digest.len());
137 out.push(0x01);
138 out.push(ctx.len() as u8);
139 out.extend_from_slice(ctx);
140 out.extend_from_slice(oid);
141 out.extend_from_slice(&digest);
142 out
143}
144
145#[cfg(test)]
146mod tests {
147 use super::*;
148
149 #[test]
150 fn oids_are_11_byte_der_tlv() {
151 for ph in [
152 PreHash::Sha256,
153 PreHash::Sha384,
154 PreHash::Sha512,
155 PreHash::Sha224,
156 PreHash::Sha512_224,
157 PreHash::Sha512_256,
158 PreHash::Sha3_224,
159 PreHash::Sha3_256,
160 PreHash::Sha3_384,
161 PreHash::Sha3_512,
162 PreHash::Shake128,
163 PreHash::Shake256,
164 ] {
165 let oid = ph.oid();
166 assert_eq!(oid.len(), 11);
167 assert_eq!(oid[0], 0x06); assert_eq!(oid[1], 0x09); assert_eq!(&oid[2..10], &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02]);
170 }
171 assert_eq!(PreHash::Sha256.oid()[10], 0x01);
173 assert_eq!(PreHash::Sha512.oid()[10], 0x03);
174 assert_eq!(PreHash::Shake128.oid()[10], 0x0B);
175 }
176
177 #[test]
178 fn shake_prehash_output_lengths() {
179 assert_eq!(PreHash::Shake128.hash(b"x").len(), 32); assert_eq!(PreHash::Shake256.hash(b"x").len(), 64); assert_eq!(PreHash::Sha256.hash(b"x").len(), 32);
182 assert_eq!(PreHash::Sha512.hash(b"x").len(), 64);
183 }
184
185 #[test]
186 fn pure_m_prime_layout() {
187 let mp = m_prime_pure(b"ctx", b"msg");
188 assert_eq!(mp[0], 0x00);
189 assert_eq!(mp[1], 3);
190 assert_eq!(&mp[2..5], b"ctx");
191 assert_eq!(&mp[5..], b"msg");
192 }
193
194 #[test]
195 fn prehash_m_prime_layout() {
196 let mp = m_prime_prehash(b"", PreHash::Sha256, b"msg");
197 assert_eq!(mp[0], 0x01);
198 assert_eq!(mp[1], 0); assert_eq!(&mp[2..13], PreHash::Sha256.oid());
200 assert_eq!(mp.len(), 2 + 11 + 32);
201 }
202
203 fn hexd(s: &str) -> Vec<u8> {
204 (0..s.len())
205 .step_by(2)
206 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
207 .collect()
208 }
209
210 #[test]
211 fn prehash_m_prime_matches_fips_reference() {
212 let refs: &[(PreHash, &str)] = &[
216 (
217 PreHash::Sha384,
218 "01000609608648016503040202cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7",
219 ),
220 (
221 PreHash::Shake256,
222 "0100060960864801650304020c483366601360a8771c6863080cc4114d8db44530f8f1e1ee4f94ea37e78b5739d5a15bef186a5386c75744c0527e1faa9f8726e462a12a4feb06bd8801e751e4",
223 ),
224 (
225 PreHash::Sha3_224,
226 "01000609608648016503040207e642824c3f8cf24ad09234ee7d3c766fc9a3a5168d0c94ad73b46fdf",
227 ),
228 (
229 PreHash::Sha3_256,
230 "010006096086480165030402083a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532",
231 ),
232 (
233 PreHash::Sha3_384,
234 "01000609608648016503040209ec01498288516fc926459f58e2c6ad8df9b473cb0fc08c2596da7cf0e49be4b298d88cea927ac7f539f1edf228376d25",
235 ),
236 (
237 PreHash::Sha3_512,
238 "0100060960864801650304020ab751850b1a57168a5693cd924b6b096e08f621827444f70d884f5d0240d2712e10e116e9192af3c91a7ec57647e3934057340b4cf408d5a56592f8274eec53f0",
239 ),
240 (
241 PreHash::Sha224,
242 "0100060960864801650304020423097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7",
243 ),
244 (
245 PreHash::Sha256,
246 "01000609608648016503040201ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
247 ),
248 (
249 PreHash::Sha512,
250 "01000609608648016503040203ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
251 ),
252 (
253 PreHash::Sha512_224,
254 "010006096086480165030402054634270f707b6a54daae7530460842e20e37ed265ceee9a43e8924aa",
255 ),
256 (
257 PreHash::Sha512_256,
258 "0100060960864801650304020653048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23",
259 ),
260 (
261 PreHash::Shake128,
262 "0100060960864801650304020b5881092dd818bf5cf8a3ddb793fbcba74097d5c526a6d35f97b83351940f2cc8",
263 ),
264 ];
265 for (ph, want) in refs {
266 assert_eq!(m_prime_prehash(b"", *ph, b"abc"), hexd(want), "{ph:?} M' mismatch");
267 }
268 }
269}