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