hash_based_signatures/signature/
winternitz.rs1pub mod d;
2pub mod domination_free_function;
3
4use crate::signature::winternitz::d::D;
5use crate::signature::winternitz::domination_free_function::domination_free_function;
6use crate::signature::{HashType, SignatureScheme};
7use crate::utils::{bits_to_unsigned_ints, get_least_significant_bits, hash};
8use anyhow::{bail, Result};
9use rand::prelude::*;
10use rand_chacha::ChaCha20Rng;
11use rayon::prelude::*;
12use std::iter;
13
14pub type WinternitzKey = Vec<[u8; 32]>;
18
19pub type WinternitzSignature = (u64, Vec<[u8; 32]>);
22
23#[derive(Clone)]
47pub struct WinternitzSignatureScheme {
48 sk: WinternitzKey,
49 pk: WinternitzKey,
50 d: D,
51}
52
53fn hash_chain(input: HashType, start: u8, end: u8) -> HashType {
57 let mut current_hash_value = input;
58 let mut counter_buffer = [0u8; 32];
59
60 for i in start..end {
61 let index_bitstring = get_least_significant_bits(i as usize, 32);
63 let index_bytes = bits_to_unsigned_ints(&index_bitstring);
64 assert_eq!(index_bytes.len(), 4);
65 for i in 0..4 {
66 counter_buffer[i] = index_bytes[i];
67 }
68
69 current_hash_value = hash(&[counter_buffer, current_hash_value].concat());
70 }
71 current_hash_value
72}
73
74#[cfg(not(target_arch = "wasm32"))]
75fn hash_chain_parallel(
76 inputs: &Vec<HashType>,
77 starts: impl Iterator<Item = u8>,
78 ends: impl Iterator<Item = u8>,
79) -> Vec<HashType> {
80 let starts: Vec<u8> = starts.take(inputs.len()).collect();
82 let ends: Vec<u8> = ends.take(inputs.len()).collect();
83
84 inputs
85 .par_iter()
86 .zip(starts.par_iter())
87 .zip(ends.par_iter())
88 .map(|((input, start), end)| hash_chain(*input, *start, *end))
89 .collect()
90}
91
92#[cfg(target_arch = "wasm32")]
93fn hash_chain_parallel(
94 inputs: &Vec<HashType>,
95 starts: impl Iterator<Item = u8>,
96 ends: impl Iterator<Item = u8>,
97) -> Vec<HashType> {
98 let starts: Vec<u8> = starts.take(inputs.len()).collect();
100 let ends: Vec<u8> = ends.take(inputs.len()).collect();
101
102 inputs
103 .iter()
104 .zip(starts.iter())
105 .zip(ends.iter())
106 .map(|((input, start), end)| hash_chain(*input, *start, *end))
107 .collect()
108}
109
110impl WinternitzSignatureScheme {
111 pub fn new(seed: [u8; 32], d: D) -> Self {
113 let mut rng = ChaCha20Rng::from_seed(seed);
114
115 let mut buffer = [0u8; 32];
116 let mut sk = Vec::with_capacity(d.signature_and_key_size());
117
118 for _ in 0..d.signature_and_key_size() {
120 rng.fill_bytes(&mut buffer);
121 sk.push(buffer);
122 }
123 let pk = hash_chain_parallel(&sk, iter::repeat(0), iter::repeat(d.d as u8));
124
125 Self { sk, pk, d }
126 }
127
128 pub fn public_key_from_message_and_signature(
131 message: HashType,
132 signature: &WinternitzSignature,
133 ) -> Result<WinternitzKey> {
134 let (d, signature) = signature;
135 let d = D::try_from(*d)?;
136
137 let times_to_hash = domination_free_function(message, &d);
138
139 if times_to_hash.len() != signature.len() {
140 bail!("Signature has invalid length");
141 }
142
143 let expected_pk = hash_chain_parallel(
144 &signature,
145 times_to_hash.into_iter(),
146 iter::repeat(d.d as u8),
147 );
148
149 Ok(expected_pk)
150 }
151}
152
153impl SignatureScheme<WinternitzKey, HashType, WinternitzSignature> for WinternitzSignatureScheme {
154 fn public_key(&self) -> WinternitzKey {
155 self.pk.clone()
156 }
157
158 fn sign(&mut self, message: HashType) -> WinternitzSignature {
159 let times_to_hash = domination_free_function(message, &self.d);
160 assert_eq!(times_to_hash.len(), self.sk.len());
161
162 let signature = hash_chain_parallel(&self.sk, iter::repeat(0), times_to_hash.into_iter());
163
164 (self.d.d, signature)
165 }
166
167 fn verify(pk: WinternitzKey, message: HashType, signature: &WinternitzSignature) -> bool {
168 match WinternitzSignatureScheme::public_key_from_message_and_signature(message, signature) {
169 Ok(expected_public_key) => expected_public_key == pk,
170 Err(_) => false,
171 }
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use crate::signature::winternitz::d::D;
178 use crate::signature::winternitz::WinternitzSignatureScheme;
179 use crate::signature::SignatureScheme;
180
181 fn get_signature_scheme(d: D) -> WinternitzSignatureScheme {
182 let seed = [0u8; 32];
183 WinternitzSignatureScheme::new(seed, d)
184 }
185
186 #[test]
187 fn test_correct_signature_d1() {
188 let mut signature_scheme = get_signature_scheme(D::new(1));
189 let signature = signature_scheme.sign([1u8; 32]);
190 assert!(WinternitzSignatureScheme::verify(
191 signature_scheme.public_key(),
192 [1u8; 32],
193 &signature
194 ))
195 }
196
197 #[test]
198 fn test_correct_signature_d3() {
199 let mut signature_scheme = get_signature_scheme(D::new(3));
200 let signature = signature_scheme.sign([1u8; 32]);
201 assert!(WinternitzSignatureScheme::verify(
202 signature_scheme.public_key(),
203 [1u8; 32],
204 &signature
205 ))
206 }
207
208 #[test]
209 fn test_correct_signature_d15() {
210 let mut signature_scheme = get_signature_scheme(D::new(15));
211 let signature = signature_scheme.sign([1u8; 32]);
212 assert!(WinternitzSignatureScheme::verify(
213 signature_scheme.public_key(),
214 [1u8; 32],
215 &signature
216 ))
217 }
218
219 #[test]
220 fn test_correct_signature_d255() {
221 let mut signature_scheme = get_signature_scheme(D::new(255));
222 let signature = signature_scheme.sign([1u8; 32]);
223 assert!(WinternitzSignatureScheme::verify(
224 signature_scheme.public_key(),
225 [1u8; 32],
226 &signature
227 ))
228 }
229}