hash_based_signatures/signature/
winternitz.rs

1pub 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
14/// Private or Public key.
15/// The length depends on Winternitz parameter `d` and is roughly
16/// `256 / log2(d)`.
17pub type WinternitzKey = Vec<[u8; 32]>;
18
19/// Winternitz signature.
20/// The first element is Winternitz parameter `d`, the second is the actual signature.
21pub type WinternitzSignature = (u64, Vec<[u8; 32]>);
22
23/// Winternitz signatures, as described in Section 14.3
24/// in the [textbook](http://toc.cryptobook.us/) by Boneh & Shoup.
25///
26/// To create a one-time signature, the scheme hashes secret key values
27/// a number of times, as determined by the `domination_free_function`.
28/// The parameter `d` trades off signature size (the higher, the smaller the signature)
29/// and computation time (the higher, the longer the time).
30///
31/// # Examples
32///
33/// ```
34/// use hash_based_signatures::signature::SignatureScheme;
35/// use hash_based_signatures::signature::winternitz::d::D;
36/// use hash_based_signatures::signature::winternitz::WinternitzSignatureScheme;
37///
38/// let mut signature_scheme = WinternitzSignatureScheme::new([0u8; 32], D::new(15));
39/// let signature0 = signature_scheme.sign([0u8; 32]);
40/// assert!(WinternitzSignatureScheme::verify(
41///     signature_scheme.public_key(),
42///     [0u8; 32],
43///     &signature0
44/// ));
45/// ```
46#[derive(Clone)]
47pub struct WinternitzSignatureScheme {
48    sk: WinternitzKey,
49    pk: WinternitzKey,
50    d: D,
51}
52
53/// Computes the hash chain of a given (intermediate) input.
54/// To do so, hash `i` is computed as `Sha256(i, <input>)`, with `i` going from
55/// `start` (inclusive) to `end` (exclusive).
56fn 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        // Encode i as a 32-bit value into the buffer
62        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    // Materialize starts and ends, to allow for parallelization
81    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    // Same as above, but using `iter()` instead of `par_iter()` to avoid spawning threads.
99    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    /// Builds a Winternitz signature scheme from the given `seed`.
112    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        // create secrets
119        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    /// Given a message and signature, computes the public key belonging to the private
129    /// key that signed the message.
130    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}