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 = 2;
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    /// Generates a new secret key for the specified authentication scheme using the provided
127    /// random number generator.
128    ///
129    /// Returns an error if the specified authentication scheme is not supported.
130    pub fn with_scheme_and_rng<R: Rng + CryptoRng>(
131        scheme: AuthScheme,
132        rng: &mut R,
133    ) -> Result<Self, AuthSchemeError> {
134        match scheme {
135            AuthScheme::Falcon512Rpo => Ok(Self::new_falcon512_rpo_with_rng(rng)),
136            AuthScheme::EcdsaK256Keccak => Ok(Self::new_ecdsa_k256_keccak_with_rng(rng)),
137        }
138    }
139
140    /// Generates a new secret key for the specified authentication scheme from the
141    /// OS-provided randomness.
142    ///
143    /// Returns an error if the specified authentication scheme is not supported.
144    #[cfg(feature = "std")]
145    pub fn with_scheme(scheme: AuthScheme) -> Result<Self, AuthSchemeError> {
146        match scheme {
147            AuthScheme::Falcon512Rpo => Ok(Self::new_falcon512_rpo()),
148            AuthScheme::EcdsaK256Keccak => Ok(Self::new_ecdsa_k256_keccak()),
149        }
150    }
151
152    /// Returns the authentication scheme of this secret key.
153    pub fn auth_scheme(&self) -> AuthScheme {
154        match self {
155            AuthSecretKey::Falcon512Rpo(_) => AuthScheme::Falcon512Rpo,
156            AuthSecretKey::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak,
157        }
158    }
159
160    /// Returns a public key associated with this secret key.
161    pub fn public_key(&self) -> PublicKey {
162        match self {
163            AuthSecretKey::Falcon512Rpo(key) => PublicKey::Falcon512Rpo(key.public_key()),
164            AuthSecretKey::EcdsaK256Keccak(key) => PublicKey::EcdsaK256Keccak(key.public_key()),
165        }
166    }
167
168    /// Signs the provided message with this secret key.
169    pub fn sign(&self, message: Word) -> Signature {
170        match self {
171            AuthSecretKey::Falcon512Rpo(key) => Signature::Falcon512Rpo(key.sign(message)),
172            AuthSecretKey::EcdsaK256Keccak(key) => Signature::EcdsaK256Keccak(key.sign(message)),
173        }
174    }
175}
176
177impl Serializable for AuthSecretKey {
178    fn write_into<W: ByteWriter>(&self, target: &mut W) {
179        self.auth_scheme().write_into(target);
180        match self {
181            AuthSecretKey::Falcon512Rpo(key) => key.write_into(target),
182            AuthSecretKey::EcdsaK256Keccak(key) => key.write_into(target),
183        }
184    }
185}
186
187impl Deserializable for AuthSecretKey {
188    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
189        match source.read::<AuthScheme>()? {
190            AuthScheme::Falcon512Rpo => {
191                let secret_key = falcon512_rpo::SecretKey::read_from(source)?;
192                Ok(AuthSecretKey::Falcon512Rpo(secret_key))
193            },
194            AuthScheme::EcdsaK256Keccak => {
195                let secret_key = ecdsa_k256_keccak::SecretKey::read_from(source)?;
196                Ok(AuthSecretKey::EcdsaK256Keccak(secret_key))
197            },
198        }
199    }
200}
201
202// PUBLIC KEY
203// ================================================================================================
204
205/// Commitment to a public key.
206#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
207pub struct PublicKeyCommitment(Word);
208
209impl core::fmt::Display for PublicKeyCommitment {
210    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
211        write!(f, "{}", self.0)
212    }
213}
214
215impl From<falcon512_rpo::PublicKey> for PublicKeyCommitment {
216    fn from(value: falcon512_rpo::PublicKey) -> Self {
217        Self(value.to_commitment())
218    }
219}
220
221impl From<PublicKeyCommitment> for Word {
222    fn from(value: PublicKeyCommitment) -> Self {
223        value.0
224    }
225}
226
227impl From<Word> for PublicKeyCommitment {
228    fn from(value: Word) -> Self {
229        Self(value)
230    }
231}
232
233/// Public keys of the standard authentication schemes available in the Miden protocol.
234#[derive(Clone, Debug)]
235#[non_exhaustive]
236pub enum PublicKey {
237    Falcon512Rpo(falcon512_rpo::PublicKey),
238    EcdsaK256Keccak(ecdsa_k256_keccak::PublicKey),
239}
240
241impl PublicKey {
242    /// Returns the authentication scheme of this public key.
243    pub fn auth_scheme(&self) -> AuthScheme {
244        match self {
245            PublicKey::Falcon512Rpo(_) => AuthScheme::Falcon512Rpo,
246            PublicKey::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak,
247        }
248    }
249
250    /// Returns a commitment to this public key.
251    pub fn to_commitment(&self) -> PublicKeyCommitment {
252        match self {
253            PublicKey::Falcon512Rpo(key) => key.to_commitment().into(),
254            PublicKey::EcdsaK256Keccak(key) => key.to_commitment().into(),
255        }
256    }
257
258    /// Verifies the provided signature against the provided message and this public key.
259    pub fn verify(&self, message: Word, signature: Signature) -> bool {
260        match (self, signature) {
261            (PublicKey::Falcon512Rpo(key), Signature::Falcon512Rpo(sig)) => {
262                key.verify(message, &sig)
263            },
264            (PublicKey::EcdsaK256Keccak(key), Signature::EcdsaK256Keccak(sig)) => {
265                key.verify(message, &sig)
266            },
267            _ => false,
268        }
269    }
270}
271
272impl Serializable for PublicKey {
273    fn write_into<W: ByteWriter>(&self, target: &mut W) {
274        self.auth_scheme().write_into(target);
275        match self {
276            PublicKey::Falcon512Rpo(pub_key) => pub_key.write_into(target),
277            PublicKey::EcdsaK256Keccak(pub_key) => pub_key.write_into(target),
278        }
279    }
280}
281
282impl Deserializable for PublicKey {
283    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
284        match source.read::<AuthScheme>()? {
285            AuthScheme::Falcon512Rpo => {
286                let pub_key = falcon512_rpo::PublicKey::read_from(source)?;
287                Ok(PublicKey::Falcon512Rpo(pub_key))
288            },
289            AuthScheme::EcdsaK256Keccak => {
290                let pub_key = ecdsa_k256_keccak::PublicKey::read_from(source)?;
291                Ok(PublicKey::EcdsaK256Keccak(pub_key))
292            },
293        }
294    }
295}
296
297// SIGNATURE
298// ================================================================================================
299
300/// Represents a signature object ready for native verification.
301///
302/// In order to use this signature within the Miden VM, a preparation step may be necessary to
303/// convert the native signature into a vector of field elements that can be loaded into the advice
304/// provider. To prepare the signature, use the provided `to_prepared_signature` method:
305/// ```rust,no_run
306/// use miden_protocol::account::auth::Signature;
307/// use miden_protocol::crypto::dsa::falcon512_rpo::SecretKey;
308/// use miden_protocol::{Felt, Word};
309///
310/// let secret_key = SecretKey::new();
311/// let message = Word::default();
312/// let signature: Signature = secret_key.sign(message).into();
313/// let prepared_signature: Vec<Felt> = signature.to_prepared_signature(message);
314/// ```
315#[derive(Clone, Debug)]
316#[repr(u8)]
317pub enum Signature {
318    Falcon512Rpo(falcon512_rpo::Signature) = FALCON_512_RPO,
319    EcdsaK256Keccak(ecdsa_k256_keccak::Signature) = ECDSA_K256_KECCAK,
320}
321
322impl Signature {
323    /// Returns the authentication scheme of this signature.
324    pub fn auth_scheme(&self) -> AuthScheme {
325        match self {
326            Signature::Falcon512Rpo(_) => AuthScheme::Falcon512Rpo,
327            Signature::EcdsaK256Keccak(_) => AuthScheme::EcdsaK256Keccak,
328        }
329    }
330
331    /// Converts this signature to a sequence of field elements in the format expected by the
332    /// native verification procedure in the VM.
333    ///
334    /// The order of elements in the returned vector is reversed because it is expected that the
335    /// data will be pushed into the advice stack
336    pub fn to_prepared_signature(&self, msg: Word) -> Vec<Felt> {
337        // TODO: the `expect()` should be changed to an error; but that will be a part of a bigger
338        // refactoring
339        let mut result = match self {
340            Signature::Falcon512Rpo(sig) => prepare_falcon512_rpo_signature(sig),
341            Signature::EcdsaK256Keccak(sig) => {
342                let pk = ecdsa_k256_keccak::PublicKey::recover_from(msg, sig)
343                    .expect("inferring public key from signature and message should succeed");
344                miden_core_lib::dsa::ecdsa_k256_keccak::encode_signature(&pk, sig)
345            },
346        };
347
348        // reverse the signature data so that when it is pushed onto the advice stack, the first
349        // element of the vector is at the top of the stack
350        result.reverse();
351        result
352    }
353}
354
355impl From<falcon512_rpo::Signature> for Signature {
356    fn from(signature: falcon512_rpo::Signature) -> Self {
357        Signature::Falcon512Rpo(signature)
358    }
359}
360
361impl Serializable for Signature {
362    fn write_into<W: ByteWriter>(&self, target: &mut W) {
363        self.auth_scheme().write_into(target);
364        match self {
365            Signature::Falcon512Rpo(signature) => signature.write_into(target),
366            Signature::EcdsaK256Keccak(signature) => signature.write_into(target),
367        }
368    }
369}
370
371impl Deserializable for Signature {
372    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
373        match source.read::<AuthScheme>()? {
374            AuthScheme::Falcon512Rpo => {
375                let signature = falcon512_rpo::Signature::read_from(source)?;
376                Ok(Signature::Falcon512Rpo(signature))
377            },
378            AuthScheme::EcdsaK256Keccak => {
379                let signature = ecdsa_k256_keccak::Signature::read_from(source)?;
380                Ok(Signature::EcdsaK256Keccak(signature))
381            },
382        }
383    }
384}
385
386// SIGNATURE PREPARATION
387// ================================================================================================
388
389/// Converts a Falcon [falcon512_rpo::Signature] to a vector of values to be pushed onto the
390/// advice stack. The values are the ones required for a Falcon signature verification inside the VM
391/// and they are:
392///
393/// 1. The challenge point at which we evaluate the polynomials in the subsequent three bullet
394///    points, i.e. `h`, `s2` and `pi`, to check the product relationship.
395/// 2. The expanded public key represented as the coefficients of a polynomial `h` of degree < 512.
396/// 3. The signature represented as the coefficients of a polynomial `s2` of degree < 512.
397/// 4. The product of the above two polynomials `pi` in the ring of polynomials with coefficients in
398///    the Miden field.
399/// 5. The nonce represented as 8 field elements.
400fn prepare_falcon512_rpo_signature(sig: &falcon512_rpo::Signature) -> Vec<Felt> {
401    use falcon512_rpo::Polynomial;
402
403    // The signature is composed of a nonce and a polynomial s2
404    // The nonce is represented as 8 field elements.
405    let nonce = sig.nonce();
406    // We convert the signature to a polynomial
407    let s2 = sig.sig_poly();
408    // We also need in the VM the expanded key corresponding to the public key that was provided
409    // via the operand stack
410    let h = sig.public_key();
411    // Lastly, for the probabilistic product routine that is part of the verification procedure,
412    // we need to compute the product of the expanded key and the signature polynomial in
413    // the ring of polynomials with coefficients in the Miden field.
414    let pi = Polynomial::mul_modulo_p(h, s2);
415
416    // We now push the expanded key, the signature polynomial, and the product of the
417    // expanded key and the signature polynomial to the advice stack. We also push
418    // the challenge point at which the previous polynomials will be evaluated.
419    // Finally, we push the nonce needed for the hash-to-point algorithm.
420
421    let mut polynomials: Vec<Felt> =
422        h.coefficients.iter().map(|a| Felt::from(a.value() as u32)).collect();
423    polynomials.extend(s2.coefficients.iter().map(|a| Felt::from(a.value() as u32)));
424    polynomials.extend(pi.iter().map(|a| Felt::new(*a)));
425
426    let digest_polynomials = Hasher::hash_elements(&polynomials);
427    let challenge = (digest_polynomials[0], digest_polynomials[1]);
428
429    let mut result: Vec<Felt> = vec![challenge.0, challenge.1];
430    result.extend_from_slice(&polynomials);
431    result.extend_from_slice(&nonce.to_elements());
432
433    result
434}