miden_tx/auth/
tx_authenticator.rs

1use alloc::{collections::BTreeMap, string::ToString, sync::Arc, vec::Vec};
2
3use miden_lib::utils::sync::RwLock;
4use miden_objects::account::{AccountDelta, AuthSecretKey};
5use rand::Rng;
6use vm_processor::{Digest, Felt, Word};
7
8use super::signatures::get_falcon_signature;
9use crate::errors::AuthenticationError;
10
11// TRANSACTION AUTHENTICATOR
12// ================================================================================================
13
14/// Defines an authenticator for transactions.
15///
16/// The main purpose of the authenticator is to generate signatures for a given message against
17/// a key managed by the authenticator. That is, the authenticator maintains a set of public-
18/// private key pairs, and can be requested to generate signatures against any of the managed keys.
19///
20/// The public keys are defined by [Digest]'s which are the hashes of the actual public keys.
21pub trait TransactionAuthenticator {
22    /// Retrieves a signature for a specific message as a list of [Felt].
23    ///
24    /// The request is initiated by the VM as a consequence of the SigToStack advice
25    /// injector.
26    ///
27    /// - `pub_key`: The public key used for signature generation.
28    /// - `message`: The message to sign, usually a commitment to the transaction data.
29    /// - `account_delta`: An informational parameter describing the changes made to the account up
30    ///   to the point of calling `get_signature()`. This allows the authenticator to review any
31    ///   alterations to the account prior to signing. It should not be directly used in the
32    ///   signature computation.
33    fn get_signature(
34        &self,
35        pub_key: Word,
36        message: Word,
37        account_delta: &AccountDelta,
38    ) -> Result<Vec<Felt>, AuthenticationError>;
39}
40
41/// This blanket implementation is required to allow `Option<&T>` to be mapped to `Option<&dyn
42/// TransactionAuthenticator`>.
43impl<T> TransactionAuthenticator for &T
44where
45    T: TransactionAuthenticator + ?Sized,
46{
47    fn get_signature(
48        &self,
49        pub_key: Word,
50        message: Word,
51        account_delta: &AccountDelta,
52    ) -> Result<Vec<Felt>, AuthenticationError> {
53        TransactionAuthenticator::get_signature(*self, pub_key, message, account_delta)
54    }
55}
56
57// BASIC AUTHENTICATOR
58// ================================================================================================
59
60#[derive(Clone, Debug)]
61/// Represents a signer for [AuthSecretKey] keys.
62pub struct BasicAuthenticator<R> {
63    /// pub_key |-> secret_key mapping
64    keys: BTreeMap<Digest, AuthSecretKey>,
65    rng: Arc<RwLock<R>>,
66}
67
68impl<R: Rng> BasicAuthenticator<R> {
69    #[cfg(feature = "std")]
70    pub fn new(keys: &[(Word, AuthSecretKey)]) -> BasicAuthenticator<rand::rngs::StdRng> {
71        use rand::{SeedableRng, rngs::StdRng};
72
73        let rng = StdRng::from_os_rng();
74        BasicAuthenticator::<StdRng>::new_with_rng(keys, rng)
75    }
76
77    pub fn new_with_rng(keys: &[(Word, AuthSecretKey)], rng: R) -> Self {
78        let mut key_map = BTreeMap::new();
79        for (word, secret_key) in keys {
80            key_map.insert(word.into(), secret_key.clone());
81        }
82
83        BasicAuthenticator {
84            keys: key_map,
85            rng: Arc::new(RwLock::new(rng)),
86        }
87    }
88}
89
90impl<R: Rng> TransactionAuthenticator for BasicAuthenticator<R> {
91    /// Gets a signature over a message, given a public key.
92    /// The key should be included in the `keys` map and should be a variant of [AuthSecretKey].
93    ///
94    /// Supported signature schemes:
95    /// - RpoFalcon512
96    ///
97    /// # Errors
98    /// If the public key is not contained in the `keys` map,
99    /// [`AuthenticationError::UnknownPublicKey`] is returned.
100    fn get_signature(
101        &self,
102        pub_key: Word,
103        message: Word,
104        account_delta: &AccountDelta,
105    ) -> Result<Vec<Felt>, AuthenticationError> {
106        let _ = account_delta;
107        let mut rng = self.rng.write();
108
109        match self.keys.get(&pub_key.into()) {
110            Some(key) => match key {
111                AuthSecretKey::RpoFalcon512(falcon_key) => {
112                    get_falcon_signature(falcon_key, message, &mut *rng)
113                },
114            },
115            None => Err(AuthenticationError::UnknownPublicKey(format!(
116                "public key {} is not contained in the authenticator's keys",
117                Digest::from(pub_key)
118            ))),
119        }
120    }
121}
122
123// HELPER FUNCTIONS
124// ================================================================================================
125
126impl TransactionAuthenticator for () {
127    fn get_signature(
128        &self,
129        _pub_key: Word,
130        _message: Word,
131        _account_delta: &AccountDelta,
132    ) -> Result<Vec<Felt>, AuthenticationError> {
133        Err(AuthenticationError::RejectedSignature(
134            "default authenticator cannot provide signatures".to_string(),
135        ))
136    }
137}
138
139#[cfg(test)]
140mod test {
141    use miden_lib::utils::{Deserializable, Serializable};
142    use miden_objects::{account::AuthSecretKey, crypto::dsa::rpo_falcon512::SecretKey};
143
144    #[test]
145    fn serialize_auth_key() {
146        let secret_key = SecretKey::new();
147        let auth_key = AuthSecretKey::RpoFalcon512(secret_key.clone());
148        let serialized = auth_key.to_bytes();
149        let deserialized = AuthSecretKey::read_from_bytes(&serialized).unwrap();
150
151        match deserialized {
152            AuthSecretKey::RpoFalcon512(key) => assert_eq!(secret_key.to_bytes(), key.to_bytes()),
153        }
154    }
155}