Skip to main content

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, 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, Binomial, BinomialExtensionField, BinomiallyExtendable,
27        BoundedPowers, ExtensionAlgebra, ExtensionField, Field, HasTwoAdicBinomialExtension,
28        InjectiveMonomial, Packable, PermutationMonomial, Powers, PrimeCharacteristicRing,
29        PrimeField, PrimeField64, QuotientMap, RawDataSerializable, TwoAdicField,
30        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 `miden-lifted-stark`:
49    //! - [`proof`] — [`proof::StarkProofData`] (wire artifact), [`proof::StarkProof`] (structured
50    //!   view), [`proof::StarkDigest`], [`proof::StarkOutput`], [`proof::TranscriptChallenger`],
51    //!   [`proof::TranscriptData`]
52    //! - [`air`] — AIR traits, builders, symbolic types (includes all of `p3-air`)
53    //! - [`pcs`] — PCS parameters, DEEP + FRI sub-proofs
54    //! - [`lmcs`] — Lifted Merkle commitment scheme
55    //! - [`hasher`] — Stateful hasher primitives
56    //! - [`prover`] — [`ProverInstance::prove`]
57    //! - [`verifier`] — [`VerifierInstance::verify`]
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 miden_lifted_stark::{
68        GenericStarkConfig, Preprocessed, PreprocessedValidationError, ProverInstance, StarkConfig,
69        VerifierInstance,
70    };
71    // Lifted-stark sub-modules (re-exported as-is)
72    pub use miden_lifted_stark::{air, debug, hasher, lmcs, pcs, proof, prover, verifier};
73
74    // Upstream Plonky3: challenger
75    pub mod challenger {
76        pub use p3_challenger::{
77            CanFinalizeDigest, CanObserve, DuplexChallenger, FieldChallenger, GrindingChallenger,
78            HashChallenger, SerializingChallenger64,
79        };
80    }
81
82    // Upstream Plonky3: dft
83    pub mod dft {
84        pub use p3_dft::{NaiveDft, Radix2DitParallel, TwoAdicSubgroupDft};
85    }
86
87    // Upstream Plonky3: matrix
88    pub mod matrix {
89        pub use p3_matrix::{Matrix, dense::RowMajorMatrix};
90    }
91
92    // Upstream Plonky3: symmetric
93    pub mod symmetric {
94        pub use p3_symmetric::{
95            CompressionFunctionFromHasher, CryptographicPermutation, PaddingFreeSponge,
96            Permutation, SerializingHasher, TruncatedPermutation,
97        };
98    }
99}
100
101// TYPE ALIASES
102// ================================================================================================
103
104/// An alias for a key-value map.
105///
106/// When the `std` feature is enabled, this is an alias for [`std::collections::HashMap`].
107/// Otherwise, this is an alias for [`alloc::collections::BTreeMap`].
108#[cfg(feature = "std")]
109pub type Map<K, V> = std::collections::HashMap<K, V>;
110
111/// An alias for a key-value map.
112///
113/// When the `std` feature is enabled, this is an alias for [`std::collections::HashMap`].
114/// Otherwise, this is an alias for [`alloc::collections::BTreeMap`].
115#[cfg(not(feature = "std"))]
116pub type Map<K, V> = alloc::collections::BTreeMap<K, V>;
117
118#[cfg(not(feature = "std"))]
119pub use alloc::collections::btree_map::Entry as MapEntry;
120#[cfg(not(feature = "std"))]
121pub use alloc::collections::btree_map::IntoIter as MapIntoIter;
122#[cfg(feature = "std")]
123pub use std::collections::hash_map::Entry as MapEntry;
124#[cfg(feature = "std")]
125pub use std::collections::hash_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/// Field element representing ZERO in the Miden base field.
145pub const ZERO: Felt = Felt::ZERO;
146
147/// Field element representing ONE in the Miden base field.
148pub const ONE: Felt = Felt::ONE;
149
150/// Array of field elements representing word of ZEROs in the Miden base field.
151pub const EMPTY_WORD: Word = Word::new([ZERO; Word::NUM_ELEMENTS]);
152
153// TRAITS
154// ================================================================================================
155
156/// Defines how to compute a commitment to an object represented as a sequence of field elements.
157pub trait SequentialCommit {
158    /// A type of the commitment which must be derivable from [Word].
159    type Commitment: From<Word>;
160
161    /// Computes the commitment to the object.
162    ///
163    /// The default implementation of this function uses Poseidon2 hash function to hash the
164    /// sequence of elements returned from [Self::to_elements()].
165    fn to_commitment(&self) -> Self::Commitment {
166        hash::poseidon2::Poseidon2::hash_elements(&self.to_elements()).into()
167    }
168
169    /// Returns a representation of the object as a sequence of fields elements.
170    fn to_elements(&self) -> alloc::vec::Vec<Felt>;
171}
172
173// BATCH INVERSION
174// ================================================================================================
175
176mod batch_inversion {
177    use p3_maybe_rayon::prelude::*;
178
179    use super::{Felt, ONE, ZERO, field::Field};
180
181    /// Parallel batch inversion using Montgomery's trick, with zeros left unchanged.
182    ///
183    /// Processes chunks in parallel using rayon, each chunk using Montgomery's trick.
184    pub fn batch_inversion_allow_zeros(values: &mut [Felt]) {
185        const CHUNK_SIZE: usize = 1024;
186
187        values.par_chunks_mut(CHUNK_SIZE).for_each(|output_chunk| {
188            let len = output_chunk.len();
189            let mut scratch = [ZERO; CHUNK_SIZE];
190            scratch[..len].copy_from_slice(output_chunk);
191            batch_inversion_helper(&scratch[..len], output_chunk);
192        });
193    }
194
195    /// Montgomery's trick for batch inversion, handling zeros.
196    fn batch_inversion_helper(values: &[Felt], result: &mut [Felt]) {
197        debug_assert_eq!(values.len(), result.len());
198
199        if values.is_empty() {
200            return;
201        }
202
203        // Forward pass: compute cumulative products, skipping zeros
204        let mut last = ONE;
205        for (result, &value) in result.iter_mut().zip(values.iter()) {
206            *result = last;
207            if value != ZERO {
208                last *= value;
209            }
210        }
211
212        // Invert the final cumulative product
213        last = last.inverse();
214
215        // Backward pass: compute individual inverses
216        for i in (0..values.len()).rev() {
217            if values[i] == ZERO {
218                result[i] = ZERO;
219            } else {
220                result[i] *= last;
221                last *= values[i];
222            }
223        }
224    }
225
226    #[cfg(test)]
227    mod tests {
228        use alloc::vec::Vec;
229
230        use super::*;
231
232        #[test]
233        fn test_batch_inversion_allow_zeros() {
234            let mut column = Vec::from([
235                Felt::new_unchecked(2),
236                ZERO,
237                Felt::new_unchecked(4),
238                Felt::new_unchecked(5),
239            ]);
240            batch_inversion_allow_zeros(&mut column);
241
242            assert_eq!(column[0], Felt::new_unchecked(2).inverse());
243            assert_eq!(column[1], ZERO);
244            assert_eq!(column[2], Felt::new_unchecked(4).inverse());
245            assert_eq!(column[3], Felt::new_unchecked(5).inverse());
246        }
247
248        #[test]
249        fn test_batch_inversion_allow_zeros_spans_fixed_chunks() {
250            let mut v: Vec<Felt> = (1_u64..=2050).map(Felt::new_unchecked).collect();
251            let expected: Vec<Felt> = v.iter().copied().map(|x| x.inverse()).collect();
252            batch_inversion_allow_zeros(&mut v);
253            assert_eq!(v, expected);
254        }
255
256        #[test]
257        fn test_batch_inversion_allow_zeros_zero_on_chunk_boundary() {
258            let mut v = vec![Felt::new_unchecked(7); 1025];
259            v[1023] = ZERO;
260            batch_inversion_allow_zeros(&mut v);
261            assert_eq!(v[1023], ZERO);
262            for i in (0..1023).chain(1024..1025) {
263                assert_eq!(v[i], Felt::new_unchecked(7).inverse());
264            }
265        }
266    }
267}
268
269// TESTS
270// ================================================================================================
271
272#[cfg(test)]
273mod tests {
274
275    #[test]
276    #[should_panic]
277    fn debug_assert_is_checked() {
278        // enforce the release checks to always have `RUSTFLAGS="-C debug-assertions"`.
279        //
280        // some upstream tests are performed with `debug_assert`, and we want to assert its
281        // correctness downstream.
282        //
283        // for reference, check
284        // https://github.com/0xMiden/miden-vm/issues/433
285        debug_assert!(false);
286    }
287
288    #[test]
289    #[should_panic]
290    #[allow(arithmetic_overflow)]
291    fn overflow_panics_for_test() {
292        // overflows might be disabled if tests are performed in release mode. these are critical,
293        // mandatory checks as overflows might be attack vectors.
294        //
295        // to enable overflow checks in release mode, ensure `RUSTFLAGS="-C overflow-checks"`
296        let a = 1_u64;
297        let b = 64;
298        assert_ne!(a << b, 0);
299    }
300}