package bls12381 import ( "fmt" "io" "math/big" "github.com/pkg/errors" "source.quilibrium.com/quilibrium/monorepo/nekryptology/internal" "source.quilibrium.com/quilibrium/monorepo/nekryptology/pkg/core/curves/native" ) var ( g2x = fp2{ A: fp{ 0xf5f2_8fa2_0294_0a10, 0xb3f5_fb26_87b4_961a, 0xa1a8_93b5_3e2a_e580, 0x9894_999d_1a3c_aee9, 0x6f67_b763_1863_366b, 0x0581_9192_4350_bcd7, }, B: fp{ 0xa5a9_c075_9e23_f606, 0xaaa0_c59d_bccd_60c3, 0x3bb1_7e18_e286_7806, 0x1b1a_b6cc_8541_b367, 0xc2b6_ed0e_f215_8547, 0x1192_2a09_7360_edf3, }, } g2y = fp2{ A: fp{ 0x4c73_0af8_6049_4c4a, 0x597c_fa1f_5e36_9c5a, 0xe7e6_856c_aa0a_635a, 0xbbef_b5e9_6e0d_495f, 0x07d3_a975_f0ef_25a2, 0x0083_fd8e_7e80_dae5, }, B: fp{ 0xadc0_fc92_df64_b05d, 0x18aa_270a_2b14_61dc, 0x86ad_ac6a_3be4_eba0, 0x7949_5c4e_c93d_a33a, 0xe717_5850_a43c_caed, 0x0b2b_c2a1_63de_1bf2, }, } curveG2B = fp2{ A: fp{ 0xaa27_0000_000c_fff3, 0x53cc_0032_fc34_000a, 0x478f_e97a_6b0a_807f, 0xb1d3_7ebe_e6ba_24d7, 0x8ec9_733b_bf78_ab2f, 0x09d6_4551_3d83_de7e, }, B: fp{ 0xaa27_0000_000c_fff3, 0x53cc_0032_fc34_000a, 0x478f_e97a_6b0a_807f, 0xb1d3_7ebe_e6ba_24d7, 0x8ec9_733b_bf78_ab2f, 0x09d6_4551_3d83_de7e, }, } curveG23B = fp2{ A: fp{ 0x447600000027552e, 0xdcb8009a43480020, 0x6f7ee9ce4a6e8b59, 0xb10330b7c0a95bc6, 0x6140b1fcfb1e54b7, 0x0381be097f0bb4e1, }, B: fp{ 0x447600000027552e, 0xdcb8009a43480020, 0x6f7ee9ce4a6e8b59, 0xb10330b7c0a95bc6, 0x6140b1fcfb1e54b7, 0x0381be097f0bb4e1, }, } sswuMapA = fp2{ A: fp{}, B: fp{ 0xe53a000003135242, 0x01080c0fdef80285, 0xe7889edbe340f6bd, 0x0b51375126310601, 0x02d6985717c744ab, 0x1220b4e979ea5467, }, } sswuMapB = fp2{ A: fp{ 0x22ea00000cf89db2, 0x6ec832df71380aa4, 0x6e1b94403db5a66e, 0x75bf3c53a79473ba, 0x3dd3a569412c0a34, 0x125cdb5e74dc4fd1, }, B: fp{ 0x22ea00000cf89db2, 0x6ec832df71380aa4, 0x6e1b94403db5a66e, 0x75bf3c53a79473ba, 0x3dd3a569412c0a34, 0x125cdb5e74dc4fd1, }, } sswuMapZ = fp2{ A: fp{ 0x87ebfffffff9555c, 0x656fffe5da8ffffa, 0x0fd0749345d33ad2, 0xd951e663066576f4, 0xde291a3d41e980d3, 0x0815664c7dfe040d, }, B: fp{ 0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x07e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x040ab3263eff0206, }, } sswuMapZInv = fp2{ A: fp{ 0xacd0000000011110, 0x9dd9999dc88ccccd, 0xb5ca2ac9b76352bf, 0xf1b574bcf4bc90ce, 0x42dab41f28a77081, 0x132fc6ac14cd1e12, }, B: fp{ 0xe396ffffffff2223, 0x4fbf332fcd0d9998, 0x0c4bbd3c1aff4cc4, 0x6b9c91267926ca58, 0x29ae4da6aef7f496, 0x10692e942f195791, }, } swwuMapMbDivA = fp2{ A: fp{ 0x903c555555474fb3, 0x5f98cc95ce451105, 0x9f8e582eefe0fade, 0xc68946b6aebbd062, 0x467a4ad10ee6de53, 0x0e7146f483e23a05, }, B: fp{ 0x29c2aaaaaab85af8, 0xbf133368e30eeefa, 0xc7a27a7206cffb45, 0x9dee04ce44c9425c, 0x04a15ce53464ce83, 0x0b8fcaf5b59dac95, }, } g2IsoXNum = []fp2{ { A: fp{ 0x47f671c71ce05e62, 0x06dd57071206393e, 0x7c80cd2af3fd71a2, 0x048103ea9e6cd062, 0xc54516acc8d037f6, 0x13808f550920ea41, }, B: fp{ 0x47f671c71ce05e62, 0x06dd57071206393e, 0x7c80cd2af3fd71a2, 0x048103ea9e6cd062, 0xc54516acc8d037f6, 0x13808f550920ea41, }, }, { A: fp{ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, }, B: fp{ 0x5fe55555554c71d0, 0x873fffdd236aaaa3, 0x6a6b4619b26ef918, 0x21c2888408874945, 0x2836cda7028cabc5, 0x0ac73310a7fd5abd, }, }, { A: fp{ 0x0a0c5555555971c3, 0xdb0c00101f9eaaae, 0xb1fb2f941d797997, 0xd3960742ef416e1c, 0xb70040e2c20556f4, 0x149d7861e581393b, }, B: fp{ 0xaff2aaaaaaa638e8, 0x439fffee91b55551, 0xb535a30cd9377c8c, 0x90e144420443a4a2, 0x941b66d3814655e2, 0x0563998853fead5e, }, }, { A: fp{ 0x40aac71c71c725ed, 0x190955557a84e38e, 0xd817050a8f41abc3, 0xd86485d4c87f6fb1, 0x696eb479f885d059, 0x198e1a74328002d2, }, B: fp{ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, }, }, } g2IsoXDen = []fp2{ { A: fp{ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, }, B: fp{ 0x1f3affffff13ab97, 0xf25bfc611da3ff3e, 0xca3757cb3819b208, 0x3e6427366f8cec18, 0x03977bc86095b089, 0x04f69db13f39a952, }, }, { A: fp{ 0x447600000027552e, 0xdcb8009a43480020, 0x6f7ee9ce4a6e8b59, 0xb10330b7c0a95bc6, 0x6140b1fcfb1e54b7, 0x0381be097f0bb4e1, }, B: fp{ 0x7588ffffffd8557d, 0x41f3ff646e0bffdf, 0xf7b1e8d2ac426aca, 0xb3741acd32dbb6f8, 0xe9daf5b9482d581f, 0x167f53e0ba7431b8, }, }, { A: fp{ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, }, B: fp{ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, }, }, } g2IsoYNum = []fp2{ { A: fp{ 0x96d8f684bdfc77be, 0xb530e4f43b66d0e2, 0x184a88ff379652fd, 0x57cb23ecfae804e1, 0x0fd2e39eada3eba9, 0x08c8055e31c5d5c3, }, B: fp{ 0x96d8f684bdfc77be, 0xb530e4f43b66d0e2, 0x184a88ff379652fd, 0x57cb23ecfae804e1, 0x0fd2e39eada3eba9, 0x08c8055e31c5d5c3, }, }, { A: fp{ 000000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, }, B: fp{ 0xbf0a71c71c91b406, 0x4d6d55d28b7638fd, 0x9d82f98e5f205aee, 0xa27aa27b1d1a18d5, 0x02c3b2b2d2938e86, 0x0c7d13420b09807f, }, }, { A: fp{ 0xd7f9555555531c74, 0x21cffff748daaaa8, 0x5a9ad1866c9bbe46, 0x4870a2210221d251, 0x4a0db369c0a32af1, 0x02b1ccc429ff56af, }, B: fp{ 0xe205aaaaaaac8e37, 0xfcdc000768795556, 0x0c96011a8a1537dd, 0x1c06a963f163406e, 0x010df44c82a881e6, 0x174f45260f808feb, }, }, { A: fp{ 0xa470bda12f67f35c, 0xc0fe38e23327b425, 0xc9d3d0f2c6f0678d, 0x1c55c9935b5a982e, 0x27f6c0e2f0746764, 0x117c5e6e28aa9054, }, B: fp{ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, }, }, } g2IsoYDen = []fp2{ { A: fp{ 0x0162fffffa765adf, 0x8f7bea480083fb75, 0x561b3c2259e93611, 0x11e19fc1a9c875d5, 0xca713efc00367660, 0x03c6a03d41da1151, }, B: fp{ 0x0162fffffa765adf, 0x8f7bea480083fb75, 0x561b3c2259e93611, 0x11e19fc1a9c875d5, 0xca713efc00367660, 0x03c6a03d41da1151, }, }, { A: fp{ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, }, B: fp{ 0x5db0fffffd3b02c5, 0xd713f52358ebfdba, 0x5ea60761a84d161a, 0xbb2c75a34ea6c44a, 0x0ac6735921c1119b, 0x0ee3d913bdacfbf6, }, }, { A: fp{ 0x66b10000003affc5, 0xcb1400e764ec0030, 0xa73e5eb56fa5d106, 0x8984c913a0fe09a9, 0x11e10afb78ad7f13, 0x05429d0e3e918f52, }, B: fp{ 0x534dffffffc4aae6, 0x5397ff174c67ffcf, 0xbff273eb870b251d, 0xdaf2827152870915, 0x393a9cbaca9e2dc3, 0x14be74dbfaee5748, }, }, { A: fp{ 0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493, }, B: fp{ 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x0000000000000000, }, }, } // 1 / ((u+1) ^ ((q-1)/3)) psiCoeffX = fp2{ A: fp{}, B: fp{ 0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a, }, } // 1 / ((u+1) ^ (p-1)/2) psiCoeffY = fp2{ A: fp{ 0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0x0bd592fc7d825ec8, }, B: fp{ 0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0x0e2b7eedbbfd87d2, }, } // 1 / 2 ^ ((q-1)/3) psi2CoeffX = fp2{ A: fp{ 0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x03f97d6e83d050d2, 0x18f0206554638741, }, B: fp{}, } ) // G2 is a point in g2 type G2 struct { x, y, z fp2 } // Random creates a random point on the curve // from the specified reader func (g2 *G2) Random(reader io.Reader) (*G2, error) { var seed [native.WideFieldBytes]byte n, err := reader.Read(seed[:]) if err != nil { return nil, errors.Wrap(err, "random could not read from stream") } if n != native.WideFieldBytes { return nil, fmt.Errorf("insufficient bytes read %d when %d are needed", n, WideFieldBytes) } dst := []byte("BLS12381G2_XMD:SHA-256_SSWU_RO_") return g2.Hash(native.EllipticPointHasherSha256(), seed[:], dst), nil } // Hash uses the hasher to map bytes to a valid point func (g2 *G2) Hash(hash *native.EllipticPointHasher, msg, dst []byte) *G2 { var u []byte var u0, u1 fp2 var r0, r1, q0, q1 G2 switch hash.Type() { case native.XMD: u = native.ExpandMsgXmd(hash, msg, dst, 256) case native.XOF: u = native.ExpandMsgXof(hash, msg, dst, 256) } var buf [96]byte copy(buf[:64], internal.ReverseScalarBytes(u[:64])) u0.A.SetBytesWide(&buf) copy(buf[:64], internal.ReverseScalarBytes(u[64:128])) u0.B.SetBytesWide(&buf) copy(buf[:64], internal.ReverseScalarBytes(u[128:192])) u1.A.SetBytesWide(&buf) copy(buf[:64], internal.ReverseScalarBytes(u[192:])) u1.B.SetBytesWide(&buf) r0.sswu(&u0) r1.sswu(&u1) q0.isogenyMap(&r0) q1.isogenyMap(&r1) g2.Add(&q0, &q1) return g2.ClearCofactor(g2) } // Identity returns the identity point func (g2 *G2) Identity() *G2 { g2.x.SetZero() g2.y.SetOne() g2.z.SetZero() return g2 } // Generator returns the base point func (g2 *G2) Generator() *G2 { g2.x.Set(&g2x) g2.y.Set(&g2y) g2.z.SetOne() return g2 } // IsIdentity returns true if this point is at infinity func (g2 *G2) IsIdentity() int { return g2.z.IsZero() } // IsOnCurve determines if this point represents a valid curve point func (g2 *G2) IsOnCurve() int { // Y^2 Z = X^3 + b Z^3 var lhs, rhs, t fp2 lhs.Square(&g2.y) lhs.Mul(&lhs, &g2.z) rhs.Square(&g2.x) rhs.Mul(&rhs, &g2.x) t.Square(&g2.z) t.Mul(&t, &g2.z) t.Mul(&t, &curveG2B) rhs.Add(&rhs, &t) return lhs.Equal(&rhs) } // InCorrectSubgroup returns 1 if the point is torsion free, 0 otherwise func (g2 *G2) InCorrectSubgroup() int { var t G2 t.multiply(g2, &fqModulusBytes) return t.IsIdentity() } // Add adds this point to another point. func (g2 *G2) Add(arg1, arg2 *G2) *G2 { // Algorithm 7, https://eprint.iacr.org/2015/1060.pdf var t0, t1, t2, t3, t4, x3, y3, z3 fp2 t0.Mul(&arg1.x, &arg2.x) t1.Mul(&arg1.y, &arg2.y) t2.Mul(&arg1.z, &arg2.z) t3.Add(&arg1.x, &arg1.y) t4.Add(&arg2.x, &arg2.y) t3.Mul(&t3, &t4) t4.Add(&t0, &t1) t3.Sub(&t3, &t4) t4.Add(&arg1.y, &arg1.z) x3.Add(&arg2.y, &arg2.z) t4.Mul(&t4, &x3) x3.Add(&t1, &t2) t4.Sub(&t4, &x3) x3.Add(&arg1.x, &arg1.z) y3.Add(&arg2.x, &arg2.z) x3.Mul(&x3, &y3) y3.Add(&t0, &t2) y3.Sub(&x3, &y3) x3.Double(&t0) t0.Add(&t0, &x3) t2.MulBy3b(&t2) z3.Add(&t1, &t2) t1.Sub(&t1, &t2) y3.MulBy3b(&y3) x3.Mul(&t4, &y3) t2.Mul(&t3, &t1) x3.Sub(&t2, &x3) y3.Mul(&y3, &t0) t1.Mul(&t1, &z3) y3.Add(&t1, &y3) t0.Mul(&t0, &t3) z3.Mul(&z3, &t4) z3.Add(&z3, &t0) g2.x.Set(&x3) g2.y.Set(&y3) g2.z.Set(&z3) return g2 } // Sub subtracts the two points func (g2 *G2) Sub(arg1, arg2 *G2) *G2 { var t G2 t.Neg(arg2) return g2.Add(arg1, &t) } // Double this point func (g2 *G2) Double(a *G2) *G2 { // Algorithm 9, https://eprint.iacr.org/2015/1060.pdf var t0, t1, t2, x3, y3, z3 fp2 t0.Square(&a.y) z3.Double(&t0) z3.Double(&z3) z3.Double(&z3) t1.Mul(&a.y, &a.z) t2.Square(&a.z) t2.MulBy3b(&t2) x3.Mul(&t2, &z3) y3.Add(&t0, &t2) z3.Mul(&t1, &z3) t1.Double(&t2) t2.Add(&t2, &t1) t0.Sub(&t0, &t2) y3.Mul(&t0, &y3) y3.Add(&y3, &x3) t1.Mul(&a.x, &a.y) x3.Mul(&t0, &t1) x3.Double(&x3) e := a.IsIdentity() g2.x.CMove(&x3, t0.SetZero(), e) g2.z.CMove(&z3, &t0, e) g2.y.CMove(&y3, t0.SetOne(), e) return g2 } // Mul multiplies this point by the input scalar func (g2 *G2) Mul(a *G2, s *native.Field) *G2 { bytes := s.Bytes() return g2.multiply(a, &bytes) } func (g2 *G2) multiply(a *G2, bytes *[native.FieldBytes]byte) *G2 { var p G2 precomputed := [16]*G2{} precomputed[0] = new(G2).Identity() precomputed[1] = new(G2).Set(a) for i := 2; i < 16; i += 2 { precomputed[i] = new(G2).Double(precomputed[i>>1]) precomputed[i+1] = new(G2).Add(precomputed[i], a) } p.Identity() for i := 0; i < 256; i += 4 { // Brouwer / windowing method. window size of 4. for j := 0; j < 4; j++ { p.Double(&p) } window := bytes[32-1-i>>3] >> (4 - i&0x04) & 0x0F p.Add(&p, precomputed[window]) } return g2.Set(&p) } // MulByX multiplies by BLS X using double and add func (g2 *G2) MulByX(a *G2) *G2 { // Skip first bit since its always zero var s, t, r G2 r.Identity() t.Set(a) for x := paramX >> 1; x != 0; x >>= 1 { t.Double(&t) s.Add(&r, &t) r.CMove(&r, &s, int(x&1)) } // Since BLS_X is negative, flip the sign return g2.Neg(&r) } // ClearCofactor using [Budroni-Pintore](https://ia.cr/2017/419). // This is equivalent to multiplying by h_{eff} = 3(z^2 - 1) * h_2 // where h_2 is the cofactor of G_2 and z is the parameter of BLS12-381. func (g2 *G2) ClearCofactor(a *G2) *G2 { var t1, t2, t3, pt G2 t1.MulByX(a) t2.psi(a) pt.Double(a) pt.psi2(&pt) t3.Add(&t1, &t2) t3.MulByX(&t3) pt.Add(&pt, &t3) pt.Sub(&pt, &t1) pt.Sub(&pt, &t2) pt.Sub(&pt, a) return g2.Set(&pt) } // Neg negates this point func (g2 *G2) Neg(a *G2) *G2 { g2.Set(a) g2.y.CNeg(&a.y, -(a.IsIdentity() - 1)) return g2 } // Set copies a into g2 func (g2 *G2) Set(a *G2) *G2 { g2.x.Set(&a.x) g2.y.Set(&a.y) g2.z.Set(&a.z) return g2 } // BigInt returns the x and y as big.Ints in affine func (g2 *G2) BigInt() (x, y *big.Int) { var t G2 out := t.ToUncompressed() x = new(big.Int).SetBytes(out[:WideFieldBytes]) y = new(big.Int).SetBytes(out[WideFieldBytes:]) return } // SetBigInt creates a point from affine x, y // and returns the point if it is on the curve func (g2 *G2) SetBigInt(x, y *big.Int) (*G2, error) { var tt [DoubleWideFieldBytes]byte if len(x.Bytes()) == 0 && len(y.Bytes()) == 0 { return g2.Identity(), nil } x.FillBytes(tt[:WideFieldBytes]) y.FillBytes(tt[WideFieldBytes:]) return g2.FromUncompressed(&tt) } // ToCompressed serializes this element into compressed form. func (g2 *G2) ToCompressed() [WideFieldBytes]byte { var out [WideFieldBytes]byte var t G2 t.ToAffine(g2) xABytes := t.x.A.Bytes() xBBytes := t.x.B.Bytes() copy(out[:FieldBytes], internal.ReverseScalarBytes(xBBytes[:])) copy(out[FieldBytes:], internal.ReverseScalarBytes(xABytes[:])) isInfinity := byte(g2.IsIdentity()) // Compressed flag out[0] |= 1 << 7 // Is infinity out[0] |= (1 << 6) & -isInfinity // Sign of y only set if not infinity out[0] |= (byte(t.y.LexicographicallyLargest()) << 5) & (isInfinity - 1) return out } // FromCompressed deserializes this element from compressed form. func (g2 *G2) FromCompressed(input *[WideFieldBytes]byte) (*G2, error) { var xFp, yFp fp2 var xA, xB [FieldBytes]byte var p G2 compressedFlag := int((input[0] >> 7) & 1) infinityFlag := int((input[0] >> 6) & 1) sortFlag := int((input[0] >> 5) & 1) if compressedFlag != 1 { return nil, errors.New("compressed flag must be set") } if infinityFlag == 1 { return g2.Identity(), nil } copy(xB[:], internal.ReverseScalarBytes(input[:FieldBytes])) copy(xA[:], internal.ReverseScalarBytes(input[FieldBytes:])) // Mask away the flag bits xB[FieldBytes-1] &= 0x1F _, validA := xFp.A.SetBytes(&xA) _, validB := xFp.B.SetBytes(&xB) if validA&validB != 1 { return nil, errors.New("invalid bytes - not in field") } // Recover a y-coordinate given x by y = sqrt(x^3 + 4) yFp.Square(&xFp) yFp.Mul(&yFp, &xFp) yFp.Add(&yFp, &curveG2B) _, wasSquare := yFp.Sqrt(&yFp) if wasSquare != 1 { return nil, errors.New("point is not on the curve") } yFp.CNeg(&yFp, yFp.LexicographicallyLargest()^sortFlag) p.x.Set(&xFp) p.y.Set(&yFp) p.z.SetOne() if p.InCorrectSubgroup() == 0 { return nil, errors.New("point is not in correct subgroup") } return g2.Set(&p), nil } // ToUncompressed serializes this element into uncompressed form. func (g2 *G2) ToUncompressed() [DoubleWideFieldBytes]byte { var out [DoubleWideFieldBytes]byte var t G2 t.ToAffine(g2) bytes := t.x.B.Bytes() copy(out[:FieldBytes], internal.ReverseScalarBytes(bytes[:])) bytes = t.x.A.Bytes() copy(out[FieldBytes:WideFieldBytes], internal.ReverseScalarBytes(bytes[:])) bytes = t.y.B.Bytes() copy(out[WideFieldBytes:WideFieldBytes+FieldBytes], internal.ReverseScalarBytes(bytes[:])) bytes = t.y.A.Bytes() copy(out[WideFieldBytes+FieldBytes:], internal.ReverseScalarBytes(bytes[:])) isInfinity := byte(g2.IsIdentity()) out[0] |= (1 << 6) & -isInfinity return out } // FromUncompressed deserializes this element from uncompressed form. func (g2 *G2) FromUncompressed(input *[DoubleWideFieldBytes]byte) (*G2, error) { var a, b fp var t [FieldBytes]byte var p G2 infinityFlag := int((input[0] >> 6) & 1) if infinityFlag == 1 { return g2.Identity(), nil } copy(t[:], internal.ReverseScalarBytes(input[:FieldBytes])) // Mask away top bits t[FieldBytes-1] &= 0x1F _, valid := b.SetBytes(&t) if valid == 0 { return nil, errors.New("invalid bytes - x.B not in field") } copy(t[:], internal.ReverseScalarBytes(input[FieldBytes:WideFieldBytes])) _, valid = a.SetBytes(&t) if valid == 0 { return nil, errors.New("invalid bytes - x.A not in field") } p.x.B.Set(&b) p.x.A.Set(&a) copy(t[:], internal.ReverseScalarBytes(input[WideFieldBytes:WideFieldBytes+FieldBytes])) _, valid = b.SetBytes(&t) if valid == 0 { return nil, errors.New("invalid bytes - y.B not in field") } copy(t[:], internal.ReverseScalarBytes(input[FieldBytes+WideFieldBytes:])) _, valid = a.SetBytes(&t) if valid == 0 { return nil, errors.New("invalid bytes - y.A not in field") } p.y.B.Set(&b) p.y.A.Set(&a) p.z.SetOne() if p.IsOnCurve() == 0 { return nil, errors.New("point is not on the curve") } if p.InCorrectSubgroup() == 0 { return nil, errors.New("point is not in correct subgroup") } return g2.Set(&p), nil } // ToAffine converts the point into affine coordinates func (g2 *G2) ToAffine(a *G2) *G2 { var wasInverted int var zero, x, y, z fp2 _, wasInverted = z.Invert(&a.z) x.Mul(&a.x, &z) y.Mul(&a.y, &z) g2.x.CMove(&zero, &x, wasInverted) g2.y.CMove(&zero, &y, wasInverted) g2.z.CMove(&zero, z.SetOne(), wasInverted) return g2 } // GetX returns the affine X coordinate func (g2 *G2) GetX() *fp2 { var t G2 t.ToAffine(g2) return &t.x } // GetY returns the affine Y coordinate func (g2 *G2) GetY() *fp2 { var t G2 t.ToAffine(g2) return &t.y } // Equal returns 1 if the two points are equal 0 otherwise. func (g2 *G2) Equal(rhs *G2) int { var x1, x2, y1, y2 fp2 var e1, e2 int // This technique avoids inversions x1.Mul(&g2.x, &rhs.z) x2.Mul(&rhs.x, &g2.z) y1.Mul(&g2.y, &rhs.z) y2.Mul(&rhs.y, &g2.z) e1 = g2.z.IsZero() e2 = rhs.z.IsZero() // Both at infinity or coordinates are the same return (e1 & e2) | (^e1 & ^e2)&x1.Equal(&x2)&y1.Equal(&y2) } // CMove sets g2 = arg1 if choice == 0 and g2 = arg2 if choice == 1 func (g2 *G2) CMove(arg1, arg2 *G2, choice int) *G2 { g2.x.CMove(&arg1.x, &arg2.x, choice) g2.y.CMove(&arg1.y, &arg2.y, choice) g2.z.CMove(&arg1.z, &arg2.z, choice) return g2 } // SumOfProducts computes the multi-exponentiation for the specified // points and scalars and stores the result in `g2`. // Returns an error if the lengths of the arguments is not equal. func (g2 *G2) SumOfProducts(points []*G2, scalars []*native.Field) (*G2, error) { const Upper = 256 const W = 4 const Windows = Upper / W // careful--use ceiling division in case this doesn't divide evenly var sum G2 if len(points) != len(scalars) { return nil, fmt.Errorf("length mismatch") } bucketSize := 1 << W windows := make([]G2, Windows) bytes := make([][32]byte, len(scalars)) buckets := make([]G2, bucketSize) for i := 0; i < len(windows); i++ { windows[i].Identity() } for i, scalar := range scalars { bytes[i] = scalar.Bytes() } for j := 0; j < len(windows); j++ { for i := 0; i < bucketSize; i++ { buckets[i].Identity() } for i := 0; i < len(scalars); i++ { // j*W to get the nibble // >> 3 to convert to byte, / 8 // (W * j & W) gets the nibble, mod W // 1 << W - 1 to get the offset index := bytes[i][j*W>>3] >> (W * j & W) & (1< 0; i-- { sum.Add(&sum, &buckets[i]) windows[j].Add(&windows[j], &sum) } } g2.Identity() for i := len(windows) - 1; i >= 0; i-- { for j := 0; j < W; j++ { g2.Double(g2) } g2.Add(g2, &windows[i]) } return g2, nil } func (g2 *G2) psi(a *G2) *G2 { g2.x.FrobeniusMap(&a.x) g2.y.FrobeniusMap(&a.y) // z = frobenius(z) g2.z.FrobeniusMap(&a.z) // x = frobenius(x)/((u+1)^((p-1)/3)) g2.x.Mul(&g2.x, &psiCoeffX) // y = frobenius(y)/(u+1)^((p-1)/2) g2.y.Mul(&g2.y, &psiCoeffY) return g2 } func (g2 *G2) psi2(a *G2) *G2 { // x = frobenius^2(x)/2^((p-1)/3); note that q^2 is the order of the field. g2.x.Mul(&a.x, &psi2CoeffX) // y = -frobenius^2(y); note that q^2 is the order of the field. g2.y.Neg(&a.y) g2.z.Set(&a.z) return g2 } func (g2 *G2) sswu(u *fp2) *G2 { /// simplified swu map for q = 9 mod 16 where AB == 0 // var tv1, tv2, x1, x2, gx1, gx2, x, y, y2, t fp2 tv1.Square(u) tv1.Mul(&tv1, &sswuMapZ) tv2.Square(&tv1) x1.Add(&tv1, &tv2) x1.Invert(&x1) x1.Add(&x1, (&fp2{}).SetOne()) x1.CMove(&x1, &sswuMapZInv, x1.IsZero()) x1.Mul(&x1, &swwuMapMbDivA) gx1.Square(&x1) gx1.Add(&gx1, &sswuMapA) gx1.Mul(&gx1, &x1) gx1.Add(&gx1, &sswuMapB) x2.Mul(&tv1, &x1) tv2.Mul(&tv2, &tv1) gx2.Mul(&gx1, &tv2) _, e2 := t.Sqrt(&gx1) x.CMove(&x2, &x1, e2) y2.CMove(&gx2, &gx1, e2) y.Sqrt(&y2) y.CNeg(&y, u.Sgn0()^y.Sgn0()) g2.x.Set(&x) g2.y.Set(&y) g2.z.SetOne() return g2 } func (g2 *G2) isogenyMap(a *G2) *G2 { const Degree = 4 var xs [Degree]fp2 xs[0].SetOne() xs[1].Set(&a.x) xs[2].Square(&a.x) for i := 3; i < Degree; i++ { xs[i].Mul(&xs[i-1], &a.x) } xNum := computeKFp2(xs[:], g2IsoXNum) xDen := computeKFp2(xs[:], g2IsoXDen) yNum := computeKFp2(xs[:], g2IsoYNum) yDen := computeKFp2(xs[:], g2IsoYDen) g2.x.Invert(&xDen) g2.x.Mul(&g2.x, &xNum) g2.y.Invert(&yDen) g2.y.Mul(&g2.y, &yNum) g2.y.Mul(&g2.y, &a.y) g2.z.SetOne() return g2 } func computeKFp2(xxs []fp2, k []fp2) fp2 { var xx, t fp2 for i := range k { xx.Add(&xx, t.Mul(&xxs[i], &k[i])) } return xx }