iota_sdk_crypto/zklogin/
mod.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::collections::HashMap;
6
7use iota_types::{Jwk, JwkId, UserSignature, ZkLoginAuthenticator};
8use poseidon::POSEIDON;
9use signature::Verifier;
10
11use crate::SignatureError;
12
13mod poseidon;
14mod verify;
15
16#[cfg(test)]
17mod tests;
18
19#[derive(Debug, Clone, PartialEq, Default)]
20pub struct ZkloginVerifier {
21    proof_verifying_key: verify::VerifyingKey,
22    jwks: HashMap<JwkId, Jwk>,
23}
24
25impl ZkloginVerifier {
26    fn new(proof_verifying_key: verify::VerifyingKey) -> Self {
27        Self {
28            proof_verifying_key,
29            jwks: Default::default(),
30        }
31    }
32
33    pub fn new_mainnet() -> Self {
34        Self::new(verify::VerifyingKey::new_mainnet())
35    }
36
37    /// Load a fixed verifying key from zkLogin.vkey output. This is based on a
38    /// local setup and should not be used in production.
39    pub fn new_dev() -> Self {
40        Self::new(verify::VerifyingKey::new_dev())
41    }
42
43    pub fn jwks(&self) -> &HashMap<JwkId, Jwk> {
44        &self.jwks
45    }
46
47    pub fn jwks_mut(&mut self) -> &mut HashMap<JwkId, Jwk> {
48        &mut self.jwks
49    }
50}
51
52impl Verifier<ZkLoginAuthenticator> for ZkloginVerifier {
53    fn verify(
54        &self,
55        message: &[u8],
56        signature: &ZkLoginAuthenticator,
57    ) -> Result<(), SignatureError> {
58        // 1. check that we have a valid corresponding Jwk
59        let jwk_id = signature.inputs.jwk_id();
60        let jwk = self.jwks.get(jwk_id).ok_or_else(|| {
61            SignatureError::from_source(format!(
62                "unable to find corresponding jwk with id '{jwk_id:?}' for provided authenticator",
63            ))
64        })?;
65
66        // 2. verify that the provided SimpleSignature is valid
67        crate::simple::SimpleVerifier.verify(message, &signature.signature)?;
68
69        // 3. verify groth16 proof
70        self.proof_verifying_key.verify_zklogin(
71            jwk,
72            &signature.inputs,
73            &signature.signature,
74            signature.max_epoch,
75        )
76    }
77}
78
79impl Verifier<UserSignature> for ZkloginVerifier {
80    fn verify(&self, message: &[u8], signature: &UserSignature) -> Result<(), SignatureError> {
81        let UserSignature::ZkLogin(zklogin_authenticator) = signature else {
82            return Err(SignatureError::from_source("not a zklogin signature"));
83        };
84
85        self.verify(message, zklogin_authenticator.as_ref())
86    }
87}