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(message);
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    ///
307    /// The order of elements in the returned vector is reversed because it is expected that the
308    /// data will be pushed into the advice stack
309    pub fn to_prepared_signature(&self, msg: Word) -> Vec<Felt> {
310        // TODO: the `expect()` should be changed to an error; but that will be a part of a bigger
311        // refactoring
312        let mut result = match self {
313            Signature::RpoFalcon512(sig) => prepare_rpo_falcon512_signature(sig),
314            Signature::EcdsaK256Keccak(sig) => miden_stdlib::prepare_ecdsa_signature(msg, sig)
315                .expect("inferring public key from signature and message should succeed"),
316        };
317
318        // reverse the signature data so that when it is pushed onto the advice stack, the first
319        // element of the vector is at the top of the stack
320        result.reverse();
321        result
322    }
323}
324
325impl From<rpo_falcon512::Signature> for Signature {
326    fn from(signature: rpo_falcon512::Signature) -> Self {
327        Signature::RpoFalcon512(signature)
328    }
329}
330
331impl Serializable for Signature {
332    fn write_into<W: ByteWriter>(&self, target: &mut W) {
333        self.auth_scheme().write_into(target);
334        match self {
335            Signature::RpoFalcon512(signature) => signature.write_into(target),
336            Signature::EcdsaK256Keccak(signature) => signature.write_into(target),
337        }
338    }
339}
340
341impl Deserializable for Signature {
342    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
343        match source.read::<AuthScheme>()? {
344            AuthScheme::RpoFalcon512 => {
345                let signature = rpo_falcon512::Signature::read_from(source)?;
346                Ok(Signature::RpoFalcon512(signature))
347            },
348            AuthScheme::EcdsaK256Keccak => {
349                let signature = ecdsa_k256_keccak::Signature::read_from(source)?;
350                Ok(Signature::EcdsaK256Keccak(signature))
351            },
352        }
353    }
354}
355
356// SIGNATURE PREPARATION
357// ================================================================================================
358
359/// Converts a Falcon [rpo_falcon512::Signature] to a vector of values to be pushed onto the
360/// advice stack. The values are the ones required for a Falcon signature verification inside the VM
361/// and they are:
362///
363/// 1. The challenge point at which we evaluate the polynomials in the subsequent three bullet
364///    points, i.e. `h`, `s2` and `pi`, to check the product relationship.
365/// 2. The expanded public key represented as the coefficients of a polynomial `h` of degree < 512.
366/// 3. The signature represented as the coefficients of a polynomial `s2` of degree < 512.
367/// 4. The product of the above two polynomials `pi` in the ring of polynomials with coefficients in
368///    the Miden field.
369/// 5. The nonce represented as 8 field elements.
370fn prepare_rpo_falcon512_signature(sig: &rpo_falcon512::Signature) -> Vec<Felt> {
371    use rpo_falcon512::Polynomial;
372
373    // The signature is composed of a nonce and a polynomial s2
374    // The nonce is represented as 8 field elements.
375    let nonce = sig.nonce();
376    // We convert the signature to a polynomial
377    let s2 = sig.sig_poly();
378    // We also need in the VM the expanded key corresponding to the public key that was provided
379    // via the operand stack
380    let h = sig.public_key();
381    // Lastly, for the probabilistic product routine that is part of the verification procedure,
382    // we need to compute the product of the expanded key and the signature polynomial in
383    // the ring of polynomials with coefficients in the Miden field.
384    let pi = Polynomial::mul_modulo_p(h, s2);
385
386    // We now push the expanded key, the signature polynomial, and the product of the
387    // expanded key and the signature polynomial to the advice stack. We also push
388    // the challenge point at which the previous polynomials will be evaluated.
389    // Finally, we push the nonce needed for the hash-to-point algorithm.
390
391    let mut polynomials: Vec<Felt> =
392        h.coefficients.iter().map(|a| Felt::from(a.value() as u32)).collect();
393    polynomials.extend(s2.coefficients.iter().map(|a| Felt::from(a.value() as u32)));
394    polynomials.extend(pi.iter().map(|a| Felt::new(*a)));
395
396    let digest_polynomials = Hasher::hash_elements(&polynomials);
397    let challenge = (digest_polynomials[0], digest_polynomials[1]);
398
399    let mut result: Vec<Felt> = vec![challenge.0, challenge.1];
400    result.extend_from_slice(&polynomials);
401    result.extend_from_slice(&nonce.to_elements());
402
403    result
404}