Skip to main content

guardian_client/keystore/
falcon.rs

1use guardian_shared::SignatureScheme;
2use guardian_shared::hex::IntoHex;
3use miden_protocol::Word;
4use miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey;
5use miden_protocol::utils::serde::Serializable;
6
7use super::Signer;
8
9/// In-memory Falcon signer used for GUARDIAN authentication and multisig signing.
10///
11/// The underlying Miden secret key zeroizes on drop. This type avoids exposing
12/// the key material through cloning or accessor APIs.
13pub struct FalconKeyStore {
14    secret_key: SecretKey,
15    commitment: Word,
16    commitment_hex: String,
17    public_key_hex: String,
18}
19
20impl FalconKeyStore {
21    /// Creates a new signer from a Falcon secret key.
22    pub fn new(secret_key: SecretKey) -> Self {
23        let public_key = secret_key.public_key();
24        let commitment = public_key.to_commitment();
25        let commitment_hex = format!("0x{}", hex::encode(commitment.to_bytes()));
26        let public_key_hex = (&public_key).into_hex();
27
28        Self {
29            secret_key,
30            commitment,
31            commitment_hex,
32            public_key_hex,
33        }
34    }
35
36    /// Generates a new random signer.
37    pub fn generate() -> Self {
38        Self::new(SecretKey::new())
39    }
40}
41
42impl Signer for FalconKeyStore {
43    fn scheme(&self) -> SignatureScheme {
44        SignatureScheme::Falcon
45    }
46
47    fn commitment(&self) -> Word {
48        self.commitment
49    }
50
51    fn commitment_hex(&self) -> String {
52        self.commitment_hex.clone()
53    }
54
55    fn public_key_hex(&self) -> String {
56        self.public_key_hex.clone()
57    }
58
59    fn sign_word_hex(&self, message: Word) -> String {
60        self.secret_key.sign(message).into_hex()
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use guardian_shared::hex::FromHex;
67    use miden_protocol::crypto::dsa::falcon512_poseidon2::{PublicKey, Signature};
68
69    use super::*;
70
71    #[test]
72    fn generate_creates_valid_signer() {
73        let signer = FalconKeyStore::generate();
74        assert!(signer.commitment_hex().starts_with("0x"));
75        assert_eq!(signer.commitment_hex().len(), 66);
76    }
77
78    #[test]
79    fn new_from_secret_key_derives_correct_commitment() {
80        let secret_key = SecretKey::new();
81        let expected_commitment = secret_key.public_key().to_commitment();
82        let signer = FalconKeyStore::new(secret_key);
83        assert_eq!(signer.commitment(), expected_commitment);
84    }
85
86    #[test]
87    fn sign_word_produces_verifiable_signature() {
88        let signer = FalconKeyStore::generate();
89        let message = Word::default();
90        let signature_hex = signer.sign_word_hex(message);
91        let signature = Signature::from_hex(&signature_hex).unwrap();
92        let public_key = PublicKey::from_hex(&signer.public_key_hex()).unwrap();
93        assert!(public_key.verify(message, &signature));
94    }
95
96    #[test]
97    fn public_key_hex_roundtrips() {
98        let signer = FalconKeyStore::generate();
99        let public_key = PublicKey::from_hex(&signer.public_key_hex()).unwrap();
100        assert_eq!(public_key.to_commitment(), signer.commitment());
101    }
102}