mithril_stm/signature_scheme/unique_schnorr_signature/
signing_key.rs1use anyhow::{Context, anyhow};
2use rand_core::{CryptoRng, RngCore};
3use serde::{Deserialize, Serialize};
4
5use super::{
6 BaseFieldElement, PrimeOrderProjectivePoint, ProjectivePoint, ScalarFieldElement,
7 SchnorrVerificationKey, UniqueSchnorrSignature, UniqueSchnorrSignatureError,
8 compute_truncated_digest,
9};
10use crate::StmResult;
11
12#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
14pub struct SchnorrSigningKey(pub(crate) ScalarFieldElement);
15
16impl SchnorrSigningKey {
17 pub fn generate<R: RngCore + CryptoRng>(rng: &mut R) -> StmResult<Self> {
19 let scalar = ScalarFieldElement::new_random_nonzero_scalar(rng)
20 .with_context(|| "Failed to generate a non zero Schnorr signing key.")?;
21 Ok(SchnorrSigningKey(scalar))
22 }
23
24 pub fn sign<R: RngCore + CryptoRng>(
47 &self,
48 msg: &[u8],
49 rng: &mut R,
50 ) -> StmResult<UniqueSchnorrSignature> {
51 let prime_order_generator_point = PrimeOrderProjectivePoint::create_generator();
53 let verification_key = SchnorrVerificationKey::new_from_signing_key(self.clone())
54 .with_context(|| "Could not generate verification key from signing key.")?;
55
56 let msg_hash_point = ProjectivePoint::hash_to_projective_point(msg);
58
59 let commitment_point = self.0 * msg_hash_point;
60
61 let random_scalar = ScalarFieldElement::new_random_nonzero_scalar(rng)
62 .with_context(|| "Random scalar generation failed during signing.")?;
63
64 let random_point_1 = random_scalar * msg_hash_point;
65 let random_point_2 = random_scalar * prime_order_generator_point;
66
67 let points_coordinates: Vec<BaseFieldElement> = [
71 msg_hash_point,
72 ProjectivePoint::from(verification_key.0),
73 commitment_point,
74 random_point_1,
75 ProjectivePoint::from(random_point_2),
76 ]
77 .iter()
78 .flat_map(|point| {
79 let (u, v) = point.get_coordinates();
80 [u, v]
81 })
82 .collect();
83
84 let challenge = compute_truncated_digest(&points_coordinates);
85 let challenge_times_sk = challenge * self.0;
86 let response = random_scalar - challenge_times_sk;
87
88 Ok(UniqueSchnorrSignature {
89 commitment_point,
90 response,
91 challenge,
92 })
93 }
94
95 pub fn to_bytes(&self) -> [u8; 32] {
97 self.0.to_bytes()
98 }
99
100 pub fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
104 if bytes.len() < 32 {
105 return Err(anyhow!(UniqueSchnorrSignatureError::Serialization)).with_context(
106 || "Not enough bytes provided to re-construct a Schnorr signing key.",
107 );
108 }
109 let scalar_field_element = ScalarFieldElement::from_bytes(bytes)
110 .with_context(|| "Could not construct Schnorr signing key from given bytes.")?;
111 Ok(SchnorrSigningKey(scalar_field_element))
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use rand_chacha::ChaCha20Rng;
118 use rand_core::SeedableRng;
119
120 use crate::signature_scheme::SchnorrSigningKey;
121
122 #[test]
123 fn generate_signing_key() {
124 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
125 let sk = SchnorrSigningKey::generate(&mut rng);
126
127 assert!(sk.is_ok(), "Signing key generation should succeed");
128 }
129
130 #[test]
131 fn generate_different_keys() {
132 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
133 let sk1 = SchnorrSigningKey::generate(&mut rng).unwrap();
134 let sk2 = SchnorrSigningKey::generate(&mut rng).unwrap();
135
136 assert_ne!(sk1, sk2, "Different keys should be generated");
138 }
139
140 #[test]
141 fn from_bytes_not_enough_bytes() {
142 let bytes = vec![0u8; 31];
143 let result = SchnorrSigningKey::from_bytes(&bytes);
144
145 result.expect_err("Should fail with insufficient bytes");
146 }
147
148 #[test]
149 fn from_bytes_exact_size() {
150 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
151 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
152 let sk_bytes = sk.to_bytes();
153
154 let sk_restored = SchnorrSigningKey::from_bytes(&sk_bytes).unwrap();
155
156 assert_eq!(sk, sk_restored);
157 }
158
159 #[test]
160 fn from_bytes_extra_bytes() {
161 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
162 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
163 let sk_bytes = sk.to_bytes();
164
165 let mut extended_bytes = sk_bytes.to_vec();
166 extended_bytes.extend_from_slice(&[0xFF; 10]);
167
168 let sk_restored = SchnorrSigningKey::from_bytes(&extended_bytes).unwrap();
169
170 assert_eq!(sk, sk_restored);
171 }
172
173 #[test]
174 fn to_bytes_is_deterministic() {
175 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
176 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
177
178 let bytes1 = sk.to_bytes();
179 let bytes2 = sk.to_bytes();
180
181 assert_eq!(bytes1, bytes2, "to_bytes should be deterministic");
182 }
183
184 mod golden {
185 use super::*;
186
187 const GOLDEN_BYTES: &[u8; 32] = &[
188 126, 191, 239, 197, 88, 151, 248, 254, 187, 143, 86, 35, 29, 62, 90, 13, 196, 71, 234,
189 5, 90, 124, 205, 194, 51, 192, 228, 133, 25, 140, 157, 7,
190 ];
191
192 fn golden_value() -> SchnorrSigningKey {
193 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
194 SchnorrSigningKey::generate(&mut rng).unwrap()
195 }
196
197 #[test]
198 fn golden_conversions() {
199 let value = SchnorrSigningKey::from_bytes(GOLDEN_BYTES)
200 .expect("This from bytes should not fail");
201 assert_eq!(golden_value().0, value.0);
202
203 let serialized = SchnorrSigningKey::to_bytes(&value);
204 let golden_serialized = SchnorrSigningKey::to_bytes(&golden_value());
205 assert_eq!(golden_serialized, serialized);
206 }
207 }
208
209 mod golden_json {
210 use super::*;
211
212 const GOLDEN_JSON: &str = r#"[126, 191, 239, 197, 88, 151, 248, 254, 187, 143, 86, 35, 29, 62, 90, 13, 196, 71, 234, 5, 90, 124, 205, 194, 51, 192, 228, 133, 25, 140, 157, 7]"#;
213
214 fn golden_value() -> SchnorrSigningKey {
215 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
216 SchnorrSigningKey::generate(&mut rng).unwrap()
217 }
218
219 #[test]
220 fn golden_conversions() {
221 let value = serde_json::from_str(GOLDEN_JSON)
222 .expect("This JSON deserialization should not fail");
223 assert_eq!(golden_value(), value);
224
225 let serialized =
226 serde_json::to_string(&value).expect("This JSON serialization should not fail");
227 let golden_serialized = serde_json::to_string(&golden_value())
228 .expect("This JSON serialization should not fail");
229 assert_eq!(golden_serialized, serialized);
230 }
231 }
232}