Skip to main content

miden_protocol/account/
auth.rs

1use alloc::borrow::ToOwned;
2use alloc::string::ToString;
3use alloc::vec::Vec;
4use core::str::FromStr;
5
6use rand::{CryptoRng, Rng};
7
8use crate::crypto::dsa::{ecdsa_k256_keccak, falcon512_poseidon2};
9use crate::errors::AuthSchemeError;
10use crate::utils::serde::{
11    ByteReader,
12    ByteWriter,
13    Deserializable,
14    DeserializationError,
15    Serializable,
16};
17use crate::{Felt, Word};
18
19// AUTH SCHEME
20// ================================================================================================
21
22/// Identifier of signature schemes use for transaction authentication
23const FALCON512_POSEIDON2: u8 = 2;
24const ECDSA_K256_KECCAK: u8 = 1;
25
26const FALCON512_POSEIDON2_STR: &str = "Falcon512Poseidon2";
27const ECDSA_K256_KECCAK_STR: &str = "EcdsaK256Keccak";
28
29/// Defines standard authentication schemes (i.e., signature schemes) available in the Miden
30/// protocol.
31#[derive(Copy, Clone, Debug, PartialEq, Eq)]
32#[non_exhaustive]
33#[repr(u8)]
34pub enum AuthScheme {
35    /// A deterministic Falcon512 signature scheme.
36    ///
37    /// This version differs from the reference Falcon512 implementation in its use of the poseidon2
38    /// hash function in its hash-to-point algorithm to make signatures very efficient to verify
39    /// inside Miden VM.
40    Falcon512Poseidon2 = FALCON512_POSEIDON2,
41
42    /// ECDSA signature scheme over secp256k1 curve using Keccak to hash the messages when signing.
43    EcdsaK256Keccak = ECDSA_K256_KECCAK,
44}
45
46impl AuthScheme {
47    /// Returns a numerical value of this auth scheme.
48    pub fn as_u8(&self) -> u8 {
49        *self as u8
50    }
51}
52
53impl core::fmt::Display for AuthScheme {
54    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
55        match self {
56            Self::Falcon512Poseidon2 => f.write_str(FALCON512_POSEIDON2_STR),
57            Self::EcdsaK256Keccak => f.write_str(ECDSA_K256_KECCAK_STR),
58        }
59    }
60}
61
62impl TryFrom<u8> for AuthScheme {
63    type Error = AuthSchemeError;
64
65    fn try_from(value: u8) -> Result<Self, Self::Error> {
66        match value {
67            FALCON512_POSEIDON2 => Ok(Self::Falcon512Poseidon2),
68            ECDSA_K256_KECCAK => Ok(Self::EcdsaK256Keccak),
69            value => Err(AuthSchemeError::InvalidAuthSchemeIdentifier(value.to_string())),
70        }
71    }
72}
73
74impl FromStr for AuthScheme {
75    type Err = AuthSchemeError;
76
77    fn from_str(input: &str) -> Result<Self, Self::Err> {
78        match input {
79            FALCON512_POSEIDON2_STR => Ok(AuthScheme::Falcon512Poseidon2),
80            ECDSA_K256_KECCAK_STR => Ok(AuthScheme::EcdsaK256Keccak),
81            other => Err(AuthSchemeError::InvalidAuthSchemeIdentifier(other.to_owned())),
82        }
83    }
84}
85
86impl Serializable for AuthScheme {
87    fn write_into<W: ByteWriter>(&self, target: &mut W) {
88        target.write_u8(*self as u8);
89    }
90
91    fn get_size_hint(&self) -> usize {
92        // auth scheme is encoded as a single byte
93        size_of::<u8>()
94    }
95}
96
97impl Deserializable for AuthScheme {
98    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
99        match source.read_u8()? {
100            FALCON512_POSEIDON2 => Ok(Self::Falcon512Poseidon2),
101            ECDSA_K256_KECCAK => Ok(Self::EcdsaK256Keccak),
102            value => Err(DeserializationError::InvalidValue(format!(
103                "auth scheme identifier `{value}` is not valid"
104            ))),
105        }
106    }
107}
108
109// AUTH SECRET KEY
110// ================================================================================================
111
112/// Secret keys of the standard [`AuthScheme`]s available in the Miden protocol.
113#[derive(Clone, Debug, PartialEq, Eq)]
114#[non_exhaustive]
115#[repr(u8)]
116pub enum AuthSecretKey {
117    Falcon512Poseidon2(falcon512_poseidon2::SecretKey) = FALCON512_POSEIDON2,
118    EcdsaK256Keccak(ecdsa_k256_keccak::SecretKey) = ECDSA_K256_KECCAK,
119}
120
121impl AuthSecretKey {
122    /// Generates an Falcon512Poseidon2 secret key from the OS-provided randomness.
123    #[cfg(feature = "std")]
124    pub fn new_falcon512_poseidon2() -> Self {
125        Self::Falcon512Poseidon2(falcon512_poseidon2::SecretKey::new())
126    }
127
128    /// Generates an Falcon512Poseidon2 secrete key using the provided random number generator.
129    pub fn new_falcon512_poseidon2_with_rng<R: Rng>(rng: &mut R) -> Self {
130        Self::Falcon512Poseidon2(falcon512_poseidon2::SecretKey::with_rng(rng))
131    }
132
133    /// Generates an EcdsaK256Keccak secret key from the OS-provided randomness.
134    #[cfg(feature = "std")]
135    pub fn new_ecdsa_k256_keccak() -> Self {
136        Self::EcdsaK256Keccak(ecdsa_k256_keccak::SecretKey::new())
137    }
138
139    /// Generates an EcdsaK256Keccak secret key using the provided random number generator.
140    pub fn new_ecdsa_k256_keccak_with_rng<R: Rng + CryptoRng>(rng: &mut R) -> Self {
141        Self::EcdsaK256Keccak(ecdsa_k256_keccak::SecretKey::with_rng(rng))
142    }
143
144    /// Generates a new secret key for the specified authentication scheme using the provided
145    /// random number generator.
146    ///
147    /// Returns an error if the specified authentication scheme is not supported.
148    pub fn with_scheme_and_rng<R: Rng + CryptoRng>(
149        scheme: AuthScheme,
150        rng: &mut R,
151    ) -> Result<Self, AuthSchemeError> {
152        match scheme {
153            AuthScheme::Falcon512Poseidon2 => Ok(Self::new_falcon512_poseidon2_with_rng(rng)),
154            AuthScheme::EcdsaK256Keccak => Ok(Self::new_ecdsa_k256_keccak_with_rng(rng)),
155        }
156    }
157
158    /// Generates a new secret key for the specified authentication scheme from the
159    /// OS-provided randomness.
160    ///
161    /// Returns an error if the specified authentication scheme is not supported.
162    #[cfg(feature = "std")]
163    pub fn with_scheme(scheme: AuthScheme) -> Result<Self, AuthSchemeError> {
164        match scheme {
165            AuthScheme::Falcon512Poseidon2 => Ok(Self::new_falcon512_poseidon2()),
166            AuthScheme::EcdsaK256Keccak => Ok(Self::new_ecdsa_k256_keccak()),
167        }
168    }
169
170    /// Returns the authentication scheme of this secret key.
171    pub fn auth_scheme(&self) -> AuthScheme {
172        match self {
173            AuthSecretKey::Falcon512Poseidon2(_) => AuthScheme::Falcon512Poseidon2,
174            AuthSecretKey::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak,
175        }
176    }
177
178    /// Returns a public key associated with this secret key.
179    pub fn public_key(&self) -> PublicKey {
180        match self {
181            AuthSecretKey::Falcon512Poseidon2(key) => {
182                PublicKey::Falcon512Poseidon2(key.public_key())
183            },
184            AuthSecretKey::EcdsaK256Keccak(key) => PublicKey::EcdsaK256Keccak(key.public_key()),
185        }
186    }
187
188    /// Signs the provided message with this secret key.
189    pub fn sign(&self, message: Word) -> Signature {
190        match self {
191            AuthSecretKey::Falcon512Poseidon2(key) => {
192                Signature::Falcon512Poseidon2(key.sign(message))
193            },
194            AuthSecretKey::EcdsaK256Keccak(key) => Signature::EcdsaK256Keccak(key.sign(message)),
195        }
196    }
197}
198
199impl Serializable for AuthSecretKey {
200    fn write_into<W: ByteWriter>(&self, target: &mut W) {
201        self.auth_scheme().write_into(target);
202        match self {
203            AuthSecretKey::Falcon512Poseidon2(key) => key.write_into(target),
204            AuthSecretKey::EcdsaK256Keccak(key) => key.write_into(target),
205        }
206    }
207}
208
209impl Deserializable for AuthSecretKey {
210    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
211        match source.read::<AuthScheme>()? {
212            AuthScheme::Falcon512Poseidon2 => {
213                let secret_key = falcon512_poseidon2::SecretKey::read_from(source)?;
214                Ok(AuthSecretKey::Falcon512Poseidon2(secret_key))
215            },
216            AuthScheme::EcdsaK256Keccak => {
217                let secret_key = ecdsa_k256_keccak::SecretKey::read_from(source)?;
218                Ok(AuthSecretKey::EcdsaK256Keccak(secret_key))
219            },
220        }
221    }
222}
223
224// PUBLIC KEY
225// ================================================================================================
226
227/// Commitment to a public key.
228#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
229pub struct PublicKeyCommitment(Word);
230
231impl core::fmt::Display for PublicKeyCommitment {
232    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
233        write!(f, "{}", self.0)
234    }
235}
236
237impl From<falcon512_poseidon2::PublicKey> for PublicKeyCommitment {
238    fn from(value: falcon512_poseidon2::PublicKey) -> Self {
239        Self(value.to_commitment())
240    }
241}
242
243impl From<PublicKeyCommitment> for Word {
244    fn from(value: PublicKeyCommitment) -> Self {
245        value.0
246    }
247}
248
249impl From<Word> for PublicKeyCommitment {
250    fn from(value: Word) -> Self {
251        Self(value)
252    }
253}
254
255/// Public keys of the standard authentication schemes available in the Miden protocol.
256#[derive(Clone, Debug)]
257#[non_exhaustive]
258pub enum PublicKey {
259    Falcon512Poseidon2(falcon512_poseidon2::PublicKey),
260    EcdsaK256Keccak(ecdsa_k256_keccak::PublicKey),
261}
262
263impl PublicKey {
264    /// Returns the authentication scheme of this public key.
265    pub fn auth_scheme(&self) -> AuthScheme {
266        match self {
267            PublicKey::Falcon512Poseidon2(_) => AuthScheme::Falcon512Poseidon2,
268            PublicKey::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak,
269        }
270    }
271
272    /// Returns a commitment to this public key.
273    pub fn to_commitment(&self) -> PublicKeyCommitment {
274        match self {
275            PublicKey::Falcon512Poseidon2(key) => key.to_commitment().into(),
276            PublicKey::EcdsaK256Keccak(key) => key.to_commitment().into(),
277        }
278    }
279
280    /// Verifies the provided signature against the provided message and this public key.
281    pub fn verify(&self, message: Word, signature: Signature) -> bool {
282        match (self, signature) {
283            (PublicKey::Falcon512Poseidon2(key), Signature::Falcon512Poseidon2(sig)) => {
284                key.verify(message, &sig)
285            },
286            (PublicKey::EcdsaK256Keccak(key), Signature::EcdsaK256Keccak(sig)) => {
287                key.verify(message, &sig)
288            },
289            _ => false,
290        }
291    }
292}
293
294impl Serializable for PublicKey {
295    fn write_into<W: ByteWriter>(&self, target: &mut W) {
296        self.auth_scheme().write_into(target);
297        match self {
298            PublicKey::Falcon512Poseidon2(pub_key) => pub_key.write_into(target),
299            PublicKey::EcdsaK256Keccak(pub_key) => pub_key.write_into(target),
300        }
301    }
302}
303
304impl Deserializable for PublicKey {
305    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
306        match source.read::<AuthScheme>()? {
307            AuthScheme::Falcon512Poseidon2 => {
308                let pub_key = falcon512_poseidon2::PublicKey::read_from(source)?;
309                Ok(PublicKey::Falcon512Poseidon2(pub_key))
310            },
311            AuthScheme::EcdsaK256Keccak => {
312                let pub_key = ecdsa_k256_keccak::PublicKey::read_from(source)?;
313                Ok(PublicKey::EcdsaK256Keccak(pub_key))
314            },
315        }
316    }
317}
318
319// SIGNATURE
320// ================================================================================================
321
322/// Represents a signature object ready for native verification.
323///
324/// In order to use this signature within the Miden VM, a preparation step may be necessary to
325/// convert the native signature into a vector of field elements that can be loaded into the advice
326/// provider. To prepare the signature, use the provided `to_prepared_signature` method:
327/// ```rust,no_run
328/// use miden_protocol::account::auth::Signature;
329/// use miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey;
330/// use miden_protocol::{Felt, Word};
331///
332/// let secret_key = SecretKey::new();
333/// let message = Word::default();
334/// let signature: Signature = secret_key.sign(message).into();
335/// let prepared_signature: Vec<Felt> = signature.to_prepared_signature(message);
336/// ```
337#[derive(Clone, Debug)]
338#[repr(u8)]
339pub enum Signature {
340    Falcon512Poseidon2(falcon512_poseidon2::Signature) = FALCON512_POSEIDON2,
341    EcdsaK256Keccak(ecdsa_k256_keccak::Signature) = ECDSA_K256_KECCAK,
342}
343
344impl Signature {
345    /// Returns the authentication scheme of this signature.
346    pub fn auth_scheme(&self) -> AuthScheme {
347        match self {
348            Signature::Falcon512Poseidon2(_) => AuthScheme::Falcon512Poseidon2,
349            Signature::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak,
350        }
351    }
352
353    /// Converts this signature to a sequence of field elements in the format expected by the
354    /// native verification procedure in the VM.
355    ///
356    /// The order of elements in the returned vector is reversed because it is expected that the
357    /// data will be pushed into the advice stack
358    pub fn to_prepared_signature(&self, msg: Word) -> Vec<Felt> {
359        // TODO: the `expect()` should be changed to an error; but that will be a part of a bigger
360        // refactoring
361        match self {
362            Signature::Falcon512Poseidon2(sig) => {
363                miden_core_lib::dsa::falcon512_poseidon2::encode_signature(sig.public_key(), sig)
364            },
365            Signature::EcdsaK256Keccak(sig) => {
366                let pk = ecdsa_k256_keccak::PublicKey::recover_from(msg, sig)
367                    .expect("inferring public key from signature and message should succeed");
368                miden_core_lib::dsa::ecdsa_k256_keccak::encode_signature(&pk, sig)
369            },
370        }
371    }
372}
373
374impl From<falcon512_poseidon2::Signature> for Signature {
375    fn from(signature: falcon512_poseidon2::Signature) -> Self {
376        Signature::Falcon512Poseidon2(signature)
377    }
378}
379
380impl Serializable for Signature {
381    fn write_into<W: ByteWriter>(&self, target: &mut W) {
382        self.auth_scheme().write_into(target);
383        match self {
384            Signature::Falcon512Poseidon2(signature) => signature.write_into(target),
385            Signature::EcdsaK256Keccak(signature) => signature.write_into(target),
386        }
387    }
388}
389
390impl Deserializable for Signature {
391    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
392        match source.read::<AuthScheme>()? {
393            AuthScheme::Falcon512Poseidon2 => {
394                let signature = falcon512_poseidon2::Signature::read_from(source)?;
395                Ok(Signature::Falcon512Poseidon2(signature))
396            },
397            AuthScheme::EcdsaK256Keccak => {
398                let signature = ecdsa_k256_keccak::Signature::read_from(source)?;
399                Ok(Signature::EcdsaK256Keccak(signature))
400            },
401        }
402    }
403}