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