mithril_stm/signature_scheme/schnorr_signature/
verification_key.rs

1use anyhow::{Context, anyhow};
2use dusk_jubjub::SubgroupPoint as JubjubSubgroup;
3use group::{Group, GroupEncoding};
4
5use super::SchnorrSignatureError;
6use crate::StmResult;
7
8/// Schnorr verification key, it consists of a point on the Jubjub curve
9/// vk = g * sk, where g is a generator
10#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
11pub struct SchnorrVerificationKey(pub(crate) JubjubSubgroup);
12
13impl SchnorrVerificationKey {
14    /// Convert a `SchnorrVerificationKey` into bytes.
15    pub fn to_bytes(self) -> [u8; 32] {
16        self.0.to_bytes()
17    }
18
19    /// Convert bytes into a `SchnorrVerificationKey`.
20    ///
21    /// The bytes must represent a Jubjub Subgroup point or the conversion will fail
22    pub fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
23        if bytes.len() < 32 {
24            return Err(anyhow!(SchnorrSignatureError::SerializationError)).with_context(
25                || "Not enough bytes provided to create a Schnorr verification key.",
26            );
27        }
28        let verification_key_bytes = bytes[0..32]
29            .try_into()
30            .map_err(|_| anyhow!(SchnorrSignatureError::SerializationError))
31            .with_context(|| "Failed to obtain the Schnorr verification key's bytes.")?;
32        let point = JubjubSubgroup::from_bytes(&verification_key_bytes)
33            .into_option()
34            .ok_or(anyhow!(SchnorrSignatureError::SerializationError))
35            .with_context(|| "Failed to create a JubjubSubgroup point from the given bytes.")?;
36
37        Ok(SchnorrVerificationKey(point))
38    }
39}
40
41impl From<&crate::SchnorrSigningKey> for SchnorrVerificationKey {
42    /// Convert a Schnorr secret key into a verification key
43    ///
44    /// This is done by computing `vk = g * sk` where g is the generator
45    /// of the subgroup and sk is the schnorr secret key
46    fn from(signing_key: &crate::SchnorrSigningKey) -> Self {
47        let generator = JubjubSubgroup::generator();
48
49        SchnorrVerificationKey(generator * signing_key.0)
50    }
51}
52
53#[cfg(test)]
54mod tests {
55
56    use dusk_jubjub::Fq as JubjubBase;
57    use dusk_jubjub::SubgroupPoint as JubjubSubgroup;
58    use ff::Field;
59    use group::Group;
60    use rand_chacha::ChaCha20Rng;
61    use rand_core::SeedableRng;
62
63    use crate::signature_scheme::{SchnorrSigningKey, SchnorrVerificationKey};
64
65    #[test]
66    fn generate_verification_key() {
67        let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
68        let sk = SchnorrSigningKey::try_generate(&mut rng).unwrap();
69        let g = JubjubSubgroup::generator();
70        let vk = g * sk.0;
71
72        let vk_from_sk = SchnorrVerificationKey::from(&sk);
73
74        assert_eq!(vk, vk_from_sk.0);
75    }
76
77    #[test]
78    fn verify_fail_verification_key_not_on_curve() {
79        let msg = vec![0, 0, 0, 1];
80        let seed = [0u8; 32];
81        let mut rng = ChaCha20Rng::from_seed(seed);
82        let sk = SchnorrSigningKey::try_generate(&mut rng).unwrap();
83        let vk1 = SchnorrVerificationKey::from(&sk);
84        let sig = sk.sign(&msg, &mut rng).unwrap();
85        let vk2 = SchnorrVerificationKey(JubjubSubgroup::from_raw_unchecked(
86            JubjubBase::ONE,
87            JubjubBase::ONE,
88        ));
89
90        let result1 = sig.verify(&msg, &vk1);
91        let result2 = sig.verify(&msg, &vk2);
92
93        result1.expect("Correct verification key used, test should pass.");
94
95        result2.expect_err("Invalid verification key used, test should fail.");
96    }
97
98    #[test]
99    fn serialize_deserialize_vk() {
100        let seed = 0;
101        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(seed);
102        let sk = SchnorrSigningKey::try_generate(&mut rng).unwrap();
103        let vk = SchnorrVerificationKey::from(&sk);
104
105        let vk_bytes = vk.to_bytes();
106        let vk2 = SchnorrVerificationKey::from_bytes(&vk_bytes).unwrap();
107
108        assert_eq!(vk.0, vk2.0);
109    }
110
111    mod golden {
112
113        use rand_chacha::ChaCha20Rng;
114        use rand_core::SeedableRng;
115
116        use crate::signature_scheme::{SchnorrSigningKey, SchnorrVerificationKey};
117
118        const GOLDEN_BYTES: &[u8; 32] = &[
119            144, 52, 95, 161, 127, 253, 49, 32, 140, 217, 231, 207, 32, 238, 244, 196, 97, 241, 47,
120            95, 101, 9, 70, 136, 194, 66, 187, 253, 200, 32, 218, 43,
121        ];
122
123        fn golden_value() -> SchnorrVerificationKey {
124            let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
125            let sk = SchnorrSigningKey::try_generate(&mut rng).unwrap();
126            SchnorrVerificationKey::from(&sk)
127        }
128
129        #[test]
130        fn golden_conversions() {
131            let value = SchnorrVerificationKey::from_bytes(GOLDEN_BYTES)
132                .expect("This from bytes should not fail");
133            assert_eq!(golden_value(), value);
134
135            let serialized = SchnorrVerificationKey::to_bytes(value);
136            let golden_serialized = SchnorrVerificationKey::to_bytes(golden_value());
137            assert_eq!(golden_serialized, serialized);
138        }
139    }
140}