1use anyhow::{Context, anyhow};
2use serde::{Deserialize, Serialize};
3
4use crate::StmResult;
5
6use super::{
7 BaseFieldElement, PrimeOrderProjectivePoint, ProjectivePoint, ScalarFieldElement,
8 SchnorrVerificationKey, UniqueSchnorrSignatureError, compute_poseidon_digest,
9};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17pub struct UniqueSchnorrSignature {
18 pub(crate) commitment_point: ProjectivePoint,
20 pub(crate) response: ScalarFieldElement,
22 pub(crate) challenge: BaseFieldElement,
24}
25
26impl UniqueSchnorrSignature {
27 pub fn verify(&self, msg: &[u8], verification_key: &SchnorrVerificationKey) -> StmResult<()> {
47 verification_key
49 .is_valid()
50 .with_context(|| "Signature verification failed due to invalid verification key")?;
51
52 let prime_order_generator_point = PrimeOrderProjectivePoint::create_generator();
53
54 let msg_hash_point = ProjectivePoint::hash_to_projective_point(msg)?;
56
57 let challenge_as_scalar = ScalarFieldElement::from_base_field(&self.challenge)?;
59 let random_point_1_recomputed =
60 self.response * msg_hash_point + challenge_as_scalar * self.commitment_point;
61
62 let random_point_2_recomputed =
64 self.response * prime_order_generator_point + challenge_as_scalar * verification_key.0;
65
66 let points_coordinates: Vec<BaseFieldElement> = [
69 msg_hash_point,
70 ProjectivePoint::from(verification_key.0),
71 self.commitment_point,
72 random_point_1_recomputed,
73 ProjectivePoint::from(random_point_2_recomputed),
74 ]
75 .iter()
76 .flat_map(|point| {
77 let (u, v) = point.get_coordinates();
78 [u, v]
79 })
80 .collect();
81
82 let challenge_recomputed = compute_poseidon_digest(&points_coordinates);
83
84 if challenge_recomputed != self.challenge {
85 return Err(anyhow!(UniqueSchnorrSignatureError::SignatureInvalid(
86 Box::new(*self)
87 )));
88 }
89
90 Ok(())
91 }
92
93 pub fn to_bytes(self) -> [u8; 96] {
95 let mut out = [0; 96];
96 out[0..32].copy_from_slice(&self.commitment_point.to_bytes());
97 out[32..64].copy_from_slice(&self.response.to_bytes());
98 out[64..96].copy_from_slice(&self.challenge.to_bytes());
99
100 out
101 }
102
103 pub fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
105 if bytes.len() < 96 {
106 return Err(anyhow!(UniqueSchnorrSignatureError::Serialization))
107 .with_context(|| "Not enough bytes provided to create a signature.");
108 }
109
110 let commitment_point = ProjectivePoint::from_bytes(
111 bytes
112 .get(0..32)
113 .ok_or(UniqueSchnorrSignatureError::Serialization)
114 .with_context(|| "Could not get the bytes of `commitment_point`")?,
115 )
116 .with_context(|| "Could not convert bytes to `commitment_point`")?;
117
118 let response = ScalarFieldElement::from_bytes(
119 bytes
120 .get(32..64)
121 .ok_or(UniqueSchnorrSignatureError::Serialization)
122 .with_context(|| "Could not get the bytes of `response`")?,
123 )
124 .with_context(|| "Could not convert the bytes to `response`")?;
125
126 let challenge = BaseFieldElement::from_bytes(
127 bytes
128 .get(64..96)
129 .ok_or(UniqueSchnorrSignatureError::Serialization)
130 .with_context(|| "Could not get the bytes of `challenge`")?,
131 )
132 .with_context(|| "Could not convert bytes to `challenge`")?;
133
134 Ok(Self {
135 commitment_point,
136 response,
137 challenge,
138 })
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use rand_chacha::ChaCha20Rng;
145 use rand_core::SeedableRng;
146
147 use crate::signature_scheme::{
148 SchnorrSigningKey, SchnorrVerificationKey, UniqueSchnorrSignature,
149 };
150
151 #[test]
152 fn valid_signature_verification() {
153 let msg = vec![0, 0, 0, 1];
154 let seed = [0u8; 32];
155 let mut rng = ChaCha20Rng::from_seed(seed);
156 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
157 let vk = SchnorrVerificationKey::new_from_signing_key(sk.clone()).unwrap();
158
159 let sig = sk.sign(&msg, &mut rng).unwrap();
160
161 sig.verify(&msg, &vk)
162 .expect("Valid signature should verify successfully");
163 }
164
165 #[test]
166 fn invalid_signature() {
167 let msg = vec![0, 0, 0, 1];
168 let msg2 = vec![0, 0, 0, 2];
169 let seed = [0u8; 32];
170 let mut rng = ChaCha20Rng::from_seed(seed);
171 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
172 let vk = SchnorrVerificationKey::new_from_signing_key(sk.clone()).unwrap();
173 let sk2 = SchnorrSigningKey::generate(&mut rng).unwrap();
174 let vk2 = SchnorrVerificationKey::new_from_signing_key(sk2).unwrap();
175
176 let sig = sk.sign(&msg, &mut rng).unwrap();
177 let sig2 = sk.sign(&msg2, &mut rng).unwrap();
178
179 let result1 = sig.verify(&msg, &vk2);
181 let result2 = sig2.verify(&msg, &vk);
182
183 result1.expect_err("Wrong verification key used, test should fail.");
184 result2.expect_err("Wrong message used, test should fail.");
186 }
187
188 #[test]
189 fn from_bytes_signature_not_enough_bytes() {
190 let msg = vec![0u8; 95];
191 let result = UniqueSchnorrSignature::from_bytes(&msg);
192 result.expect_err("Not enough bytes.");
193 }
194
195 #[test]
196 fn from_bytes_signature_exact_size() {
197 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
198 let msg = vec![1, 2, 3];
199 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
200
201 let sig = sk.sign(&msg, &mut rng).unwrap();
202 let sig_bytes: [u8; 96] = sig.to_bytes();
203
204 let sig_restored = UniqueSchnorrSignature::from_bytes(&sig_bytes).unwrap();
205 assert_eq!(sig, sig_restored);
206 }
207
208 #[test]
209 fn from_bytes_signature_extra_bytes() {
210 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
211 let msg = vec![1, 2, 3];
212 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
213
214 let sig = sk.sign(&msg, &mut rng).unwrap();
215 let sig_bytes: [u8; 96] = sig.to_bytes();
216
217 let mut extended_bytes = sig_bytes.to_vec();
218 extended_bytes.extend_from_slice(&[0xFF; 10]);
219
220 let sig_restored = UniqueSchnorrSignature::from_bytes(&extended_bytes).unwrap();
221 assert_eq!(sig, sig_restored);
222 }
223
224 #[test]
225 fn to_bytes_is_deterministic() {
226 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
227 let msg = vec![1, 2, 3];
228 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
229
230 let sig = sk.sign(&msg, &mut rng).unwrap();
231
232 let bytes1 = sig.to_bytes();
234 let bytes2 = sig.to_bytes();
235
236 assert_eq!(bytes1, bytes2);
237 }
238
239 #[test]
240 fn signature_roundtrip_preserves_verification() {
241 let mut rng = ChaCha20Rng::from_seed([42u8; 32]);
242 let msg = vec![5, 6, 7, 8, 9];
243 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
244 let vk = SchnorrVerificationKey::new_from_signing_key(sk.clone()).unwrap();
245
246 let sig = sk.sign(&msg, &mut rng).unwrap();
248 sig.verify(&msg, &vk).expect("Original signature should verify");
249
250 let sig_bytes = sig.to_bytes();
252 let sig_restored = UniqueSchnorrSignature::from_bytes(&sig_bytes).unwrap();
253
254 sig_restored
256 .verify(&msg, &vk)
257 .expect("Restored signature should verify");
258 }
259
260 mod golden {
261 use super::*;
262
263 const GOLDEN_BYTES: &[u8; 96] = &[
264 39, 90, 41, 56, 174, 106, 33, 173, 254, 49, 113, 116, 208, 4, 121, 177, 236, 223, 173,
265 108, 193, 135, 214, 159, 99, 93, 108, 202, 201, 200, 141, 148, 230, 175, 77, 63, 232,
266 229, 34, 36, 7, 205, 254, 86, 70, 160, 49, 87, 114, 98, 20, 88, 141, 224, 113, 109,
267 208, 226, 177, 140, 55, 79, 174, 10, 121, 59, 197, 98, 35, 17, 213, 33, 65, 143, 110,
268 106, 145, 86, 141, 107, 204, 91, 39, 205, 113, 69, 87, 194, 239, 242, 188, 14, 194,
269 239, 173, 14,
270 ];
271
272 fn golden_value() -> UniqueSchnorrSignature {
273 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
274 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
275 let msg = [0u8; 32];
276 sk.sign(&msg, &mut rng).unwrap()
277 }
278
279 #[test]
280 fn golden_conversions() {
281 let value = UniqueSchnorrSignature::from_bytes(GOLDEN_BYTES)
282 .expect("This from bytes should not fail");
283 assert_eq!(golden_value(), value);
284
285 let serialized = UniqueSchnorrSignature::to_bytes(value);
286 let golden_serialized = UniqueSchnorrSignature::to_bytes(golden_value());
287 assert_eq!(golden_serialized, serialized);
288 }
289 }
290
291 mod golden_json {
292 use super::*;
293
294 const GOLDEN_JSON: &str = r#"
295 {
296 "commitment_point": [39, 90, 41, 56, 174, 106, 33, 173, 254, 49, 113, 116, 208, 4, 121, 177, 236, 223, 173, 108, 193, 135, 214, 159, 99, 93, 108, 202, 201, 200, 141, 148],
297 "response": [230, 175, 77, 63, 232, 229, 34, 36, 7, 205, 254, 86, 70, 160, 49, 87, 114, 98, 20, 88, 141, 224, 113, 109, 208, 226, 177, 140, 55, 79, 174, 10],
298 "challenge": [121, 59, 197, 98, 35, 17, 213, 33, 65, 143, 110, 106, 145, 86, 141, 107, 204, 91, 39, 205, 113, 69, 87, 194, 239, 242, 188, 14, 194, 239, 173, 14]
299 }"#;
300
301 fn golden_value() -> UniqueSchnorrSignature {
302 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
303 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
304 let msg = [0u8; 32];
305 sk.sign(&msg, &mut rng).unwrap()
306 }
307
308 #[test]
309 fn golden_conversions() {
310 let value = serde_json::from_str(GOLDEN_JSON)
311 .expect("This JSON deserialization should not fail");
312 assert_eq!(golden_value(), value);
313
314 let serialized =
315 serde_json::to_string(&value).expect("This JSON serialization should not fail");
316 let golden_serialized = serde_json::to_string(&golden_value())
317 .expect("This JSON serialization should not fail");
318 assert_eq!(golden_serialized, serialized);
319 }
320 }
321}