Skip to main content

guardian_client/keystore/
ecdsa.rs

1use guardian_shared::SignatureScheme;
2use miden_protocol::Word;
3use miden_protocol::crypto::dsa::ecdsa_k256_keccak::SigningKey as SecretKey;
4use miden_protocol::utils::serde::Serializable;
5
6use super::Signer;
7
8/// In-memory ECDSA signer used for GUARDIAN authentication and multisig signing.
9pub struct EcdsaKeyStore {
10    secret_key: std::sync::Mutex<SecretKey>,
11    commitment: Word,
12    commitment_hex: String,
13    public_key_hex: String,
14}
15
16impl EcdsaKeyStore {
17    /// Creates a new signer from an ECDSA secret key.
18    pub fn new(secret_key: SecretKey) -> Self {
19        let public_key = secret_key.public_key();
20        let commitment = public_key.to_commitment();
21        let commitment_hex = format!("0x{}", hex::encode(commitment.to_bytes()));
22        let public_key_hex = format!("0x{}", hex::encode(public_key.to_bytes()));
23
24        Self {
25            secret_key: std::sync::Mutex::new(secret_key),
26            commitment,
27            commitment_hex,
28            public_key_hex,
29        }
30    }
31
32    /// Generates a new random signer.
33    pub fn generate() -> Self {
34        Self::new(SecretKey::new())
35    }
36}
37
38impl Signer for EcdsaKeyStore {
39    fn scheme(&self) -> SignatureScheme {
40        SignatureScheme::Ecdsa
41    }
42
43    fn commitment(&self) -> Word {
44        self.commitment
45    }
46
47    fn commitment_hex(&self) -> String {
48        self.commitment_hex.clone()
49    }
50
51    fn public_key_hex(&self) -> String {
52        self.public_key_hex.clone()
53    }
54
55    fn sign_word_hex(&self, message: Word) -> String {
56        format!(
57            "0x{}",
58            hex::encode(self.secret_key.lock().unwrap().sign(message).to_bytes())
59        )
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use miden_protocol::crypto::dsa::ecdsa_k256_keccak::{PublicKey, Signature};
66    use miden_protocol::utils::serde::Deserializable;
67
68    use super::*;
69
70    #[test]
71    fn generate_creates_valid_signer() {
72        let signer = EcdsaKeyStore::generate();
73        assert!(signer.commitment_hex().starts_with("0x"));
74        assert_eq!(signer.commitment_hex().len(), 66);
75    }
76
77    #[test]
78    fn new_from_secret_key_derives_correct_commitment() {
79        let secret_key = SecretKey::new();
80        let expected_commitment = secret_key.public_key().to_commitment();
81        let signer = EcdsaKeyStore::new(secret_key);
82        assert_eq!(signer.commitment(), expected_commitment);
83    }
84
85    #[test]
86    fn sign_word_hex_produces_verifiable_signature() {
87        let signer = EcdsaKeyStore::generate();
88        let message = Word::default();
89        let signature_hex = signer.sign_word_hex(message);
90        let signature_bytes = hex::decode(signature_hex.trim_start_matches("0x")).unwrap();
91        let signature = Signature::read_from_bytes(&signature_bytes).unwrap();
92        let public_key_bytes =
93            hex::decode(signer.public_key_hex().trim_start_matches("0x")).unwrap();
94        let public_key = PublicKey::read_from_bytes(&public_key_bytes).unwrap();
95        assert!(public_key.verify(message, &signature));
96    }
97}