mithril_stm/signature_scheme/unique_schnorr_signature/
verification_key.rs1use std::{
2 cmp::Ordering,
3 hash::{Hash, Hasher},
4};
5
6use anyhow::{Context, Ok, anyhow};
7use serde::{Deserialize, Serialize};
8
9use crate::{StmResult, signature_scheme::BaseFieldElement};
10
11use super::{
12 PrimeOrderProjectivePoint, ProjectivePoint, SchnorrSigningKey, UniqueSchnorrSignatureError,
13};
14
15#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
18pub struct SchnorrVerificationKey(pub(crate) PrimeOrderProjectivePoint);
19
20impl SchnorrVerificationKey {
21 pub fn new_from_signing_key(signing_key: SchnorrSigningKey) -> StmResult<Self> {
26 if signing_key.0.is_zero() | signing_key.0.is_one() {
27 return Err(anyhow!(UniqueSchnorrSignatureError::InvalidSigningKey))
28 .with_context(|| "Verification key generation failed.");
29 }
30 let generator = PrimeOrderProjectivePoint::create_generator();
31
32 Ok(SchnorrVerificationKey(signing_key.0 * generator))
33 }
34
35 pub fn is_valid(&self) -> StmResult<Self> {
36 let projective_point = ProjectivePoint::from(self.0);
37 if !projective_point.is_prime_order() {
38 return Err(anyhow!(UniqueSchnorrSignatureError::PointIsNotPrimeOrder(
39 Box::new(self.0)
40 )));
41 }
42 self.0.is_on_curve()?;
43
44 Ok(*self)
45 }
46
47 pub fn to_bytes(self) -> [u8; 64] {
50 let (x, y) = self.0.get_coordinates();
51 let mut output = [0; 64];
52 output[0..32].copy_from_slice(&x.to_bytes());
53 output[32..64].copy_from_slice(&y.to_bytes());
54 output
55 }
56
57 pub fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
61 if bytes.len() < 64 {
62 return Err(anyhow!(UniqueSchnorrSignatureError::Serialization)).with_context(
63 || "Not enough bytes provided to construct a Schnorr verification key.",
64 );
65 }
66 let x = BaseFieldElement::from_bytes(&bytes[0..32])?;
67 let y = BaseFieldElement::from_bytes(&bytes[32..64])?;
68 let prime_order_projective_point = PrimeOrderProjectivePoint::from_coordinates(x, y)
69 .with_context(|| "Cannot construct Schnorr verification key from given bytes.")?;
70
71 Ok(SchnorrVerificationKey(prime_order_projective_point))
72 }
73}
74
75impl Hash for SchnorrVerificationKey {
76 fn hash<H: Hasher>(&self, state: &mut H) {
77 Hash::hash_slice(&self.to_bytes(), state)
78 }
79}
80
81impl PartialOrd for SchnorrVerificationKey {
82 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
83 Some(std::cmp::Ord::cmp(self, other))
84 }
85}
86
87impl Ord for SchnorrVerificationKey {
88 fn cmp(&self, other: &Self) -> Ordering {
89 self.to_bytes().cmp(&other.to_bytes())
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use rand_chacha::ChaCha20Rng;
96 use rand_core::SeedableRng;
97
98 use crate::signature_scheme::{SchnorrSigningKey, SchnorrVerificationKey};
99
100 #[test]
101 fn create_verification_key_from_signing_key() {
102 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
103 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
104
105 let vk = SchnorrVerificationKey::new_from_signing_key(sk);
106
107 assert!(
108 vk.is_ok(),
109 "Verification key creation should succeed for valid signing key"
110 );
111 }
112
113 #[test]
114 fn different_signing_keys_produce_different_verification_keys() {
115 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
116 let sk1 = SchnorrSigningKey::generate(&mut rng).unwrap();
117 let sk2 = SchnorrSigningKey::generate(&mut rng).unwrap();
118
119 let vk1 = SchnorrVerificationKey::new_from_signing_key(sk1).unwrap();
120 let vk2 = SchnorrVerificationKey::new_from_signing_key(sk2).unwrap();
121
122 assert_ne!(
123 vk1, vk2,
124 "Different signing keys should produce different verification keys"
125 );
126 }
127
128 #[test]
129 fn same_signing_key_produces_same_verification_key() {
130 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
131 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
132
133 let vk1 = SchnorrVerificationKey::new_from_signing_key(sk.clone()).unwrap();
134 let vk2 = SchnorrVerificationKey::new_from_signing_key(sk).unwrap();
135
136 assert_eq!(
137 vk1, vk2,
138 "Same signing key should produce same verification key"
139 );
140 }
141
142 #[test]
143 fn valid_verification_key_passes_validation() {
144 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
145 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
146 let vk = SchnorrVerificationKey::new_from_signing_key(sk).unwrap();
147
148 let result = vk.is_valid();
149
150 assert!(
151 result.is_ok(),
152 "Valid verification key should pass validation"
153 );
154 }
155
156 #[test]
157 fn from_bytes_not_enough_bytes() {
158 let bytes = vec![0u8; 31];
159 let result = SchnorrVerificationKey::from_bytes(&bytes);
160
161 result.expect_err("Should fail with insufficient bytes");
162 }
163
164 #[test]
165 fn from_bytes_exact_size() {
166 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
167 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
168 let vk = SchnorrVerificationKey::new_from_signing_key(sk).unwrap();
169 let vk_bytes = vk.to_bytes();
170
171 let vk_restored = SchnorrVerificationKey::from_bytes(&vk_bytes).unwrap();
172
173 assert_eq!(vk, vk_restored);
174 }
175
176 #[test]
177 fn from_bytes_extra_bytes() {
178 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
179 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
180 let vk = SchnorrVerificationKey::new_from_signing_key(sk).unwrap();
181 let vk_bytes = vk.to_bytes();
182
183 let mut extended_bytes = vk_bytes.to_vec();
184 extended_bytes.extend_from_slice(&[0xFF; 10]);
185
186 let vk_restored = SchnorrVerificationKey::from_bytes(&extended_bytes).unwrap();
187
188 assert_eq!(vk, vk_restored);
189 }
190
191 #[test]
192 fn to_bytes_is_deterministic() {
193 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
194 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
195 let vk = SchnorrVerificationKey::new_from_signing_key(sk).unwrap();
196
197 let bytes1 = vk.to_bytes();
198 let bytes2 = vk.to_bytes();
199
200 assert_eq!(bytes1, bytes2, "to_bytes should be deterministic");
201 }
202
203 mod golden {
204 use super::*;
205
206 const GOLDEN_BYTES: &[u8; 64] = &[
207 186, 22, 69, 162, 1, 67, 125, 160, 104, 197, 105, 109, 200, 34, 186, 196, 171, 155,
208 191, 178, 11, 116, 108, 8, 111, 249, 47, 39, 137, 55, 62, 62, 144, 52, 95, 161, 127,
209 253, 49, 32, 140, 217, 231, 207, 32, 238, 244, 196, 97, 241, 47, 95, 101, 9, 70, 136,
210 194, 66, 187, 253, 200, 32, 218, 43,
211 ];
212
213 fn golden_value() -> SchnorrVerificationKey {
214 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
215 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
216 SchnorrVerificationKey::new_from_signing_key(sk).unwrap()
217 }
218
219 #[test]
220 fn golden_conversions() {
221 let value = SchnorrVerificationKey::from_bytes(GOLDEN_BYTES)
222 .expect("This from bytes should not fail");
223 assert_eq!(golden_value(), value);
224
225 let serialized = SchnorrVerificationKey::to_bytes(value);
226 let golden_serialized = SchnorrVerificationKey::to_bytes(golden_value());
227 assert_eq!(golden_serialized, serialized);
228 }
229 }
230
231 mod golden_json {
232 use super::*;
233
234 const GOLDEN_JSON: &str = r#"[144, 52, 95, 161, 127, 253, 49, 32, 140, 217, 231, 207, 32, 238, 244, 196, 97, 241, 47, 95, 101, 9, 70, 136, 194, 66, 187, 253, 200, 32, 218, 43]"#;
235
236 fn golden_value() -> SchnorrVerificationKey {
237 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
238 let sk = SchnorrSigningKey::generate(&mut rng).unwrap();
239 SchnorrVerificationKey::new_from_signing_key(sk).unwrap()
240 }
241
242 #[test]
243 fn golden_conversions() {
244 let value = serde_json::from_str(GOLDEN_JSON)
245 .expect("This JSON deserialization should not fail");
246 assert_eq!(golden_value(), value);
247
248 let serialized =
249 serde_json::to_string(&value).expect("This JSON serialization should not fail");
250 let golden_serialized = serde_json::to_string(&golden_value())
251 .expect("This JSON serialization should not fail");
252 assert_eq!(golden_serialized, serialized);
253 }
254 }
255}