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