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::SigningKey) = 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::SigningKey::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::SigningKey::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::SigningKey::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<ecdsa_k256_keccak::PublicKey> for PublicKeyCommitment {
244    fn from(value: ecdsa_k256_keccak::PublicKey) -> Self {
245        Self(value.to_commitment())
246    }
247}
248
249impl From<PublicKeyCommitment> for Word {
250    fn from(value: PublicKeyCommitment) -> Self {
251        value.0
252    }
253}
254
255impl From<Word> for PublicKeyCommitment {
256    fn from(value: Word) -> Self {
257        Self(value)
258    }
259}
260
261/// Public keys of the standard authentication schemes available in the Miden protocol.
262#[derive(Clone, Debug)]
263#[non_exhaustive]
264pub enum PublicKey {
265    Falcon512Poseidon2(falcon512_poseidon2::PublicKey),
266    EcdsaK256Keccak(ecdsa_k256_keccak::PublicKey),
267}
268
269impl PublicKey {
270    /// Returns the authentication scheme of this public key.
271    pub fn auth_scheme(&self) -> AuthScheme {
272        match self {
273            PublicKey::Falcon512Poseidon2(_) => AuthScheme::Falcon512Poseidon2,
274            PublicKey::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak,
275        }
276    }
277
278    /// Returns a commitment to this public key.
279    pub fn to_commitment(&self) -> PublicKeyCommitment {
280        match self {
281            PublicKey::Falcon512Poseidon2(key) => key.to_commitment().into(),
282            PublicKey::EcdsaK256Keccak(key) => key.to_commitment().into(),
283        }
284    }
285
286    /// Verifies the provided signature against the provided message and this public key.
287    pub fn verify(&self, message: Word, signature: Signature) -> bool {
288        match (self, signature) {
289            (PublicKey::Falcon512Poseidon2(key), Signature::Falcon512Poseidon2(sig)) => {
290                key.verify(message, &sig)
291            },
292            (PublicKey::EcdsaK256Keccak(key), Signature::EcdsaK256Keccak(sig)) => {
293                key.verify(message, &sig)
294            },
295            _ => false,
296        }
297    }
298}
299
300impl Serializable for PublicKey {
301    fn write_into<W: ByteWriter>(&self, target: &mut W) {
302        self.auth_scheme().write_into(target);
303        match self {
304            PublicKey::Falcon512Poseidon2(pub_key) => pub_key.write_into(target),
305            PublicKey::EcdsaK256Keccak(pub_key) => pub_key.write_into(target),
306        }
307    }
308}
309
310impl Deserializable for PublicKey {
311    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
312        match source.read::<AuthScheme>()? {
313            AuthScheme::Falcon512Poseidon2 => {
314                let pub_key = falcon512_poseidon2::PublicKey::read_from(source)?;
315                Ok(PublicKey::Falcon512Poseidon2(pub_key))
316            },
317            AuthScheme::EcdsaK256Keccak => {
318                let pub_key = ecdsa_k256_keccak::PublicKey::read_from(source)?;
319                Ok(PublicKey::EcdsaK256Keccak(pub_key))
320            },
321        }
322    }
323}
324
325// SIGNATURE
326// ================================================================================================
327
328/// Represents a signature object ready for native verification.
329///
330/// In order to use this signature within the Miden VM, a preparation step may be necessary to
331/// convert the native signature into a vector of field elements that can be loaded into the advice
332/// provider. To prepare the signature, use the provided `to_prepared_signature` method:
333/// ```rust,no_run
334/// use miden_protocol::account::auth::Signature;
335/// use miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey;
336/// use miden_protocol::{Felt, Word};
337///
338/// let secret_key = SecretKey::new();
339/// let message = Word::default();
340/// let signature: Signature = secret_key.sign(message).into();
341/// let prepared_signature: Vec<Felt> = signature.to_prepared_signature(message);
342/// ```
343#[derive(Clone, Debug)]
344#[repr(u8)]
345pub enum Signature {
346    Falcon512Poseidon2(falcon512_poseidon2::Signature) = FALCON512_POSEIDON2,
347    EcdsaK256Keccak(ecdsa_k256_keccak::Signature) = ECDSA_K256_KECCAK,
348}
349
350impl Signature {
351    /// Returns the authentication scheme of this signature.
352    pub fn auth_scheme(&self) -> AuthScheme {
353        match self {
354            Signature::Falcon512Poseidon2(_) => AuthScheme::Falcon512Poseidon2,
355            Signature::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak,
356        }
357    }
358
359    /// Converts this signature to a sequence of field elements in the format expected by the
360    /// native verification procedure in the VM.
361    ///
362    /// The order of elements in the returned vector is reversed because it is expected that the
363    /// data will be pushed into the advice stack
364    pub fn to_prepared_signature(&self, msg: Word) -> Vec<Felt> {
365        // TODO: the `expect()` should be changed to an error; but that will be a part of a bigger
366        // refactoring
367        match self {
368            Signature::Falcon512Poseidon2(sig) => {
369                miden_core_lib::dsa::falcon512_poseidon2::encode_signature(sig.public_key(), sig)
370            },
371            Signature::EcdsaK256Keccak(sig) => {
372                let pk = ecdsa_k256_keccak::PublicKey::recover_from(msg, sig)
373                    .expect("inferring public key from signature and message should succeed");
374                miden_core_lib::dsa::ecdsa_k256_keccak::encode_signature(&pk, sig)
375            },
376        }
377    }
378}
379
380impl From<falcon512_poseidon2::Signature> for Signature {
381    fn from(signature: falcon512_poseidon2::Signature) -> Self {
382        Signature::Falcon512Poseidon2(signature)
383    }
384}
385
386impl Serializable for Signature {
387    fn write_into<W: ByteWriter>(&self, target: &mut W) {
388        self.auth_scheme().write_into(target);
389        match self {
390            Signature::Falcon512Poseidon2(signature) => signature.write_into(target),
391            Signature::EcdsaK256Keccak(signature) => signature.write_into(target),
392        }
393    }
394}
395
396impl Deserializable for Signature {
397    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
398        match source.read::<AuthScheme>()? {
399            AuthScheme::Falcon512Poseidon2 => {
400                let signature = falcon512_poseidon2::Signature::read_from(source)?;
401                Ok(Signature::Falcon512Poseidon2(signature))
402            },
403            AuthScheme::EcdsaK256Keccak => {
404                let signature = ecdsa_k256_keccak::Signature::read_from(source)?;
405                Ok(Signature::EcdsaK256Keccak(signature))
406            },
407        }
408    }
409}