github.com/cloudflare/circl@v1.5.0/ecc/bls12381/g2.go (about)

     1  package bls12381
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/subtle"
     6  	"fmt"
     7  
     8  	"github.com/cloudflare/circl/ecc/bls12381/ff"
     9  	"github.com/cloudflare/circl/expander"
    10  )
    11  
    12  // G2Size is the length in bytes of an element in G2 in uncompressed form..
    13  const G2Size = 2 * ff.Fp2Size
    14  
    15  // G2SizeCompressed is the length in bytes of an element in G2 in compressed form.
    16  const G2SizeCompressed = ff.Fp2Size
    17  
    18  // G2 is a point in the twist of the BLS12 curve over Fp2.
    19  type G2 struct{ x, y, z ff.Fp2 }
    20  
    21  func (g G2) String() string { return fmt.Sprintf("x: %v\ny: %v\nz: %v", g.x, g.y, g.z) }
    22  
    23  // Bytes serializes a G2 element in uncompressed form.
    24  func (g G2) Bytes() []byte { return g.encodeBytes(false) }
    25  
    26  // Bytes serializes a G2 element in compressed form.
    27  func (g G2) BytesCompressed() []byte { return g.encodeBytes(true) }
    28  
    29  // SetBytes sets g to the value in bytes, and returns a non-nil error if not in G2.
    30  func (g *G2) SetBytes(b []byte) error {
    31  	if len(b) < G2SizeCompressed {
    32  		return errInputLength
    33  	}
    34  
    35  	// Check for invalid prefixes
    36  	switch b[0] & 0xE0 {
    37  	case 0x20, 0x60, 0xE0:
    38  		return errEncoding
    39  	}
    40  
    41  	isCompressed := int((b[0] >> 7) & 0x1)
    42  	isInfinity := int((b[0] >> 6) & 0x1)
    43  	isBigYCoord := int((b[0] >> 5) & 0x1)
    44  
    45  	if isInfinity == 1 {
    46  		l := G2Size
    47  		if isCompressed == 1 {
    48  			l = G2SizeCompressed
    49  		}
    50  		zeros := make([]byte, l-1)
    51  		if (b[0]&0x1F) != 0 || subtle.ConstantTimeCompare(b[1:l], zeros) != 1 {
    52  			return errEncoding
    53  		}
    54  		g.SetIdentity()
    55  		return nil
    56  	}
    57  
    58  	x := (&[ff.Fp2Size]byte{})[:]
    59  	copy(x, b)
    60  	x[0] &= 0x1F
    61  	if err := g.x.UnmarshalBinary(x); err != nil {
    62  		return err
    63  	}
    64  
    65  	if isCompressed == 1 {
    66  		x3b := &ff.Fp2{}
    67  		x3b.Sqr(&g.x)
    68  		x3b.Mul(x3b, &g.x)
    69  		x3b.Add(x3b, &g2Params.b)
    70  		if g.y.Sqrt(x3b) == 0 {
    71  			return errEncoding
    72  		}
    73  		if g.y.IsNegative() != isBigYCoord {
    74  			g.y.Neg()
    75  		}
    76  	} else {
    77  		if len(b) < G2Size {
    78  			return errInputLength
    79  		}
    80  		if err := g.y.UnmarshalBinary(b[ff.Fp2Size:G2Size]); err != nil {
    81  			return err
    82  		}
    83  	}
    84  
    85  	g.z.SetOne()
    86  	if !g.IsOnG2() {
    87  		return errEncoding
    88  	}
    89  	return nil
    90  }
    91  
    92  func (g G2) encodeBytes(compressed bool) []byte {
    93  	g.toAffine()
    94  	var isCompressed, isInfinity, isBigYCoord byte
    95  	if compressed {
    96  		isCompressed = 1
    97  	}
    98  	if g.z.IsZero() == 1 {
    99  		isInfinity = 1
   100  	}
   101  	if isCompressed == 1 && isInfinity == 0 {
   102  		isBigYCoord = byte(g.y.IsNegative())
   103  	}
   104  
   105  	bytes, _ := g.x.MarshalBinary()
   106  	if isCompressed == 0 {
   107  		yBytes, _ := g.y.MarshalBinary()
   108  		bytes = append(bytes, yBytes...)
   109  	}
   110  	if isInfinity == 1 {
   111  		l := len(bytes)
   112  		for i := 0; i < l; i++ {
   113  			bytes[i] = 0
   114  		}
   115  	}
   116  
   117  	bytes[0] = bytes[0]&0x1F | headerEncoding(isCompressed, isInfinity, isBigYCoord)
   118  
   119  	return bytes
   120  }
   121  
   122  // Neg inverts g.
   123  func (g *G2) Neg() { g.y.Neg() }
   124  
   125  // SetIdentity assigns g to the identity element.
   126  func (g *G2) SetIdentity() { g.x = ff.Fp2{}; g.y.SetOne(); g.z = ff.Fp2{} }
   127  
   128  // isValidProjective returns true if the point is not a projective point.
   129  func (g *G2) isValidProjective() bool { return (g.x.IsZero() & g.y.IsZero() & g.z.IsZero()) != 1 }
   130  
   131  // IsOnG2 returns true if the point is in the group G2.
   132  func (g *G2) IsOnG2() bool { return g.isValidProjective() && g.isOnCurve() && g.isRTorsion() }
   133  
   134  // IsIdentity return true if the point is the identity of G2.
   135  func (g *G2) IsIdentity() bool { return g.isValidProjective() && (g.z.IsZero() == 1) }
   136  
   137  // cmov sets g to P if b == 1
   138  func (g *G2) cmov(P *G2, b int) {
   139  	(&g.x).CMov(&g.x, &P.x, b)
   140  	(&g.y).CMov(&g.y, &P.y, b)
   141  	(&g.z).CMov(&g.z, &P.z, b)
   142  }
   143  
   144  // isRTorsion returns true if point is in the r-torsion subgroup.
   145  func (g *G2) isRTorsion() bool {
   146  	// Bowe, "Faster Subgroup Checks for BLS12-381" (https://eprint.iacr.org/2019/814)
   147  	_z := bls12381.minusZ[:]
   148  	Q := *g
   149  	Q.psi()                   // Q = \psi(g)
   150  	Q.scalarMultShort(_z, &Q) // Q = -[z]\psi(g)
   151  	Q.Add(&Q, g)              // Q = -[z]\psi(g)+g
   152  	Q.psi()                   // Q = -[z]\psi^2(g)+\psi(g)
   153  	Q.psi()                   // Q = -[z]\psi^3(g)+\psi^2(g)
   154  
   155  	return Q.IsEqual(g) // Equivalent to verification equation in paper
   156  }
   157  
   158  // psi is the Galbraith-Scott endomorphism. See https://eprint.iacr.org/2008/117.
   159  func (g *G2) psi() {
   160  	g.x.Frob(&g.x)
   161  	g.y.Frob(&g.y)
   162  	g.z.Frob(&g.z)
   163  	g.x.Mul(&g2Psi.alpha, &g.x)
   164  	g.y.Mul(&g2Psi.beta, &g.y)
   165  }
   166  
   167  // clearCofactor maps g to a point in the r-torsion subgroup.
   168  //
   169  // This method multiplies g times a multiple of the cofactor as proposed by
   170  // Fuentes-Knapp-Rodríguez at https://doi.org/10.1007/978-3-642-28496-0_25.
   171  //
   172  // The explicit formulas for BLS curves are in Section 4.1 of Budroni-Pintore
   173  // "Efficient hash maps to G2 on BLS curves" at https://eprint.iacr.org/2017/419
   174  //
   175  //	h(a)P = [x^2-x-1]P + [x-1]ψ(P) + ψ^2(2P)
   176  func (g *G2) clearCofactor() {
   177  	x := bls12381.minusZ[:]
   178  	xP, psiP := &G2{}, &G2{}
   179  	_2P := *g
   180  
   181  	_2P.Double()              // 2P
   182  	_2P.psi()                 // ψ(2P)
   183  	_2P.psi()                 // ψ^2(2P)
   184  	xP.scalarMultShort(x, g)  // -xP
   185  	xP.Add(xP, g)             // -xP + P = [-x+1]P
   186  	*psiP = *xP               //
   187  	psiP.psi()                // ψ(-xP + P) = [-x+1]ψ(P)
   188  	xP.scalarMultShort(x, xP) // x^2P - xP = [x^2-x]P
   189  	g.Add(g, psiP)            // P + [-x+1]ψ(P)
   190  	g.Neg()                   // -P + [x-1]ψ(P)
   191  	g.Add(g, xP)              // [x^2-x-1]P + [x-1]ψ(P)
   192  	g.Add(g, &_2P)            // [x^2-x-1]P + [x-1]ψ(P) + 2ψ^2(P)
   193  }
   194  
   195  // Double updates g = 2g.
   196  func (g *G2) Double() { doubleAndLine(g, nil) }
   197  
   198  // Add updates g=P+Q.
   199  func (g *G2) Add(P, Q *G2) { addAndLine(g, P, Q, nil) }
   200  
   201  // ScalarMult calculates g = kP.
   202  func (g *G2) ScalarMult(k *Scalar, P *G2) { b, _ := k.MarshalBinary(); g.scalarMult(b, P) }
   203  
   204  // scalarMult calculates g = kP, where k is the scalar in big-endian order.
   205  func (g *G2) scalarMult(k []byte, P *G2) {
   206  	var Q G2
   207  	Q.SetIdentity()
   208  	T := &G2{}
   209  	var mults [16]G2
   210  	mults[0].SetIdentity()
   211  	mults[1] = *P
   212  	for i := 1; i < 8; i++ {
   213  		mults[2*i] = mults[i]
   214  		mults[2*i].Double()
   215  		mults[2*i+1].Add(&mults[2*i], P)
   216  	}
   217  	N := 8 * len(k)
   218  	for i := 0; i < N; i += 4 {
   219  		Q.Double()
   220  		Q.Double()
   221  		Q.Double()
   222  		Q.Double()
   223  		idx := 0xf & (k[i/8] >> uint(4-i%8))
   224  		for j := 0; j < 16; j++ {
   225  			T.cmov(&mults[j], subtle.ConstantTimeByteEq(idx, uint8(j)))
   226  		}
   227  		Q.Add(&Q, T)
   228  	}
   229  	*g = Q
   230  }
   231  
   232  // scalarMultShort multiplies by a short, constant scalar k, where k is the
   233  // scalar in big-endian order. Runtime depends on the scalar.
   234  func (g *G2) scalarMultShort(k []byte, P *G2) {
   235  	// Since the scalar is short and low Hamming weight not much helps.
   236  	var Q G2
   237  	Q.SetIdentity()
   238  	N := 8 * len(k)
   239  	for i := 0; i < N; i++ {
   240  		Q.Double()
   241  		bit := 0x1 & (k[i/8] >> uint(7-i%8))
   242  		if bit != 0 {
   243  			Q.Add(&Q, P)
   244  		}
   245  	}
   246  	*g = Q
   247  }
   248  
   249  // IsEqual returns true if g and p are equivalent.
   250  func (g *G2) IsEqual(p *G2) bool {
   251  	var lx, rx, ly, ry ff.Fp2
   252  	lx.Mul(&g.x, &p.z) // lx = x1*z2
   253  	rx.Mul(&p.x, &g.z) // rx = x2*z1
   254  	lx.Sub(&lx, &rx)   // lx = lx-rx
   255  	ly.Mul(&g.y, &p.z) // ly = y1*z2
   256  	ry.Mul(&p.y, &g.z) // ry = y2*z1
   257  	ly.Sub(&ly, &ry)   // ly = ly-ry
   258  	return lx.IsZero() == 1 && ly.IsZero() == 1
   259  }
   260  
   261  // EncodeToCurve is a non-uniform encoding from an input byte string (and
   262  // an optional domain separation tag) to elements in G2. This function must not
   263  // be used as a hash function, otherwise use G2.Hash instead.
   264  func (g *G2) Encode(input, dst []byte) {
   265  	const L = 64
   266  	pseudo := expander.NewExpanderMD(crypto.SHA256, dst).Expand(input, 2*L)
   267  
   268  	var u ff.Fp2
   269  	u[0].SetBytes(pseudo[0*L : 1*L])
   270  	u[1].SetBytes(pseudo[1*L : 2*L])
   271  
   272  	var q isogG2Point
   273  	q.sswu(&u)
   274  	g.evalIsogG2(&q)
   275  	g.clearCofactor()
   276  }
   277  
   278  // Hash produces an element of G2 from the hash of an input byte string and
   279  // an optional domain separation tag. This function is safe to use when a
   280  // random oracle returning points in G2 be required.
   281  func (g *G2) Hash(input, dst []byte) {
   282  	const L = 64
   283  	pseudo := expander.NewExpanderMD(crypto.SHA256, dst).Expand(input, 4*L)
   284  
   285  	var u0, u1 ff.Fp2
   286  	u0[0].SetBytes(pseudo[0*L : 1*L])
   287  	u0[1].SetBytes(pseudo[1*L : 2*L])
   288  	u1[0].SetBytes(pseudo[2*L : 3*L])
   289  	u1[1].SetBytes(pseudo[3*L : 4*L])
   290  
   291  	var q0, q1 isogG2Point
   292  	q0.sswu(&u0)
   293  	q1.sswu(&u1)
   294  	var p0, p1 G2
   295  	p0.evalIsogG2(&q0)
   296  	p1.evalIsogG2(&q1)
   297  	g.Add(&p0, &p1)
   298  	g.clearCofactor()
   299  }
   300  
   301  // isOnCurve returns true if g is a valid point on the curve.
   302  func (g *G2) isOnCurve() bool {
   303  	var x3, z3, y2 ff.Fp2
   304  	y2.Sqr(&g.y)             // y2 = y^2
   305  	y2.Mul(&y2, &g.z)        // y2 = y^2*z
   306  	x3.Sqr(&g.x)             // x3 = x^2
   307  	x3.Mul(&x3, &g.x)        // x3 = x^3
   308  	z3.Sqr(&g.z)             // z3 = z^2
   309  	z3.Mul(&z3, &g.z)        // z3 = z^3
   310  	z3.Mul(&z3, &g2Params.b) // z3 = (4+4i)*z^3
   311  	x3.Add(&x3, &z3)         // x3 = x^3 + (4+4i)*z^3
   312  	y2.Sub(&y2, &x3)         // y2 = y^2*z - (x^3 + (4+4i)*z^3)
   313  	return y2.IsZero() == 1
   314  }
   315  
   316  // toAffine updates g with its affine representation.
   317  func (g *G2) toAffine() {
   318  	if g.z.IsZero() != 1 {
   319  		var invZ ff.Fp2
   320  		invZ.Inv(&g.z)
   321  		g.x.Mul(&g.x, &invZ)
   322  		g.y.Mul(&g.y, &invZ)
   323  		g.z.SetOne()
   324  	}
   325  }
   326  
   327  // G2Generator returns the generator point of G2.
   328  func G2Generator() *G2 {
   329  	var G G2
   330  	G.x = g2Params.genX
   331  	G.y = g2Params.genY
   332  	G.z.SetOne()
   333  	return &G
   334  }