Prad Nukala 807b2e86ec
feature/1220 origin handle exists method (#1241)
* feat: add docs and CI workflow for publishing to onsonr.dev

* (refactor): Move hway,motr executables to their own repos

* feat: simplify devnet and testnet configurations

* refactor: update import path for didcrypto package

* docs(networks): Add README with project overview, architecture, and community links

* refactor: Move network configurations to deploy directory

* build: update golang version to 1.23

* refactor: move logger interface to appropriate package

* refactor: Move devnet configuration to networks/devnet

* chore: improve release process with date variable

* (chore): Move Crypto Library

* refactor: improve code structure and readability in DID module

* feat: integrate Trunk CI checks

* ci: optimize CI workflow by removing redundant build jobs

---------

Co-authored-by: Darp Alakun <i@prad.nu>
2025-01-06 17:06:10 +00:00

1123 lines
23 KiB
Go
Executable File

package bls12381
import (
"fmt"
"io"
"math/big"
"github.com/pkg/errors"
"github.com/onsonr/sonr/crypto/core/curves/native"
"github.com/onsonr/sonr/crypto/internal"
)
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{
0o00000000000000000,
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<<W - 1) // little-endian
buckets[index].Add(&buckets[index], points[i])
}
sum.Identity()
for i := bucketSize - 1; i > 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
// <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-11.html>
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
}