Skip to main content

signedby_sdk/
verification_key.rs

1//! Groth16 verification key handling
2//!
3//! The verification key is a small (4KB) file that enables anyone to verify
4//! SignedByMe proofs. It can be bundled with your application for fully
5//! offline verification.
6
7use ark_bn254::{Bn254, Fq, Fq2, G1Affine, G2Affine};
8use ark_groth16::VerifyingKey;
9use ark_serialize::CanonicalDeserialize;
10use serde::{Deserialize, Serialize};
11
12use crate::error::SdkError;
13
14/// Groth16 verification key for BN254 curve
15#[derive(Clone)]
16pub struct VerificationKey {
17    /// The ark-groth16 verifying key
18    pub(crate) inner: VerifyingKey<Bn254>,
19}
20
21/// JSON representation of verification key (snarkjs format)
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct VerificationKeyJson {
24    pub protocol: String,
25    pub curve: String,
26    #[serde(rename = "nPublic")]
27    pub n_public: usize,
28    pub vk_alpha_1: Vec<String>,
29    pub vk_beta_2: Vec<Vec<String>>,
30    pub vk_gamma_2: Vec<Vec<String>>,
31    pub vk_delta_2: Vec<Vec<String>>,
32    #[serde(rename = "vk_alphabeta_12")]
33    pub vk_alphabeta_12: Option<Vec<Vec<Vec<String>>>>,
34    #[serde(rename = "IC")]
35    pub ic: Vec<Vec<String>>,
36}
37
38impl VerificationKey {
39    /// Load verification key from snarkjs JSON format
40    pub fn from_json(json: &str) -> Result<Self, SdkError> {
41        let vk_json: VerificationKeyJson = serde_json::from_str(json)?;
42        Self::from_snarkjs(&vk_json)
43    }
44    
45    /// Load from parsed snarkjs JSON
46    pub fn from_snarkjs(vk_json: &VerificationKeyJson) -> Result<Self, SdkError> {
47        // Parse alpha (G1)
48        let alpha_g1 = parse_g1(&vk_json.vk_alpha_1)?;
49        
50        // Parse beta (G2)
51        let beta_g2 = parse_g2(&vk_json.vk_beta_2)?;
52        
53        // Parse gamma (G2)
54        let gamma_g2 = parse_g2(&vk_json.vk_gamma_2)?;
55        
56        // Parse delta (G2)
57        let delta_g2 = parse_g2(&vk_json.vk_delta_2)?;
58        
59        // Parse IC (G1 array)
60        let gamma_abc_g1: Vec<G1Affine> = vk_json.ic
61            .iter()
62            .map(|p| parse_g1(p))
63            .collect::<Result<Vec<_>, _>>()?;
64        
65        let inner = VerifyingKey {
66            alpha_g1,
67            beta_g2,
68            gamma_g2,
69            delta_g2,
70            gamma_abc_g1,
71        };
72        
73        Ok(Self { inner })
74    }
75    
76    /// Load from ark-serialized binary format
77    pub fn from_bytes(bytes: &[u8]) -> Result<Self, SdkError> {
78        let inner = VerifyingKey::<Bn254>::deserialize_uncompressed(bytes)
79            .map_err(|e| SdkError::InvalidVerificationKey(format!("Deserialize failed: {:?}", e)))?;
80        Ok(Self { inner })
81    }
82    
83    /// Number of public inputs expected
84    pub fn num_public_inputs(&self) -> usize {
85        self.inner.gamma_abc_g1.len() - 1
86    }
87}
88
89/// Parse a G1 point from snarkjs string array [x, y, z]
90fn parse_g1(coords: &[String]) -> Result<G1Affine, SdkError> {
91    if coords.len() < 2 {
92        return Err(SdkError::InvalidVerificationKey("G1 needs at least 2 coordinates".into()));
93    }
94    
95    let x = parse_fq(&coords[0])?;
96    let y = parse_fq(&coords[1])?;
97    
98    Ok(G1Affine::new(x, y))
99}
100
101/// Parse a G2 point from snarkjs nested string array [[x0, x1], [y0, y1], [z0, z1]]
102fn parse_g2(coords: &[Vec<String>]) -> Result<G2Affine, SdkError> {
103    if coords.len() < 2 {
104        return Err(SdkError::InvalidVerificationKey("G2 needs at least 2 coordinate pairs".into()));
105    }
106    
107    let x = Fq2::new(parse_fq(&coords[0][0])?, parse_fq(&coords[0][1])?);
108    let y = Fq2::new(parse_fq(&coords[1][0])?, parse_fq(&coords[1][1])?);
109    
110    Ok(G2Affine::new(x, y))
111}
112
113/// Parse a field element from decimal string
114fn parse_fq(s: &str) -> Result<Fq, SdkError> {
115    use ark_ff::PrimeField;
116    use num_bigint::BigUint;
117    use std::str::FromStr;
118    
119    let n = BigUint::from_str(s)
120        .map_err(|e| SdkError::InvalidVerificationKey(format!("Invalid field element: {}", e)))?;
121    
122    let bytes = n.to_bytes_le();
123    let mut padded = [0u8; 32];
124    let len = bytes.len().min(32);
125    padded[..len].copy_from_slice(&bytes[..len]);
126    
127    Ok(Fq::from_le_bytes_mod_order(&padded))
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133    
134    #[test]
135    fn test_parse_fq() {
136        let result = parse_fq("123456789");
137        assert!(result.is_ok());
138    }
139}