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    ///
44    /// # Privacy
45    ///
46    /// Unlike [`Falcon512Poseidon2`](Self::Falcon512Poseidon2), which is verified entirely
47    /// in-circuit, this scheme is verified via a precompile. Under the current native
48    /// re-verification model, the precompile calldata - the raw 33-byte compressed secp256k1 public
49    /// key and the 65-byte signature - must be carried inside the transaction proof for it to
50    /// verify: the verifier recomputes the precompile transcript from that calldata and binds it
51    /// into the proof's public inputs, so it cannot be stripped or withheld. As a result the public
52    /// key and signature are disclosed to the node operator and to any party on the transaction
53    /// submission or gossip path, even though the account commits on-chain only to `Poseidon2(pk)`.
54    ///
55    /// This means `EcdsaK256Keccak` does not provide the public-key privacy that commitment-based
56    /// storage otherwise implies. Integrators requiring signer-key privacy should select
57    /// [`Falcon512Poseidon2`](Self::Falcon512Poseidon2) instead, which emits no public-key or
58    /// signature calldata.
59    EcdsaK256Keccak = ECDSA_K256_KECCAK,
60}
61
62impl AuthScheme {
63    /// Returns a numerical value of this auth scheme.
64    pub fn as_u8(&self) -> u8 {
65        *self as u8
66    }
67}
68
69impl core::fmt::Display for AuthScheme {
70    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
71        match self {
72            Self::Falcon512Poseidon2 => f.write_str(FALCON512_POSEIDON2_STR),
73            Self::EcdsaK256Keccak => f.write_str(ECDSA_K256_KECCAK_STR),
74        }
75    }
76}
77
78impl TryFrom<u8> for AuthScheme {
79    type Error = AuthSchemeError;
80
81    fn try_from(value: u8) -> Result<Self, Self::Error> {
82        match value {
83            FALCON512_POSEIDON2 => Ok(Self::Falcon512Poseidon2),
84            ECDSA_K256_KECCAK => Ok(Self::EcdsaK256Keccak),
85            value => Err(AuthSchemeError::InvalidAuthSchemeIdentifier(value.to_string())),
86        }
87    }
88}
89
90impl FromStr for AuthScheme {
91    type Err = AuthSchemeError;
92
93    fn from_str(input: &str) -> Result<Self, Self::Err> {
94        match input {
95            FALCON512_POSEIDON2_STR => Ok(AuthScheme::Falcon512Poseidon2),
96            ECDSA_K256_KECCAK_STR => Ok(AuthScheme::EcdsaK256Keccak),
97            other => Err(AuthSchemeError::InvalidAuthSchemeIdentifier(other.to_owned())),
98        }
99    }
100}
101
102impl Serializable for AuthScheme {
103    fn write_into<W: ByteWriter>(&self, target: &mut W) {
104        target.write_u8(*self as u8);
105    }
106
107    fn get_size_hint(&self) -> usize {
108        // auth scheme is encoded as a single byte
109        size_of::<u8>()
110    }
111}
112
113impl Deserializable for AuthScheme {
114    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
115        match source.read_u8()? {
116            FALCON512_POSEIDON2 => Ok(Self::Falcon512Poseidon2),
117            ECDSA_K256_KECCAK => Ok(Self::EcdsaK256Keccak),
118            value => Err(DeserializationError::InvalidValue(format!(
119                "auth scheme identifier `{value}` is not valid"
120            ))),
121        }
122    }
123}
124
125// AUTH SECRET KEY
126// ================================================================================================
127
128/// Secret keys of the standard [`AuthScheme`]s available in the Miden protocol.
129#[derive(Clone, Debug, PartialEq, Eq)]
130#[non_exhaustive]
131#[repr(u8)]
132pub enum AuthSecretKey {
133    Falcon512Poseidon2(falcon512_poseidon2::SecretKey) = FALCON512_POSEIDON2,
134    EcdsaK256Keccak(ecdsa_k256_keccak::SigningKey) = ECDSA_K256_KECCAK,
135}
136
137impl AuthSecretKey {
138    /// Generates an Falcon512Poseidon2 secret key from the OS-provided randomness.
139    #[cfg(feature = "std")]
140    pub fn new_falcon512_poseidon2() -> Self {
141        Self::Falcon512Poseidon2(falcon512_poseidon2::SecretKey::new())
142    }
143
144    /// Generates an Falcon512Poseidon2 secrete key using the provided random number generator.
145    pub fn new_falcon512_poseidon2_with_rng<R: Rng>(rng: &mut R) -> Self {
146        Self::Falcon512Poseidon2(falcon512_poseidon2::SecretKey::with_rng::<R>(rng))
147    }
148
149    /// Generates an EcdsaK256Keccak secret key from the OS-provided randomness.
150    ///
151    /// Note: the EcdsaK256Keccak scheme discloses the signer's public key and signature at proving
152    /// time and therefore does not provide public-key privacy. See
153    /// [`AuthScheme::EcdsaK256Keccak`] for details.
154    #[cfg(feature = "std")]
155    pub fn new_ecdsa_k256_keccak() -> Self {
156        Self::EcdsaK256Keccak(ecdsa_k256_keccak::SigningKey::new())
157    }
158
159    /// Generates an EcdsaK256Keccak secret key using the provided random number generator.
160    ///
161    /// Note: the EcdsaK256Keccak scheme discloses the signer's public key and signature at proving
162    /// time and therefore does not provide public-key privacy. See
163    /// [`AuthScheme::EcdsaK256Keccak`] for details.
164    pub fn new_ecdsa_k256_keccak_with_rng<R: Rng + CryptoRng>(rng: &mut R) -> Self {
165        Self::EcdsaK256Keccak(ecdsa_k256_keccak::SigningKey::with_rng::<R>(rng))
166    }
167
168    /// Generates a new secret key for the specified authentication scheme using the provided
169    /// random number generator.
170    ///
171    /// Returns an error if the specified authentication scheme is not supported.
172    pub fn with_scheme_and_rng<R: Rng + CryptoRng>(
173        scheme: AuthScheme,
174        rng: &mut R,
175    ) -> Result<Self, AuthSchemeError> {
176        match scheme {
177            AuthScheme::Falcon512Poseidon2 => Ok(Self::new_falcon512_poseidon2_with_rng(rng)),
178            AuthScheme::EcdsaK256Keccak => Ok(Self::new_ecdsa_k256_keccak_with_rng(rng)),
179        }
180    }
181
182    /// Generates a new secret key for the specified authentication scheme from the
183    /// OS-provided randomness.
184    ///
185    /// Returns an error if the specified authentication scheme is not supported.
186    #[cfg(feature = "std")]
187    pub fn with_scheme(scheme: AuthScheme) -> Result<Self, AuthSchemeError> {
188        match scheme {
189            AuthScheme::Falcon512Poseidon2 => Ok(Self::new_falcon512_poseidon2()),
190            AuthScheme::EcdsaK256Keccak => Ok(Self::new_ecdsa_k256_keccak()),
191        }
192    }
193
194    /// Returns the authentication scheme of this secret key.
195    pub fn auth_scheme(&self) -> AuthScheme {
196        match self {
197            AuthSecretKey::Falcon512Poseidon2(_) => AuthScheme::Falcon512Poseidon2,
198            AuthSecretKey::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak,
199        }
200    }
201
202    /// Returns a public key associated with this secret key.
203    pub fn public_key(&self) -> PublicKey {
204        match self {
205            AuthSecretKey::Falcon512Poseidon2(key) => {
206                PublicKey::Falcon512Poseidon2(key.public_key())
207            },
208            AuthSecretKey::EcdsaK256Keccak(key) => PublicKey::EcdsaK256Keccak(key.public_key()),
209        }
210    }
211
212    /// Signs the provided message with this secret key.
213    pub fn sign(&self, message: Word) -> Signature {
214        match self {
215            AuthSecretKey::Falcon512Poseidon2(key) => {
216                Signature::Falcon512Poseidon2(key.sign(message))
217            },
218            AuthSecretKey::EcdsaK256Keccak(key) => Signature::EcdsaK256Keccak(key.sign(message)),
219        }
220    }
221}
222
223impl Serializable for AuthSecretKey {
224    fn write_into<W: ByteWriter>(&self, target: &mut W) {
225        self.auth_scheme().write_into(target);
226        match self {
227            AuthSecretKey::Falcon512Poseidon2(key) => key.write_into(target),
228            AuthSecretKey::EcdsaK256Keccak(key) => key.write_into(target),
229        }
230    }
231}
232
233impl Deserializable for AuthSecretKey {
234    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
235        match source.read::<AuthScheme>()? {
236            AuthScheme::Falcon512Poseidon2 => {
237                let secret_key = falcon512_poseidon2::SecretKey::read_from(source)?;
238                Ok(AuthSecretKey::Falcon512Poseidon2(secret_key))
239            },
240            AuthScheme::EcdsaK256Keccak => {
241                let secret_key = ecdsa_k256_keccak::SigningKey::read_from(source)?;
242                Ok(AuthSecretKey::EcdsaK256Keccak(secret_key))
243            },
244        }
245    }
246}
247
248// PUBLIC KEY
249// ================================================================================================
250
251/// Commitment to a public key.
252#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
253pub struct PublicKeyCommitment(Word);
254
255impl core::fmt::Display for PublicKeyCommitment {
256    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
257        write!(f, "{}", self.0)
258    }
259}
260
261impl From<falcon512_poseidon2::PublicKey> for PublicKeyCommitment {
262    fn from(value: falcon512_poseidon2::PublicKey) -> Self {
263        Self(value.to_commitment())
264    }
265}
266
267impl From<ecdsa_k256_keccak::PublicKey> for PublicKeyCommitment {
268    fn from(value: ecdsa_k256_keccak::PublicKey) -> Self {
269        Self(value.to_commitment())
270    }
271}
272
273impl From<PublicKeyCommitment> for Word {
274    fn from(value: PublicKeyCommitment) -> Self {
275        value.0
276    }
277}
278
279impl From<Word> for PublicKeyCommitment {
280    fn from(value: Word) -> Self {
281        Self(value)
282    }
283}
284
285/// Public keys of the standard authentication schemes available in the Miden protocol.
286#[derive(Clone, Debug)]
287#[non_exhaustive]
288pub enum PublicKey {
289    Falcon512Poseidon2(falcon512_poseidon2::PublicKey),
290    EcdsaK256Keccak(ecdsa_k256_keccak::PublicKey),
291}
292
293impl PublicKey {
294    /// Returns the authentication scheme of this public key.
295    pub fn auth_scheme(&self) -> AuthScheme {
296        match self {
297            PublicKey::Falcon512Poseidon2(_) => AuthScheme::Falcon512Poseidon2,
298            PublicKey::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak,
299        }
300    }
301
302    /// Returns a commitment to this public key.
303    pub fn to_commitment(&self) -> PublicKeyCommitment {
304        match self {
305            PublicKey::Falcon512Poseidon2(key) => key.to_commitment().into(),
306            PublicKey::EcdsaK256Keccak(key) => key.to_commitment().into(),
307        }
308    }
309
310    /// Verifies the provided signature against the provided message and this public key.
311    pub fn verify(&self, message: Word, signature: Signature) -> bool {
312        match (self, signature) {
313            (PublicKey::Falcon512Poseidon2(key), Signature::Falcon512Poseidon2(sig)) => {
314                key.verify(message, &sig)
315            },
316            (PublicKey::EcdsaK256Keccak(key), Signature::EcdsaK256Keccak(sig)) => {
317                key.verify(message, &sig)
318            },
319            _ => false,
320        }
321    }
322}
323
324impl Serializable for PublicKey {
325    fn write_into<W: ByteWriter>(&self, target: &mut W) {
326        self.auth_scheme().write_into(target);
327        match self {
328            PublicKey::Falcon512Poseidon2(pub_key) => pub_key.write_into(target),
329            PublicKey::EcdsaK256Keccak(pub_key) => pub_key.write_into(target),
330        }
331    }
332}
333
334impl Deserializable for PublicKey {
335    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
336        match source.read::<AuthScheme>()? {
337            AuthScheme::Falcon512Poseidon2 => {
338                let pub_key = falcon512_poseidon2::PublicKey::read_from(source)?;
339                Ok(PublicKey::Falcon512Poseidon2(pub_key))
340            },
341            AuthScheme::EcdsaK256Keccak => {
342                let pub_key = ecdsa_k256_keccak::PublicKey::read_from(source)?;
343                Ok(PublicKey::EcdsaK256Keccak(pub_key))
344            },
345        }
346    }
347}
348
349// SIGNATURE
350// ================================================================================================
351
352/// Represents a signature object ready for native verification.
353///
354/// In order to use this signature within the Miden VM, a preparation step may be necessary to
355/// convert the native signature into a vector of field elements that can be loaded into the advice
356/// provider. To prepare the signature, use the provided `to_prepared_signature` method:
357/// ```rust,no_run
358/// use miden_protocol::account::auth::Signature;
359/// use miden_protocol::crypto::dsa::falcon512_poseidon2::SecretKey;
360/// use miden_protocol::{Felt, Word};
361///
362/// let secret_key = SecretKey::new();
363/// let message = Word::default();
364/// let signature: Signature = secret_key.sign(message).into();
365/// let prepared_signature: Vec<Felt> = signature.to_prepared_signature(message);
366/// ```
367#[derive(Clone, Debug)]
368#[repr(u8)]
369pub enum Signature {
370    Falcon512Poseidon2(falcon512_poseidon2::Signature) = FALCON512_POSEIDON2,
371    EcdsaK256Keccak(ecdsa_k256_keccak::Signature) = ECDSA_K256_KECCAK,
372}
373
374impl Signature {
375    /// Returns the authentication scheme of this signature.
376    pub fn auth_scheme(&self) -> AuthScheme {
377        match self {
378            Signature::Falcon512Poseidon2(_) => AuthScheme::Falcon512Poseidon2,
379            Signature::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak,
380        }
381    }
382
383    /// Converts this signature to a sequence of field elements in the format expected by the
384    /// native verification procedure in the VM.
385    ///
386    /// The order of elements in the returned vector is reversed because it is expected that the
387    /// data will be pushed into the advice stack
388    pub fn to_prepared_signature(&self, msg: Word) -> Vec<Felt> {
389        // TODO: the `expect()` should be changed to an error; but that will be a part of a bigger
390        // refactoring
391        match self {
392            Signature::Falcon512Poseidon2(sig) => {
393                miden_core_lib::dsa::falcon512_poseidon2::encode_signature(sig.public_key(), sig)
394            },
395            Signature::EcdsaK256Keccak(sig) => {
396                let pk = ecdsa_k256_keccak::PublicKey::recover_from(msg, sig)
397                    .expect("inferring public key from signature and message should succeed");
398                miden_core_lib::dsa::ecdsa_k256_keccak::encode_signature(&pk, sig)
399            },
400        }
401    }
402}
403
404impl From<falcon512_poseidon2::Signature> for Signature {
405    fn from(signature: falcon512_poseidon2::Signature) -> Self {
406        Signature::Falcon512Poseidon2(signature)
407    }
408}
409
410impl Serializable for Signature {
411    fn write_into<W: ByteWriter>(&self, target: &mut W) {
412        self.auth_scheme().write_into(target);
413        match self {
414            Signature::Falcon512Poseidon2(signature) => signature.write_into(target),
415            Signature::EcdsaK256Keccak(signature) => signature.write_into(target),
416        }
417    }
418}
419
420impl Deserializable for Signature {
421    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
422        match source.read::<AuthScheme>()? {
423            AuthScheme::Falcon512Poseidon2 => {
424                let signature = falcon512_poseidon2::Signature::read_from(source)?;
425                Ok(Signature::Falcon512Poseidon2(signature))
426            },
427            AuthScheme::EcdsaK256Keccak => {
428                let signature = ecdsa_k256_keccak::Signature::read_from(source)?;
429                Ok(Signature::EcdsaK256Keccak(signature))
430            },
431        }
432    }
433}