Skip to main content

miden_protocol/account/
auth.rs

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