Towards Zero (Knowledge): Pairing-based Cryptography using CIRCL

There are a few companies I have a good deal of time for. IBM is one company who have existed as a leader for over 100 years, and their…

Towards Zero (Knowledge): Pairing-based Cryptography using CIRCL

There are a few companies I have a good deal of time for. IBM is one company who have existed as a leader for over 100 years, and their research work on cryptography has shown them to be a world-leader. Their work on Hyperledger, too, has shown their leadership in the field. Another company I highly respect for their approach to cryptography is Cloudflare.

As an active user of Cloudflare’s product — which helps proxy Web requests to my site — I can see they have a strong leadership in Web-based security. But, it is their strive to improve cryptography, and especially around the TLS handshake, that I admire them the most. They thus contribute to the advancement of technology and share their developments. As part of this they have developed an excellent cryptography library known as CIRCL (Cloudflare Interoperable Reusable Cryptographic Library) and which uses the Golang language. One of the more recent additions is the integration of pairing-based cryptography, and which is used extensively within private preserving methods, such as in zk-Snarks.

Pairing-based cryptography

I am fascinated by cryptography pairing (pairing-friendly curve cryptography) within elliptic curves, and the potential it gives for signing, anonymisation and zero-knowledge proofs. So let’s show the potential, and demonstrate these an example. I will be using the CIRCL Go library [here], and which is a super fast implementation of pairing-based cryptography.

With pairing-based cryptography we have two cyclic groups (G1 and G2), and which are of an order of a prime number (n). A pairing on (G1,G2,GT) defines the function e:GG2→GT, and where g1 is a generator for G1 and g2 is a generator for G2. If we have the elliptic curve points of U1 and U2 on G1 and V1 and V2 on G2, we get the bilinear mapping of:

If U is a point on G1, and V is a point on G2, we get:

In this example, we will prove these mappings for a pairing for each of the above. We will use the pairing friendly BLS12–381 curve. This has a prime number of 381 bits and a security level of around 128 bits. Along with this it uses the Ate pairing method. So here is a basic Go program to implement [here]:

package main
import (
"crypto/rand"
"fmt"
	"github.com/cloudflare/circl/ecc/bls12381"
"github.com/cloudflare/circl/ecc/bls12381/ff"
)
func main() {
	g1 := bls12381.G1Generator()
g2 := bls12381.G2Generator()
	a := new(ff.Scalar)
a.Random(rand.Reader)
b := new(ff.Scalar)
b.Random(rand.Reader)
	ab := &bls12381.Scalar{}
ab.Mul(a, b)
	a_bin, _ := a.MarshalBinary()
b_bin, _ := b.MarshalBinary()
ab_bin, _ := ab.MarshalBinary()
fmt.Printf("Computing two random scalars\n")
fmt.Printf("a=%x\n", a_bin)
fmt.Printf("b=%x\n", b_bin)
fmt.Printf("ab=%x\n", ab_bin)
	aG1 := &bls12381.G1{}
bG2 := &bls12381.G2{}
abG1 := &bls12381.G1{}
aG1.ScalarMult(a, g1)
bG2.ScalarMult(b, g2)
abG1.ScalarMult(ab, g1)
	lhs := bls12381.Pair(aG1, bG2)
tmp := bls12381.Pair(g1, g2)
rhs := &bls12381.Gt{}
rhs.Exp(tmp, ab)
	rhs_bin, _ := rhs.MarshalBinary()
lhs_bin, _ := lhs.MarshalBinary()
fmt.Printf("\n\nComputing pairings\n")
fmt.Printf("e(aG1,bG2)=%x (first 20 bytes)\n", rhs_bin[0:20])
fmt.Printf("e(G1,G2)^{ab}=%x (first 20 bytes\n", lhs_bin[0:20])
	if lhs.IsEqual(rhs) {
fmt.Printf("Pairing match: e(aG1,bG2)=e(G1,G2)^{ab}")
}
	lhs = bls12381.Pair(abG1, g2)
lhs_bin, _ = rhs.MarshalBinary()
	fmt.Printf("\n\nComputing pairings\n")
fmt.Printf("e(aG1,bG2)=%x (first 20 bytes)\n", rhs_bin[0:20])
fmt.Printf("e(G1,G2)^{ab}=%x (first 20 bytes\n", lhs_bin[0:20])
if lhs.IsEqual(rhs) {
fmt.Printf("Pairing match: e(aG1,bG2)=e(abG1,G2)")
}
}

The pairings are 576 bytes long, but we just truncate to 20 bytes in this case. A sample run is [here]:

Computing two random scalars
a=48555bb965503b6ed302c9173094ca3e43dabb163f43113478128bd55e652144
b=351f2460efe906bee3aa067af91263617413e978f02eb3447fd6312dd2c67a4b
ab=2c7ede0f9f16d63310810301bbfbdd33c10101df96a38f68829b38273648ed99

Computing pairings
e(aG1,bG2)=0e84502bc8c7891b514364008d1f253a50c0d552 (first 20 bytes)
e(G1,G2)^{ab}=0e84502bc8c7891b514364008d1f253a50c0d552 (first 20 bytes
Pairing match: e(aG1,bG2)=e(G1,G2)^{ab}
Computing pairings
e(aG1,bG2)=0e84502bc8c7891b514364008d1f253a50c0d552 (first 20 bytes)
e(G1,G2)^{ab}=0e84502bc8c7891b514364008d1f253a50c0d552 (first 20 bytes
Pairing match: e(aG1,bG2)=e(abG1,G2)

And there you go. If you are interesting in knowing more about the CIRLC library, try here:

https://asecuritysite.com/circl

and for pairing-based cryptography:

https://asecuritysite.com/pairing/