miden_objects/account/
auth.rs

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