diff --git a/bls/bititerator.go b/bls/bititerator.go new file mode 100644 index 0000000..61bfd9a --- /dev/null +++ b/bls/bititerator.go @@ -0,0 +1,24 @@ +package bls + +// BitIterator is an iterator through bits. +type BitIterator struct { + arr []uint64 + n uint +} + +// NewBitIterator creates a new bit iterator given an array of ints. +func NewBitIterator(arr []uint64) BitIterator { + return BitIterator{arr, uint(len(arr) * 64)} +} + +// Next returns the next bit in the bit iterator with the +// second return value as true when finished. +func (bi *BitIterator) Next() (bool, bool) { + if bi.n == 0 { + return false, true + } + bi.n-- + part := bi.n / 64 + bit := bi.n - (part * 64) + return bi.arr[part]&(1< 0, false +} diff --git a/bls/bititerator_test.go b/bls/bititerator_test.go new file mode 100644 index 0000000..09b09a8 --- /dev/null +++ b/bls/bititerator_test.go @@ -0,0 +1,26 @@ +package bls_test + +import ( + "testing" + + "github.com/phoreproject/bls" +) + +func TestBitIterator(t *testing.T) { + a := bls.NewBitIterator([]uint64{0xa953d79b83f6ab59, 0x6dea2059e200bd39}) + expected := "01101101111010100010000001011001111000100000000010111101001110011010100101010011110101111001101110000011111101101010101101011001" + + for _, e := range expected { + bit, done := a.Next() + if done { + t.Fatal("iterator finished too soon") + } + if bit != (e == '1') { + t.Fatal("invalid bit") + } + } + + if _, done := a.Next(); done != true { + t.Fatal("iterator did not finish in time") + } +} diff --git a/bls/curve_test.go b/bls/curve_test.go new file mode 100644 index 0000000..0730301 --- /dev/null +++ b/bls/curve_test.go @@ -0,0 +1 @@ +package bls_test diff --git a/bls/fq.go b/bls/fq.go new file mode 100644 index 0000000..11ea4ec --- /dev/null +++ b/bls/fq.go @@ -0,0 +1,349 @@ +package bls + +import ( + "crypto/rand" + "fmt" + "hash" + "io" +) + +// FQ is an element in a field. +type FQ struct { + n FQRepr +} + +var bigZero = NewFQRepr(0) +var bigOne = NewFQRepr(1) +var bigTwo = NewFQRepr(2) + +// FQZero is the zero FQ element +var FQZero = FQReprToFQRaw(bigZero) + +// FQOne is the one FQ element +var FQOne = FQReprToFQ(bigOne) + +// QFieldModulus is the modulus of the field. +var QFieldModulus, _ = FQReprFromString("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787", 10) + +// FQR2 is R^2 % Q. +var FQR2, _ = FQReprFromString("2708263910654730174793787626328176511836455197166317677006154293982164122222515399004018013397331347120527951271750", 10) + +// Copy creates a copy of the field element. +func (f FQ) Copy() FQ { + return f +} + +// IsValid checks if the element is valid. +func (f *FQ) IsValid() bool { + return f.n[5]&0xf000000000000000 == 0 || f.n.Cmp(QFieldModulus) < 0 +} + +func (f *FQ) reduceAssign() { + if !f.IsValid() { + f.n.SubNoBorrow(QFieldModulus) + } +} + +// FQReprToFQ gets a pointer to a FQ given a pointer +// to an FQRepr +func FQReprToFQ(o FQRepr) FQ { + r := FQ{n: o} + if r.IsValid() { + r.MulAssign(FQ{FQR2}) + return r + } + return FQ{} +} + +// FQReprToFQRaw gets a pointer to a FQ without converting +// to montgomery form. +func FQReprToFQRaw(o FQRepr) FQ { + return FQ{n: o} +} + +// AddAssign multiplies a field element by this one. +func (f *FQ) AddAssign(other FQ) { + f.n.AddNoCarry(other.n) + f.reduceAssign() +} + +func (f *FQ) montReduce(hi [6]uint64, lo [6]uint64) { + f.n = MontReduce(hi, lo) + f.reduceAssign() +} + +// MulAssign multiplies a field element by this one. +func (f *FQ) MulAssign(other FQ) { + hi, lo := MultiplyFQRepr(f.n, other.n) + f.montReduce(hi, lo) +} + +// SubAssign subtracts a field element from this one. +func (f *FQ) SubAssign(other FQ) { + if other.n.Cmp(f.n) > 0 { + f.n.AddNoCarry(QFieldModulus) + } + f.n.SubNoBorrow(other.n) +} + +// DivAssign divides the field element by another +func (f *FQ) DivAssign(other FQ) { + otherInv, _ := other.Inverse() + f.MulAssign(otherInv) +} + +// Exp raises the element to a specific power. +func (f FQ) Exp(n FQRepr) FQ { + iter := NewBitIterator(n[:]) + res := FQOne.Copy() + foundOne := false + next, done := iter.Next() + for !done { + if foundOne { + res.SquareAssign() + } else { + foundOne = next + } + if next { + res.MulAssign(f) + } + next, done = iter.Next() + } + return res +} + +// Equals checks equality of two field elements. +func (f FQ) Equals(other FQ) bool { + return f.n.Equals(other.n) +} + +// NegAssign gets the negative value of the field element mod QFieldModulus. +func (f *FQ) NegAssign() { + if !f.IsZero() { + tmp := QFieldModulus.Copy() + tmp.SubNoBorrow(f.n) + f.n = tmp + } +} + +func (f FQ) String() string { + return fmt.Sprintf("Fq(0x%s)", f.ToRepr().String()) +} + +// Cmp compares this field element to another. +func (f FQ) Cmp(other FQ) int { + fr1 := f.ToRepr() + return fr1.Cmp(other.ToRepr()) +} + +// DoubleAssign doubles the element +func (f *FQ) DoubleAssign() { + f.n.Mul2() + f.reduceAssign() +} + +// IsZero checks if the field element is zero. +func (f FQ) IsZero() bool { + return f.n.Equals(bigZero) +} + +// SquareAssign squares a field element. +func (f *FQ) SquareAssign() { + r1, carry := MACWithCarry(0, f.n[0], f.n[1], 0) + r2, carry := MACWithCarry(0, f.n[0], f.n[2], carry) + r3, carry := MACWithCarry(0, f.n[0], f.n[3], carry) + r4, carry := MACWithCarry(0, f.n[0], f.n[4], carry) + r5, carry := MACWithCarry(0, f.n[0], f.n[5], carry) + r6 := carry + r3, carry = MACWithCarry(r3, f.n[1], f.n[2], 0) + r4, carry = MACWithCarry(r4, f.n[1], f.n[3], carry) + r5, carry = MACWithCarry(r5, f.n[1], f.n[4], carry) + r6, carry = MACWithCarry(r6, f.n[1], f.n[5], carry) + r7 := carry + r5, carry = MACWithCarry(r5, f.n[2], f.n[3], 0) + r6, carry = MACWithCarry(r6, f.n[2], f.n[4], carry) + r7, carry = MACWithCarry(r7, f.n[2], f.n[5], carry) + r8 := carry + r7, carry = MACWithCarry(r7, f.n[3], f.n[4], 0) + r8, carry = MACWithCarry(r8, f.n[3], f.n[5], carry) + r9 := carry + r9, carry = MACWithCarry(r9, f.n[4], f.n[5], 0) + r10 := carry + r11 := r10 >> 63 + r10 = (r10 << 1) | (r9 >> 63) + r9 = (r9 << 1) | (r8 >> 63) + r8 = (r8 << 1) | (r7 >> 63) + r7 = (r7 << 1) | (r6 >> 63) + r6 = (r6 << 1) | (r5 >> 63) + r5 = (r5 << 1) | (r4 >> 63) + r4 = (r4 << 1) | (r3 >> 63) + r3 = (r3 << 1) | (r2 >> 63) + r2 = (r2 << 1) | (r1 >> 63) + r1 = r1 << 1 + + carry = 0 + r0, carry := MACWithCarry(0, f.n[0], f.n[0], carry) + r1, carry = AddWithCarry(r1, 0, carry) + r2, carry = MACWithCarry(r2, f.n[1], f.n[1], carry) + r3, carry = AddWithCarry(r3, 0, carry) + r4, carry = MACWithCarry(r4, f.n[2], f.n[2], carry) + r5, carry = AddWithCarry(r5, 0, carry) + r6, carry = MACWithCarry(r6, f.n[3], f.n[3], carry) + r7, carry = AddWithCarry(r7, 0, carry) + r8, carry = MACWithCarry(r8, f.n[4], f.n[4], carry) + r9, carry = AddWithCarry(r9, 0, carry) + r10, carry = MACWithCarry(r10, f.n[5], f.n[5], carry) + r11, carry = AddWithCarry(r11, 0, carry) + f.montReduce([6]uint64{r6, r7, r8, r9, r10, r11}, [6]uint64{r0, r1, r2, r3, r4, r5}) +} + +var negativeOneFQ = FQReprToFQ(negativeOne) + +// Sqrt calculates the square root of the field element. +func (f FQ) Sqrt() (FQ, bool) { + // Shank's algorithm for q mod 4 = 3 + // https://eprint.iacr.org/2012/685.pdf (page 9, algorithm 2) + + a1 := f.Exp(qMinus3Over4) + a0 := a1.Copy() + a0.SquareAssign() + a0.MulAssign(f) + + if a0.Equals(negativeOneFQ) { + return FQ{}, false + } + a1.MulAssign(f) + return a1, true +} + +func isEven(b FQRepr) bool { + return b.IsEven() +} + +// Inverse finds the inverse of the field element. +func (f FQ) Inverse() (FQ, bool) { + if f.IsZero() { + return FQ{}, false + } + u := f.n.Copy() + v := QFieldModulus.Copy() + b := FQReprToFQRaw(FQR2.Copy()) + c := FQZero.Copy() + + for u.Cmp(bigOne) != 0 && v.Cmp(bigOne) != 0 { + for isEven(u) { + u.Div2() + if isEven(b.n) { + b.n.Div2() + } else { + b.n.AddNoCarry(QFieldModulus) + b.n.Div2() + } + } + + for isEven(v) { + v.Div2() + if isEven(c.n) { + c.n.Div2() + } else { + c.n.AddNoCarry(QFieldModulus) + c.n.Div2() + } + } + + if u.Cmp(v) >= 0 { + u.SubNoBorrow(v) + b.SubAssign(c) + } else { + v.SubNoBorrow(u) + c.SubAssign(b) + } + } + if u.Cmp(bigOne) == 0 { + return b, true + } + return c, true +} + +// Parity checks if the point is greater than the point negated. +func (f FQ) Parity() bool { + neg := f.Copy() + neg.NegAssign() + return f.Cmp(neg) > 0 +} + +// MulBits multiplies the number by a big number. +func (f FQ) MulBits(b *FQRepr) FQ { + res := FQZero.Copy() + for i := uint(0); i < b.BitLen(); i++ { + res.DoubleAssign() + if b.Bit(i) { + res.AddAssign(f) + } + } + return res +} + +// MulBytes multiplies the number by some bytes. +func (f FQ) MulBytes(b []byte) FQ { + res := FQZero.Copy() + for i := uint(0); i < uint(len(b)*8); i++ { + res.DoubleAssign() + if b[i/8]&(1<<(i%8)) != 0 { + res.AddAssign(f) + } + } + return res +} + +// HashFQ calculates a new FQ2 value based on a hash. +func HashFQ(hasher hash.Hash) FQ { + digest := hasher.Sum(nil) + return FQOne.MulBytes(digest) +} + +var qMinus1Over2 = FQRepr{0xdcff7fffffffd555, 0xf55ffff58a9ffff, 0xb39869507b587b12, 0xb23ba5c279c2895f, 0x258dd3db21a5d66b, 0xd0088f51cbff34d} + +// LegendreSymbol is the legendre symbol of an element. +type LegendreSymbol uint8 + +const ( + // LegendreZero is the legendre symbol of zero. + LegendreZero = LegendreSymbol(iota) + + // LegendreQuadraticResidue is the legendre symbol of quadratic residue. + LegendreQuadraticResidue + + // LegendreQuadraticNonResidue is the legendre symbol of quadratic non-residue. + LegendreQuadraticNonResidue +) + +// Legendre gets the legendre symbol of the element. +func (f *FQ) Legendre() LegendreSymbol { + o := f.Exp(qMinus1Over2) + if o.IsZero() { + return LegendreZero + } else if o.Equals(FQOne) { + return LegendreQuadraticResidue + } else { + return LegendreQuadraticNonResidue + } +} + +// ToRepr gets the 256-bit representation of the field element. +func (f *FQ) ToRepr() FQRepr { + out := f.Copy() + out.montReduce([6]uint64{0, 0, 0, 0, 0, 0}, [6]uint64{f.n[0], f.n[1], f.n[2], f.n[3], f.n[4], f.n[5]}) + return out.n +} + +// RandFQ generates a random FQ element. +func RandFQ(reader io.Reader) (FQ, error) { + r, err := rand.Int(reader, QFieldModulus.ToBig()) + if err != nil { + return FQ{}, err + } + b, _ := FQReprFromBigInt(r) + f := FQReprToFQ(b) + return f, nil +} diff --git a/bls/fq12.go b/bls/fq12.go new file mode 100644 index 0000000..7a5028d --- /dev/null +++ b/bls/fq12.go @@ -0,0 +1,237 @@ +package bls + +import ( + "fmt" + "io" +) + +// FQ12 is an element of Fq12, represented by c0 + c1 * w. +type FQ12 struct { + c0 *FQ6 + c1 *FQ6 +} + +// NewFQ12 creates a new FQ12 element from two FQ6 elements. +func NewFQ12(c0 *FQ6, c1 *FQ6) *FQ12 { + return &FQ12{ + c0: c0, + c1: c1, + } +} + +func (f *FQ12) String() string { + return fmt.Sprintf("Fq12(%s + %s * w)", f.c0, f.c1) +} + +// ConjugateAssign returns the conjugate of the FQ12 element. +func (f *FQ12) ConjugateAssign() { + f.c1.NegAssign() +} + +// MulBy014Assign multiplies FQ12 element by 3 FQ2 elements. +func (f *FQ12) MulBy014Assign(c0 FQ2, c1 FQ2, c4 FQ2) { + aa := f.c0.Copy() + aa.MulBy01Assign(c0, c1) + + bb := f.c1.Copy() + bb.MulBy1Assign(c4) + + c1.AddAssign(c4) + f.c1.AddAssign(f.c0) + f.c1.MulBy01Assign(c0, c1) + f.c1.SubAssign(aa) + f.c1.SubAssign(bb) + f.c0 = bb.Copy() + f.c0.MulByNonresidueAssign() + f.c0.AddAssign(aa) +} + +// FQ12Zero is the zero element of FQ12. +var FQ12Zero = NewFQ12(FQ6Zero, FQ6Zero) + +// FQ12One is the one element of FQ12. +var FQ12One = NewFQ12(FQ6One, FQ6Zero) + +// Equals checks if two FQ12 elements are equal. +func (f FQ12) Equals(other *FQ12) bool { + return f.c0.Equals(other.c0) && f.c1.Equals(other.c1) +} + +// DoubleAssign doubles each coefficient in an FQ12 element. +func (f *FQ12) DoubleAssign() { + f.c0.DoubleAssign() + f.c1.DoubleAssign() +} + +// NegAssign negates each coefficient in an FQ12 element. +func (f *FQ12) NegAssign() { + f.c1.NegAssign() + f.c0.NegAssign() +} + +// AddAssign adds two FQ12 elements together. +func (f *FQ12) AddAssign(other *FQ12) { + f.c0.AddAssign(other.c0) + f.c1.AddAssign(other.c1) +} + +// IsZero returns if the FQ12 element is zero. +func (f *FQ12) IsZero() bool { + return f.c0.IsZero() && f.c1.IsZero() +} + +// SubAssign subtracts one FQ12 element from another. +func (f *FQ12) SubAssign(other *FQ12) { + f.c0.SubAssign(other.c0) + f.c1.SubAssign(other.c1) +} + +// RandFQ12 generates a random FQ12 element. +func RandFQ12(reader io.Reader) (*FQ12, error) { + a, err := RandFQ6(reader) + if err != nil { + return nil, err + } + b, err := RandFQ6(reader) + if err != nil { + return nil, err + } + return NewFQ12(a, b), nil +} + +// Copy returns a copy of the FQ12 element. +func (f FQ12) Copy() *FQ12 { + return NewFQ12(f.c0.Copy(), f.c1.Copy()) +} + +// Exp raises the element ot a specific power. +func (f FQ12) Exp(n FQRepr) *FQ12 { + nCopy := n.Copy() + res := FQ12One.Copy() + fi := f.Copy() + for nCopy.Cmp(bigZero) != 0 { + if !isEven(nCopy) { + res.MulAssign(fi) + } + fi.MulAssign(fi) + nCopy.Rsh(1) + } + return res +} + +var frobeniusCoeffFQ12c1 = [12]FQ2{ + FQ2One, + NewFQ2( + FQReprToFQRaw(FQRepr{0x7089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x8f2220fb0fb66eb}), + FQReprToFQRaw(FQRepr{0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf}), + ), + NewFQ2( + FQReprToFQRaw(FQRepr{0xecfb361b798dba3a, 0xc100ddb891865a2c, 0xec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x110f184e51c5f59}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), + NewFQ2( + FQReprToFQRaw(FQRepr{0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0xbd592fc7d825ec8}), + FQReprToFQRaw(FQRepr{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0xe2b7eedbbfd87d2}), + ), + NewFQ2( + FQReprToFQRaw(FQRepr{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), + NewFQ2( + FQReprToFQRaw(FQRepr{0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x95ba654ed2226b, 0x2e370eccc86f7dd}), + FQReprToFQRaw(FQRepr{0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd}), + ), + NewFQ2( + FQReprToFQRaw(FQRepr{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), + NewFQ2( + FQReprToFQRaw(FQRepr{0xb2f66aad4ce5d646, 0x5842a06bfc497cec, 0xcf4895d42599d394, 0xc11b9cba40a8e8d0, 0x2e3813cbe5a0de89, 0x110eefda88847faf}), + FQReprToFQRaw(FQRepr{0x7089552b319d465, 0xc6695f92b50a8313, 0x97e83cccd117228f, 0xa35baecab2dc29ee, 0x1ce393ea5daace4d, 0x8f2220fb0fb66eb}), + ), + NewFQ2( + FQReprToFQRaw(FQRepr{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), + NewFQ2( + FQReprToFQRaw(FQRepr{0x7bcfa7a25aa30fda, 0xdc17dec12a927e7c, 0x2f088dd86b4ebef1, 0xd1ca2087da74d4a7, 0x2da2596696cebc1d, 0xe2b7eedbbfd87d2}), + FQReprToFQRaw(FQRepr{0x3e2f585da55c9ad1, 0x4294213d86c18183, 0x382844c88b623732, 0x92ad2afd19103e18, 0x1d794e4fac7cf0b9, 0xbd592fc7d825ec8}), + ), + NewFQ2( + FQReprToFQRaw(FQRepr{0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), + NewFQ2( + FQReprToFQRaw(FQRepr{0x82d83cf50dbce43f, 0xa2813e53df9d018f, 0xc6f0caa53c65e181, 0x7525cf528d50fe95, 0x4a85ed50f4798a6b, 0x171da0fd6cf8eebd}), + FQReprToFQRaw(FQRepr{0x3726c30af242c66c, 0x7c2ac1aad1b6fe70, 0xa04007fbba4b14a2, 0xef517c3266341429, 0x95ba654ed2226b, 0x2e370eccc86f7dd}), + ), +} + +// FrobeniusMapAssign calculates the frobenius map of an FQ12 element. +func (f *FQ12) FrobeniusMapAssign(power uint8) { + f.c0.FrobeniusMapAssign(power) + f.c1.FrobeniusMapAssign(power) + f.c1.c0.MulAssign(frobeniusCoeffFQ12c1[power%12]) + f.c1.c1.MulAssign(frobeniusCoeffFQ12c1[power%12]) + f.c1.c2.MulAssign(frobeniusCoeffFQ12c1[power%12]) +} + +// SquareAssign squares the FQ2 element. +func (f *FQ12) SquareAssign() { + ab := f.c0.Copy() + ab.MulAssign(f.c1) + c0c1 := f.c0.Copy() + c0c1.AddAssign(f.c1) + c0 := f.c1.Copy() + c0.MulByNonresidueAssign() + c0.AddAssign(f.c0) + c0.MulAssign(c0c1) + c0.SubAssign(ab) + f.c1 = ab.Copy() + f.c1.AddAssign(ab) + ab.MulByNonresidueAssign() + c0.SubAssign(ab) + f.c0 = c0 +} + +// MulAssign multiplies two FQ12 elements together. +func (f *FQ12) MulAssign(other *FQ12) { + aa := f.c0.Copy() + aa.MulAssign(other.c0) + bb := f.c1.Copy() + bb.MulAssign(other.c1) + o := other.c0.Copy() + o.AddAssign(other.c1) + + f.c1.AddAssign(f.c0) + f.c1.MulAssign(o) + f.c1.SubAssign(aa) + f.c1.SubAssign(bb) + f.c0 = bb.Copy() + f.c0.MulByNonresidueAssign() + f.c0.AddAssign(aa) +} + +// InverseAssign finds the inverse of an FQ12 +func (f *FQ12) InverseAssign() bool { + c0s := f.c0.Copy() + c0s.SquareAssign() + c1s := f.c1.Copy() + c1s.SquareAssign() + c1s.MulByNonresidueAssign() + c0s.SubAssign(c1s) + + if !c0s.InverseAssign() { + return false + } + + tmp := NewFQ12(c0s.Copy(), c0s.Copy()) + tmp.c0.MulAssign(f.c0) + tmp.c1.MulAssign(f.c1) + tmp.c1.NegAssign() + + f.c0 = tmp.c0 + f.c1 = tmp.c1 + + return true +} diff --git a/bls/fq12_test.go b/bls/fq12_test.go new file mode 100644 index 0000000..4d403e7 --- /dev/null +++ b/bls/fq12_test.go @@ -0,0 +1,325 @@ +package bls_test + +import ( + "crypto/rand" + "testing" + + "github.com/phoreproject/bls" +) + +func TestFQ12MulBy014(t *testing.T) { + for i := 0; i < 1000; i++ { + c0, err := bls.RandFQ2(rand.Reader) + if err != nil { + t.Fatal(err) + } + c1, err := bls.RandFQ2(rand.Reader) + if err != nil { + t.Fatal(err) + } + c5, err := bls.RandFQ2(rand.Reader) + if err != nil { + t.Fatal(err) + } + a, err := bls.RandFQ12(rand.Reader) + if err != nil { + t.Fatal(err) + } + b := a.Copy() + b.MulAssign(bls.NewFQ12( + bls.NewFQ6(c0, c1, bls.FQ2Zero), + bls.NewFQ6(bls.FQ2Zero, c5, bls.FQ2Zero), + )) + a.MulBy014Assign(c0, c1, c5) + + if !a.Equals(b) { + t.Fatal("MulBy014 is broken.") + } + } +} + +func TestFQ12Squaring(t *testing.T) { + x := NewXORShift(2) + a, _ := bls.RandFQ12(x) + + b := a.Copy() + a.SquareAssign() + b.MulAssign(b) + + if !a.Equals(b) { + t.Fatal("squaring didn't work") + } +} + +func TestFQ12RandomFrobenius(t *testing.T) { + x := NewXORShift(2) + + for i := 0; i < 10; i++ { + for j := 0; j < 14; j++ { + a, _ := bls.RandFQ12(x) + b := a.Copy() + + for k := 0; k < j; k++ { + a = a.Exp(bls.QFieldModulus) + } + b.FrobeniusMapAssign(uint8(j)) + + if !a.Equals(b) { + t.Fatal("frobenius map does not match exponent") + } + } + } +} + +func TestFQ12RandomMultiplication(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ12(x) + b, _ := bls.RandFQ12(x) + c, _ := bls.RandFQ12(x) + + t0 := a.Copy() + t0.MulAssign(b) + t0.MulAssign(c) + + t1 := a.Copy() + t1.MulAssign(c) + t1.MulAssign(b) + + t2 := b.Copy() + t2.MulAssign(c) + t2.MulAssign(a) + + if !t0.Equals(t1) { + t.Fatal("expected (a*b)*c == (a*c)*b") + } + + if !t1.Equals(t2) { + t.Fatal("expected (a*c)*b == (b*c)*a") + } + } +} + +func TestFQ12RandomAddition(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ12(x) + b, _ := bls.RandFQ12(x) + c, _ := bls.RandFQ12(x) + + t0 := a.Copy() + t0.AddAssign(b) + t0.AddAssign(c) + + t1 := a.Copy() + t1.AddAssign(c) + t1.AddAssign(b) + + t2 := b.Copy() + t2.AddAssign(c) + t2.AddAssign(a) + + if !t0.Equals(t1) { + t.Fatal("expected (a+b)+c == (a+c)+b") + } + + if !t1.Equals(t2) { + t.Fatal("expected (a+c)+b == (b+c)+a") + } + } +} + +func TestFQ12RandomSubtraction(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ12(x) + b, _ := bls.RandFQ12(x) + + t0 := a.Copy() + t0.SubAssign(b) + + t1 := b.Copy() + t1.SubAssign(a) + + t2 := t0.Copy() + t2.AddAssign(t1) + + if !t2.IsZero() { + t.Fatal("expected (a - b) + (b - a) = 0") + } + } +} + +func TestFQ12RandomNegation(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ12(x) + b := a.Copy() + + b.NegAssign() + b.AddAssign(a) + + if !b.IsZero() { + t.Fatal("expected (a + ~a) = 0") + } + } +} + +func TestFQ12RandomDoubling(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ12(x) + b := a.Copy() + + a.AddAssign(b) + b.DoubleAssign() + + if !a.Equals(b) { + t.Fatal("expected 2a = 2a") + } + } +} + +func TestFQ12RandomSquaring(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ12(x) + b := a.Copy() + + a.MulAssign(b) + b.SquareAssign() + + if !a.Equals(b) { + t.Fatal("expected a^2 = a^2") + } + } +} + +func TestFQ12RandomInversion(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ12(x) + b := a.Copy() + + b.InverseAssign() + + a.MulAssign(b) + + if !a.Equals(bls.FQ12One) { + t.Fatal("expected a * a^-1 = 1") + } + } +} + +func TestFQ12RandomExpansion(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ12(x) + b, _ := bls.RandFQ12(x) + c, _ := bls.RandFQ12(x) + d, _ := bls.RandFQ12(x) + + t0 := a.Copy() + t0.AddAssign(b) + t1 := c.Copy() + t1.AddAssign(d) + t0.MulAssign(t1) + + t2 := a.Copy() + t2.MulAssign(c) + t3 := b.Copy() + t3.MulAssign(c) + t4 := a.Copy() + t4.MulAssign(d) + t5 := b.Copy() + t5.MulAssign(d) + + t2.AddAssign(t3) + t2.AddAssign(t4) + t2.AddAssign(t5) + + if !t2.Equals(t0) { + t.Fatal("(a + b)(c + d) should = (a*c + b*c + a*d + b*d)") + } + } +} + +func BenchmarkFQ12MulAssign(b *testing.B) { + type addData struct { + f1 *bls.FQ12 + f2 *bls.FQ12 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ12(r) + f2, _ := bls.RandFQ12(r) + inData[i] = addData{ + f1: f1, + f2: f2, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.MulAssign(inData[count].f2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQ12SquareAssign(b *testing.B) { + type addData struct { + f1 *bls.FQ12 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ12(r) + inData[i] = addData{ + f1: f1, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.SquareAssign() + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQ12InverseAssign(b *testing.B) { + type addData struct { + f1 *bls.FQ12 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ12(r) + inData[i] = addData{ + f1: f1, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.InverseAssign() + count = (count + 1) % g1MulAssignSamples + } +} diff --git a/bls/fq2.go b/bls/fq2.go new file mode 100644 index 0000000..d26eff9 --- /dev/null +++ b/bls/fq2.go @@ -0,0 +1,285 @@ +package bls + +import ( + "fmt" + "hash" + "io" + "math/big" +) + +var oneLsh384MinusOne, _ = FQReprFromBigInt(new(big.Int).Sub(new(big.Int).Lsh(bigOne.ToBig(), 384), bigOne.ToBig())) + +// FQ2 represents an element of Fq2, represented by c0 + c1 * u. +type FQ2 struct { + c0 FQ + c1 FQ +} + +// NewFQ2 constructs a new FQ2 element given two FQ elements. +func NewFQ2(c0 FQ, c1 FQ) FQ2 { + return FQ2{ + c0: c0, + c1: c1, + } +} + +func (f FQ2) String() string { + return fmt.Sprintf("Fq2(%s + %s * u)", f.c0, f.c1) +} + +// Cmp compares two FQ2 elements. +func (f FQ2) Cmp(other FQ2) int { + cOut := f.c1.Cmp(other.c1) + if cOut != 0 { + return cOut + } + return f.c0.Cmp(other.c0) +} + +// MultiplyByNonresidueAssign multiplies this element by the cubic and quadratic +// nonresidue 1 + u. +func (f *FQ2) MultiplyByNonresidueAssign() { + oldC0 := f.c0.Copy() + f.c0.SubAssign(f.c1) + f.c1.AddAssign(oldC0) +} + +// Norm gets the norm of Fq2 as extension field in i over Fq. +func (f *FQ2) Norm() FQ { + t0 := f.c0.Copy() + t1 := f.c1.Copy() + t0.SquareAssign() + t1.SquareAssign() + t1.AddAssign(t0) + return t1 +} + +// FQ2Zero gets the zero element of the field. +var FQ2Zero = FQ2{ + c0: FQZero, + c1: FQZero, +} + +// FQ2One gets the one-element of the field. +var FQ2One = FQ2{ + c0: FQOne, + c1: FQZero, +} + +// IsZero checks if the field element is zero. +func (f FQ2) IsZero() bool { + return f.c0.IsZero() && f.c1.IsZero() +} + +// SquareAssign squares the FQ2 element. +func (f *FQ2) SquareAssign() { + ab := f.c0.Copy() + ab.MulAssign(f.c1) + c0c1 := f.c0.Copy() + c0c1.AddAssign(f.c1) + c0 := f.c1.Copy() + c0.NegAssign() + c0.AddAssign(f.c0) + c0.MulAssign(c0c1) + c0.SubAssign(ab) + c0.AddAssign(ab) + ab.AddAssign(ab) + f.c0 = c0 + f.c1 = ab +} + +// DoubleAssign doubles an FQ2 element. +func (f *FQ2) DoubleAssign() { + f.c0.DoubleAssign() + f.c1.DoubleAssign() +} + +// NegAssign negates a FQ2 element. +func (f *FQ2) NegAssign() { + f.c0.NegAssign() + f.c1.NegAssign() +} + +// AddAssign adds two FQ2 elements together. +func (f *FQ2) AddAssign(other FQ2) { + f.c0.AddAssign(other.c0) + f.c1.AddAssign(other.c1) +} + +// SubAssign subtracts one field element from another. +func (f *FQ2) SubAssign(other FQ2) { + f.c0.SubAssign(other.c0) + f.c1.SubAssign(other.c1) +} + +// MulAssign multiplies two FQ2 elements together. +func (f *FQ2) MulAssign(other FQ2) { + aa := f.c0.Copy() + aa.MulAssign(other.c0) + bb := f.c1.Copy() + bb.MulAssign(other.c1) + o := other.c0.Copy() + o.AddAssign(other.c1) + f.c1.AddAssign(f.c0) + f.c1.MulAssign(o) + f.c1.SubAssign(aa) + f.c1.SubAssign(bb) + + f.c0 = aa + f.c0.SubAssign(bb) +} + +// InverseAssign finds the inverse of the field element. +func (f *FQ2) InverseAssign() bool { + t1 := f.c1.Copy() + t1.SquareAssign() + t0 := f.c0.Copy() + t0.SquareAssign() + t0.AddAssign(t1) + t, success := t0.Inverse() + if !success { + return false + } + f.c0.MulAssign(t) + f.c1.MulAssign(t) + f.c1.NegAssign() + return true +} + +var frobeniusCoeffFQ2c1 = [2]FQ{ + FQOne, + FQReprToFQRaw(FQRepr{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206}), +} + +// FrobeniusMapAssign multiplies the element by the Frobenius automorphism +// coefficient. +func (f *FQ2) FrobeniusMapAssign(power uint8) { + f.c1.MulAssign(frobeniusCoeffFQ2c1[power%2]) +} + +// Legendre gets the legendre symbol of the FQ2 element. +func (f FQ2) Legendre() LegendreSymbol { + norm := f.Norm() + + return norm.Legendre() +} + +var qMinus3Over4 = fqReprFromHexUnchecked("680447a8e5ff9a692c6e9ed90d2eb35d91dd2e13ce144afd9cc34a83dac3d8907aaffffac54ffffee7fbfffffffeaaa") + +// Exp raises the element ot a specific power. +func (f FQ2) Exp(n FQRepr) FQ2 { + iter := NewBitIterator(n[:]) + res := FQ2One.Copy() + foundOne := false + next, done := iter.Next() + for !done { + if foundOne { + res.SquareAssign() + } else { + foundOne = next + } + if next { + res.MulAssign(f) + } + next, done = iter.Next() + } + return res +} + +// -(2**384 mod q) mod q +var negativeOne, _ = FQReprFromString("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559786", 10) + +// Equals checks if this FQ2 equals another one. +func (f FQ2) Equals(other FQ2) bool { + return f.Cmp(other) == 0 +} + +// Sqrt finds the sqrt of a field element. +func (f FQ2) Sqrt() (FQ2, bool) { + // Algorithm 9, https://eprint.iacr.org/2012/685.pdf + if f.IsZero() { + return FQ2Zero, true + } + a1 := f.Exp(qMinus3Over4) + alpha := a1.Copy() + alpha.SquareAssign() + alpha.MulAssign(f) + a0 := alpha.Copy() + a0.FrobeniusMapAssign(1) + a0.MulAssign(alpha) + + neg1 := FQ2{ + c0: negativeOneFQ, + c1: FQZero, + } + + if a0.Equals(neg1) { + return FQ2{}, false + } + a1.MulAssign(f) + + if alpha.Equals(neg1) { + a1.MulAssign(FQ2{ + c0: FQZero, + c1: FQOne, + }) + return a1, true + } + alpha.AddAssign(FQ2One) + alpha = alpha.Exp(qMinus1Over2) + alpha.MulAssign(a1) + return alpha, true +} + +// Copy returns a copy of the field element. +func (f *FQ2) Copy() FQ2 { + return *f +} + +// RandFQ2 generates a random FQ2 element. +func RandFQ2(reader io.Reader) (FQ2, error) { + i0, err := RandFQ(reader) + if err != nil { + return FQ2{}, err + } + i1, err := RandFQ(reader) + if err != nil { + return FQ2{}, err + } + return NewFQ2( + i0, + i1, + ), nil +} + +// Parity checks if the point is greater than the point negated. +func (f FQ2) Parity() bool { + neg := f.Copy() + neg.NegAssign() + return f.Cmp(neg) > 0 +} + +// MulBits multiplies the number by a big number. +func (f FQ2) MulBits(b *big.Int) FQ2 { + res := FQ2Zero + for i := 0; i < b.BitLen(); i++ { + res.DoubleAssign() + if b.Bit(b.BitLen()-1-i) == 1 { + res.AddAssign(f) + } + } + return res +} + +// DivAssign divides the FQ2 element by another FQ2 element. +func (f *FQ2) DivAssign(other FQ2) { + other.InverseAssign() + f.MulAssign(other) +} + +// HashFQ2 calculates a new FQ2 value based on a hash. +func HashFQ2(hasher hash.Hash) FQ2 { + digest := hasher.Sum(nil) + newB := new(big.Int).SetBytes(digest) + return FQ2One.MulBits(newB) +} diff --git a/bls/fq2_test.go b/bls/fq2_test.go new file mode 100644 index 0000000..3a5b439 --- /dev/null +++ b/bls/fq2_test.go @@ -0,0 +1,602 @@ +package bls_test + +import ( + "crypto/rand" + "testing" + + "github.com/phoreproject/bls" +) + +var ( + bigOne = bls.NewFQRepr(1) + bigTwo = bls.NewFQRepr(2) +) + +func TestFQ2Ordering(t *testing.T) { + a := bls.NewFQ2(bls.FQZero.Copy(), bls.FQZero.Copy()) + b := a.Copy() + + if a.Cmp(b) != 0 { + t.Error("a != b after cloning a to b") + } + b.AddAssign(bls.NewFQ2(bls.FQOne, bls.FQZero)) + if a.Cmp(b) >= 0 { + t.Error("a >= b after adding to b") + } + a.AddAssign(bls.NewFQ2(bls.FQOne, bls.FQZero)) + if a.Cmp(b) != 0 { + t.Error("a != b after adding to a and b") + } + b.AddAssign(bls.NewFQ2(bls.FQZero, bls.FQOne)) + if a.Cmp(b) >= 0 { + t.Error("a >= b after adding to b.c1") + } + a.AddAssign(bls.NewFQ2(bls.FQOne, bls.FQZero)) + if a.Cmp(b) >= 0 { + t.Error("c0 is taking precedent over c1") + } + a.AddAssign(bls.NewFQ2(bls.FQZero, bls.FQOne)) + if a.Cmp(b) <= 0 { + t.Error("FQ2(2, 1) <= FQ2(1, 1)") + } + b.AddAssign(bls.NewFQ2(bls.FQOne, bls.FQZero)) + if a.Cmp(b) != 0 { + t.Error("FQ2(2, 1) != FQ2(2, 1)") + } +} + +func TestFQ2Basics(t *testing.T) { + f := bls.NewFQ2(bls.FQZero, bls.FQZero) + if !f.Equals(bls.FQ2Zero) { + t.Error("FQ2Zero != FQ2(0, 0)") + } + + f = bls.NewFQ2(bls.FQOne, bls.FQZero) + if !f.Equals(bls.FQ2One) { + t.Error("FQ2One != FQ2(1, 0)") + } + + if bls.FQ2One.IsZero() { + t.Error("FQ2One.IsZero() == true") + } + if !bls.FQ2Zero.IsZero() { + t.Error("FQ2Zero.IsZero() != true") + } + f = bls.NewFQ2(bls.FQZero, bls.FQOne) + if f.IsZero() { + t.Error("FQ2(0, 1).IsZero() == true") + } +} + +func TestFQ2Squaring(t *testing.T) { + a := bls.NewFQ2(bls.FQOne, bls.FQOne) + a.SquareAssign() + expected := bls.NewFQ2(bls.FQZero, bls.FQReprToFQ(bigTwo)) + if !a.Equals(expected) { + t.Error("FQ(1, 1).Square() != FQ(0, 2)") + } + + a = bls.NewFQ2(bls.FQZero, bls.FQOne) + a.SquareAssign() + neg1 := bls.FQOne.Copy() + neg1.NegAssign() + expected = bls.NewFQ2(neg1, bls.FQZero) + if !a.Equals(expected) { + t.Error("FQ(0, 1).Square() != FQ(-1, 0)") + } + + a0, _ := bls.FQReprFromString("7080c5fa1d8e04241b76dcc1c3fbe5ef7f295a94e58ae7c90e34aab6fb6a6bd4eef5c946536f6029c2c6309bbf8b598", 16) + a1, _ := bls.FQReprFromString("10d1615e75250a21fc58a7b7be815407bfb99020604137a0dac5a4c911a4353e6ad3291177c8c7e538f473b3c870a4ab", 16) + a = bls.NewFQ2(bls.FQReprToFQ(a0), bls.FQReprToFQ(a1)) + a.SquareAssign() + expected0, _ := bls.FQReprFromString("7eac81369c433614cf17b5893c3d327cb674157618da1760dc46ab8fad67ae0b9f2a66eae1073baf262c28c538bcf68", 16) + expected1, _ := bls.FQReprFromString("1542a61c8a8db994739c983042779a6538d0d7275a9689e1e75138bce4cec7aaa23eb7e12dd54d98c1579cf58e980cf8", 16) + expected = bls.NewFQ2(bls.FQReprToFQ(expected0), bls.FQReprToFQ(expected1)) + if !a.Equals(expected) { + t.Error("adding FQ2s together not giving expected result") + } +} + +func TestFQ2Mul(t *testing.T) { + a0, _ := bls.FQReprFromString("787155314249811040945591987890197698923521563000754191852630526325910580973833767165585004431929413598958418534147", 10) + a1, _ := bls.FQReprFromString("3701300508704531706796752084315366662585685126320813849768310480839238335689933757701592694115590705658584012352975", 10) + a := bls.NewFQ2(bls.FQReprToFQ(a0), bls.FQReprToFQ(a1)) + b0, _ := bls.FQReprFromString("428812050294605776242613635112226565923171104978712194146076278968219164256021918826122932548851868482795586147198", 10) + b1, _ := bls.FQReprFromString("3983398787459011889399209724009438146317670502764434060579092960070359230210191684510008045453936255762435213464276", 10) + b := bls.NewFQ2(bls.FQReprToFQ(b0), bls.FQReprToFQ(b1)) + o0, _ := bls.FQReprFromString("3589136112018482378111625145859346905296349062557304864057134187313531783880863512355763193523645312504462844151780", 10) + o1, _ := bls.FQReprFromString("297668880544309439220785639618566999376775158493905442186353189401526493115249312794616596981510685741636811493754", 10) + o := bls.NewFQ2(bls.FQReprToFQ(o0), bls.FQReprToFQ(o1)) + a.MulAssign(b) + if !a.Equals(o) { + t.Error("FQ2 Mul not working properly") + } +} + +func TestFQ2Inverse(t *testing.T) { + invZero := bls.FQ2Zero.Copy() + if invZero.InverseAssign() { + t.Error("inverse of zero is returning a non-nil value") + } + + a0, _ := bls.FQReprFromString("787155314249811040945591987890197698923521563000754191852630526325910580973833767165585004431929413598958418534147", 10) + a1, _ := bls.FQReprFromString("3701300508704531706796752084315366662585685126320813849768310480839238335689933757701592694115590705658584012352975", 10) + a := bls.NewFQ2(bls.FQReprToFQ(a0), bls.FQReprToFQ(a1)) + o0, _ := bls.FQReprFromString("2973628342543885151746258559273164780300946315949957485732289400047711856966246507651218645021769655299729012680084", 10) + o1, _ := bls.FQReprFromString("2498621830671500058873354492670308521035465162737562150317014095994530700756690595853342128497677859200322409409716", 10) + o := bls.NewFQ2(bls.FQReprToFQ(o0), bls.FQReprToFQ(o1)) + a.InverseAssign() + if !a.Equals(o) { + t.Error("FQ2 Inv not working properly") + } +} + +func TestFQ2Addition(t *testing.T) { + a0, _ := bls.FQReprFromString("149598838772691373030373252224260385709104435267292980765191905060056642409050296541472646296924142271996639838151", 10) + a1, _ := bls.FQReprFromString("181009733668961021523236619221743585335449215747654796739195280796206588018924306389615593874039575085936328761563", 10) + a := bls.NewFQ2(bls.FQReprToFQ(a0), bls.FQReprToFQ(a1)) + b0, _ := bls.FQReprFromString("3048378325167389389927636537438764659078261027448144976696012669013445082263630955837031612987742627153430205370098", 10) + b1, _ := bls.FQReprFromString("2745732935355827219217491212445536316258315122004495924168966599922007114897473294472413581011479900113332636006841", 10) + b := bls.NewFQ2(bls.FQReprToFQ(b0), bls.FQReprToFQ(b1)) + o0, _ := bls.FQReprFromString("3197977163940080762958009789663025044787365462715437957461204574073501724672681252378504259284666769425426845208249", 10) + o1, _ := bls.FQReprFromString("2926742669024788240740727831667279901593764337752150720908161880718213702916397600862029174885519475199268964768404", 10) + o := bls.NewFQ2(bls.FQReprToFQ(o0), bls.FQReprToFQ(o1)) + a.AddAssign(b) + if !a.Equals(o) { + t.Error("FQ2 add not working properly") + } +} + +func TestFQ2Subtraction(t *testing.T) { + a0, _ := bls.FQReprFromString("149598838772691373030373252224260385709104435267292980765191905060056642409050296541472646296924142271996639838151", 10) + a1, _ := bls.FQReprFromString("181009733668961021523236619221743585335449215747654796739195280796206588018924306389615593874039575085936328761563", 10) + a := bls.NewFQ2(bls.FQReprToFQ(a0), bls.FQReprToFQ(a1)) + b0, _ := bls.FQReprFromString("3048378325167389389927636537438764659078261027448144976696012669013445082263630955837031612987742627153430205370098", 10) + b1, _ := bls.FQReprFromString("2745732935355827219217491212445536316258315122004495924168966599922007114897473294472413581011479900113332636006841", 10) + b := bls.NewFQ2(bls.FQReprToFQ(b0), bls.FQReprToFQ(b1)) + o0, _ := bls.FQReprFromString("1103630068826969376520526540521399883187726227758155889401237372170643210636257205147128662438197179156460707027840", 10) + o1, _ := bls.FQReprFromString("1437686353534801195723535232512111425634016913682166757902286816998231123612288876359889641991575339010497965314509", 10) + o := bls.NewFQ2(bls.FQReprToFQ(o0), bls.FQReprToFQ(o1)) + a.SubAssign(b) + if !a.Equals(o) { + t.Error("FQ2 sub not working properly") + } +} + +func TestFQ2Negation(t *testing.T) { + a0, _ := bls.FQReprFromString("149598838772691373030373252224260385709104435267292980765191905060056642409050296541472646296924142271996639838151", 10) + a1, _ := bls.FQReprFromString("181009733668961021523236619221743585335449215747654796739195280796206588018924306389615593874039575085936328761563", 10) + a := bls.NewFQ2(bls.FQReprToFQ(a0), bls.FQReprToFQ(a1)) + o0, _ := bls.FQReprFromString("3852810716448976020387416573511643770847778384671714904566866231063975008081787567901214982832091521765897632721636", 10) + o1, _ := bls.FQReprFromString("3821399821552706371894553206514160571221433604191353088592862855327825062471913558053072035254976088951957943798224", 10) + o := bls.NewFQ2(bls.FQReprToFQ(o0), bls.FQReprToFQ(o1)) + a.NegAssign() + if !a.Equals(o) { + t.Error("FQ2 negation not working properly") + } +} + +func TestFQ2Doubling(t *testing.T) { + a0, _ := bls.FQReprFromString("149598838772691373030373252224260385709104435267292980765191905060056642409050296541472646296924142271996639838151", 10) + a1, _ := bls.FQReprFromString("181009733668961021523236619221743585335449215747654796739195280796206588018924306389615593874039575085936328761563", 10) + a := bls.NewFQ2(bls.FQReprToFQ(a0), bls.FQReprToFQ(a1)) + o0, _ := bls.FQReprFromString("299197677545382746060746504448520771418208870534585961530383810120113284818100593082945292593848284543993279676302", 10) + o1, _ := bls.FQReprFromString("362019467337922043046473238443487170670898431495309593478390561592413176037848612779231187748079150171872657523126", 10) + o := bls.NewFQ2(bls.FQReprToFQ(o0), bls.FQReprToFQ(o1)) + a.DoubleAssign() + if !a.Equals(o) { + t.Error("FQ2 double not working properly") + } +} + +func TestFQ2FrobeniusMap(t *testing.T) { + + a0, _ := bls.FQReprFromString("149598838772691373030373252224260385709104435267292980765191905060056642409050296541472646296924142271996639838151", 10) + a1, _ := bls.FQReprFromString("181009733668961021523236619221743585335449215747654796739195280796206588018924306389615593874039575085936328761563", 10) + a := bls.NewFQ2(bls.FQReprToFQ(a0), bls.FQReprToFQ(a1)) + o00, _ := bls.FQReprFromString("149598838772691373030373252224260385709104435267292980765191905060056642409050296541472646296924142271996639838151", 10) + o01, _ := bls.FQReprFromString("181009733668961021523236619221743585335449215747654796739195280796206588018924306389615593874039575085936328761563", 10) + o0 := bls.NewFQ2(bls.FQReprToFQ(o00), bls.FQReprToFQ(o01)) + a.FrobeniusMapAssign(0) + if !a.Equals(o0) { + t.Error("FQ2 frobenius map not working properly") + } + o10, _ := bls.FQReprFromString("149598838772691373030373252224260385709104435267292980765191905060056642409050296541472646296924142271996639838151", 10) + o11, _ := bls.FQReprFromString("3821399821552706371894553206514160571221433604191353088592862855327825062471913558053072035254976088951957943798224", 10) + o1 := bls.NewFQ2(bls.FQReprToFQ(o10), bls.FQReprToFQ(o11)) + a.FrobeniusMapAssign(1) + if !a.Equals(o1) { + t.Error("FQ2 frobenius map not working properly") + } + o20, _ := bls.FQReprFromString("149598838772691373030373252224260385709104435267292980765191905060056642409050296541472646296924142271996639838151", 10) + o21, _ := bls.FQReprFromString("181009733668961021523236619221743585335449215747654796739195280796206588018924306389615593874039575085936328761563", 10) + o2 := bls.NewFQ2(bls.FQReprToFQ(o20), bls.FQReprToFQ(o21)) + a.FrobeniusMapAssign(1) + if !a.Equals(o2) { + t.Error("FQ2 frobenius map not working properly") + } + o30, _ := bls.FQReprFromString("149598838772691373030373252224260385709104435267292980765191905060056642409050296541472646296924142271996639838151", 10) + o31, _ := bls.FQReprFromString("181009733668961021523236619221743585335449215747654796739195280796206588018924306389615593874039575085936328761563", 10) + o3 := bls.NewFQ2(bls.FQReprToFQ(o30), bls.FQReprToFQ(o31)) + a.FrobeniusMapAssign(2) + if !a.Equals(o3) { + t.Error("FQ2 frobenius map not working properly") + } +} + +func TestFQ2Sqrt(t *testing.T) { + a0, _ := bls.FQReprFromString("1199141494453035944462437764039889211157529566989597369793908631238550963749870321413115974274142904836460239708711", 10) + a1, _ := bls.FQReprFromString("2275726519012059234888434151906712303065680739818771469342471201468687921783347469538129328635084833206431018046147", 10) + a := bls.NewFQ2(bls.FQReprToFQ(a0), bls.FQReprToFQ(a1)) + o0, _ := bls.FQReprFromString("2610880034315515246135455194558180576521075490930523809216746350127301781195091608845140759868139294094360490170565", 10) + o1, _ := bls.FQReprFromString("1361064970384811010963395494397997021963412519774828006120608106648478662746078959841022528364671568910239381710674", 10) + o := bls.NewFQ2(bls.FQReprToFQ(o0), bls.FQReprToFQ(o1)) + sqrtA, success := a.Sqrt() + if !success || !sqrtA.Equals(o) { + t.Error("FQ2 sqrt not working properly") + } + a0, _ = bls.FQReprFromString("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015661931409599199851", 10) + a = bls.NewFQ2(bls.FQReprToFQ(a0), bls.FQZero) + o1, _ = bls.FQReprFromString("4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894226663331", 10) + o = bls.NewFQ2(bls.FQZero, bls.FQReprToFQ(o1)) + sqrtA, success = a.Sqrt() + if !success || !sqrtA.Equals(o) { + t.Error("FQ2 sqrt not working properly") + } +} + +func TestFQ2Legendre(t *testing.T) { + if bls.LegendreZero != bls.FQ2Zero.Legendre() { + t.Error("legendre of zero field element does not equal LegendreZero") + } + + m1 := bls.FQ2One.Copy() + m1.NegAssign() + if bls.LegendreQuadraticResidue != m1.Legendre() { + t.Error("sqrt(-1) is not quadratic residue") + } + m1.MultiplyByNonresidueAssign() + if bls.LegendreQuadraticNonResidue != m1.Legendre() { + t.Error("1.Neg().MulByNonresidue() is quadratic residue") + } +} + +func TestFQ2MulNonresidue(t *testing.T) { + nqr := bls.NewFQ2(bls.FQOne, bls.FQOne) + + for i := 0; i < 1000; i++ { + a, err := bls.RandFQ2(rand.Reader) + if err != nil { + t.Fatal(err) + } + b := a.Copy() + a.MultiplyByNonresidueAssign() + b.MulAssign(nqr) + + if !a.Equals(b) { + t.Error("a != b") + } + } +} + +func TestFQ2RandomFrobenius(t *testing.T) { + x := NewXORShift(2) + + for i := 0; i < 10; i++ { + for j := 0; j < 14; j++ { + a, _ := bls.RandFQ2(x) + b := a.Copy() + + for k := 0; k < j; k++ { + a = a.Exp(bls.QFieldModulus) + } + b.FrobeniusMapAssign(uint8(j)) + + if !a.Equals(b) { + t.Fatal("frobenius map does not match exponent") + } + } + } +} + +func TestFQ2RandomMultiplication(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ2(x) + b, _ := bls.RandFQ2(x) + c, _ := bls.RandFQ2(x) + + t0 := a.Copy() + t0.MulAssign(b) + t0.MulAssign(c) + + t1 := a.Copy() + t1.MulAssign(c) + t1.MulAssign(b) + + t2 := b.Copy() + t2.MulAssign(c) + t2.MulAssign(a) + + if !t0.Equals(t1) { + t.Fatal("expected (a*b)*c == (a*c)*b") + } + + if !t1.Equals(t2) { + t.Fatal("expected (a*c)*b == (b*c)*a") + } + } +} + +func TestFQ2RandomAddition(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ2(x) + b, _ := bls.RandFQ2(x) + c, _ := bls.RandFQ2(x) + + t0 := a.Copy() + t0.AddAssign(b) + t0.AddAssign(c) + + t1 := a.Copy() + t1.AddAssign(c) + t1.AddAssign(b) + + t2 := b.Copy() + t2.AddAssign(c) + t2.AddAssign(a) + + if !t0.Equals(t1) { + t.Fatal("expected (a+b)+c == (a+c)+b") + } + + if !t1.Equals(t2) { + t.Fatal("expected (a+c)+b == (b+c)+a") + } + } +} + +func TestFQ2RandomSubtraction(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ2(x) + b, _ := bls.RandFQ2(x) + + t0 := a.Copy() + t0.SubAssign(b) + + t1 := b.Copy() + t1.SubAssign(a) + + t2 := t0.Copy() + t2.AddAssign(t1) + + if !t2.IsZero() { + t.Fatal("expected (a - b) + (b - a) = 0") + } + } +} + +func TestFQ2RandomNegation(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ2(x) + b := a.Copy() + + b.NegAssign() + b.AddAssign(a) + + if !b.IsZero() { + t.Fatal("expected (a + ~a) = 0") + } + } +} + +func TestFQ2RandomDoubling(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ2(x) + b := a.Copy() + + a.AddAssign(b) + b.DoubleAssign() + + if !a.Equals(b) { + t.Fatal("expected 2a = 2a") + } + } +} + +func TestFQ2RandomSquaring(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ2(x) + b := a.Copy() + + a.MulAssign(b) + b.SquareAssign() + + if !a.Equals(b) { + t.Fatal("expected a^2 = a^2") + } + } +} + +func TestFQ2RandomInversion(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ2(x) + b := a.Copy() + + b.InverseAssign() + + a.MulAssign(b) + + if !a.Equals(bls.FQ2One) { + t.Fatal("expected a * a^-1 = 1") + } + } +} + +func TestFQ2RandomExpansion(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ2(x) + b, _ := bls.RandFQ2(x) + c, _ := bls.RandFQ2(x) + d, _ := bls.RandFQ2(x) + + t0 := a.Copy() + t0.AddAssign(b) + t1 := c.Copy() + t1.AddAssign(d) + t0.MulAssign(t1) + + t2 := a.Copy() + t2.MulAssign(c) + t3 := b.Copy() + t3.MulAssign(c) + t4 := a.Copy() + t4.MulAssign(d) + t5 := b.Copy() + t5.MulAssign(d) + + t2.AddAssign(t3) + t2.AddAssign(t4) + t2.AddAssign(t5) + + if !t2.Equals(t0) { + t.Fatal("(a + b)(c + d) should = (a*c + b*c + a*d + b*d)") + } + } +} + +func BenchmarkFQ2AddAssign(b *testing.B) { + type addData struct { + f1 bls.FQ2 + f2 bls.FQ2 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ2(r) + f2, _ := bls.RandFQ2(r) + inData[i] = addData{ + f1: f1, + f2: f2, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.AddAssign(inData[count].f2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQ2SubAssign(b *testing.B) { + type subData struct { + f1 bls.FQ2 + f2 bls.FQ2 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]subData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ2(r) + f2, _ := bls.RandFQ2(r) + inData[i] = subData{ + f1: f1, + f2: f2, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.SubAssign(inData[count].f2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQ2MulAssign(b *testing.B) { + type mulData struct { + f1 bls.FQ2 + f2 bls.FQ2 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]mulData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ2(r) + f2, _ := bls.RandFQ2(r) + inData[i] = mulData{ + f1: f1, + f2: f2, + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.MulAssign(inData[count].f2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQ2SquareAssign(b *testing.B) { + type squareData struct { + f1 bls.FQ2 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]squareData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ2(r) + inData[i] = squareData{ + f1: f1, + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.SquareAssign() + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQ2InverseAssign(b *testing.B) { + type invData struct { + f1 bls.FQ2 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]invData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ2(r) + inData[i] = invData{ + f1: f1, + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.InverseAssign() + count = (count + 1) % g1MulAssignSamples + } +} diff --git a/bls/fq6.go b/bls/fq6.go new file mode 100644 index 0000000..1d948a3 --- /dev/null +++ b/bls/fq6.go @@ -0,0 +1,368 @@ +package bls + +import ( + "fmt" + "io" +) + +// FQ6 is an element of FQ6 represented by c0 + c1*v + v2*v**2 +type FQ6 struct { + c0 FQ2 + c1 FQ2 + c2 FQ2 +} + +// NewFQ6 creates a new FQ6 element. +func NewFQ6(c0 FQ2, c1 FQ2, c2 FQ2) *FQ6 { + return &FQ6{ + c0: c0, + c1: c1, + c2: c2, + } +} + +func (f FQ6) String() string { + return fmt.Sprintf("Fq6(%s + %s*v + %s*v^2)", f.c0, f.c1, f.c2) +} + +// Copy creates a copy of the field element. +func (f FQ6) Copy() *FQ6 { + return NewFQ6(f.c0, f.c1, f.c2) +} + +// MulByNonresidueAssign multiplies by quadratic nonresidue v. +func (f *FQ6) MulByNonresidueAssign() { + f.c0, f.c1, f.c2 = f.c2, f.c0, f.c1 + f.c0.MultiplyByNonresidueAssign() +} + +// MulBy1Assign multiplies the FQ6 by an FQ2. +func (f *FQ6) MulBy1Assign(c1 FQ2) { + b := f.c1.Copy() + b.MulAssign(c1) + tmp := f.c1.Copy() + tmp.AddAssign(f.c2) + t1 := c1.Copy() + t1.MulAssign(tmp) + t1.SubAssign(b) + t1.MultiplyByNonresidueAssign() + tmp = f.c0.Copy() + tmp.AddAssign(f.c1) + t2 := c1.Copy() + t2.MulAssign(tmp) + t2.SubAssign(b) + f.c0 = t1 + f.c1 = t2 + f.c2 = b +} + +// MulBy01Assign multiplies by c0 and c1. +func (f *FQ6) MulBy01Assign(c0 FQ2, c1 FQ2) { + a := f.c0.Copy() + a.MulAssign(c0) + b := f.c1.Copy() + b.MulAssign(c1) + + tmp := f.c1.Copy() + tmp.AddAssign(f.c2) + t1 := c1.Copy() + t1.MulAssign(tmp) + t1.SubAssign(b) + t1.MultiplyByNonresidueAssign() + t1.AddAssign(a) + tmp = f.c0.Copy() + tmp.AddAssign(f.c2) + t3 := c0.Copy() + t3.MulAssign(tmp) + t3.SubAssign(a) + t3.AddAssign(b) + tmp = f.c0.Copy() + tmp.AddAssign(f.c1) + t2 := c0.Copy() + t2.AddAssign(c1) + t2.MulAssign(tmp) + t2.SubAssign(a) + t2.SubAssign(b) + + f.c0 = t1 + f.c1 = t2 + f.c2 = t3 +} + +// FQ6Zero represents the zero value of FQ6. +var FQ6Zero = NewFQ6(FQ2Zero, FQ2Zero, FQ2Zero) + +// FQ6One represents the one value of FQ6. +var FQ6One = NewFQ6(FQ2One, FQ2Zero, FQ2Zero) + +// Equals checks if two FQ6 elements are equal. +func (f FQ6) Equals(other *FQ6) bool { + return f.c0.Equals(other.c0) && f.c1.Equals(other.c1) && f.c2.Equals(other.c2) +} + +// IsZero checks if the FQ6 element is zero. +func (f FQ6) IsZero() bool { + return f.Equals(FQ6Zero) +} + +// DoubleAssign doubles the coefficients of the FQ6 element. +func (f *FQ6) DoubleAssign() { + f.c0.DoubleAssign() + f.c1.DoubleAssign() + f.c2.DoubleAssign() +} + +// NegAssign negates the coefficients of the FQ6 element. +func (f *FQ6) NegAssign() { + f.c0.NegAssign() + f.c1.NegAssign() + f.c2.NegAssign() +} + +// AddAssign the coefficients of the FQ6 element to another. +func (f *FQ6) AddAssign(other *FQ6) { + f.c0.AddAssign(other.c0) + f.c1.AddAssign(other.c1) + f.c2.AddAssign(other.c2) +} + +// SubAssign subtracts the coefficients of the FQ6 element from another. +func (f *FQ6) SubAssign(other *FQ6) { + f.c0.SubAssign(other.c0) + f.c1.SubAssign(other.c1) + f.c2.SubAssign(other.c2) +} + +var bigThree = NewFQRepr(3) +var bigThreeFQ = FQReprToFQ(bigThree) + +var fq2nqr = NewFQ2( + FQOne, + FQOne, +) + +var frobeniusCoeffFQ6c1 = [6]FQ2{ + // Fq2(u + 1)**(((q^0) - 1) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}), + FQReprToFQRaw(FQRepr{0, 0, 0, 0, 0, 0}), + ), + // Fq2(u + 1)**(((q^1) - 1) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + FQReprToFQRaw(FQRepr{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741}), + ), + // Fq2(u + 1)**(((q^2) - 1) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), + // Fq2(u + 1)**(((q^3) - 1) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + FQReprToFQRaw(FQRepr{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}), + ), + // Fq2(u + 1)**(((q^4) - 1) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), + // Fq2(u + 1)**(((q^5) - 1) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + FQReprToFQRaw(FQRepr{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160}), + ), +} + +var frobeniusCoeffFQ6c2 = [6]FQ2{ + // Fq2(u + 1)**(((2q^0) - 2) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0x760900000002fffd, 0xebf4000bc40c0002, 0x5f48985753c758ba, 0x77ce585370525745, 0x5c071a97a256ec6d, 0x15f65ec3fa80e493}), + FQReprToFQRaw(FQRepr{0, 0, 0, 0, 0, 0}), + ), + // Fq2(u + 1)**(((2q^1) - 2) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0x890dc9e4867545c3, 0x2af322533285a5d5, 0x50880866309b7e2c, 0xa20d1b8c7e881024, 0x14e4f04fe2db9068, 0x14e56d3f1564853a}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), + // Fq2(u + 1)**(((2q^2) - 2) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0xcd03c9e48671f071, 0x5dab22461fcda5d2, 0x587042afd3851b95, 0x8eb60ebe01bacb9e, 0x3f97d6e83d050d2, 0x18f0206554638741}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), + // Fq2(u + 1)**(((2q^3) - 2) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0x43f5fffffffcaaae, 0x32b7fff2ed47fffd, 0x7e83a49a2e99d69, 0xeca8f3318332bb7a, 0xef148d1ea0f4c069, 0x40ab3263eff0206}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), + // Fq2(u + 1)**(((2q^4) - 2) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0x30f1361b798a64e8, 0xf3b8ddab7ece5a2a, 0x16a8ca3ac61577f7, 0xc26a2ff874fd029b, 0x3636b76660701c6e, 0x51ba4ab241b6160}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), + // Fq2(u + 1)**(((2q^5) - 2) / 3) + NewFQ2( + FQReprToFQRaw(FQRepr{0xecfb361b798dba3a, 0xc100ddb891865a2c, 0xec08ff1232bda8e, 0xd5c13cc6f1ca4721, 0x47222a47bf7b5c04, 0x110f184e51c5f59}), + FQReprToFQRaw(FQRepr{0x0, 0x0, 0x0, 0x0, 0x0, 0x0}), + ), +} + +// FrobeniusMapAssign runs the frobenius map algorithm with a certain power. +func (f *FQ6) FrobeniusMapAssign(power uint8) { + f.c0.FrobeniusMapAssign(power) + f.c1.FrobeniusMapAssign(power) + f.c2.FrobeniusMapAssign(power) + + f.c1.MulAssign(frobeniusCoeffFQ6c1[power%6]) + f.c2.MulAssign(frobeniusCoeffFQ6c2[power%6]) +} + +// SquareAssign squares the FQ6 element. +func (f *FQ6) SquareAssign() { + s0 := f.c0.Copy() + s0.SquareAssign() + ab := f.c0.Copy() + ab.MulAssign(f.c1) + s1 := ab.Copy() + s1.DoubleAssign() + s2 := f.c0.Copy() + s2.SubAssign(f.c1) + s2.AddAssign(f.c2) + s2.SquareAssign() + bc := f.c1.Copy() + bc.MulAssign(f.c2) + s3 := bc.Copy() + s3.DoubleAssign() + s4 := f.c2.Copy() + s4.SquareAssign() + + f.c0 = s3.Copy() + f.c0.MultiplyByNonresidueAssign() + f.c0.AddAssign(s0) + + f.c1 = s4.Copy() + f.c1.MultiplyByNonresidueAssign() + f.c1.AddAssign(s1) + + f.c2 = s1.Copy() + f.c2.AddAssign(s2) + f.c2.AddAssign(s3) + f.c2.SubAssign(s0) + f.c2.SubAssign(s4) +} + +// MulAssign multiplies two FQ6 elements together. +func (f *FQ6) MulAssign(other *FQ6) { + aa := f.c0.Copy() + aa.MulAssign(other.c0) + bb := f.c1.Copy() + bb.MulAssign(other.c1) + cc := f.c2.Copy() + cc.MulAssign(other.c2) + + tmp := f.c1.Copy() + tmp.AddAssign(f.c2) + t1 := other.c1.Copy() + t1.AddAssign(other.c2) + t1.MulAssign(tmp) + t1.SubAssign(bb) + t1.SubAssign(cc) + t1.MultiplyByNonresidueAssign() + t1.AddAssign(aa) + + tmp = f.c0.Copy() + tmp.AddAssign(f.c2) + f.c2 = other.c0.Copy() + f.c2.AddAssign(other.c2) + f.c2.MulAssign(tmp) + f.c2.SubAssign(aa) + f.c2.AddAssign(bb) + f.c2.SubAssign(cc) + tmp = f.c0.Copy() + tmp.AddAssign(f.c1) + f.c1 = other.c0.Copy() + f.c1.AddAssign(other.c1) + f.c1.MulAssign(tmp) + f.c1.SubAssign(aa) + f.c1.SubAssign(bb) + cc.MultiplyByNonresidueAssign() + f.c1.AddAssign(cc) + + f.c0 = t1 +} + +// InverseAssign finds the inverse of the FQ6 element. +func (f *FQ6) InverseAssign() bool { + c0 := f.c2.Copy() + c0.MultiplyByNonresidueAssign() + c0.MulAssign(f.c1) + c0.NegAssign() + c0s := f.c0.Copy() + c0s.SquareAssign() + c0.AddAssign(c0s) + c1 := f.c2.Copy() + c1.SquareAssign() + c1.MultiplyByNonresidueAssign() + c0c1 := f.c0.Copy() + c0c1.MulAssign(f.c1) + c0c2 := f.c0.Copy() + c0c2.MulAssign(f.c2) + c1c2 := f.c1.Copy() + c1c2.MulAssign(f.c2) + c1.SubAssign(c0c1) + c2 := f.c1.Copy() + c2.SquareAssign() + c2.SubAssign(c0c2) + + tmp1 := f.c2.Copy() + tmp1.MulAssign(c1) + tmp2 := f.c1.Copy() + tmp2.MulAssign(c2) + tmp1.AddAssign(tmp2) + tmp1.MultiplyByNonresidueAssign() + tmp2 = f.c0.Copy() + tmp2.MulAssign(c0) + tmp1.AddAssign(tmp2) + if !tmp1.InverseAssign() { + return false + } + f.c0 = tmp1.Copy() + f.c0.MulAssign(c0) + f.c1 = tmp1.Copy() + f.c1.MulAssign(c1) + f.c2 = tmp1.Copy() + f.c2.MulAssign(c2) + return true +} + +// Exp raises the element ot a specific power. +func (f FQ6) Exp(n *FQRepr) *FQ6 { + nCopy := n.Copy() + res := FQ6One.Copy() + fi := f.Copy() + for nCopy.Cmp(bigZero) != 0 { + if !isEven(nCopy) { + res.MulAssign(fi) + } + fi.MulAssign(fi) + nCopy.Rsh(1) + } + return res +} + +// RandFQ6 generates a random FQ6 element. +func RandFQ6(reader io.Reader) (*FQ6, error) { + c0, err := RandFQ2(reader) + if err != nil { + return nil, err + } + c1, err := RandFQ2(reader) + if err != nil { + return nil, err + } + c2, err := RandFQ2(reader) + if err != nil { + return nil, err + } + return NewFQ6(c0, c1, c2), nil +} diff --git a/bls/fq6_test.go b/bls/fq6_test.go new file mode 100644 index 0000000..a67a0aa --- /dev/null +++ b/bls/fq6_test.go @@ -0,0 +1,391 @@ +package bls_test + +import ( + "crypto/rand" + "testing" + + "github.com/phoreproject/bls" +) + +func TestFQ6MultiplyByNonresidue(t *testing.T) { + nqr := bls.NewFQ6(bls.FQ2Zero, bls.FQ2One, bls.FQ2Zero) + + for i := 0; i < 1000; i++ { + a, err := bls.RandFQ6(rand.Reader) + if err != nil { + t.Fatal(err) + } + b := a.Copy() + b.MulAssign(nqr) + a.MulByNonresidueAssign() + if !a.Equals(b) { + t.Fatal("FQ6.MulByNonresidue not working properly") + } + } +} + +func TestFQ6MultiplyBy1(t *testing.T) { + for i := 0; i < 1000; i++ { + c1, err := bls.RandFQ2(rand.Reader) + if err != nil { + t.Fatal(err) + } + a, err := bls.RandFQ6(rand.Reader) + if err != nil { + t.Fatal(err) + } + b := a.Copy() + b.MulAssign(bls.NewFQ6(bls.FQ2Zero, c1, bls.FQ2Zero)) + a.MulBy1Assign(c1) + + if !a.Equals(b) { + t.Fatal("FQ6.MulBy1 not working") + } + } +} + +func TestFQ6MultiplyBy01(t *testing.T) { + for i := 0; i < 1000; i++ { + c0, err := bls.RandFQ2(rand.Reader) + if err != nil { + t.Fatal(err) + } + c1, err := bls.RandFQ2(rand.Reader) + if err != nil { + t.Fatal(err) + } + a, err := bls.RandFQ6(rand.Reader) + if err != nil { + t.Fatal(err) + } + b := a.Copy() + b.MulAssign(bls.NewFQ6(c0, c1, bls.FQ2Zero)) + a.MulBy01Assign(c0, c1) + + if !a.Equals(b) { + t.Fatal("FQ6.MulBy1 not working") + } + } +} + +// TODO: FQ6 frob is broken + +//func TestFQ6RandomFrobenius(t *testing.T) { +// x := NewXORShift(2) +// +// for i := 0; i < 10; i++ { +// for j := 0; j < 14; j++ { +// a, _ := bls.RandFQ6(x) +// b := a.Copy() +// +// for k := 0; k < j; k++ { +// a = a.Exp(bls.QFieldModulus) +// } +// b.FrobeniusMapAssign(uint8(j)) +// +// if !a.Equals(b) { +// t.Fatal("frobenius map does not match exponent") +// } +// } +// } +//} + +func TestFQ6RandomMultiplication(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ6(x) + b, _ := bls.RandFQ6(x) + c, _ := bls.RandFQ6(x) + + t0 := a.Copy() + t0.MulAssign(b) + t0.MulAssign(c) + + t1 := a.Copy() + t1.MulAssign(c) + t1.MulAssign(b) + + t2 := b.Copy() + t2.MulAssign(c) + t2.MulAssign(a) + + if !t0.Equals(t1) { + t.Fatal("expected (a*b)*c == (a*c)*b") + } + + if !t1.Equals(t2) { + t.Fatal("expected (a*c)*b == (b*c)*a") + } + } +} + +func TestFQ6RandomAddition(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ6(x) + b, _ := bls.RandFQ6(x) + c, _ := bls.RandFQ6(x) + + t0 := a.Copy() + t0.AddAssign(b) + t0.AddAssign(c) + + t1 := a.Copy() + t1.AddAssign(c) + t1.AddAssign(b) + + t2 := b.Copy() + t2.AddAssign(c) + t2.AddAssign(a) + + if !t0.Equals(t1) { + t.Fatal("expected (a+b)+c == (a+c)+b") + } + + if !t1.Equals(t2) { + t.Fatal("expected (a+c)+b == (b+c)+a") + } + } +} + +func TestFQ6RandomSubtraction(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ6(x) + b, _ := bls.RandFQ6(x) + + t0 := a.Copy() + t0.SubAssign(b) + + t1 := b.Copy() + t1.SubAssign(a) + + t2 := t0.Copy() + t2.AddAssign(t1) + + if !t2.IsZero() { + t.Fatal("expected (a - b) + (b - a) = 0") + } + } +} + +func TestFQ6RandomNegation(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ6(x) + b := a.Copy() + + b.NegAssign() + b.AddAssign(a) + + if !b.IsZero() { + t.Fatal("expected (a + ~a) = 0") + } + } +} + +func TestFQ6RandomDoubling(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ6(x) + b := a.Copy() + + a.AddAssign(b) + b.DoubleAssign() + + if !a.Equals(b) { + t.Fatal("expected 2a = 2a") + } + } +} + +func TestFQ6RandomSquaring(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ6(x) + b := a.Copy() + + a.MulAssign(b) + b.SquareAssign() + + if !a.Equals(b) { + t.Fatal("expected a^2 = a^2") + } + } +} + +func TestFQ6RandomInversion(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ6(x) + b := a.Copy() + + b.InverseAssign() + + a.MulAssign(b) + + if !a.Equals(bls.FQ6One) { + t.Fatal("expected a * a^-1 = 1") + } + } +} + +func TestFQ6RandomExpansion(t *testing.T) { + x := NewXORShift(3) + + for i := 0; i < 10000; i++ { + a, _ := bls.RandFQ6(x) + b, _ := bls.RandFQ6(x) + c, _ := bls.RandFQ6(x) + d, _ := bls.RandFQ6(x) + + t0 := a.Copy() + t0.AddAssign(b) + t1 := c.Copy() + t1.AddAssign(d) + t0.MulAssign(t1) + + t2 := a.Copy() + t2.MulAssign(c) + t3 := b.Copy() + t3.MulAssign(c) + t4 := a.Copy() + t4.MulAssign(d) + t5 := b.Copy() + t5.MulAssign(d) + + t2.AddAssign(t3) + t2.AddAssign(t4) + t2.AddAssign(t5) + + if !t2.Equals(t0) { + t.Fatal("(a + b)(c + d) should = (a*c + b*c + a*d + b*d)") + } + } +} + +func BenchmarkFQ6AddAssign(b *testing.B) { + type addData struct { + f1 *bls.FQ6 + f2 *bls.FQ6 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ6(r) + f2, _ := bls.RandFQ6(r) + inData[i] = addData{ + f1: f1, + f2: f2, + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.AddAssign(inData[count].f2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQ6SubAssign(b *testing.B) { + type addData struct { + f1 *bls.FQ6 + f2 *bls.FQ6 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ6(r) + f2, _ := bls.RandFQ6(r) + inData[i] = addData{ + f1: f1, + f2: f2, + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.SubAssign(inData[count].f2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQ6MulAssign(b *testing.B) { + type addData struct { + f1 *bls.FQ6 + f2 *bls.FQ6 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ6(r) + f2, _ := bls.RandFQ6(r) + inData[i] = addData{ + f1: f1, + f2: f2, + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.MulAssign(inData[count].f2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQ6SquareAssign(b *testing.B) { + type addData struct { + f1 *bls.FQ6 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ6(r) + inData[i] = addData{ + f1: f1, + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.SquareAssign() + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQ6InverseAssign(b *testing.B) { + type addData struct { + f1 *bls.FQ6 + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ6(r) + inData[i] = addData{ + f1: f1, + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.InverseAssign() + count = (count + 1) % g1MulAssignSamples + } +} diff --git a/bls/fq_test.go b/bls/fq_test.go new file mode 100644 index 0000000..f287656 --- /dev/null +++ b/bls/fq_test.go @@ -0,0 +1,401 @@ +package bls_test + +import ( + "crypto/rand" + "math/big" + "testing" + + "github.com/phoreproject/bls" +) + +const TestSamples = 1000 + +func TestFQFromString(t *testing.T) { + r := NewXORShift(1) + + for i := 0; i < TestSamples; i++ { + n, _ := rand.Int(r, bls.QFieldModulus.ToBig()) + s := n.String() + + f, err := bls.FQReprFromString(s, 10) + if err != nil { + t.Fatal(err) + } + if f.ToBig().Cmp(n) != 0 { + t.Fatalf("big number does not match FQ (expected %x, got %s)", n, f) + } + } +} + +func TestFQOneZero(t *testing.T) { + if bls.FQOne.ToRepr().ToBig().Cmp(big.NewInt(1)) != 0 { + t.Errorf("one does not equal 1. (expected: 1, actual: %s)", bls.FQOne.ToRepr().ToBig()) + } + if bls.FQZero.ToRepr().ToBig().Cmp(big.NewInt(0)) != 0 { + t.Errorf("one does not equal 0. (expected: 0, actual: %s)", bls.FQZero.ToRepr().ToBig()) + } +} + +func TestFQCopy(t *testing.T) { + r := NewXORShift(1) + + for i := 0; i < TestSamples; i++ { + n, _ := rand.Int(r, bls.QFieldModulus.ToBig()) + + f, err := bls.FQReprFromBigInt(n) + if err != nil { + t.Fatal(err) + } + + fCopy := f.Copy() + fCopy.Div2() + + if f.Equals(fCopy) { + t.Fatal("copy doesn't work") + } + } +} + +var QFieldModulusBig = bls.QFieldModulus.ToBig() + +func TestAddAssign(t *testing.T) { + r := NewXORShift(1) + total := big.NewInt(0) + totalFQ := bls.FQReprToFQ(bls.NewFQRepr(0)) + + for i := 0; i < TestSamples; i++ { + n, _ := rand.Int(r, bls.QFieldModulus.ToBig()) + + f, err := bls.FQReprFromBigInt(n) + if err != nil { + t.Fatal(err) + } + + totalFQ.AddAssign(bls.FQReprToFQ(f)) + total.Add(total, n) + total.Mod(total, QFieldModulusBig) + + if totalFQ.ToRepr().ToBig().Cmp(total) != 0 { + t.Error("addition totals do not match between big int and FQ") + } + } +} + +func TestMulAssign(t *testing.T) { + r := NewXORShift(1) + total := big.NewInt(0) + totalFQ := bls.FQReprToFQ(bls.NewFQRepr(0)) + + for i := 0; i < TestSamples; i++ { + n, _ := rand.Int(r, bls.QFieldModulus.ToBig()) + + f, err := bls.FQReprFromBigInt(n) + if err != nil { + t.Fatal(err) + } + + totalFQ.MulAssign(bls.FQReprToFQ(f)) + total.Mul(total, n) + total.Mod(total, QFieldModulusBig) + + if totalFQ.ToRepr().ToBig().Cmp(total) != 0 { + t.Error("multiplication totals do not match between big int and FQ") + } + } +} + +func TestSubAssign(t *testing.T) { + r := NewXORShift(1) + total := big.NewInt(0) + totalFQ := bls.FQReprToFQ(bls.NewFQRepr(0)) + + for i := 0; i < TestSamples; i++ { + n, _ := rand.Int(r, bls.QFieldModulus.ToBig()) + + f, err := bls.FQReprFromBigInt(n) + if err != nil { + t.Fatal(err) + } + + totalFQ.SubAssign(bls.FQReprToFQ(f)) + total.Sub(total, n) + total.Mod(total, QFieldModulusBig) + + if totalFQ.ToRepr().ToBig().Cmp(total) != 0 { + t.Fatal("subtraction totals do not match between big int and FQ") + } + } +} + +func TestSquare(t *testing.T) { + // r := NewXORShift(1) + total := big.NewInt(2398928) + totalFQ := bls.FQReprToFQ(bls.NewFQRepr(2398928)) + + for i := 0; i < TestSamples; i++ { + totalFQ.SquareAssign() + total.Mul(total, total) + total.Mod(total, QFieldModulusBig) + + if totalFQ.ToRepr().ToBig().Cmp(total) != 0 { + t.Fatal("exp totals do not match between big int and FQ") + } + } +} + +func TestExp(t *testing.T) { + r := NewXORShift(1) + total := big.NewInt(2) + totalFQ := bls.FQReprToFQ(bls.NewFQRepr(2)) + + for i := 0; i < 1; i++ { + n, _ := rand.Int(r, QFieldModulusBig) + + f, err := bls.FQReprFromBigInt(n) + if err != nil { + t.Fatal(err) + } + + totalFQ = totalFQ.Exp(f) + total.Exp(total, n, QFieldModulusBig) + + if totalFQ.ToRepr().ToBig().Cmp(total) != 0 { + t.Fatal("exp totals do not match between big int and FQ") + } + } +} + +func TestSqrt(t *testing.T) { + r := NewXORShift(1) + + for i := 0; i < 1000; i++ { + f, err := bls.RandFQ(r) + if err != nil { + t.Fatal(err) + } + + a, success := f.Sqrt() + if !success { + continue + } + a.SquareAssign() + + if !a.Equals(f) { + t.Fatal("sqrt(a)^2 != a") + } + } +} + +func TestInverse(t *testing.T) { + // r := NewXORShift(1) + + for i := 0; i < 1; i++ { + fRepr, err := bls.FQReprFromString("08aad39fba5b1d27bd5706262b1e2ee6c3da7dff5974ecbb0bee2bd75d4bc10973d8e59fd31f225247a335deb379592c", 16) + if err != nil { + t.Fatal(err) + } + + f := bls.FQReprToFQ(fRepr) + + fInv, _ := f.Inverse() + f.MulAssign(fInv) + + if !f.Equals(bls.FQOne) { + t.Fatal("a*a^-1 != 1") + } + } +} + +func BenchmarkFQAddAssign(b *testing.B) { + type addData struct { + f1 bls.FQ + f2 bls.FQ + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ(r) + f2, _ := bls.RandFQ(r) + inData[i] = addData{ + f1: f1, + f2: f2, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.AddAssign(inData[count].f2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQSubAssign(b *testing.B) { + type subData struct { + f1 bls.FQ + f2 bls.FQ + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]subData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ(r) + f2, _ := bls.RandFQ(r) + inData[i] = subData{ + f1: f1, + f2: f2, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.SubAssign(inData[count].f2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQMulAssign(b *testing.B) { + type mulData struct { + f1 bls.FQ + f2 bls.FQ + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]mulData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ(r) + f2, _ := bls.RandFQ(r) + inData[i] = mulData{ + f1: f1, + f2: f2, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + g := inData[count].f1.Copy() + g.MulAssign(inData[count].f2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQMul2(b *testing.B) { + type doubleData struct { + f1 bls.FQ + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]doubleData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ(r) + inData[i] = doubleData{ + f1: f1, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.DoubleAssign() + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQSquare(b *testing.B) { + type squareData struct { + f1 bls.FQ + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]squareData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ(r) + inData[i] = squareData{ + f1: f1, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.SquareAssign() + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQInverse(b *testing.B) { + type invData struct { + f1 bls.FQ + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]invData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ(r) + inData[i] = invData{ + f1: f1, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.Inverse() + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQNegate(b *testing.B) { + type negData struct { + f1 bls.FQ + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]negData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ(r) + inData[i] = negData{ + f1: f1, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.NegAssign() + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFQSqrt(b *testing.B) { + type sqrtData struct { + f1 bls.FQ + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]sqrtData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandFQ(r) + inData[i] = sqrtData{ + f1: f1, + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].f1.Sqrt() + count = (count + 1) % g1MulAssignSamples + } +} diff --git a/bls/fqrepr.go b/bls/fqrepr.go new file mode 100644 index 0000000..c1a8fff --- /dev/null +++ b/bls/fqrepr.go @@ -0,0 +1,277 @@ +package bls + +import ( + "encoding/binary" + "errors" + "fmt" + "math/big" + "math/bits" +) + +//go:generate go run asm/asm.go -out primitivefuncs_amd64.s + +// FQRepr represents a uint384. The least significant bits are first. +type FQRepr [6]uint64 + +// IsOdd checks if the FQRepr is odd. +func (f FQRepr) IsOdd() bool { + return f[0]&1 == 1 +} + +// IsEven checks if the FQRepr is even. +func (f FQRepr) IsEven() bool { + return f[0]&1 == 0 +} + +// IsZero checks if the FQRepr is zero. +func (f FQRepr) IsZero() bool { + for _, f := range f { + if f != 0 { + return false + } + } + return true +} + +// NewFQRepr creates a new number given a uint64. +func NewFQRepr(n uint64) FQRepr { + return FQRepr{n, 0, 0, 0, 0, 0} +} + +// Rsh shifts the FQRepr right by a certain number of bits. +func (f *FQRepr) Rsh(n uint) { + if n >= 64*6 { + f[0] = 0 + f[1] = 0 + f[2] = 0 + f[3] = 0 + f[4] = 0 + f[5] = 0 + return + } + + for n >= 64 { + t := uint64(0) + for i := 5; i >= 0; i-- { + t, f[i] = f[i], t + } + n -= 64 + } + + if n > 0 { + t := uint64(0) + for i := 5; i >= 0; i-- { + t2 := f[i] << (64 - n) + f[i] >>= n + f[i] |= t + t = t2 + } + } +} + +// Div2 divides the FQRepr by 2. +func (f *FQRepr) Div2() { + t := uint64(0) + for i := 5; i >= 0; i-- { + t2 := f[i] << 63 + f[i] >>= 1 + f[i] |= t + t = t2 + } +} + +// Mul2 multiplies the FQRepr by 2. +func (f *FQRepr) Mul2() { + last := uint64(0) + for i := 0; i < 6; i++ { + tmp := f[i] >> 63 + f[i] <<= 1 + f[i] |= last + last = tmp + } +} + +// Lsh shifts the FQRepr left by a certain number of bits. +func (f *FQRepr) Lsh(n uint) { + if n >= 64*6 { + f[0] = 0 + f[1] = 0 + f[2] = 0 + f[3] = 0 + f[4] = 0 + f[5] = 0 + return + } + + for n >= 64 { + t := uint64(0) + for i := 0; i < 6; i++ { + t, f[i] = f[i], t + } + n -= 64 + } + + if n > 0 { + t := uint64(0) + for i := 0; i < 6; i++ { + t2 := f[i] >> (64 - n) + f[i] <<= n + f[i] |= t + t = t2 + } + } +} + +// AddNoCarry adds two FQReprs to another and does not handle +// carry. +func (f *FQRepr) AddNoCarry(g FQRepr) { + *f = AddNoCarry(*f, g) +} + +// SubNoBorrow subtracts two FQReprs from another and does not handle +// borrow. +func (f *FQRepr) SubNoBorrow(g FQRepr) { + *f = SubNoBorrow(*f, g) +} + +// Equals checks if two FQRepr's are equal. +func (f *FQRepr) Equals(g FQRepr) bool { + return f[0] == g[0] && f[1] == g[1] && f[2] == g[2] && f[3] == g[3] && f[4] == g[4] && f[5] == g[5] +} + +// Cmp compares two FQRepr's +func (f *FQRepr) Cmp(g FQRepr) int { + for i := 5; i >= 0; i-- { + if f[i] == g[i] { + continue + } + if f[i] > g[i] { + return 1 + } else if f[i] < g[i] { + return -1 + } + } + return 0 +} + +// Copy copies a FQRepr to a new instance and returns it. +func (f *FQRepr) Copy() FQRepr { + return *f +} + +// ToString converts the FQRepr to a string. +func (f FQRepr) String() string { + return fmt.Sprintf("%016x%016x%016x%016x%016x%016x", f[5], f[4], f[3], f[2], f[1], f[0]) +} + +// BitLen counts the number of bits the number is. +func (f FQRepr) BitLen() uint { + ret := uint(6 * 64) + for i := 5; i >= 0; i-- { + leading := uint(bits.LeadingZeros64(f[i])) + ret -= leading + if leading != 64 { + break + } + } + + return ret +} + +// FQReprFromBytes gets a new FQRepr from big-endian bytes. +func FQReprFromBytes(b [48]byte) FQRepr { + m0 := binary.BigEndian.Uint64(b[0:8]) + m1 := binary.BigEndian.Uint64(b[8:16]) + m2 := binary.BigEndian.Uint64(b[16:24]) + m3 := binary.BigEndian.Uint64(b[24:32]) + m4 := binary.BigEndian.Uint64(b[32:40]) + m5 := binary.BigEndian.Uint64(b[40:48]) + return FQRepr{m5, m4, m3, m2, m1, m0} +} + +// Bytes gets the bytes used for an FQRepr. +func (f FQRepr) Bytes() [48]byte { + var out [48]byte + binary.BigEndian.PutUint64(out[0:8], f[5]) + binary.BigEndian.PutUint64(out[8:16], f[4]) + binary.BigEndian.PutUint64(out[16:24], f[3]) + binary.BigEndian.PutUint64(out[24:32], f[2]) + binary.BigEndian.PutUint64(out[32:40], f[1]) + binary.BigEndian.PutUint64(out[40:48], f[0]) + return out +} + +// Bit checks if a bit is set (little-endian) +func (f FQRepr) Bit(n uint) bool { + return f[n/64]&(1<<(n%64)) != 0 +} + +// FQReprFromString creates a FQRepr from a string. +func FQReprFromString(s string, b uint) (FQRepr, error) { + out, valid := new(big.Int).SetString(s, int(b)) + if !valid { + return FQRepr{}, errors.New("FQRepr not valid") + } + return FQReprFromBigInt(out) +} + +func fqReprFromHexUnchecked(s string) FQRepr { + out, _ := new(big.Int).SetString(s, 16) + return fqReprFromBigIntUnchecked(out) +} + +func fqReprFromStringUnchecked(s string, b uint) FQRepr { + out, _ := new(big.Int).SetString(s, int(b)) + return fqReprFromBigIntUnchecked(out) +} + +// ToBig gets the big.Int representation of the FQRepr. +func (f FQRepr) ToBig() *big.Int { + out := big.NewInt(0) + for i := 5; i >= 0; i-- { + out.Add(out, new(big.Int).SetUint64(f[i])) + if i != 0 { + out.Lsh(out, 64) + } + } + return out +} + +var bigIntZero = big.NewInt(0) +var oneLsh64MinusOne = new(big.Int).SetUint64(0xffffffffffffffff) + +// FQReprFromBigInt create a FQRepr from a big.Int. +func FQReprFromBigInt(n *big.Int) (FQRepr, error) { + if n.BitLen() > 384 || n.Sign() == -1 { + return FQRepr{}, errors.New("invalid input string") + } + + out := new(big.Int).Set(n) + + newf := NewFQRepr(0) + i := 0 + for out.Cmp(bigIntZero) != 0 { + o := new(big.Int).And(out, oneLsh64MinusOne) + newf[i] = o.Uint64() + i++ + out.Rsh(out, 64) + } + + return newf, nil +} + +// FQReprFromBigInt create a FQRepr from a big.Int. +func fqReprFromBigIntUnchecked(n *big.Int) FQRepr { + out := new(big.Int).Set(n) + + newf := NewFQRepr(0) + i := 0 + for out.Cmp(bigIntZero) != 0 { + o := new(big.Int).And(out, oneLsh64MinusOne) + newf[i] = o.Uint64() + i++ + out.Rsh(out, 64) + } + + return newf +} diff --git a/bls/fqrepr_test.go b/bls/fqrepr_test.go new file mode 100644 index 0000000..ac05770 --- /dev/null +++ b/bls/fqrepr_test.go @@ -0,0 +1,147 @@ +package bls_test + +import ( + "crypto/rand" + "math/big" + "testing" + + "github.com/phoreproject/bls" +) + +func TestEvenOdd(t *testing.T) { + in := bls.NewFQRepr(1) + if in.IsEven() { + t.Error("1 should not be even") + } + if !in.IsOdd() { + t.Error("1 should be odd") + } + in = bls.NewFQRepr(2) + if !in.IsEven() { + t.Error("2 should be even") + } + if in.IsOdd() { + t.Error("2 should not be odd") + } +} + +func TestIsZero(t *testing.T) { + in := bls.NewFQRepr(0) + if !in.IsZero() { + t.Error("0.IsZero() should be true") + } +} + +func TestRsh(t *testing.T) { + in := bls.NewFQRepr(100) + in.Rsh(1) + if !in.Equals(bls.NewFQRepr(50)) { + t.Error("100 >> 1 should equal 50") + } + i, _ := bls.FQReprFromString("10000000000000000000000000", 10) + iRsh1, _ := bls.FQReprFromString("5000000000000000000000000", 10) + i.Rsh(1) + if !i.Equals(iRsh1) { + t.Error("10000000000000000000000000 >> 1 should equal 5000000000000000000000000") + } +} + +func TestLsh(t *testing.T) { + in := bls.NewFQRepr(100) + in.Lsh(1) + if !in.Equals(bls.NewFQRepr(200)) { + t.Error("100 << 1 should equal 200") + } + i, _ := bls.FQReprFromString("10000000000000000000000000", 10) + iRsh1, _ := bls.FQReprFromString("20000000000000000000000000", 10) + i.Lsh(1) + if !i.Equals(iRsh1) { + t.Error("10000000000000000000000000 << 1 should equal 20000000000000000000000000") + } +} + +func TestRandomMACWithCarry(t *testing.T) { + carry := uint64(0) + carryBig := big.NewInt(0) + current := uint64(0) + currentBig := big.NewInt(0) + + r := NewXORShift(200) + + for i := 0; i < TestSamples; i++ { + a, _ := rand.Int(r, new(big.Int).SetUint64(0xffffffffffffffff)) + b, _ := rand.Int(r, new(big.Int).SetUint64(0xffffffffffffffff)) + current, carry = bls.MACWithCarry(current, a.Uint64(), b.Uint64(), carry) + + a.Mul(a, b) + currentBig.Add(currentBig, a) + currentBig.Add(currentBig, carryBig) + carryBig = new(big.Int).Rsh(currentBig, 64) + currentBig.And(currentBig, new(big.Int).SetUint64(0xffffffffffffffff)) + + if current != currentBig.Uint64() { + t.Fatal("current != currentBig") + } + + if carry != carryBig.Uint64() { + t.Fatal("current != currentBig") + } + } +} + +func TestRandomAddWithCarry(t *testing.T) { + carry := uint64(0) + carryBig := big.NewInt(0) + current := uint64(0) + currentBig := big.NewInt(0) + + r := NewXORShift(200) + + for i := 0; i < TestSamples; i++ { + a, _ := rand.Int(r, new(big.Int).SetUint64(0xffffffffffffffff)) + current, carry = bls.AddWithCarry(current, a.Uint64(), carry) + + currentBig.Add(currentBig, a) + currentBig.Add(currentBig, carryBig) + carryBig = new(big.Int).Rsh(currentBig, 64) + currentBig.And(currentBig, new(big.Int).SetUint64(0xffffffffffffffff)) + + if current != currentBig.Uint64() { + t.Fatal("current != currentBig") + } + + if carry != carryBig.Uint64() { + t.Fatal("current != currentBig") + } + } +} + +func TestRandomBytesFromBytes(t *testing.T) { + r := NewXORShift(200) + + for i := 0; i < TestSamples; i++ { + a, _ := bls.RandFQ(r) + + repr := a.ToRepr() + + reprBytes := repr.Bytes() + newRepr := bls.FQReprFromBytes(reprBytes) + + if !newRepr.Equals(repr) { + t.Fatal("FQRepr serialization/deserialization failed") + } + } +} + +func TestMontReduce(t *testing.T) { + hi := bls.FQRepr{1839480447532984087, 3926924786351924057, 16772763484671214791, 603559161728877186, 17550439636508622399, 43104129123962762} + lo := bls.FQRepr{3464588527451676294, 7118675286620626963, 8738819137066355118, 12891471781342038311, 14889512650477226038, 12078494706333498109} + + expected := bls.FQRepr{13451288730302620273, 10097742279870053774, 15949884091978425806, 5885175747529691540, 1016841820992199104, 845620083434234474} + + out := bls.FQRepr(bls.MontReduce(hi, lo)) + + if !out.Equals(expected) { + t.Fatal("mont reduce returning incorrect values") + } +} diff --git a/bls/fr.go b/bls/fr.go new file mode 100644 index 0000000..a6123f0 --- /dev/null +++ b/bls/fr.go @@ -0,0 +1,344 @@ +package bls + +import ( + "crypto/rand" + "fmt" + "hash" + "io" +) + +// FR is an element in a field. +type FR struct { + n *FRRepr +} + +// RFieldModulus is the modulus of the R field. +var RFieldModulus, _ = FRReprFromString("52435875175126190479447740508185965837690552500527637822603658699938581184513", 10) + +// IsValid checks if the element is valid. +func (f *FR) IsValid() bool { + return f.n.Cmp(RFieldModulus) < 0 +} + +func (f *FR) reduceAssign() { + if !f.IsValid() { + f.n.SubNoBorrow(RFieldModulus) + } +} + +// Copy copies an FR element. +func (f *FR) Copy() *FR { + return &FR{f.n.Copy()} +} + +// FRR is 2**256 % r used for moving numbers into Montgomery form. +var FRR, _ = FRReprFromString("10920338887063814464675503992315976177888879664585288394250266608035967270910", 10) + +// FRR2 is R^2 % r. +var FRR2, _ = FRReprFromString("3294906474794265442129797520630710739278575682199800681788903916070560242797", 10) + +// FRReprToFR gets a pointer to a FR given a pointer +// to an FRRepr +func FRReprToFR(o *FRRepr) *FR { + r := &FR{n: o} + if r.IsValid() { + r.MulAssign(&FR{FRR2}) + return r + } + return nil +} + +// AddAssign multiplies a field element by this one. +func (f *FR) AddAssign(other *FR) { + f.n.AddNoCarry(other.n) + f.reduceAssign() +} + +const montInvFR = uint64(0xfffffffeffffffff) + +func (f *FR) montReduce(r0 uint64, r1 uint64, r2 uint64, r3 uint64, r4 uint64, r5 uint64, r6 uint64, r7 uint64) { + k := r0 * montInvFR + _, carry := MACWithCarry(r0, k, RFieldModulus[0], 0) + r1, carry = MACWithCarry(r1, k, RFieldModulus[1], carry) + r2, carry = MACWithCarry(r2, k, RFieldModulus[2], carry) + r3, carry = MACWithCarry(r3, k, RFieldModulus[3], carry) + r4, carry = AddWithCarry(r4, 0, carry) + carry2 := carry + k = r1 * montInvFR + _, carry = MACWithCarry(r1, k, RFieldModulus[0], 0) + r2, carry = MACWithCarry(r2, k, RFieldModulus[1], carry) + r3, carry = MACWithCarry(r3, k, RFieldModulus[2], carry) + r4, carry = MACWithCarry(r4, k, RFieldModulus[3], carry) + r5, carry = AddWithCarry(r5, carry2, carry) + carry2 = carry + k = r2 * montInvFR + _, carry = MACWithCarry(r2, k, RFieldModulus[0], 0) + r3, carry = MACWithCarry(r3, k, RFieldModulus[1], carry) + r4, carry = MACWithCarry(r4, k, RFieldModulus[2], carry) + r5, carry = MACWithCarry(r5, k, RFieldModulus[3], carry) + r6, carry = AddWithCarry(r6, carry2, carry) + carry2 = carry + k = r3 * montInvFR + _, carry = MACWithCarry(r3, k, RFieldModulus[0], 0) + r4, carry = MACWithCarry(r4, k, RFieldModulus[1], carry) + r5, carry = MACWithCarry(r5, k, RFieldModulus[2], carry) + r6, carry = MACWithCarry(r6, k, RFieldModulus[3], carry) + r7, _ = AddWithCarry(r7, carry2, carry) + f.n[0] = r4 + f.n[1] = r5 + f.n[2] = r6 + f.n[3] = r7 + f.reduceAssign() +} + +// MulAssign multiplies a field element by this one. +func (f FR) MulAssign(other *FR) { + r0, carry := MACWithCarry(0, f.n[0], other.n[0], 0) + r1, carry := MACWithCarry(0, f.n[0], other.n[1], carry) + r2, carry := MACWithCarry(0, f.n[0], other.n[2], carry) + r3, carry := MACWithCarry(0, f.n[0], other.n[3], carry) + r4 := carry + r1, carry = MACWithCarry(r1, f.n[1], other.n[0], 0) + r2, carry = MACWithCarry(r2, f.n[1], other.n[1], carry) + r3, carry = MACWithCarry(r3, f.n[1], other.n[2], carry) + r4, carry = MACWithCarry(r4, f.n[1], other.n[3], carry) + r5 := carry + r2, carry = MACWithCarry(r2, f.n[2], other.n[0], 0) + r3, carry = MACWithCarry(r3, f.n[2], other.n[1], carry) + r4, carry = MACWithCarry(r4, f.n[2], other.n[2], carry) + r5, carry = MACWithCarry(r5, f.n[2], other.n[3], carry) + r6 := carry + r3, carry = MACWithCarry(r3, f.n[3], other.n[0], 0) + r4, carry = MACWithCarry(r4, f.n[3], other.n[1], carry) + r5, carry = MACWithCarry(r5, f.n[3], other.n[2], carry) + r6, carry = MACWithCarry(r6, f.n[3], other.n[3], carry) + r7 := carry + f.montReduce(r0, r1, r2, r3, r4, r5, r6, r7) +} + +// SubAssign subtracts a field element from this one. +func (f *FR) SubAssign(other *FR) { + if other.n.Cmp(f.n) > 0 { + f.n.AddNoCarry(RFieldModulus) + } + f.n.SubNoBorrow(other.n) +} + +var frOne = NewFRRepr(1) +var frZero = NewFRRepr(0) +var bigOneFR = FRReprToFR(frOne) +var bigZeroFR = FRReprToFR(frZero) + +// Exp raises the element to a specific power. +func (f *FR) Exp(n *FRRepr) *FR { + nCopy := n.Copy() + fi := f.Copy() + fNew := bigOneFR.Copy() + for nCopy.Cmp(frZero) != 0 { + if nCopy.IsOdd() { + fNew.MulAssign(fi) + } + fi.SquareAssign() + nCopy.Rsh(1) + } + return fNew +} + +// Equals checks equality of two field elements. +func (f FR) Equals(other *FR) bool { + return f.n.Equals(other.n) +} + +// NegAssign gets the negative value of the field element mod RFieldModulus. +func (f *FR) NegAssign() { + if !f.IsZero() { + tmp := RFieldModulus.Copy() + tmp.SubNoBorrow(f.n) + f.n = tmp + } +} + +func (f FR) String() string { + return fmt.Sprintf("FR(0x%s)", f.ToRepr().String()) +} + +// Cmp compares this field element to another. +func (f FR) Cmp(other *FR) int { + return f.ToRepr().Cmp(other.ToRepr()) +} + +// DoubleAssign doubles the element +func (f *FR) DoubleAssign() { + f.n.Mul2() + f.reduceAssign() +} + +// IsZero checks if the field element is zero. +func (f FR) IsZero() bool { + return f.n.Cmp(frZero) == 0 +} + +// SquareAssign squares a field element. +func (f *FR) SquareAssign() { + r1, carry := MACWithCarry(0, f.n[0], f.n[1], 0) + r2, carry := MACWithCarry(0, f.n[0], f.n[2], carry) + r3, carry := MACWithCarry(0, f.n[0], f.n[3], carry) + r4 := carry + r3, carry = MACWithCarry(r3, f.n[1], f.n[2], 0) + r4, carry = MACWithCarry(r4, f.n[1], f.n[3], carry) + r5 := carry + r5, carry = MACWithCarry(r5, f.n[2], f.n[3], 0) + r6 := carry + r7 := r6 >> 63 + r6 = (r6 << 1) | (r5 >> 63) + r5 = (r5 << 1) | (r4 >> 63) + r4 = (r4 << 1) | (r3 >> 63) + r3 = (r3 << 1) | (r2 >> 63) + r2 = (r2 << 1) | (r1 >> 63) + r1 = r1 << 1 + + carry = 0 + r0, carry := MACWithCarry(0, f.n[0], f.n[0], carry) + r1, carry = AddWithCarry(r1, 0, carry) + r2, carry = MACWithCarry(r2, f.n[1], f.n[1], carry) + r3, carry = AddWithCarry(r3, 0, carry) + r4, carry = MACWithCarry(r4, f.n[2], f.n[2], carry) + r5, carry = AddWithCarry(r5, 0, carry) + r6, carry = MACWithCarry(r6, f.n[3], f.n[3], carry) + r7, carry = AddWithCarry(r7, 0, carry) + f.montReduce(r0, r1, r2, r3, r4, r5, r6, r7) +} + +// Sqrt calculates the square root of the field element. +func (f FR) Sqrt() *FR { + // TODO: fixme + return FRReprToFR(frOne) +} + +// Inverse finds the inverse of the field element. +func (f FR) Inverse() *FR { + if f.IsZero() { + return nil + } + u := f.n.Copy() + v := RFieldModulus.Copy() + b := &FR{FRR2.Copy()} + c := bigZeroFR.Copy() + + one := &FRRepr{1, 0, 0, 0} + for u.Cmp(one) != 0 && v.Cmp(one) != 0 { + for u.IsEven() { + u.Div2() + if b.n.IsEven() { + b.n.Div2() + } else { + b.n.AddNoCarry(RFieldModulus) + b.n.Div2() + } + } + + for v.IsEven() { + v.Div2() + if c.n.IsEven() { + c.n.Div2() + } else { + c.n.AddNoCarry(RFieldModulus) + c.n.Div2() + } + } + + if u.Cmp(v) >= 0 { + u.SubNoBorrow(v) + b.SubAssign(c) + } else { + v.SubNoBorrow(u) + c.SubAssign(b) + } + } + if u.Equals(one) { + return b + } + return c +} + +// Parity checks if the point is greater than the point negated. +func (f FR) Parity() bool { + neg := f.Copy() + neg.NegAssign() + return f.Cmp(neg) > 0 +} + +// MulBits multiplies the number by a big number. +func (f FR) MulBits(b *FRRepr) *FR { + res := bigZeroFR.Copy() + for i := uint(0); i < b.BitLen(); i++ { + res.DoubleAssign() + if b.Bit(i) { + res.AddAssign(&f) + } + } + return res +} + +// MulBytes multiplies the number by some bytes. +func (f FR) MulBytes(b []byte) *FR { + res := bigZeroFR.Copy() + for i := uint(0); i < uint(len(b)*8); i++ { + res.DoubleAssign() + if b[i/8]&(1<<(i%8)) != 0 { + res.AddAssign(&f) + } + } + return res +} + +// HashFR calculates a new FR2 value based on a hash. +func HashFR(hasher hash.Hash) *FR { + digest := hasher.Sum(nil) + return bigOneFR.MulBytes(digest) +} + +var rMinus1Over2, _ = FRReprFromString("26217937587563095239723870254092982918845276250263818911301829349969290592256", 10) + +// Legendre gets the legendre symbol of the element. +func (f *FR) Legendre() LegendreSymbol { + o := f.Exp(rMinus1Over2) + if o.IsZero() { + return LegendreZero + } else if o.Equals(bigOneFR) { + return LegendreQuadraticResidue + } else { + return LegendreQuadraticNonResidue + } +} + +// ToRepr gets the 256-bit representation of the field element. +func (f *FR) ToRepr() *FRRepr { + out := f.Copy() + out.montReduce( + f.n[0], + f.n[1], + f.n[2], + f.n[3], + 0, + 0, + 0, + 0, + ) + return out.n +} + +// Bytes gets the representation of the FR in bytes. +func (f *FR) Bytes() [32]byte { + return f.ToRepr().Bytes() +} + +// RandFR generates a random FR element. +func RandFR(reader io.Reader) (*FR, error) { + r, err := rand.Int(reader, RFieldModulus.ToBig()) + if err != nil { + return nil, err + } + b, _ := FRReprFromBigInt(r) + return FRReprToFR(b), nil +} diff --git a/bls/fr_test.go b/bls/fr_test.go new file mode 100644 index 0000000..bee9829 --- /dev/null +++ b/bls/fr_test.go @@ -0,0 +1,18 @@ +package bls + +import ( + "crypto/rand" + "testing" +) + +func TestFRInverse(t *testing.T) { + one := FRReprToFR(&FRRepr{1, 0, 0, 0}) + for i := 0; i < 10; i++ { + newFR, _ := RandFR(rand.Reader) + inverse := newFR.Inverse() + newFR.MulAssign(inverse) + if !one.Equals(newFR) { + t.Errorf("Multiplication with inverse must be one.") + } + } +} \ No newline at end of file diff --git a/bls/frrepr.go b/bls/frrepr.go new file mode 100644 index 0000000..1ef23ec --- /dev/null +++ b/bls/frrepr.go @@ -0,0 +1,249 @@ +package bls + +import ( + "encoding/binary" + "errors" + "fmt" + "math/big" + "math/bits" +) + +// FRRepr represents a uint256. +type FRRepr [4]uint64 + +// IsOdd checks if the FRRepr is odd. +func (f FRRepr) IsOdd() bool { + return f[0]&1 == 1 +} + +// IsEven checks if the FRRepr is even. +func (f FRRepr) IsEven() bool { + return f[0]&1 == 0 +} + +// IsZero checks if the FRRepr is zero. +func (f FRRepr) IsZero() bool { + for _, f := range f { + if f != 0 { + return false + } + } + return true +} + +// NewFRRepr creates a new number given a uint64. +func NewFRRepr(n uint64) *FRRepr { + return &FRRepr{n, 0, 0, 0} +} + +// Rsh shifts the FRRepr right by a certain number of bits. +func (f *FRRepr) Rsh(n uint) { + if n >= 64*4 { + out := NewFRRepr(0) + *f = *out + return + } + + for n >= 64 { + t := uint64(0) + for i := 3; i >= 0; i-- { + t, f[i] = f[i], t + } + n -= 64 + } + + if n > 0 { + t := uint64(0) + for i := 3; i >= 0; i-- { + t2 := f[i] << (64 - n) + f[i] >>= n + f[i] |= t + t = t2 + } + } +} + +// Div2 divides the FRRepr by 2. +func (f *FRRepr) Div2() { + t := uint64(0) + for i := 3; i >= 0; i-- { + t2 := f[i] << 63 + f[i] >>= 1 + f[i] |= t + t = t2 + } +} + +// Mul2 multiplies the FRRepr by 2. +func (f *FRRepr) Mul2() { + last := uint64(0) + for i := 0; i < 4; i++ { + tmp := f[i] >> 63 + f[i] <<= 1 + f[i] |= last + last = tmp + } +} + +// Lsh shifts the FRRepr left by a certain number of bits. +func (f *FRRepr) Lsh(n uint) { + if n >= 64*4 { + out := NewFRRepr(0) + *f = *out + return + } + + for n >= 64 { + t := uint64(0) + for i := 0; i < 4; i++ { + t, f[i] = f[i], t + } + n -= 64 + } + + if n > 0 { + t := uint64(0) + for i := 0; i < 4; i++ { + t2 := f[i] >> (64 - n) + f[i] <<= n + f[i] |= t + t = t2 + } + } +} + +// AddNoCarry adds two FRReprs to another and does not handle +// carry. +func (f *FRRepr) AddNoCarry(g *FRRepr) { + carry := uint64(0) + for i := 0; i < 4; i++ { + f[i], carry = AddWithCarry(f[i], g[i], carry) + } +} + +// SubNoBorrow subtracts two FRReprs from another and does not handle +// borrow. +func (f *FRRepr) SubNoBorrow(g *FRRepr) { + borrow := uint64(0) + for i := 0; i < 4; i++ { + f[i], borrow = SubWithBorrow(f[i], g[i], borrow) + } +} + +// Equals checks if two FRRepr's are equal. +func (f *FRRepr) Equals(g *FRRepr) bool { + return f[0] == g[0] && f[1] == g[1] && f[2] == g[2] && f[3] == g[3] +} + +// Cmp compares two FRRepr's +func (f *FRRepr) Cmp(g *FRRepr) int { + for i := 3; i >= 0; i-- { + if f[i] > g[i] { + return 1 + } else if f[i] < g[i] { + return -1 + } + } + return 0 +} + +// Copy copies a FRRepr to a new instance and returns it. +func (f *FRRepr) Copy() *FRRepr { + var newBytes [4]uint64 + copy(newBytes[:], f[:]) + newf := FRRepr(newBytes) + return &newf +} + +// ToString converts the FRRepr to a string. +func (f FRRepr) String() string { + return fmt.Sprintf("%016x%016x%016x%016x", f[3], f[2], f[1], f[0]) +} + +// BitLen counts the number of bits the number is. +func (f FRRepr) BitLen() uint { + ret := uint(4 * 64) + for i := 3; i >= 0; i-- { + leading := uint(bits.LeadingZeros64(f[i])) + ret -= leading + if leading != 64 { + break + } + } + + return ret +} + +// FRReprFromBytes gets a new FRRepr from big-endian bytes. +func FRReprFromBytes(b [32]byte) *FRRepr { + m0 := binary.BigEndian.Uint64(b[0:8]) + m1 := binary.BigEndian.Uint64(b[8:16]) + m2 := binary.BigEndian.Uint64(b[16:24]) + m3 := binary.BigEndian.Uint64(b[24:32]) + return &FRRepr{m3, m2, m1, m0} +} + +// Bytes gets the bytes used for an FRRepr. +func (f FRRepr) Bytes() [32]byte { + var out [32]byte + binary.BigEndian.PutUint64(out[0:8], f[3]) + binary.BigEndian.PutUint64(out[8:16], f[2]) + binary.BigEndian.PutUint64(out[16:24], f[1]) + binary.BigEndian.PutUint64(out[24:32], f[0]) + return out +} + +// Bit checks if a bit is set (little-endian) +func (f FRRepr) Bit(n uint) bool { + return f[n/64]&(1<<(n%64)) != 0 +} + +// FRReprFromString creates a FRRepr from a string. +func FRReprFromString(s string, b uint) (*FRRepr, error) { + out, valid := new(big.Int).SetString(s, int(b)) + if !valid { + return nil, errors.New("FRRepr not valid") + } + return FRReprFromBigInt(out) +} + +// ToBig gets the big.Int representation of the FRRepr. +func (f FRRepr) ToBig() *big.Int { + out := big.NewInt(0) + for i := 3; i >= 0; i-- { + out.Add(out, new(big.Int).SetUint64(f[i])) + if i != 0 { + out.Lsh(out, 64) + } + } + return out +} + +// FRReprFromBigInt create a FRRepr from a big.Int. +func FRReprFromBigInt(n *big.Int) (*FRRepr, error) { + if n.BitLen() > 256 || n.Sign() == -1 { + return nil, errors.New("invalid input string") + } + + out := new(big.Int).Set(n) + + newf := NewFRRepr(0) + i := 0 + for out.Cmp(bigIntZero) != 0 { + o := new(big.Int).And(out, oneLsh64MinusOne) + newf[i] = o.Uint64() + i++ + out.Rsh(out, 64) + } + + return newf, nil +} + +// ToFQ converts an FRRepr to an FQ. +func (f *FRRepr) ToFQ() FQRepr { + newf := NewFQRepr(f[0]) + newf[1] = f[1] + newf[2] = f[2] + newf[3] = f[3] + return newf +} diff --git a/bls/g1.go b/bls/g1.go new file mode 100644 index 0000000..cc48c89 --- /dev/null +++ b/bls/g1.go @@ -0,0 +1,709 @@ +package bls + +import ( + "errors" + "fmt" + "io" +) + +// G1Affine is an affine point on the G1 curve. +type G1Affine struct { + x FQ + y FQ + infinity bool +} + +// NewG1Affine constructs a new G1Affine point. +func NewG1Affine(x FQ, y FQ) *G1Affine { + return &G1Affine{x: x, y: y, infinity: false} +} + +// G1AffineZero represents the point at infinity on G1. +var G1AffineZero = &G1Affine{FQZero.Copy(), FQOne.Copy(), true} + +// IETF BLS standard +var g1GeneratorX, _ = FQReprFromString("3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507", 10) +var g1GeneratorY, _ = FQReprFromString("1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569", 10) + +// BCoeff of the G1 curve. +var BCoeff = FQReprToFQRaw(FQRepr{0xaa270000000cfff3, 0x53cc0032fc34000a, 0x478fe97a6b0a807f, 0xb1d37ebee6ba24d7, 0x8ec9733bbf78ab2f, 0x9d645513d83de7e}) + +// G1AffineOne represents the point at 1 on G1. +var G1AffineOne = &G1Affine{FQReprToFQ(g1GeneratorX), FQReprToFQ(g1GeneratorY), false} + +func (g G1Affine) String() string { + if g.infinity { + return fmt.Sprintf("G1(infinity)") + } + return fmt.Sprintf("G1(x=%s, y=%s)", g.x, g.y) +} + +// Copy returns a copy of the G1Affine point. +func (g G1Affine) Copy() *G1Affine { + return &G1Affine{g.x.Copy(), g.y.Copy(), g.infinity} +} + +// IsZero checks if the point is infinity. +func (g G1Affine) IsZero() bool { + return g.infinity +} + +// NegAssign negates the point. +func (g *G1Affine) NegAssign() { + if !g.IsZero() { + g.y.NegAssign() + } +} + +// ToProjective converts an affine point to a projective one. +func (g G1Affine) ToProjective() *G1Projective { + if g.IsZero() { + return G1ProjectiveZero.Copy() + } + return NewG1Projective(g.x, g.y, FQOne) +} + +// Mul performs a EC multiply operation on the point. +func (g G1Affine) Mul(b FQRepr) *G1Projective { + res := G1ProjectiveZero.Copy() + for i := uint(0); uint(i) < b.BitLen(); i++ { + o := b.Bit(b.BitLen() - i - 1) + res = res.Double() + if o { + res = res.AddAffine(&g) + } + } + return res +} + +// MulFR performs a EC multiply operation on the point. +func (g G1Affine) MulFR(b *FRRepr) *G1Projective { + res := G1ProjectiveZero.Copy() + for i := uint(0); uint(i) < b.BitLen(); i++ { + o := b.Bit(b.BitLen() - i - 1) + res = res.Double() + if o { + res = res.AddAffine(&g) + } + } + return res +} + +// IsOnCurve checks if a point is on the G1 curve. +func (g G1Affine) IsOnCurve() bool { + if g.infinity { + return true + } + y2 := g.y.Copy() + y2.SquareAssign() + x3b := g.x.Copy() + x3b.SquareAssign() + x3b.MulAssign(g.x) + x3b.AddAssign(BCoeff) + + return y2.Equals(x3b) +} + +// GetG1PointFromX attempts to reconstruct an affine point given +// an x-coordinate. The point is not guaranteed to be in the subgroup. +// If and only if `greatest` is set will the lexicographically +// largest y-coordinate be selected. +func GetG1PointFromX(x FQ, greatest bool) *G1Affine { + x3b := x.Copy() + x3b.SquareAssign() + x3b.MulAssign(x) + x3b.AddAssign(BCoeff) + + y, success := x3b.Sqrt() + + if !success { + return nil + } + + negY := y.Copy() + negY.NegAssign() + + yVal := negY + if (y.Cmp(negY) < 0) != greatest { + yVal = y + } + return NewG1Affine(x, yVal) +} + +var frChar, _ = FRReprFromString("52435875175126190479447740508185965837690552500527637822603658699938581184513", 10) + +// IsInCorrectSubgroupAssumingOnCurve checks if the point multiplied by the +// field characteristic equals zero. +func (g G1Affine) IsInCorrectSubgroupAssumingOnCurve() bool { + tmp := g.Copy() + tmp.MulFR(frChar) + return tmp.MulFR(frChar).IsZero() +} + +// G1 cofactor = (x - 1)^2 / 3 = 76329603384216526031706109802092473003 +var g1Cofactor, _ = FQReprFromString("76329603384216526031706109802092473003", 10) + +// ScaleByCofactor scales the G1Affine point by the cofactor. +func (g G1Affine) ScaleByCofactor() *G1Projective { + return g.Mul(g1Cofactor) +} + +// Equals checks if two affine points are equal. +func (g G1Affine) Equals(other *G1Affine) bool { + return (g.infinity == other.infinity && g.infinity == true) || (g.x.Equals(other.x) && g.y.Equals(other.y)) +} + +// SerializeBytes returns the serialized bytes for the point represented. +func (g *G1Affine) SerializeBytes() [96]byte { + out := [96]byte{} + + xBytes := g.x.ToRepr().Bytes() + yBytes := g.y.ToRepr().Bytes() + + copy(out[0:48], xBytes[:]) + copy(out[48:96], yBytes[:]) + + return out +} + +// SetRawBytes sets the coords given the serialized bytes. +func (g *G1Affine) SetRawBytes(uncompressed [96]byte) { + var xBytes [48]byte + var yBytes [48]byte + copy(xBytes[:], uncompressed[0:48]) + copy(yBytes[:], uncompressed[48:96]) + g.x = FQ{ + n: FQReprFromBytes(xBytes), + } + g.y = FQ{ + n: FQReprFromBytes(yBytes), + } +} + +// DecompressG1 decompresses the big int into an affine point and checks +// if it is in the correct prime group. +func DecompressG1(b [48]byte) (*G1Affine, error) { + affine, err := DecompressG1Unchecked(b) + if err != nil { + return nil, err + } + + if !affine.IsInCorrectSubgroupAssumingOnCurve() { + return nil, errors.New("not in correct subgroup") + } + return affine, nil +} + +// DecompressG1Unchecked decompresses the big int into an affine point without +// checking if it's in the correct prime group. +func DecompressG1Unchecked(b [48]byte) (*G1Affine, error) { + var copyBytes [48]byte + copy(copyBytes[:], b[:]) + + if len(copyBytes) == 0 || copyBytes[0]&(1<<7) == 0 { + return nil, errors.New("unexpected compression mode") + } + + if copyBytes[0]&(1<<6) != 0 { + // this is the point at infinity + copyBytes[0] &= 0x3f + + for _, b := range copyBytes { + if b != 0 { + return nil, errors.New("unexpected information in compressed infinity") + } + } + + return G1AffineZero.Copy(), nil + } + greatest := copyBytes[0]&(1<<5) != 0 + + copyBytes[0] &= 0x1f + + x := FQReprFromBytes(copyBytes) + xFQ := FQReprToFQ(x) + + return GetG1PointFromX(xFQ, greatest), nil +} + +// CompressG1 compresses a G1 point into an int. +func CompressG1(affine *G1Affine) [48]byte { + res := [48]byte{} + if affine.IsZero() { + res[0] |= 1 << 6 + } else { + out0 := affine.x.ToRepr().Bytes() + + copy(res[48-len(out0):], out0[:]) + + negY := affine.y.Copy() + negY.NegAssign() + + if affine.y.Cmp(negY) > 0 { + res[0] |= 1 << 5 + } + } + + res[0] |= 1 << 7 + return res +} + +// G1Projective is a projective point on the G1 curve. +type G1Projective struct { + x FQ + y FQ + z FQ +} + +// NewG1Projective creates a new G1Projective point. +func NewG1Projective(x FQ, y FQ, z FQ) *G1Projective { + return &G1Projective{x, y, z} +} + +// G1ProjectiveZero is the point at infinity where Z = 0. +var G1ProjectiveZero = &G1Projective{FQZero.Copy(), FQOne.Copy(), FQZero.Copy()} + +// G1ProjectiveOne is the generator point on G1. +var G1ProjectiveOne = G1AffineOne.ToProjective() + +func (g G1Projective) String() string { + if g.IsZero() { + return "G1: Infinity" + } + return g.ToAffine().String() +} + +// Copy returns a copy of the G1Projective point. +func (g G1Projective) Copy() *G1Projective { + return NewG1Projective(g.x.Copy(), g.y.Copy(), g.z.Copy()) +} + +// IsZero checks if the G1Projective point is zero. +func (g G1Projective) IsZero() bool { + return g.z.IsZero() +} + +// Equal checks if two projective points are equal. +func (g G1Projective) Equal(other *G1Projective) bool { + if g.IsZero() { + return other.IsZero() + } + if other.IsZero() { + return false + } + + z1 := g.z.Copy() + z1.SquareAssign() + z2 := other.z.Copy() + z2.SquareAssign() + + tmp1 := g.x.Copy() + tmp1.MulAssign(z2) + tmp2 := other.x.Copy() + tmp2.MulAssign(z1) + if !tmp1.Equals(tmp2) { + return false + } + + z1.MulAssign(g.z) + z1.MulAssign(other.y) + z2.MulAssign(other.z) + z2.MulAssign(g.y) + + return z1.Equals(z2) +} + +// ToAffine converts a G1Projective point to affine form. +func (g G1Projective) ToAffine() *G1Affine { + if g.IsZero() { + return G1AffineZero.Copy() + } else if g.z.IsZero() { + return NewG1Affine(g.x, g.y) + } + + // nonzero so must have an inverse + zInv, _ := g.z.Inverse() + zInvSquared := zInv.Copy() + zInvSquared.SquareAssign() + x := g.x.Copy() + x.MulAssign(zInvSquared) + y := g.y.Copy() + y.MulAssign(zInvSquared) + y.MulAssign(zInv) + + return NewG1Affine(x, y) +} + +// Double performs EC doubling on the point. +func (g G1Projective) Double() *G1Projective { + if g.IsZero() { + return g.Copy() + } + + // A = x1^2 + a := g.x.Copy() + a.SquareAssign() + + // B = y1^2 + b := g.y.Copy() + b.SquareAssign() + + // C = B^2 + c := b.Copy() + c.SquareAssign() + + // D = 2*((X1+B)^2-A-C) + d := g.x.Copy() + d.AddAssign(b) + d.SquareAssign() + d.SubAssign(a) + d.SubAssign(c) + d.DoubleAssign() + + // E = 3*A + e := a.Copy() + e.DoubleAssign() + e.AddAssign(a) + + // F = E^2 + f := e.Copy() + f.SquareAssign() + + // z3 = 2*Y1*Z1 + newZ := g.z.Copy() + newZ.MulAssign(g.y) + newZ.DoubleAssign() + + // x3 = F-2*D + newX := f.Copy() + newX.SubAssign(d) + newX.SubAssign(d) + + c.DoubleAssign() + c.DoubleAssign() + c.DoubleAssign() + + newY := d.Copy() + newY.SubAssign(newX) + newY.MulAssign(e) + newY.SubAssign(c) + + return NewG1Projective(newX, newY, newZ) +} + +// Add performs an EC Add operation with another point. +func (g G1Projective) Add(other *G1Projective) *G1Projective { + if g.IsZero() { + return other.Copy() + } + if other.IsZero() { + return g.Copy() + } + + // Z1Z1 = Z1^2 + z1z1 := g.z.Copy() + z1z1.SquareAssign() + + // Z2Z2 = Z2^2 + z2z2 := other.z.Copy() + z2z2.SquareAssign() + + // U1 = X1*Z2Z2 + u1 := g.x.Copy() + u1.MulAssign(z2z2) + + // U2 = x2*Z1Z1 + u2 := other.x.Copy() + u2.MulAssign(z1z1) + + // S1 = Y1*Z2*Z2Z2 + s1 := g.y.Copy() + s1.MulAssign(other.z) + s1.MulAssign(z2z2) + + // S2 = Y2*Z1*Z1Z1 + s2 := other.y.Copy() + s2.MulAssign(g.z) + s2.MulAssign(z1z1) + + if u1.Equals(u2) && s1.Equals(s2) { + // points are equal + return g.Double() + } + + // H = U2-U1 + h := u2.Copy() + h.SubAssign(u1) + + // I = (2*H)^2 + i := h.Copy() + i.DoubleAssign() + i.SquareAssign() + + // J = H * I + j := h.Copy() + j.MulAssign(i) + + // r = 2*(S2-S1) + s2.SubAssign(s1) + s2.DoubleAssign() + + // U1 = U1*I + u1.MulAssign(i) + + // X3 = r^2 - J - 2*V + newX := s2.Copy() + newX.SquareAssign() + newX.SubAssign(j) + newX.SubAssign(u1) + newX.SubAssign(u1) + + // Y3 = r*(V - X3) - 2*S1*J + u1.SubAssign(newX) + u1.MulAssign(s2) + s1.MulAssign(j) + s1.DoubleAssign() + u1.SubAssign(s1) + + // Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H + newZ := g.z.Copy() + newZ.AddAssign(other.z) + newZ.SquareAssign() + newZ.SubAssign(z1z1) + newZ.SubAssign(z2z2) + newZ.MulAssign(h) + + return NewG1Projective(newX, u1, newZ) +} + +// AddAffine performs an EC Add operation with an affine point. +func (g G1Projective) AddAffine(other *G1Affine) *G1Projective { + if g.IsZero() { + return other.ToProjective() + } + if other.IsZero() { + return g.Copy() + } + + // Z1Z1 = Z1^2 + z1z1 := g.z.Copy() + z1z1.SquareAssign() + + // U2 = x2*Z1Z1 + u2 := other.x.Copy() + u2.MulAssign(z1z1) + + // S2 = Y2*Z1*Z1Z1 + s2 := other.y.Copy() + s2.MulAssign(g.z) + s2.MulAssign(z1z1) + + if g.x.Equals(u2) && g.y.Equals(s2) { + // points are equal + return g.Double() + } + + // H = U2-X1 + u2.SubAssign(g.x) + + // HH = H^2 + hh := u2.Copy() + hh.SquareAssign() + + // I = 4*HH + i := hh.Copy() + i.DoubleAssign() + i.DoubleAssign() + + // J = H * I + j := u2.Copy() + j.MulAssign(i) + + // r = 2*(S2-Y1) + s2.SubAssign(g.y) + s2.DoubleAssign() + + // v = X1*I + v := g.x.Copy() + v.MulAssign(i) + + // X3 = r^2 - J - 2*V + newX := s2.Copy() + newX.SquareAssign() + newX.SubAssign(j) + newX.SubAssign(v) + newX.SubAssign(v) + + // Y3 = r*(V - X3) - 2*Y1*J + newY := v.Copy() + newY.SubAssign(newX) + newY.MulAssign(s2) + i0 := g.y.Copy() + i0.MulAssign(j) + i0.DoubleAssign() + newY.SubAssign(i0) + + // Z3 = (Z1+H)^2 - Z1Z1 - HH + newZ := g.z.Copy() + newZ.AddAssign(u2) + newZ.SquareAssign() + newZ.SubAssign(z1z1) + newZ.SubAssign(hh) + + return NewG1Projective(newX, newY, newZ) +} + +// Mul performs a EC multiply operation on the point. +func (g G1Projective) Mul(b FQRepr) *G1Projective { + res := G1ProjectiveZero.Copy() + for i := uint(0); i < uint(b.BitLen()); i++ { + o := b.Bit(b.BitLen() - i - 1) + res = res.Double() + if o { + res = res.Add(&g) + } + } + return res +} + +// MulFR performs a EC multiply operation on the point. +func (g G1Projective) MulFR(b *FRRepr) *G1Projective { + res := G1ProjectiveZero.Copy() + for i := uint(0); i < b.BitLen(); i++ { + o := b.Bit(b.BitLen() - i - 1) + res = res.Double() + if o { + res = res.Add(&g) + } + } + return res +} + +// RandG1 generates a random G1 element. +func RandG1(r io.Reader) (*G1Projective, error) { + for { + b := make([]byte, 1) + _, err := r.Read(b) + if err != nil { + return nil, err + } + greatest := false + if b[0]%2 == 0 { + greatest = true + } + f, err := RandFQ(r) + if err != nil { + return nil, err + } + p := GetG1PointFromX(f, greatest) + if p == nil { + continue + } + p1 := p.ScaleByCofactor() + if !p.IsZero() { + return p1, nil + } + } +} + +var ellPARepr, _ = FQReprFromString("144698a3b8e9433d693a02c96d4982b0ea985383ee66a8d8e8981aefd881ac98936f8da0e0f97f5cf428082d584c1d", 16) +var ellPBRepr, _ = FQReprFromString("12e2908d11688030018b12e8753eee3b2016c1f0f24f4070a0b9c14fcef35ef55a23215a316ceaa5d1cc48e98e172be0", 16) + +var ellPA = FQReprToFQ(ellPARepr) +var ellPB = FQReprToFQ(ellPBRepr) + +// returns -1 if x is larger than -x otherwise 1 +func signFQ(f FQ) FQ { + if f.Cmp(FQReprToFQ(qMinus1Over2)) > 0 { + return negativeOneFQ + } + return FQOne +} + +func optimizedSWUMapHelper(t FQ) *G1Affine { + numDenCommon := negativeOneFQ.Copy() + numDenCommon.SquareAssign() + + tSquared := t.Copy() + tSquared.SquareAssign() + + t2 := tSquared.Copy() + t2.SquareAssign() + + numDenCommon.MulAssign(t2) + + negOneTimesTSquared := negativeOneFQ.Copy() + negOneTimesTSquared.MulAssign(tSquared) + + numDenCommon.AddAssign(negOneTimesTSquared) + + var x0 FQ + if numDenCommon.Equals(FQZero) { + xiA := negativeOneFQ.Copy() + xiA.MulAssign(ellPA) + x0 = ellPB.Copy() + x0.DivAssign(xiA) + } else { + ellPATimesCommon := ellPA.Copy() + ellPATimesCommon.MulAssign(numDenCommon) + numDenCommon.AddAssign(FQOne) + negEllPB := ellPB.Copy() + negEllPB.NegAssign() + x0 = negEllPB + x0.MulAssign(numDenCommon) + x0.DivAssign(ellPATimesCommon) + } + + x0Cubed := x0.Copy() + x0Cubed.SquareAssign() + x0Cubed.MulAssign(x0) + + ellPAX0 := ellPA.Copy() + ellPAX0.MulAssign(x0) + + gx0 := x0Cubed + gx0.AddAssign(ellPAX0) + gx0.AddAssign(ellPB) + + sqrtGX0, found := gx0.Sqrt() + + var x FQ + var y FQ + + if found { + // g(x0) is square, so return it's sqrt + x = x0 + y = sqrtGX0 + } else { + // g(x0) is not square, so find g(x1) = xi * t^2 * x0 + x1 := negativeOneFQ.Copy() + x1.MulAssign(tSquared) + x1.MulAssign(x0) + + ellPAX1 := ellPA.Copy() + ellPAX1.MulAssign(x1) + + gx1 := x1.Copy() + gx1.SquareAssign() + gx1.MulAssign(x1) + gx1.AddAssign(ellPAX1) + gx1.AddAssign(ellPB) + + sqrtGX1, found := gx1.Sqrt() + if !found { + panic("this should never happen") + } + + x = x1 + y = sqrtGX1 + } + + signT := signFQ(t) + signYT := signFQ(y) + + signYT.MulAssign(signT) + + y.MulAssign(signYT) + + return NewG1Affine(x, y) +} diff --git a/bls/g1_test.go b/bls/g1_test.go new file mode 100644 index 0000000..4fd366c --- /dev/null +++ b/bls/g1_test.go @@ -0,0 +1,210 @@ +package bls_test + +import ( + "testing" + + "github.com/phoreproject/bls" +) + +func TestG1Generator(t *testing.T) { + x := bls.FQZero.Copy() + i := 0 + + for { + // y^2 = x^3 + b + rhs := x.Copy() + rhs.SquareAssign() + rhs.MulAssign(x) + rhs.AddAssign(bls.BCoeff) + + y, success := rhs.Sqrt() + + if success { + negY := y.Copy() + negY.NegAssign() + pY := negY + + if y.Cmp(negY) < 0 { + pY = y + } + + p := bls.NewG1Affine(x, pY) + + if p.IsInCorrectSubgroupAssumingOnCurve() { + t.Fatal("new point should be in subgroup") + } + + g1 := p.ScaleByCofactor() + + if !g1.IsZero() { + if i != 4 { + t.Fatal("non-zero point should be 4th point") + } + + g1 := g1.ToAffine() + + if !g1.IsInCorrectSubgroupAssumingOnCurve() { + t.Fatal("point is not in correct subgroup") + } + + if !g1.Equals(bls.G1AffineOne) { + t.Fatal("point is not equal to generator point") + } + break + } + } + + i++ + x.AddAssign(bls.FQOne) + } +} + +func TestG1DoublingCorrectness(t *testing.T) { + p := bls.NewG1Projective( + bls.FQReprToFQ(bls.FQRepr{0x47fd1f891d6e8bbf, 0x79a3b0448f31a2aa, 0x81f3339e5f9968f, 0x485e77d50a5df10d, 0x4c6fcac4b55fd479, 0x86ed4d9906fb064}), + bls.FQReprToFQ(bls.FQRepr{0xd25ee6461538c65, 0x9f3bbb2ecd3719b9, 0xa06fd3f1e540910d, 0xcefca68333c35288, 0x570c8005f8573fa6, 0x152ca696fe034442}), + bls.FQOne, + ) + + newP := p.Double() + + expectedP := bls.NewG1Affine( + bls.FQReprToFQ(bls.FQRepr{0xf939ddfe0ead7018, 0x3b03942e732aecb, 0xce0e9c38fdb11851, 0x4b914c16687dcde0, 0x66c8baf177d20533, 0xaf960cff3d83833}), + bls.FQReprToFQ(bls.FQRepr{0x3f0675695f5177a8, 0x2b6d82ae178a1ba0, 0x9096380dd8e51b11, 0x1771a65b60572f4e, 0x8b547c1313b27555, 0x135075589a687b1e}), + ) + + if !newP.ToAffine().Equals(expectedP) { + t.Fatal("doubling is incorrect") + } +} + +func TestG1AdditionCorrectness(t *testing.T) { + p1 := bls.NewG1Projective( + bls.FQReprToFQ(bls.FQRepr{0x47fd1f891d6e8bbf, 0x79a3b0448f31a2aa, 0x81f3339e5f9968f, 0x485e77d50a5df10d, 0x4c6fcac4b55fd479, 0x86ed4d9906fb064}), + bls.FQReprToFQ(bls.FQRepr{0xd25ee6461538c65, 0x9f3bbb2ecd3719b9, 0xa06fd3f1e540910d, 0xcefca68333c35288, 0x570c8005f8573fa6, 0x152ca696fe034442}), + bls.FQOne, + ) + + p2 := bls.NewG1Projective( + bls.FQReprToFQ(bls.FQRepr{0xeec78f3096213cbf, 0xa12beb1fea1056e6, 0xc286c0211c40dd54, 0x5f44314ec5e3fb03, 0x24e8538737c6e675, 0x8abd623a594fba8}), + bls.FQReprToFQ(bls.FQRepr{0x6b0528f088bb7044, 0x2fdeb5c82917ff9e, 0x9a5181f2fac226ad, 0xd65104c6f95a872a, 0x1f2998a5a9c61253, 0xe74846154a9e44}), + bls.FQOne, + ) + + newP := p1.Add(p2).ToAffine() + + expectedP := bls.NewG1Affine( + bls.FQReprToFQ(bls.FQRepr{0x6dd3098f22235df, 0xe865d221c8090260, 0xeb96bb99fa50779f, 0xc4f9a52a428e23bb, 0xd178b28dd4f407ef, 0x17fb8905e9183c69}), + bls.FQReprToFQ(bls.FQRepr{0xd0de9d65292b7710, 0xf6a05f2bcf1d9ca7, 0x1040e27012f20b64, 0xeec8d1a5b7466c58, 0x4bc362649dce6376, 0x430cbdc5455b00a}), + ) + + if !newP.Equals(expectedP) { + t.Fatal("addition is incorrect") + } +} + +type XORShift struct { + state uint64 +} + +func NewXORShift(state uint64) *XORShift { + return &XORShift{state} +} + +func (xor *XORShift) Read(b []byte) (int, error) { + for i := range b { + x := xor.state + x ^= x << 13 + x ^= x >> 7 + x ^= x << 17 + b[i] = uint8(x) + xor.state = x + } + return len(b), nil +} + +const g1MulAssignSamples = 200 + +func BenchmarkG1MulAssign(b *testing.B) { + type mulData struct { + g *bls.G1Projective + f *bls.FR + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]mulData{} + for i := 0; i < g1MulAssignSamples; i++ { + gx, _ := bls.RandFQ(r) + gy, _ := bls.RandFQ(r) + gz, _ := bls.RandFQ(r) + randFR, _ := bls.RandFR(r) + inData[i] = mulData{ + g: bls.NewG1Projective(gx, gy, gz), + f: randFR, + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].g.Mul(inData[count].f.ToRepr().ToFQ()) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkG1AddAssign(b *testing.B) { + type addData struct { + g1 *bls.G1Projective + g2 *bls.G1Projective + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + g1x, _ := bls.RandFQ(r) + g1y, _ := bls.RandFQ(r) + g1z, _ := bls.RandFQ(r) + g2x, _ := bls.RandFQ(r) + g2y, _ := bls.RandFQ(r) + g2z, _ := bls.RandFQ(r) + inData[i] = addData{ + g1: bls.NewG1Projective(g1x, g1y, g1z), + g2: bls.NewG1Projective(g2x, g2y, g2z), + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].g1.Add(inData[count].g2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkG1AddAssignMixed(b *testing.B) { + type addData struct { + g1 *bls.G1Projective + g2 *bls.G1Affine + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + g1x, _ := bls.RandFQ(r) + g1y, _ := bls.RandFQ(r) + g1z, _ := bls.RandFQ(r) + g2x, _ := bls.RandFQ(r) + g2y, _ := bls.RandFQ(r) + inData[i] = addData{ + g1: bls.NewG1Projective(g1x, g1y, g1z), + g2: bls.NewG1Affine(g2x, g2y), + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].g1.AddAffine(inData[count].g2) + count = (count + 1) % g1MulAssignSamples + } +} diff --git a/bls/g1pubs/bls.go b/bls/g1pubs/bls.go new file mode 100644 index 0000000..3be0194 --- /dev/null +++ b/bls/g1pubs/bls.go @@ -0,0 +1,301 @@ +package g1pubs + +import ( + "bytes" + "io" + "log" + "sort" + + "github.com/phoreproject/bls" +) + +// Signature is a message signature. +type Signature struct { + s *bls.G2Projective +} + +// Serialize serializes a signature in compressed form. +func (s *Signature) Serialize() [96]byte { + return bls.CompressG2(s.s.ToAffine()) +} + +func (s *Signature) String() string { + return s.s.String() +} + +// NewSignatureFromG2 creates a new signature from a G2 +// element. +func NewSignatureFromG2(g2 *bls.G2Affine) *Signature { + return &Signature{g2.ToProjective()} +} + +// DeserializeSignature deserializes a signature from bytes. +func DeserializeSignature(b [96]byte) (*Signature, error) { + a, err := bls.DecompressG2(b) + if err != nil { + return nil, err + } + + return &Signature{s: a.ToProjective()}, nil +} + +// Copy returns a copy of the signature. +func (s *Signature) Copy() *Signature { + return &Signature{s.s.Copy()} +} + +// PublicKey is a public key. +type PublicKey struct { + p *bls.G1Projective +} + +func (p PublicKey) String() string { + return p.p.String() +} + +// Serialize serializes a public key to bytes. +func (p PublicKey) Serialize() [48]byte { + return bls.CompressG1(p.p.ToAffine()) +} + +// NewPublicKeyFromG1 creates a new public key from a G1 element. +func NewPublicKeyFromG1(g1 *bls.G1Affine) *PublicKey { + return &PublicKey{g1.ToProjective()} +} + +func concatAppend(slices [][]byte) []byte { + var tmp []byte + for _, s := range slices { + tmp = append(tmp, s...) + } + return tmp +} + +// Equals checks if two public keys are equal +func (p PublicKey) Equals(other PublicKey) bool { + return p.p.Equal(other.p) +} + +// DeserializePublicKey deserializes a public key from +// bytes. +func DeserializePublicKey(b [48]byte) (*PublicKey, error) { + a, err := bls.DecompressG1(b) + if err != nil { + return nil, err + } + + return &PublicKey{p: a.ToProjective()}, nil +} + +// SecretKey represents a BLS private key. +type SecretKey struct { + f *bls.FR +} + +// GetFRElement gets the underlying FR element. +func (s SecretKey) GetFRElement() *bls.FR { + return s.f +} + +func (s SecretKey) String() string { + return s.f.String() +} + +// Serialize serializes a secret key to bytes. +func (s SecretKey) Serialize() [32]byte { + return s.f.Bytes() +} + +// DeserializeSecretKey deserializes a secret key from +// bytes. +func DeserializeSecretKey(b [32]byte) *SecretKey { + return &SecretKey{bls.FRReprToFR(bls.FRReprFromBytes(b))} +} + +// DeriveSecretKey derives a secret key from +// bytes. +func DeriveSecretKey(b [32]byte) *SecretKey { + return &SecretKey{bls.HashSecretKey(b)} +} + +// Sign signs a message with a secret key. +func Sign(message []byte, key *SecretKey) *Signature { + h := bls.HashG2(message).MulFR(key.f.ToRepr()) + return &Signature{s: h} +} + +// SignWithDomain signs a message with a secret key and its domain. +func SignWithDomain(message [32]byte, key *SecretKey, domain uint64) *Signature { + h := bls.HashG2WithDomain(message, domain).MulFR(key.f.ToRepr()) + return &Signature{s: h} +} + +// PrivToPub converts the private key into a public key. +func PrivToPub(k *SecretKey) *PublicKey { + return &PublicKey{p: bls.G1AffineOne.MulFR(k.f.ToRepr())} +} + +// RandKey generates a random secret key. +func RandKey(r io.Reader) (*SecretKey, error) { + k, err := bls.RandFR(r) + if err != nil { + return nil, err + } + s := &SecretKey{f: k} + return s, nil +} + +// KeyFromFQRepr returns a new key based on a FQRepr in +// FR. +func KeyFromFQRepr(i *bls.FRRepr) *SecretKey { + return &SecretKey{f: bls.FRReprToFR(i)} +} + +// Verify verifies a signature against a message and a public key. +func Verify(m []byte, pub *PublicKey, sig *Signature) bool { + h := bls.HashG2(m) + lhs := bls.Pairing(bls.G1ProjectiveOne, sig.s) + rhs := bls.Pairing(pub.p, h.ToProjective()) + return lhs.Equals(rhs) +} + +// VerifyWithDomain verifies a signature against a message and a public key and a domain +func VerifyWithDomain(m [32]byte, pub *PublicKey, sig *Signature, domain uint64) bool { + h := bls.HashG2WithDomain(m, domain) + lhs := bls.Pairing(bls.G1ProjectiveOne, sig.s) + rhs := bls.Pairing(pub.p, h.ToAffine().ToProjective()) + return lhs.Equals(rhs) +} + +// AggregateSignatures adds up all of the signatures. +func AggregateSignatures(s []*Signature) *Signature { + newSig := &Signature{s: bls.G2ProjectiveZero.Copy()} + for _, sig := range s { + newSig.Aggregate(sig) + } + return newSig +} + +// Aggregate adds one signature to another +func (s *Signature) Aggregate(other *Signature) { + newS := s.s.Add(other.s) + s.s = newS +} + +// AggregatePublicKeys adds public keys together. +func AggregatePublicKeys(p []*PublicKey) *PublicKey { + newPub := &PublicKey{p: bls.G1ProjectiveZero.Copy()} + for _, pub := range p { + newPub.Aggregate(pub) + } + return newPub +} + +// Aggregate adds two public keys together. +func (p *PublicKey) Aggregate(other *PublicKey) { + newP := p.p.Add(other.p) + p.p = newP +} + +// Copy copies the public key and returns it. +func (p *PublicKey) Copy() *PublicKey { + return &PublicKey{p: p.p.Copy()} +} + +// NewAggregateSignature creates a blank aggregate signature. +func NewAggregateSignature() *Signature { + return &Signature{s: bls.G2ProjectiveZero.Copy()} +} + +// NewAggregatePubkey creates a blank public key. +func NewAggregatePubkey() *PublicKey { + return &PublicKey{p: bls.G1ProjectiveZero.Copy()} +} + +// implement `Interface` in sort package. +type sortableByteArray [][]byte + +func (b sortableByteArray) Len() int { + return len(b) +} + +func (b sortableByteArray) Less(i, j int) bool { + // bytes package already implements Comparable for []byte. + switch bytes.Compare(b[i], b[j]) { + case -1: + return true + case 0, 1: + return false + default: + log.Panic("not fail-able with `bytes.Comparable` bounded [-1, 1].") + return false + } +} + +func (b sortableByteArray) Swap(i, j int) { + b[j], b[i] = b[i], b[j] +} + +func sortByteArrays(src [][]byte) [][]byte { + sorted := sortableByteArray(src) + sort.Sort(sorted) + return sorted +} + +// VerifyAggregate verifies each public key against each message. +func (s *Signature) VerifyAggregate(pubKeys []*PublicKey, msgs [][]byte) bool { + if len(pubKeys) != len(msgs) { + return false + } + + msgsCopy := make([][]byte, len(msgs)) + + for i, m := range msgs { + msgsCopy[i] = make([]byte, len(m)) + copy(msgsCopy[i], m) + } + + msgsSorted := sortByteArrays(msgsCopy) + lastMsg := []byte(nil) + + // check for duplicates + for _, m := range msgsSorted { + if bytes.Equal(m, lastMsg) { + return false + } + lastMsg = m + } + + lhs := bls.Pairing(bls.G1ProjectiveOne, s.s) + rhs := bls.FQ12One.Copy() + for i := range pubKeys { + h := bls.HashG2(msgs[i]) + rhs.MulAssign(bls.Pairing(pubKeys[i].p, h.ToProjective())) + } + return lhs.Equals(rhs) +} + +// VerifyAggregateCommon verifies each public key against a message. +// This is vulnerable to rogue public-key attack. Each user must +// provide a proof-of-knowledge of the public key. +func (s *Signature) VerifyAggregateCommon(pubKeys []*PublicKey, msg []byte) bool { + h := bls.HashG2(msg) + lhs := bls.Pairing(bls.G1ProjectiveOne, s.s) + rhs := bls.FQ12One.Copy() + for _, p := range pubKeys { + rhs.MulAssign(bls.Pairing(p.p, h.ToProjective())) + } + return lhs.Equals(rhs) +} + +// VerifyAggregateCommonWithDomain verifies each public key against a message and +// its domain. +func (s *Signature) VerifyAggregateCommonWithDomain(pubKeys []*PublicKey, msg [32]byte, domain uint64) bool { + h := bls.HashG2WithDomain(msg, domain) + lhs := bls.Pairing(bls.G1ProjectiveOne, s.s) + rhs := bls.FQ12One.Copy() + for _, p := range pubKeys { + rhs.MulAssign(bls.Pairing(p.p, h.ToAffine().ToProjective())) + } + return lhs.Equals(rhs) +} diff --git a/bls/g1pubs/bls_test.go b/bls/g1pubs/bls_test.go new file mode 100644 index 0000000..6cda092 --- /dev/null +++ b/bls/g1pubs/bls_test.go @@ -0,0 +1,386 @@ +package g1pubs_test + +import ( + "errors" + "fmt" + "testing" + + "github.com/phoreproject/bls" + "github.com/phoreproject/bls/g1pubs" +) + +type XORShift struct { + state uint64 +} + +func NewXORShift(state uint64) *XORShift { + return &XORShift{state} +} + +func (xor *XORShift) Read(b []byte) (int, error) { + for i := range b { + x := xor.state + x ^= x << 13 + x ^= x >> 7 + x ^= x << 17 + b[i] = uint8(x) + xor.state = x + } + return len(b), nil +} + +func SignVerify(loopCount int) error { + r := NewXORShift(1) + for i := 0; i < 1; i++ { + priv, _ := g1pubs.RandKey(r) + pub := g1pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf("Hello world! 16 characters %d", i)) + sig := g1pubs.Sign(msg, priv) + if !g1pubs.Verify(msg, pub, sig) { + return errors.New("sig did not verify") + } + } + return nil +} + +func SignVerifyWithDomain(loopCount int) error { + r := NewXORShift(1) + for i := 0; i < 1; i++ { + priv, _ := g1pubs.RandKey(r) + pub := g1pubs.PrivToPub(priv) + var hashedMsg [32]byte + copy(hashedMsg[:], []byte(fmt.Sprintf("Hello world! 16 characters %d", i))) + sig := g1pubs.SignWithDomain(hashedMsg, priv, 1) + if !g1pubs.VerifyWithDomain(hashedMsg, pub, sig, 1) { + return errors.New("sig did not verify") + } + } + return nil +} + +func SignVerifyAggregateCommonMessage(loopCount int) error { + r := NewXORShift(2) + pubkeys := make([]*g1pubs.PublicKey, 0, 1000) + sigs := make([]*g1pubs.Signature, 0, 1000) + msg := []byte(">16 character identical message") + for i := 0; i < loopCount; i++ { + priv, _ := g1pubs.RandKey(r) + pub := g1pubs.PrivToPub(priv) + sig := g1pubs.Sign(msg, priv) + pubkeys = append(pubkeys, pub) + sigs = append(sigs, sig) + if i < 10 || i > (loopCount-5) { + newSig := g1pubs.AggregateSignatures(sigs) + if !newSig.VerifyAggregateCommon(pubkeys, msg) { + return errors.New("sig did not verify") + } + } + } + return nil +} + +func SignVerifyAggregateCommonMessageWithDomain(loopCount int) error { + var hashedMessage [32]byte + domain := 100 + r := NewXORShift(2) + pubkeys := make([]*g1pubs.PublicKey, 0, 1000) + sigs := make([]*g1pubs.Signature, 0, 1000) + copy(hashedMessage[:], []byte(">16 character identical message")) + + for i := 0; i < loopCount; i++ { + priv, _ := g1pubs.RandKey(r) + pub := g1pubs.PrivToPub(priv) + sig := g1pubs.SignWithDomain(hashedMessage, priv, uint64(domain)) + pubkeys = append(pubkeys, pub) + sigs = append(sigs, sig) + if i < 10 || i > (loopCount-5) { + newSig := g1pubs.AggregateSignatures(sigs) + if !newSig.VerifyAggregateCommonWithDomain(pubkeys, hashedMessage, uint64(domain)) { + return fmt.Errorf("sig did not verify for loop %d", i) + } + } + } + return nil +} + +func SignVerifyAggregateCommonMessageMissingSig(loopCount int) error { + r := NewXORShift(3) + skippedSig := loopCount / 2 + pubkeys := make([]*g1pubs.PublicKey, 0, 1000) + sigs := make([]*g1pubs.Signature, 0, 1000) + msg := []byte(">16 character identical message") + for i := 0; i < loopCount; i++ { + priv, _ := g1pubs.RandKey(r) + pub := g1pubs.PrivToPub(priv) + sig := g1pubs.Sign(msg, priv) + pubkeys = append(pubkeys, pub) + if i != skippedSig { + sigs = append(sigs, sig) + } + if i < 10 || i > (loopCount-5) { + newSig := g1pubs.AggregateSignatures(sigs) + if newSig.VerifyAggregateCommon(pubkeys, msg) != (i < skippedSig) { + return errors.New("sig did not verify") + } + } + } + return nil +} + +func AggregateSignatures(loopCount int) error { + r := NewXORShift(4) + pubkeys := make([]*g1pubs.PublicKey, 0, 1000) + msgs := make([][]byte, 0, 1000) + sigs := make([]*g1pubs.Signature, 0, 1000) + for i := 0; i < loopCount; i++ { + priv, _ := g1pubs.RandKey(r) + pub := g1pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf(">16 character identical message %d", i)) + sig := g1pubs.Sign(msg, priv) + pubkeys = append(pubkeys, pub) + msgs = append(msgs, msg) + sigs = append(sigs, sig) + + if i < 10 || i > (loopCount-5) { + newSig := g1pubs.AggregateSignatures(sigs) + if !newSig.VerifyAggregate(pubkeys, msgs) { + return errors.New("sig did not verify") + } + } + } + return nil +} + +func TestSignVerify(t *testing.T) { + err := SignVerify(10) + if err != nil { + t.Fatal(err) + } +} + +func TestSignVerifyWithDomain(t *testing.T) { + err := SignVerifyWithDomain(10) + if err != nil { + t.Fatal(err) + } +} + +func TestSignVerifyAggregateCommon(t *testing.T) { + err := SignVerifyAggregateCommonMessage(10) + if err != nil { + t.Fatal(err) + } +} + +func TestSignVerifyAggregateCommonWithDomain(t *testing.T) { + err := SignVerifyAggregateCommonMessageWithDomain(10) + if err != nil { + t.Fatal(err) + } +} + +func TestSignVerifyAggregateCommonMissingSig(t *testing.T) { + err := SignVerifyAggregateCommonMessageMissingSig(10) + if err != nil { + t.Fatal(err) + } +} + +func TestSignAggregateSigs(t *testing.T) { + err := AggregateSignatures(10) + if err != nil { + t.Fatal(err) + } +} + +func TestAggregateSignaturesDuplicatedMessages(t *testing.T) { + r := NewXORShift(5) + + pubkeys := make([]*g1pubs.PublicKey, 0, 1000) + msgs := make([][]byte, 0, 1000) + sigs := g1pubs.NewAggregateSignature() + + key, _ := g1pubs.RandKey(r) + pub := g1pubs.PrivToPub(key) + message := []byte(">16 char first message") + sig := g1pubs.Sign(message, key) + pubkeys = append(pubkeys, pub) + msgs = append(msgs, message) + sigs.Aggregate(sig) + + if !sigs.VerifyAggregate(pubkeys, msgs) { + t.Fatal("signature does not verify") + } + + key2, _ := g1pubs.RandKey(r) + pub2 := g1pubs.PrivToPub(key2) + message2 := []byte(">16 char second message") + sig2 := g1pubs.Sign(message2, key2) + pubkeys = append(pubkeys, pub2) + msgs = append(msgs, message2) + sigs.Aggregate(sig2) + + if !sigs.VerifyAggregate(pubkeys, msgs) { + t.Fatal("signature does not verify") + } + + key3, _ := g1pubs.RandKey(r) + pub3 := g1pubs.PrivToPub(key3) + sig3 := g1pubs.Sign(message2, key3) + pubkeys = append(pubkeys, pub3) + msgs = append(msgs, message2) + sigs.Aggregate(sig3) + + if sigs.VerifyAggregate(pubkeys, msgs) { + t.Fatal("signature verifies with duplicate message") + } +} + +func TestAggregateSigsSeparate(t *testing.T) { + x := NewXORShift(20) + priv1, _ := g1pubs.RandKey(x) + priv2, _ := g1pubs.RandKey(x) + priv3, _ := g1pubs.RandKey(x) + + pub1 := g1pubs.PrivToPub(priv1) + pub2 := g1pubs.PrivToPub(priv2) + pub3 := g1pubs.PrivToPub(priv3) + + msg := []byte("test 1") + sig1 := g1pubs.Sign(msg, priv1) + sig2 := g1pubs.Sign(msg, priv2) + sig3 := g1pubs.Sign(msg, priv3) + + aggSigs := g1pubs.AggregateSignatures([]*g1pubs.Signature{sig1, sig2, sig3}) + + aggPubs := g1pubs.NewAggregatePubkey() + aggPubs.Aggregate(pub1) + aggPubs.Aggregate(pub2) + aggPubs.Aggregate(pub3) + + valid := g1pubs.Verify(msg, aggPubs, aggSigs) + if !valid { + t.Fatal("expected aggregate signature to be valid") + } +} + +func BenchmarkBLSAggregateSignature(b *testing.B) { + r := NewXORShift(5) + priv, _ := g1pubs.RandKey(r) + msg := []byte(fmt.Sprintf(">16 character identical message")) + sig := g1pubs.Sign(msg, priv) + + s := g1pubs.NewAggregateSignature() + b.ResetTimer() + for i := 0; i < b.N; i++ { + s.Aggregate(sig) + } +} + +func BenchmarkBLSSign(b *testing.B) { + r := NewXORShift(5) + privs := make([]*g1pubs.SecretKey, b.N) + for i := range privs { + privs[i], _ = g1pubs.RandKey(r) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + + msg := []byte(fmt.Sprintf("Hello world! 16 characters %d", i)) + g1pubs.Sign(msg, privs[i]) + // if !g1pubs.Verify(msg, pub, sig) { + // return errors.New("sig did not verify") + // } + } +} + +func BenchmarkBLSVerify(b *testing.B) { + r := NewXORShift(5) + priv, _ := g1pubs.RandKey(r) + pub := g1pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf(">16 character identical message")) + sig := g1pubs.Sign(msg, priv) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + g1pubs.Verify(msg, pub, sig) + } +} + +func TestSignatureSerializeDeserialize(t *testing.T) { + r := NewXORShift(1) + priv, _ := g1pubs.RandKey(r) + pub := g1pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf(">16 character identical message")) + sig := g1pubs.Sign(msg, priv) + + if !g1pubs.Verify(msg, pub, sig) { + t.Fatal("message did not verify before serialization/deserialization") + } + + sigSer := sig.Serialize() + sigDeser, err := g1pubs.DeserializeSignature(sigSer) + if err != nil { + t.Fatal(err) + } + if !g1pubs.Verify(msg, pub, sigDeser) { + t.Fatal("message did not verify after serialization/deserialization") + } +} + +func TestPubkeySerializeDeserialize(t *testing.T) { + r := NewXORShift(1) + priv, _ := g1pubs.RandKey(r) + pub := g1pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf(">16 character identical message")) + sig := g1pubs.Sign(msg, priv) + + if !g1pubs.Verify(msg, pub, sig) { + t.Fatal("message did not verify before serialization/deserialization of pubkey") + } + + pubSer := pub.Serialize() + pubDeser, err := g1pubs.DeserializePublicKey(pubSer) + if err != nil { + t.Fatal(err) + } + if !g1pubs.Verify(msg, pubDeser, sig) { + t.Fatal("message did not verify after serialization/deserialization of pubkey") + } +} + +func TestSecretkeySerializeDeserialize(t *testing.T) { + r := NewXORShift(3) + priv, _ := g1pubs.RandKey(r) + privSer := priv.Serialize() + privNew := g1pubs.DeserializeSecretKey(privSer) + pub := g1pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf(">16 character identical message")) + sig := g1pubs.Sign(msg, privNew) + + if !g1pubs.Verify(msg, pub, sig) { + t.Fatal("message did not verify before serialization/deserialization of secret") + } + + pubSer := pub.Serialize() + pubDeser, err := g1pubs.DeserializePublicKey(pubSer) + if err != nil { + t.Fatal(err) + } + if !g1pubs.Verify(msg, pubDeser, sig) { + t.Fatal("message did not verify after serialization/deserialization of secret") + } +} + +func TestDeriveSecretKey(t *testing.T) { + var secKeyIn [32]byte + copy(secKeyIn[:], []byte("11223344556677889900112233445566")) + k := g1pubs.DeriveSecretKey(secKeyIn) + + expectedElement, _ := bls.FRReprFromString("414e2c2a330cf94edb70e1c88efa851e80fe5eb14ff08fe5b7e588b4fe9899e4", 16) + expectedFRElement := bls.FRReprToFR(expectedElement) + + if !expectedFRElement.Equals(k.GetFRElement()) { + t.Fatal("expected secret key to match") + } +} diff --git a/bls/g2.go b/bls/g2.go new file mode 100644 index 0000000..40a2ba3 --- /dev/null +++ b/bls/g2.go @@ -0,0 +1,1082 @@ +package bls + +import ( + "crypto/sha256" + "encoding/binary" + "errors" + "fmt" + "io" + "math/big" +) + +// G2Affine is an affine point on the G2 curve. +type G2Affine struct { + x FQ2 + y FQ2 + infinity bool +} + +// NewG2Affine constructs a new G2Affine point. +func NewG2Affine(x FQ2, y FQ2) *G2Affine { + return &G2Affine{x: x, y: y, infinity: false} +} + +// G2AffineZero represents the point at infinity on G2. +var G2AffineZero = &G2Affine{FQ2Zero, FQ2One, true} + +var g2GeneratorXC1, _ = FQReprFromString("13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e", 16) +var g2GeneratorXC0, _ = FQReprFromString("24aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8", 16) +var g2GeneratorYC1, _ = FQReprFromString("606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be", 16) +var g2GeneratorYC0, _ = FQReprFromString("ce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801", 16) + +// BCoeffFQ2 of the G2 curve. +var BCoeffFQ2 = NewFQ2(BCoeff, BCoeff) + +// G2AffineOne represents the point at 1 on G2. +var G2AffineOne = &G2Affine{ + x: NewFQ2( + FQReprToFQ(g2GeneratorXC0), + FQReprToFQ(g2GeneratorXC1), + ), + y: NewFQ2( + FQReprToFQ(g2GeneratorYC0), + FQReprToFQ(g2GeneratorYC1), + ), infinity: false} + +func (g G2Affine) String() string { + if g.infinity { + return fmt.Sprintf("G2(Infinity)") + } + return fmt.Sprintf("G2(x=%s, y=%s)", g.x, g.y) +} + +// Copy returns a copy of the G2Affine point. +func (g G2Affine) Copy() *G2Affine { + return &G2Affine{g.x.Copy(), g.y.Copy(), g.infinity} +} + +// IsZero checks if the point is infinity. +func (g G2Affine) IsZero() bool { + return g.infinity +} + +// NegAssign negates the point. +func (g *G2Affine) NegAssign() { + if !g.IsZero() { + g.y.NegAssign() + } +} + +// ToProjective converts an affine point to a projective one. +func (g G2Affine) ToProjective() *G2Projective { + if g.IsZero() { + return G2ProjectiveZero.Copy() + } + + return NewG2Projective(g.x, g.y, FQ2One) +} + +// Mul performs a EC multiply operation on the point. +func (g G2Affine) Mul(b FQRepr) *G2Projective { + res := G2ProjectiveZero.Copy() + for i := uint(0); i < b.BitLen(); i++ { + o := b.Bit(b.BitLen() - i - 1) + res = res.Double() + if o { + res = res.AddAffine(&g) + } + } + return res +} + +// MulFR performs a EC multiply operation on the point. +func (g G2Affine) MulFR(b *FRRepr) *G2Projective { + res := G2ProjectiveZero.Copy() + for i := uint(0); i < b.BitLen(); i++ { + o := b.Bit(b.BitLen() - i - 1) + res = res.Double() + if o { + res = res.AddAffine(&g) + } + } + return res +} + +// MulBig performs a EC multiply operation on the point. +func (g G2Affine) MulBig(b big.Int) *G2Projective { + res := G2ProjectiveZero.Copy() + for i := 0; i < b.BitLen(); i++ { + o := b.Bit(b.BitLen() - i - 1) + res = res.Double() + if o == 1 { + res = res.AddAffine(&g) + } + } + return res +} + +// IsOnCurve checks if a point is on the G2 curve. +func (g G2Affine) IsOnCurve() bool { + if g.infinity { + return true + } + y2 := g.y.Copy() + y2.SquareAssign() + x3b := g.x.Copy() + x3b.SquareAssign() + x3b.MulAssign(g.x) + x3b.AddAssign(BCoeffFQ2) + + return y2.Equals(x3b) +} + +// G2 cofactor = (x^8 - 4 x^7 + 5 x^6) - (4 x^4 + 6 x^3 - 4 x^2 - 4 x + 13) // 9 +var g2Cofactor, _ = new(big.Int).SetString("5d543a95414e7f1091d50792876a202cd91de4547085abaa68a205b2e5a7ddfa628f1cb4d9e82ef21537e293a6691ae1616ec6e786f0c70cf1c38e31c7238e5", 16) + +// ScaleByCofactor scales the G2Affine point by the cofactor. +func (g G2Affine) ScaleByCofactor() *G2Projective { + return g.MulBig(*g2Cofactor) +} + +// Equals checks if two affine points are equal. +func (g G2Affine) Equals(other *G2Affine) bool { + return (g.infinity == other.infinity) || (g.x.Equals(other.x) && g.y.Equals(other.y)) +} + +// GetG2PointFromX attempts to reconstruct an affine point given +// an x-coordinate. The point is not guaranteed to be in the subgroup. +// If and only if `greatest` is set will the lexicographically +// largest y-coordinate be selected. +func GetG2PointFromX(x FQ2, greatest bool) *G2Affine { + x3b := x.Copy() + x3b.SquareAssign() + x3b.MulAssign(x) + x3b.AddAssign(BCoeffFQ2) + + y, success := x3b.Sqrt() + + if !success { + return nil + } + + negY := y.Copy() + negY.NegAssign() + + yVal := negY + if (y.Cmp(negY) < 0) != greatest { + yVal = y + } + return NewG2Affine(x, yVal) +} + +// SerializeBytes returns the serialized bytes for the points represented. +func (g *G2Affine) SerializeBytes() [192]byte { + out := [192]byte{} + + xC0Bytes := g.x.c0.ToRepr().Bytes() + xC1Bytes := g.x.c1.ToRepr().Bytes() + yC0Bytes := g.y.c0.ToRepr().Bytes() + yC1Bytes := g.y.c1.ToRepr().Bytes() + + copy(out[0:48], xC0Bytes[:]) + copy(out[48:96], xC1Bytes[:]) + copy(out[96:144], yC0Bytes[:]) + copy(out[144:192], yC1Bytes[:]) + + return out +} + +// SetRawBytes sets the coords given the serialized bytes. +func (g *G2Affine) SetRawBytes(uncompressed [192]byte) error { + + var xC0Bytes [48]byte + var xC1Bytes [48]byte + var yC0Bytes [48]byte + var yC1Bytes [48]byte + + copy(xC0Bytes[:], uncompressed[0:48]) + copy(xC1Bytes[:], uncompressed[48:96]) + copy(yC0Bytes[:], uncompressed[96:144]) + copy(yC1Bytes[:], uncompressed[144:192]) + + xc0FQ := FQReprToFQ(FQReprFromBytes(xC0Bytes)) + xc1FQ := FQReprToFQ(FQReprFromBytes(xC1Bytes)) + yc0FQ := FQReprToFQ(FQReprFromBytes(yC0Bytes)) + yc1FQ := FQReprToFQ(FQReprFromBytes(yC1Bytes)) + + g.x = FQ2{ + c0: xc0FQ, + c1: xc1FQ, + } + g.y = FQ2{ + c0: yc0FQ, + c1: yc1FQ, + } + return nil +} + +// DecompressG2 decompresses a G2 point from a big int and checks +// if it is in the correct subgroup. +func DecompressG2(c [96]byte) (*G2Affine, error) { + affine, err := DecompressG2Unchecked(c) + if err != nil { + return nil, err + } + + if !affine.IsInCorrectSubgroupAssumingOnCurve() { + return nil, errors.New("point is not in correct subgroup") + } + return affine, nil +} + +// DecompressG2Unchecked decompresses a G2 point from a big int. +func DecompressG2Unchecked(c [96]byte) (*G2Affine, error) { + if c[0]&(1<<7) == 0 { + return nil, errors.New("unexpected compression mode") + } + + if c[0]&(1<<6) != 0 { + c[0] &= 0x3f + + for _, b := range c { + if b != 0 { + return nil, errors.New("unexpected information in infinity point on G2") + } + } + return G2AffineZero.Copy(), nil + } + greatest := c[0]&(1<<5) != 0 + + c[0] &= 0x1f + + var xC0Bytes [48]byte + var xC1Bytes [48]byte + + copy(xC1Bytes[:], c[:48]) + copy(xC0Bytes[:], c[48:]) + + xC0 := FQReprFromBytes(xC0Bytes) + xC0FQ := FQReprToFQ(xC0) + xC1 := FQReprFromBytes(xC1Bytes) + xC1FQ := FQReprToFQ(xC1) + + x := NewFQ2(xC0FQ, xC1FQ) + + return GetG2PointFromX(x, greatest), nil +} + +// CompressG2 compresses a G2 point into an int. +func CompressG2(affine *G2Affine) [96]byte { + res := [96]byte{} + if affine.IsZero() { + res[0] |= 1 << 6 + } else { + out0 := affine.x.c1.ToRepr().Bytes() + out1 := affine.x.c0.ToRepr().Bytes() + copy(res[:48], out0[:]) + copy(res[48:], out1[:]) + + negY := affine.y.Copy() + negY.NegAssign() + + if affine.y.Cmp(negY) > 0 { + res[0] |= 1 << 5 + } + } + + res[0] |= 1 << 7 + + return res +} + +// IsInCorrectSubgroupAssumingOnCurve checks if the point multiplied by the +// field characteristic equals zero. +func (g G2Affine) IsInCorrectSubgroupAssumingOnCurve() bool { + return g.MulFR(frChar).IsZero() +} + +// G2Projective is a projective point on the G2 curve. +type G2Projective struct { + x FQ2 + y FQ2 + z FQ2 +} + +// NewG2Projective creates a new G2Projective point. +func NewG2Projective(x FQ2, y FQ2, z FQ2) *G2Projective { + return &G2Projective{x, y, z} +} + +// G2ProjectiveZero is the point at infinity where Z = 0. +var G2ProjectiveZero = &G2Projective{FQ2Zero.Copy(), FQ2One.Copy(), FQ2Zero.Copy()} + +// G2ProjectiveOne is the generator point on G2. +var G2ProjectiveOne = G2AffineOne.ToProjective() + +func (g G2Projective) String() string { + if g.IsZero() { + return "G2: Infinity" + } + return g.ToAffine().String() +} + +// Copy returns a copy of the G2Projective point. +func (g G2Projective) Copy() *G2Projective { + return NewG2Projective(g.x.Copy(), g.y.Copy(), g.z.Copy()) +} + +// IsZero checks if the G2Projective point is zero. +func (g G2Projective) IsZero() bool { + return g.z.IsZero() +} + +// Equal checks if two projective points are equal. +func (g G2Projective) Equals(other *G2Projective) bool { + if g.IsZero() { + return other.IsZero() + } + if other.IsZero() { + return false + } + + z1 := g.z.Copy() + z1.SquareAssign() + z2 := other.z.Copy() + z2.SquareAssign() + + tmp1 := g.x.Copy() + tmp1.MulAssign(z2) + tmp2 := other.x.Copy() + tmp2.MulAssign(z1) + if !tmp1.Equals(tmp2) { + return false + } + lhs := z1 + lhs.MulAssign(g.z) + lhs.MulAssign(other.y) + + rhs := z2 + rhs.MulAssign(other.z) + rhs.MulAssign(g.y) + + return lhs.Equals(rhs) +} + +// ToAffine converts a G2Projective point to affine form. +func (g G2Projective) ToAffine() *G2Affine { + if g.IsZero() { + return G2AffineZero + } else if g.z.Equals(FQ2One) { + return NewG2Affine(g.x, g.y) + } + + // nonzero so must have an inverse + zInv := g.z.Copy() + zInv.InverseAssign() + zInvSquared := zInv.Copy() + zInvSquared.SquareAssign() + + x := g.x.Copy() + x.MulAssign(zInvSquared) + + y := g.y.Copy() + y.MulAssign(zInvSquared) + y.MulAssign(zInv) + + return NewG2Affine(x, y) +} + +// Double performs EC doubling on the point. +func (g G2Projective) Double() *G2Projective { + if g.IsZero() { + return g.Copy() + } + + // A = x1^2 + a := g.x.Copy() + a.SquareAssign() + + // B = y1^2 + b := g.y.Copy() + b.SquareAssign() + + // C = B^2 + c := b.Copy() + c.SquareAssign() + + // D = 2*((X1+B)^2-A-C) + d := g.x.Copy() + d.AddAssign(b) + d.SquareAssign() + d.SubAssign(a) + d.SubAssign(c) + d.DoubleAssign() + + // E = 3*A + e := a.Copy() + e.DoubleAssign() + e.AddAssign(a) + + // F = E^2 + f := e.Copy() + f.SquareAssign() + + // z3 = 2*Y1*Z1 + newZ := g.z.Copy() + newZ.MulAssign(g.y) + newZ.DoubleAssign() + + // x3 = F-2*D + newX := f.Copy() + newX.SubAssign(d) + newX.SubAssign(d) + + c.DoubleAssign() + c.DoubleAssign() + c.DoubleAssign() + + newY := d.Copy() + newY.SubAssign(newX) + newY.MulAssign(e) + newY.SubAssign(c) + + return NewG2Projective(newX, newY, newZ) +} + +// Add performs an EC Add operation with another point. +func (g G2Projective) Add(other *G2Projective) *G2Projective { + if g.IsZero() { + return other.Copy() + } + if other.IsZero() { + return g.Copy() + } + + // Z1Z1 = Z1^2 + z1z1 := g.z.Copy() + z1z1.SquareAssign() + + // Z2Z2 = Z2^2 + z2z2 := other.z.Copy() + z2z2.SquareAssign() + + // U1 = X1*Z2Z2 + u1 := g.x.Copy() + u1.MulAssign(z2z2) + + // U2 = x2*Z1Z1 + u2 := other.x.Copy() + u2.MulAssign(z1z1) + + // S1 = Y1*Z2*Z2Z2 + s1 := g.y.Copy() + s1.MulAssign(other.z) + s1.MulAssign(z2z2) + + // S2 = Y2*Z1*Z1Z1 + s2 := other.y.Copy() + s2.MulAssign(g.z) + s2.MulAssign(z1z1) + + if u1.Equals(u2) && s1.Equals(s2) { + // points are equal + return g.Double() + } + + // H = U2-U1 + h := u2.Copy() + h.SubAssign(u1) + + // I = (2*H)^2 + i := h.Copy() + i.DoubleAssign() + i.SquareAssign() + + // J = H * I + j := h.Copy() + j.MulAssign(i) + + // r = 2*(S2-S1) + r := s2.Copy() + r.SubAssign(s1) + r.DoubleAssign() + + // v = U1*I + u1.MulAssign(i) + + // X3 = r^2 - J - 2*V + newX := r.Copy() + newX.SquareAssign() + newX.SubAssign(j) + newX.SubAssign(u1) + newX.SubAssign(u1) + + // Y3 = r*(V - X3) - 2*S1*J + u1.SubAssign(newX) + u1.MulAssign(r) + s1.MulAssign(j) + s1.DoubleAssign() + u1.SubAssign(s1) + + // Z3 = ((Z1+Z2)^2 - Z1Z1 - Z2Z2)*H + newZ := g.z.Copy() + newZ.AddAssign(other.z) + newZ.SquareAssign() + newZ.SubAssign(z1z1) + newZ.SubAssign(z2z2) + newZ.MulAssign(h) + + return NewG2Projective(newX, u1, newZ) +} + +// AddAffine performs an EC Add operation with an affine point. +func (g G2Projective) AddAffine(other *G2Affine) *G2Projective { + if g.IsZero() { + return other.ToProjective() + } + if other.IsZero() { + return g.Copy() + } + + // Z1Z1 = Z1^2 + z1z1 := g.z.Copy() + z1z1.SquareAssign() + + // U2 = x2*Z1Z1 + u2 := other.x.Copy() + u2.MulAssign(z1z1) + + // S2 = Y2*Z1*Z1Z1 + s2 := other.y.Copy() + s2.MulAssign(g.z) + s2.MulAssign(z1z1) + + if g.x.Equals(u2) && g.y.Equals(s2) { + // points are equal + return g.Double() + } + + // H = U2-X1 + u2.SubAssign(g.x) + + // HH = H^2 + hh := u2.Copy() + hh.SquareAssign() + + // I = 4*HH + i := hh.Copy() + i.DoubleAssign() + i.DoubleAssign() + + // J = H * I + j := u2.Copy() + j.MulAssign(i) + + // r = 2*(S2-Y1) + s2.SubAssign(g.y) + s2.DoubleAssign() + + // v = X1*I + v := g.x.Copy() + v.MulAssign(i) + + // X3 = r^2 - J - 2*V + newX := s2.Copy() + newX.SquareAssign() + newX.SubAssign(j) + newX.SubAssign(v) + newX.SubAssign(v) + + // Y3 = r*(V - X3) - 2*Y1*J + newY := v.Copy() + newY.SubAssign(newX) + newY.MulAssign(s2) + i0 := g.y.Copy() + i0.MulAssign(j) + i0.DoubleAssign() + newY.SubAssign(i0) + + // Z3 = (Z1+H)^2 - Z1Z1 - HH + newZ := g.z.Copy() + newZ.AddAssign(u2) + newZ.SquareAssign() + newZ.SubAssign(z1z1) + newZ.SubAssign(hh) + + return NewG2Projective(newX, newY, newZ) +} + +// Mul performs a EC multiply operation on the point. +func (g G2Projective) Mul(b FQRepr) *G2Projective { + res := G2ProjectiveZero.Copy() + for i := uint(0); i < uint(b.BitLen()); i++ { + o := b.Bit(b.BitLen() - i - 1) + res = res.Double() + if o { + res = res.Add(&g) + } + } + return res +} + +// MulFR performs a EC multiply operation on the point. +func (g G2Projective) MulFR(b *FRRepr) *G2Projective { + res := G2ProjectiveZero.Copy() + for i := uint(0); i < uint(b.BitLen()); i++ { + o := b.Bit(b.BitLen() - i - 1) + res = res.Double() + if o { + res = res.Add(&g) + } + } + return res +} + +var blsX, _ = FQReprFromString("d201000000010000", 16) + +const blsIsNegative = true + +// G2Prepared is a prepared G2 point multiplication by blsX. +type G2Prepared struct { + coeffs [][3]FQ2 + infinity bool +} + +// IsZero checks if the point is at infinity. +func (g G2Prepared) IsZero() bool { + return g.infinity +} + +// G2AffineToPrepared performs multiplication of the affine point by blsX. +func G2AffineToPrepared(q *G2Affine) *G2Prepared { + if q.IsZero() { + return &G2Prepared{infinity: true} + } + + doublingStep := func(r *G2Projective) (FQ2, FQ2, FQ2) { + tmp0 := r.x.Copy() + tmp0.SquareAssign() + tmp1 := r.y.Copy() + tmp1.SquareAssign() + tmp2 := tmp1.Copy() + tmp2.SquareAssign() + tmp3 := tmp1.Copy() + tmp3.AddAssign(r.x) + tmp3.SquareAssign() + tmp3.SubAssign(tmp0) + tmp3.SubAssign(tmp2) + tmp3.DoubleAssign() + tmp4 := tmp0.Copy() + tmp4.DoubleAssign() + tmp4.AddAssign(tmp0) + tmp6 := r.x.Copy() + tmp6.AddAssign(tmp4) + tmp5 := tmp4.Copy() + tmp5.SquareAssign() + zSquared := r.z.Copy() + zSquared.SquareAssign() + r.x = tmp5.Copy() + r.x.SubAssign(tmp3) + r.x.SubAssign(tmp3) + r.z = r.z.Copy() + r.z.AddAssign(r.y) + r.z.SquareAssign() + r.z.SubAssign(tmp1) + r.z.SubAssign(zSquared) + r.y = tmp3.Copy() + r.y.SubAssign(r.x) + r.y.MulAssign(tmp4) + tmp2.DoubleAssign() + tmp2.DoubleAssign() + tmp2.DoubleAssign() + r.y = r.y.Copy() + r.y.SubAssign(tmp2) + tmp3 = tmp4.Copy() + tmp3.MulAssign(zSquared) + tmp3.DoubleAssign() + tmp3.NegAssign() + tmp6 = tmp6.Copy() + tmp6.SquareAssign() + tmp6.SubAssign(tmp0) + tmp6.SubAssign(tmp5) + tmp1.DoubleAssign() + tmp1.DoubleAssign() + tmp6.SubAssign(tmp1) + tmp0 = r.z.Copy() + tmp0.MulAssign(zSquared) + tmp0.DoubleAssign() + return tmp0, tmp3, tmp6 + } + + additionStep := func(r *G2Projective, q *G2Affine) (FQ2, FQ2, FQ2) { + zSquared := r.z.Copy() + zSquared.SquareAssign() + ySquared := q.y.Copy() + ySquared.SquareAssign() + t0 := zSquared.Copy() + t0.MulAssign(q.x) + t1 := q.y.Copy() + t1.AddAssign(r.z) + t1.SquareAssign() + t1.SubAssign(ySquared) + t1.SubAssign(zSquared) + t1.MulAssign(zSquared) + t2 := t0.Copy() + t2.SubAssign(r.x) + t3 := t2.Copy() + t3.SquareAssign() + t4 := t3.Copy() + t4.DoubleAssign() + t4.DoubleAssign() + t5 := t4.Copy() + t5.MulAssign(t2) + t6 := t1.Copy() + t6.SubAssign(r.y) + t6.SubAssign(r.y) + t9 := t6.Copy() + t9.MulAssign(q.x) + t7 := t4.Copy() + t7.MulAssign(r.x) + r.x = t6.Copy() + r.x.SquareAssign() + r.x.SubAssign(t5) + r.x.SubAssign(t7) + r.x.SubAssign(t7) + r.z = r.z.Copy() + r.z.AddAssign(t2) + r.z.SquareAssign() + r.z.SubAssign(zSquared) + r.z.SubAssign(t3) + t10 := q.y.Copy() + t10.AddAssign(r.z) + t8 := t7.Copy() + t8.SubAssign(r.x) + t8.MulAssign(t6) + t0 = r.y.Copy() + t0.MulAssign(t5) + t0.DoubleAssign() + r.y = t8.Copy() + r.y.SubAssign(t0) + t10.SquareAssign() + t10.SubAssign(ySquared) + zSquared = r.z.Copy() + zSquared.SquareAssign() + t10.SubAssign(zSquared) + t9.DoubleAssign() + t9.SubAssign(t10) + t10 = r.z.Copy() + t10.DoubleAssign() + t6.NegAssign() + t6.DoubleAssign() + + return t10, t6, t9 + } + + var coeffs [][3]FQ2 + r := q.ToProjective() + + foundOne := false + blsXRsh1 := blsX.Copy() + blsXRsh1.Rsh(1) + for i := uint(0); i <= blsXRsh1.BitLen(); i++ { + set := blsXRsh1.Bit(blsXRsh1.BitLen() - i) + if !foundOne { + foundOne = set + continue + } + + o0, o1, o2 := doublingStep(r) + + coeffs = append(coeffs, [3]FQ2{o0, o1, o2}) + + if set { + o0, o1, o2 := additionStep(r, q) + coeffs = append(coeffs, [3]FQ2{o0, o1, o2}) + } + } + o0, o1, o2 := doublingStep(r) + + coeffs = append(coeffs, [3]FQ2{o0, o1, o2}) + + return &G2Prepared{coeffs, false} +} + +// RandG2 generates a random G2 element. +func RandG2(r io.Reader) (*G2Projective, error) { + for { + b := make([]byte, 1) + _, err := r.Read(b) + if err != nil { + return nil, err + } + greatest := false + if b[0]%2 == 0 { + greatest = true + } + f, err := RandFQ2(r) + if err != nil { + return nil, err + } + p := GetG2PointFromX(f, greatest) + if p == nil { + continue + } + p1 := p.ScaleByCofactor() + if !p.IsZero() { + return p1, nil + } + } +} + +var swencSqrtNegThree, _ = FQReprFromString("1586958781458431025242759403266842894121773480562120986020912974854563298150952611241517463240701", 10) +var swencSqrtNegThreeMinusOneDivTwo, _ = FQReprFromString("793479390729215512621379701633421447060886740281060493010456487427281649075476305620758731620350", 10) +var swencSqrtNegThreeFQ2 = NewFQ2(FQReprToFQ(swencSqrtNegThree), FQZero.Copy()) +var swencSqrtNegThreeMinusOneDivTwoFQ2 = NewFQ2(FQReprToFQ(swencSqrtNegThreeMinusOneDivTwo), FQZero.Copy()) + +// SWEncodeG2 implements the Shallue-van de Woestijne encoding. +func SWEncodeG2(t FQ2) *G2Affine { + if t.IsZero() { + return G2AffineZero.Copy() + } + + parity := t.Parity() + + w := t.Copy() + w.SquareAssign() + w.AddAssign(BCoeffFQ2) + w.AddAssign(FQ2One) + + if w.IsZero() { + ret := G2AffineOne.Copy() + if parity { + ret.NegAssign() + } + return ret + } + + w.InverseAssign() + w.MulAssign(swencSqrtNegThreeFQ2) + w.MulAssign(t) + + x1 := w.Copy() + x1.MulAssign(t) + x1.NegAssign() + x1.AddAssign(swencSqrtNegThreeMinusOneDivTwoFQ2) + if p := GetG2PointFromX(x1, parity); p != nil { + return p + } + + x2 := x1.Copy() + x2.NegAssign() + x2.SubAssign(FQ2One) + if p := GetG2PointFromX(x2, parity); p != nil { + return p + } + + x3 := w.Copy() + x3.SquareAssign() + x3.InverseAssign() + x3.AddAssign(FQ2One) + return GetG2PointFromX(x3, parity) +} + +var ell2pA = NewFQ2( + FQReprToFQ(NewFQRepr(0)), + FQReprToFQ(NewFQRepr(240)), +) + +var ell2pB = NewFQ2( + FQReprToFQ(NewFQRepr(1012)), + FQReprToFQ(NewFQRepr(1012)), +) + +var qSquaredPlus7Over16 = fqReprFromHexUnchecked("1380cd6fab1fecf3b854bdc8b278c1a18b5978a3b6a3ce0f8d649df8b904b89b1700ffff04feffffcb7f3fffffffc000") + +var rv1 = FQReprToFQ(fqReprFromHexUnchecked("6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09")) +var qMinusRV1 = FQReprToFQ(fqReprFromHexUnchecked("135203e60180a68ee2e9c448d77a2cd91c3dedd930b1cf60ef396489f61eb45e304466cf3e67fa0af1ee7b04121bdea2")) + +var rootsOfUnity = []FQ2{ + NewFQ2(FQOne, FQZero), + NewFQ2(FQZero, FQOne), + NewFQ2(rv1, rv1), + NewFQ2(rv1, qMinusRV1), +} + +var ev1 = FQReprToFQ(fqReprFromHexUnchecked("2c4a7244a026bd3e305cc456ad9e235ed85f8b53954258ec8186bb3d4eccef7c4ee7b8d4b9e063a6c88d0aa3e03ba01")) +var ev2 = FQReprToFQ(fqReprFromHexUnchecked("85fa8cd9105715e641892a0f9a4bb2912b58b8d32f26594c60679cc7973076dc6638358daf3514d6426a813ae01f51a")) +var qMinusEv2 = FQReprToFQ(fqReprFromHexUnchecked("11a1691ca87a753be703151549a6f1ae51c1bff7c092ad2aa12a58d47d3deeb658487ca5d660aeb255d857ec51fdb591")) +var etas = []FQ2{ + NewFQ2(ev1, FQZero), + NewFQ2(FQZero, ev1), + NewFQ2(ev2, ev2), + NewFQ2(ev2, qMinusEv2), +} + +// returns -1 if x is larger than -x otherwise 1 +func signFQ2(f FQ2) int { + thresh := FQReprToFQ(qMinus1Over2) + if f.c1.Cmp(thresh) > 0 { + return -1 + } + if f.c1.Cmp(FQZero) > 0 { + return 1 + } + if f.c0.Cmp(thresh) > 0 { + return -1 + } + if f.c0.Cmp(FQZero) > 0 { + return 1 + } + return 1 +} + +func OptimizedSWU2MapHelper(t FQ2) *G2Affine { + numDenCommon := fq2nqr.Copy() + numDenCommon.SquareAssign() + + tSquared := t.Copy() + tSquared.SquareAssign() + + t4 := tSquared.Copy() + t4.SquareAssign() + + numDenCommon.MulAssign(t4) + + negOneTimesTSquared := fq2nqr.Copy() + negOneTimesTSquared.MulAssign(tSquared) + + numDenCommon.AddAssign(negOneTimesTSquared) + + var x0 FQ2 + if numDenCommon.Equals(FQ2Zero) { + xiA := fq2nqr.Copy() + xiA.MulAssign(ell2pA) + x0 = ell2pB.Copy() + x0.DivAssign(xiA) + } else { + ell2paTimesCommon := ell2pA.Copy() + ell2paTimesCommon.MulAssign(numDenCommon) + negEll2pb := ell2pB.Copy() + negEll2pb.NegAssign() + numDenCommon.AddAssign(FQ2One) + negEll2pb.MulAssign(numDenCommon) + x0 = negEll2pb + x0.DivAssign(ell2paTimesCommon) + } + + x0Cubed := x0.Copy() + x0Cubed.SquareAssign() + x0Cubed.MulAssign(x0) + + ellPAX0 := ell2pA.Copy() + ellPAX0.MulAssign(x0) + + gx0 := x0Cubed + gx0.AddAssign(ellPAX0) + gx0.AddAssign(ell2pB) + + sqrtGX0, found := gx0.Sqrt() + if found { + y0Squared := sqrtGX0.Copy() + y0Squared.SquareAssign() + + if y0Squared.Equals(gx0) { + signT := signFQ2(t) + signYT := signFQ2(sqrtGX0) + + if signT != signYT { + sqrtGX0.NegAssign() + } + + // g(x0) = y^2 as required + return NewG2Affine(x0, sqrtGX0) + } + } + + tCubed := tSquared.Copy() + tCubed.MulAssign(t) + + t6 := tCubed.Copy() + t6.SquareAssign() + + x1 := fq2nqr.Copy() + x1.MulAssign(tSquared) + x1.MulAssign(x0) + + gx1 := fq2nqr.Copy() + gx1.SquareAssign() + gx1.MulAssign(fq2nqr) + gx1.MulAssign(t6) + gx1.MulAssign(gx0) + + y1, found := gx1.Sqrt() + if !found { + panic("This should never happen!") + } + + y12 := y1.Copy() + y12.SquareAssign() + if y12.Equals(gx1) { + signT := signFQ2(t) + signYT := signFQ2(y1) + + if signT != signYT { + y1.NegAssign() + } + + return NewG2Affine(x1, y1) + } + + return nil +} + +// hashFunc returns the SHA-256 hash of the input +func hashFunc(in []byte) []byte { + h := sha256.New() + h.Write(in) + return h.Sum(nil) +} + +func HashG2WithDomain(messageHash [32]byte, domain uint64) *G2Projective { + var domainBytes [8]byte + binary.BigEndian.PutUint64(domainBytes[:], domain) + + xReBytes := append(messageHash[:], domainBytes[:]...) + xReBytes = append(xReBytes, '\x01') + + xImBytes := append(messageHash[:], domainBytes[:]...) + xImBytes = append(xImBytes, '\x02') + + xRe := new(big.Int) + xRe.SetBytes(hashFunc(xReBytes)) + + xIm := new(big.Int) + xIm.SetBytes(hashFunc(xImBytes)) + + // hash function is only 256 bits so this will never overflow + xReFQ, _ := FQReprFromBigInt(xRe) + xImFQ, _ := FQReprFromBigInt(xIm) + + x0 := NewFQ2(FQReprToFQ(xReFQ), FQReprToFQ(xImFQ)) + + for { + gx0 := x0.Copy() + gx0.SquareAssign() + gx0.MulAssign(x0) + + gx0.AddAssign(BCoeffFQ2) + + y0, found := gx0.Sqrt() + + if found { + // favor the lower y value + if !y0.Parity() { + y0.NegAssign() + } + + return NewG2Affine(x0, y0).ScaleByCofactor() + } + + x0.AddAssign(FQ2One) + } +} diff --git a/bls/g2_test.go b/bls/g2_test.go new file mode 100644 index 0000000..24fd6e1 --- /dev/null +++ b/bls/g2_test.go @@ -0,0 +1,91 @@ +package bls_test + +import ( + "testing" + + "github.com/phoreproject/bls" +) + +func BenchmarkG2MulAssign(b *testing.B) { + type mulData struct { + g *bls.G2Projective + f *bls.FR + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]mulData{} + for i := 0; i < g1MulAssignSamples; i++ { + gx, _ := bls.RandFQ2(r) + gy, _ := bls.RandFQ2(r) + gz, _ := bls.RandFQ2(r) + randFR, _ := bls.RandFR(r) + inData[i] = mulData{ + g: bls.NewG2Projective(gx, gy, gz), + f: randFR, + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].g.Mul(inData[count].f.ToRepr().ToFQ()) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkG2AddAssign(b *testing.B) { + type addData struct { + g1 *bls.G2Projective + g2 *bls.G2Projective + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + g1x, _ := bls.RandFQ2(r) + g1y, _ := bls.RandFQ2(r) + g1z, _ := bls.RandFQ2(r) + g2x, _ := bls.RandFQ2(r) + g2y, _ := bls.RandFQ2(r) + g2z, _ := bls.RandFQ2(r) + inData[i] = addData{ + g1: bls.NewG2Projective(g1x, g1y, g1z), + g2: bls.NewG2Projective(g2x, g2y, g2z), + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].g1.Add(inData[count].g2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkG2AddAssignMixed(b *testing.B) { + type addData struct { + g1 *bls.G2Projective + g2 *bls.G2Affine + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + g1x, _ := bls.RandFQ2(r) + g1y, _ := bls.RandFQ2(r) + g1z, _ := bls.RandFQ2(r) + g2x, _ := bls.RandFQ2(r) + g2y, _ := bls.RandFQ2(r) + inData[i] = addData{ + g1: bls.NewG2Projective(g1x, g1y, g1z), + g2: bls.NewG2Affine(g2x, g2y), + } + } + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + inData[count].g1.AddAffine(inData[count].g2) + count = (count + 1) % g1MulAssignSamples + } +} diff --git a/bls/g2pubs/bls.go b/bls/g2pubs/bls.go new file mode 100644 index 0000000..01d5afa --- /dev/null +++ b/bls/g2pubs/bls.go @@ -0,0 +1,275 @@ +package g2pubs + +import ( + "bytes" + "io" + "log" + "sort" + + "github.com/phoreproject/bls" +) + +// Signature is a message signature. +type Signature struct { + s *bls.G1Projective +} + +// Serialize serializes a signature in compressed form. +func (s *Signature) Serialize() [48]byte { + return bls.CompressG1(s.s.ToAffine()) +} + +func (s *Signature) String() string { + return s.s.String() +} + +// NewSignatureFromG1 creates a new signature from a G1 +// element. +func NewSignatureFromG1(g1 *bls.G1Affine) *Signature { + return &Signature{g1.ToProjective()} +} + +// DeserializeSignature deserializes a signature from bytes. +func DeserializeSignature(b [48]byte) (*Signature, error) { + a, err := bls.DecompressG1(b) + if err != nil { + return nil, err + } + + return &Signature{s: a.ToProjective()}, nil +} + +// Copy returns a copy of the signature. +func (s *Signature) Copy() *Signature { + return &Signature{s.s.Copy()} +} + +// PublicKey is a public key. +type PublicKey struct { + p *bls.G2Projective +} + +func (p PublicKey) String() string { + return p.p.String() +} + +// Serialize serializes a public key to bytes. +func (p PublicKey) Serialize() [96]byte { + return bls.CompressG2(p.p.ToAffine()) +} + +// NewPublicKeyFromG2 creates a new public key from a G2 element. +func NewPublicKeyFromG2(g2 *bls.G2Affine) *PublicKey { + return &PublicKey{g2.ToProjective()} +} + +func concatAppend(slices [][]byte) []byte { + var tmp []byte + for _, s := range slices { + tmp = append(tmp, s...) + } + return tmp +} + +// Equals checks if two public keys are equal +func (p PublicKey) Equals(other PublicKey) bool { + return p.p.Equals(other.p) +} + +// DeserializePublicKey deserializes a public key from +// bytes. +func DeserializePublicKey(b [96]byte) (*PublicKey, error) { + a, err := bls.DecompressG2(b) + if err != nil { + return nil, err + } + + return &PublicKey{p: a.ToProjective()}, nil +} + +// SecretKey represents a BLS private key. +type SecretKey struct { + f *bls.FR +} + +// GetFRElement gets the underlying FR element. +func (s SecretKey) GetFRElement() *bls.FR { + return s.f +} + +func (s SecretKey) String() string { + return s.f.String() +} + +// Serialize serializes a secret key to bytes. +func (s SecretKey) Serialize() [32]byte { + return s.f.Bytes() +} + +// DeserializeSecretKey deserializes a secret key from +// bytes. +func DeserializeSecretKey(b [32]byte) *SecretKey { + return &SecretKey{bls.FRReprToFR(bls.FRReprFromBytes(b))} +} + +// DeriveSecretKey derives a secret key from +// bytes. +func DeriveSecretKey(b [32]byte) *SecretKey { + return &SecretKey{bls.HashSecretKey(b)} +} + +// Sign signs a message with a secret key. +func Sign(message []byte, key *SecretKey) *Signature { + h := bls.HashG1(message).MulFR(key.f.ToRepr()) + return &Signature{s: h} +} + +// PrivToPub converts the private key into a public key. +func PrivToPub(k *SecretKey) *PublicKey { + return &PublicKey{p: bls.G2AffineOne.MulFR(k.f.ToRepr())} +} + +// RandKey generates a random secret key. +func RandKey(r io.Reader) (*SecretKey, error) { + k, err := bls.RandFR(r) + if err != nil { + return nil, err + } + s := &SecretKey{f: k} + return s, nil +} + +// KeyFromFQRepr returns a new key based on a FQRepr in +// FR. +func KeyFromFQRepr(i *bls.FRRepr) *SecretKey { + return &SecretKey{f: bls.FRReprToFR(i)} +} + +// Verify verifies a signature against a message and a public key. +func Verify(m []byte, pub *PublicKey, sig *Signature) bool { + h := bls.HashG1(m) + lhs := bls.Pairing(sig.s, bls.G2ProjectiveOne) + rhs := bls.Pairing(h.ToProjective(), pub.p) + return lhs.Equals(rhs) +} + +// AggregateSignatures adds up all of the signatures. +func AggregateSignatures(s []*Signature) *Signature { + newSig := &Signature{s: bls.G1ProjectiveZero.Copy()} + for _, sig := range s { + newSig.Aggregate(sig) + } + return newSig +} + +// Aggregate adds one signature to another +func (s *Signature) Aggregate(other *Signature) { + newS := s.s.Add(other.s) + s.s = newS +} + +// AggregatePublicKeys adds public keys together. +func AggregatePublicKeys(p []*PublicKey) *PublicKey { + newPub := &PublicKey{p: bls.G2ProjectiveZero.Copy()} + for _, pub := range p { + newPub.Aggregate(pub) + } + return newPub +} + +// Aggregate adds two public keys together. +func (p *PublicKey) Aggregate(other *PublicKey) { + newP := p.p.Add(other.p) + p.p = newP +} + +// Copy copies the public key and returns it. +func (p *PublicKey) Copy() *PublicKey { + return &PublicKey{p: p.p.Copy()} +} + +// NewAggregateSignature creates a blank aggregate signature. +func NewAggregateSignature() *Signature { + return &Signature{s: bls.G1ProjectiveZero.Copy()} +} + +// NewAggregatePubkey creates a blank public key. +func NewAggregatePubkey() *PublicKey { + return &PublicKey{p: bls.G2ProjectiveZero.Copy()} +} + +// implement `Interface` in sort package. +type sortableByteArray [][]byte + +func (b sortableByteArray) Len() int { + return len(b) +} + +func (b sortableByteArray) Less(i, j int) bool { + // bytes package already implements Comparable for []byte. + switch bytes.Compare(b[i], b[j]) { + case -1: + return true + case 0, 1: + return false + default: + log.Panic("not fail-able with `bytes.Comparable` bounded [-1, 1].") + return false + } +} + +func (b sortableByteArray) Swap(i, j int) { + b[j], b[i] = b[i], b[j] +} + +func sortByteArrays(src [][]byte) [][]byte { + sorted := sortableByteArray(src) + sort.Sort(sorted) + return sorted +} + +// VerifyAggregate verifies each public key against each message. +func (s *Signature) VerifyAggregate(pubKeys []*PublicKey, msgs [][]byte) bool { + if len(pubKeys) != len(msgs) { + return false + } + + msgsCopy := make([][]byte, len(msgs)) + + for i, m := range msgs { + msgsCopy[i] = make([]byte, len(m)) + copy(msgsCopy[i], m) + } + + msgsSorted := sortByteArrays(msgsCopy) + lastMsg := []byte(nil) + + // check for duplicates + for _, m := range msgsSorted { + if bytes.Equal(m, lastMsg) { + return false + } + lastMsg = m + } + + lhs := bls.Pairing(s.s, bls.G2ProjectiveOne) + rhs := bls.FQ12One.Copy() + for i := range pubKeys { + h := bls.HashG1(msgs[i]) + rhs.MulAssign(bls.Pairing(h.ToProjective(), pubKeys[i].p)) + } + return lhs.Equals(rhs) +} + +// VerifyAggregateCommon verifies each public key against a message. +// This is vulnerable to rogue public-key attack. Each user must +// provide a proof-of-knowledge of the public key. +func (s *Signature) VerifyAggregateCommon(pubKeys []*PublicKey, msg []byte) bool { + h := bls.HashG1(msg) + lhs := bls.Pairing(s.s, bls.G2ProjectiveOne) + rhs := bls.FQ12One.Copy() + for _, p := range pubKeys { + rhs.MulAssign(bls.Pairing(h.ToProjective(), p.p)) + } + return lhs.Equals(rhs) +} diff --git a/bls/g2pubs/bls_test.go b/bls/g2pubs/bls_test.go new file mode 100644 index 0000000..cdcedfa --- /dev/null +++ b/bls/g2pubs/bls_test.go @@ -0,0 +1,333 @@ +package g2pubs_test + +import ( + "errors" + "fmt" + "testing" + + "github.com/phoreproject/bls" + "github.com/phoreproject/bls/g2pubs" +) + +type XORShift struct { + state uint64 +} + +func NewXORShift(state uint64) *XORShift { + return &XORShift{state} +} + +func (xor *XORShift) Read(b []byte) (int, error) { + for i := range b { + x := xor.state + x ^= x << 13 + x ^= x >> 7 + x ^= x << 17 + b[i] = uint8(x) + xor.state = x + } + return len(b), nil +} + +func SignVerify(loopCount int) error { + r := NewXORShift(1) + for i := 0; i < loopCount; i++ { + priv, _ := g2pubs.RandKey(r) + pub := g2pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf("Hello world! 16 characters %d", i)) + sig := g2pubs.Sign(msg, priv) + if !g2pubs.Verify(msg, pub, sig) { + return errors.New("sig did not verify") + } + } + return nil +} + +func SignVerifyAggregateCommonMessage(loopCount int) error { + r := NewXORShift(2) + pubkeys := make([]*g2pubs.PublicKey, 0, 1000) + sigs := make([]*g2pubs.Signature, 0, 1000) + msg := []byte(">16 character identical message") + for i := 0; i < loopCount; i++ { + priv, _ := g2pubs.RandKey(r) + pub := g2pubs.PrivToPub(priv) + sig := g2pubs.Sign(msg, priv) + pubkeys = append(pubkeys, pub) + sigs = append(sigs, sig) + if i < 10 || i > (loopCount-5) { + newSig := g2pubs.AggregateSignatures(sigs) + if !newSig.VerifyAggregateCommon(pubkeys, msg) { + return errors.New("sig did not verify") + } + } + } + return nil +} + +func SignVerifyAggregateCommonMessageMissingSig(loopCount int) error { + r := NewXORShift(3) + skippedSig := loopCount / 2 + pubkeys := make([]*g2pubs.PublicKey, 0, 1000) + sigs := make([]*g2pubs.Signature, 0, 1000) + msg := []byte(">16 character identical message") + for i := 0; i < loopCount; i++ { + priv, _ := g2pubs.RandKey(r) + pub := g2pubs.PrivToPub(priv) + sig := g2pubs.Sign(msg, priv) + pubkeys = append(pubkeys, pub) + if i != skippedSig { + sigs = append(sigs, sig) + } + if i < 10 || i > (loopCount-5) { + newSig := g2pubs.AggregateSignatures(sigs) + if newSig.VerifyAggregateCommon(pubkeys, msg) != (i < skippedSig) { + return errors.New("sig did not verify") + } + } + } + return nil +} + +func AggregateSignatures(loopCount int) error { + r := NewXORShift(4) + pubkeys := make([]*g2pubs.PublicKey, 0, 1000) + msgs := make([][]byte, 0, 1000) + sigs := make([]*g2pubs.Signature, 0, 1000) + for i := 0; i < loopCount; i++ { + priv, _ := g2pubs.RandKey(r) + pub := g2pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf(">16 character identical message %d", i)) + sig := g2pubs.Sign(msg, priv) + pubkeys = append(pubkeys, pub) + msgs = append(msgs, msg) + sigs = append(sigs, sig) + + if i < 10 || i > (loopCount-5) { + newSig := g2pubs.AggregateSignatures(sigs) + if !newSig.VerifyAggregate(pubkeys, msgs) { + return errors.New("sig did not verify") + } + } + } + return nil +} + +func TestSignVerify(t *testing.T) { + err := SignVerify(10) + if err != nil { + t.Fatal(err) + } +} + +func TestSignVerifyAggregateCommon(t *testing.T) { + err := SignVerifyAggregateCommonMessage(10) + if err != nil { + t.Fatal(err) + } +} + +func TestSignVerifyAggregateCommonMissingSig(t *testing.T) { + err := SignVerifyAggregateCommonMessageMissingSig(10) + if err != nil { + t.Fatal(err) + } +} + +func TestSignAggregateSigs(t *testing.T) { + err := AggregateSignatures(10) + if err != nil { + t.Fatal(err) + } +} + +func TestAggregateSignaturesDuplicatedMessages(t *testing.T) { + r := NewXORShift(5) + + pubkeys := make([]*g2pubs.PublicKey, 0, 1000) + msgs := make([][]byte, 0, 1000) + sigs := g2pubs.NewAggregateSignature() + + key, _ := g2pubs.RandKey(r) + pub := g2pubs.PrivToPub(key) + message := []byte(">16 char first message") + sig := g2pubs.Sign(message, key) + pubkeys = append(pubkeys, pub) + msgs = append(msgs, message) + sigs.Aggregate(sig) + + if !sigs.VerifyAggregate(pubkeys, msgs) { + t.Fatal("signature does not verify") + } + + key2, _ := g2pubs.RandKey(r) + pub2 := g2pubs.PrivToPub(key2) + message2 := []byte(">16 char second message") + sig2 := g2pubs.Sign(message2, key2) + pubkeys = append(pubkeys, pub2) + msgs = append(msgs, message2) + sigs.Aggregate(sig2) + + if !sigs.VerifyAggregate(pubkeys, msgs) { + t.Fatal("signature does not verify") + } + + key3, _ := g2pubs.RandKey(r) + pub3 := g2pubs.PrivToPub(key3) + sig3 := g2pubs.Sign(message2, key3) + pubkeys = append(pubkeys, pub3) + msgs = append(msgs, message2) + sigs.Aggregate(sig3) + + if sigs.VerifyAggregate(pubkeys, msgs) { + t.Fatal("signature verifies with duplicate message") + } +} + +func TestAggregateSigsSeparate(t *testing.T) { + x := NewXORShift(20) + priv1, _ := g2pubs.RandKey(x) + priv2, _ := g2pubs.RandKey(x) + priv3, _ := g2pubs.RandKey(x) + + pub1 := g2pubs.PrivToPub(priv1) + pub2 := g2pubs.PrivToPub(priv2) + pub3 := g2pubs.PrivToPub(priv3) + + msg := []byte("test 1") + sig1 := g2pubs.Sign(msg, priv1) + sig2 := g2pubs.Sign(msg, priv2) + sig3 := g2pubs.Sign(msg, priv3) + + aggSigs := g2pubs.AggregateSignatures([]*g2pubs.Signature{sig1, sig2, sig3}) + + aggPubs := g2pubs.NewAggregatePubkey() + aggPubs.Aggregate(pub1) + aggPubs.Aggregate(pub2) + aggPubs.Aggregate(pub3) + + valid := g2pubs.Verify(msg, aggPubs, aggSigs) + if !valid { + t.Fatal("expected aggregate signature to be valid") + } +} + +func BenchmarkBLSAggregateSignature(b *testing.B) { + r := NewXORShift(5) + priv, _ := g2pubs.RandKey(r) + msg := []byte(fmt.Sprintf(">16 character identical message")) + sig := g2pubs.Sign(msg, priv) + + s := g2pubs.NewAggregateSignature() + b.ResetTimer() + for i := 0; i < b.N; i++ { + s.Aggregate(sig) + } +} + +func BenchmarkBLSSign(b *testing.B) { + r := NewXORShift(5) + privs := make([]*g2pubs.SecretKey, b.N) + for i := range privs { + privs[i], _ = g2pubs.RandKey(r) + } + b.ResetTimer() + for i := 0; i < b.N; i++ { + + msg := []byte(fmt.Sprintf("Hello world! 16 characters %d", i)) + g2pubs.Sign(msg, privs[i]) + // if !g2pubs.Verify(msg, pub, sig) { + // return errors.New("sig did not verify") + // } + } +} + +func BenchmarkBLSVerify(b *testing.B) { + r := NewXORShift(5) + priv, _ := g2pubs.RandKey(r) + pub := g2pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf(">16 character identical message")) + sig := g2pubs.Sign(msg, priv) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + g2pubs.Verify(msg, pub, sig) + } +} + +func TestSignatureSerializeDeserialize(t *testing.T) { + r := NewXORShift(1) + priv, _ := g2pubs.RandKey(r) + pub := g2pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf(">16 character identical message")) + sig := g2pubs.Sign(msg, priv) + + if !g2pubs.Verify(msg, pub, sig) { + t.Fatal("message did not verify before serialization/deserialization") + } + + sigSer := sig.Serialize() + sigDeser, err := g2pubs.DeserializeSignature(sigSer) + if err != nil { + t.Fatal(err) + } + if !g2pubs.Verify(msg, pub, sigDeser) { + t.Fatal("message did not verify after serialization/deserialization") + } +} + +func TestPubkeySerializeDeserialize(t *testing.T) { + r := NewXORShift(1) + priv, _ := g2pubs.RandKey(r) + pub := g2pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf(">16 character identical message")) + sig := g2pubs.Sign(msg, priv) + + if !g2pubs.Verify(msg, pub, sig) { + t.Fatal("message did not verify before serialization/deserialization of pubkey") + } + + pubSer := pub.Serialize() + pubDeser, err := g2pubs.DeserializePublicKey(pubSer) + if err != nil { + t.Fatal(err) + } + if !g2pubs.Verify(msg, pubDeser, sig) { + t.Fatal("message did not verify after serialization/deserialization of pubkey") + } +} + +func TestSecretkeySerializeDeserialize(t *testing.T) { + r := NewXORShift(3) + priv, _ := g2pubs.RandKey(r) + privSer := priv.Serialize() + privNew := g2pubs.DeserializeSecretKey(privSer) + pub := g2pubs.PrivToPub(priv) + msg := []byte(fmt.Sprintf(">16 character identical message")) + sig := g2pubs.Sign(msg, privNew) + + if !g2pubs.Verify(msg, pub, sig) { + t.Fatal("message did not verify before serialization/deserialization of secret") + } + + pubSer := pub.Serialize() + pubDeser, err := g2pubs.DeserializePublicKey(pubSer) + if err != nil { + t.Fatal(err) + } + if !g2pubs.Verify(msg, pubDeser, sig) { + t.Fatal("message did not verify after serialization/deserialization of secret") + } +} + +func TestDeriveSecretKey(t *testing.T) { + var secKeyIn [32]byte + copy(secKeyIn[:], []byte("11223344556677889900112233445566")) + k := g2pubs.DeriveSecretKey(secKeyIn) + + expectedElement, _ := bls.FRReprFromString("414e2c2a330cf94edb70e1c88efa851e80fe5eb14ff08fe5b7e588b4fe9899e4", 16) + expectedFRElement := bls.FRReprToFR(expectedElement) + + if !expectedFRElement.Equals(k.GetFRElement()) { + t.Fatal("expected secret key to match") + } +} diff --git a/bls/hash.go b/bls/hash.go new file mode 100644 index 0000000..cfd6a3f --- /dev/null +++ b/bls/hash.go @@ -0,0 +1,411 @@ +package bls + +import ( + "crypto/sha256" + "encoding/binary" + "math/big" +) + +func HashSecretKey(b [32]byte) *FR { + // this implements hash_to_field as defined in the IETF standard with: + // msg = b + // ctr = 0 + // m = 1 + // hash_fn = SHA256 + // hash_reps = 2 + + h := sha256.New() + h.Write(b[:]) + msgHash := h.Sum(nil) + msgPrime := append(msgHash, 0x00) + + t := []byte{} + for j := uint16(1); j <= uint16(2); j++ { + h := sha256.New() + h.Write(msgPrime) + h.Write([]byte{0x01}) + var jByte [2]byte + binary.BigEndian.PutUint16(jByte[:], j) + h.Write(jByte[1:]) + out := h.Sum(nil) + t = append(t, out...) + } + + tBig := new(big.Int) + tBig.SetBytes(t) + tBig.Mod(tBig, RFieldModulus.ToBig()) + tFR, _ := FRReprFromBigInt(tBig) + return FRReprToFR(tFR) +} + +func hp(msg []byte, ctr uint8) FQ { + // this implements hash_to_field as defined in the IETF standard with: + // msg = msg + // ctr = ctr + // m = 1 + // hash_fn = SHA256 + // hash_reps = 2 + h := sha256.New() + h.Write(msg) + msgHash := h.Sum(nil) + var ctrBytes [2]byte + binary.BigEndian.PutUint16(ctrBytes[:], uint16(ctr)) + msgPrime := append(msgHash, ctrBytes[1:]...) + + t := []byte{} + for j := uint16(1); j <= uint16(2); j++ { + h := sha256.New() + h.Write(msgPrime) + h.Write([]byte{0x01}) + var jByte [2]byte + binary.BigEndian.PutUint16(jByte[:], j) + h.Write(jByte[1:]) + out := h.Sum(nil) + t = append(t, out...) + } + + tBig := new(big.Int) + tBig.SetBytes(t) + tBig.Mod(tBig, QFieldModulus.ToBig()) + tFQ, _ := FQReprFromBigInt(tBig) + return FQReprToFQ(tFQ) +} + +func hp2(msg []byte, ctr uint8) FQ2 { + // this implements hash_to_field as defined in the IETF standard with: + // msg = msg + // ctr = ctr + // m = 2 + // hash_fn = SHA256 + // hash_reps = 2 + h := sha256.New() + h.Write(msg) + msgHash := h.Sum(nil) + var ctrBytes [2]byte + binary.BigEndian.PutUint16(ctrBytes[:], uint16(ctr)) + msgPrime := append(msgHash, ctrBytes[1:]...) + + var fqs [2]FQ + for i := uint16(1); i <= 2; i++ { + t := []byte{} + + for j := uint16(1); j <= uint16(2); j++ { + h := sha256.New() + h.Write(msgPrime) + var iByte [2]byte + binary.BigEndian.PutUint16(iByte[:], i) + h.Write(iByte[1:]) + var jByte [2]byte + binary.BigEndian.PutUint16(jByte[:], j) + h.Write(jByte[1:]) + out := h.Sum(nil) + t = append(t, out...) + } + + tBig := new(big.Int) + tBig.SetBytes(t) + tBig.Mod(tBig, QFieldModulus.ToBig()) + tFQ, _ := FQReprFromBigInt(tBig) + fqs[i-1] = FQReprToFQ(tFQ) + } + + return NewFQ2(fqs[0], fqs[1]) +} + +// 11-isogeny from Ell1' to Ell1 map coefficients +var xNum11 = []FQ{ + FQReprToFQ(fqReprFromHexUnchecked("11a05f2b1e833340b809101dd99815856b303e88a2d7005ff2627b56cdb4e2c85610c2d5f2e62d6eaeac1662734649b7")), + FQReprToFQ(fqReprFromHexUnchecked("17294ed3e943ab2f0588bab22147a81c7c17e75b2f6a8417f565e33c70d1e86b4838f2a6f318c356e834eef1b3cb83bb")), + FQReprToFQ(fqReprFromHexUnchecked("d54005db97678ec1d1048c5d10a9a1bce032473295983e56878e501ec68e25c958c3e3d2a09729fe0179f9dac9edcb0")), + FQReprToFQ(fqReprFromHexUnchecked("1778e7166fcc6db74e0609d307e55412d7f5e4656a8dbf25f1b33289f1b330835336e25ce3107193c5b388641d9b6861")), + FQReprToFQ(fqReprFromHexUnchecked("e99726a3199f4436642b4b3e4118e5499db995a1257fb3f086eeb65982fac18985a286f301e77c451154ce9ac8895d9")), + FQReprToFQ(fqReprFromHexUnchecked("1630c3250d7313ff01d1201bf7a74ab5db3cb17dd952799b9ed3ab9097e68f90a0870d2dcae73d19cd13c1c66f652983")), + FQReprToFQ(fqReprFromHexUnchecked("d6ed6553fe44d296a3726c38ae652bfb11586264f0f8ce19008e218f9c86b2a8da25128c1052ecaddd7f225a139ed84")), + FQReprToFQ(fqReprFromHexUnchecked("17b81e7701abdbe2e8743884d1117e53356de5ab275b4db1a682c62ef0f2753339b7c8f8c8f475af9ccb5618e3f0c88e")), + FQReprToFQ(fqReprFromHexUnchecked("80d3cf1f9a78fc47b90b33563be990dc43b756ce79f5574a2c596c928c5d1de4fa295f296b74e956d71986a8497e317")), + FQReprToFQ(fqReprFromHexUnchecked("169b1f8e1bcfa7c42e0c37515d138f22dd2ecb803a0c5c99676314baf4bb1b7fa3190b2edc0327797f241067be390c9e")), + FQReprToFQ(fqReprFromHexUnchecked("10321da079ce07e272d8ec09d2565b0dfa7dccdde6787f96d50af36003b14866f69b771f8c285decca67df3f1605fb7b")), + FQReprToFQ(fqReprFromHexUnchecked("6e08c248e260e70bd1e962381edee3d31d79d7e22c837bc23c0bf1bc24c6b68c24b1b80b64d391fa9c8ba2e8ba2d229")), +} + +var xDen11 = []FQ{ + FQReprToFQ(fqReprFromHexUnchecked("8ca8d548cff19ae18b2e62f4bd3fa6f01d5ef4ba35b48ba9c9588617fc8ac62b558d681be343df8993cf9fa40d21b1c")), + FQReprToFQ(fqReprFromHexUnchecked("12561a5deb559c4348b4711298e536367041e8ca0cf0800c0126c2588c48bf5713daa8846cb026e9e5c8276ec82b3bff")), + FQReprToFQ(fqReprFromHexUnchecked("b2962fe57a3225e8137e629bff2991f6f89416f5a718cd1fca64e00b11aceacd6a3d0967c94fedcfcc239ba5cb83e19")), + FQReprToFQ(fqReprFromHexUnchecked("3425581a58ae2fec83aafef7c40eb545b08243f16b1655154cca8abc28d6fd04976d5243eecf5c4130de8938dc62cd8")), + FQReprToFQ(fqReprFromHexUnchecked("13a8e162022914a80a6f1d5f43e7a07dffdfc759a12062bb8d6b44e833b306da9bd29ba81f35781d539d395b3532a21e")), + FQReprToFQ(fqReprFromHexUnchecked("e7355f8e4e667b955390f7f0506c6e9395735e9ce9cad4d0a43bcef24b8982f7400d24bc4228f11c02df9a29f6304a5")), + FQReprToFQ(fqReprFromHexUnchecked("772caacf16936190f3e0c63e0596721570f5799af53a1894e2e073062aede9cea73b3538f0de06cec2574496ee84a3a")), + FQReprToFQ(fqReprFromHexUnchecked("14a7ac2a9d64a8b230b3f5b074cf01996e7f63c21bca68a81996e1cdf9822c580fa5b9489d11e2d311f7d99bbdcc5a5e")), + FQReprToFQ(fqReprFromHexUnchecked("a10ecf6ada54f825e920b3dafc7a3cce07f8d1d7161366b74100da67f39883503826692abba43704776ec3a79a1d641")), + FQReprToFQ(fqReprFromHexUnchecked("95fc13ab9e92ad4476d6e3eb3a56680f682b4ee96f7d03776df533978f31c1593174e4b4b7865002d6384d168ecdd0a")), + FQReprToFQ(fqReprFromHexUnchecked("1")), +} + +var yNum11 = []FQ{ + FQReprToFQ(fqReprFromHexUnchecked("90d97c81ba24ee0259d1f094980dcfa11ad138e48a869522b52af6c956543d3cd0c7aee9b3ba3c2be9845719707bb33")), + FQReprToFQ(fqReprFromHexUnchecked("134996a104ee5811d51036d776fb46831223e96c254f383d0f906343eb67ad34d6c56711962fa8bfe097e75a2e41c696")), + FQReprToFQ(fqReprFromHexUnchecked("cc786baa966e66f4a384c86a3b49942552e2d658a31ce2c344be4b91400da7d26d521628b00523b8dfe240c72de1f6")), + FQReprToFQ(fqReprFromHexUnchecked("1f86376e8981c217898751ad8746757d42aa7b90eeb791c09e4a3ec03251cf9de405aba9ec61deca6355c77b0e5f4cb")), + FQReprToFQ(fqReprFromHexUnchecked("8cc03fdefe0ff135caf4fe2a21529c4195536fbe3ce50b879833fd221351adc2ee7f8dc099040a841b6daecf2e8fedb")), + FQReprToFQ(fqReprFromHexUnchecked("16603fca40634b6a2211e11db8f0a6a074a7d0d4afadb7bd76505c3d3ad5544e203f6326c95a807299b23ab13633a5f0")), + FQReprToFQ(fqReprFromHexUnchecked("4ab0b9bcfac1bbcb2c977d027796b3ce75bb8ca2be184cb5231413c4d634f3747a87ac2460f415ec961f8855fe9d6f2")), + FQReprToFQ(fqReprFromHexUnchecked("987c8d5333ab86fde9926bd2ca6c674170a05bfe3bdd81ffd038da6c26c842642f64550fedfe935a15e4ca31870fb29")), + FQReprToFQ(fqReprFromHexUnchecked("9fc4018bd96684be88c9e221e4da1bb8f3abd16679dc26c1e8b6e6a1f20cabe69d65201c78607a360370e577bdba587")), + FQReprToFQ(fqReprFromHexUnchecked("e1bba7a1186bdb5223abde7ada14a23c42a0ca7915af6fe06985e7ed1e4d43b9b3f7055dd4eba6f2bafaaebca731c30")), + FQReprToFQ(fqReprFromHexUnchecked("19713e47937cd1be0dfd0b8f1d43fb93cd2fcbcb6caf493fd1183e416389e61031bf3a5cce3fbafce813711ad011c132")), + FQReprToFQ(fqReprFromHexUnchecked("18b46a908f36f6deb918c143fed2edcc523559b8aaf0c2462e6bfe7f911f643249d9cdf41b44d606ce07c8a4d0074d8e")), + FQReprToFQ(fqReprFromHexUnchecked("b182cac101b9399d155096004f53f447aa7b12a3426b08ec02710e807b4633f06c851c1919211f20d4c04f00b971ef8")), + FQReprToFQ(fqReprFromHexUnchecked("245a394ad1eca9b72fc00ae7be315dc757b3b080d4c158013e6632d3c40659cc6cf90ad1c232a6442d9d3f5db980133")), + FQReprToFQ(fqReprFromHexUnchecked("5c129645e44cf1102a159f748c4a3fc5e673d81d7e86568d9ab0f5d396a7ce46ba1049b6579afb7866b1e715475224b")), + FQReprToFQ(fqReprFromHexUnchecked("15e6be4e990f03ce4ea50b3b42df2eb5cb181d8f84965a3957add4fa95af01b2b665027efec01c7704b456be69c8b604")), +} + +var yDen11 = []FQ{ + FQReprToFQ(fqReprFromHexUnchecked("16112c4c3a9c98b252181140fad0eae9601a6de578980be6eec3232b5be72e7a07f3688ef60c206d01479253b03663c1")), + FQReprToFQ(fqReprFromHexUnchecked("1962d75c2381201e1a0cbd6c43c348b885c84ff731c4d59ca4a10356f453e01f78a4260763529e3532f6102c2e49a03d")), + FQReprToFQ(fqReprFromHexUnchecked("58df3306640da276faaae7d6e8eb15778c4855551ae7f310c35a5dd279cd2eca6757cd636f96f891e2538b53dbf67f2")), + FQReprToFQ(fqReprFromHexUnchecked("16b7d288798e5395f20d23bf89edb4d1d115c5dbddbcd30e123da489e726af41727364f2c28297ada8d26d98445f5416")), + FQReprToFQ(fqReprFromHexUnchecked("be0e079545f43e4b00cc912f8228ddcc6d19c9f0f69bbb0542eda0fc9dec916a20b15dc0fd2ededda39142311a5001d")), + FQReprToFQ(fqReprFromHexUnchecked("8d9e5297186db2d9fb266eaac783182b70152c65550d881c5ecd87b6f0f5a6449f38db9dfa9cce202c6477faaf9b7ac")), + FQReprToFQ(fqReprFromHexUnchecked("166007c08a99db2fc3ba8734ace9824b5eecfdfa8d0cf8ef5dd365bc400a0051d5fa9c01a58b1fb93d1a1399126a775c")), + FQReprToFQ(fqReprFromHexUnchecked("16a3ef08be3ea7ea03bcddfabba6ff6ee5a4375efa1f4fd7feb34fd206357132b920f5b00801dee460ee415a15812ed9")), + FQReprToFQ(fqReprFromHexUnchecked("1866c8ed336c61231a1be54fd1d74cc4f9fb0ce4c6af5920abc5750c4bf39b4852cfe2f7bb9248836b233d9d55535d4a")), + FQReprToFQ(fqReprFromHexUnchecked("167a55cda70a6e1cea820597d94a84903216f763e13d87bb5308592e7ea7d4fbc7385ea3d529b35e346ef48bb8913f55")), + FQReprToFQ(fqReprFromHexUnchecked("4d2f259eea405bd48f010a01ad2911d9c6dd039bb61a6290e591b36e636a5c871a5c29f4f83060400f8b49cba8f6aa8")), + FQReprToFQ(fqReprFromHexUnchecked("accbb67481d033ff5852c1e48c50c477f94ff8aefce42d28c0f9a88cea7913516f968986f7ebbea9684b529e2561092")), + FQReprToFQ(fqReprFromHexUnchecked("ad6b9514c767fe3c3613144b45f1496543346d98adf02267d5ceef9a00d9b8693000763e3b90ac11e99b138573345cc")), + FQReprToFQ(fqReprFromHexUnchecked("2660400eb2e4f3b628bdd0d53cd76f2bf565b94e72927c1cb748df27942480e420517bd8714cc80d1fadc1326ed06f7")), + FQReprToFQ(fqReprFromHexUnchecked("e0fa1d816ddc03e6b24255e0d7819c171c40f65e273b853324efcd6356caa205ca2f570f13497804415473a1d634b8f")), + FQReprToFQ(fqReprFromHexUnchecked("1")), +} + +var mapCoeffs11 = [][]FQ{xNum11, xDen11, yNum11, yDen11} + +func iso11(p *G1Affine) *G1Affine { + x := p.x + y := p.y + mapVals := make([]FQ, 4) + + for idx, coeffs := range mapCoeffs11 { + mapVals[idx] = coeffs[len(coeffs)-1] + for coeffIdx := len(coeffs) - 2; coeffIdx >= 0; coeffIdx-- { + mapVals[idx].MulAssign(x) + mapVals[idx].AddAssign(coeffs[coeffIdx]) + } + } + + newX := mapVals[0] + newX.DivAssign(mapVals[1]) + + newY := y.Copy() + newY.MulAssign(mapVals[2]) + newY.DivAssign(mapVals[3]) + + return NewG1Affine(newX, newY) +} + +var xNum3 = []FQ2{ + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6")), + FQReprToFQ(fqReprFromHexUnchecked("5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97d6")), + ), + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("0")), + FQReprToFQ(fqReprFromHexUnchecked("11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71a")), + ), + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71e")), + FQReprToFQ(fqReprFromHexUnchecked("8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38d")), + ), + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("171d6541fa38ccfaed6dea691f5fb614cb14b4e7f4e810aa22d6108f142b85757098e38d0f671c7188e2aaaaaaaa5ed1")), + FQReprToFQ(fqReprFromHexUnchecked("0")), + ), +} + +var xDen3 = []FQ2{ + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("0")), + FQReprToFQ(fqReprFromHexUnchecked("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa63")), + ), + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("c")), + FQReprToFQ(fqReprFromHexUnchecked("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa9f")), + ), + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("1")), + FQReprToFQ(fqReprFromHexUnchecked("0")), + ), +} + +var yNum3 = []FQ2{ + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706")), + FQReprToFQ(fqReprFromHexUnchecked("1530477c7ab4113b59a4c18b076d11930f7da5d4a07f649bf54439d87d27e500fc8c25ebf8c92f6812cfc71c71c6d706")), + ), + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("0")), + FQReprToFQ(fqReprFromHexUnchecked("5c759507e8e333ebb5b7a9a47d7ed8532c52d39fd3a042a88b58423c50ae15d5c2638e343d9c71c6238aaaaaaaa97be")), + ), + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("11560bf17baa99bc32126fced787c88f984f87adf7ae0c7f9a208c6b4f20a4181472aaa9cb8d555526a9ffffffffc71c")), + FQReprToFQ(fqReprFromHexUnchecked("8ab05f8bdd54cde190937e76bc3e447cc27c3d6fbd7063fcd104635a790520c0a395554e5c6aaaa9354ffffffffe38f")), + ), + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("124c9ad43b6cf79bfbf7043de3811ad0761b0f37a1e26286b0e977c69aa274524e79097a56dc4bd9e1b371c71c718b10")), + FQReprToFQ(fqReprFromHexUnchecked("0")), + ), +} + +var yDen3 = []FQ2{ + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb")), + FQReprToFQ(fqReprFromHexUnchecked("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa8fb")), + ), + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("0")), + FQReprToFQ(fqReprFromHexUnchecked("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffa9d3")), + ), + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("12")), + FQReprToFQ(fqReprFromHexUnchecked("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaa99")), + ), + NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("1")), + FQReprToFQ(fqReprFromHexUnchecked("0")), + ), +} + +var mapCoeffs3 = [][]FQ2{xNum3, xDen3, yNum3, yDen3} + +func iso3(p *G2Affine) *G2Affine { + x := p.x + y := p.y + mapVals := make([]FQ2, 4) + + for idx, coeffs := range mapCoeffs3 { + mapVals[idx] = coeffs[len(coeffs)-1] + for coeffIdx := len(coeffs) - 2; coeffIdx >= 0; coeffIdx-- { + mapVals[idx].MulAssign(x) + mapVals[idx].AddAssign(coeffs[coeffIdx]) + } + } + + newX := mapVals[0] + newX.DivAssign(mapVals[1]) + + newY := y.Copy() + newY.MulAssign(mapVals[2]) + newY.DivAssign(mapVals[3]) + + return NewG2Affine(newX, newY) +} + +// ClearH clears the cofactor for Ell1. +func ClearH(p *G1Affine) *G1Affine { + xP := p.Mul(NewFQRepr(0xd201000000010000)) + return xP.AddAffine(p).ToAffine() +} + +func optimizedSWUMap(t1 *FQ, t2 *FQ) *G1Affine { + Pp := optimizedSWUMapHelper(*t1) + + if t2 != nil { + Pp2 := optimizedSWUMapHelper(*t2) + + Pp = Pp.ToProjective().AddAffine(Pp2).ToAffine() + } + Pp = iso11(Pp) + return ClearH(Pp) +} + +const cipherSuite = 0x01 + +// HashG1 converts a message to a point on the G2 curve. +func HashG1(msg []byte) *G1Affine { + cipherSuiteAndMessage := append([]byte{cipherSuite}, msg...) + t1 := hp(cipherSuiteAndMessage, 0) + t2 := hp(cipherSuiteAndMessage, 1) + return optimizedSWUMap(&t1, &t2) +} + +var iwsc = NewFQ2( + FQReprToFQ(fqReprFromHexUnchecked("d0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd556")), + FQReprToFQ(fqReprFromHexUnchecked("d0088f51cbff34d258dd3db21a5d66bb23ba5c279c2895fb39869507b587b120f55ffff58a9ffffdcff7fffffffd555")), +) + +var kQiX = FQReprToFQ(fqReprFromHexUnchecked("1a0111ea397fe699ec02408663d4de85aa0d857d89759ad4897d29650fb85f9b409427eb4f49fffd8bfd00000000aaad")) +var kQiY = FQReprToFQ(fqReprFromHexUnchecked("6af0e0437ff400b6831e36d6bd17ffe48395dabc2d3435e77f76e17009241c5ee67992f72ec05f4c81084fbede3cc09")) + +func psi(g *G2Affine) *G2Affine { + newX := fq2nqr.Copy() + qiX := iwsc.Copy() + qiX.MulAssign(g.x) + qiX.c0.MulAssign(kQiX) + qiX.c1.MulAssign(kQiX) + qiX.c1.NegAssign() + newX.MulAssign(qiX) + + newY := fq2nqr.Copy() + qiY := iwsc.Copy() + qiY.MulAssign(g.y) + + y0y1 := qiY.c0.Copy() + y0y1.AddAssign(qiY.c1) + y0y1.MulAssign(kQiY) + + y0SubY1 := qiY.c0.Copy() + y0SubY1.SubAssign(qiY.c1) + y0SubY1.MulAssign(kQiY) + + qiY = NewFQ2(y0y1, y0SubY1) + newY.MulAssign(qiY) + + return NewG2Affine(newX, newY) +} + +func clearH2(p *G2Affine) *G2Affine { + work := p.Mul(NewFQRepr(0xd201000000010000)) + + work = work.AddAffine(p) + + minusPsiP := psi(p) + minusPsiP.NegAssign() + + work = work.AddAffine(minusPsiP) + + work = work.Mul(NewFQRepr(0xd201000000010000)) + work = work.AddAffine(minusPsiP) + + negP := p.Copy() + negP.NegAssign() + + work = work.AddAffine(negP) + p2 := p.ToProjective().Double().ToAffine() + psiPsi2P := psi(psi(p2)) + work = work.AddAffine(psiPsi2P) + return work.ToAffine() +} + +func optimizedSWUMap2(t1 *FQ2, t2 *FQ2) *G2Affine { + Pp := OptimizedSWU2MapHelper(*t1) + + if t2 != nil { + Pp2 := OptimizedSWU2MapHelper(*t2) + + Pp = Pp.ToProjective().AddAffine(Pp2).ToAffine() + } + Pp = iso3(Pp) + + return clearH2(Pp) +} + +// HashG2 converts a message to a point on the G2 curve. +func HashG2(msg []byte) *G2Affine { + cipherSuiteAndMessage := append([]byte{cipherSuite}, msg...) + t1 := hp2(cipherSuiteAndMessage, 0) + t2 := hp2(cipherSuiteAndMessage, 1) + h := optimizedSWUMap2(&t1, &t2) + return h +} diff --git a/bls/hash_test.go b/bls/hash_test.go new file mode 100644 index 0000000..afbbf4d --- /dev/null +++ b/bls/hash_test.go @@ -0,0 +1,102 @@ +package bls_test + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "testing" + + "github.com/phoreproject/bls" +) + +var expectedG1X, _ = bls.FQReprFromString("157b33991dfbf91105d15160da770421049539e3708dbe7d0fac0bcdcf70cde4e0ba392e05d248ebeea681629653501e", 16) +var expectedG1Y, _ = bls.FQReprFromString("186b975a6b00f9ed4f4415de4a7df651ed0d1ac4a2e50d145028b596b6493ec4ec6a3259d4897676c2c2c67af5f581d5", 16) + +var expectedG1Hash = bls.NewG1Affine( + bls.FQReprToFQ(expectedG1X), + bls.FQReprToFQ(expectedG1Y), +) + +func TestHashG1(t *testing.T) { + actualHash := bls.HashG1([]byte("the message to be signed")) + + if !actualHash.Equals(expectedG1Hash) { + t.Fatal("expected hash to match other implementations") + } +} + +func BenchmarkHashG1(t *testing.B) { + data := make([][]byte, 100) + r := NewXORShift(2) + + h := sha256.New() + for i := range data { + h.Reset() + randBytes := make([]byte, 32) + r.Read(randBytes) + h.Write(randBytes) + data[i] = h.Sum(nil) + } + + t.ResetTimer() + + for i := 0; i < t.N; i++ { + bls.HashG1(data[i%len(data)]) + } +} + +var expectedG2c0X, _ = bls.FQReprFromString("cac64370233bfc0a5cb46981969ef1aec583bb661084c7940581cda548f5bf015c74bf23ccdb87816a3cd96ebdb2bfd", 16) +var expectedG2c1X, _ = bls.FQReprFromString("11f6e0fdfbc31b55c04cda9c896099c9f135c2eb2c504cfe0b98e98ef0e511583a9b1eb47b4c19904d820c2eab6608a9", 16) +var expectedG2c0Y, _ = bls.FQReprFromString("19ffc47d113a320834d1b3979a932c1224195be2cd83b7cd70e1800b56d9a8b94a3ce0e303cdda31e191ecc48223906f", 16) +var expectedG2c1Y, _ = bls.FQReprFromString("c75d7aae0477efd4219f9d01950fa9e83378162d7befe1e9df0e506503f04d7a49250d6a85f09dffa245a8fe583d3fd", 16) + +var expectedG2Hash = bls.NewG2Affine( + bls.NewFQ2( + bls.FQReprToFQ(expectedG2c0X), + bls.FQReprToFQ(expectedG2c1X), + ), + bls.NewFQ2( + bls.FQReprToFQ(expectedG2c0Y), + bls.FQReprToFQ(expectedG2c1Y), + ), +) + +func TestHashG2(t *testing.T) { + actualHash := bls.HashG2([]byte("the message to be signed")) + + if !actualHash.Equals(expectedG2Hash) { + t.Fatal("expected hash to match other implementations") + } +} + +var expectedSerializedG2, _ = hex.DecodeString("a6ef29e7241e1a1cc60fee328e3290c023d55a6701db500eefab7f91391a8b8726fd0024121e64637281f907137fe268187b4baca36388e96194b73a7d532f6eea6bc098778dbfd3404584613b5ba9da97d5602e31fdbe9270b863876529b254") + +func TestHashG2WithDomain(t *testing.T) { + actualHash := bls.HashG2WithDomain([32]byte{}, 0) + + compressedPoint := bls.CompressG2(actualHash.ToAffine()) + + if !bytes.Equal(expectedSerializedG2, compressedPoint[:]) { + t.Fatal("expected hash to match test") + } +} + +func BenchmarkHashG2(t *testing.B) { + data := make([][]byte, 100) + r := NewXORShift(2) + + h := sha256.New() + for i := range data { + h.Reset() + randBytes := make([]byte, 32) + r.Read(randBytes) + h.Write(randBytes) + data[i] = h.Sum(nil) + } + + t.ResetTimer() + + for i := 0; i < t.N; i++ { + bls.HashG2(data[i%len(data)]) + } +} diff --git a/bls/pairing.go b/bls/pairing.go new file mode 100644 index 0000000..2bbae1b --- /dev/null +++ b/bls/pairing.go @@ -0,0 +1,136 @@ +package bls + +// MillerLoopItem are the inputs to the miller loop. +type MillerLoopItem struct { + P *G1Affine + Q *G2Prepared +} + +type pairingItem struct { + p *G1Affine + q [][3]FQ2 + qIndex int +} + +// MillerLoop runs the miller loop algorithm. +func MillerLoop(items []MillerLoopItem) *FQ12 { + pairs := make([]pairingItem, len(items)) + for i, item := range items { + if !item.P.IsZero() && !item.Q.IsZero() { + pairs[i] = pairingItem{ + p: item.P.Copy(), + q: item.Q.coeffs, + qIndex: 0, + } + } + } + + ell := func(f *FQ12, coeffs [3]FQ2, p *G1Affine) { + + c0 := coeffs[0] + c1 := coeffs[1] + + c0.c0.MulAssign(p.y) + c0.c1.MulAssign(p.y) + c1.c0.MulAssign(p.x) + c1.c1.MulAssign(p.x) + + f.MulBy014Assign(coeffs[2], c1, c0) + } + + f := FQ12One.Copy() + + foundOne := false + blsXRsh1 := blsX.Copy() + blsXRsh1.Rsh(1) + for q := uint(0); q <= blsXRsh1.BitLen(); q++ { + set := blsXRsh1.Bit(blsXRsh1.BitLen() - q) + if !foundOne { + foundOne = set + continue + } + + for i, pair := range pairs { + ell(f, pair.q[pair.qIndex], pair.p.Copy()) + pairs[i].qIndex++ + } + if set { + for i, pair := range pairs { + ell(f, pair.q[pair.qIndex], pair.p.Copy()) + pairs[i].qIndex++ + } + } + + f.SquareAssign() + } + for i, pair := range pairs { + ell(f, pair.q[pair.qIndex], pair.p.Copy()) + pairs[i].qIndex++ + } + + if blsIsNegative { + f.ConjugateAssign() + } + return f +} + +// FinalExponentiation performs the final exponentiation on the +// FQ12 element. +func FinalExponentiation(r *FQ12) *FQ12 { + f1 := r.Copy() + f1.ConjugateAssign() + f2 := r.Copy() + if !f2.InverseAssign() { + return nil + } + r = f1.Copy() + r.MulAssign(f2) + f2 = r.Copy() + r.FrobeniusMapAssign(2) + r.MulAssign(f2) + + ExpByX := func(f *FQ12, x FQRepr) *FQ12 { + newf := f.Exp(x) + if blsIsNegative { + newf.ConjugateAssign() + } + return newf + } + + x := blsX.Copy() + + y0 := r.Copy() + y0.SquareAssign() + y1 := ExpByX(y0, x) + x.Rsh(1) + y2 := ExpByX(y1, x) + x.Lsh(1) + y3 := r.Copy() + y3.ConjugateAssign() + y1 = y1.Copy() + y1.MulAssign(y3) + y1.ConjugateAssign() + y1.MulAssign(y2) + y2 = ExpByX(y1, x) + y3 = ExpByX(y2, x) + y1.ConjugateAssign() + y3.MulAssign(y1) + y1.ConjugateAssign() + y1.FrobeniusMapAssign(3) + y2.FrobeniusMapAssign(2) + y1.MulAssign(y2) + y2 = ExpByX(y3, x) + y2.MulAssign(y0) + y2.MulAssign(r) + y1.MulAssign(y2) + y3.FrobeniusMapAssign(1) + y1.MulAssign(y3) + return y1 +} + +// Pairing performs a pairing given the G1 and G2 elements. +func Pairing(p *G1Projective, q *G2Projective) *FQ12 { + return FinalExponentiation(MillerLoop([]MillerLoopItem{ + {p.ToAffine(), G2AffineToPrepared(q.ToAffine())}, + })) +} diff --git a/bls/pairing_test.go b/bls/pairing_test.go new file mode 100644 index 0000000..1966f98 --- /dev/null +++ b/bls/pairing_test.go @@ -0,0 +1,152 @@ +package bls_test + +import ( + "testing" + + "github.com/phoreproject/bls" +) + +var c000, _ = bls.FQReprFromString("2819105605953691245277803056322684086884703000473961065716485506033588504203831029066448642358042597501014294104502", 10) +var c001, _ = bls.FQReprFromString("1323968232986996742571315206151405965104242542339680722164220900812303524334628370163366153839984196298685227734799", 10) +var c010, _ = bls.FQReprFromString("2987335049721312504428602988447616328830341722376962214011674875969052835043875658579425548512925634040144704192135", 10) +var c011, _ = bls.FQReprFromString("3879723582452552452538684314479081967502111497413076598816163759028842927668327542875108457755966417881797966271311", 10) +var c020, _ = bls.FQReprFromString("261508182517997003171385743374653339186059518494239543139839025878870012614975302676296704930880982238308326681253", 10) +var c021, _ = bls.FQReprFromString("231488992246460459663813598342448669854473942105054381511346786719005883340876032043606739070883099647773793170614", 10) +var c100, _ = bls.FQReprFromString("3993582095516422658773669068931361134188738159766715576187490305611759126554796569868053818105850661142222948198557", 10) +var c101, _ = bls.FQReprFromString("1074773511698422344502264006159859710502164045911412750831641680783012525555872467108249271286757399121183508900634", 10) +var c110, _ = bls.FQReprFromString("2727588299083545686739024317998512740561167011046940249988557419323068809019137624943703910267790601287073339193943", 10) +var c111, _ = bls.FQReprFromString("493643299814437640914745677854369670041080344349607504656543355799077485536288866009245028091988146107059514546594", 10) +var c120, _ = bls.FQReprFromString("734401332196641441839439105942623141234148957972407782257355060229193854324927417865401895596108124443575283868655", 10) +var c121, _ = bls.FQReprFromString("2348330098288556420918672502923664952620152483128593484301759394583320358354186482723629999370241674973832318248497", 10) + +func TestResultAgainstRelic(t *testing.T) { + out := bls.Pairing(bls.G1ProjectiveOne.Copy(), bls.G2ProjectiveOne.Copy()) + expected := bls.NewFQ12( + bls.NewFQ6( + bls.NewFQ2( + bls.FQReprToFQ(c000), + bls.FQReprToFQ(c001), + ), + bls.NewFQ2( + bls.FQReprToFQ(c010), + bls.FQReprToFQ(c011), + ), + bls.NewFQ2( + bls.FQReprToFQ(c020), + bls.FQReprToFQ(c021), + ), + ), + bls.NewFQ6( + bls.NewFQ2( + bls.FQReprToFQ(c100), + bls.FQReprToFQ(c101), + ), + bls.NewFQ2( + bls.FQReprToFQ(c110), + bls.FQReprToFQ(c111), + ), + bls.NewFQ2( + bls.FQReprToFQ(c120), + bls.FQReprToFQ(c121), + ), + ), + ) + + if !out.Equals(expected) { + t.Fatal("pairing result is wrong") + } +} + +func BenchmarkG2Prepare(b *testing.B) { + type addData struct { + g2 *bls.G2Affine + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f1, _ := bls.RandG2(r) + inData[i] = addData{ + g2: f1.ToAffine(), + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + bls.G2AffineToPrepared(inData[count].g2) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkMillerLoop(b *testing.B) { + type addData struct { + p *bls.G1Affine + q *bls.G2Prepared + } + + r := NewXORShift(1) + inData := [g1MulAssignSamples]addData{} + for i := 0; i < g1MulAssignSamples; i++ { + f2, _ := bls.RandG2(r) + f1, _ := bls.RandG1(r) + inData[i] = addData{ + q: bls.G2AffineToPrepared(f2.ToAffine()), + p: f1.ToAffine(), + } + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + bls.MillerLoop([]bls.MillerLoopItem{{P: inData[count].p, Q: inData[count].q}}) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkFinalExponentiation(b *testing.B) { + r := NewXORShift(1) + inData := [g1MulAssignSamples]*bls.FQ12{} + for i := 0; i < g1MulAssignSamples; i++ { + f2, _ := bls.RandG2(r) + f1, _ := bls.RandG1(r) + inData[i] = bls.MillerLoop([]bls.MillerLoopItem{ + { + Q: bls.G2AffineToPrepared(f2.ToAffine()), + P: f1.ToAffine(), + }, + }) + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + bls.FinalExponentiation(inData[count]) + count = (count + 1) % g1MulAssignSamples + } +} + +func BenchmarkPairing(b *testing.B) { + type pairingData struct { + g1 *bls.G1Projective + g2 *bls.G2Projective + } + r := NewXORShift(1) + inData := [g1MulAssignSamples]pairingData{} + for i := 0; i < g1MulAssignSamples; i++ { + f2, _ := bls.RandG2(r) + f1, _ := bls.RandG1(r) + inData[i] = pairingData{g1: f1, g2: f2} + } + + b.ResetTimer() + + count := 0 + for i := 0; i < b.N; i++ { + bls.Pairing(inData[count].g1, inData[count].g2) + count = (count + 1) % g1MulAssignSamples + } +} diff --git a/bls/primitivefuncs_amd64.s b/bls/primitivefuncs_amd64.s new file mode 100644 index 0000000..44d3db2 --- /dev/null +++ b/bls/primitivefuncs_amd64.s @@ -0,0 +1,1522 @@ +// Code generated by command: go run asm.go -out primitivefuncs_amd64.s. DO NOT EDIT. + +// func MACWithCarry(a uint64, b uint64, c uint64, carry uint64) (uint64, uint64) +TEXT ·MACWithCarry(SB), $0-48 + MOVQ b+8(FP), CX + MOVQ c+16(FP), AX + + // Multiply b and c + MULQ CX + MOVQ a+0(FP), CX + + // Add a + ADDQ CX, AX + + // Add to result carry if needed + ADCQ $0x00, DX + MOVQ carry+24(FP), CX + + // Add input carry to running result + ADDQ CX, AX + + // Add to result carry if needed + ADCQ $0x00, DX + MOVQ AX, ret+32(FP) + MOVQ DX, ret1+40(FP) + RET + +// func SubWithBorrow(a uint64, b uint64, borrow uint64) (uint64, uint64) +TEXT ·SubWithBorrow(SB), $0-40 + MOVQ a+0(FP), AX + MOVQ b+8(FP), CX + + // a = a - b + XORQ DX, DX + SUBQ CX, AX + + // Zero out borrow1 and set if overflowed + SETCS DL + MOVQ borrow+16(FP), CX + + // a = a - borrow + XORQ BX, BX + SUBQ CX, AX + + // Zero out borrow2 and set if overflowed + SETCS BL + + // borrow2 = borrow2 | borrow1 + ORQ DX, BX + MOVQ AX, ret+24(FP) + MOVQ BX, ret1+32(FP) + RET + +// func AddWithCarry(a uint64, b uint64, carry uint64) (uint64, uint64) +TEXT ·AddWithCarry(SB), $0-40 + MOVQ a+0(FP), AX + MOVQ b+8(FP), CX + MOVQ carry+16(FP), DX + + // Zero out new carry + XORQ BX, BX + + // Add a + b + ADDQ CX, AX + + // Add to new carry if needed + ADCQ $0x00, BX + + // Add old carry + ADDQ DX, AX + + // Add to new carry if needed + ADCQ $0x00, BX + MOVQ AX, ret+24(FP) + MOVQ BX, ret1+32(FP) + RET + +// func MultiplyFQRepr(a [6]uint64, b [6]uint64) (hi [6]uint64, lo [6]uint64) +TEXT ·MultiplyFQRepr(SB), $0-192 + // carry = 0 + XORQ R10, R10 + MOVQ a_0+0(FP), AX + MOVQ b_0+48(FP), BX + + // registers[0] = 0 + XORQ CX, CX + + // carry = ((registers[0] + a[0] * b[0] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[0] = (registers[0] + a[0] * b[0] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ CX, AX + ADCQ $0x00, DX + ADDQ R10, AX + ADCQ $0x00, DX + MOVQ DX, R10 + MOVQ AX, CX + MOVQ a_0+0(FP), AX + MOVQ b_1+56(FP), BX + + // registers[1] = 0 + XORQ BP, BP + + // carry = ((registers[1] + a[0] * b[1] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[1] = (registers[1] + a[0] * b[1] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ BP, AX + ADCQ $0x00, DX + ADDQ R10, AX + ADCQ $0x00, DX + MOVQ DX, R10 + MOVQ AX, BP + MOVQ a_0+0(FP), AX + MOVQ b_2+64(FP), BX + + // registers[2] = 0 + XORQ SI, SI + + // carry = ((registers[2] + a[0] * b[2] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[2] = (registers[2] + a[0] * b[2] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ SI, AX + ADCQ $0x00, DX + ADDQ R10, AX + ADCQ $0x00, DX + MOVQ DX, R10 + MOVQ AX, SI + MOVQ a_0+0(FP), AX + MOVQ b_3+72(FP), BX + + // registers[3] = 0 + XORQ DI, DI + + // carry = ((registers[3] + a[0] * b[3] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[3] = (registers[3] + a[0] * b[3] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ R10, AX + ADCQ $0x00, DX + MOVQ DX, R10 + MOVQ AX, DI + MOVQ a_0+0(FP), AX + MOVQ b_4+80(FP), BX + + // registers[4] = 0 + XORQ R8, R8 + + // carry = ((registers[4] + a[0] * b[4] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[4] = (registers[4] + a[0] * b[4] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R8, AX + ADCQ $0x00, DX + ADDQ R10, AX + ADCQ $0x00, DX + MOVQ DX, R10 + MOVQ AX, R8 + MOVQ a_0+0(FP), AX + MOVQ b_5+88(FP), BX + + // registers[5] = 0 + XORQ R9, R9 + + // carry = ((registers[5] + a[0] * b[5] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[5] = (registers[5] + a[0] * b[5] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R9, AX + ADCQ $0x00, DX + ADDQ R10, AX + ADCQ $0x00, DX + MOVQ DX, R10 + MOVQ AX, R9 + + // registers[6] = carry + MOVQ R10, R10 + + // carry = 0 + XORQ R11, R11 + MOVQ a_1+8(FP), AX + MOVQ b_0+48(FP), BX + + // carry = ((registers[1] + a[1] * b[0] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[1] = (registers[1] + a[1] * b[0] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ BP, AX + ADCQ $0x00, DX + ADDQ R11, AX + ADCQ $0x00, DX + MOVQ DX, R11 + MOVQ AX, BP + MOVQ a_1+8(FP), AX + MOVQ b_1+56(FP), BX + + // carry = ((registers[2] + a[1] * b[1] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[2] = (registers[2] + a[1] * b[1] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ SI, AX + ADCQ $0x00, DX + ADDQ R11, AX + ADCQ $0x00, DX + MOVQ DX, R11 + MOVQ AX, SI + MOVQ a_1+8(FP), AX + MOVQ b_2+64(FP), BX + + // carry = ((registers[3] + a[1] * b[2] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[3] = (registers[3] + a[1] * b[2] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ R11, AX + ADCQ $0x00, DX + MOVQ DX, R11 + MOVQ AX, DI + MOVQ a_1+8(FP), AX + MOVQ b_3+72(FP), BX + + // carry = ((registers[4] + a[1] * b[3] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[4] = (registers[4] + a[1] * b[3] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R8, AX + ADCQ $0x00, DX + ADDQ R11, AX + ADCQ $0x00, DX + MOVQ DX, R11 + MOVQ AX, R8 + MOVQ a_1+8(FP), AX + MOVQ b_4+80(FP), BX + + // carry = ((registers[5] + a[1] * b[4] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[5] = (registers[5] + a[1] * b[4] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R9, AX + ADCQ $0x00, DX + ADDQ R11, AX + ADCQ $0x00, DX + MOVQ DX, R11 + MOVQ AX, R9 + MOVQ a_1+8(FP), AX + MOVQ b_5+88(FP), BX + + // carry = ((registers[6] + a[1] * b[5] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[6] = (registers[6] + a[1] * b[5] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R10, AX + ADCQ $0x00, DX + ADDQ R11, AX + ADCQ $0x00, DX + MOVQ DX, R11 + MOVQ AX, R10 + + // registers[7] = carry + MOVQ R11, R11 + + // carry = 0 + XORQ R12, R12 + MOVQ a_2+16(FP), AX + MOVQ b_0+48(FP), BX + + // carry = ((registers[2] + a[2] * b[0] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[2] = (registers[2] + a[2] * b[0] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ SI, AX + ADCQ $0x00, DX + ADDQ R12, AX + ADCQ $0x00, DX + MOVQ DX, R12 + MOVQ AX, SI + MOVQ a_2+16(FP), AX + MOVQ b_1+56(FP), BX + + // carry = ((registers[3] + a[2] * b[1] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[3] = (registers[3] + a[2] * b[1] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ R12, AX + ADCQ $0x00, DX + MOVQ DX, R12 + MOVQ AX, DI + MOVQ a_2+16(FP), AX + MOVQ b_2+64(FP), BX + + // carry = ((registers[4] + a[2] * b[2] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[4] = (registers[4] + a[2] * b[2] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R8, AX + ADCQ $0x00, DX + ADDQ R12, AX + ADCQ $0x00, DX + MOVQ DX, R12 + MOVQ AX, R8 + MOVQ a_2+16(FP), AX + MOVQ b_3+72(FP), BX + + // carry = ((registers[5] + a[2] * b[3] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[5] = (registers[5] + a[2] * b[3] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R9, AX + ADCQ $0x00, DX + ADDQ R12, AX + ADCQ $0x00, DX + MOVQ DX, R12 + MOVQ AX, R9 + MOVQ a_2+16(FP), AX + MOVQ b_4+80(FP), BX + + // carry = ((registers[6] + a[2] * b[4] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[6] = (registers[6] + a[2] * b[4] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R10, AX + ADCQ $0x00, DX + ADDQ R12, AX + ADCQ $0x00, DX + MOVQ DX, R12 + MOVQ AX, R10 + MOVQ a_2+16(FP), AX + MOVQ b_5+88(FP), BX + + // carry = ((registers[7] + a[2] * b[5] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[7] = (registers[7] + a[2] * b[5] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R11, AX + ADCQ $0x00, DX + ADDQ R12, AX + ADCQ $0x00, DX + MOVQ DX, R12 + MOVQ AX, R11 + + // registers[8] = carry + MOVQ R12, R12 + + // carry = 0 + XORQ R13, R13 + MOVQ a_3+24(FP), AX + MOVQ b_0+48(FP), BX + + // carry = ((registers[3] + a[3] * b[0] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[3] = (registers[3] + a[3] * b[0] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ R13, AX + ADCQ $0x00, DX + MOVQ DX, R13 + MOVQ AX, DI + MOVQ a_3+24(FP), AX + MOVQ b_1+56(FP), BX + + // carry = ((registers[4] + a[3] * b[1] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[4] = (registers[4] + a[3] * b[1] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R8, AX + ADCQ $0x00, DX + ADDQ R13, AX + ADCQ $0x00, DX + MOVQ DX, R13 + MOVQ AX, R8 + MOVQ a_3+24(FP), AX + MOVQ b_2+64(FP), BX + + // carry = ((registers[5] + a[3] * b[2] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[5] = (registers[5] + a[3] * b[2] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R9, AX + ADCQ $0x00, DX + ADDQ R13, AX + ADCQ $0x00, DX + MOVQ DX, R13 + MOVQ AX, R9 + MOVQ a_3+24(FP), AX + MOVQ b_3+72(FP), BX + + // carry = ((registers[6] + a[3] * b[3] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[6] = (registers[6] + a[3] * b[3] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R10, AX + ADCQ $0x00, DX + ADDQ R13, AX + ADCQ $0x00, DX + MOVQ DX, R13 + MOVQ AX, R10 + MOVQ a_3+24(FP), AX + MOVQ b_4+80(FP), BX + + // carry = ((registers[7] + a[3] * b[4] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[7] = (registers[7] + a[3] * b[4] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R11, AX + ADCQ $0x00, DX + ADDQ R13, AX + ADCQ $0x00, DX + MOVQ DX, R13 + MOVQ AX, R11 + MOVQ a_3+24(FP), AX + MOVQ b_5+88(FP), BX + + // carry = ((registers[8] + a[3] * b[5] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[8] = (registers[8] + a[3] * b[5] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R12, AX + ADCQ $0x00, DX + ADDQ R13, AX + ADCQ $0x00, DX + MOVQ DX, R13 + MOVQ AX, R12 + + // registers[9] = carry + MOVQ R13, R13 + + // carry = 0 + XORQ R14, R14 + MOVQ a_4+32(FP), AX + MOVQ b_0+48(FP), BX + + // carry = ((registers[4] + a[4] * b[0] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[4] = (registers[4] + a[4] * b[0] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R8, AX + ADCQ $0x00, DX + ADDQ R14, AX + ADCQ $0x00, DX + MOVQ DX, R14 + MOVQ AX, R8 + MOVQ a_4+32(FP), AX + MOVQ b_1+56(FP), BX + + // carry = ((registers[5] + a[4] * b[1] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[5] = (registers[5] + a[4] * b[1] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R9, AX + ADCQ $0x00, DX + ADDQ R14, AX + ADCQ $0x00, DX + MOVQ DX, R14 + MOVQ AX, R9 + MOVQ a_4+32(FP), AX + MOVQ b_2+64(FP), BX + + // carry = ((registers[6] + a[4] * b[2] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[6] = (registers[6] + a[4] * b[2] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R10, AX + ADCQ $0x00, DX + ADDQ R14, AX + ADCQ $0x00, DX + MOVQ DX, R14 + MOVQ AX, R10 + MOVQ a_4+32(FP), AX + MOVQ b_3+72(FP), BX + + // carry = ((registers[7] + a[4] * b[3] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[7] = (registers[7] + a[4] * b[3] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R11, AX + ADCQ $0x00, DX + ADDQ R14, AX + ADCQ $0x00, DX + MOVQ DX, R14 + MOVQ AX, R11 + MOVQ a_4+32(FP), AX + MOVQ b_4+80(FP), BX + + // carry = ((registers[8] + a[4] * b[4] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[8] = (registers[8] + a[4] * b[4] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R12, AX + ADCQ $0x00, DX + ADDQ R14, AX + ADCQ $0x00, DX + MOVQ DX, R14 + MOVQ AX, R12 + MOVQ a_4+32(FP), AX + MOVQ b_5+88(FP), BX + + // carry = ((registers[9] + a[4] * b[5] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[9] = (registers[9] + a[4] * b[5] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R13, AX + ADCQ $0x00, DX + ADDQ R14, AX + ADCQ $0x00, DX + MOVQ DX, R14 + MOVQ AX, R13 + + // registers[10] = carry + MOVQ R14, R14 + + // carry = 0 + XORQ R15, R15 + MOVQ a_5+40(FP), AX + MOVQ b_0+48(FP), BX + + // carry = ((registers[5] + a[5] * b[0] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[5] = (registers[5] + a[5] * b[0] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R9, AX + ADCQ $0x00, DX + ADDQ R15, AX + ADCQ $0x00, DX + MOVQ DX, R15 + MOVQ AX, R9 + MOVQ a_5+40(FP), AX + MOVQ b_1+56(FP), BX + + // carry = ((registers[6] + a[5] * b[1] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[6] = (registers[6] + a[5] * b[1] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R10, AX + ADCQ $0x00, DX + ADDQ R15, AX + ADCQ $0x00, DX + MOVQ DX, R15 + MOVQ AX, R10 + MOVQ a_5+40(FP), AX + MOVQ b_2+64(FP), BX + + // carry = ((registers[7] + a[5] * b[2] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[7] = (registers[7] + a[5] * b[2] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R11, AX + ADCQ $0x00, DX + ADDQ R15, AX + ADCQ $0x00, DX + MOVQ DX, R15 + MOVQ AX, R11 + MOVQ a_5+40(FP), AX + MOVQ b_3+72(FP), BX + + // carry = ((registers[8] + a[5] * b[3] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[8] = (registers[8] + a[5] * b[3] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R12, AX + ADCQ $0x00, DX + ADDQ R15, AX + ADCQ $0x00, DX + MOVQ DX, R15 + MOVQ AX, R12 + MOVQ a_5+40(FP), AX + MOVQ b_4+80(FP), BX + + // carry = ((registers[9] + a[5] * b[4] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[9] = (registers[9] + a[5] * b[4] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R13, AX + ADCQ $0x00, DX + ADDQ R15, AX + ADCQ $0x00, DX + MOVQ DX, R15 + MOVQ AX, R13 + MOVQ a_5+40(FP), AX + MOVQ b_5+88(FP), BX + + // carry = ((registers[10] + a[5] * b[5] + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + // registers[10] = (registers[10] + a[5] * b[5] + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ AX, AX + MULQ BX + ADDQ R14, AX + ADCQ $0x00, DX + ADDQ R15, AX + ADCQ $0x00, DX + MOVQ DX, R15 + MOVQ AX, R14 + + // registers[11] = carry + MOVQ R15, AX + + // lo[0] = registers[0] + MOVQ CX, lo_0+144(FP) + + // lo[1] = registers[1] + MOVQ BP, lo_1+152(FP) + + // lo[2] = registers[2] + MOVQ SI, lo_2+160(FP) + + // lo[3] = registers[3] + MOVQ DI, lo_3+168(FP) + + // lo[4] = registers[4] + MOVQ R8, lo_4+176(FP) + + // lo[5] = registers[5] + MOVQ R9, lo_5+184(FP) + + // hi[0] = registers[6] + MOVQ R10, hi_0+96(FP) + + // hi[1] = registers[7] + MOVQ R11, hi_1+104(FP) + + // hi[2] = registers[8] + MOVQ R12, hi_2+112(FP) + + // hi[3] = registers[9] + MOVQ R13, hi_3+120(FP) + + // hi[4] = registers[10] + MOVQ R14, hi_4+128(FP) + + // hi[5] = registers[11] + MOVQ AX, hi_5+136(FP) + RET + +// func MontReduce(hi [6]uint64, lo [6]uint64) (out [6]uint64) +TEXT ·MontReduce(SB), $96-144 + // reg = [0] * 12 + // temp = r[0] + MOVQ lo_0+48(FP), AX + + // reg[0] = temp + MOVQ AX, (SP) + + // temp = r[1] + MOVQ lo_1+56(FP), AX + + // reg[1] = temp + MOVQ AX, 8(SP) + + // temp = r[2] + MOVQ lo_2+64(FP), AX + + // reg[2] = temp + MOVQ AX, 16(SP) + + // temp = r[3] + MOVQ lo_3+72(FP), AX + + // reg[3] = temp + MOVQ AX, 24(SP) + + // temp = r[4] + MOVQ lo_4+80(FP), AX + + // reg[4] = temp + MOVQ AX, 32(SP) + + // temp = r[5] + MOVQ lo_5+88(FP), AX + + // reg[5] = temp + MOVQ AX, 40(SP) + + // temp = r[6] + MOVQ hi_0+0(FP), AX + + // reg[6] = temp + MOVQ AX, 48(SP) + + // temp = r[7] + MOVQ hi_1+8(FP), AX + + // reg[7] = temp + MOVQ AX, 56(SP) + + // temp = r[8] + MOVQ hi_2+16(FP), AX + + // reg[8] = temp + MOVQ AX, 64(SP) + + // temp = r[9] + MOVQ hi_3+24(FP), AX + + // reg[9] = temp + MOVQ AX, 72(SP) + + // temp = r[10] + MOVQ hi_4+32(FP), AX + + // reg[10] = temp + MOVQ AX, 80(SP) + + // temp = r[11] + MOVQ hi_5+40(FP), AX + + // reg[11] = temp + MOVQ AX, 88(SP) + + // carryOver = 0 + XORQ CX, CX + + // rax = 9940570264628428797 + MOVQ $0x89f3fffcfffcfffd, AX + + // k = reg[0] + MOVQ (SP), BP + + // rax = (rax * k) & 0xFFFFFFFFFFFFFFFF + MULQ BP + + // k = rax + MOVQ AX, BP + + // carry = 0 + XORQ SI, SI + + // carryTemp = ((reg[0] + QFieldModulus[0] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ (SP), DI + MOVQ $0xb9feffffffffaaab, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // carry = carryTemp + // carryTemp = ((reg[1] + QFieldModulus[1] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 8(SP), DI + MOVQ $0x1eabfffeb153ffff, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[1] = (reg[1] + QFieldModulus[1] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 8(SP) + + // carry = carryTemp + // carryTemp = ((reg[2] + QFieldModulus[2] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 16(SP), DI + MOVQ $0x6730d2a0f6b0f624, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[2] = (reg[2] + QFieldModulus[2] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 16(SP) + + // carry = carryTemp + // carryTemp = ((reg[3] + QFieldModulus[3] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 24(SP), DI + MOVQ $0x64774b84f38512bf, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[3] = (reg[3] + QFieldModulus[3] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 24(SP) + + // carry = carryTemp + // carryTemp = ((reg[4] + QFieldModulus[4] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 32(SP), DI + MOVQ $0x4b1ba7b6434bacd7, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[4] = (reg[4] + QFieldModulus[4] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 32(SP) + + // carry = carryTemp + // carryTemp = ((reg[5] + QFieldModulus[5] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 40(SP), DI + MOVQ $0x1a0111ea397fe69a, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[5] = (reg[5] + QFieldModulus[5] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 40(SP) + + // carry = carryTemp + // newCarry = 0 + XORQ BX, BX + + // lastReg = reg[6] + MOVQ 48(SP), AX + + // newCarry = ((lastReg + carry + carryOver) >> 64) & 0xFFFFFFFFFFFFFFFF + // lastReg = (lastReg + carry + carryOver) & 0xFFFFFFFFFFFFFFFF + ADDQ SI, AX + ADCQ $0x00, BX + ADDQ CX, AX + ADCQ $0x00, BX + + // carryOver = newCarry + MOVQ BX, CX + + // reg[6] = lastReg + MOVQ AX, 48(SP) + + // rax = 9940570264628428797 + MOVQ $0x89f3fffcfffcfffd, AX + + // k = reg[1] + MOVQ 8(SP), BP + + // rax = (rax * k) & 0xFFFFFFFFFFFFFFFF + MULQ BP + + // k = rax + MOVQ AX, BP + + // carry = 0 + XORQ SI, SI + + // carryTemp = ((reg[1] + QFieldModulus[0] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 8(SP), DI + MOVQ $0xb9feffffffffaaab, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // carry = carryTemp + // carryTemp = ((reg[2] + QFieldModulus[1] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 16(SP), DI + MOVQ $0x1eabfffeb153ffff, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[2] = (reg[2] + QFieldModulus[1] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 16(SP) + + // carry = carryTemp + // carryTemp = ((reg[3] + QFieldModulus[2] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 24(SP), DI + MOVQ $0x6730d2a0f6b0f624, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[3] = (reg[3] + QFieldModulus[2] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 24(SP) + + // carry = carryTemp + // carryTemp = ((reg[4] + QFieldModulus[3] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 32(SP), DI + MOVQ $0x64774b84f38512bf, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[4] = (reg[4] + QFieldModulus[3] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 32(SP) + + // carry = carryTemp + // carryTemp = ((reg[5] + QFieldModulus[4] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 40(SP), DI + MOVQ $0x4b1ba7b6434bacd7, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[5] = (reg[5] + QFieldModulus[4] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 40(SP) + + // carry = carryTemp + // carryTemp = ((reg[6] + QFieldModulus[5] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 48(SP), DI + MOVQ $0x1a0111ea397fe69a, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[6] = (reg[6] + QFieldModulus[5] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 48(SP) + + // carry = carryTemp + // newCarry = 0 + XORQ BX, BX + + // lastReg = reg[7] + MOVQ 56(SP), AX + + // newCarry = ((lastReg + carry + carryOver) >> 64) & 0xFFFFFFFFFFFFFFFF + // lastReg = (lastReg + carry + carryOver) & 0xFFFFFFFFFFFFFFFF + ADDQ SI, AX + ADCQ $0x00, BX + ADDQ CX, AX + ADCQ $0x00, BX + + // carryOver = newCarry + MOVQ BX, CX + + // reg[7] = lastReg + MOVQ AX, 56(SP) + + // rax = 9940570264628428797 + MOVQ $0x89f3fffcfffcfffd, AX + + // k = reg[2] + MOVQ 16(SP), BP + + // rax = (rax * k) & 0xFFFFFFFFFFFFFFFF + MULQ BP + + // k = rax + MOVQ AX, BP + + // carry = 0 + XORQ SI, SI + + // carryTemp = ((reg[2] + QFieldModulus[0] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 16(SP), DI + MOVQ $0xb9feffffffffaaab, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // carry = carryTemp + // carryTemp = ((reg[3] + QFieldModulus[1] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 24(SP), DI + MOVQ $0x1eabfffeb153ffff, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[3] = (reg[3] + QFieldModulus[1] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 24(SP) + + // carry = carryTemp + // carryTemp = ((reg[4] + QFieldModulus[2] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 32(SP), DI + MOVQ $0x6730d2a0f6b0f624, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[4] = (reg[4] + QFieldModulus[2] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 32(SP) + + // carry = carryTemp + // carryTemp = ((reg[5] + QFieldModulus[3] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 40(SP), DI + MOVQ $0x64774b84f38512bf, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[5] = (reg[5] + QFieldModulus[3] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 40(SP) + + // carry = carryTemp + // carryTemp = ((reg[6] + QFieldModulus[4] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 48(SP), DI + MOVQ $0x4b1ba7b6434bacd7, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[6] = (reg[6] + QFieldModulus[4] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 48(SP) + + // carry = carryTemp + // carryTemp = ((reg[7] + QFieldModulus[5] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 56(SP), DI + MOVQ $0x1a0111ea397fe69a, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[7] = (reg[7] + QFieldModulus[5] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 56(SP) + + // carry = carryTemp + // newCarry = 0 + XORQ BX, BX + + // lastReg = reg[8] + MOVQ 64(SP), AX + + // newCarry = ((lastReg + carry + carryOver) >> 64) & 0xFFFFFFFFFFFFFFFF + // lastReg = (lastReg + carry + carryOver) & 0xFFFFFFFFFFFFFFFF + ADDQ SI, AX + ADCQ $0x00, BX + ADDQ CX, AX + ADCQ $0x00, BX + + // carryOver = newCarry + MOVQ BX, CX + + // reg[8] = lastReg + MOVQ AX, 64(SP) + + // rax = 9940570264628428797 + MOVQ $0x89f3fffcfffcfffd, AX + + // k = reg[3] + MOVQ 24(SP), BP + + // rax = (rax * k) & 0xFFFFFFFFFFFFFFFF + MULQ BP + + // k = rax + MOVQ AX, BP + + // carry = 0 + XORQ SI, SI + + // carryTemp = ((reg[3] + QFieldModulus[0] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 24(SP), DI + MOVQ $0xb9feffffffffaaab, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // carry = carryTemp + // carryTemp = ((reg[4] + QFieldModulus[1] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 32(SP), DI + MOVQ $0x1eabfffeb153ffff, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[4] = (reg[4] + QFieldModulus[1] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 32(SP) + + // carry = carryTemp + // carryTemp = ((reg[5] + QFieldModulus[2] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 40(SP), DI + MOVQ $0x6730d2a0f6b0f624, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[5] = (reg[5] + QFieldModulus[2] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 40(SP) + + // carry = carryTemp + // carryTemp = ((reg[6] + QFieldModulus[3] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 48(SP), DI + MOVQ $0x64774b84f38512bf, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[6] = (reg[6] + QFieldModulus[3] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 48(SP) + + // carry = carryTemp + // carryTemp = ((reg[7] + QFieldModulus[4] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 56(SP), DI + MOVQ $0x4b1ba7b6434bacd7, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[7] = (reg[7] + QFieldModulus[4] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 56(SP) + + // carry = carryTemp + // carryTemp = ((reg[8] + QFieldModulus[5] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 64(SP), DI + MOVQ $0x1a0111ea397fe69a, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[8] = (reg[8] + QFieldModulus[5] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 64(SP) + + // carry = carryTemp + // newCarry = 0 + XORQ BX, BX + + // lastReg = reg[9] + MOVQ 72(SP), AX + + // newCarry = ((lastReg + carry + carryOver) >> 64) & 0xFFFFFFFFFFFFFFFF + // lastReg = (lastReg + carry + carryOver) & 0xFFFFFFFFFFFFFFFF + ADDQ SI, AX + ADCQ $0x00, BX + ADDQ CX, AX + ADCQ $0x00, BX + + // carryOver = newCarry + MOVQ BX, CX + + // reg[9] = lastReg + MOVQ AX, 72(SP) + + // rax = 9940570264628428797 + MOVQ $0x89f3fffcfffcfffd, AX + + // k = reg[4] + MOVQ 32(SP), BP + + // rax = (rax * k) & 0xFFFFFFFFFFFFFFFF + MULQ BP + + // k = rax + MOVQ AX, BP + + // carry = 0 + XORQ SI, SI + + // carryTemp = ((reg[4] + QFieldModulus[0] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 32(SP), DI + MOVQ $0xb9feffffffffaaab, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // carry = carryTemp + // carryTemp = ((reg[5] + QFieldModulus[1] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 40(SP), DI + MOVQ $0x1eabfffeb153ffff, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[5] = (reg[5] + QFieldModulus[1] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 40(SP) + + // carry = carryTemp + // carryTemp = ((reg[6] + QFieldModulus[2] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 48(SP), DI + MOVQ $0x6730d2a0f6b0f624, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[6] = (reg[6] + QFieldModulus[2] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 48(SP) + + // carry = carryTemp + // carryTemp = ((reg[7] + QFieldModulus[3] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 56(SP), DI + MOVQ $0x64774b84f38512bf, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[7] = (reg[7] + QFieldModulus[3] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 56(SP) + + // carry = carryTemp + // carryTemp = ((reg[8] + QFieldModulus[4] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 64(SP), DI + MOVQ $0x4b1ba7b6434bacd7, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[8] = (reg[8] + QFieldModulus[4] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 64(SP) + + // carry = carryTemp + // carryTemp = ((reg[9] + QFieldModulus[5] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 72(SP), DI + MOVQ $0x1a0111ea397fe69a, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[9] = (reg[9] + QFieldModulus[5] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 72(SP) + + // carry = carryTemp + // newCarry = 0 + XORQ BX, BX + + // lastReg = reg[10] + MOVQ 80(SP), AX + + // newCarry = ((lastReg + carry + carryOver) >> 64) & 0xFFFFFFFFFFFFFFFF + // lastReg = (lastReg + carry + carryOver) & 0xFFFFFFFFFFFFFFFF + ADDQ SI, AX + ADCQ $0x00, BX + ADDQ CX, AX + ADCQ $0x00, BX + + // carryOver = newCarry + MOVQ BX, CX + + // reg[10] = lastReg + MOVQ AX, 80(SP) + + // rax = 9940570264628428797 + MOVQ $0x89f3fffcfffcfffd, AX + + // k = reg[5] + MOVQ 40(SP), BP + + // rax = (rax * k) & 0xFFFFFFFFFFFFFFFF + MULQ BP + + // k = rax + MOVQ AX, BP + + // carry = 0 + XORQ SI, SI + + // carryTemp = ((reg[5] + QFieldModulus[0] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 40(SP), DI + MOVQ $0xb9feffffffffaaab, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // carry = carryTemp + // carryTemp = ((reg[6] + QFieldModulus[1] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 48(SP), DI + MOVQ $0x1eabfffeb153ffff, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[6] = (reg[6] + QFieldModulus[1] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 48(SP) + + // carry = carryTemp + // carryTemp = ((reg[7] + QFieldModulus[2] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 56(SP), DI + MOVQ $0x6730d2a0f6b0f624, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[7] = (reg[7] + QFieldModulus[2] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 56(SP) + + // carry = carryTemp + // carryTemp = ((reg[8] + QFieldModulus[3] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 64(SP), DI + MOVQ $0x64774b84f38512bf, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[8] = (reg[8] + QFieldModulus[3] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 64(SP) + + // carry = carryTemp + // carryTemp = ((reg[9] + QFieldModulus[4] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 72(SP), DI + MOVQ $0x4b1ba7b6434bacd7, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[9] = (reg[9] + QFieldModulus[4] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 72(SP) + + // carry = carryTemp + // carryTemp = ((reg[10] + QFieldModulus[5] * k + carry) >> 64) & 0xFFFFFFFFFFFFFFFF + MOVQ 80(SP), DI + MOVQ $0x1a0111ea397fe69a, AX + MULQ BP + ADDQ DI, AX + ADCQ $0x00, DX + ADDQ SI, AX + ADCQ $0x00, DX + MOVQ DX, SI + MOVQ AX, DI + + // reg[10] = (reg[10] + QFieldModulus[5] * k + carry) & 0xFFFFFFFFFFFFFFFF + MOVQ DI, 80(SP) + + // carry = carryTemp + // newCarry = 0 + XORQ BX, BX + + // lastReg = reg[11] + MOVQ 88(SP), AX + + // newCarry = ((lastReg + carry + carryOver) >> 64) & 0xFFFFFFFFFFFFFFFF + // lastReg = (lastReg + carry + carryOver) & 0xFFFFFFFFFFFFFFFF + ADDQ SI, AX + ADCQ $0x00, BX + ADDQ CX, AX + ADCQ $0x00, BX + + // carryOver = newCarry + MOVQ BX, CX + + // reg[11] = lastReg + MOVQ AX, 88(SP) + MOVQ 48(SP), AX + + // out[0] = reg[6] + MOVQ AX, out_0+96(FP) + MOVQ 56(SP), AX + + // out[1] = reg[7] + MOVQ AX, out_1+104(FP) + MOVQ 64(SP), AX + + // out[2] = reg[8] + MOVQ AX, out_2+112(FP) + MOVQ 72(SP), AX + + // out[3] = reg[9] + MOVQ AX, out_3+120(FP) + MOVQ 80(SP), AX + + // out[4] = reg[10] + MOVQ AX, out_4+128(FP) + MOVQ 88(SP), AX + + // out[5] = reg[11] + MOVQ AX, out_5+136(FP) + RET + +// func AddNoCarry(a [6]uint64, b [6]uint64) [6]uint64 +TEXT ·AddNoCarry(SB), $0-144 + MOVQ a_0+0(FP), AX + MOVQ b_0+48(FP), CX + MOVQ a_1+8(FP), DX + MOVQ b_1+56(FP), BX + MOVQ a_2+16(FP), BP + MOVQ b_2+64(FP), SI + MOVQ a_3+24(FP), DI + MOVQ b_3+72(FP), R8 + MOVQ a_4+32(FP), R9 + MOVQ b_4+80(FP), R10 + MOVQ a_5+40(FP), R11 + MOVQ b_5+88(FP), R12 + ADDQ AX, CX + ADCQ DX, BX + ADCQ BP, SI + ADCQ DI, R8 + ADCQ R9, R10 + ADCQ R11, R12 + MOVQ CX, ret_0+96(FP) + MOVQ BX, ret_1+104(FP) + MOVQ SI, ret_2+112(FP) + MOVQ R8, ret_3+120(FP) + MOVQ R10, ret_4+128(FP) + MOVQ R12, ret_5+136(FP) + RET + +// func SubNoBorrow(a [6]uint64, b [6]uint64) [6]uint64 +TEXT ·SubNoBorrow(SB), $0-144 + MOVQ a_0+0(FP), AX + MOVQ b_0+48(FP), CX + MOVQ a_1+8(FP), DX + MOVQ b_1+56(FP), BX + MOVQ a_2+16(FP), BP + MOVQ b_2+64(FP), SI + MOVQ a_3+24(FP), DI + MOVQ b_3+72(FP), R8 + MOVQ a_4+32(FP), R9 + MOVQ b_4+80(FP), R10 + MOVQ a_5+40(FP), R11 + MOVQ b_5+88(FP), R12 + SUBQ CX, AX + SBBQ BX, DX + SBBQ SI, BP + SBBQ R8, DI + SBBQ R10, R9 + SBBQ R12, R11 + MOVQ AX, ret_0+96(FP) + MOVQ DX, ret_1+104(FP) + MOVQ BP, ret_2+112(FP) + MOVQ DI, ret_3+120(FP) + MOVQ R9, ret_4+128(FP) + MOVQ R11, ret_5+136(FP) + RET diff --git a/bls/primitivefuncs_test.go b/bls/primitivefuncs_test.go new file mode 100644 index 0000000..3f0a77c --- /dev/null +++ b/bls/primitivefuncs_test.go @@ -0,0 +1,285 @@ +package bls_test + +import ( + "crypto/rand" + "math/big" + "testing" + + "github.com/phoreproject/bls" +) + +func BenchmarkMACWithCarry(b *testing.B) { + carry := uint64(0) + for i := 0; i < b.N; i++ { + _, carry = bls.MACWithCarry(0xFFFFFFFF00000000, 0x00000000FFFFFFFF, 0x1234567812345678, carry) + } +} + +func BenchmarkSubWithCarry(b *testing.B) { + borrow := uint64(0) + for i := 0; i < b.N; i++ { + _, borrow = bls.SubWithBorrow(0xFFFFFFFF00000000, 0x00000000FFFFFFFF, borrow) + } +} + +func TestSubWithCarry(t *testing.T) { + cases := []struct { + a uint64 + b uint64 + borrow uint64 + out uint64 + outBorrow uint64 + }{ + { + a: 0, + b: 3, + borrow: 0, + out: 18446744073709551613, + outBorrow: 1, + }, + { + a: 0, + b: 2, + borrow: 0, + out: 18446744073709551614, + outBorrow: 1, + }, + { + a: 0, + b: 2, + borrow: 1, + out: 18446744073709551613, + outBorrow: 1, + }, + { + a: 2, + b: 0, + borrow: 0, + out: 2, + outBorrow: 0, + }, + { + a: 2, + b: 0, + borrow: 1, + out: 1, + outBorrow: 0, + }, + { + a: 2, + b: 1, + borrow: 0, + out: 1, + outBorrow: 0, + }, + { + a: 2, + b: 1, + borrow: 1, + out: 0, + outBorrow: 0, + }, + { + a: 0, + b: 0, + borrow: 1, + out: 18446744073709551615, + outBorrow: 1, + }, + } + + for _, c := range cases { + borrow := c.borrow + out, borrow := bls.SubWithBorrow(c.a, c.b, borrow) + if out != c.out { + t.Fatalf("%d - %d - %d is giving incorrect answer of %d instead of %d", c.a, c.b, c.borrow, out, c.out) + } + if borrow != c.outBorrow { + t.Fatalf("%d - %d - %d is giving incorrect borrow of %d instead of %d", c.a, c.b, c.borrow, borrow, c.outBorrow) + } + } +} + +func BenchmarkAddWithCarry(b *testing.B) { + borrow := uint64(0) + for i := 0; i < b.N; i++ { + _, borrow = bls.AddWithCarry(0xFFFFFFFF00000000, 0x00000000FFFFFFFF, borrow) + } +} + +func TestAddWithCarry(t *testing.T) { + cases := []struct { + a uint64 + b uint64 + carry uint64 + out uint64 + outCarry uint64 + }{ + { + 1, + 1, + 0, + 2, + 0, + }, + { + 1, + 0, + 0, + 1, + 0, + }, + { + 0, + 1, + 0, + 1, + 0, + }, + { + 1, + 1, + 1, + 3, + 0, + }, + { + 18446744073709551615, + 1, + 0, + 0, + 1, + }, + { + 18446744073709551615, + 0, + 1, + 0, + 1, + }, + { + 0, + 0, + 0, + 0, + 0, + }, + { + 0, + 4043378133346814763, + 0, + 4043378133346814763, + 0, + }, + } + + for _, c := range cases { + carry := c.carry + out, carry := bls.AddWithCarry(c.a, c.b, carry) + if out != c.out { + t.Errorf("%d + %d + %d is giving incorrect answer of %d instead of %d", c.a, c.b, c.carry, out, c.out) + } + if carry != c.outCarry { + t.Errorf("%d + %d + %d is giving incorrect carry of %d instead of %d", c.a, c.b, c.carry, carry, c.outCarry) + } + } +} + +func TestMACWithCarry(t *testing.T) { + cases := []struct { + a uint64 + b uint64 + c uint64 + carry uint64 + out uint64 + outCarry uint64 + }{ + { + 0, + 1, + 1, + 0, + 1, + 0, + }, + { + 0, + 4294967296, + 4294967296, + 0, + 0, + 1, + }, + { + 0, + 4294967296, + 4294967296, + 1, + 1, + 1, + }, + { + 5, + 4294967296, + 4294967296, + 1, + 6, + 1, + }, + } + + for _, c := range cases { + out, carry := bls.MACWithCarry(c.a, c.b, c.c, c.carry) + if out != c.out { + t.Fatalf("%d + %d * %d + %d is giving incorrect answer of %d instead of %d", c.a, c.b, c.c, c.carry, out, c.out) + } + if carry != c.outCarry { + t.Fatalf("%d + %d * %d + %d is giving incorrect carry of %d instead of %d", c.a, c.b, c.c, c.carry, carry, c.outCarry) + } + } +} + +var oneLsh384MinusOne = new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 384), big.NewInt(1)) + +func TestMultiplyFQReprOverflow(t *testing.T) { + f0 := bls.FQRepr{4276637899304358534, 4043378133346814763, 8835052805473178628, 2680116066972705497, 18387885609531466875, 90398708109242637} + f1 := bls.FQRepr{6568974633585825615, 15677163513955518067, 16490785605261833339, 9784757811163378176, 10803760609847905278, 1860524254683672351} + + expectedLo, _ := new(big.Int).SetString("11236684981931288970748045803123803013547375290961326241645975575468319186166860317135684626985730476857897168732506", 10) + expectedHi, _ := new(big.Int).SetString("19474954426416495774628754584220018623553762741680760536161274135926885656938068449644545696043757020826963123558", 10) + + hi, lo := bls.MultiplyFQRepr(f0, f1) + loBig := bls.FQRepr(lo).ToBig() + hiBig := bls.FQRepr(hi).ToBig() + + if loBig.Cmp(expectedLo) != 0 { + t.Fatalf("expected lo bits to equal %x, got: %x", expectedLo, loBig) + } + + if hiBig.Cmp(expectedHi) != 0 { + t.Fatalf("expected hi bits to equal %x, got: %x", expectedHi, hiBig) + } +} + +func TestRandomMultiplyFQRepr(t *testing.T) { + r := NewXORShift(1) + total := big.NewInt(1) + totalFQ := bls.NewFQRepr(1) + + for i := 0; i < 1000000; i++ { + n, _ := rand.Int(r, bls.QFieldModulus.ToBig()) + + f, err := bls.FQReprFromBigInt(n) + if err != nil { + t.Fatal(err) + } + + _, totalFQ = bls.MultiplyFQRepr(totalFQ, f) + total.Mul(total, n) + total.And(total, oneLsh384MinusOne) + + if bls.FQRepr(totalFQ).ToBig().Cmp(total) != 0 { + t.Fatal("multiplication totals do not match between big int and FQ") + } + } +} diff --git a/bls/stub.go b/bls/stub.go new file mode 100644 index 0000000..c1285a7 --- /dev/null +++ b/bls/stub.go @@ -0,0 +1,33 @@ +// +build amd64 +// +build gc + +package bls + +// This file is for assembly functions that are not able +// to be generated by Avo. + +// MultiplyFQRepr multiplies two FQRepr values together. +func MultiplyFQRepr(a, b [6]uint64) (hi [6]uint64, lo [6]uint64) + +// MontReduce reduces the 768-bit value using montgomery reduction. +func MontReduce(hi, lo [6]uint64) (out [6]uint64) + +// AddNoCarry finds the value of 384-bit a + b and returns the +// resulting 384-bit value. +func AddNoCarry(a, b [6]uint64) [6]uint64 + +// SubNoBorrow finds the value of 384-bit a - b and returns the +// resulting 384-bit value. +func SubNoBorrow(a, b [6]uint64) [6]uint64 + +// AddWithCarry finds the value a + b + carry and returns the +// full 128-bit value in 2 64-bit integers. +func AddWithCarry(a, b, carry uint64) (uint64, uint64) + +// SubWithBorrow finds the value a - b - borrow and returns the +// result and the borrow. +func SubWithBorrow(a, b, borrow uint64) (uint64, uint64) + +// MACWithCarry finds the value a + b * c + carry and returns the +// full 128-bit value in 2 64-bit integers. +func MACWithCarry(a, b, c, carry uint64) (uint64, uint64) diff --git a/bls/stub_fallback.go b/bls/stub_fallback.go new file mode 100644 index 0000000..d920fb9 --- /dev/null +++ b/bls/stub_fallback.go @@ -0,0 +1,155 @@ +// +build !amd64 !gc + +package bls + +import ( + "math/big" + "math/bits" +) + +// MultiplyFQRepr multiplies two FQRepr values together. +func MultiplyFQRepr(a, b [6]uint64) (hi [6]uint64, lo [6]uint64) { + carry := uint64(0) + lo[0], carry = MACWithCarry(0, a[0], b[0], 0) + lo[1], carry = MACWithCarry(0, a[0], b[1], carry) + lo[2], carry = MACWithCarry(0, a[0], b[2], carry) + lo[3], carry = MACWithCarry(0, a[0], b[3], carry) + lo[4], carry = MACWithCarry(0, a[0], b[4], carry) + lo[5], carry = MACWithCarry(0, a[0], b[5], carry) + hi[0] = carry + lo[1], carry = MACWithCarry(lo[1], a[1], b[0], 0) + lo[2], carry = MACWithCarry(lo[2], a[1], b[1], carry) + lo[3], carry = MACWithCarry(lo[3], a[1], b[2], carry) + lo[4], carry = MACWithCarry(lo[4], a[1], b[3], carry) + lo[5], carry = MACWithCarry(lo[5], a[1], b[4], carry) + hi[0], carry = MACWithCarry(hi[0], a[1], b[5], carry) + hi[1] = carry + lo[2], carry = MACWithCarry(lo[2], a[2], b[0], 0) + lo[3], carry = MACWithCarry(lo[3], a[2], b[1], carry) + lo[4], carry = MACWithCarry(lo[4], a[2], b[2], carry) + lo[5], carry = MACWithCarry(lo[5], a[2], b[3], carry) + hi[0], carry = MACWithCarry(hi[0], a[2], b[4], carry) + hi[1], carry = MACWithCarry(hi[1], a[2], b[5], carry) + hi[2] = carry + lo[3], carry = MACWithCarry(lo[3], a[3], b[0], 0) + lo[4], carry = MACWithCarry(lo[4], a[3], b[1], carry) + lo[5], carry = MACWithCarry(lo[5], a[3], b[2], carry) + hi[0], carry = MACWithCarry(hi[0], a[3], b[3], carry) + hi[1], carry = MACWithCarry(hi[1], a[3], b[4], carry) + hi[2], carry = MACWithCarry(hi[2], a[3], b[5], carry) + hi[3] = carry + lo[4], carry = MACWithCarry(lo[4], a[4], b[0], 0) + lo[5], carry = MACWithCarry(lo[5], a[4], b[1], carry) + hi[0], carry = MACWithCarry(hi[0], a[4], b[2], carry) + hi[1], carry = MACWithCarry(hi[1], a[4], b[3], carry) + hi[2], carry = MACWithCarry(hi[2], a[4], b[4], carry) + hi[3], carry = MACWithCarry(hi[3], a[4], b[5], carry) + hi[4] = carry + lo[5], carry = MACWithCarry(lo[5], a[5], b[0], 0) + hi[0], carry = MACWithCarry(hi[0], a[5], b[1], carry) + hi[1], carry = MACWithCarry(hi[1], a[5], b[2], carry) + hi[2], carry = MACWithCarry(hi[2], a[5], b[3], carry) + hi[3], carry = MACWithCarry(hi[3], a[5], b[4], carry) + hi[4], carry = MACWithCarry(hi[4], a[5], b[5], carry) + hi[5] = carry + + return hi, lo +} + +const montInvFQ = uint64(0x89f3fffcfffcfffd) + +func MontReduce(hi, lo [6]uint64) [6]uint64 { + k := lo[0] * montInvFQ + _, carry := MACWithCarry(lo[0], k, QFieldModulus[0], 0) + lo[1], carry = MACWithCarry(lo[1], k, QFieldModulus[1], carry) + lo[2], carry = MACWithCarry(lo[2], k, QFieldModulus[2], carry) + lo[3], carry = MACWithCarry(lo[3], k, QFieldModulus[3], carry) + lo[4], carry = MACWithCarry(lo[4], k, QFieldModulus[4], carry) + lo[5], carry = MACWithCarry(lo[5], k, QFieldModulus[5], carry) + hi[0], carry = AddWithCarry(hi[0], 0, carry) + carry2 := carry + k = lo[1] * montInvFQ + _, carry = MACWithCarry(lo[1], k, QFieldModulus[0], 0) + lo[2], carry = MACWithCarry(lo[2], k, QFieldModulus[1], carry) + lo[3], carry = MACWithCarry(lo[3], k, QFieldModulus[2], carry) + lo[4], carry = MACWithCarry(lo[4], k, QFieldModulus[3], carry) + lo[5], carry = MACWithCarry(lo[5], k, QFieldModulus[4], carry) + hi[0], carry = MACWithCarry(hi[0], k, QFieldModulus[5], carry) + hi[1], carry = AddWithCarry(hi[1], carry2, carry) + carry2 = carry + k = lo[2] * montInvFQ + _, carry = MACWithCarry(lo[2], k, QFieldModulus[0], 0) + lo[3], carry = MACWithCarry(lo[3], k, QFieldModulus[1], carry) + lo[4], carry = MACWithCarry(lo[4], k, QFieldModulus[2], carry) + lo[5], carry = MACWithCarry(lo[5], k, QFieldModulus[3], carry) + hi[0], carry = MACWithCarry(hi[0], k, QFieldModulus[4], carry) + hi[1], carry = MACWithCarry(hi[1], k, QFieldModulus[5], carry) + hi[2], carry = AddWithCarry(hi[2], carry2, carry) + carry2 = carry + k = lo[3] * montInvFQ + _, carry = MACWithCarry(lo[3], k, QFieldModulus[0], 0) + lo[4], carry = MACWithCarry(lo[4], k, QFieldModulus[1], carry) + lo[5], carry = MACWithCarry(lo[5], k, QFieldModulus[2], carry) + hi[0], carry = MACWithCarry(hi[0], k, QFieldModulus[3], carry) + hi[1], carry = MACWithCarry(hi[1], k, QFieldModulus[4], carry) + hi[2], carry = MACWithCarry(hi[2], k, QFieldModulus[5], carry) + hi[3], carry = AddWithCarry(hi[3], carry2, carry) + carry2 = carry + k = lo[4] * montInvFQ + _, carry = MACWithCarry(lo[4], k, QFieldModulus[0], 0) + lo[5], carry = MACWithCarry(lo[5], k, QFieldModulus[1], carry) + hi[0], carry = MACWithCarry(hi[0], k, QFieldModulus[2], carry) + hi[1], carry = MACWithCarry(hi[1], k, QFieldModulus[3], carry) + hi[2], carry = MACWithCarry(hi[2], k, QFieldModulus[4], carry) + hi[3], carry = MACWithCarry(hi[3], k, QFieldModulus[5], carry) + hi[4], carry = AddWithCarry(hi[4], carry2, carry) + carry2 = carry + k = lo[5] * montInvFQ + _, carry = MACWithCarry(lo[5], k, QFieldModulus[0], 0) + hi[0], carry = MACWithCarry(hi[0], k, QFieldModulus[1], carry) + hi[1], carry = MACWithCarry(hi[1], k, QFieldModulus[2], carry) + hi[2], carry = MACWithCarry(hi[2], k, QFieldModulus[3], carry) + hi[3], carry = MACWithCarry(hi[3], k, QFieldModulus[4], carry) + hi[4], carry = MACWithCarry(hi[4], k, QFieldModulus[5], carry) + hi[5], carry = AddWithCarry(hi[5], carry2, carry) + return hi +} + +// +func AddNoCarry(a, b [6]uint64) (out [6]uint64) { + carry := uint64(0) + for i := 0; i < 6; i++ { + out[i], carry = AddWithCarry(a[i], b[i], carry) + } + + return out +} + +func SubNoBorrow(a, b [6]uint64) (out [6]uint64) { + borrow := uint64(0) + for i := 0; i < 6; i++ { + out[i], borrow = SubWithBorrow(a[i], b[i], borrow) + } + return out +} + +func AddWithCarry(a, b, carry uint64) (uint64, uint64) { + out, outCarry := bits.Add(uint(a), uint(b), 0) + out, outCarry2 := bits.Add(uint(out), uint(carry), 0) + return uint64(out), uint64(outCarry + outCarry2) +} + +var oneLsh64 = new(big.Int).Add(new(big.Int).SetUint64(0xffffffffffffffff), big.NewInt(1)) + +func SubWithBorrow(a, b, borrow uint64) (uint64, uint64) { + o, c := bits.Sub(uint(a), uint(b), uint(borrow)) + return uint64(o), uint64(c) +} + +func MACWithCarry(a, b, c, carry uint64) (out uint64, newCarry uint64) { + carryOut, bc := bits.Mul(uint(b), uint(c)) + abc, carryOut2 := bits.Add(uint(bc), uint(a), 0) + abcc, carryOut3 := bits.Add(uint(abc), uint(carry), 0) + + return uint64(abcc), uint64(carryOut + carryOut2 + carryOut3) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b28dba0 --- /dev/null +++ b/go.mod @@ -0,0 +1,7 @@ +module github.com/boohyunsik/tpke + +require ( + github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 + github.com/phoreproject/bls v0.0.0-20190621015719-e008a268030e + golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..69d8459 --- /dev/null +++ b/go.sum @@ -0,0 +1,29 @@ +github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8= +github.com/dlespiau/covertool v0.0.0-20180314162135-b0c4c6d0583a/go.mod h1:/eQMcW3eA1bzKx23ZYI2H3tXPdJB5JWYTHzoUPBvQY4= +github.com/google/pprof v0.0.0-20190309163659-77426154d546/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353 h1:X/79QL0b4YJVO5+OsPH9rF2u428CIrGL/jLmPsoOQQ4= +github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy+E1PZ3C9ctsPRlvOPAFPkCNlcPBDkt0N3U= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/mmcloughlin/avo v0.0.0-20190318053554-7a0eb66183da/go.mod h1:lf5GMZxA5kz8dnCweJuER5Rmbx6dDu6qvw0fO3uYKK8= +github.com/phoreproject/bls v0.0.0-20190621015719-e008a268030e h1:tKwNa11mmojxtuUdtSb5Iy7kKmcEDAgnsRf3RPKuLh8= +github.com/phoreproject/bls v0.0.0-20190621015719-e008a268030e/go.mod h1:7pK0Ldy91shCmI47LLTn3i3rfTQcHiJJvPqGqzvN5nE= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8= +golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190326090315-15845e8f865b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190106171756-3ef68632349c/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190325223049-1d95b17f1b04/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/poly.go b/poly.go index bdfcf91..f0a2006 100644 --- a/poly.go +++ b/poly.go @@ -2,7 +2,7 @@ package tpke import ( "github.com/leesper/go_rng" - "github.com/bls" + "github.com/boohyunsik/tpke/bls" "math/rand" "time" ) diff --git a/poly_test.go b/poly_test.go index 00159ba..1ebd618 100644 --- a/poly_test.go +++ b/poly_test.go @@ -1,7 +1,7 @@ package tpke import ( - "github.com/bls" + "github.com/boohyunsik/tpke/bls" "testing" ) diff --git a/public_key.go b/public_key.go index 3e0e8ad..0504fb4 100644 --- a/public_key.go +++ b/public_key.go @@ -4,7 +4,7 @@ import ( "crypto/rand" "errors" "fmt" - "github.com/bls" + "github.com/boohyunsik/tpke/bls" ) type PublicKey struct { diff --git a/public_key_test.go b/public_key_test.go index dee9f99..eaaa183 100644 --- a/public_key_test.go +++ b/public_key_test.go @@ -2,7 +2,7 @@ package tpke import ( "crypto/rand" - "github.com/bls" + "github.com/boohyunsik/tpke/bls" "testing" ) diff --git a/secret_key.go b/secret_key.go index d966408..fec193b 100644 --- a/secret_key.go +++ b/secret_key.go @@ -2,7 +2,7 @@ package tpke import ( "errors" - "github.com/bls" + "github.com/boohyunsik/tpke/bls" ) type SecretKey struct { diff --git a/secret_key_test.go b/secret_key_test.go index 39dc7b3..785b56b 100644 --- a/secret_key_test.go +++ b/secret_key_test.go @@ -1,7 +1,7 @@ package tpke import ( - "github.com/bls" + "github.com/boohyunsik/tpke/bls" "testing" ) diff --git a/signature.go b/signature.go index 40c1c26..8509193 100644 --- a/signature.go +++ b/signature.go @@ -1,6 +1,6 @@ package tpke -import "github.com/bls" +import "github.com/boohyunsik/tpke/bls" type Signature struct { G2 *bls.G2Projective diff --git a/tpke.go b/tpke.go index 5dd084f..47b932a 100644 --- a/tpke.go +++ b/tpke.go @@ -2,7 +2,7 @@ package tpke import ( "fmt" - "github.com/bls" + "github.com/boohyunsik/tpke/bls" ) type CipherText struct { diff --git a/tpke_test.go b/tpke_test.go index bbbffc4..651d650 100644 --- a/tpke_test.go +++ b/tpke_test.go @@ -4,7 +4,7 @@ import ( "crypto/rand" "crypto/sha256" "encoding/binary" - "github.com/bls" + "github.com/boohyunsik/tpke/bls" "github.com/leesper/go_rng" "math/big" "testing" diff --git a/utils.go b/utils.go index 3e69220..9cf1037 100644 --- a/utils.go +++ b/utils.go @@ -3,7 +3,7 @@ package tpke import ( "encoding/binary" "errors" - "github.com/bls" + "github.com/boohyunsik/tpke/bls" "golang.org/x/crypto/blake2b" "golang.org/x/crypto/sha3" )