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