package bls12381 import ( "encoding/binary" "math/big" "sync" "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves/native" ) type Fq [native.FieldLimbs]uint64 var bls12381FqInitonce sync.Once var bls12381FqParams native.FieldParams // 2^S * t = MODULUS - 1 with t odd const fqS = 32 // qInv = -(q^{-1} mod 2^64) mod 2^64 const qInv = 0xfffffffeffffffff // fqGenerator = 7 (multiplicative fqGenerator of r-1 order, that is also quadratic nonresidue) var fqGenerator = [native.FieldLimbs]uint64{0x0000000efffffff1, 0x17e363d300189c0f, 0xff9c57876f8457b0, 0x351332208fc5a8c4} // fqModulus var fqModulus = [native.FieldLimbs]uint64{0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48} func Bls12381FqNew() *native.Field { return &native.Field{ Value: [native.FieldLimbs]uint64{}, Params: getBls12381FqParams(), Arithmetic: bls12381FqArithmetic{}, } } func bls12381FqParamsInit() { bls12381FqParams = native.FieldParams{ R: [native.FieldLimbs]uint64{0x00000001fffffffe, 0x5884b7fa00034802, 0x998c4fefecbc4ff5, 0x1824b159acc5056f}, R2: [native.FieldLimbs]uint64{0xc999e990f3f29c6d, 0x2b6cedcb87925c23, 0x05d314967254398f, 0x0748d9d99f59ff11}, R3: [native.FieldLimbs]uint64{0xc62c1807439b73af, 0x1b3e0d188cf06990, 0x73d13c71c7b5f418, 0x6e2a5bb9c8db33e9}, Modulus: [native.FieldLimbs]uint64{0xffffffff00000001, 0x53bda402fffe5bfe, 0x3339d80809a1d805, 0x73eda753299d7d48}, BiModulus: new(big.Int).SetBytes([]byte{ 0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, 0x09, 0xa1, 0xd8, 0x05, 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01}), } } func getBls12381FqParams() *native.FieldParams { bls12381FqInitonce.Do(bls12381FqParamsInit) return &bls12381FqParams } // bls12381FqArithmetic is a struct with all the methods needed for working // in mod q type bls12381FqArithmetic struct{} // ToMontgomery converts this field to montgomery form func (f bls12381FqArithmetic) ToMontgomery(out, arg *[native.FieldLimbs]uint64) { // arg.R^0 * R^2 / R = arg.R f.Mul(out, arg, &getBls12381FqParams().R2) } // FromMontgomery converts this field from montgomery form func (f bls12381FqArithmetic) FromMontgomery(out, arg *[native.FieldLimbs]uint64) { // Mul by 1 is division by 2^256 mod q //f.Mul(out, arg, &[native.FieldLimbs]uint64{1, 0, 0, 0}) f.montReduce(out, &[native.FieldLimbs * 2]uint64{arg[0], arg[1], arg[2], arg[3], 0, 0, 0, 0}) } // Neg performs modular negation func (f bls12381FqArithmetic) Neg(out, arg *[native.FieldLimbs]uint64) { // Subtract `arg` from `fqModulus`. Ignore final borrow // since it can't underflow. var t [native.FieldLimbs]uint64 var borrow uint64 t[0], borrow = sbb(fqModulus[0], arg[0], 0) t[1], borrow = sbb(fqModulus[1], arg[1], borrow) t[2], borrow = sbb(fqModulus[2], arg[2], borrow) t[3], _ = sbb(fqModulus[3], arg[3], borrow) // t could be `fqModulus` if `arg`=0. Set mask=0 if self=0 // and 0xff..ff if `arg`!=0 mask := t[0] | t[1] | t[2] | t[3] mask = -((mask | -mask) >> 63) out[0] = t[0] & mask out[1] = t[1] & mask out[2] = t[2] & mask out[3] = t[3] & mask } // Square performs modular square func (f bls12381FqArithmetic) Square(out, arg *[native.FieldLimbs]uint64) { var r [2 * native.FieldLimbs]uint64 var carry uint64 r[1], carry = mac(0, arg[0], arg[1], 0) r[2], carry = mac(0, arg[0], arg[2], carry) r[3], r[4] = mac(0, arg[0], arg[3], carry) r[3], carry = mac(r[3], arg[1], arg[2], 0) r[4], r[5] = mac(r[4], arg[1], arg[3], carry) r[5], r[6] = mac(r[5], arg[2], arg[3], 0) r[7] = r[6] >> 63 r[6] = (r[6] << 1) | r[5]>>63 r[5] = (r[5] << 1) | r[4]>>63 r[4] = (r[4] << 1) | r[3]>>63 r[3] = (r[3] << 1) | r[2]>>63 r[2] = (r[2] << 1) | r[1]>>63 r[1] = r[1] << 1 r[0], carry = mac(0, arg[0], arg[0], 0) r[1], carry = adc(0, r[1], carry) r[2], carry = mac(r[2], arg[1], arg[1], carry) r[3], carry = adc(0, r[3], carry) r[4], carry = mac(r[4], arg[2], arg[2], carry) r[5], carry = adc(0, r[5], carry) r[6], carry = mac(r[6], arg[3], arg[3], carry) r[7], _ = adc(0, r[7], carry) f.montReduce(out, &r) } // Mul performs modular multiplication func (f bls12381FqArithmetic) Mul(out, arg1, arg2 *[native.FieldLimbs]uint64) { // Schoolbook multiplication var r [2 * native.FieldLimbs]uint64 var carry uint64 r[0], carry = mac(0, arg1[0], arg2[0], 0) r[1], carry = mac(0, arg1[0], arg2[1], carry) r[2], carry = mac(0, arg1[0], arg2[2], carry) r[3], r[4] = mac(0, arg1[0], arg2[3], carry) r[1], carry = mac(r[1], arg1[1], arg2[0], 0) r[2], carry = mac(r[2], arg1[1], arg2[1], carry) r[3], carry = mac(r[3], arg1[1], arg2[2], carry) r[4], r[5] = mac(r[4], arg1[1], arg2[3], carry) r[2], carry = mac(r[2], arg1[2], arg2[0], 0) r[3], carry = mac(r[3], arg1[2], arg2[1], carry) r[4], carry = mac(r[4], arg1[2], arg2[2], carry) r[5], r[6] = mac(r[5], arg1[2], arg2[3], carry) r[3], carry = mac(r[3], arg1[3], arg2[0], 0) r[4], carry = mac(r[4], arg1[3], arg2[1], carry) r[5], carry = mac(r[5], arg1[3], arg2[2], carry) r[6], r[7] = mac(r[6], arg1[3], arg2[3], carry) f.montReduce(out, &r) } // Add performs modular addition func (f bls12381FqArithmetic) Add(out, arg1, arg2 *[native.FieldLimbs]uint64) { var t [native.FieldLimbs]uint64 var carry uint64 t[0], carry = adc(arg1[0], arg2[0], 0) t[1], carry = adc(arg1[1], arg2[1], carry) t[2], carry = adc(arg1[2], arg2[2], carry) t[3], _ = adc(arg1[3], arg2[3], carry) // Subtract the fqModulus to ensure the value // is smaller. f.Sub(out, &t, &fqModulus) } // Sub performs modular subtraction func (f bls12381FqArithmetic) Sub(out, arg1, arg2 *[native.FieldLimbs]uint64) { d0, borrow := sbb(arg1[0], arg2[0], 0) d1, borrow := sbb(arg1[1], arg2[1], borrow) d2, borrow := sbb(arg1[2], arg2[2], borrow) d3, borrow := sbb(arg1[3], arg2[3], borrow) // If underflow occurred on the final limb, borrow 0xff...ff, otherwise // borrow = 0x00...00. Conditionally mask to add the fqModulus borrow = -borrow d0, carry := adc(d0, fqModulus[0]&borrow, 0) d1, carry = adc(d1, fqModulus[1]&borrow, carry) d2, carry = adc(d2, fqModulus[2]&borrow, carry) d3, _ = adc(d3, fqModulus[3]&borrow, carry) out[0] = d0 out[1] = d1 out[2] = d2 out[3] = d3 } // Sqrt performs modular square root func (f bls12381FqArithmetic) Sqrt(wasSquare *int, out, arg *[native.FieldLimbs]uint64) { // See sqrt_ts_ct at // https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.4 // c1 := fqS // c2 := (q - 1) / (2^c1) c2 := [4]uint64{ 0xfffe5bfeffffffff, 0x09a1d80553bda402, 0x299d7d483339d808, 0x0000000073eda753, } // c3 := (c2 - 1) / 2 c3 := [native.FieldLimbs]uint64{ 0x7fff2dff7fffffff, 0x04d0ec02a9ded201, 0x94cebea4199cec04, 0x0000000039f6d3a9, } //c4 := fqGenerator var c5 [native.FieldLimbs]uint64 native.Pow(&c5, &fqGenerator, &c2, getBls12381FqParams(), f) //c5 := [native.FieldLimbs]uint64{0x1015708f7e368fe1, 0x31c6c5456ecc4511, 0x5281fe8998a19ea1, 0x0279089e10c63fe8} var z, t, b, c, tv [native.FieldLimbs]uint64 native.Pow(&z, arg, &c3, getBls12381FqParams(), f) f.Square(&t, &z) f.Mul(&t, &t, arg) f.Mul(&z, &z, arg) copy(b[:], t[:]) copy(c[:], c5[:]) for i := fqS; i >= 2; i-- { for j := 1; j <= i-2; j++ { f.Square(&b, &b) } // if b == 1 flag = 0 else flag = 1 flag := -(&native.Field{ Value: b, Params: getBls12381FqParams(), Arithmetic: f, }).IsOne() + 1 f.Mul(&tv, &z, &c) f.Selectznz(&z, &z, &tv, flag) f.Square(&c, &c) f.Mul(&tv, &t, &c) f.Selectznz(&t, &t, &tv, flag) copy(b[:], t[:]) } f.Square(&c, &z) *wasSquare = (&native.Field{ Value: c, Params: getBls12381FqParams(), Arithmetic: f, }).Equal(&native.Field{ Value: *arg, Params: getBls12381FqParams(), Arithmetic: f, }) f.Selectznz(out, out, &z, *wasSquare) } // Invert performs modular inverse func (f bls12381FqArithmetic) Invert(wasInverted *int, out, arg *[native.FieldLimbs]uint64) { // Using an addition chain from // https://github.com/kwantam/addchain var t0, t1, t2, t3, t4, t5, t6, t7, t8 [native.FieldLimbs]uint64 var t9, t11, t12, t13, t14, t15, t16, t17 [native.FieldLimbs]uint64 f.Square(&t0, arg) f.Mul(&t1, &t0, arg) f.Square(&t16, &t0) f.Square(&t6, &t16) f.Mul(&t5, &t6, &t0) f.Mul(&t0, &t6, &t16) f.Mul(&t12, &t5, &t16) f.Square(&t2, &t6) f.Mul(&t7, &t5, &t6) f.Mul(&t15, &t0, &t5) f.Square(&t17, &t12) f.Mul(&t1, &t1, &t17) f.Mul(&t3, &t7, &t2) f.Mul(&t8, &t1, &t17) f.Mul(&t4, &t8, &t2) f.Mul(&t9, &t8, &t7) f.Mul(&t7, &t4, &t5) f.Mul(&t11, &t4, &t17) f.Mul(&t5, &t9, &t17) f.Mul(&t14, &t7, &t15) f.Mul(&t13, &t11, &t12) f.Mul(&t12, &t11, &t17) f.Mul(&t15, &t15, &t12) f.Mul(&t16, &t16, &t15) f.Mul(&t3, &t3, &t16) f.Mul(&t17, &t17, &t3) f.Mul(&t0, &t0, &t17) f.Mul(&t6, &t6, &t0) f.Mul(&t2, &t2, &t6) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, &t17) native.Pow2k(&t0, &t0, 9, f) f.Mul(&t0, &t0, &t16) native.Pow2k(&t0, &t0, 9, f) f.Mul(&t0, &t0, &t15) native.Pow2k(&t0, &t0, 9, f) f.Mul(&t0, &t0, &t15) native.Pow2k(&t0, &t0, 7, f) f.Mul(&t0, &t0, &t14) native.Pow2k(&t0, &t0, 7, f) f.Mul(&t0, &t0, &t13) native.Pow2k(&t0, &t0, 10, f) f.Mul(&t0, &t0, &t12) native.Pow2k(&t0, &t0, 9, f) f.Mul(&t0, &t0, &t11) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, &t8) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, arg) native.Pow2k(&t0, &t0, 14, f) f.Mul(&t0, &t0, &t9) native.Pow2k(&t0, &t0, 10, f) f.Mul(&t0, &t0, &t8) native.Pow2k(&t0, &t0, 15, f) f.Mul(&t0, &t0, &t7) native.Pow2k(&t0, &t0, 10, f) f.Mul(&t0, &t0, &t6) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, &t5) native.Pow2k(&t0, &t0, 16, f) f.Mul(&t0, &t0, &t3) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, &t2) native.Pow2k(&t0, &t0, 7, f) f.Mul(&t0, &t0, &t4) native.Pow2k(&t0, &t0, 9, f) f.Mul(&t0, &t0, &t2) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, &t3) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, &t2) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, &t2) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, &t2) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, &t3) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, &t2) native.Pow2k(&t0, &t0, 8, f) f.Mul(&t0, &t0, &t2) native.Pow2k(&t0, &t0, 5, f) f.Mul(&t0, &t0, &t1) native.Pow2k(&t0, &t0, 5, f) f.Mul(&t0, &t0, &t1) *wasInverted = (&native.Field{ Value: *arg, Params: getBls12381FqParams(), Arithmetic: f, }).IsNonZero() f.Selectznz(out, out, &t0, *wasInverted) } // FromBytes converts a little endian byte array into a field element func (f bls12381FqArithmetic) FromBytes(out *[native.FieldLimbs]uint64, arg *[native.FieldBytes]byte) { out[0] = binary.LittleEndian.Uint64(arg[:8]) out[1] = binary.LittleEndian.Uint64(arg[8:16]) out[2] = binary.LittleEndian.Uint64(arg[16:24]) out[3] = binary.LittleEndian.Uint64(arg[24:]) } // ToBytes converts a field element to a little endian byte array func (f bls12381FqArithmetic) ToBytes(out *[native.FieldBytes]byte, arg *[native.FieldLimbs]uint64) { binary.LittleEndian.PutUint64(out[:8], arg[0]) binary.LittleEndian.PutUint64(out[8:16], arg[1]) binary.LittleEndian.PutUint64(out[16:24], arg[2]) binary.LittleEndian.PutUint64(out[24:], arg[3]) } // Selectznz performs conditional select. // selects arg1 if choice == 0 and arg2 if choice == 1 func (f bls12381FqArithmetic) Selectznz(out, arg1, arg2 *[native.FieldLimbs]uint64, choice int) { b := uint64(-choice) out[0] = arg1[0] ^ ((arg1[0] ^ arg2[0]) & b) out[1] = arg1[1] ^ ((arg1[1] ^ arg2[1]) & b) out[2] = arg1[2] ^ ((arg1[2] ^ arg2[2]) & b) out[3] = arg1[3] ^ ((arg1[3] ^ arg2[3]) & b) } func (f bls12381FqArithmetic) montReduce(out *[native.FieldLimbs]uint64, r *[2 * native.FieldLimbs]uint64) { // Taken from Algorithm 14.32 in Handbook of Applied Cryptography var r1, r2, r3, r4, r5, r6, carry, carry2, k uint64 var rr [native.FieldLimbs]uint64 k = r[0] * qInv _, carry = mac(r[0], k, fqModulus[0], 0) r1, carry = mac(r[1], k, fqModulus[1], carry) r2, carry = mac(r[2], k, fqModulus[2], carry) r3, carry = mac(r[3], k, fqModulus[3], carry) r4, carry2 = adc(r[4], 0, carry) k = r1 * qInv _, carry = mac(r1, k, fqModulus[0], 0) r2, carry = mac(r2, k, fqModulus[1], carry) r3, carry = mac(r3, k, fqModulus[2], carry) r4, carry = mac(r4, k, fqModulus[3], carry) r5, carry2 = adc(r[5], carry2, carry) k = r2 * qInv _, carry = mac(r2, k, fqModulus[0], 0) r3, carry = mac(r3, k, fqModulus[1], carry) r4, carry = mac(r4, k, fqModulus[2], carry) r5, carry = mac(r5, k, fqModulus[3], carry) r6, carry2 = adc(r[6], carry2, carry) k = r3 * qInv _, carry = mac(r3, k, fqModulus[0], 0) rr[0], carry = mac(r4, k, fqModulus[1], carry) rr[1], carry = mac(r5, k, fqModulus[2], carry) rr[2], carry = mac(r6, k, fqModulus[3], carry) rr[3], _ = adc(r[7], carry2, carry) f.Sub(out, &rr, &fqModulus) }