Skip to main content

BLS12-381

zisklib functions for BLS12-381 field arithmetic (Fp, Fp2, Fr, Fp6, Fp12), G1 and G2 curve operations, Miller loop, final exponentiation, optimal Ate pairing, KZG verification, and hash-to-curve helpers.

Overview

BLS12-381 is a ~128-bit BLS pairing-friendly curve, powering Ethereum 2.0 signatures, Filecoin proofs, KZG / EIP-4844 commitments, and modern zkSNARKs. It's y² = x³ + 4 over Fp (381 bits), embedding degree 12, with the tower Fp ⊂ Fp2 = Fp[u]/(u² + 1) ⊂ Fp6 = Fp2[v]/(v³ − (u+1)) ⊂ Fp12 = Fp6[w]/(w² − v). G1 lives over Fp, G2 on a sextic twist over Fp2, and GT in Fp12; the scalar field Fr is 255 bits. Pairings are computed in two stages: a Miller loop yields an Fp12 element, then a final exponentiation maps it into the cyclotomic subgroup of Fp12, where exponentiation by the BLS parameter x is cheap. KZG proof verification (one pairing check) and a simplified-SWU map-to-curve for BLS signatures sit on top.

The page is organized bottom-up:

SectionProvidesDepends on
Base field (Fp)Arithmetic for every coordinate computation.
Extension field (Fp2)Quadratic extension Fp[u]/(u² + 1); host of G2 coordinates.Fp
Scalar field (Fr)Arithmetic for scalar multiplication and exponents.
UtilitiesByte/limb conversions bridging external encodings.
G1 curvePoint operations on y² = x³ + 4 over Fp.Fp
G2 / twistPoint operations on the sextic twist over Fp2.Fp2
Fp6 towerCubic extension Fp2[v]/(v³ − (u+1)).Fp2
Fp12 towerQuadratic extension Fp6[w]/(w² − v); host of GT.Fp6
Miller loopMaps a (G1, G2) pair to an Fp12 element — the first half of pairing.G1, G2/twist, Fp12
Final exponentiationMaps an Fp12 element into the cyclotomic subgroup.Fp12
Cyclotomic subgroupCompressed squaring and exponentiation by x in the small-order Fp12 subgroup.Fp12
PairingHigh-level pairing_bls12_381, pairing_batch_bls12_381, pairing_check_bls12_381.G1, G2/twist, Fp12
KZGKZG polynomial commitment proof verification (one pairing check; EIP-4844).G1, G2/twist, Pairing
Hash-to-curveSimplified-SWU map from Fp / Fp2 elements onto G1 / G2.Fp, Fp2, G1, G2/twist

Base field (Fp)

The BLS12-381 base field Fp, where p is a 381-bit prime and the curve equation y² = x³ + 4 is defined. Elements are represented as [u64; 6]: six little-endian limbs encoding a 384-bit value reduced modulo p. Every higher layer of the tower (Fp2, Fp6, Fp12) and both curve groups ultimately reduce to operations here.

Returns the sign of an Fp element: the least significant bit of its canonical representative, as defined in RFC 9380.

pub fn sgn0_fp_bls12_381(x: &[u64; 6]) -> u64

Parameters

NameTypeDescription
x&[u64; 6]BLS12-381 Fp element as 6 LE limbs.

Returns

TypeDescription
u640 if the element is even, 1 if odd (LSB of the canonical representative).

Example

let sign = zisklib::sgn0_fp_bls12_381(&x_limbs);

Extension field (Fp2)

The quadratic extension Fp2 = Fp[u] / (u² + 1). Elements are pairs (re, im) of Fp values, stored as [u64; 12] (the first six limbs are re, the next six are im). G2 coordinates are Fp2 elements, and Fp6 and Fp12 are towers built on top of Fp2, so this section feeds both the G2 twist and the pairing target.

Sign of a BLS12-381 Fp2 element, as defined in RFC 9380 for hash-to-curve.

pub fn sgn0_fp2_bls12_381(a: &[u64; 12]) -> u64

Parameters

NameTypeDescription
a&[u64; 12]BLS12-381 Fp2 element as 12 LE limbs (real ++ imaginary).

Returns

TypeDescription
u64The sign bit as defined in RFC 9380: based on the LSB of the first non-zero component.

Example

let sign = zisklib::sgn0_fp2_bls12_381(&a);

Scalar field (Fr)

The scalar field Fr, which is the prime-order subgroup order shared by G1, G2, and GT. Scalars are 256-bit values represented as [u64; 4] in little-endian limb order, and they are the exponents used in scalar multiplication on the curve and in pairing-based protocols. KZG and BLS signature scalars all live here.

Reduces a 256-bit integer modulo the BLS12-381 scalar field order r. Returns the input unchanged when it is already in range.

pub fn reduce_fr_bls12_381(x: &[u64; 4]) -> [u64; 4]

Parameters

NameTypeDescription
x&[u64; 4]256-bit integer as 4 LE limbs.

Returns

TypeDescription
[u64; 4]x mod r in the BLS12-381 scalar field.

Example

let reduced = zisklib::reduce_fr_bls12_381(&scalar);

Utilities

Encoding and conversion helpers that bridge external byte representations with the internal limb layouts used by the rest of the module. These cover scalar-byte conversions and other glue needed when feeding inputs into the field, curve, and pairing routines below.

Converts a big-endian 32-byte scalar to 4 little-endian u64 limbs. No range check is performed; use reduce_fr_bls12_381 if you need the result modulo r.

pub fn scalar_bytes_be_to_u64_le_bls12_381(
bytes: &[u8; 32],
) -> [u64; 4]

Parameters

NameTypeDescription
bytes&[u8; 32]Big-endian 32-byte scalar representation.

Returns

TypeDescription
[u64; 4]The same value as 4 little-endian u64 limbs.

Example

let limbs = zisklib::scalar_bytes_be_to_u64_le_bls12_381(&be_bytes);

G1 curve

The prime-order subgroup G1 of the curve y² = x³ + 4 over Fp. Points are stored as [u64; 12] (the affine coordinates x ++ y, six limbs each), with the identity encoded as the all-zero buffer. Two flavors of group law are provided: the add_* and dbl_* routines assume non-zero, non-equal inputs and are faster, while the add_complete_* variants handle every case (identity, equal inputs, opposite inputs) at the cost of extra checks. G1 is the first argument of the pairing and the group used by KZG commitments.

Decompresses a 48-byte ZCash-format compressed G1 point. The top 3 bits of the first byte are flags: bit 7 is the compression flag (must be 1), bit 6 is the infinity flag, bit 5 is the sign of y. Returns Err if the input is malformed or does not lie on the curve.

pub fn decompress_bls12_381(
input: &[u8; 48],
) -> Result<[u64; 12], &'static str>

Parameters

NameTypeDescription
input&[u8; 48]Compressed G1 point in the standard ZCash serialization (flags in the top 3 bits of byte 0).

Returns

TypeDescription
Result<[u64; 12], &'static str>Ok([u64; 12]) with the decompressed affine point (6 LE limbs for x ++ 6 LE limbs for y), or Err(&'static str) describing why decompression failed.

Example

let point = zisklib::decompress_bls12_381(&compressed_bytes)
.expect("invalid compressed G1 point");

G2 / twist

The prime-order subgroup G2, defined on a sextic twist of the curve over Fp2. Points are stored as [u64; 24] (a pair of Fp2 coordinates), with the identity encoded as the all-zero buffer. As in G1, the add_* and dbl_* routines assume non-zero, non-equal inputs while the add_complete_* variants handle every edge case including the identity. G2 is the second argument of the pairing and the group BLS signatures live in.

Decompresses a 96-byte ZCash-format compressed G2 point on the BLS12-381 twist E': y² = x³ + 4·(1+u). The top 3 bits of the first byte are flags identical in meaning to the G1 form. Returns the affine point together with a boolean indicating whether the encoded point is the identity.

pub fn decompress_twist_bls12_381(
input: &[u8; 96],
) -> Result<([u64; 24], bool), &'static str>

Parameters

NameTypeDescription
input&[u8; 96]Compressed G2 point: 48 bytes for x_i (with flags in byte 0) followed by 48 bytes for x_r.

Returns

TypeDescription
Result<([u64; 24], bool), &'static str>Ok((point, is_identity)) with the decompressed affine point as 24 LE limbs (Fp2 x ++ Fp2 y) and a flag that is true when the input encodes the point at infinity, or Err(&'static str) describing why decompression failed.

Example

let (point, is_inf) = zisklib::decompress_twist_bls12_381(&bytes)
.expect("invalid compressed G2 point");

Fp6 tower

The sextic extension Fp6 = Fp2[v] / (v³ − (u + 1)), built directly on top of Fp2. Elements are represented as [u64; 36] — three Fp2 coefficients in the basis 1, v, v². Fp6 is the intermediate layer between Fp2 and Fp12: every Fp12 multiplication ultimately decomposes into Fp6 operations, which is why this section sits alongside the pairing internals rather than next to the other field sections.

Addition in the BLS12-381 sextic extension Fp6 = Fp2[v]/(v³ − (1+u)). Elements are stored as three Fp2 coefficients: (c0, c1, c2) laid out as [u64; 36].

pub fn add_fp6_bls12_381(a: &[u64; 36], b: &[u64; 36]) -> [u64; 36]

Parameters

NameTypeDescription
a&[u64; 36]First Fp6 element as three Fp2 limbs (c0 ++ c1 ++ c2).
b&[u64; 36]Second Fp6 element as three Fp2 limbs.

Returns

TypeDescription
[u64; 36]a + b in BLS12-381 Fp6.

Fp12 tower

The dodecic extension Fp12 = Fp6[w] / (w² − v), the pairing target field. Elements are represented as [u64; 72] — two Fp6 coefficients in the basis 1, w. The Miller loop accumulates an Fp12 element, final exponentiation maps it into the GT subgroup, and the cyclotomic-subgroup routines provide cheaper variants for the particular shape of elements that show up after that mapping.

Multiplication in the BLS12-381 dodecic extension Fp12 = Fp6[w]/(w² − v). Elements are stored as two Fp6 coefficients (c0, c1) laid out as [u64; 72]. Uses Karatsuba in Fp6.

pub fn mul_fp12_bls12_381(a: &[u64; 72], b: &[u64; 72]) -> [u64; 72]

Parameters

NameTypeDescription
a&[u64; 72]First Fp12 operand.
b&[u64; 72]Second Fp12 operand.

Returns

TypeDescription
[u64; 72]a * b in BLS12-381 Fp12.

Miller loop

The Miller loop, the first half of the optimal Ate pairing. It walks the binary expansion of the BLS parameter, evaluating line functions through points of G2 at a G1 point and accumulating the product into an Fp12 element. The result is not yet pairing-canonical — it must be passed through the final exponentiation below before it lives in GT.

Computes the Miller loop for a non-zero G1 point p and a non-zero G2 point q, following the optimal Ate exponent for BLS12-381. Line coefficients (λ, μ) are hinted and verified on the fly.

pub fn miller_loop_bls12_381(
p: &[u64; 12],
q: &[u64; 24],
) -> [u64; 72]

Parameters

NameTypeDescription
p&[u64; 12]G1 point as 12 LE limbs. Must be non-zero.
q&[u64; 24]G2 point as 24 LE limbs. Must be non-zero.

Returns

TypeDescription
[u64; 72]Miller loop output as an Fp12 element — the inverse of f_(abs X, q)(p) (the final conjugation accounts for the negative sign of X).
warning

Not optimized for the infinity case. Use pairing_bls12_381 for end-to-end pairings or pre-filter identity inputs.


Final exponentiation

The second half of the optimal Ate pairing: raises an Fp12 element to the power (p¹² − 1) / r, mapping the Miller-loop output into the order-r GT subgroup of Fp12. It is split into an easy part ((p⁶ − 1)(p² + 1)) and a hard part ((p⁴ − p² + 1)/r) evaluated via cyclotomic exponentiations defined in the next section. The implementation is not optimized for the degenerate case where the input is already 1.

Raises an Fp12 element to the power (p¹² − 1)/r, computing the final exponentiation of the BLS12-381 Ate pairing. Splits into an easy part ((p⁶ − 1)(p² + 1)) followed by the hard part (p⁴ − p² + 1)/r evaluated via cyclotomic exponentiations.

pub fn final_exp_bls12_381(f: &[u64; 72]) -> [u64; 72]

Parameters

NameTypeDescription
f&[u64; 72]Fp12 element, typically the output of a Miller loop.

Returns

TypeDescription
[u64; 72]f^((p¹² − 1)/r) as an Fp12 element in the GT subgroup.
warning

Not optimized for the case f == 1.


Cyclotomic subgroup

The cyclotomic subgroup of Fp12 — the small-order subgroup where pairing outputs live after final exponentiation. Elements of GT sit inside this subgroup, and its special structure allows squarings and exponentiations that are significantly cheaper than generic Fp12 operations. A compressed representation as [u64; 48] (four Fp2 entries) is also exposed, and these routines are what the final exponentiation and pairing-equation evaluations rely on internally.

Compresses an Fp12 element in the cyclotomic subgroup GΦ₆(p²) from six Fp2 coefficients down to four: the encoding maps a = (a0 + a4·v + a3·v²) + (a2 + a1·v + a5·v²)·w to [a2, a3, a4, a5].

pub fn compress_cyclo_bls12_381(a: &[u64; 72]) -> [u64; 48]

Parameters

NameTypeDescription
a&[u64; 72]Fp12 element assumed to lie in the cyclotomic subgroup GΦ₆(p²).

Returns

TypeDescription
[u64; 48]The compressed representation [a2, a3, a4, a5] as four packed Fp2 elements.
warning

If the input does not lie in GΦ₆(p²) then the compress / decompress round-trip is not well-defined and the output of decompress_cyclo_bls12_381 will not match a.


Pairing

The high-level optimal Ate pairing e: G1 × G2 → GT, packaging the Miller loop and the final exponentiation into a single call. The output is a GT element stored as [u64; 72] (an Fp12 in the cyclotomic subgroup). This is the entry point KZG verification and BLS signature verification ultimately call into.

Optimal Ate pairing e(p, q) on BLS12-381. Returns the GT element as [u64; 72] (Fp12). Returns the GT identity 1 when either input is the point at infinity.

pub fn pairing_bls12_381(
p: &[u64; 12],
q: &[u64; 24],
) -> [u64; 72]

Parameters

NameTypeDescription
p&[u64; 12]G1 point as 12 LE limbs.
q&[u64; 24]G2 point as 24 LE limbs.

Returns

TypeDescription
[u64; 72]The pairing result e(p, q) as an Fp12 GT element.

Example

let gt = zisklib::pairing_bls12_381(&g1_point, &g2_point);

KZG

KZG polynomial-commitment proof verification, as used by EIP-4844 for Ethereum blob data. Verification reduces to checking a single pairing equation of the form e(commitment − [y]G, G) = e(proof, [z]G − [x]G), where the right-hand-side [z]G and [x]G come from the trusted-setup parameters. This section consumes the G1, G2, and Pairing pieces above and exposes a single high-level verification entry point.

Verifies a KZG opening proof against the EIP-4844 trusted setup hard-coded in zisklib. Checks that the polynomial committed by commitment_bytes evaluates to y_bytes at z_bytes using the witness proof_bytes. All inputs are big-endian byte arrays; z_bytes and y_bytes must be canonical scalars (strictly less than r).

pub fn verify_kzg_proof(
z_bytes: &[u8; 32],
y_bytes: &[u8; 32],
commitment_bytes: &[u8; 48],
proof_bytes: &[u8; 48],
) -> bool

Parameters

NameTypeDescription
z_bytes&[u8; 32]Evaluation point as a big-endian 32-byte scalar. Must satisfy z < r.
y_bytes&[u8; 32]Claimed evaluation value as a big-endian 32-byte scalar. Must satisfy y < r.
commitment_bytes&[u8; 48]KZG commitment C as a compressed G1 point (big-endian).
proof_bytes&[u8; 48]Opening proof π as a compressed G1 point (big-endian).

Returns

TypeDescription
booltrue if the KZG proof is valid, false otherwise (including if any input fails to decode or z / y are out of range).

Example

let valid = zisklib::verify_kzg_proof(
&z_bytes,
&y_bytes,
&commitment,
&proof,
);
assert!(valid);

Hash-to-curve

Hash-to-curve / map-to-curve primitives that produce a point on the curve from a field element, using the simplified SWU map for both G1 and G2. These are the building blocks of RFC 9380 hash-to-curve suites — in particular, BLS signatures rely on hashing messages into G2 — and they combine the sgn0 and field-arithmetic helpers from the earlier sections with the curve groups defined above.

Maps an Fp element to a G1 point in the prime-order subgroup using the simplified SWU method on the 11-isogenous curve, followed by the isogeny back to E and cofactor clearing (RFC 9380).

pub fn map_to_curve_g1_bls12_381(
u: &[u64; 6],
) -> Result<[u64; 12], u8>

Parameters

NameTypeDescription
u&[u64; 6]Fp field element as 6 LE limbs. Must be canonical (u < p).

Returns

TypeDescription
Result<[u64; 12], u8>Ok([u64; 12]) with a G1 point in the prime-order subgroup, or Err(1) if u ≥ p.

Example

let point = zisklib::map_to_curve_g1_bls12_381(&u_limbs)
.expect("u not in Fp");