Try it Live Run BLS12-381 examples in the interactive playground
Future Plans: This page is planned and under active development. Examples are placeholders and will be replaced with accurate, tested content.
Pairing Operations
The pairing operation is the mathematical foundation that makes BLS signatures possible. It’s a bilinear map that enables signature aggregation and efficient batch verification.
Pairing Definition
Optimal Ate Pairing : e: G1 × G2 → GT
Maps two elliptic curve points to an element in a multiplicative group GT (subgroup of Fp12).
Mathematical Properties
Bilinearity :
e(aP, bQ) = e(P, Q)^(ab) for all a,b ∈ Fr, P ∈ G1, Q ∈ G2
Non-degeneracy :
e(G1_generator, G2_generator) ≠ 1
Efficiency : Computable in polynomial time (~1-2ms)
Algorithm Overview
Miller Loop
Core of pairing computation. Evaluates line functions along curve doubling/addition:
Miller Loop Constant (BLS12-381):
t = 0xd201000000010000 (curve parameter)
Iterations: 64 (bit length of t)
Steps :
Initialize f = 1, T = Q
For each bit of t (from MSB):
Double: f ← f² · l_T,T(P), T ← 2T
If bit is 1: f ← f · l_T,Q(P), T ← T + Q
Return f
Final Exponentiation
Raises Miller loop result to specific power to ensure result is in prime-order subgroup:
exponent = (p^12 - 1) / r
where p = field modulus, r = curve order
Optimization : Split into easy part and hard part
Easy: (p^6 - 1)(p^2 + 1)
Hard: Cyclotomic exponentiation
Usage
Single Pairing
import { bls12_381 } from '@tevm/voltaire/crypto' ;
async function computePairing (
g1Point : Uint8Array , // 128 bytes
g2Point : Uint8Array // 256 bytes
) : Promise < Uint8Array > {
const input = new Uint8Array ( 384 );
input . set ( g1Point , 0 );
input . set ( g2Point , 128 );
const output = Bytes32 ();
await bls12_381 . pairing ( input , output );
// Note: Precompile returns pairing CHECK (result == 1)
// For raw pairing value, would need different API
return output ;
}
Multi-Pairing (Product Check)
BLS12-381 precompile computes:
e(P1, Q1) · e(P2, Q2) · ... · e(Pn, Qn) == 1
async function multiPairingCheck (
pairs : Array <{ g1 : Uint8Array , g2 : Uint8Array }>
) : Promise < boolean > {
const n = pairs . length ;
const input = new Uint8Array ( 384 * n );
for ( let i = 0 ; i < n ; i ++ ) {
const offset = 384 * i ;
input . set ( pairs [ i ]. g1 , offset );
input . set ( pairs [ i ]. g2 , offset + 128 );
}
const output = Bytes32 ();
await bls12_381 . pairing ( input , output );
return output [ 31 ] === 0x01 ;
}
BLS Signature Verification
Pairing enables signature verification:
Verification Equation :
e(signature, G2_generator) = e(H(message), publicKey)
Rearranged for single pairing check :
e(signature, G2_gen) · e(-H(message), pubkey) = 1
async function verifySignature (
signature : Uint8Array , // G1
publicKey : Uint8Array , // G2
message : Uint8Array
) : Promise < boolean > {
const messagePoint = await hashToG1 ( message );
const negMessage = negateG1 ( messagePoint );
return multiPairingCheck ([
{ g1: signature , g2: G2_GENERATOR },
{ g1: negMessage , g2: publicKey }
]);
}
Optimization Techniques
Precomputation
For fixed G2 points, precompute line functions:
// Validator pubkeys are fixed - precompute once
const precomputedPubKey = precomputeG2Lines ( validatorPubKey );
// Reuse in multiple verifications
await verifyWithPrecomputed ( signature , precomputedPubKey , message );
Batch Verification
Verify n signatures with n+1 pairings instead of 2n:
Product(e(sig_i, G2)) = Product(e(H(msg_i), pubkey_i))
Cost : ~2ms + 23ms × n vs ~2ms × 2n
Miller Loop Reuse
When G2 points are identical, Miller loop needs computation only once.
Field Arithmetic
Fp12 Tower Extension
Fp → Fp2 → Fp6 → Fp12
Fp2 = Fp[u] / (u² + 1)
Fp6 = Fp2[v] / (v³ - (1 + u))
Fp12 = Fp6[w] / (w² - v)
Frobenius Endomorphism
Fast exponentiation in extension fields:
φ(x) = x^p (Frobenius map)
For x ∈ Fp12: φ(x) computable via coordinate transformation
Used in : Final exponentiation optimization
Security Considerations
Subgroup Checks
Critical : Verify points are in prime-order subgroups
G1 subgroup: order r (255-bit)
G2 subgroup: order r (cofactor h2 = large)
GT subgroup: order r
Attack : Invalid curve attacks if subgroup not checked
// BLST automatically validates:
// - Point on curve
// - Point in correct subgroup
// - Field element validity
// Will throw error if invalid
await bls12_381 . pairing ( input , output );
Pairing Inversion
Infeasible : Computing Q from e(P, Q) given P and result
No known attack faster than ~2^128 operations.
Native (BLST) :
Single pairing: ~1.2 ms
Miller loop: ~0.8 ms
Final exponentiation: ~0.4 ms
Multi-pairing (n pairs): ~1.2ms + 0.9ms × n
Comparison :
BN254 pairing: ~0.6 ms (less secure)
BLS12-377: ~2 ms (more secure)
BLS24-315: ~5 ms (quantum-resistant candidate)
Implementation
Source : src/crypto/crypto.zig
Uses BLST library (C) via FFI:
Optimized Miller loop
Assembly-accelerated field arithmetic
Constant-time operations
References