hash_based_signatures/signature/
basic_lamport.rs

1use rand::prelude::*;
2use rand_chacha::ChaCha20Rng;
3use serde::{Deserialize, Serialize};
4use serde_big_array::BigArray;
5
6use crate::signature::{HashType, SignatureScheme};
7use crate::utils::hash;
8
9pub type BasicLamportKey = [[[u8; 32]; 2]; 256];
10
11// Needs to be wrapped in a struct, so that we can derive the (de)serialization traits
12#[derive(PartialEq, Serialize, Deserialize)]
13pub struct BasicLamportSignature {
14    #[serde(with = "BigArray")]
15    preimages: [[u8; 32]; 256],
16}
17
18/// The basic Lamport one-time signature, as described in Section 14.1
19/// in the [textbook](http://toc.cryptobook.us/) by Boneh & Shoup.
20///
21/// In short:
22/// - The key generation algorithm generates `2 * 256` random integers, used as the signing key.
23///   Also, hashes of these integers are published as the public key.
24/// - To sign a 256-bit message, the 256 integers of the signing key (corresponding to the
25///   256 bits of the message) are released as the signature
26/// - The verifier can easily verify that they hash to the right values in the public key
27///
28/// # Examples
29///
30/// ```
31/// use hash_based_signatures::signature::basic_lamport::BasicLamportSignatureScheme;
32/// use hash_based_signatures::signature::SignatureScheme;
33///
34/// let mut signature_scheme = BasicLamportSignatureScheme::new([0u8; 32]);
35/// let signature = signature_scheme.sign([1u8; 32]);
36/// assert!(BasicLamportSignatureScheme::verify(
37///     signature_scheme.public_key(),
38///     [1u8; 32],
39///     &signature
40/// ))
41/// ```
42#[derive(Clone)]
43pub struct BasicLamportSignatureScheme {
44    sk: BasicLamportKey,
45    pk: BasicLamportKey,
46    message: Option<HashType>,
47}
48
49impl BasicLamportSignatureScheme {
50    /// Generates a new one-time key pair from the given `seed` and instantiates the scheme.
51    pub fn new(seed: [u8; 32]) -> Self {
52        let mut rng = ChaCha20Rng::from_seed(seed);
53        let mut sk = [[[0; 32]; 2]; 256];
54
55        // create secrets
56        for bit_to_sign in 0..256 {
57            for bit in 0..2 {
58                rng.fill_bytes(&mut sk[bit_to_sign][bit]);
59            }
60        }
61
62        // hash secrets to public keys
63        let mut pk = [[[0; 32]; 2]; 256];
64        for bit_to_sign in 0..256 {
65            for bit in 0..2 {
66                pk[bit_to_sign][bit] = hash(&sk[bit_to_sign][bit]);
67            }
68        }
69        Self {
70            pk,
71            sk,
72            message: None,
73        }
74    }
75}
76
77impl SignatureScheme<BasicLamportKey, HashType, BasicLamportSignature>
78    for BasicLamportSignatureScheme
79{
80    fn public_key(&self) -> BasicLamportKey {
81        self.pk
82    }
83
84    /// Signs a message.
85    ///
86    /// # Panics
87    ///
88    /// Panics if the scheme is used more than once to sign *different* messages.
89    /// Note that there could still be a different instance with the same secret key,
90    /// which would not be detected.
91    fn sign(&mut self, message: HashType) -> BasicLamportSignature {
92        if let Some(existing_message) = self.message {
93            if existing_message != message {
94                panic!("One-time signature has been used to sign more than one message!")
95            }
96        }
97        self.message = Some(message);
98
99        let mut signature: [[u8; 32]; 256] = [[0; 32]; 256];
100        for byte_index in 0..32 {
101            let byte = message[byte_index];
102            for local_bit_index in 0..8 {
103                let bit_index = byte_index * 8 + local_bit_index;
104                if byte & (1 << local_bit_index) != 0 {
105                    signature[bit_index] = self.sk[bit_index][1];
106                } else {
107                    signature[bit_index] = self.sk[bit_index][0];
108                }
109            }
110        }
111        BasicLamportSignature {
112            preimages: signature,
113        }
114    }
115
116    fn verify(pk: BasicLamportKey, message: HashType, signature: &BasicLamportSignature) -> bool {
117        let mut is_correct = true;
118        for byte_index in 0..32 {
119            let byte = message[byte_index];
120            for local_bit_index in 0..8 {
121                let bit_index = byte_index * 8 + local_bit_index;
122                let hash = hash(&signature.preimages[bit_index]);
123                let pk_index_to_expect = (byte & (1 << local_bit_index) != 0) as usize;
124                is_correct &= hash == pk[bit_index][pk_index_to_expect];
125            }
126        }
127        is_correct
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use crate::signature::basic_lamport::{BasicLamportSignature, BasicLamportSignatureScheme};
134    use crate::signature::SignatureScheme;
135
136    fn get_signature_scheme() -> BasicLamportSignatureScheme {
137        let seed = [0u8; 32];
138        BasicLamportSignatureScheme::new(seed)
139    }
140
141    #[test]
142    fn test_correct_signature() {
143        let mut signature_scheme = get_signature_scheme();
144        let signature = signature_scheme.sign([1u8; 32]);
145        assert!(BasicLamportSignatureScheme::verify(
146            signature_scheme.public_key(),
147            [1u8; 32],
148            &signature
149        ))
150    }
151
152    #[test]
153    fn test_incorrect_signature() {
154        let signature_scheme = get_signature_scheme();
155        let incorrect_signature = BasicLamportSignature {
156            preimages: [[0u8; 32]; 256],
157        };
158        assert!(!BasicLamportSignatureScheme::verify(
159            signature_scheme.public_key(),
160            [1u8; 32],
161            &incorrect_signature
162        ))
163    }
164
165    #[test]
166    fn test_can_sign_same_message() {
167        let mut signature_scheme = get_signature_scheme();
168        signature_scheme.sign([1u8; 32]);
169        signature_scheme.sign([1u8; 32]);
170    }
171
172    #[test]
173    #[should_panic]
174    fn test_cant_sign_different_messages() {
175        let mut signature_scheme = get_signature_scheme();
176        signature_scheme.sign([1u8; 32]);
177        signature_scheme.sign([2u8; 32]);
178    }
179}