hash_based_signatures/signature/
q_indexed_signature.rs

1use crate::merkle_tree::{MerkleProof, MerkleTree};
2use crate::signature::winternitz::d::D;
3use crate::signature::winternitz::{WinternitzKey, WinternitzSignature, WinternitzSignatureScheme};
4use crate::signature::{HashType, SignatureScheme};
5use rand::prelude::*;
6use rand_chacha::ChaCha20Rng;
7use serde::{Deserialize, Serialize};
8
9/// The q-indexed signature scheme, as described in Section 14.6.1
10/// in the [textbook](http://toc.cryptobook.us/) by Boneh & Shoup.
11///
12/// It instantiates `q` one-time signatures schemes (currently `WinternitzSignatureScheme`)
13/// and uses it to sign up to `q` messages.
14/// To shrink the public key to a single hash, a `MerkleTree` is used:
15/// The signatures contains the one-time public key that was used, along with a Merkle
16/// proof.
17///
18/// # Examples
19///
20/// ```
21/// use hash_based_signatures::signature::q_indexed_signature::QIndexedSignatureScheme;
22/// use hash_based_signatures::signature::SignatureScheme;
23/// use hash_based_signatures::signature::winternitz::d::D;
24///
25/// let mut signature_scheme = QIndexedSignatureScheme::new(2, [0; 32], D::new(255));
26/// let signature0 = signature_scheme.sign((0, [0u8; 32]));
27/// let signature1 = signature_scheme.sign((1, [1u8; 32]));
28///
29/// assert!(QIndexedSignatureScheme::verify(
30///     signature_scheme.public_key(),
31///     (0, [0u8; 32]),
32///     &signature0
33/// ));
34/// assert!(QIndexedSignatureScheme::verify(
35///     signature_scheme.public_key(),
36///     (1, [1u8; 32]),
37///     &signature1
38/// ));
39/// ```
40#[derive(Clone)]
41pub struct QIndexedSignatureScheme {
42    one_time_signatures: Vec<WinternitzSignatureScheme>,
43    public_key_merkle_tree: MerkleTree<WinternitzKey>,
44}
45
46#[derive(PartialEq, Serialize, Deserialize)]
47pub struct QIndexedSignature {
48    /// Merkle proof used to verify that the used Winternitz public key
49    /// is actually valid.
50    /// Note that the used Winternitz public key itself is not included
51    /// in the signature, but can be computed from the signature and the
52    /// message. This saves a lot of bytes!
53    pub proof: MerkleProof<WinternitzKey>,
54
55    /// Winternitz signature of the data being signed
56    pub one_time_signature: WinternitzSignature,
57}
58
59impl QIndexedSignatureScheme {
60    /// Builds a q-indexed signature scheme from the given `seed`.
61    ///
62    /// # Panics
63    ///
64    /// Panics if `q` is not a power of two.
65    pub fn new(q: usize, seed: [u8; 32], d: D) -> Self {
66        let mut rng = ChaCha20Rng::from_seed(seed);
67        let mut seed_for_sub_scheme: [u8; 32] = [0; 32];
68        let mut one_time_signatures = Vec::new();
69        for _ in 0..q {
70            rng.fill_bytes(&mut seed_for_sub_scheme);
71            one_time_signatures.push(WinternitzSignatureScheme::new(seed_for_sub_scheme, d));
72        }
73
74        let public_keys: Vec<WinternitzKey> =
75            one_time_signatures.iter().map(|s| s.public_key()).collect();
76
77        let public_key_merkle_tree = MerkleTree::new(&public_keys);
78
79        Self {
80            one_time_signatures,
81            public_key_merkle_tree,
82        }
83    }
84}
85
86impl SignatureScheme<HashType, (usize, HashType), QIndexedSignature> for QIndexedSignatureScheme {
87    fn public_key(&self) -> HashType {
88        *self.public_key_merkle_tree.get_root_hash()
89    }
90
91    /// Signs a message.
92    ///
93    /// # Panics
94    ///
95    /// Panics if the scheme is used more than once to sign *different* messages with the
96    /// same index.
97    /// Note that there could still be a different instance with the same secret key,
98    /// which would not be detected.
99    fn sign(&mut self, message: (usize, HashType)) -> QIndexedSignature {
100        let (i, message) = message;
101        let proof = self.public_key_merkle_tree.get_proof(i);
102        QIndexedSignature {
103            proof,
104            one_time_signature: self.one_time_signatures[i].sign(message),
105        }
106    }
107
108    fn verify(pk: HashType, message: (usize, HashType), signature: &QIndexedSignature) -> bool {
109        let (i_m, message) = message;
110
111        if i_m != signature.proof.index {
112            return false;
113        }
114
115        // Since the Winternitz public key can actually be computed from the message
116        // and the signature, we don't explicitly store it, but instead compute it
117        // and verify the Merkle proof.
118        // Note that this means that we never call `WinternitzSignatureScheme::verify()`,
119        // which would be redundant.
120        match WinternitzSignatureScheme::public_key_from_message_and_signature(
121            message,
122            &signature.one_time_signature,
123        ) {
124            Err(_) => false,
125            Ok(winternitz_pk) => signature.proof.verify(pk, &winternitz_pk),
126        }
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use crate::signature::q_indexed_signature::QIndexedSignatureScheme;
133    use crate::signature::winternitz::d::D;
134    use crate::signature::SignatureScheme;
135
136    fn get_signature_scheme() -> QIndexedSignatureScheme {
137        let seed = [0u8; 32];
138        QIndexedSignatureScheme::new(4, seed, D::new(255))
139    }
140
141    #[test]
142    fn test_correct_signatures() {
143        let mut signature_scheme = get_signature_scheme();
144
145        let signature0 = signature_scheme.sign((0, [0u8; 32]));
146        assert!(QIndexedSignatureScheme::verify(
147            signature_scheme.public_key(),
148            (0, [0u8; 32]),
149            &signature0
150        ));
151
152        let signature3 = signature_scheme.sign((3, [3u8; 32]));
153        assert!(QIndexedSignatureScheme::verify(
154            signature_scheme.public_key(),
155            (3, [3u8; 32]),
156            &signature3
157        ))
158    }
159
160    #[test]
161    fn test_incorrect_signature() {
162        let mut signature_scheme = get_signature_scheme();
163        let signature = signature_scheme.sign((0, [0u8; 32]));
164        assert!(!QIndexedSignatureScheme::verify(
165            signature_scheme.public_key(),
166            (0, [1u8; 32]), // Different data
167            &signature
168        ));
169        assert!(!QIndexedSignatureScheme::verify(
170            signature_scheme.public_key(),
171            (1, [0u8; 32]), // Different index
172            &signature
173        ))
174    }
175
176    #[test]
177    fn test_can_sign_same_message() {
178        let mut signature_scheme = get_signature_scheme();
179        signature_scheme.sign((0, [0u8; 32]));
180        signature_scheme.sign((0, [0u8; 32]));
181    }
182}