We present noir-bigint, a library that implements modular arithmetic between big-integers using a big-integer modulus. Concretely, this library allows the developer to compute
The BigNum
struct is the base struct that represents a big integer, where each limb is represented using 120 bits. The representation works by storing the limbs in a fixed-length array of size
-
new() -> Self
: creates a newBigNum
instance representing zero. -
one() -> Self
: creates a newBigNum
iprocess can be repeated recursivelnstance representing the number one. -
from_byte_be<NBytes>(x: [u8; NBytes]) -> Self
: takes an array of bytes in big-endian format. In the representation, each 120-bit limb is represented using 15 bytes. We also require that the size of the byte array is long enough to cover the number of bits in the modulus. -
validate_in_range(self)
: checks if each limb ofself
can be represented in 120 bits. Also, we check that the number is less than$2^{\lceil \log_2 p \rceil}$ . This is a lazy check to obtain a better performance that is done instead of checking thatself
is less than$p$ . -
assert_is_not_equal(self, other: Self)
: checks if$a = b \mod N$ . This method is sound but not complete. It may happen that$a \neq b$ but$a = b \mod N$ ; however, the probability that this occurs is small. -
validate_quotient_in_range(self)
: validates that the quotient produced fromevaluate_quadratic_expression()
. This validation consists of checking that each limb has 120 bits and that the last limb has the same number of bits that the last limb of the modulus$p$ plus 6. This six comes from the fact that we allow multiplications of numbers with at most 64 limbs. -
validate_in_field(self: Self)
: validate thatself
is in the field$\mathbb{Z}_p$ where$p$ is the modulus parameter. -
conditional_select(self: Self, other: Self, predicate: bool)
: given the value in thepredicate
return eitherself
ifpredicate
is zero orother
ifpredicate
is 1. -
evaluate_quadratic_expression<LHS_N, RHS_N, NUM_PRODUCTS, ADD_N>(lhs_products: [[BNExpressionInput<N, Params>; LHS_N]; NUM_PRODUCTS], rhs_products: [[BNExpressionInput<N, Params>; RHS_N]; NUM_PRODUCTS], linear_terms: [BNExpressionInput<N, Params>; ADD_N])
: constraints to the following quadratic expression:
The function receives the right-hand side products, the left-hand side products, and the linear terms and computes the appropriate
The BigNum<N, Params>
struct implements the following arithmetic traits
std::ops::Add
std::ops::Sub
std::ops::Mul
std::ops::Div
Therefore, through our library, developers can obtain
Also, the BigNum<N, Params>
implements the std::cmp::Eq
trait. With this trait, you can tell whether two BigNum
instances are equal limb by limb.
To be able to use the BigNum
struct, we first need to define the parameters that will be used to compute arithmetic operations. The list of parameters that need to be defined are:
- The modulus
$p$ : this parameter should be specified as an array of field elements. This array consists of the limbs of the modulus in 120-bitField
elements. - Double modulus
$2p$ : this parameter is the modulus$p$ of the previous described parameter multiplied by two. This parameter optimizes the computation of$2p$ , which is needed in some functions. This double modulus is also specified as an array of 120-bitField
elements representing the value's limbs. - The number of bits of the modulus.
- The algorithm that will be used to multiply two elements. Here, we have two choices: schoolbook multiplication or one of the fix-length Karatsuba multiplication algorithms. The multiplication algorithms are covered in the detailed documentation.
- The Barrett reduction parameters:
- the reduction parameter, and
- the parameter
$k$ .
For more information about Barrett reduction, we refer the reader to the book "Guide to Elliptic Curve Cryptography" by Hankerson, Menezes, and Vanstone, Section 2.2.4.
We defined the trait BigNumParamsTrait
to set the parameters, which defines functions to access each parameter. The developer must define a struct that implements those parameters and implement the corresponding functions that we show next:
trait BigNumParamsTrait<N> {
fn modulus() -> [Field; N];
fn double_modulus() -> [Field; N];
fn redc_param() -> [Field; N];
fn k() -> u64;
fn modulus_bits() -> u64;
fn mult(a: [Field; N], b: [Field; N]) -> ArrayX<Field, N, 2>;
}
In the library, you may find some implementations of the BigNumParamsTrait
in the folder src/fields/
as an example.