Skip to main content

miden_core_lib/
dsa.rs

1//! Digital Signature Algorithm (DSA) helper functions.
2//!
3//! This module provides functions for signing messages and encoding signatures in the format
4//! expected by the corresponding MASM verification procedures.
5//!
6//! Each submodule corresponds to a specific signature scheme:
7//! - [`ecdsa_k256_keccak`]: ECDSA over secp256k1 with Keccak256 hashing
8//! - [`eddsa_ed25519`]: EdDSA over Ed25519 with SHA-512 hashing
9//! - [`falcon512_poseidon2`]: Falcon-512 with Poseidon2 hashing
10
11// ECDSA K256 KECCAK
12// ================================================================================================
13
14/// ECDSA secp256k1 with Keccak256 signature helpers.
15///
16/// Functions in this module generate data for the
17/// `miden::core::crypto::dsa::ecdsa_k256_keccak::verify` MASM procedure.
18pub mod ecdsa_k256_keccak {
19    extern crate alloc;
20
21    use alloc::vec::Vec;
22
23    use miden_core::{Felt, Word, serde::Serializable, utils::bytes_to_packed_u32_elements};
24    use miden_crypto::dsa::ecdsa_k256_keccak::{PublicKey, SecretKey, Signature};
25
26    /// Signs the provided message with the supplied secret key and encodes this signature and the
27    /// associated public key into a vector of field elements in the format expected by
28    /// `miden::core::crypto::dsa::ecdsa_k256_keccak::verify` procedure.
29    ///
30    /// See [`encode_signature()`] for more info.
31    pub fn sign(sk: &SecretKey, msg: Word) -> Vec<Felt> {
32        let pk = sk.public_key();
33        let sig = sk.sign(msg);
34        encode_signature(&pk, &sig)
35    }
36
37    /// Encodes the provided public key and signature into a vector of field elements in the format
38    /// expected by `miden::core::crypto::dsa::ecdsa_k256_keccak::verify` procedure.
39    ///
40    /// 1. The compressed secp256k1 public key encoded as 9 packed-u32 felts (33 bytes total).
41    /// 2. The ECDSA signature encoded as 17 packed-u32 felts (66 bytes total).
42    ///
43    /// The two chunks are concatenated as `[PK[9] || SIG[17]]` so they can be streamed straight to
44    /// the advice provider before invoking `ecdsa_k256_keccak::verify`.
45    pub fn encode_signature(pk: &PublicKey, sig: &Signature) -> Vec<Felt> {
46        let mut out = Vec::new();
47        let pk_bytes = pk.to_bytes();
48        out.extend(bytes_to_packed_u32_elements(&pk_bytes));
49        let sig_bytes = sig.to_bytes();
50        out.extend(bytes_to_packed_u32_elements(&sig_bytes));
51        out
52    }
53}
54
55// EDDSA ED25519
56// ================================================================================================
57
58/// EdDSA Ed25519 with SHA-512 signature helpers.
59///
60/// Functions in this module generate data for the
61/// `miden::core::crypto::dsa::eddsa_ed25519::verify` MASM procedure.
62pub mod eddsa_ed25519 {
63    extern crate alloc;
64
65    use alloc::vec::Vec;
66
67    use miden_core::{Felt, Word, serde::Serializable, utils::bytes_to_packed_u32_elements};
68    use miden_crypto::dsa::eddsa_25519_sha512::{PublicKey, SecretKey, Signature};
69
70    /// Signs the provided message with the supplied secret key and encodes this signature and the
71    /// associated public key into a vector of field elements in the format expected by
72    /// `miden::core::crypto::dsa::eddsa_ed25519::verify` procedure.
73    ///
74    /// See [`encode_signature()`] for more info.
75    pub fn sign(sk: &SecretKey, msg: Word) -> Vec<Felt> {
76        let pk = sk.public_key();
77        let sig = sk.sign(msg);
78        encode_signature(&pk, &sig)
79    }
80
81    /// Encodes the provided public key and signature into a vector of field elements in the format
82    /// expected by `miden::core::crypto::dsa::eddsa_ed25519::verify` procedure.
83    ///
84    /// The encoding format is:
85    /// 1. The Ed25519 public key encoded as 8 packed-u32 felts (32 bytes total).
86    /// 2. The EdDSA signature encoded as 16 packed-u32 felts (64 bytes total).
87    ///
88    /// The two chunks are concatenated as `[PK[8] || SIG[16]]` so they can be streamed straight to
89    /// the advice provider before invoking `eddsa_ed25519::verify`.
90    pub fn encode_signature(pk: &PublicKey, sig: &Signature) -> Vec<Felt> {
91        let mut out = Vec::new();
92        let pk_bytes = pk.to_bytes();
93        out.extend(bytes_to_packed_u32_elements(&pk_bytes));
94        let sig_bytes = sig.to_bytes();
95        out.extend(bytes_to_packed_u32_elements(&sig_bytes));
96        out
97    }
98}
99
100// FALCON 512 POSEIDON2
101// ================================================================================================
102
103/// Falcon-512 with Poseidon2 hashing signature helpers.
104///
105/// Functions in this module generate data for the
106/// `miden::core::crypto::dsa::falcon512_poseidon2::verify` MASM procedure.
107pub mod falcon512_poseidon2 {
108    extern crate alloc;
109
110    use alloc::vec::Vec;
111
112    // Re-export signature type for users
113    pub use miden_core::crypto::dsa::falcon512_poseidon2::{PublicKey, SecretKey, Signature};
114    use miden_core::{
115        Felt, Word,
116        crypto::{dsa::falcon512_poseidon2::Polynomial, hash::Poseidon2},
117    };
118
119    /// Signs the provided message with the provided secret key and returns the resulting signature
120    /// encoded in the format required by the `falcon512_poseidon2::verify` procedure, or `None` if
121    /// the secret key is malformed due to either incorrect length or failed decoding.
122    ///
123    /// This is equivalent to calling [`encode_signature`] on the result of signing the message.
124    ///
125    /// See [`encode_signature`] for the encoding format.
126    pub fn sign(sk: &SecretKey, msg: Word) -> Option<Vec<Felt>> {
127        let sig = sk.sign(msg);
128        Some(encode_signature(sig.public_key(), &sig))
129    }
130
131    /// Encodes the provided Falcon public key and signature into a vector of field elements in the
132    /// format expected by `miden::core::crypto::dsa::falcon512_poseidon2::verify` procedure.
133    ///
134    /// The encoding format is (in reverse order on the advice stack):
135    ///
136    /// 1. The challenge point, a tuple of elements representing an element in the quadratic
137    ///    extension field, at which we evaluate the polynomials in the subsequent three points to
138    ///    check the product relationship.
139    /// 2. The expanded public key represented as the coefficients of a polynomial of degree < 512.
140    /// 3. The signature represented as the coefficients of a polynomial of degree < 512.
141    /// 4. The product of the above two polynomials in the ring of polynomials with coefficients in
142    ///    the Miden field.
143    /// 5. The nonce represented as 8 field elements.
144    ///
145    /// The result can be streamed straight to the advice provider before invoking
146    /// `falcon512_poseidon2::verify`.
147    pub fn encode_signature(pk: &PublicKey, sig: &Signature) -> Vec<Felt> {
148        use alloc::vec;
149
150        // The signature is composed of a nonce and a polynomial s2
151
152        // The nonce is represented as 8 field elements.
153        let nonce = sig.nonce();
154
155        // We convert the signature to a polynomial
156        let s2 = sig.sig_poly();
157
158        // Lastly, for the probabilistic product routine that is part of the verification
159        // procedure, we need to compute the product of the expanded key and the signature
160        // polynomial in the ring of polynomials with coefficients in the Miden field.
161        let pi = Polynomial::mul_modulo_p(pk, s2);
162
163        // We now push the expanded key, the signature polynomial, and the product of the
164        // expanded key and the signature polynomial to the advice stack. We also push
165        // the challenge point at which the previous polynomials will be evaluated.
166        // Finally, we push the nonce needed for the hash-to-point algorithm.
167
168        let mut polynomials = pk.to_elements();
169        polynomials.extend(s2.to_elements());
170        polynomials.extend(pi.iter().map(|a| Felt::new(*a)));
171
172        let digest_polynomials = Poseidon2::hash_elements(&polynomials);
173        let challenge = (digest_polynomials[0], digest_polynomials[1]);
174
175        // Push tau1 first, then tau0, so adv_push.2 produces _le format [tau0, tau1, ...] directly
176        let mut result: Vec<Felt> = vec![challenge.1, challenge.0];
177        result.extend_from_slice(&polynomials);
178        result.extend_from_slice(&nonce.to_elements());
179
180        result
181    }
182}