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, 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 `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 miden_lifted_stark::{GenericStarkConfig, StarkConfig};
68    // Lifted-stark sub-modules (re-exported as-is)
69    pub use 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/// An alias for a key-value map.
111///
112/// When the `std` feature is enabled, this is an alias for [`std::collections::HashMap`].
113/// Otherwise, this is an alias for [`alloc::collections::BTreeMap`].
114#[cfg(not(feature = "std"))]
115pub type Map<K, V> = alloc::collections::BTreeMap<K, V>;
116
117#[cfg(not(feature = "std"))]
118pub use alloc::collections::btree_map::Entry as MapEntry;
119#[cfg(not(feature = "std"))]
120pub use alloc::collections::btree_map::IntoIter as MapIntoIter;
121#[cfg(feature = "std")]
122pub use std::collections::hash_map::Entry as MapEntry;
123#[cfg(feature = "std")]
124pub use std::collections::hash_map::IntoIter as MapIntoIter;
125
126/// An alias for a simple set.
127///
128/// When the `std` feature is enabled, this is an alias for [`std::collections::HashSet`].
129/// Otherwise, this is an alias for [`alloc::collections::BTreeSet`].
130#[cfg(feature = "std")]
131pub type Set<V> = std::collections::HashSet<V>;
132
133/// An alias for a simple set.
134///
135/// When the `std` feature is enabled, this is an alias for [`std::collections::HashSet`].
136/// Otherwise, this is an alias for [`alloc::collections::BTreeSet`].
137#[cfg(not(feature = "std"))]
138pub type Set<V> = alloc::collections::BTreeSet<V>;
139
140// CONSTANTS
141// ================================================================================================
142
143/// Field element representing ZERO in the Miden base field.
144pub const ZERO: Felt = Felt::ZERO;
145
146/// Field element representing ONE in the Miden base field.
147pub const ONE: Felt = Felt::ONE;
148
149/// Array of field elements representing word of ZEROs in the Miden base field.
150pub const EMPTY_WORD: Word = Word::new([ZERO; Word::NUM_ELEMENTS]);
151
152// TRAITS
153// ================================================================================================
154
155/// Defines how to compute a commitment to an object represented as a sequence of field elements.
156pub trait SequentialCommit {
157    /// A type of the commitment which must be derivable from [Word].
158    type Commitment: From<Word>;
159
160    /// Computes the commitment to the object.
161    ///
162    /// The default implementation of this function uses Poseidon2 hash function to hash the
163    /// sequence of elements returned from [Self::to_elements()].
164    fn to_commitment(&self) -> Self::Commitment {
165        hash::poseidon2::Poseidon2::hash_elements(&self.to_elements()).into()
166    }
167
168    /// Returns a representation of the object as a sequence of fields elements.
169    fn to_elements(&self) -> alloc::vec::Vec<Felt>;
170}
171
172// BATCH INVERSION
173// ================================================================================================
174
175mod batch_inversion {
176    use p3_maybe_rayon::prelude::*;
177
178    use super::{Felt, ONE, ZERO, field::Field};
179
180    /// Parallel batch inversion using Montgomery's trick, with zeros left unchanged.
181    ///
182    /// Processes chunks in parallel using rayon, each chunk using Montgomery's trick.
183    pub fn batch_inversion_allow_zeros(values: &mut [Felt]) {
184        const CHUNK_SIZE: usize = 1024;
185
186        values.par_chunks_mut(CHUNK_SIZE).for_each(|output_chunk| {
187            let len = output_chunk.len();
188            let mut scratch = [ZERO; CHUNK_SIZE];
189            scratch[..len].copy_from_slice(output_chunk);
190            batch_inversion_helper(&scratch[..len], output_chunk);
191        });
192    }
193
194    /// Montgomery's trick for batch inversion, handling zeros.
195    fn batch_inversion_helper(values: &[Felt], result: &mut [Felt]) {
196        debug_assert_eq!(values.len(), result.len());
197
198        if values.is_empty() {
199            return;
200        }
201
202        // Forward pass: compute cumulative products, skipping zeros
203        let mut last = ONE;
204        for (result, &value) in result.iter_mut().zip(values.iter()) {
205            *result = last;
206            if value != ZERO {
207                last *= value;
208            }
209        }
210
211        // Invert the final cumulative product
212        last = last.inverse();
213
214        // Backward pass: compute individual inverses
215        for i in (0..values.len()).rev() {
216            if values[i] == ZERO {
217                result[i] = ZERO;
218            } else {
219                result[i] *= last;
220                last *= values[i];
221            }
222        }
223    }
224
225    #[cfg(test)]
226    mod tests {
227        use alloc::vec::Vec;
228
229        use super::*;
230
231        #[test]
232        fn test_batch_inversion_allow_zeros() {
233            let mut column = Vec::from([
234                Felt::new_unchecked(2),
235                ZERO,
236                Felt::new_unchecked(4),
237                Felt::new_unchecked(5),
238            ]);
239            batch_inversion_allow_zeros(&mut column);
240
241            assert_eq!(column[0], Felt::new_unchecked(2).inverse());
242            assert_eq!(column[1], ZERO);
243            assert_eq!(column[2], Felt::new_unchecked(4).inverse());
244            assert_eq!(column[3], Felt::new_unchecked(5).inverse());
245        }
246
247        #[test]
248        fn test_batch_inversion_allow_zeros_spans_fixed_chunks() {
249            let mut v: Vec<Felt> = (1_u64..=2050).map(Felt::new_unchecked).collect();
250            let expected: Vec<Felt> = v.iter().copied().map(|x| x.inverse()).collect();
251            batch_inversion_allow_zeros(&mut v);
252            assert_eq!(v, expected);
253        }
254
255        #[test]
256        fn test_batch_inversion_allow_zeros_zero_on_chunk_boundary() {
257            let mut v = vec![Felt::new_unchecked(7); 1025];
258            v[1023] = ZERO;
259            batch_inversion_allow_zeros(&mut v);
260            assert_eq!(v[1023], ZERO);
261            for i in (0..1023).chain(1024..1025) {
262                assert_eq!(v[i], Felt::new_unchecked(7).inverse());
263            }
264        }
265    }
266}
267
268// TESTS
269// ================================================================================================
270
271#[cfg(test)]
272mod tests {
273
274    #[test]
275    #[should_panic]
276    fn debug_assert_is_checked() {
277        // enforce the release checks to always have `RUSTFLAGS="-C debug-assertions"`.
278        //
279        // some upstream tests are performed with `debug_assert`, and we want to assert its
280        // correctness downstream.
281        //
282        // for reference, check
283        // https://github.com/0xMiden/miden-vm/issues/433
284        debug_assert!(false);
285    }
286
287    #[test]
288    #[should_panic]
289    #[allow(arithmetic_overflow)]
290    fn overflow_panics_for_test() {
291        // overflows might be disabled if tests are performed in release mode. these are critical,
292        // mandatory checks as overflows might be attack vectors.
293        //
294        // to enable overflow checks in release mode, ensure `RUSTFLAGS="-C overflow-checks"`
295        let a = 1_u64;
296        let b = 64;
297        assert_ne!(a << b, 0);
298    }
299}