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}