Skip to main content

risc0_bigint2/
lib.rs

1// Copyright 2025 RISC Zero, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#[cfg(test)]
16#[cfg(feature = "num-bigint-dig")]
17extern crate num_bigint_dig as num_bigint;
18
19pub mod ec;
20pub mod ffi;
21pub mod field;
22pub mod rsa;
23
24pub(crate) const WORD_SIZE: usize = 4;
25
26/// Trait for converting values to a u32 array to be used for bigint2 acceleration.
27pub trait ToBigInt2Buffer<const WIDTH: usize> {
28    /// Convert value to a u32 array to be used for bigint2 acceleration.
29    fn to_u32_array(&self) -> [u32; WIDTH];
30
31    /// Convert from array into the original type.
32    fn from_u32_array(array: [u32; WIDTH]) -> Self;
33}
34
35#[inline]
36// Checks if two u32 arrays representing big integers with little-endian digit order satisfy lhs < rhs
37fn is_less<const N: usize>(lhs: &[u32; N], rhs: &[u32; N]) -> bool {
38    for i in (0..N).rev() {
39        if lhs[i] < rhs[i] {
40            return true;
41        }
42        if lhs[i] > rhs[i] {
43            return false;
44        }
45    }
46    false
47}
48
49#[cfg(all(target_os = "zkvm", target_arch = "riscv32"))]
50const _: () = {
51    assert!(
52        core::option_env!("RISC0_FEATURE_bigint2").is_some(),
53        r#"
54RISC Zero zkVM feature bigint2 is not available, and is required by this crate.
55
56If you'd like to use bigint2, please upgrade to risc0-zkvm and risc0-build and ensure the required
57feature flags are enabled. See the RISC Zero dev docs for more information.
58https://dev.risczero.com/api/zkvm/acceleration
59"#
60    );
61};
62
63#[cfg(feature = "num-bigint")]
64impl<const WIDTH: usize> ToBigInt2Buffer<WIDTH> for num_bigint::BigUint {
65    fn to_u32_array(&self) -> [u32; WIDTH] {
66        let digits = self.to_u32_digits();
67        assert!(
68            digits.len() <= WIDTH,
69            "Input too large: {} words exceeds width of {} words",
70            digits.len(),
71            WIDTH,
72        );
73
74        let mut result = [0u32; WIDTH];
75        result[..digits.len()].copy_from_slice(&digits);
76        result
77    }
78
79    fn from_u32_array(array: [u32; WIDTH]) -> Self {
80        Self::from_slice(&array)
81    }
82}
83
84#[cfg(feature = "num-bigint-dig")]
85impl<const WIDTH: usize> ToBigInt2Buffer<WIDTH> for num_bigint_dig::BigUint {
86    fn to_u32_array(&self) -> [u32; WIDTH] {
87        let mut result = [0u32; WIDTH];
88        let bytes = self.to_bytes_le();
89        let max_width = WIDTH * 4;
90        assert!(
91            bytes.len() <= max_width,
92            "Input too large: {} bytes exceeds width of {} bytes",
93            bytes.len(),
94            max_width,
95        );
96
97        let mut chunks = bytes.chunks_exact(WORD_SIZE);
98        for (i, chunk) in chunks.by_ref().enumerate() {
99            result[i] = u32::from_le_bytes(chunk.try_into().unwrap());
100        }
101
102        let remainder = chunks.remainder();
103        if !remainder.is_empty() {
104            let idx = bytes.len() / WORD_SIZE;
105            let mut word = 0u32;
106            for (i, &byte) in remainder.iter().enumerate() {
107                word |= (byte as u32) << (i * 8);
108            }
109            result[idx] = word;
110        }
111
112        result
113    }
114
115    fn from_u32_array(array: [u32; WIDTH]) -> Self {
116        Self::from_slice(&array)
117    }
118}
119
120#[cfg(test)]
121struct BigUintWrap(num_bigint::BigUint);
122
123#[cfg(test)]
124impl std::str::FromStr for BigUintWrap {
125    type Err = num_bigint::ParseBigIntError;
126
127    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
128        use num_traits::Num as _;
129        Ok(BigUintWrap(num_bigint::BigUint::from_str_radix(s, 16)?))
130    }
131}