miden_stdlib/
lib.rs

1#![no_std]
2
3pub mod handlers;
4
5extern crate alloc;
6
7use alloc::{sync::Arc, vec, vec::Vec};
8
9use miden_assembly::{Library, mast::MastForest, utils::Deserializable};
10use miden_core::{EventId, Felt, Word};
11use miden_processor::{EventHandler, HostLibrary};
12use miden_utils_sync::LazyLock;
13
14use crate::handlers::{
15    falcon_div::{FALCON_DIV_EVENT_ID, handle_falcon_div},
16    keccak256::{KECCAK_HASH_MEMORY_EVENT_ID, handle_keccak_hash_memory},
17    smt_peek::{SMT_PEEK_EVENT_ID, handle_smt_peek},
18    sorted_array::{
19        LOWERBOUND_ARRAY_EVENT_ID, LOWERBOUND_KEY_VALUE_EVENT_ID, handle_lowerbound_array,
20        handle_lowerbound_key_value,
21    },
22    u64_div::{U64_DIV_EVENT_ID, handle_u64_div},
23};
24
25// STANDARD LIBRARY
26// ================================================================================================
27
28/// TODO: add docs
29#[derive(Clone)]
30pub struct StdLibrary(Library);
31
32impl AsRef<Library> for StdLibrary {
33    fn as_ref(&self) -> &Library {
34        &self.0
35    }
36}
37
38impl From<StdLibrary> for Library {
39    fn from(value: StdLibrary) -> Self {
40        value.0
41    }
42}
43
44impl From<&StdLibrary> for HostLibrary {
45    fn from(stdlib: &StdLibrary) -> Self {
46        Self {
47            mast_forest: stdlib.mast_forest().clone(),
48            handlers: stdlib.handlers(),
49        }
50    }
51}
52
53impl StdLibrary {
54    /// Serialized representation of the Miden standard library.
55    pub const SERIALIZED: &'static [u8] =
56        include_bytes!(concat!(env!("OUT_DIR"), "/assets/std.masl"));
57
58    /// Returns a reference to the [MastForest] underlying the Miden standard library.
59    pub fn mast_forest(&self) -> &Arc<MastForest> {
60        self.0.mast_forest()
61    }
62
63    /// Returns a reference to the underlying [`Library`].
64    pub fn library(&self) -> &Library {
65        &self.0
66    }
67
68    /// List of all `EventHandlers` required to run all of the standard library.
69    pub fn handlers(&self) -> Vec<(EventId, Arc<dyn EventHandler>)> {
70        vec![
71            (KECCAK_HASH_MEMORY_EVENT_ID, Arc::new(handle_keccak_hash_memory)),
72            (SMT_PEEK_EVENT_ID, Arc::new(handle_smt_peek)),
73            (U64_DIV_EVENT_ID, Arc::new(handle_u64_div)),
74            (FALCON_DIV_EVENT_ID, Arc::new(handle_falcon_div)),
75            (LOWERBOUND_ARRAY_EVENT_ID, Arc::new(handle_lowerbound_array)),
76            (LOWERBOUND_KEY_VALUE_EVENT_ID, Arc::new(handle_lowerbound_key_value)),
77        ]
78    }
79}
80
81impl Default for StdLibrary {
82    fn default() -> Self {
83        static STDLIB: LazyLock<StdLibrary> = LazyLock::new(|| {
84            let contents =
85                Library::read_from_bytes(StdLibrary::SERIALIZED).expect("failed to read std masl!");
86            StdLibrary(contents)
87        });
88        STDLIB.clone()
89    }
90}
91
92// FALCON SIGNATURE
93// ================================================================================================
94
95/// Signs the provided message with the provided secret key and returns the resulting signature
96/// encoded in the format required by the rpo_faclcon512::verify procedure, or `None` if the secret
97/// key is malformed due to either incorrect length or failed decoding.
98///
99/// The values are the ones required for a Falcon signature verification inside the VM and they are:
100///
101/// 1. The challenge point, a tuple of elements representing an element in the quadratic extension
102///    field, at which we evaluate the polynomials in the subsequent three points to check the
103///    product relationship.
104/// 2. The expanded public key represented as the coefficients of a polynomial of degree < 512.
105/// 3. The signature represented as the coefficients of a polynomial of degree < 512.
106/// 4. The product of the above two polynomials in the ring of polynomials with coefficients in the
107///    Miden field.
108/// 5. The nonce represented as 8 field elements.
109#[cfg(feature = "std")]
110pub fn falcon_sign(sk: &[Felt], msg: Word) -> Option<Vec<Felt>> {
111    use alloc::vec;
112
113    use miden_core::{
114        Felt,
115        crypto::{
116            dsa::rpo_falcon512::{Polynomial, SecretKey},
117            hash::Rpo256,
118        },
119        utils::Deserializable,
120    };
121
122    // Create the corresponding secret key
123    let mut sk_bytes = Vec::with_capacity(sk.len());
124    for element in sk {
125        let value = element.as_int();
126        if value > u8::MAX as u64 {
127            return None;
128        }
129        sk_bytes.push(value as u8);
130    }
131
132    let sk = SecretKey::read_from_bytes(&sk_bytes).ok()?;
133
134    // We can now generate the signature
135    let sig = sk.sign(msg);
136
137    // The signature is composed of a nonce and a polynomial s2
138
139    // The nonce is represented as 8 field elements.
140    let nonce = sig.nonce();
141
142    // We convert the signature to a polynomial
143    let s2 = sig.sig_poly();
144
145    // We also need in the VM the expanded key corresponding to the public key the was provided
146    // via the operand stack
147    let h = sk.public_key();
148
149    // Lastly, for the probabilistic product routine that is part of the verification procedure,
150    // we need to compute the product of the expanded key and the signature polynomial in
151    // the ring of polynomials with coefficients in the Miden field.
152    let pi = Polynomial::mul_modulo_p(&h, s2);
153
154    // We now push the expanded key, the signature polynomial, and the product of the
155    // expanded key and the signature polynomial to the advice stack. We also push
156    // the challenge point at which the previous polynomials will be evaluated.
157    // Finally, we push the nonce needed for the hash-to-point algorithm.
158
159    let mut polynomials: Vec<Felt> =
160        h.coefficients.iter().map(|a| Felt::from(a.value() as u32)).collect();
161    polynomials.extend(s2.coefficients.iter().map(|a| Felt::from(a.value() as u32)));
162    polynomials.extend(pi.iter().map(|a| Felt::new(*a)));
163
164    let digest_polynomials = Rpo256::hash_elements(&polynomials);
165    let challenge = (digest_polynomials[0], digest_polynomials[1]);
166
167    let mut result: Vec<Felt> = vec![challenge.0, challenge.1];
168    result.extend_from_slice(&polynomials);
169    result.extend_from_slice(&nonce.to_elements());
170
171    result.reverse();
172    Some(result)
173}
174
175#[cfg(not(feature = "std"))]
176pub fn falcon_sign(_pk_sk: &[Felt], _msg: Word) -> Option<Vec<Felt>> {
177    None
178}
179
180// TESTS
181// ================================================================================================
182
183#[cfg(test)]
184mod tests {
185    use miden_assembly::LibraryPath;
186
187    use super::*;
188
189    #[test]
190    fn test_compile() {
191        let path = "std::math::u64::overflowing_add".parse::<LibraryPath>().unwrap();
192        let stdlib = StdLibrary::default();
193        let exists = stdlib.0.module_infos().any(|module| {
194            module
195                .procedures()
196                .any(|(_, proc)| module.path().clone().append(&proc.name).unwrap() == path)
197        });
198
199        assert!(exists);
200    }
201}