1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#![cfg_attr(not(feature = "alloc"), no_std)]
#![doc = include_str!("../README.md")]
#![warn(clippy::pedantic)] // Be pedantic by default
//#![allow(non_snake_case)] // Allow notation matching the spec
#![allow(clippy::module_name_repetitions)] // There are many types of signature and otherwise this gets confusing
#![allow(clippy::similar_names)] // TODO: Consider resolving these
#![allow(clippy::clone_on_copy)] // Be explicit about moving data
#![deny(missing_docs)] // Require all public interfaces to be documented

//! # Usage
//! This crate implements the Stateless Hash-based Digital Signature Algorithm (SLH-DSA) based on the draft
//! standard by NIST in FIPS-205. SLH-DSA (based on the SPHINCS+ submission) is a signature algorithm designed
//! to be resistant to quantum computers.
//!
//! While the API exposed by SLH-DSA is the same as conventional signature schemes, it is important
//! to note that the signatures produced by the algorithm are much larger than classical schemes like EdDSA,
//! ranging from over 7KB for the smallest parameter set to nearly 50KB at the largest
//!
//! This crate currently allocates signatures and intermediate values on the stack, which may cause problems for
//! environments with limited stack space.
//!
//!
//! ```
//! use slh_dsa::*;
//! use signature::*;
//!
//! let mut rng = rand::thread_rng();
//!
//! // Generate a signing key using the SHAKE128f parameter set
//! let sk = SigningKey::<Shake128f>::new(&mut rng);
//!
//! // Generate the corresponding public key
//! let vk = sk.verifying_key();
//!
//! // Serialize the verifying key and distribute
//! let vk_bytes = vk.to_bytes();
//!
//! // Sign a message
//! let message = b"Hello world";
//! let sig = sk.sign_with_rng(&mut rng, message); // .sign() can be used for deterministic signatures
//!
//! // Deserialize a verifying key
//! let vk_deserialized = vk_bytes.try_into().unwrap();
//! assert_eq!(vk, vk_deserialized);
//!
//! assert!(vk_deserialized.verify(message, &sig).is_ok())
//! ```

pub use signature;

mod address;
mod fors;
mod hashes;
mod hypertree;
mod signature_encoding;
mod signing_key;
mod util;
mod verifying_key;
mod wots;
mod xmss;

pub use signature_encoding::*;
pub use signing_key::*;
pub use verifying_key::*;

use fors::ForsParams;
pub use hashes::*;

/// Specific parameters for each of the 12 FIPS parameter sets
#[allow(private_bounds)] // Intentionally un-usable type
pub trait ParameterSet:
    ForsParams + SigningKeyLen + VerifyingKeyLen + SignatureLen + PartialEq + Eq
{
    /// Human-readable name for parameter set, matching the FIPS-205 designations
    const NAME: &'static str;
}

#[cfg(test)]
mod tests {
    use super::*;
    use rand::Rng;
    use signature::*;

    fn test_sign_verify<P: ParameterSet>() {
        let mut rng = rand::thread_rng();
        let sk = SigningKey::<P>::new(&mut rng);
        let vk = sk.verifying_key();
        let msg = b"Hello, world!";
        let sig = sk.try_sign(msg).unwrap();
        vk.verify(msg, &sig).unwrap();
    }

    #[test]
    fn test_sign_verify_shake_128f() {
        test_sign_verify::<Shake128f>();
    }

    #[test]
    fn test_sign_verify_shake_128s() {
        test_sign_verify::<Shake128s>();
    }

    #[test]
    fn test_sign_verify_shake_192f() {
        test_sign_verify::<Shake192f>();
    }

    #[test]
    fn test_sign_verify_shake_192s() {
        test_sign_verify::<Shake192s>();
    }

    #[test]
    fn test_sign_verify_shake_256f() {
        test_sign_verify::<Shake256f>();
    }

    #[test]
    fn test_sign_verify_shake_256s() {
        test_sign_verify::<Shake256s>();
    }

    #[test]
    fn test_sign_verify_sha2_128f() {
        test_sign_verify::<Sha2_128f>();
    }

    #[test]
    fn test_sign_verify_sha2_128s() {
        test_sign_verify::<Sha2_128s>();
    }

    #[test]
    fn test_sign_verify_sha2_192f() {
        test_sign_verify::<Sha2_192f>();
    }

    #[test]
    fn test_sign_verify_sha2_192s() {
        test_sign_verify::<Sha2_192s>();
    }

    #[test]
    fn test_sign_verify_sha2_256f() {
        test_sign_verify::<Sha2_256f>();
    }

    #[test]
    fn test_sign_verify_sha2_256s() {
        test_sign_verify::<Sha2_256s>();
    }

    // Check signature fails on modified message
    #[test]
    fn test_sign_verify_shake_128f_fail_on_modified_message() {
        let mut rng = rand::thread_rng();
        let sk = SigningKey::<Shake128f>::new(&mut rng);
        let msg = b"Hello, world!";
        let modified_msg = b"Goodbye, world!";

        let sig = sk.try_sign(msg).unwrap();
        let vk = sk.verifying_key();
        assert!(vk.verify(msg, &sig).is_ok());
        assert!(vk.verify(modified_msg, &sig).is_err());
    }

    #[test]
    fn test_sign_verify_fail_with_wrong_verifying_key() {
        let mut rng = rand::thread_rng();
        let sk = SigningKey::<Shake128f>::new(&mut rng);
        let wrong_sk = SigningKey::<Shake128f>::new(&mut rng); // Generate a different signing key
        let msg = b"Hello, world!";

        let sig = sk.try_sign(msg).unwrap();
        let vk = sk.verifying_key();
        let wrong_vk = wrong_sk.verifying_key(); // Get the verifying key of the wrong signing key
        assert!(vk.verify(msg, &sig).is_ok());
        assert!(wrong_vk.verify(msg, &sig).is_err()); // This should fail because the verifying key does not match the signing key used
    }

    #[test]
    fn test_sign_verify_fail_on_modified_signature() {
        let mut rng = rand::thread_rng();
        let sk = SigningKey::<Shake128f>::new(&mut rng);
        let msg = b"Hello, world!";

        let mut sig_bytes = sk.try_sign(msg).unwrap().to_bytes();
        // Randomly modify one byte in the signature
        let sig_len = sig_bytes.len();
        let random_byte_index = rng.gen_range(0..sig_len);
        sig_bytes[random_byte_index] ^= 0xff; // Invert one byte to ensure it's different
        let sig = (&sig_bytes).into();

        let vk = sk.verifying_key();
        assert!(
            vk.verify(msg, &sig).is_err(),
            "Verification should fail with a modified signature"
        );
    }

    #[test]
    fn test_successive_signatures_not_equal() {
        let mut rng = rand::thread_rng();
        let sk = SigningKey::<Shake128f>::new(&mut rng);
        let msg = b"Hello, world!";

        let sig1 = sk.try_sign_with_rng(&mut rng, msg).unwrap();
        let sig2 = sk.try_sign_with_rng(&mut rng, msg).unwrap();

        assert_ne!(
            sig1, sig2,
            "Two successive randomized signatures over the same message should not be equal"
        );
    }
}