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