Introduction

zkSecurity was contracted by Aztec to audit their Bigfield implementation in the Barretenberg library. The audit was conducted between June 10th and June 28th. The specific commit that was audited is 4ba553ba31.

Methodology

zkSecurity did line-by-line review of the Barretenberg bigfield and field modules. We used a SageMath script to symbolically simplify the custom gate expressions used throughout the Barretenberg bigfield implementation to ease the subsequent manual review of the simplified expressions.

Overview of Barretenberg Bigfield

The Barretenberg bigfield module implements arithmetic of a prime field different from the native field of the circuit, i.e. it implements 𝔽p arithmetic using a (UltraPlonK) circuit over 𝔽r. In particular, it can be used to emulate a prime field which is (somewhat) larger than the native field.

Within Barretenberg, bigfield is primarily used inside the biggroup module, which implements constraints for elliptic curve operations over the emulated base field. Applications of this includes verifying a Bn254-based SNARK inside a BN254-based SNARK, or checking secp256k1-based ECDSA signatures inside a Bn254-based SNARK.

How to Embed a Field

For context, let’s briefly outline the high-level strategy employed by the bigfield module to emulate a foreign field 𝔽p.

Motivation. Because the foreign field 𝔽p=/(p) is larger than the native field 𝔽r=/(r), it is not possible to “embed” an element of 𝔽p into 𝔽r; representing it as an integer encoded in the native field. Furthermore, when we embed 𝔽p into another ring, we must ensure that all arithmetic operations can be executed without overflow: the ring is “large enough” to contain the result. As long as this is achieved, the prover can then non-deterministically reduce the representative modulo p to enable further computation in the foreign field.

A Bigger Ring. To obtain a ring sufficiently large that this embedding is possible and to allow for executing all arithmetic operations without overflow, the Barretenberg bigfield module lifts elements of the field 𝔽p=/(p) into the ring r·2k=/(r·2k), where r is the modulus of the native field and k is sufficiently large i.e. r·2kp. This ring enables emulation of integer arithmetic () as long as the elements involved are smaller than r·2k; such that to reduction modulo r·2k occurs. Emulation of 𝔽p=/(p) is done by emulating arithmetic (on bounded integers) inside r·2k and non-deterministically reducing the result modulo p (the prover witnesses the reduction and proves it correct).

CRT Decomposition. Since 2k and r are coprime, the integer ring above is isomorphic to the product r·2kr×2k and the isomorphism is given by the Chinese Remainder Theorem (CRT). Barretenberg bigfield uses this to represent an element of r·2k using its CRT decomposition: as a pair of elements in r and 2k. This enables operating independently on the two components for much better performance. Representing and computing on the r-component is trivially achieved by using the native field arithmetic, while the computation on the 2k-component is achieved by decomposing into a set of small limbs such that limb-wise intermediate results never overflow the native field.

Bounds and Range Checks. It might be immediate to the reader, but a potential source of vulnerabilities in any such emulated field is “overflows”: since the arithmetic is really done by emulating p inside r·2k which itself is emulated inside r using limb decomposition. Issue could arise in which the arithmetic is satisfied in /(r·2k) but not in and thus not in p, or in which the emulation of r·2k is incorrect because a limb overflows the native field during emulated operations in 2k. To avoid such issues, Barretenberg bigfield keeps track of the maximum sizes of all involved operands and intermediate results when generating constraints. This approach allows Barretenberg to be “lazy” about enforcing reductions modulo p, for instance allowing multiple additions to be performed before reducing the result modulo p.