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