2021-02-22 17:48:14 +00:00

267 lines
8.1 KiB
Go

// Package bn256 implements a particular bilinear group at the 128-bit security
// level.
//
// Bilinear groups are the basis of many of the new cryptographic protocols that
// have been proposed over the past decade. They consist of a triplet of groups
// (G₁, G₂ and GT) such that there exists a function e(g₁ˣ,g₂ʸ)=gTˣʸ (where gₓ
// is a generator of the respective group). That function is called a pairing
// function.
//
// This package specifically implements the Optimal Ate pairing over a 256-bit
// Barreto-Naehrig curve as described in
// http://cryptojedi.org/papers/dclxvi-20100714.pdf. Its output is compatible
// with the implementation described in that paper.
package bn256
import (
"errors"
)
// This file implement some util functions for the MPC
// especially the serialization and deserialization functions for points in G1
// Constants related to the bn256 pairing friendly curve
const (
Fq2ElementSize = 2 * FqElementSize
G2CompressedSize = Fq2ElementSize + 1 // + 1 accounts for the additional byte used for masking
G2UncompressedSize = 2*Fq2ElementSize + 1 // + 1 accounts for the additional byte used for masking
)
// EncodeUncompressed converts the compressed point e into bytes
// Take a point P in Jacobian form (where each coordinate is MontEncoded)
// and encodes it by going back to affine coordinates and montDecode all coordinates
// This function does not modify the point e
// (the variable `temp` is introduced to avoid to modify e)
func (e *G2) EncodeUncompressed() []byte {
// Check nil pointers
if e.p == nil {
e.p = &twistPoint{}
}
// Set the right flags
ret := make([]byte, G2UncompressedSize)
if e.p.IsInfinity() {
// Flag the encoding with the infinity flag
ret[0] |= serializationInfinity
return ret
}
// Marshal
marshal := e.Marshal()
// The encoding = flags || marshalledPoint
copy(ret[1:], marshal)
return ret
}
// DecodeUncompressed decodes a point in the uncompressed form
// Take a point P encoded (ie: written in affine form where each coordinate is MontDecoded)
// and encodes it by going back to Jacobian coordinates and montEncode all coordinates
func (e *G2) DecodeUncompressed(encoding []byte) error {
if len(encoding) != G2UncompressedSize {
return errors.New("wrong encoded point size")
}
if encoding[0]&serializationCompressed != 0 { // Also test the length of the encoding to make sure it is 65bytes
return errors.New("point is compressed")
}
if encoding[0]&serializationBigY != 0 { // Also test that the bigY flag if not set
return errors.New("bigY flag should not be set")
}
// Unmarshal the points and check their caps
if e.p == nil {
e.p = &twistPoint{}
}
// Removes the bits of the masking (This does a bitwise AND with `0001 1111`)
// And thus removes the first 3 bits corresponding to the masking
// Useless for now because in bn256, we added a full byte to enable masking
// However, this is needed if we work over BLS12 and its underlying field
bin := make([]byte, G2UncompressedSize)
copy(bin, encoding)
bin[0] &= serializationMask
// Decode the point at infinity in the compressed form
if encoding[0]&serializationInfinity != 0 {
// Makes sense to check that all bytes of bin are 0x0 since we removed the masking above}
for i := range bin {
if bin[i] != 0 {
return errors.New("invalid infinity encoding")
}
}
e.p.SetInfinity()
return nil
}
// We remove the flags and unmarshal the data
_, err := e.Unmarshal(encoding[1:])
return err
}
func (e *G2) IsHigherY() bool {
// Check nil pointers
if e.p == nil {
e.p = &twistPoint{}
e.p.MakeAffine()
}
// Note: the structures attributes are quite confusing here
// In fact, each element of Fp2 is a polynomial with 2 terms
// the `x` and `y` denote these coefficients, ie: xi + y
// However, `x` and `y` are also used to denote the x and y **coordinates**
// of an elliptic curve point. Hence, e.p.y represents the y-coordinate of the
// point e, and e.p.y.y represents the **coefficient** y of the y-coordinate
// of the elliptic curve point e.
//
// TODO: Rename the coefficients of the elements of Fp2 as c0 and c1 to clarify the code
yCoordY := &gfP{}
yCoordY.Set(&e.p.y.y)
yCoordYNeg := &gfP{}
gfpNeg(yCoordYNeg, yCoordY)
res := gfpCmp(yCoordY, yCoordYNeg)
if res == 1 { // yCoordY > yCoordNegY
return true
} else if res == -1 {
return false
}
return false
}
func (e *G2) EncodeCompressed() []byte {
// Check nil pointers
if e.p == nil {
e.p = &twistPoint{}
}
e.p.MakeAffine()
ret := make([]byte, G2CompressedSize)
// Flag the encoding with the compressed flag
ret[0] |= serializationCompressed
if e.p.IsInfinity() {
// Flag the encoding with the infinity flag
ret[0] |= serializationInfinity
return ret
}
if e.IsHigherY() {
// Flag the encoding with the bigY flag
ret[0] |= serializationBigY
}
// We start the serialization of the coordinates at the index 1
// Since the index 0 in the `ret` corresponds to the masking
//
// `temp` contains the the x-coordinate of the point
// Thus, to fully encode `temp`, we need to Marshal it's x coefficient and y coefficient
temp := gfP2Decode(&e.p.x)
temp.x.Marshal(ret[1:])
temp.y.Marshal(ret[FqElementSize+1:])
return ret
}
// Takes a MontEncoded x and finds the corresponding y (one of the two possible y's)
func getYFromMontEncodedXG2(x *gfP2) (*gfP2, error) {
// Check nil pointers
if x == nil {
return nil, errors.New("Cannot retrieve the y-coordinate from a nil pointer")
}
x2 := new(gfP2).Mul(x, x)
x3 := new(gfP2).Mul(x2, x)
rhs := new(gfP2).Add(x3, twistB) // twistB is MontEncoded, since it is create with newGFp
yCoord, err := rhs.Sqrt()
if err != nil {
return nil, err
}
return yCoord, nil
}
// DecodeCompressed decodes a point in the compressed form
// Take a point P in G2 decoded (ie: written in affine form where each coordinate is MontDecoded)
// and encodes it by going back to Jacobian coordinates and montEncode all coordinates
func (e *G2) DecodeCompressed(encoding []byte) error {
if len(encoding) != G2CompressedSize {
return errors.New("wrong encoded point size")
}
if encoding[0]&serializationCompressed == 0 { // Also test the length of the encoding to make sure it is 33bytes
return errors.New("point isn't compressed")
}
// Unmarshal the points and check their caps
if e.p == nil {
e.p = &twistPoint{}
} else {
e.p.x.SetZero()
e.p.y.SetZero()
e.p.z.SetOne()
e.p.t.SetOne()
}
// Removes the bits of the masking (This does a bitwise AND with `0001 1111`)
// And thus removes the first 3 bits corresponding to the masking
bin := make([]byte, G2CompressedSize)
copy(bin, encoding)
bin[0] &= serializationMask
// Decode the point at infinity in the compressed form
if encoding[0]&serializationInfinity != 0 {
if encoding[0]&serializationBigY != 0 {
return errors.New("high Y bit improperly set")
}
// Similar to `for i:=0; i<len(bin); i++ {}`
for i := range bin {
// Makes sense to check that all bytes of bin are 0x0 since we removed the masking above
if bin[i] != 0 {
return errors.New("invalid infinity encoding")
}
}
e.p.SetInfinity()
return nil
}
// Decompress the point P (P =/= ∞)
var err error
if err = e.p.x.x.Unmarshal(bin[1:]); err != nil {
return err
}
if err = e.p.x.y.Unmarshal(bin[FqElementSize+1:]); err != nil {
return err
}
// MontEncode our field elements for fast finite field arithmetic
// Needs to be done since the z and t coordinates are also encoded (ie: created with newGFp)
montEncode(&e.p.x.x, &e.p.x.x)
montEncode(&e.p.x.y, &e.p.x.y)
y, err := getYFromMontEncodedXG2(&e.p.x)
if err != nil {
return err
}
e.p.y = *y
// The flag serializationBigY is set (so the point pt with the higher Y is encoded)
// but the point e retrieved from the `getYFromX` is NOT the higher, then we inverse
if !e.IsHigherY() {
if encoding[0]&serializationBigY != 0 {
e.Neg(e)
}
} else {
if encoding[0]&serializationBigY == 0 { // The point given by getYFromX is the higher but the mask is not set for higher y
e.Neg(e)
}
}
// No need to check that the point e.p is on the curve
// since we retrieved y from x by using the curve equation.
// Adding it would be redundant
return nil
}