1use alloc::boxed::Box;
2use alloc::collections::BTreeMap;
3use alloc::string::ToString;
4use alloc::sync::Arc;
5use alloc::vec::Vec;
6
7use miden_processor::FutureMaybeSend;
8use miden_protocol::account::auth::{AuthSecretKey, PublicKey, PublicKeyCommitment, Signature};
9use miden_protocol::crypto::SequentialCommit;
10use miden_protocol::transaction::TransactionSummary;
11use miden_protocol::{Felt, Hasher, Word};
12
13use crate::errors::AuthenticationError;
14use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
15
16#[derive(Debug, Clone)]
29pub enum SigningInputs {
30 TransactionSummary(Box<TransactionSummary>),
31 Arbitrary(Vec<Felt>),
32 Blind(Word),
33}
34
35impl SequentialCommit for SigningInputs {
36 type Commitment = Word;
37
38 fn to_elements(&self) -> Vec<Felt> {
39 match self {
40 SigningInputs::TransactionSummary(tx_summary) => tx_summary.as_ref().to_elements(),
41 SigningInputs::Arbitrary(elements) => elements.clone(),
42 SigningInputs::Blind(word) => word.as_elements().to_vec(),
43 }
44 }
45
46 fn to_commitment(&self) -> Self::Commitment {
47 match self {
48 SigningInputs::TransactionSummary(tx_summary) => tx_summary.as_ref().to_commitment(),
50 SigningInputs::Arbitrary(elements) => Hasher::hash_elements(elements),
52 SigningInputs::Blind(word) => *word,
54 }
55 }
56}
57
58impl SigningInputs {
60 pub fn to_commitment(&self) -> Word {
62 <Self as SequentialCommit>::to_commitment(self)
63 }
64
65 pub fn to_elements(&self) -> Vec<Felt> {
67 <Self as SequentialCommit>::to_elements(self)
68 }
69}
70
71impl Serializable for SigningInputs {
75 fn write_into<W: ByteWriter>(&self, target: &mut W) {
76 match self {
77 SigningInputs::TransactionSummary(tx_summary) => {
78 target.write_u8(0);
79 tx_summary.as_ref().write_into(target);
80 },
81 SigningInputs::Arbitrary(elements) => {
82 target.write_u8(1);
83 elements.write_into(target);
84 },
85 SigningInputs::Blind(word) => {
86 target.write_u8(2);
87 word.write_into(target);
88 },
89 }
90 }
91}
92
93impl Deserializable for SigningInputs {
94 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
95 let discriminant = source.read_u8()?;
96 match discriminant {
97 0 => {
98 let tx_summary: TransactionSummary = source.read()?;
99 Ok(SigningInputs::TransactionSummary(Box::new(tx_summary)))
100 },
101 1 => {
102 let elements: Vec<Felt> = source.read()?;
103 Ok(SigningInputs::Arbitrary(elements))
104 },
105 2 => {
106 let word: Word = source.read()?;
107 Ok(SigningInputs::Blind(word))
108 },
109 other => Err(DeserializationError::InvalidValue(format!(
110 "invalid SigningInputs variant: {other}"
111 ))),
112 }
113 }
114}
115
116pub trait TransactionAuthenticator {
128 fn get_signature(
140 &self,
141 pub_key_commitment: PublicKeyCommitment,
142 signing_inputs: &SigningInputs,
143 ) -> impl FutureMaybeSend<Result<Signature, AuthenticationError>>;
144
145 fn get_public_key(
147 &self,
148 pub_key_commitment: PublicKeyCommitment,
149 ) -> impl FutureMaybeSend<Option<Arc<PublicKey>>>;
150}
151
152#[derive(Debug, Clone, Copy)]
157pub struct UnreachableAuth {
158 _protect: core::marker::PhantomData<u8>,
160}
161
162impl TransactionAuthenticator for UnreachableAuth {
163 #[allow(clippy::manual_async_fn)]
164 fn get_signature(
165 &self,
166 _pub_key_commitment: PublicKeyCommitment,
167 _signing_inputs: &SigningInputs,
168 ) -> impl FutureMaybeSend<Result<Signature, AuthenticationError>> {
169 async { unreachable!("Type `UnreachableAuth` must not be instantiated") }
170 }
171
172 fn get_public_key(
173 &self,
174 _pub_key_commitment: PublicKeyCommitment,
175 ) -> impl FutureMaybeSend<Option<Arc<PublicKey>>> {
176 async { unreachable!("Type `UnreachableAuth` must not be instantiated") }
177 }
178}
179
180#[derive(Clone, Debug)]
185pub struct BasicAuthenticator {
186 keys: BTreeMap<PublicKeyCommitment, (AuthSecretKey, Arc<PublicKey>)>,
188}
189
190impl BasicAuthenticator {
191 pub fn new(keys: &[AuthSecretKey]) -> Self {
192 let mut key_map = BTreeMap::new();
193 for secret_key in keys {
194 let pub_key = secret_key.public_key();
195 key_map.insert(pub_key.to_commitment(), (secret_key.clone(), pub_key.into()));
196 }
197
198 BasicAuthenticator { keys: key_map }
199 }
200
201 pub fn from_key_pairs(keys: &[(AuthSecretKey, PublicKey)]) -> Self {
202 let mut key_map = BTreeMap::new();
203 for (secret_key, public_key) in keys {
204 key_map.insert(
205 public_key.to_commitment(),
206 (secret_key.clone(), public_key.clone().into()),
207 );
208 }
209
210 BasicAuthenticator { keys: key_map }
211 }
212
213 pub fn keys(&self) -> &BTreeMap<PublicKeyCommitment, (AuthSecretKey, Arc<PublicKey>)> {
218 &self.keys
219 }
220}
221
222impl TransactionAuthenticator for BasicAuthenticator {
223 fn get_signature(
231 &self,
232 pub_key_commitment: PublicKeyCommitment,
233 signing_inputs: &SigningInputs,
234 ) -> impl FutureMaybeSend<Result<Signature, AuthenticationError>> {
235 let message = signing_inputs.to_commitment();
236
237 async move {
238 match self.keys.get(&pub_key_commitment) {
239 Some((auth_key, _)) => Ok(auth_key.sign(message)),
240 None => Err(AuthenticationError::UnknownPublicKey(pub_key_commitment)),
241 }
242 }
243 }
244
245 fn get_public_key(
249 &self,
250 pub_key_commitment: PublicKeyCommitment,
251 ) -> impl FutureMaybeSend<Option<Arc<PublicKey>>> {
252 async move { self.keys.get(&pub_key_commitment).map(|(_, pub_key)| pub_key.clone()) }
253 }
254}
255
256impl TransactionAuthenticator for () {
260 #[allow(clippy::manual_async_fn)]
261 fn get_signature(
262 &self,
263 _pub_key_commitment: PublicKeyCommitment,
264 _signing_inputs: &SigningInputs,
265 ) -> impl FutureMaybeSend<Result<Signature, AuthenticationError>> {
266 async {
267 Err(AuthenticationError::RejectedSignature(
268 "default authenticator cannot provide signatures".to_string(),
269 ))
270 }
271 }
272
273 fn get_public_key(
274 &self,
275 _pub_key_commitment: PublicKeyCommitment,
276 ) -> impl FutureMaybeSend<Option<Arc<PublicKey>>> {
277 async { None }
278 }
279}
280
281#[cfg(test)]
285mod test {
286 use miden_protocol::account::auth::AuthSecretKey;
287 use miden_protocol::utils::{Deserializable, Serializable};
288 use miden_protocol::{Felt, Word};
289
290 use super::SigningInputs;
291
292 #[test]
293 fn serialize_auth_key() {
294 let auth_key = AuthSecretKey::new_falcon512_rpo();
295 let serialized = auth_key.to_bytes();
296 let deserialized = AuthSecretKey::read_from_bytes(&serialized).unwrap();
297
298 assert_eq!(auth_key, deserialized);
299 }
300
301 #[test]
302 fn serialize_deserialize_signing_inputs_arbitrary() {
303 let elements = vec![
304 Felt::new(0),
305 Felt::new(1),
306 Felt::new(2),
307 Felt::new(3),
308 Felt::new(4),
309 Felt::new(5),
310 Felt::new(6),
311 Felt::new(7),
312 ];
313 let inputs = SigningInputs::Arbitrary(elements.clone());
314 let bytes = inputs.to_bytes();
315 let decoded = SigningInputs::read_from_bytes(&bytes).unwrap();
316
317 match decoded {
318 SigningInputs::Arbitrary(decoded_elements) => {
319 assert_eq!(decoded_elements, elements);
320 },
321 _ => panic!("expected Arbitrary variant"),
322 }
323 }
324
325 #[test]
326 fn serialize_deserialize_signing_inputs_blind() {
327 let word = Word::from([Felt::new(10), Felt::new(20), Felt::new(30), Felt::new(40)]);
328 let inputs = SigningInputs::Blind(word);
329 let bytes = inputs.to_bytes();
330 let decoded = SigningInputs::read_from_bytes(&bytes).unwrap();
331
332 match decoded {
333 SigningInputs::Blind(w) => {
334 assert_eq!(w, word);
335 },
336 _ => panic!("expected Blind variant"),
337 }
338 }
339}