mithril_stm/signature_scheme/unique_schnorr_signature/
verification_key.rs

1use 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/// Schnorr verification key, it consists of a point on the Jubjub curve
16/// vk = g * sk, where g is a generator
17#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
18pub struct SchnorrVerificationKey(pub(crate) PrimeOrderProjectivePoint);
19
20impl SchnorrVerificationKey {
21    /// Convert a Schnorr signing key into a verification key
22    ///
23    /// This is done by computing `vk = g * sk` where g is the generator
24    /// of the subgroup and sk is the schnorr signing key
25    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    /// Convert a `SchnorrVerificationKey` into bytes by decomposing it into
44    /// its coordinates first.
45    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    /// Convert bytes into a `SchnorrVerificationKey`.
54    ///
55    /// The bytes must represent two Jubjub Base field elements or the conversion will fail
56    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}