Crypto Pairing With Rust … The MIRACL Way

We are extremely privildged to partner with some amazing companies. These companies are typically small and innovative, and MIRACL is one…

Crypto Pairing With Rust … The MIRACL Way

We are extremely privileged to partner with some amazing companies. These companies are typically small and innovative, and MIRACL is one of these. At the core of MIRACLs work is the deep knowledge of public-key encryption, especially focused on elliptic curve methods. One of their libraries supports the BN254 curve and which is all ready for an extremely useful method: pairing-based cryptography. This method can be used in many advanced methods including zero-knowledge proofs and in attribute-based encryption (ABE).

So a while back I created a whole lot of Golang demonstrators:

https://asecuritysite.com/pairing/

But over the last few weeks, I’ve been implementing a whole range of methods with Rust. I searched widely and tried many libraries for Rust integration, but none really gave me the required flexibility. And so I found MIRACL, and it is truly a million miles ahead of any other library in this area, so let’s investigate a basic implementation.

Implementation

With crypto pairing, we have two elliptic curves: G1 and G2, and then map the points on these curves onto a third curve: G. The pairing of the points on G1 (P) and G2 (Q) is defined as a pairing function, and where we determine e(Q,P). For the based points of P and Q, and scalar values of a and b, this has special mappings of:

The following code uses the MIRACL Rust library to compute the mappings [here]:

extern crate rand_core;
use mcore::bn254::big;
use mcore::bn254::ecp;
use mcore::bn254::ecp2;
use mcore::bn254::fp;
use mcore::bn254::pair;
use mcore::bn254::rom;
use mcore::rand::{RAND,RAND_impl};
use rand::Rng;
fn main() {
let random_bytes = rand::thread_rng().gen::<[u8; 32]>();
let mut rng = RAND_impl::new();

rng.seed(32, &random_bytes);
println!("bn254 Pairings");
println!("Modulus size {:} bits", fp::MODBITS);
let P = ecp::ECP::generator();
let Q = ecp2::ECP2::generator();
let max = big::BIG::new_ints(&rom::CURVE_ORDER);
let r = big::BIG::randomnum(&max, &mut rng);
let s = big::BIG::randomnum(&max, &mut rng);
println!("r={}",r.to_string());
println!("s={}",s.to_string());
println!("\nP (on G1)= {}",P);
println!("Q (on G2)= {}",Q);
let rP = pair::g1mul( &P, &r);
let sQ= pair::g2mul(&Q, &s);
println!("\nrP (on G1)= {}",rP);
println!("sQ (on G2)= {}",sQ);
let mut p1 = pair::ate(&sQ, &rP);
p1 = pair::fexp(&p1);
println!("\n=== Proof 1 ===");
let sP = pair::g1mul( &P, &s);
let rQ= pair::g2mul(&Q, &r);

let mut p2 = pair::ate(&rQ, &sP);
p2 = pair::fexp(&p2);
if (p1.equals(&p2)) { println!("e(sQ,rP)=e(rQ,sP)"); }
else { println!("Error:e(sQ,rP)!=e(rQ,sP)"); }
println!("=== Proof 2 ===");
let srP = pair::g1mul( &sP, &r);
let mut p3 = pair::ate(&Q, &srP);
p3 = pair::fexp(&p3);
if (p1.equals(&p3)) { println!("e(sQ,rP)=e(Q,srP)"); }
else { println!("Error:(sQ,rP)!=e(Q,srP)"); }
println!("=== Proof 3 ===");
let srQ = pair::g2mul( &sQ, &r);
let mut p4 = pair::ate(&srQ, &P);
p4 = pair::fexp(&p4);
if (p1.equals(&p4)) { println!("e(sQ,rP)=e(srQ,P)"); }
else { println!("Error:e(sQ,rP)!=e(srQ,P)"); }
println!("=== Proof 4 ===");
let mut p5 = pair::ate(&sQ, &P);
p5 = pair::fexp(&p5).pow(&r);
if (p1.equals(&p5)) { println!("e(sQ,rP)=e(sQ,P)^r"); }
else { println!("Error:e(sQ,rP)!=e(sQ,P)^r"); }
println!("=== Proof 5 ===");
//    let rs=mcore::bn254::big::BIG::smul(&s, &r);
let mut p6 = pair::ate(&Q, &P);
p6 = pair::fexp(&p6).pow(&r).pow(&s);
if (p1.equals(&p6)) { println!("e(sQ,rP)=e(Q,P)^(rs)"); }
else { println!("Error:e(sQ,rP)!=e(Q,P)^(rs)"); }
println!("=== Proof 6 ===");
let R = ecp2::ECP2::generator();

let mut R_plus_Q=R.clone();
R_plus_Q.add(&Q);
let mut p7 = pair::ate(&R_plus_Q, &P);
p7 = pair::fexp(&p7);
let mut p8 = pair::ate(&R, &P);
p8 = pair::fexp(&p8);
let mut p9 = pair::ate(&Q, &P);
p9 = pair::fexp(&p9);
p8.mul(&p9);
if (p7.equals(&p8)) { println!("e(R+Q,P)=e(R,P)*e(Q,P)"); }
else { println!("Error:e(R+Q,P)=e(R,P)*e(Q,P)"); }
}

And a sample run [here]:

bn254 Pairings
Modulus size 254 bits
r=124348D8061F151F64EE4FF43973E1024FEBFEA6988652B5421B8E8A88194D1C
s=0ACB846EA24914E605A844262AF29317E190CA459CD6D5466794DEFC001F4D75

P (on G1)= (2523648240000001BA344D80000000086121000000000013A700000000000012,0000000000000000000000000000000000000000000000000000000000000001)
Q (on G2)= ([061A10BB519EB62FEB8D8C7E8C61EDB6A4648BBB4898BF0D91EE4224C803FB2B,0516AAF9BA737833310AA78C5982AA5B1F4D746BAE3784B70D8C34C1E7D54CF3],[021897A06BAF93439A90E096698C822329BD0AE6BDBE09BD19F0E07891CD2B9A,0EBB2B0E7C8B15268F6D4456F5F38D37B09006FFD739C9578A2D1AEC6B3ACE9B])

rP (on G1)= (02CFA0295811FCFC99DEAFB840ECC455C9D3A2F2C2E10DEE1430C096FFAEDF64,2261F3C6C0EC61595EE1C2BB7120D261B6486EE8FEB3CBDD6E07725F804B3C74)
sQ (on G2)= ([07B98C0203B5868FE1748B3E9282E434346985D383201C0C5E63254F816AE7ED,02408A8A21757989E3D144B3112AA7E883530FAF91439FCC8E1BF886B55DC9FC],[1D2FCB483D6D429B7837927D930639FC2DAF61E28E93AB20E9867B34F8C31034,10BC96A5CA0B0A92CC8366F4EAEFDCA74D198A75238E615EEACFB59FFC043430])

=== Proof 1 ===
e(sQ,rP)=e(rQ,sP)
=== Proof 2 ===
e(sQ,rP)=e(Q,srP)
=== Proof 3 ===
e(sQ,rP)=e(srQ,P)
=== Proof 4 ===
e(sQ,rP)=e(sQ,P)^r
=== Proof 5 ===
e(sQ,rP)=e(Q,P)^(rs)
=== Proof 6 ===
e(R+Q,P)=e(R,P)*e(Q,P)

Conclusions

There are so many applications of pairing, and MIRACL’s Rust library is so easy to integrate. What’s really great, is that I managed to get the code running in Repl.it, too: