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::serde::{
15 ByteReader,
16 ByteWriter,
17 Deserializable,
18 DeserializationError,
19 Serializable,
20};
21
22#[derive(Debug, Clone)]
35pub enum SigningInputs {
36 TransactionSummary(Box<TransactionSummary>),
37 Arbitrary(Vec<Felt>),
38 Blind(Word),
39}
40
41impl SequentialCommit for SigningInputs {
42 type Commitment = Word;
43
44 fn to_elements(&self) -> Vec<Felt> {
45 match self {
46 SigningInputs::TransactionSummary(tx_summary) => tx_summary.as_ref().to_elements(),
47 SigningInputs::Arbitrary(elements) => elements.clone(),
48 SigningInputs::Blind(word) => word.as_elements().to_vec(),
49 }
50 }
51
52 fn to_commitment(&self) -> Self::Commitment {
53 match self {
54 SigningInputs::TransactionSummary(tx_summary) => tx_summary.as_ref().to_commitment(),
56 SigningInputs::Arbitrary(elements) => Hasher::hash_elements(elements),
58 SigningInputs::Blind(word) => *word,
60 }
61 }
62}
63
64impl SigningInputs {
66 pub fn to_commitment(&self) -> Word {
68 <Self as SequentialCommit>::to_commitment(self)
69 }
70
71 pub fn to_elements(&self) -> Vec<Felt> {
73 <Self as SequentialCommit>::to_elements(self)
74 }
75}
76
77impl Serializable for SigningInputs {
81 fn write_into<W: ByteWriter>(&self, target: &mut W) {
82 match self {
83 SigningInputs::TransactionSummary(tx_summary) => {
84 target.write_u8(0);
85 tx_summary.as_ref().write_into(target);
86 },
87 SigningInputs::Arbitrary(elements) => {
88 target.write_u8(1);
89 elements.write_into(target);
90 },
91 SigningInputs::Blind(word) => {
92 target.write_u8(2);
93 word.write_into(target);
94 },
95 }
96 }
97}
98
99impl Deserializable for SigningInputs {
100 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
101 let discriminant = source.read_u8()?;
102 match discriminant {
103 0 => {
104 let tx_summary: TransactionSummary = source.read()?;
105 Ok(SigningInputs::TransactionSummary(Box::new(tx_summary)))
106 },
107 1 => {
108 let elements: Vec<Felt> = source.read()?;
109 Ok(SigningInputs::Arbitrary(elements))
110 },
111 2 => {
112 let word: Word = source.read()?;
113 Ok(SigningInputs::Blind(word))
114 },
115 other => Err(DeserializationError::InvalidValue(format!(
116 "invalid SigningInputs variant: {other}"
117 ))),
118 }
119 }
120}
121
122pub trait TransactionAuthenticator {
134 fn get_signature(
146 &self,
147 pub_key_commitment: PublicKeyCommitment,
148 signing_inputs: &SigningInputs,
149 ) -> impl FutureMaybeSend<Result<Signature, AuthenticationError>>;
150
151 fn get_public_key(
153 &self,
154 pub_key_commitment: PublicKeyCommitment,
155 ) -> impl FutureMaybeSend<Option<Arc<PublicKey>>>;
156}
157
158#[derive(Debug, Clone, Copy)]
163pub struct UnreachableAuth {
164 _protect: core::marker::PhantomData<u8>,
166}
167
168impl TransactionAuthenticator for UnreachableAuth {
169 #[allow(clippy::manual_async_fn)]
170 fn get_signature(
171 &self,
172 _pub_key_commitment: PublicKeyCommitment,
173 _signing_inputs: &SigningInputs,
174 ) -> impl FutureMaybeSend<Result<Signature, AuthenticationError>> {
175 async { unreachable!("Type `UnreachableAuth` must not be instantiated") }
176 }
177
178 fn get_public_key(
179 &self,
180 _pub_key_commitment: PublicKeyCommitment,
181 ) -> impl FutureMaybeSend<Option<Arc<PublicKey>>> {
182 async { unreachable!("Type `UnreachableAuth` must not be instantiated") }
183 }
184}
185
186#[derive(Clone, Debug)]
191pub struct BasicAuthenticator {
192 keys: BTreeMap<PublicKeyCommitment, (AuthSecretKey, Arc<PublicKey>)>,
194}
195
196impl BasicAuthenticator {
197 pub fn new(keys: &[AuthSecretKey]) -> Self {
198 let mut key_map = BTreeMap::new();
199 for secret_key in keys {
200 let pub_key = secret_key.public_key();
201 key_map.insert(pub_key.to_commitment(), (secret_key.clone(), pub_key.into()));
202 }
203
204 BasicAuthenticator { keys: key_map }
205 }
206
207 pub fn from_key_pairs(keys: &[(AuthSecretKey, PublicKey)]) -> Self {
208 let mut key_map = BTreeMap::new();
209 for (secret_key, public_key) in keys {
210 key_map.insert(
211 public_key.to_commitment(),
212 (secret_key.clone(), public_key.clone().into()),
213 );
214 }
215
216 BasicAuthenticator { keys: key_map }
217 }
218
219 pub fn keys(&self) -> &BTreeMap<PublicKeyCommitment, (AuthSecretKey, Arc<PublicKey>)> {
224 &self.keys
225 }
226}
227
228impl TransactionAuthenticator for BasicAuthenticator {
229 fn get_signature(
237 &self,
238 pub_key_commitment: PublicKeyCommitment,
239 signing_inputs: &SigningInputs,
240 ) -> impl FutureMaybeSend<Result<Signature, AuthenticationError>> {
241 let message = signing_inputs.to_commitment();
242
243 async move {
244 match self.keys.get(&pub_key_commitment) {
245 Some((auth_key, _)) => Ok(auth_key.sign(message)),
246 None => Err(AuthenticationError::UnknownPublicKey(pub_key_commitment)),
247 }
248 }
249 }
250
251 fn get_public_key(
255 &self,
256 pub_key_commitment: PublicKeyCommitment,
257 ) -> impl FutureMaybeSend<Option<Arc<PublicKey>>> {
258 async move { self.keys.get(&pub_key_commitment).map(|(_, pub_key)| pub_key.clone()) }
259 }
260}
261
262impl TransactionAuthenticator for () {
266 #[allow(clippy::manual_async_fn)]
267 fn get_signature(
268 &self,
269 _pub_key_commitment: PublicKeyCommitment,
270 _signing_inputs: &SigningInputs,
271 ) -> impl FutureMaybeSend<Result<Signature, AuthenticationError>> {
272 async {
273 Err(AuthenticationError::RejectedSignature(
274 "default authenticator cannot provide signatures".to_string(),
275 ))
276 }
277 }
278
279 fn get_public_key(
280 &self,
281 _pub_key_commitment: PublicKeyCommitment,
282 ) -> impl FutureMaybeSend<Option<Arc<PublicKey>>> {
283 async { None }
284 }
285}
286
287#[cfg(test)]
291mod test {
292 use miden_protocol::account::auth::AuthSecretKey;
293 use miden_protocol::utils::serde::{Deserializable, Serializable};
294 use miden_protocol::{Felt, Word};
295
296 use super::SigningInputs;
297
298 #[test]
299 fn serialize_auth_key() {
300 let auth_key = AuthSecretKey::new_falcon512_poseidon2();
301 let serialized = auth_key.to_bytes();
302 let deserialized = AuthSecretKey::read_from_bytes(&serialized).unwrap();
303
304 assert_eq!(auth_key, deserialized);
305 }
306
307 #[test]
308 fn serialize_deserialize_signing_inputs_arbitrary() {
309 let elements = vec![
310 Felt::new(0),
311 Felt::new(1),
312 Felt::new(2),
313 Felt::new(3),
314 Felt::new(4),
315 Felt::new(5),
316 Felt::new(6),
317 Felt::new(7),
318 ];
319 let inputs = SigningInputs::Arbitrary(elements.clone());
320 let bytes = inputs.to_bytes();
321 let decoded = SigningInputs::read_from_bytes(&bytes).unwrap();
322
323 match decoded {
324 SigningInputs::Arbitrary(decoded_elements) => {
325 assert_eq!(decoded_elements, elements);
326 },
327 _ => panic!("expected Arbitrary variant"),
328 }
329 }
330
331 #[test]
332 fn serialize_deserialize_signing_inputs_blind() {
333 let word = Word::from([Felt::new(10), Felt::new(20), Felt::new(30), Felt::new(40)]);
334 let inputs = SigningInputs::Blind(word);
335 let bytes = inputs.to_bytes();
336 let decoded = SigningInputs::read_from_bytes(&bytes).unwrap();
337
338 match decoded {
339 SigningInputs::Blind(w) => {
340 assert_eq!(w, word);
341 },
342 _ => panic!("expected Blind variant"),
343 }
344 }
345}