miden_crypto/lib.rs
1#![no_std]
2
3#[macro_use]
4extern crate alloc;
5#[cfg(feature = "std")]
6extern crate std;
7
8pub mod aead;
9pub mod dsa;
10pub mod ecdh;
11pub mod hash;
12pub mod ies;
13pub mod merkle;
14pub mod rand;
15pub mod utils;
16
17// RE-EXPORTS
18// ================================================================================================
19pub use miden_field::{Felt, LexicographicWord, Word, WordError, word};
20
21pub mod field {
22 //! Traits and utilities for working with the Goldilocks finite field (i.e.,
23 //! [Felt](super::Felt)).
24
25 pub use miden_field::{
26 Algebra, BasedVectorSpace, BinomialExtensionField, BinomiallyExtendable,
27 BinomiallyExtendableAlgebra, BoundedPowers, ExtensionField, Field,
28 HasTwoAdicBinomialExtension, InjectiveMonomial, Packable, PermutationMonomial, Powers,
29 PrimeCharacteristicRing, PrimeField, PrimeField64, QuotientMap, RawDataSerializable,
30 TwoAdicField, batch_multiplicative_inverse,
31 };
32
33 pub use super::batch_inversion::batch_inversion_allow_zeros;
34}
35
36pub mod parallel {
37 //! Conditional parallel iteration primitives.
38 //!
39 //! When the `concurrent` feature is enabled, this module re-exports parallel iterator
40 //! traits from `p3-maybe-rayon` backed by rayon. Without `concurrent`, these traits
41 //! fall back to sequential iteration.
42 pub use p3_maybe_rayon::prelude::*;
43}
44
45pub mod stark {
46 //! Lifted STARK proving system based on Plonky3.
47 //!
48 //! Sub-modules from `p3-miden-lifted-stark`:
49 //! - [`proof`] — [`proof::StarkProof`], [`proof::StarkDigest`], [`proof::StarkOutput`],
50 //! [`proof::StarkTranscript`]
51 //! - [`air`] — AIR traits, builders, symbolic types (includes all of `p3-air`)
52 //! - [`fri`] — PCS parameters, DEEP + FRI types
53 //! - [`lmcs`] — Lifted Merkle commitment scheme
54 //! - [`transcript`] — Fiat-Shamir channels and transcript data
55 //! - [`hasher`] — Stateful hasher primitives
56 //! - [`prover`] — `prove_single` / `prove_multi`
57 //! - [`verifier`] — `verify_single` / `verify_multi`
58 //! - [`debug`] — Debug constraint checker for lifted AIRs
59 //!
60 //! Sub-modules from upstream Plonky3:
61 //! - [`challenger`] — Challenge generation (Fiat-Shamir)
62 //! - [`dft`] — DFT implementations
63 //! - [`matrix`] — Dense matrix types
64 //! - [`symmetric`] — Symmetric cryptographic primitives
65
66 // Top-level types from lifted-stark
67 pub use p3_miden_lifted_stark::{GenericStarkConfig, StarkConfig};
68 // Lifted-stark sub-modules (re-exported as-is)
69 pub use p3_miden_lifted_stark::{
70 air, debug, fri, hasher, lmcs, proof, prover, transcript, verifier,
71 };
72
73 // Upstream Plonky3: challenger
74 pub mod challenger {
75 pub use p3_challenger::{
76 CanFinalizeDigest, CanObserve, DuplexChallenger, FieldChallenger, GrindingChallenger,
77 HashChallenger, SerializingChallenger64,
78 };
79 }
80
81 // Upstream Plonky3: dft
82 pub mod dft {
83 pub use p3_dft::{NaiveDft, Radix2DitParallel, TwoAdicSubgroupDft};
84 }
85
86 // Upstream Plonky3: matrix
87 pub mod matrix {
88 pub use p3_matrix::{Matrix, dense::RowMajorMatrix};
89 }
90
91 // Upstream Plonky3: symmetric
92 pub mod symmetric {
93 pub use p3_symmetric::{
94 CompressionFunctionFromHasher, CryptographicPermutation, PaddingFreeSponge,
95 Permutation, SerializingHasher, TruncatedPermutation,
96 };
97 }
98}
99
100// TYPE ALIASES
101// ================================================================================================
102
103/// An alias for a key-value map.
104///
105/// When the `std` feature is enabled, this is an alias for [`std::collections::HashMap`].
106/// Otherwise, this is an alias for [`alloc::collections::BTreeMap`].
107#[cfg(feature = "std")]
108pub type Map<K, V> = std::collections::HashMap<K, V>;
109
110#[cfg(feature = "std")]
111pub use std::collections::hash_map::Entry as MapEntry;
112#[cfg(feature = "std")]
113pub use std::collections::hash_map::IntoIter as MapIntoIter;
114
115/// An alias for a key-value map.
116///
117/// When the `std` feature is enabled, this is an alias for [`std::collections::HashMap`].
118/// Otherwise, this is an alias for [`alloc::collections::BTreeMap`].
119#[cfg(not(feature = "std"))]
120pub type Map<K, V> = alloc::collections::BTreeMap<K, V>;
121
122#[cfg(not(feature = "std"))]
123pub use alloc::collections::btree_map::Entry as MapEntry;
124#[cfg(not(feature = "std"))]
125pub use alloc::collections::btree_map::IntoIter as MapIntoIter;
126
127/// An alias for a simple set.
128///
129/// When the `std` feature is enabled, this is an alias for [`std::collections::HashSet`].
130/// Otherwise, this is an alias for [`alloc::collections::BTreeSet`].
131#[cfg(feature = "std")]
132pub type Set<V> = std::collections::HashSet<V>;
133
134/// An alias for a simple set.
135///
136/// When the `std` feature is enabled, this is an alias for [`std::collections::HashSet`].
137/// Otherwise, this is an alias for [`alloc::collections::BTreeSet`].
138#[cfg(not(feature = "std"))]
139pub type Set<V> = alloc::collections::BTreeSet<V>;
140
141// CONSTANTS
142// ================================================================================================
143
144/// Number of field elements in a word.
145pub const WORD_SIZE: usize = word::WORD_SIZE_FELTS;
146
147/// Field element representing ZERO in the Miden base filed.
148pub const ZERO: Felt = Felt::ZERO;
149
150/// Field element representing ONE in the Miden base filed.
151pub const ONE: Felt = Felt::ONE;
152
153/// Array of field elements representing word of ZEROs in the Miden base field.
154pub const EMPTY_WORD: Word = Word::new([ZERO; WORD_SIZE]);
155
156// TRAITS
157// ================================================================================================
158
159/// Defines how to compute a commitment to an object represented as a sequence of field elements.
160pub trait SequentialCommit {
161 /// A type of the commitment which must be derivable from [Word].
162 type Commitment: From<Word>;
163
164 /// Computes the commitment to the object.
165 ///
166 /// The default implementation of this function uses Poseidon2 hash function to hash the
167 /// sequence of elements returned from [Self::to_elements()].
168 fn to_commitment(&self) -> Self::Commitment {
169 hash::poseidon2::Poseidon2::hash_elements(&self.to_elements()).into()
170 }
171
172 /// Returns a representation of the object as a sequence of fields elements.
173 fn to_elements(&self) -> alloc::vec::Vec<Felt>;
174}
175
176// BATCH INVERSION
177// ================================================================================================
178
179mod batch_inversion {
180 use alloc::vec::Vec;
181
182 use p3_maybe_rayon::prelude::*;
183
184 use super::{Felt, ONE, ZERO, field::Field};
185
186 /// Parallel batch inversion using Montgomery's trick, with zeros left unchanged.
187 ///
188 /// Processes chunks in parallel using rayon, each chunk using Montgomery's trick.
189 pub fn batch_inversion_allow_zeros(values: &mut [Felt]) {
190 const CHUNK_SIZE: usize = 1024;
191
192 // We need to work with a copy since we're modifying in place
193 let input: Vec<Felt> = values.to_vec();
194
195 input.par_chunks(CHUNK_SIZE).zip(values.par_chunks_mut(CHUNK_SIZE)).for_each(
196 |(input_chunk, output_chunk)| {
197 batch_inversion_helper(input_chunk, output_chunk);
198 },
199 );
200 }
201
202 /// Montgomery's trick for batch inversion, handling zeros.
203 fn batch_inversion_helper(values: &[Felt], result: &mut [Felt]) {
204 debug_assert_eq!(values.len(), result.len());
205
206 if values.is_empty() {
207 return;
208 }
209
210 // Forward pass: compute cumulative products, skipping zeros
211 let mut last = ONE;
212 for (result, &value) in result.iter_mut().zip(values.iter()) {
213 *result = last;
214 if value != ZERO {
215 last *= value;
216 }
217 }
218
219 // Invert the final cumulative product
220 last = last.inverse();
221
222 // Backward pass: compute individual inverses
223 for i in (0..values.len()).rev() {
224 if values[i] == ZERO {
225 result[i] = ZERO;
226 } else {
227 result[i] *= last;
228 last *= values[i];
229 }
230 }
231 }
232
233 #[cfg(test)]
234 mod tests {
235 use super::*;
236
237 #[test]
238 fn test_batch_inversion_allow_zeros() {
239 let mut column = Vec::from([Felt::new(2), ZERO, Felt::new(4), Felt::new(5)]);
240 batch_inversion_allow_zeros(&mut column);
241
242 assert_eq!(column[0], Felt::new(2).inverse());
243 assert_eq!(column[1], ZERO);
244 assert_eq!(column[2], Felt::new(4).inverse());
245 assert_eq!(column[3], Felt::new(5).inverse());
246 }
247 }
248}
249
250// TESTS
251// ================================================================================================
252
253#[cfg(test)]
254mod tests {
255
256 #[test]
257 #[should_panic]
258 fn debug_assert_is_checked() {
259 // enforce the release checks to always have `RUSTFLAGS="-C debug-assertions"`.
260 //
261 // some upstream tests are performed with `debug_assert`, and we want to assert its
262 // correctness downstream.
263 //
264 // for reference, check
265 // https://github.com/0xMiden/miden-vm/issues/433
266 debug_assert!(false);
267 }
268
269 #[test]
270 #[should_panic]
271 #[allow(arithmetic_overflow)]
272 fn overflow_panics_for_test() {
273 // overflows might be disabled if tests are performed in release mode. these are critical,
274 // mandatory checks as overflows might be attack vectors.
275 //
276 // to enable overflow checks in release mode, ensure `RUSTFLAGS="-C overflow-checks"`
277 let a = 1_u64;
278 let b = 64;
279 assert_ne!(a << b, 0);
280 }
281}