mithril_stm/signature_scheme/unique_schnorr_signature/
verification_key.rs

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