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