No commit suggestions generated

This commit is contained in:
Prad Nukala
2025-10-09 15:10:39 -04:00
commit a934caa7d3
323 changed files with 98121 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
package bls12381
import (
"math/bits"
"github.com/sonr-io/sonr/crypto/core/curves/native"
)
var fqModulusBytes = [native.FieldBytes]byte{
0x01,
0x00,
0x00,
0x00,
0xff,
0xff,
0xff,
0xff,
0xfe,
0x5b,
0xfe,
0xff,
0x02,
0xa4,
0xbd,
0x53,
0x05,
0xd8,
0xa1,
0x09,
0x08,
0xd8,
0x39,
0x33,
0x48,
0x7d,
0x9d,
0x29,
0x53,
0xa7,
0xed,
0x73,
}
const (
// The BLS parameter x for BLS12-381 is -0xd201000000010000
paramX = uint64(0xd201000000010000)
Limbs = 6
FieldBytes = 48
WideFieldBytes = 96
DoubleWideFieldBytes = 192
)
// mac Multiply and Accumulate - compute a + (b * c) + d, return the result and new carry
func mac(a, b, c, d uint64) (uint64, uint64) {
hi, lo := bits.Mul64(b, c)
carry2, carry := bits.Add64(a, d, 0)
hi, _ = bits.Add64(hi, 0, carry)
lo, carry = bits.Add64(lo, carry2, 0)
hi, _ = bits.Add64(hi, 0, carry)
return lo, hi
}
// adc Add w/Carry
func adc(x, y, carry uint64) (uint64, uint64) {
sum := x + y + carry
// The sum will overflow if both top bits are set (x & y) or if one of them
// is (x | y), and a carry from the lower place happened. If such a carry
// happens, the top bit will be 1 + 0 + 1 = 0 (&^ sum).
carryOut := ((x & y) | ((x | y) &^ sum)) >> 63
carryOut |= ((x & carry) | ((x | carry) &^ sum)) >> 63
carryOut |= ((y & carry) | ((y | carry) &^ sum)) >> 63
return sum, carryOut
}
// sbb Subtract with borrow
func sbb(x, y, borrow uint64) (uint64, uint64) {
diff := x - (y + borrow)
borrowOut := ((^x & y) | (^(x ^ y) & diff)) >> 63
return diff, borrowOut
}
+697
View File
@@ -0,0 +1,697 @@
package bls12381
import (
"encoding/binary"
"fmt"
"io"
"math/big"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/internal"
)
// fp field element mod p
type fp [Limbs]uint64
var (
modulus = fp{
0xb9feffffffffaaab,
0x1eabfffeb153ffff,
0x6730d2a0f6b0f624,
0x64774b84f38512bf,
0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a,
}
halfModulus = fp{
0xdcff_7fff_ffff_d556,
0x0f55_ffff_58a9_ffff,
0xb398_6950_7b58_7b12,
0xb23b_a5c2_79c2_895f,
0x258d_d3db_21a5_d66b,
0x0d00_88f5_1cbf_f34d,
}
// 2^256 mod p
r = fp{
0x760900000002fffd,
0xebf4000bc40c0002,
0x5f48985753c758ba,
0x77ce585370525745,
0x5c071a97a256ec6d,
0x15f65ec3fa80e493,
}
// 2^512 mod p
r2 = fp{
0xf4df1f341c341746,
0x0a76e6a609d104f1,
0x8de5476c4c95b6d5,
0x67eb88a9939d83c0,
0x9a793e85b519952d,
0x11988fe592cae3aa,
}
// 2^768 mod p
r3 = fp{
0xed48ac6bd94ca1e0,
0x315f831e03a7adf8,
0x9a53352a615e29dd,
0x34c04e5e921e1761,
0x2512d43565724728,
0x0aa6346091755d4d,
}
biModulus = new(big.Int).SetBytes([]byte{
0x1a, 0x01, 0x11, 0xea, 0x39, 0x7f, 0xe6, 0x9a, 0x4b, 0x1b, 0xa7, 0xb6, 0x43, 0x4b, 0xac, 0xd7, 0x64, 0x77, 0x4b, 0x84, 0xf3, 0x85, 0x12, 0xbf, 0x67, 0x30, 0xd2, 0xa0, 0xf6, 0xb0, 0xf6, 0x24, 0x1e, 0xab, 0xff, 0xfe, 0xb1, 0x53, 0xff, 0xff, 0xb9, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xaa, 0xab,
},
)
)
// inv = -(p^{-1} mod 2^64) mod 2^64
const (
inv = 0x89f3_fffc_fffc_fffd
hashBytes = 64
)
// IsZero returns 1 if fp == 0, 0 otherwise
func (f *fp) IsZero() int {
t := f[0]
t |= f[1]
t |= f[2]
t |= f[3]
t |= f[4]
t |= f[5]
return int(((int64(t) | int64(-t)) >> 63) + 1)
}
// IsNonZero returns 1 if fp != 0, 0 otherwise
func (f *fp) IsNonZero() int {
t := f[0]
t |= f[1]
t |= f[2]
t |= f[3]
t |= f[4]
t |= f[5]
return int(-((int64(t) | int64(-t)) >> 63))
}
// IsOne returns 1 if fp == 1, 0 otherwise
func (f *fp) IsOne() int {
return f.Equal(&r)
}
// Cmp returns -1 if f < rhs
// 0 if f == rhs
// 1 if f > rhs
func (f *fp) Cmp(rhs *fp) int {
gt := uint64(0)
lt := uint64(0)
for i := 5; i >= 0; i-- {
// convert to two 64-bit numbers where
// the leading bits are zeros and hold no meaning
// so rhs - f actually means gt
// and f - rhs actually means lt.
rhsH := rhs[i] >> 32
rhsL := rhs[i] & 0xffffffff
lhsH := f[i] >> 32
lhsL := f[i] & 0xffffffff
// Check the leading bit
// if negative then f > rhs
// if positive then f < rhs
gt |= (rhsH - lhsH) >> 32 & 1 &^ lt
lt |= (lhsH - rhsH) >> 32 & 1 &^ gt
gt |= (rhsL - lhsL) >> 32 & 1 &^ lt
lt |= (lhsL - rhsL) >> 32 & 1 &^ gt
}
// Make the result -1 for <, 0 for =, 1 for >
return int(gt) - int(lt)
}
// Equal returns 1 if fp == rhs, 0 otherwise
func (f *fp) Equal(rhs *fp) int {
t := f[0] ^ rhs[0]
t |= f[1] ^ rhs[1]
t |= f[2] ^ rhs[2]
t |= f[3] ^ rhs[3]
t |= f[4] ^ rhs[4]
t |= f[5] ^ rhs[5]
return int(((int64(t) | int64(-t)) >> 63) + 1)
}
// LexicographicallyLargest returns 1 if
// this element is strictly lexicographically larger than its negation
// 0 otherwise
func (f *fp) LexicographicallyLargest() int {
var ff fp
ff.fromMontgomery(f)
_, borrow := sbb(ff[0], halfModulus[0], 0)
_, borrow = sbb(ff[1], halfModulus[1], borrow)
_, borrow = sbb(ff[2], halfModulus[2], borrow)
_, borrow = sbb(ff[3], halfModulus[3], borrow)
_, borrow = sbb(ff[4], halfModulus[4], borrow)
_, borrow = sbb(ff[5], halfModulus[5], borrow)
return (int(borrow) - 1) & 1
}
// Sgn0 returns the lowest bit value
func (f *fp) Sgn0() int {
t := new(fp).fromMontgomery(f)
return int(t[0] & 1)
}
// SetOne fp = r
func (f *fp) SetOne() *fp {
f[0] = r[0]
f[1] = r[1]
f[2] = r[2]
f[3] = r[3]
f[4] = r[4]
f[5] = r[5]
return f
}
// SetZero fp = 0
func (f *fp) SetZero() *fp {
f[0] = 0
f[1] = 0
f[2] = 0
f[3] = 0
f[4] = 0
f[5] = 0
return f
}
// SetUint64 fp = rhs
func (f *fp) SetUint64(rhs uint64) *fp {
f[0] = rhs
f[1] = 0
f[2] = 0
f[3] = 0
f[4] = 0
f[5] = 0
return f.toMontgomery(f)
}
// Random generates a random field element
func (f *fp) Random(reader io.Reader) (*fp, error) {
var t [WideFieldBytes]byte
n, err := reader.Read(t[:])
if err != nil {
return nil, err
}
if n != WideFieldBytes {
return nil, fmt.Errorf("can only read %d when %d are needed", n, WideFieldBytes)
}
return f.Hash(t[:]), nil
}
// Hash converts the byte sequence into a field element
func (f *fp) Hash(input []byte) *fp {
dst := []byte("BLS12381_XMD:SHA-256_SSWU_RO_")
xmd := native.ExpandMsgXmd(native.EllipticPointHasherSha256(), input, dst, hashBytes)
var t [WideFieldBytes]byte
copy(t[:hashBytes], internal.ReverseScalarBytes(xmd))
return f.SetBytesWide(&t)
}
// toMontgomery converts this field to montgomery form
func (f *fp) toMontgomery(a *fp) *fp {
// arg.R^0 * R^2 / R = arg.R
return f.Mul(a, &r2)
}
// fromMontgomery converts this field from montgomery form
func (f *fp) fromMontgomery(a *fp) *fp {
// Mul by 1 is division by 2^256 mod q
// out.Mul(arg, &[native.FieldLimbs]uint64{1, 0, 0, 0})
return f.montReduce(&[Limbs * 2]uint64{a[0], a[1], a[2], a[3], a[4], a[5], 0, 0, 0, 0, 0, 0})
}
// Neg performs modular negation
func (f *fp) Neg(a *fp) *fp {
// Subtract `arg` from `modulus`. Ignore final borrow
// since it can't underflow.
var t [Limbs]uint64
var borrow uint64
t[0], borrow = sbb(modulus[0], a[0], 0)
t[1], borrow = sbb(modulus[1], a[1], borrow)
t[2], borrow = sbb(modulus[2], a[2], borrow)
t[3], borrow = sbb(modulus[3], a[3], borrow)
t[4], borrow = sbb(modulus[4], a[4], borrow)
t[5], _ = sbb(modulus[5], a[5], borrow)
// t could be `modulus` if `arg`=0. Set mask=0 if self=0
// and 0xff..ff if `arg`!=0
mask := a[0] | a[1] | a[2] | a[3] | a[4] | a[5]
mask = -((mask | -mask) >> 63)
f[0] = t[0] & mask
f[1] = t[1] & mask
f[2] = t[2] & mask
f[3] = t[3] & mask
f[4] = t[4] & mask
f[5] = t[5] & mask
return f
}
// Square performs modular square
func (f *fp) Square(a *fp) *fp {
var r [2 * Limbs]uint64
var carry uint64
r[1], carry = mac(0, a[0], a[1], 0)
r[2], carry = mac(0, a[0], a[2], carry)
r[3], carry = mac(0, a[0], a[3], carry)
r[4], carry = mac(0, a[0], a[4], carry)
r[5], r[6] = mac(0, a[0], a[5], carry)
r[3], carry = mac(r[3], a[1], a[2], 0)
r[4], carry = mac(r[4], a[1], a[3], carry)
r[5], carry = mac(r[5], a[1], a[4], carry)
r[6], r[7] = mac(r[6], a[1], a[5], carry)
r[5], carry = mac(r[5], a[2], a[3], 0)
r[6], carry = mac(r[6], a[2], a[4], carry)
r[7], r[8] = mac(r[7], a[2], a[5], carry)
r[7], carry = mac(r[7], a[3], a[4], 0)
r[8], r[9] = mac(r[8], a[3], a[5], carry)
r[9], r[10] = mac(r[9], a[4], a[5], 0)
r[11] = r[10] >> 63
r[10] = (r[10] << 1) | r[9]>>63
r[9] = (r[9] << 1) | r[8]>>63
r[8] = (r[8] << 1) | r[7]>>63
r[7] = (r[7] << 1) | r[6]>>63
r[6] = (r[6] << 1) | r[5]>>63
r[5] = (r[5] << 1) | r[4]>>63
r[4] = (r[4] << 1) | r[3]>>63
r[3] = (r[3] << 1) | r[2]>>63
r[2] = (r[2] << 1) | r[1]>>63
r[1] = r[1] << 1
r[0], carry = mac(0, a[0], a[0], 0)
r[1], carry = adc(0, r[1], carry)
r[2], carry = mac(r[2], a[1], a[1], carry)
r[3], carry = adc(0, r[3], carry)
r[4], carry = mac(r[4], a[2], a[2], carry)
r[5], carry = adc(0, r[5], carry)
r[6], carry = mac(r[6], a[3], a[3], carry)
r[7], carry = adc(0, r[7], carry)
r[8], carry = mac(r[8], a[4], a[4], carry)
r[9], carry = adc(0, r[9], carry)
r[10], carry = mac(r[10], a[5], a[5], carry)
r[11], _ = adc(0, r[11], carry)
return f.montReduce(&r)
}
// Double this element
func (f *fp) Double(a *fp) *fp {
return f.Add(a, a)
}
// Mul performs modular multiplication
func (f *fp) Mul(arg1, arg2 *fp) *fp {
// Schoolbook multiplication
var r [2 * Limbs]uint64
var carry uint64
r[0], carry = mac(0, arg1[0], arg2[0], 0)
r[1], carry = mac(0, arg1[0], arg2[1], carry)
r[2], carry = mac(0, arg1[0], arg2[2], carry)
r[3], carry = mac(0, arg1[0], arg2[3], carry)
r[4], carry = mac(0, arg1[0], arg2[4], carry)
r[5], r[6] = mac(0, arg1[0], arg2[5], carry)
r[1], carry = mac(r[1], arg1[1], arg2[0], 0)
r[2], carry = mac(r[2], arg1[1], arg2[1], carry)
r[3], carry = mac(r[3], arg1[1], arg2[2], carry)
r[4], carry = mac(r[4], arg1[1], arg2[3], carry)
r[5], carry = mac(r[5], arg1[1], arg2[4], carry)
r[6], r[7] = mac(r[6], arg1[1], arg2[5], carry)
r[2], carry = mac(r[2], arg1[2], arg2[0], 0)
r[3], carry = mac(r[3], arg1[2], arg2[1], carry)
r[4], carry = mac(r[4], arg1[2], arg2[2], carry)
r[5], carry = mac(r[5], arg1[2], arg2[3], carry)
r[6], carry = mac(r[6], arg1[2], arg2[4], carry)
r[7], r[8] = mac(r[7], arg1[2], arg2[5], carry)
r[3], carry = mac(r[3], arg1[3], arg2[0], 0)
r[4], carry = mac(r[4], arg1[3], arg2[1], carry)
r[5], carry = mac(r[5], arg1[3], arg2[2], carry)
r[6], carry = mac(r[6], arg1[3], arg2[3], carry)
r[7], carry = mac(r[7], arg1[3], arg2[4], carry)
r[8], r[9] = mac(r[8], arg1[3], arg2[5], carry)
r[4], carry = mac(r[4], arg1[4], arg2[0], 0)
r[5], carry = mac(r[5], arg1[4], arg2[1], carry)
r[6], carry = mac(r[6], arg1[4], arg2[2], carry)
r[7], carry = mac(r[7], arg1[4], arg2[3], carry)
r[8], carry = mac(r[8], arg1[4], arg2[4], carry)
r[9], r[10] = mac(r[9], arg1[4], arg2[5], carry)
r[5], carry = mac(r[5], arg1[5], arg2[0], 0)
r[6], carry = mac(r[6], arg1[5], arg2[1], carry)
r[7], carry = mac(r[7], arg1[5], arg2[2], carry)
r[8], carry = mac(r[8], arg1[5], arg2[3], carry)
r[9], carry = mac(r[9], arg1[5], arg2[4], carry)
r[10], r[11] = mac(r[10], arg1[5], arg2[5], carry)
return f.montReduce(&r)
}
// MulBy3b returns arg * 12 or 3 * b
func (f *fp) MulBy3b(arg *fp) *fp {
var a, t fp
a.Double(arg) // 2
t.Double(&a) // 4
a.Double(&t) // 8
a.Add(&a, &t) // 12
return f.Set(&a)
}
// Add performs modular addition
func (f *fp) Add(arg1, arg2 *fp) *fp {
var t fp
var carry uint64
t[0], carry = adc(arg1[0], arg2[0], 0)
t[1], carry = adc(arg1[1], arg2[1], carry)
t[2], carry = adc(arg1[2], arg2[2], carry)
t[3], carry = adc(arg1[3], arg2[3], carry)
t[4], carry = adc(arg1[4], arg2[4], carry)
t[5], _ = adc(arg1[5], arg2[5], carry)
// Subtract the modulus to ensure the value
// is smaller.
return f.Sub(&t, &modulus)
}
// Sub performs modular subtraction
func (f *fp) Sub(arg1, arg2 *fp) *fp {
d0, borrow := sbb(arg1[0], arg2[0], 0)
d1, borrow := sbb(arg1[1], arg2[1], borrow)
d2, borrow := sbb(arg1[2], arg2[2], borrow)
d3, borrow := sbb(arg1[3], arg2[3], borrow)
d4, borrow := sbb(arg1[4], arg2[4], borrow)
d5, borrow := sbb(arg1[5], arg2[5], borrow)
// If underflow occurred on the final limb, borrow 0xff...ff, otherwise
// borrow = 0x00...00. Conditionally mask to add the modulus
borrow = -borrow
d0, carry := adc(d0, modulus[0]&borrow, 0)
d1, carry = adc(d1, modulus[1]&borrow, carry)
d2, carry = adc(d2, modulus[2]&borrow, carry)
d3, carry = adc(d3, modulus[3]&borrow, carry)
d4, carry = adc(d4, modulus[4]&borrow, carry)
d5, _ = adc(d5, modulus[5]&borrow, carry)
f[0] = d0
f[1] = d1
f[2] = d2
f[3] = d3
f[4] = d4
f[5] = d5
return f
}
// Sqrt performs modular square root
func (f *fp) Sqrt(a *fp) (*fp, int) {
// Shank's method, as p = 3 (mod 4). This means
// exponentiate by (p+1)/4. This only works for elements
// that are actually quadratic residue,
// so check the result at the end.
var c, z fp
z.pow(a, &fp{
0xee7fbfffffffeaab,
0x07aaffffac54ffff,
0xd9cc34a83dac3d89,
0xd91dd2e13ce144af,
0x92c6e9ed90d2eb35,
0x0680447a8e5ff9a6,
})
c.Square(&z)
wasSquare := c.Equal(a)
f.CMove(f, &z, wasSquare)
return f, wasSquare
}
// Invert performs modular inverse
func (f *fp) Invert(a *fp) (*fp, int) {
// Exponentiate by p - 2
t := &fp{}
t.pow(a, &fp{
0xb9feffffffffaaa9,
0x1eabfffeb153ffff,
0x6730d2a0f6b0f624,
0x64774b84f38512bf,
0x4b1ba7b6434bacd7,
0x1a0111ea397fe69a,
})
wasInverted := a.IsNonZero()
f.CMove(a, t, wasInverted)
return f, wasInverted
}
// SetBytes converts a little endian byte array into a field element
// return 0 if the bytes are not in the field, 1 if they are
func (f *fp) SetBytes(arg *[FieldBytes]byte) (*fp, int) {
var borrow uint64
t := &fp{}
t[0] = binary.LittleEndian.Uint64(arg[:8])
t[1] = binary.LittleEndian.Uint64(arg[8:16])
t[2] = binary.LittleEndian.Uint64(arg[16:24])
t[3] = binary.LittleEndian.Uint64(arg[24:32])
t[4] = binary.LittleEndian.Uint64(arg[32:40])
t[5] = binary.LittleEndian.Uint64(arg[40:])
// Try to subtract the modulus
_, borrow = sbb(t[0], modulus[0], 0)
_, borrow = sbb(t[1], modulus[1], borrow)
_, borrow = sbb(t[2], modulus[2], borrow)
_, borrow = sbb(t[3], modulus[3], borrow)
_, borrow = sbb(t[4], modulus[4], borrow)
_, borrow = sbb(t[5], modulus[5], borrow)
// If the element is smaller than modulus then the
// subtraction will underflow, producing a borrow value
// of 1. Otherwise, it'll be zero.
mask := int(borrow)
return f.CMove(f, t.toMontgomery(t), mask), mask
}
// SetBytesWide takes 96 bytes as input and treats them as a 512-bit number.
// Attributed to https://github.com/zcash/pasta_curves/blob/main/src/fields/Fp.rs#L255
// We reduce an arbitrary 768-bit number by decomposing it into two 384-bit digits
// with the higher bits multiplied by 2^384. Thus, we perform two reductions
//
// 1. the lower bits are multiplied by r^2, as normal
// 2. the upper bits are multiplied by r^2 * 2^384 = r^3
//
// and computing their sum in the field. It remains to see that arbitrary 384-bit
// numbers can be placed into Montgomery form safely using the reduction. The
// reduction works so long as the product is less than r=2^384 multiplied by
// the modulus. This holds because for any `c` smaller than the modulus, we have
// that (2^384 - 1)*c is an acceptable product for the reduction. Therefore, the
// reduction always works so long as `c` is in the field; in this case it is either the
// constant `r2` or `r3`.
func (f *fp) SetBytesWide(a *[WideFieldBytes]byte) *fp {
d0 := &fp{
binary.LittleEndian.Uint64(a[:8]),
binary.LittleEndian.Uint64(a[8:16]),
binary.LittleEndian.Uint64(a[16:24]),
binary.LittleEndian.Uint64(a[24:32]),
binary.LittleEndian.Uint64(a[32:40]),
binary.LittleEndian.Uint64(a[40:48]),
}
d1 := &fp{
binary.LittleEndian.Uint64(a[48:56]),
binary.LittleEndian.Uint64(a[56:64]),
binary.LittleEndian.Uint64(a[64:72]),
binary.LittleEndian.Uint64(a[72:80]),
binary.LittleEndian.Uint64(a[80:88]),
binary.LittleEndian.Uint64(a[88:96]),
}
// d0*r2 + d1*r3
d0.Mul(d0, &r2)
d1.Mul(d1, &r3)
return f.Add(d0, d1)
}
// SetBigInt initializes an element from big.Int
// The value is reduced by the modulus
func (f *fp) SetBigInt(bi *big.Int) *fp {
var buffer [FieldBytes]byte
t := new(big.Int).Set(bi)
t.Mod(t, biModulus)
t.FillBytes(buffer[:])
copy(buffer[:], internal.ReverseScalarBytes(buffer[:]))
_, _ = f.SetBytes(&buffer)
return f
}
// Set copies a into fp
func (f *fp) Set(a *fp) *fp {
f[0] = a[0]
f[1] = a[1]
f[2] = a[2]
f[3] = a[3]
f[4] = a[4]
f[5] = a[5]
return f
}
// SetLimbs converts an array into a field element
// by converting to montgomery form
func (f *fp) SetLimbs(a *[Limbs]uint64) *fp {
return f.toMontgomery((*fp)(a))
}
// SetRaw converts a raw array into a field element
// Assumes input is already in montgomery form
func (f *fp) SetRaw(a *[Limbs]uint64) *fp {
f[0] = a[0]
f[1] = a[1]
f[2] = a[2]
f[3] = a[3]
f[4] = a[4]
f[5] = a[5]
return f
}
// Bytes converts a field element to a little endian byte array
func (f *fp) Bytes() [FieldBytes]byte {
var out [FieldBytes]byte
t := new(fp).fromMontgomery(f)
binary.LittleEndian.PutUint64(out[:8], t[0])
binary.LittleEndian.PutUint64(out[8:16], t[1])
binary.LittleEndian.PutUint64(out[16:24], t[2])
binary.LittleEndian.PutUint64(out[24:32], t[3])
binary.LittleEndian.PutUint64(out[32:40], t[4])
binary.LittleEndian.PutUint64(out[40:], t[5])
return out
}
// BigInt converts this element into the big.Int struct
func (f *fp) BigInt() *big.Int {
buffer := f.Bytes()
return new(big.Int).SetBytes(internal.ReverseScalarBytes(buffer[:]))
}
// Raw converts this element into the a [FieldLimbs]uint64
func (f *fp) Raw() [Limbs]uint64 {
t := new(fp).fromMontgomery(f)
return *t
}
// CMove performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f *fp) CMove(arg1, arg2 *fp, choice int) *fp {
mask := uint64(-choice)
f[0] = arg1[0] ^ ((arg1[0] ^ arg2[0]) & mask)
f[1] = arg1[1] ^ ((arg1[1] ^ arg2[1]) & mask)
f[2] = arg1[2] ^ ((arg1[2] ^ arg2[2]) & mask)
f[3] = arg1[3] ^ ((arg1[3] ^ arg2[3]) & mask)
f[4] = arg1[4] ^ ((arg1[4] ^ arg2[4]) & mask)
f[5] = arg1[5] ^ ((arg1[5] ^ arg2[5]) & mask)
return f
}
// CNeg conditionally negates a if choice == 1
func (f *fp) CNeg(a *fp, choice int) *fp {
var t fp
t.Neg(a)
return f.CMove(f, &t, choice)
}
// Exp raises base^exp.
func (f *fp) Exp(base, exp *fp) *fp {
e := (&fp{}).fromMontgomery(exp)
return f.pow(base, e)
}
func (f *fp) pow(base, e *fp) *fp {
var tmp, res fp
res.SetOne()
for i := len(e) - 1; i >= 0; i-- {
for j := 63; j >= 0; j-- {
res.Square(&res)
tmp.Mul(&res, base)
res.CMove(&res, &tmp, int(e[i]>>j)&1)
}
}
f[0] = res[0]
f[1] = res[1]
f[2] = res[2]
f[3] = res[3]
f[4] = res[4]
f[5] = res[5]
return f
}
// montReduce performs the montgomery reduction
func (f *fp) montReduce(r *[2 * Limbs]uint64) *fp {
// Taken from Algorithm 14.32 in Handbook of Applied Cryptography
var r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, carry, k uint64
var rr fp
k = r[0] * inv
_, carry = mac(r[0], k, modulus[0], 0)
r1, carry = mac(r[1], k, modulus[1], carry)
r2, carry = mac(r[2], k, modulus[2], carry)
r3, carry = mac(r[3], k, modulus[3], carry)
r4, carry = mac(r[4], k, modulus[4], carry)
r5, carry = mac(r[5], k, modulus[5], carry)
r6, r7 = adc(r[6], 0, carry)
k = r1 * inv
_, carry = mac(r1, k, modulus[0], 0)
r2, carry = mac(r2, k, modulus[1], carry)
r3, carry = mac(r3, k, modulus[2], carry)
r4, carry = mac(r4, k, modulus[3], carry)
r5, carry = mac(r5, k, modulus[4], carry)
r6, carry = mac(r6, k, modulus[5], carry)
r7, r8 = adc(r7, r[7], carry)
k = r2 * inv
_, carry = mac(r2, k, modulus[0], 0)
r3, carry = mac(r3, k, modulus[1], carry)
r4, carry = mac(r4, k, modulus[2], carry)
r5, carry = mac(r5, k, modulus[3], carry)
r6, carry = mac(r6, k, modulus[4], carry)
r7, carry = mac(r7, k, modulus[5], carry)
r8, r9 = adc(r8, r[8], carry)
k = r3 * inv
_, carry = mac(r3, k, modulus[0], 0)
r4, carry = mac(r4, k, modulus[1], carry)
r5, carry = mac(r5, k, modulus[2], carry)
r6, carry = mac(r6, k, modulus[3], carry)
r7, carry = mac(r7, k, modulus[4], carry)
r8, carry = mac(r8, k, modulus[5], carry)
r9, r10 = adc(r9, r[9], carry)
k = r4 * inv
_, carry = mac(r4, k, modulus[0], 0)
r5, carry = mac(r5, k, modulus[1], carry)
r6, carry = mac(r6, k, modulus[2], carry)
r7, carry = mac(r7, k, modulus[3], carry)
r8, carry = mac(r8, k, modulus[4], carry)
r9, carry = mac(r9, k, modulus[5], carry)
r10, r11 = adc(r10, r[10], carry)
k = r5 * inv
_, carry = mac(r5, k, modulus[0], 0)
rr[0], carry = mac(r6, k, modulus[1], carry)
rr[1], carry = mac(r7, k, modulus[2], carry)
rr[2], carry = mac(r8, k, modulus[3], carry)
rr[3], carry = mac(r9, k, modulus[4], carry)
rr[4], carry = mac(r10, k, modulus[5], carry)
rr[5], _ = adc(r11, r[11], carry)
return f.Sub(&rr, &modulus)
}
+231
View File
@@ -0,0 +1,231 @@
package bls12381
import "io"
// fp12 represents an element a + b w of fp^12 = fp^6 / w^2 - v.
type fp12 struct {
A, B fp6
}
// SetFp creates an element from a lower field
func (f *fp12) SetFp(a *fp) *fp12 {
f.A.SetFp(a)
f.B.SetZero()
return f
}
// SetFp2 creates an element from a lower field
func (f *fp12) SetFp2(a *fp2) *fp12 {
f.A.SetFp2(a)
f.B.SetZero()
return f
}
// SetFp6 creates an element from a lower field
func (f *fp12) SetFp6(a *fp6) *fp12 {
f.A.Set(a)
f.B.SetZero()
return f
}
// Set copies the value `a`
func (f *fp12) Set(a *fp12) *fp12 {
f.A.Set(&a.A)
f.B.Set(&a.B)
return f
}
// SetZero fp6 to zero
func (f *fp12) SetZero() *fp12 {
f.A.SetZero()
f.B.SetZero()
return f
}
// SetOne fp6 to multiplicative identity element
func (f *fp12) SetOne() *fp12 {
f.A.SetOne()
f.B.SetZero()
return f
}
// Random generates a random field element
func (f *fp12) Random(reader io.Reader) (*fp12, error) {
a, err := new(fp6).Random(reader)
if err != nil {
return nil, err
}
b, err := new(fp6).Random(reader)
if err != nil {
return nil, err
}
f.A.Set(a)
f.B.Set(b)
return f, nil
}
// Square computes arg^2
func (f *fp12) Square(arg *fp12) *fp12 {
var ab, apb, aTick, bTick, t fp6
ab.Mul(&arg.A, &arg.B)
apb.Add(&arg.A, &arg.B)
aTick.MulByNonResidue(&arg.B)
aTick.Add(&aTick, &arg.A)
aTick.Mul(&aTick, &apb)
aTick.Sub(&aTick, &ab)
t.MulByNonResidue(&ab)
aTick.Sub(&aTick, &t)
bTick.Double(&ab)
f.A.Set(&aTick)
f.B.Set(&bTick)
return f
}
// Invert computes this element's field inversion
func (f *fp12) Invert(arg *fp12) (*fp12, int) {
var a, b, t fp6
a.Square(&arg.A)
b.Square(&arg.B)
b.MulByNonResidue(&b)
a.Sub(&a, &b)
_, wasInverted := t.Invert(&a)
a.Mul(&arg.A, &t)
t.Neg(&t)
b.Mul(&arg.B, &t)
f.A.CMove(&f.A, &a, wasInverted)
f.B.CMove(&f.B, &b, wasInverted)
return f, wasInverted
}
// Add computes arg1+arg2
func (f *fp12) Add(arg1, arg2 *fp12) *fp12 {
f.A.Add(&arg1.A, &arg2.A)
f.B.Add(&arg1.B, &arg2.B)
return f
}
// Sub computes arg1-arg2
func (f *fp12) Sub(arg1, arg2 *fp12) *fp12 {
f.A.Sub(&arg1.A, &arg2.A)
f.B.Sub(&arg1.B, &arg2.B)
return f
}
// Mul computes arg1*arg2
func (f *fp12) Mul(arg1, arg2 *fp12) *fp12 {
var aa, bb, a2b2, a, b fp6
aa.Mul(&arg1.A, &arg2.A)
bb.Mul(&arg1.B, &arg2.B)
a2b2.Add(&arg2.A, &arg2.B)
b.Add(&arg1.A, &arg1.B)
b.Mul(&b, &a2b2)
b.Sub(&b, &aa)
b.Sub(&b, &bb)
a.MulByNonResidue(&bb)
a.Add(&a, &aa)
f.A.Set(&a)
f.B.Set(&b)
return f
}
// Neg computes the field negation
func (f *fp12) Neg(arg *fp12) *fp12 {
f.A.Neg(&arg.A)
f.B.Neg(&arg.B)
return f
}
// MulByABD computes arg * a * b * c
func (f *fp12) MulByABD(arg *fp12, a, b, d *fp2) *fp12 {
var aa, bb, aTick, bTick fp6
var bd fp2
aa.MulByAB(&arg.A, a, b)
bb.MulByB(&arg.B, d)
bd.Add(b, d)
bTick.Add(&arg.A, &arg.B)
bTick.MulByAB(&bTick, a, &bd)
bTick.Sub(&bTick, &aa)
bTick.Sub(&bTick, &bb)
aTick.MulByNonResidue(&bb)
aTick.Add(&aTick, &aa)
f.A.Set(&aTick)
f.B.Set(&bTick)
return f
}
// Conjugate computes the field conjugation
func (f *fp12) Conjugate(arg *fp12) *fp12 {
f.A.Set(&arg.A)
f.B.Neg(&arg.B)
return f
}
// FrobeniusMap raises this element to p.
func (f *fp12) FrobeniusMap(arg *fp12) *fp12 {
var a, b, up1epm1div6 fp6
// (u + 1)^((p - 1) / 6)
up1epm1div6.A = fp2{
A: fp{
0x07089552b319d465,
0xc6695f92b50a8313,
0x97e83cccd117228f,
0xa35baecab2dc29ee,
0x1ce393ea5daace4d,
0x08f2220fb0fb66eb,
},
B: fp{
0xb2f66aad4ce5d646,
0x5842a06bfc497cec,
0xcf4895d42599d394,
0xc11b9cba40a8e8d0,
0x2e3813cbe5a0de89,
0x110eefda88847faf,
},
}
a.FrobeniusMap(&arg.A)
b.FrobeniusMap(&arg.B)
// b' = b' * (u + 1)^((p - 1) / 6)
b.Mul(&b, &up1epm1div6)
f.A.Set(&a)
f.B.Set(&b)
return f
}
// Equal returns 1 if fp12 == rhs, 0 otherwise
func (f *fp12) Equal(rhs *fp12) int {
return f.A.Equal(&rhs.A) & f.B.Equal(&rhs.B)
}
// IsZero returns 1 if fp6 == 0, 0 otherwise
func (f *fp12) IsZero() int {
return f.A.IsZero() & f.B.IsZero()
}
// IsOne returns 1 if fp12 == 1, 0 otherwise
func (f *fp12) IsOne() int {
return f.A.IsOne() & f.B.IsZero()
}
// CMove performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f *fp12) CMove(arg1, arg2 *fp12, choice int) *fp12 {
f.A.CMove(&arg1.A, &arg2.A, choice)
f.B.CMove(&arg1.B, &arg2.B, choice)
return f
}
+417
View File
@@ -0,0 +1,417 @@
package bls12381
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFp12Arithmetic(t *testing.T) {
var aa, bb, cc, d, e, f fp12
a := fp12{
A: fp6{
A: fp2{
A: fp{
0x47f9cb98b1b82d58,
0x5fe911eba3aa1d9d,
0x96bf1b5f4dd81db3,
0x8100d27cc9259f5b,
0xafa20b9674640eab,
0x09bbcea7d8d9497d,
},
B: fp{
0x0303cb98b1662daa,
0xd93110aa0a621d5a,
0xbfa9820c5be4a468,
0x0ba3643ecb05a348,
0xdc3534bb1f1c25a6,
0x06c305bb19c0e1c1,
},
},
B: fp2{
A: fp{
0x46f9cb98b162d858,
0x0be9109cf7aa1d57,
0xc791bc55fece41d2,
0xf84c57704e385ec2,
0xcb49c1d9c010e60f,
0x0acdb8e158bfe3c8,
},
B: fp{
0x8aefcb98b15f8306,
0x3ea1108fe4f21d54,
0xcf79f69fa1b7df3b,
0xe4f54aa1d16b1a3c,
0xba5e4ef86105a679,
0x0ed86c0797bee5cf,
},
},
C: fp2{
A: fp{
0xcee5cb98b15c2db4,
0x71591082d23a1d51,
0xd76230e944a17ca4,
0xd19e3dd3549dd5b6,
0xa972dc1701fa66e3,
0x12e31f2dd6bde7d6,
},
B: fp{
0xad2acb98b1732d9d,
0x2cfd10dd06961d64,
0x07396b86c6ef24e8,
0xbd76e2fdb1bfc820,
0x6afea7f6de94d0d5,
0x10994b0c5744c040,
},
},
},
B: fp6{
A: fp2{
A: fp{
0x47f9cb98b1b82d58,
0x5fe911eba3aa1d9d,
0x96bf1b5f4dd81db3,
0x8100d27cc9259f5b,
0xafa20b9674640eab,
0x09bbcea7d8d9497d,
},
B: fp{
0x0303cb98b1662daa,
0xd93110aa0a621d5a,
0xbfa9820c5be4a468,
0x0ba3643ecb05a348,
0xdc3534bb1f1c25a6,
0x06c305bb19c0e1c1,
},
},
B: fp2{
A: fp{
0x46f9cb98b162d858,
0x0be9109cf7aa1d57,
0xc791bc55fece41d2,
0xf84c57704e385ec2,
0xcb49c1d9c010e60f,
0x0acdb8e158bfe3c8,
},
B: fp{
0x8aefcb98b15f8306,
0x3ea1108fe4f21d54,
0xcf79f69fa1b7df3b,
0xe4f54aa1d16b1a3c,
0xba5e4ef86105a679,
0x0ed86c0797bee5cf,
},
},
C: fp2{
A: fp{
0xcee5cb98b15c2db4,
0x71591082d23a1d51,
0xd76230e944a17ca4,
0xd19e3dd3549dd5b6,
0xa972dc1701fa66e3,
0x12e31f2dd6bde7d6,
},
B: fp{
0xad2acb98b1732d9d,
0x2cfd10dd06961d64,
0x07396b86c6ef24e8,
0xbd76e2fdb1bfc820,
0x6afea7f6de94d0d5,
0x10994b0c5744c040,
},
},
},
}
b := fp12{
A: fp6{
A: fp2{
A: fp{
0x47f9_cb98_b1b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd8_1db3,
0x8100_d272_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
},
B: fp{
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
},
},
B: fp2{
A: fp{
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0xc791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d9_c010_e60f,
0x0acd_b8e1_58bf_e348,
},
B: fp{
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a1b7_df3b,
0xe4f5_4aa1_d16b_1a3c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
},
},
C: fp2{
A: fp{
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_30e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
},
B: fp{
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_5744_c040,
},
},
},
B: fp6{
A: fp2{
A: fp{
0x47f9_cb98_b1b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd2_1db3,
0x8100_d27c_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
},
B: fp{
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
},
},
B: fp2{
A: fp{
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0xc791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d9_c010_e60f,
0x0acd_b8e1_58bf_e3c8,
},
B: fp{
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a117_df3b,
0xe4f5_4aa1_d16b_1a3c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
},
},
C: fp2{
A: fp{
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_30e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
},
B: fp{
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_5744_c040,
},
},
},
}
c := fp12{
A: fp6{
A: fp2{
A: fp{
0x47f9_cb98_71b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd8_1db3,
0x8100_d27c_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
},
B: fp{
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
},
},
B: fp2{
A: fp{
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0x7791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d9_c010_e60f,
0x0acd_b8e1_58bf_e3c8,
},
B: fp{
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a1b7_df3b,
0xe4f5_4aa1_d16b_133c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
},
},
C: fp2{
A: fp{
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_40e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
},
B: fp{
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_1744_c040,
},
},
},
B: fp6{
A: fp2{
A: fp{
0x47f9_cb98_b1b8_2d58,
0x5fe9_11eb_a3aa_1d9d,
0x96bf_1b5f_4dd8_1db3,
0x8100_d27c_c925_9f5b,
0xafa2_0b96_7464_0eab,
0x09bb_cea7_d8d9_497d,
},
B: fp{
0x0303_cb98_b166_2daa,
0xd931_10aa_0a62_1d5a,
0xbfa9_820c_5be4_a468,
0x0ba3_643e_cb05_a348,
0xdc35_34bb_1f1c_25a6,
0x06c3_05bb_19c0_e1c1,
},
},
B: fp2{
A: fp{
0x46f9_cb98_b162_d858,
0x0be9_109c_f7aa_1d57,
0xc791_bc55_fece_41d2,
0xf84c_5770_4e38_5ec2,
0xcb49_c1d3_c010_e60f,
0x0acd_b8e1_58bf_e3c8,
},
B: fp{
0x8aef_cb98_b15f_8306,
0x3ea1_108f_e4f2_1d54,
0xcf79_f69f_a1b7_df3b,
0xe4f5_4aa1_d16b_1a3c,
0xba5e_4ef8_6105_a679,
0x0ed8_6c07_97be_e5cf,
},
},
C: fp2{
A: fp{
0xcee5_cb98_b15c_2db4,
0x7159_1082_d23a_1d51,
0xd762_30e9_44a1_7ca4,
0xd19e_3dd3_549d_d5b6,
0xa972_dc17_01fa_66e3,
0x12e3_1f2d_d6bd_e7d6,
},
B: fp{
0xad2a_cb98_b173_2d9d,
0x2cfd_10dd_0696_1d64,
0x0739_6b86_c6ef_24e8,
0xbd76_e2fd_b1bf_c820,
0x6afe_a7f6_de94_d0d5,
0x1099_4b0c_5744_1040,
},
},
},
}
aa.Square(&a)
aa.Invert(&aa)
aa.Square(&aa)
aa.Add(&aa, &c)
bb.Square(&b)
bb.Invert(&bb)
bb.Square(&bb)
bb.Add(&bb, &aa)
cc.Square(&c)
cc.Invert(&cc)
cc.Square(&cc)
cc.Add(&cc, &bb)
d.Square(&aa)
e.Mul(&aa, &aa)
require.Equal(t, 1, e.Equal(&d))
d.Square(&bb)
e.Mul(&bb, &bb)
require.Equal(t, 1, e.Equal(&d))
d.Square(&cc)
e.Mul(&cc, &cc)
require.Equal(t, 1, e.Equal(&d))
d.Square(&cc)
e.Add(&aa, &bb)
d.Mul(&d, &e)
e.Mul(&cc, &cc)
e.Mul(&e, &aa)
f.Mul(&cc, &cc)
f.Mul(&f, &bb)
e.Add(&e, &f)
require.Equal(t, 1, e.Equal(&d))
d.Invert(&aa)
e.Invert(&bb)
f.Mul(&aa, &bb)
f.Invert(&f)
require.Equal(t, 1, f.Equal(e.Mul(&e, &d)))
require.Equal(t, 1, d.Mul(&d, &aa).IsOne())
require.Equal(t, 0, aa.Equal(d.FrobeniusMap(&aa)))
d.FrobeniusMap(&aa).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d).
FrobeniusMap(&d)
require.Equal(t, 1, aa.Equal(&d))
}
+344
View File
@@ -0,0 +1,344 @@
package bls12381
import (
"io"
)
// fp2 is a point in p^2
type fp2 struct {
A, B fp
}
// Set copies a into fp2
func (f *fp2) Set(a *fp2) *fp2 {
f.A.Set(&a.A)
f.B.Set(&a.B)
return f
}
// SetZero fp2 = 0
func (f *fp2) SetZero() *fp2 {
f.A.SetZero()
f.B.SetZero()
return f
}
// SetOne fp2 to the multiplicative identity element
func (f *fp2) SetOne() *fp2 {
f.A.SetOne()
f.B.SetZero()
return f
}
// SetFp creates an element from a lower field
func (f *fp2) SetFp(a *fp) *fp2 {
f.A.Set(a)
f.B.SetZero()
return f
}
// Random generates a random field element
func (f *fp2) Random(reader io.Reader) (*fp2, error) {
a, err := new(fp).Random(reader)
if err != nil {
return nil, err
}
b, err := new(fp).Random(reader)
if err != nil {
return nil, err
}
f.A = *a
f.B = *b
return f, nil
}
// IsZero returns 1 if fp2 == 0, 0 otherwise
func (f *fp2) IsZero() int {
return f.A.IsZero() & f.B.IsZero()
}
// IsOne returns 1 if fp2 == 1, 0 otherwise
func (f *fp2) IsOne() int {
return f.A.IsOne() & f.B.IsZero()
}
// Equal returns 1 if f == rhs, 0 otherwise
func (f *fp2) Equal(rhs *fp2) int {
return f.A.Equal(&rhs.A) & f.B.Equal(&rhs.B)
}
// LexicographicallyLargest returns 1 if
// this element is strictly lexicographically larger than its negation
// 0 otherwise
func (f *fp2) LexicographicallyLargest() int {
// If this element's B coefficient is lexicographically largest
// then it is lexicographically largest. Otherwise, in the event
// the B coefficient is zero and the A coefficient is
// lexicographically largest, then this element is lexicographically
// largest.
return f.B.LexicographicallyLargest() |
f.B.IsZero()&f.A.LexicographicallyLargest()
}
// Sgn0 returns the lowest bit value
func (f *fp2) Sgn0() int {
// if A = 0 return B.Sgn0 else A.Sgn0
a := f.A.IsZero()
t := f.B.Sgn0() & a
a = -a + 1
t |= f.A.Sgn0() & a
return t
}
// FrobeniusMap raises this element to p.
func (f *fp2) FrobeniusMap(a *fp2) *fp2 {
// This is always just a conjugation. If you're curious why, here's
// an article about it: https://alicebob.cryptoland.net/the-frobenius-endomorphism-with-finite-fields/
return f.Conjugate(a)
}
// Conjugate computes the conjugation of this element
func (f *fp2) Conjugate(a *fp2) *fp2 {
f.A.Set(&a.A)
f.B.Neg(&a.B)
return f
}
// MulByNonResidue computes the following:
// multiply a + bu by u + 1, getting
// au + a + bu^2 + bu
// and because u^2 = -1, we get
// (a - b) + (a + b)u
func (f *fp2) MulByNonResidue(a *fp2) *fp2 {
var aa, bb fp
aa.Sub(&a.A, &a.B)
bb.Add(&a.A, &a.B)
f.A.Set(&aa)
f.B.Set(&bb)
return f
}
// Square computes the square of this element
func (f *fp2) Square(arg *fp2) *fp2 {
var a, b, c fp
// Complex squaring:
//
// v0 = a * b
// a' = (a + b) * (a + \beta*b) - v0 - \beta * v0
// b' = 2 * v0
//
// In BLS12-381's F_{p^2}, our \beta is -1, so we
// can modify this formula:
//
// a' = (a + b) * (a - b)
// b' = 2 * a * b
a.Add(&arg.A, &arg.B)
b.Sub(&arg.A, &arg.B)
c.Add(&arg.A, &arg.A)
f.A.Mul(&a, &b)
f.B.Mul(&c, &arg.B)
return f
}
// Add performs field addition
func (f *fp2) Add(arg1, arg2 *fp2) *fp2 {
f.A.Add(&arg1.A, &arg2.A)
f.B.Add(&arg1.B, &arg2.B)
return f
}
// Double doubles specified element
func (f *fp2) Double(a *fp2) *fp2 {
f.A.Double(&a.A)
f.B.Double(&a.B)
return f
}
// Sub performs field subtraction
func (f *fp2) Sub(arg1, arg2 *fp2) *fp2 {
f.A.Sub(&arg1.A, &arg2.A)
f.B.Sub(&arg1.B, &arg2.B)
return f
}
// Mul computes Karatsuba multiplication
func (f *fp2) Mul(arg1, arg2 *fp2) *fp2 {
var v0, v1, t, a, b fp
// Karatsuba multiplication:
//
// v0 = a0 * b0
// v1 = a1 * b1
// c0 = v0 + \beta * v1
// c1 = (a0 + a1) * (b0 + b1) - v0 - v1
//
// In BLS12-381's F_{p^2}, our \beta is -1, so we
// can modify this formula. (Also, since we always
// subtract v1, we can compute v1 = -a1 * b1.)
//
// v0 = a0 * a1
// v1 = (-b0) * b1
// a' = v0 + v1
// b' = (a0 + b0) * (a1 + b1) - v0 + v1
v0.Mul(&arg1.A, &arg2.A)
v1.Mul(new(fp).Neg(&arg1.B), &arg2.B)
a.Add(&v0, &v1)
b.Add(&arg1.A, &arg1.B)
t.Add(&arg2.A, &arg2.B)
b.Mul(&b, &t)
b.Sub(&b, &v0)
b.Add(&b, &v1)
f.A.Set(&a)
f.B.Set(&b)
return f
}
func (f *fp2) Mul0(arg1 *fp2, arg2 *fp) *fp2 {
f.A.Mul(&arg1.A, arg2)
f.B.Mul(&arg1.B, arg2)
return f
}
// MulBy3b returns arg * 12 or 3 * b
func (f *fp2) MulBy3b(arg *fp2) *fp2 {
return f.Mul(arg, &curveG23B)
}
// Neg performs field negation
func (f *fp2) Neg(a *fp2) *fp2 {
f.A.Neg(&a.A)
f.B.Neg(&a.B)
return f
}
// Sqrt performs field square root
func (f *fp2) Sqrt(a *fp2) (*fp2, int) {
// Algorithm 9, https://eprint.iacr.org/2012/685.pdf
// with constant time modifications.
var a1, alpha, x0, t, res, res2 fp2
e1 := a.IsZero()
// a1 = self^((p - 3) / 4)
a1.pow(a, &[Limbs]uint64{
0xee7fbfffffffeaaa,
0x07aaffffac54ffff,
0xd9cc34a83dac3d89,
0xd91dd2e13ce144af,
0x92c6e9ed90d2eb35,
0x0680447a8e5ff9a6,
})
// alpha = a1^2 * a = a^((p - 3) / 2 + 1) = a^((p - 1) / 2)
alpha.Square(&a1)
alpha.Mul(&alpha, a)
// x0 = self^((p + 1) / 4)
x0.Mul(&a1, a)
// In the event that alpha = -1, the element is order p - 1. So
// we're just trying to get the square of an element of the subfield
// fp. This is given by x0 * u, since u = sqrt(-1). Since the element
// x0 = a + bu has b = 0, the solution is therefore au.
res2.A.Neg(&x0.B)
res2.B.Set(&x0.A)
// alpha == -1
e2 := alpha.Equal(&fp2{
A: fp{
0x43f5fffffffcaaae,
0x32b7fff2ed47fffd,
0x07e83a49a2e99d69,
0xeca8f3318332bb7a,
0xef148d1ea0f4c069,
0x040ab3263eff0206,
},
B: fp{},
})
// Otherwise, the correct solution is (1 + alpha)^((p - 1) // 2) * x0
t.SetOne()
t.Add(&t, &alpha)
t.pow(&t, &[Limbs]uint64{
0xdcff7fffffffd555,
0x0f55ffff58a9ffff,
0xb39869507b587b12,
0xb23ba5c279c2895f,
0x258dd3db21a5d66b,
0x0d0088f51cbff34d,
})
t.Mul(&t, &x0)
// if a = 0, then its zero
res.CMove(&res2, &res, e1)
// if alpha = -1, its not (1 + alpha)^((p - 1) // 2) * x0
// but au
res.CMove(&t, &res, e2)
// is the result^2 = a
t.Square(&res)
e3 := t.Equal(a)
f.CMove(f, &res, e3)
return f, e3
}
// Invert computes the multiplicative inverse of this field
// element, returning the original value of fp2
// in the case that this element is zero.
func (f *fp2) Invert(arg *fp2) (*fp2, int) {
// We wish to find the multiplicative inverse of a nonzero
// element a + bu in fp2. We leverage an identity
//
// (a + bu)(a - bu) = a^2 + b^2
//
// which holds because u^2 = -1. This can be rewritten as
//
// (a + bu)(a - bu)/(a^2 + b^2) = 1
//
// because a^2 + b^2 = 0 has no nonzero solutions for (a, b).
// This gives that (a - bu)/(a^2 + b^2) is the inverse
// of (a + bu). Importantly, this can be computing using
// only a single inversion in fp.
var a, b, t fp
a.Square(&arg.A)
b.Square(&arg.B)
a.Add(&a, &b)
_, wasInverted := t.Invert(&a)
// a * t
a.Mul(&arg.A, &t)
// b * -t
b.Neg(&t)
b.Mul(&b, &arg.B)
f.A.CMove(&f.A, &a, wasInverted)
f.B.CMove(&f.B, &b, wasInverted)
return f, wasInverted
}
// CMove performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f *fp2) CMove(arg1, arg2 *fp2, choice int) *fp2 {
f.A.CMove(&arg1.A, &arg2.A, choice)
f.B.CMove(&arg1.B, &arg2.B, choice)
return f
}
// CNeg conditionally negates a if choice == 1
func (f *fp2) CNeg(a *fp2, choice int) *fp2 {
var t fp2
t.Neg(a)
return f.CMove(f, &t, choice)
}
func (f *fp2) pow(base *fp2, exp *[Limbs]uint64) *fp2 {
res := (&fp2{}).SetOne()
tmp := (&fp2{}).SetZero()
for i := len(exp) - 1; i >= 0; i-- {
for j := 63; j >= 0; j-- {
res.Square(res)
tmp.Mul(res, base)
res.CMove(res, tmp, int(exp[i]>>j)&1)
}
}
return f.Set(res)
}
+424
View File
@@ -0,0 +1,424 @@
package bls12381
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFp2Square(t *testing.T) {
a := fp2{
A: fp{
0xc9a2183163ee70d4,
0xbc3770a7196b5c91,
0xa247f8c1304c5f44,
0xb01fc2a3726c80b5,
0xe1d293e5bbd919c9,
0x04b78e80020ef2ca,
},
B: fp{
0x952ea4460462618f,
0x238d5eddf025c62f,
0xf6c94b012ea92e72,
0x03ce24eac1c93808,
0x055950f945da483c,
0x010a768d0df4eabc,
},
}
b := fp2{
A: fp{
0xa1e09175a4d2c1fe,
0x8b33acfc204eff12,
0xe24415a11b456e42,
0x61d996b1b6ee1936,
0x1164dbe8667c853c,
0x0788557acc7d9c79,
},
B: fp{
0xda6a87cc6f48fa36,
0x0fc7b488277c1903,
0x9445ac4adc448187,
0x02616d5bc9099209,
0xdbed46772db58d48,
0x11b94d5076c7b7b1,
},
}
require.Equal(t, &b, a.Square(&a))
}
func TestFp2Mul(t *testing.T) {
a := fp2{
A: fp{
0xc9a2183163ee70d4,
0xbc3770a7196b5c91,
0xa247f8c1304c5f44,
0xb01fc2a3726c80b5,
0xe1d293e5bbd919c9,
0x04b78e80020ef2ca,
},
B: fp{
0x952ea4460462618f,
0x238d5eddf025c62f,
0xf6c94b012ea92e72,
0x03ce24eac1c93808,
0x055950f945da483c,
0x010a768d0df4eabc,
},
}
b := fp2{
A: fp{
0xa1e09175a4d2c1fe,
0x8b33acfc204eff12,
0xe24415a11b456e42,
0x61d996b1b6ee1936,
0x1164dbe8667c853c,
0x0788557acc7d9c79,
},
B: fp{
0xda6a87cc6f48fa36,
0x0fc7b488277c1903,
0x9445ac4adc448187,
0x02616d5bc9099209,
0xdbed46772db58d48,
0x11b94d5076c7b7b1,
},
}
c := fp2{
A: fp{
0xf597483e27b4e0f7,
0x610fbadf811dae5f,
0x8432af917714327a,
0x6a9a9603cf88f09e,
0xf05a7bf8bad0eb01,
0x09549131c003ffae,
},
B: fp{
0x963b02d0f93d37cd,
0xc95ce1cdb30a73d4,
0x308725fa3126f9b8,
0x56da3c167fab0d50,
0x6b5086b5f4b6d6af,
0x09c39f062f18e9f2,
},
}
require.Equal(t, 1, c.Equal(new(fp2).Mul(&a, &b)))
}
func TestFp2Add(t *testing.T) {
a := fp2{
A: fp{
0xc9a2183163ee70d4,
0xbc3770a7196b5c91,
0xa247f8c1304c5f44,
0xb01fc2a3726c80b5,
0xe1d293e5bbd919c9,
0x04b78e80020ef2ca,
},
B: fp{
0x952ea4460462618f,
0x238d5eddf025c62f,
0xf6c94b012ea92e72,
0x03ce24eac1c93808,
0x055950f945da483c,
0x010a768d0df4eabc,
},
}
b := fp2{
A: fp{
0xa1e09175a4d2c1fe,
0x8b33acfc204eff12,
0xe24415a11b456e42,
0x61d996b1b6ee1936,
0x1164dbe8667c853c,
0x0788557acc7d9c79,
},
B: fp{
0xda6a87cc6f48fa36,
0x0fc7b488277c1903,
0x9445ac4adc448187,
0x02616d5bc9099209,
0xdbed46772db58d48,
0x11b94d5076c7b7b1,
},
}
c := fp2{
A: fp{
0x6b82a9a708c132d2,
0x476b1da339ba5ba4,
0x848c0e624b91cd87,
0x11f95955295a99ec,
0xf3376fce22559f06,
0x0c3fe3face8c8f43,
},
B: fp{
0x6f992c1273ab5bc5,
0x3355136617a1df33,
0x8b0ef74c0aedaff9,
0x062f92468ad2ca12,
0xe1469770738fd584,
0x12c3c3dd84bca26d,
},
}
require.Equal(t, 1, c.Equal(new(fp2).Add(&a, &b)))
}
func TestFp2Sub(t *testing.T) {
a := fp2{
A: fp{
0xc9a2183163ee70d4,
0xbc3770a7196b5c91,
0xa247f8c1304c5f44,
0xb01fc2a3726c80b5,
0xe1d293e5bbd919c9,
0x04b78e80020ef2ca,
},
B: fp{
0x952ea4460462618f,
0x238d5eddf025c62f,
0xf6c94b012ea92e72,
0x03ce24eac1c93808,
0x055950f945da483c,
0x010a768d0df4eabc,
},
}
b := fp2{
A: fp{
0xa1e09175a4d2c1fe,
0x8b33acfc204eff12,
0xe24415a11b456e42,
0x61d996b1b6ee1936,
0x1164dbe8667c853c,
0x0788557acc7d9c79,
},
B: fp{
0xda6a87cc6f48fa36,
0x0fc7b488277c1903,
0x9445ac4adc448187,
0x02616d5bc9099209,
0xdbed46772db58d48,
0x11b94d5076c7b7b1,
},
}
c := fp2{
A: fp{
0xe1c086bbbf1b5981,
0x4fafc3a9aa705d7e,
0x2734b5c10bb7e726,
0xb2bd7776af037a3e,
0x1b895fb398a84164,
0x17304aef6f113cec,
},
B: fp{
0x74c31c7995191204,
0x3271aa5479fdad2b,
0xc9b471574915a30f,
0x65e40313ec44b8be,
0x7487b2385b7067cb,
0x09523b26d0ad19a4,
},
}
require.Equal(t, 1, c.Equal(new(fp2).Sub(&a, &b)))
}
func TestFp2Neg(t *testing.T) {
a := fp2{
A: fp{
0xc9a2183163ee70d4,
0xbc3770a7196b5c91,
0xa247f8c1304c5f44,
0xb01fc2a3726c80b5,
0xe1d293e5bbd919c9,
0x04b78e80020ef2ca,
},
B: fp{
0x952ea4460462618f,
0x238d5eddf025c62f,
0xf6c94b012ea92e72,
0x03ce24eac1c93808,
0x055950f945da483c,
0x010a768d0df4eabc,
},
}
b := fp2{
A: fp{
0xf05ce7ce9c1139d7,
0x62748f5797e8a36d,
0xc4e8d9dfc66496df,
0xb45788e181189209,
0x694913d08772930d,
0x1549836a3770f3cf,
},
B: fp{
0x24d05bb9fb9d491c,
0xfb1ea120c12e39d0,
0x7067879fc807c7b1,
0x60a9269a31bbdab6,
0x45c256bcfd71649b,
0x18f69b5d2b8afbde,
},
}
require.Equal(t, 1, b.Equal(new(fp2).Neg(&a)))
}
func TestFp2Sqrt(t *testing.T) {
a := fp2{
A: fp{
0x2beed14627d7f9e9,
0xb6614e06660e5dce,
0x06c4cc7c2f91d42c,
0x996d78474b7a63cc,
0xebaebc4c820d574e,
0x18865e12d93fd845,
},
B: fp{
0x7d828664baf4f566,
0xd17e663996ec7339,
0x679ead55cb4078d0,
0xfe3b2260e001ec28,
0x305993d043d91b68,
0x0626f03c0489b72d,
},
}
asq, wasSquare := (&fp2{}).Sqrt(&a)
require.Equal(t, 1, wasSquare)
require.Equal(t, 1, a.Equal(asq.Square(asq)))
b := fp2{
A: fp{
0x6631000000105545,
0x211400400eec000d,
0x3fa7af30c820e316,
0xc52a8b8d6387695d,
0x9fb4e61d1e83eac5,
0x005cb922afe84dc7,
},
B: fp{},
}
bsq, wasSquare := (&fp2{}).Sqrt(&b)
require.Equal(t, 1, wasSquare)
require.Equal(t, 1, b.Equal(bsq.Square(bsq)))
c := fp2{
A: fp{
0x44f600000051ffae,
0x86b8014199480043,
0xd7159952f1f3794a,
0x755d6e3dfe1ffc12,
0xd36cd6db5547e905,
0x02f8c8ecbf1867bb,
},
B: fp{},
}
csq, wasSquare := (&fp2{}).Sqrt(&c)
require.Equal(t, 1, wasSquare)
require.Equal(t, 1, c.Equal(csq.Square(csq)))
d := fp2{
A: fp{
0xc5fa1bc8fd00d7f6,
0x3830ca454606003b,
0x2b287f1104b102da,
0xa7fb30f28230f23e,
0x339cdb9ee953dbf0,
0x0d78ec51d989fc57,
},
B: fp{
0x27ec4898cf87f613,
0x9de1394e1abb05a5,
0x0947f85dc170fc14,
0x586fbc696b6114b7,
0x2b3475a4077d7169,
0x13e1c895cc4b6c22,
},
}
_, wasSquare = (&fp2{}).Sqrt(&d)
require.Equal(t, 0, wasSquare)
_, wasSquare = (&fp2{}).Sqrt(&fp2{})
require.Equal(t, 1, wasSquare)
}
func TestFp2Invert(t *testing.T) {
a := fp2{
A: fp{
0x1128ecad67549455,
0x9e7a1cff3a4ea1a8,
0xeb208d51e08bcf27,
0xe98ad40811f5fc2b,
0x736c3a59232d511d,
0x10acd42d29cfcbb6,
},
B: fp{
0xd328e37cc2f58d41,
0x948df0858a605869,
0x6032f9d56f93a573,
0x2be483ef3fffdc87,
0x30ef61f88f483c2a,
0x1333f55a35725be0,
},
}
b := fp2{
A: fp{
0x0581a1333d4f48a6,
0x58242f6ef0748500,
0x0292c955349e6da5,
0xba37721ddd95fcd0,
0x70d167903aa5dfc5,
0x11895e118b58a9d5,
},
B: fp{
0x0eda09d2d7a85d17,
0x8808e137a7d1a2cf,
0x43ae2625c1ff21db,
0xf85ac9fdf7a74c64,
0x8fccdda5b8da9738,
0x08e84f0cb32cd17d,
},
}
ainv, wasInverted := (&fp2{}).Invert(&a)
require.Equal(t, 1, wasInverted)
require.Equal(t, 1, b.Equal(ainv))
_, wasInverted = (&fp2{}).Invert(&fp2{})
require.Equal(t, 0, wasInverted)
}
func TestFp2LexicographicallyLargest(t *testing.T) {
require.Equal(t, 0, new(fp2).SetZero().LexicographicallyLargest())
require.Equal(t, 0, new(fp2).SetOne().LexicographicallyLargest())
a := fp2{
A: fp{
0x1128_ecad_6754_9455,
0x9e7a_1cff_3a4e_a1a8,
0xeb20_8d51_e08b_cf27,
0xe98a_d408_11f5_fc2b,
0x736c_3a59_232d_511d,
0x10ac_d42d_29cf_cbb6,
},
B: fp{
0xd328_e37c_c2f5_8d41,
0x948d_f085_8a60_5869,
0x6032_f9d5_6f93_a573,
0x2be4_83ef_3fff_dc87,
0x30ef_61f8_8f48_3c2a,
0x1333_f55a_3572_5be0,
},
}
require.Equal(t, 1, a.LexicographicallyLargest())
aNeg := new(fp2).Neg(&a)
require.Equal(t, 0, aNeg.LexicographicallyLargest())
a.B.SetZero()
require.Equal(t, 0, a.LexicographicallyLargest())
aNeg.B.SetZero()
require.Equal(t, 1, aNeg.LexicographicallyLargest())
}
+340
View File
@@ -0,0 +1,340 @@
package bls12381
import "io"
// fp6 represents an element
// a + b v + c v^2 of fp^6 = fp^2 / v^3 - u - 1.
type fp6 struct {
A, B, C fp2
}
// Set fp6 = a
func (f *fp6) Set(a *fp6) *fp6 {
f.A.Set(&a.A)
f.B.Set(&a.B)
f.C.Set(&a.C)
return f
}
// SetFp creates an element from a lower field
func (f *fp6) SetFp(a *fp) *fp6 {
f.A.SetFp(a)
f.B.SetZero()
f.C.SetZero()
return f
}
// SetFp2 creates an element from a lower field
func (f *fp6) SetFp2(a *fp2) *fp6 {
f.A.Set(a)
f.B.SetZero()
f.C.SetZero()
return f
}
// SetZero fp6 to zero
func (f *fp6) SetZero() *fp6 {
f.A.SetZero()
f.B.SetZero()
f.C.SetZero()
return f
}
// SetOne fp6 to multiplicative identity element
func (f *fp6) SetOne() *fp6 {
f.A.SetOne()
f.B.SetZero()
f.C.SetZero()
return f
}
// Random generates a random field element
func (f *fp6) Random(reader io.Reader) (*fp6, error) {
a, err := new(fp2).Random(reader)
if err != nil {
return nil, err
}
b, err := new(fp2).Random(reader)
if err != nil {
return nil, err
}
c, err := new(fp2).Random(reader)
if err != nil {
return nil, err
}
f.A.Set(a)
f.B.Set(b)
f.C.Set(c)
return f, nil
}
// Add computes arg1+arg2
func (f *fp6) Add(arg1, arg2 *fp6) *fp6 {
f.A.Add(&arg1.A, &arg2.A)
f.B.Add(&arg1.B, &arg2.B)
f.C.Add(&arg1.C, &arg2.C)
return f
}
// Double computes arg1+arg1
func (f *fp6) Double(arg *fp6) *fp6 {
return f.Add(arg, arg)
}
// Sub computes arg1-arg2
func (f *fp6) Sub(arg1, arg2 *fp6) *fp6 {
f.A.Sub(&arg1.A, &arg2.A)
f.B.Sub(&arg1.B, &arg2.B)
f.C.Sub(&arg1.C, &arg2.C)
return f
}
// Mul computes arg1*arg2
func (f *fp6) Mul(arg1, arg2 *fp6) *fp6 {
var aa, bb, cc, s, t1, t2, t3 fp2
aa.Mul(&arg1.A, &arg2.A)
bb.Mul(&arg1.B, &arg2.B)
cc.Mul(&arg1.C, &arg2.C)
t1.Add(&arg2.B, &arg2.C)
s.Add(&arg1.B, &arg1.C)
t1.Mul(&t1, &s)
t1.Sub(&t1, &bb)
t1.Sub(&t1, &cc)
t1.MulByNonResidue(&t1)
t1.Add(&t1, &aa)
t3.Add(&arg2.A, &arg2.C)
s.Add(&arg1.A, &arg1.C)
t3.Mul(&t3, &s)
t3.Sub(&t3, &aa)
t3.Add(&t3, &bb)
t3.Sub(&t3, &cc)
t2.Add(&arg2.A, &arg2.B)
s.Add(&arg1.A, &arg1.B)
t2.Mul(&t2, &s)
t2.Sub(&t2, &aa)
t2.Sub(&t2, &bb)
cc.MulByNonResidue(&cc)
t2.Add(&t2, &cc)
f.A.Set(&t1)
f.B.Set(&t2)
f.C.Set(&t3)
return f
}
// MulByB scales this field by a scalar in the B coefficient
func (f *fp6) MulByB(arg *fp6, b *fp2) *fp6 {
var bB, t1, t2 fp2
bB.Mul(&arg.B, b)
// (b + c) * arg2 - bB
t1.Add(&arg.B, &arg.C)
t1.Mul(&t1, b)
t1.Sub(&t1, &bB)
t1.MulByNonResidue(&t1)
t2.Add(&arg.A, &arg.B)
t2.Mul(&t2, b)
t2.Sub(&t2, &bB)
f.A.Set(&t1)
f.B.Set(&t2)
f.C.Set(&bB)
return f
}
// MulByAB scales this field by scalars in the A and B coefficients
func (f *fp6) MulByAB(arg *fp6, a, b *fp2) *fp6 {
var aA, bB, t1, t2, t3 fp2
aA.Mul(&arg.A, a)
bB.Mul(&arg.B, b)
t1.Add(&arg.B, &arg.C)
t1.Mul(&t1, b)
t1.Sub(&t1, &bB)
t1.MulByNonResidue(&t1)
t1.Add(&t1, &aA)
t2.Add(a, b)
t3.Add(&arg.A, &arg.B)
t2.Mul(&t2, &t3)
t2.Sub(&t2, &aA)
t2.Sub(&t2, &bB)
t3.Add(&arg.A, &arg.C)
t3.Mul(&t3, a)
t3.Sub(&t3, &aA)
t3.Add(&t3, &bB)
f.A.Set(&t1)
f.B.Set(&t2)
f.C.Set(&t3)
return f
}
// MulByNonResidue multiplies by quadratic nonresidue v.
func (f *fp6) MulByNonResidue(arg *fp6) *fp6 {
// Given a + bv + cv^2, this produces
// av + bv^2 + cv^3
// but because v^3 = u + 1, we have
// c(u + 1) + av + bv^2
var a, b, c fp2
a.MulByNonResidue(&arg.C)
b.Set(&arg.A)
c.Set(&arg.B)
f.A.Set(&a)
f.B.Set(&b)
f.C.Set(&c)
return f
}
// FrobeniusMap raises this element to p.
func (f *fp6) FrobeniusMap(arg *fp6) *fp6 {
var a, b, c fp2
pm1Div3 := fp2{
A: fp{},
B: fp{
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x03f97d6e83d050d2,
0x18f0206554638741,
},
}
p2m2Div3 := fp2{
A: fp{
0x890dc9e4867545c3,
0x2af322533285a5d5,
0x50880866309b7e2c,
0xa20d1b8c7e881024,
0x14e4f04fe2db9068,
0x14e56d3f1564853a,
},
B: fp{},
}
a.FrobeniusMap(&arg.A)
b.FrobeniusMap(&arg.B)
c.FrobeniusMap(&arg.C)
// b = b * (u + 1)^((p - 1) / 3)
b.Mul(&b, &pm1Div3)
// c = c * (u + 1)^((2p - 2) / 3)
c.Mul(&c, &p2m2Div3)
f.A.Set(&a)
f.B.Set(&b)
f.C.Set(&c)
return f
}
// Square computes fp6^2
func (f *fp6) Square(arg *fp6) *fp6 {
var s0, s1, s2, s3, s4, ab, bc fp2
s0.Square(&arg.A)
ab.Mul(&arg.A, &arg.B)
s1.Double(&ab)
s2.Sub(&arg.A, &arg.B)
s2.Add(&s2, &arg.C)
s2.Square(&s2)
bc.Mul(&arg.B, &arg.C)
s3.Double(&bc)
s4.Square(&arg.C)
f.A.MulByNonResidue(&s3)
f.A.Add(&f.A, &s0)
f.B.MulByNonResidue(&s4)
f.B.Add(&f.B, &s1)
// s1 + s2 + s3 - s0 - s4
f.C.Add(&s1, &s2)
f.C.Add(&f.C, &s3)
f.C.Sub(&f.C, &s0)
f.C.Sub(&f.C, &s4)
return f
}
// Invert computes this element's field inversion
func (f *fp6) Invert(arg *fp6) (*fp6, int) {
var a, b, c, s, t fp2
// a' = a^2 - (b * c).mul_by_nonresidue()
a.Mul(&arg.B, &arg.C)
a.MulByNonResidue(&a)
t.Square(&arg.A)
a.Sub(&t, &a)
// b' = (c^2).mul_by_nonresidue() - (a * b)
b.Square(&arg.C)
b.MulByNonResidue(&b)
t.Mul(&arg.A, &arg.B)
b.Sub(&b, &t)
// c' = b^2 - (a * c)
c.Square(&arg.B)
t.Mul(&arg.A, &arg.C)
c.Sub(&c, &t)
// t = ((b * c') + (c * b')).mul_by_nonresidue() + (a * a')
s.Mul(&arg.B, &c)
t.Mul(&arg.C, &b)
s.Add(&s, &t)
s.MulByNonResidue(&s)
t.Mul(&arg.A, &a)
s.Add(&s, &t)
_, wasInverted := t.Invert(&s)
// newA = a' * t^-1
s.Mul(&a, &t)
f.A.CMove(&f.A, &s, wasInverted)
// newB = b' * t^-1
s.Mul(&b, &t)
f.B.CMove(&f.B, &s, wasInverted)
// newC = c' * t^-1
s.Mul(&c, &t)
f.C.CMove(&f.C, &s, wasInverted)
return f, wasInverted
}
// Neg computes the field negation
func (f *fp6) Neg(arg *fp6) *fp6 {
f.A.Neg(&arg.A)
f.B.Neg(&arg.B)
f.C.Neg(&arg.C)
return f
}
// IsZero returns 1 if fp6 == 0, 0 otherwise
func (f *fp6) IsZero() int {
return f.A.IsZero() & f.B.IsZero() & f.C.IsZero()
}
// IsOne returns 1 if fp6 == 1, 0 otherwise
func (f *fp6) IsOne() int {
return f.A.IsOne() & f.B.IsZero() & f.B.IsZero()
}
// Equal returns 1 if fp6 == rhs, 0 otherwise
func (f *fp6) Equal(rhs *fp6) int {
return f.A.Equal(&rhs.A) & f.B.Equal(&rhs.B) & f.C.Equal(&rhs.C)
}
// CMove performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f *fp6) CMove(arg1, arg2 *fp6, choice int) *fp6 {
f.A.CMove(&arg1.A, &arg2.A, choice)
f.B.CMove(&arg1.B, &arg2.B, choice)
f.C.CMove(&arg1.C, &arg2.C, choice)
return f
}
+217
View File
@@ -0,0 +1,217 @@
package bls12381
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFp6Arithmetic(t *testing.T) {
a := fp6{
A: fp2{
A: fp{
0x47f9cb98b1b82d58,
0x5fe911eba3aa1d9d,
0x96bf1b5f4dd81db3,
0x8100d27cc9259f5b,
0xafa20b9674640eab,
0x09bbcea7d8d9497d,
},
B: fp{
0x0303cb98b1662daa,
0xd93110aa0a621d5a,
0xbfa9820c5be4a468,
0x0ba3643ecb05a348,
0xdc3534bb1f1c25a6,
0x06c305bb19c0e1c1,
},
},
B: fp2{
A: fp{
0x46f9cb98b162d858,
0x0be9109cf7aa1d57,
0xc791bc55fece41d2,
0xf84c57704e385ec2,
0xcb49c1d9c010e60f,
0x0acdb8e158bfe3c8,
},
B: fp{
0x8aefcb98b15f8306,
0x3ea1108fe4f21d54,
0xcf79f69fa1b7df3b,
0xe4f54aa1d16b1a3c,
0xba5e4ef86105a679,
0x0ed86c0797bee5cf,
},
},
C: fp2{
A: fp{
0xcee5cb98b15c2db4,
0x71591082d23a1d51,
0xd76230e944a17ca4,
0xd19e3dd3549dd5b6,
0xa972dc1701fa66e3,
0x12e31f2dd6bde7d6,
},
B: fp{
0xad2acb98b1732d9d,
0x2cfd10dd06961d64,
0x07396b86c6ef24e8,
0xbd76e2fdb1bfc820,
0x6afea7f6de94d0d5,
0x10994b0c5744c040,
},
},
}
b := fp6{
A: fp2{
A: fp{
0xf120cb98b16fd84b,
0x5fb510cff3de1d61,
0x0f21a5d069d8c251,
0xaa1fd62f34f2839a,
0x5a1335157f89913f,
0x14a3fe329643c247,
},
B: fp{
0x3516cb98b16c82f9,
0x926d10c2e1261d5f,
0x1709e01a0cc25fba,
0x96c8c960b8253f14,
0x4927c234207e51a9,
0x18aeb158d542c44e,
},
},
B: fp2{
A: fp{
0xbf0dcb98b16982fc,
0xa67910b71d1a1d5c,
0xb7c147c2b8fb06ff,
0x1efa710d47d2e7ce,
0xed20a79c7e27653c,
0x02b85294dac1dfba,
},
B: fp{
0x9d52cb98b18082e5,
0x621d111151761d6f,
0xe79882603b48af43,
0x0ad31637a4f4da37,
0xaeac737c5ac1cf2e,
0x006e7e735b48b824,
},
},
C: fp2{
A: fp{
0xe148cb98b17d2d93,
0x94d511043ebe1d6c,
0xef80bca9de324cac,
0xf77c0969282795b1,
0x9dc1009afbb68f97,
0x047931999a47ba2b,
},
B: fp{
0x253ecb98b179d841,
0xc78d10f72c061d6a,
0xf768f6f3811bea15,
0xe424fc9aab5a512b,
0x8cd58db99cab5001,
0x0883e4bfd946bc32,
},
},
}
c := fp6{
A: fp2{
A: fp{
0x6934cb98b17682ef,
0xfa4510ea194e1d67,
0xff51313d2405877e,
0xd0cdefcc2e8d0ca5,
0x7bea1ad83da0106b,
0x0c8e97e61845be39,
},
B: fp{
0x4779cb98b18d82d8,
0xb5e911444daa1d7a,
0x2f286bdaa6532fc2,
0xbca694f68baeff0f,
0x3d75e6b81a3a7a5d,
0x0a44c3c498cc96a3,
},
},
B: fp2{
A: fp{
0x8b6fcb98b18a2d86,
0xe8a111373af21d77,
0x3710a624493ccd2b,
0xa94f88280ee1ba89,
0x2c8a73d6bb2f3ac7,
0x0e4f76ead7cb98aa,
},
B: fp{
0xcf65cb98b186d834,
0x1b59112a283a1d74,
0x3ef8e06dec266a95,
0x95f87b5992147603,
0x1b9f00f55c23fb31,
0x125a2a1116ca9ab1,
},
},
C: fp2{
A: fp{
0x135bcb98b18382e2,
0x4e11111d15821d72,
0x46e11ab78f1007fe,
0x82a16e8b1547317d,
0x0ab38e13fd18bb9b,
0x1664dd3755c99cb8,
},
B: fp{
0xce65cb98b1318334,
0xc7590fdb7c3a1d2e,
0x6fcb81649d1c8eb3,
0x0d44004d1727356a,
0x3746b738a7d0d296,
0x136c144a96b134fc,
},
},
}
d := new(fp6).Square(&a)
e := new(fp6).Mul(&a, &a)
require.Equal(t, 1, e.Equal(d))
d.Square(&b)
e.Mul(&b, &b)
require.Equal(t, 1, e.Equal(d))
d.Square(&c)
e.Mul(&c, &c)
require.Equal(t, 1, e.Equal(d))
// (a + b) * c^2
d.Add(&a, &b)
d.Mul(d, new(fp6).Square(&c))
e.Mul(&c, &c)
e.Mul(e, &a)
tt := new(fp6).Mul(&c, &c)
tt.Mul(tt, &b)
e.Add(e, tt)
require.Equal(t, 1, d.Equal(e))
_, wasInverted := d.Invert(&a)
require.Equal(t, 1, wasInverted)
_, wasInverted = e.Invert(&b)
require.Equal(t, 1, wasInverted)
tt.Mul(&a, &b)
_, wasInverted = tt.Invert(tt)
require.Equal(t, 1, wasInverted)
d.Mul(d, e)
require.Equal(t, 1, tt.Equal(d))
_, _ = d.Invert(&a)
e.SetOne()
require.Equal(t, 1, e.Equal(d.Mul(d, &a)))
}
+346
View File
@@ -0,0 +1,346 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls12381
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/internal"
)
func TestFpSetOne(t *testing.T) {
var fp fp
fp.SetOne()
require.NotNil(t, fp)
require.Equal(t, fp, r)
}
func TestFpSetUint64(t *testing.T) {
var act fp
act.SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act[0], uint64(0xf6ea9fde37db5e8c))
}
func TestFpAdd(t *testing.T) {
var lhs, rhs, exp, res fp
lhs.SetOne()
rhs.SetOne()
exp.SetUint64(2)
res.Add(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(&exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
res.Add(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, exp, res)
}
}
func TestFpSub(t *testing.T) {
var lhs, rhs, exp, res fp
lhs.SetOne()
rhs.SetOne()
exp.SetZero()
res.Sub(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(&exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
res.Sub(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, exp, res)
}
}
func TestFpMul(t *testing.T) {
var lhs, rhs, exp, res fp
lhs.SetOne()
rhs.SetOne()
exp.SetOne()
res.Mul(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(&exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
res.Mul(&lhs, &rhs)
require.NotNil(t, res)
require.Equal(t, exp, res)
}
}
func TestFpDouble(t *testing.T) {
var a, e, res fp
a.SetUint64(2)
e.SetUint64(4)
require.Equal(t, &e, res.Double(&a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a.SetUint64(uint64(tv))
e.SetUint64(ttv)
require.Equal(t, &e, res.Double(&a))
}
}
func TestFpSquare(t *testing.T) {
var a, e, res fp
a.SetUint64(4)
e.SetUint64(16)
require.Equal(t, 1, e.Equal(res.Square(&a)))
a.SetUint64(2854263694)
e.SetUint64(8146821234886525636)
require.Equal(t, 1, e.Equal(res.Square(&a)))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, 1, e.Equal(res.Square(&a)), "exp = %d, j = %d", exp, j)
}
}
func TestFpNeg(t *testing.T) {
var g, a, e fp
g.SetLimbs(&[Limbs]uint64{7, 0, 0, 0, 0, 0})
a.SetOne()
a.Neg(&a)
e.SetRaw(
&[Limbs]uint64{
0x43f5fffffffcaaae,
0x32b7fff2ed47fffd,
0x07e83a49a2e99d69,
0xeca8f3318332bb7a,
0xef148d1ea0f4c069,
0x040ab3263eff0206,
},
)
require.Equal(t, 1, e.Equal(&a))
a.Neg(&g)
e.SetRaw(
&[Limbs]uint64{
0x21baffffffe90017,
0x445bffa5cba3ffed,
0xd028c5627db257bc,
0x14275ad5a2de0d96,
0x3e7434202365960e,
0x0249d4217f792796,
},
)
require.Equal(t, e, a)
}
func TestFpExp(t *testing.T) {
var a, e, by fp
e.SetUint64(8)
a.SetUint64(2)
by.SetUint64(3)
require.Equal(t, &e, a.Exp(&a, &by))
}
func TestFpSqrt(t *testing.T) {
var t1, t2, t3 fp
t1.SetUint64(2)
t2.Neg(&t1)
t3.Square(&t1)
_, wasSquare := t3.Sqrt(&t3)
require.Equal(t, 1, wasSquare)
require.Equal(t, 1, t1.Equal(&t3)|t2.Equal(&t3))
t1.SetUint64(5)
_, wasSquare = t1.Sqrt(&t1)
require.Equal(t, 0, wasSquare)
}
func TestFpInvert(t *testing.T) {
var two, twoInv, a, lhs, rhs, rhsInv fp
twoInv.SetRaw(
&[Limbs]uint64{
0x1804000000015554,
0x855000053ab00001,
0x633cb57c253c276f,
0x6e22d1ec31ebb502,
0xd3916126f2d14ca2,
0x17fbb8571a006596,
},
)
two.SetUint64(2)
_, inverted := a.Invert(&two)
require.Equal(t, 1, inverted)
require.Equal(t, &a, &twoInv)
lhs.SetUint64(9)
rhs.SetUint64(3)
_, inverted = rhsInv.Invert(&rhs)
require.Equal(t, 1, inverted)
require.Equal(t, &rhs, lhs.Mul(&lhs, &rhsInv))
rhs.SetZero()
_, inverted = lhs.Invert(&rhs)
require.Equal(t, 0, inverted)
}
func TestFpCMove(t *testing.T) {
var t1, t2, tt fp
t1.SetUint64(5)
t2.SetUint64(10)
require.Equal(t, &t1, tt.CMove(&t1, &t2, 0))
require.Equal(t, &t2, tt.CMove(&t1, &t2, 1))
}
func TestFpBytes(t *testing.T) {
var t1, t2 fp
t1.SetUint64(99)
seq := t1.Bytes()
_, suc := t2.SetBytes(&seq)
require.Equal(t, 1, suc)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, suc = t2.SetBytes(&seq)
require.Equal(t, 1, suc)
require.Equal(t, t1, t2)
}
}
func TestFpBigInt(t *testing.T) {
var t1, t2, e fp
t1.SetBigInt(big.NewInt(9999))
t2.SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e.SetRaw(
&[Limbs]uint64{
0x922af810e5e35f31,
0x6bc75973ed382d59,
0xd4716c9d4d491d42,
0x69d98d1ebeeb3f6e,
0x7e425d7b46d4a82b,
0x12d04b0965870e92,
},
)
b := new(
big.Int,
).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Neg(&e)
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFpSetBytesWideBigInt(t *testing.T) {
var a fp
var tv2 [96]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, biModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a.SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
func TestFpToMontgomery(t *testing.T) {
var v fp
v.SetUint64(2)
require.Equal(
t,
fp{
0x321300000006554f,
0xb93c0018d6c40005,
0x57605e0db0ddbb51,
0x8b256521ed1f9bcb,
0x6cf28d7901622c03,
0x11ebab9dbb81e28c,
},
v,
)
}
func TestFpFromMontgomery(t *testing.T) {
var v fp
e := fp{2, 0, 0, 0, 0, 0}
v.SetUint64(2)
v.fromMontgomery(&v)
require.Equal(t, e, v)
}
func TestFpLexicographicallyLargest(t *testing.T) {
require.Equal(t, 0, new(fp).SetZero().LexicographicallyLargest())
require.Equal(t, 0, new(fp).SetOne().LexicographicallyLargest())
require.Equal(t, 0, (&fp{
0xa1fafffffffe5557,
0x995bfff976a3fffe,
0x03f41d24d174ceb4,
0xf6547998c1995dbd,
0x778a468f507a6034,
0x020559931f7f8103,
}).LexicographicallyLargest())
require.Equal(t, 1, (&fp{
0x1804000000015554,
0x855000053ab00001,
0x633cb57c253c276f,
0x6e22d1ec31ebb502,
0xd3916126f2d14ca2,
0x17fbb8571a006596,
}).LexicographicallyLargest())
require.Equal(t, 1, (&fp{
0x43f5fffffffcaaae,
0x32b7fff2ed47fffd,
0x07e83a49a2e99d69,
0xeca8f3318332bb7a,
0xef148d1ea0f4c069,
0x040ab3263eff0206,
}).LexicographicallyLargest())
}
+455
View File
@@ -0,0 +1,455 @@
package bls12381
import (
"encoding/binary"
"math/big"
"sync"
"github.com/sonr-io/sonr/crypto/core/curves/native"
)
type Fq [native.FieldLimbs]uint64
var (
bls12381FqInitonce sync.Once
bls12381FqParams native.FieldParams
)
// 2^S * t = MODULUS - 1 with t odd
const fqS = 32
// qInv = -(q^{-1} mod 2^64) mod 2^64
const qInv = 0xfffffffeffffffff
// fqGenerator = 7 (multiplicative fqGenerator of r-1 order, that is also quadratic nonresidue)
var fqGenerator = [native.FieldLimbs]uint64{
0x0000000efffffff1,
0x17e363d300189c0f,
0xff9c57876f8457b0,
0x351332208fc5a8c4,
}
// fqModulus
var fqModulus = [native.FieldLimbs]uint64{
0xffffffff00000001,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48,
}
func Bls12381FqNew() *native.Field {
return &native.Field{
Value: [native.FieldLimbs]uint64{},
Params: getBls12381FqParams(),
Arithmetic: bls12381FqArithmetic{},
}
}
func bls12381FqParamsInit() {
bls12381FqParams = native.FieldParams{
R: [native.FieldLimbs]uint64{
0x00000001fffffffe,
0x5884b7fa00034802,
0x998c4fefecbc4ff5,
0x1824b159acc5056f,
},
R2: [native.FieldLimbs]uint64{
0xc999e990f3f29c6d,
0x2b6cedcb87925c23,
0x05d314967254398f,
0x0748d9d99f59ff11,
},
R3: [native.FieldLimbs]uint64{
0xc62c1807439b73af,
0x1b3e0d188cf06990,
0x73d13c71c7b5f418,
0x6e2a5bb9c8db33e9,
},
Modulus: [native.FieldLimbs]uint64{
0xffffffff00000001,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48,
},
BiModulus: new(big.Int).SetBytes([]byte{
0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, 0x09, 0xa1, 0xd8, 0x05, 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01,
}),
}
}
func getBls12381FqParams() *native.FieldParams {
bls12381FqInitonce.Do(bls12381FqParamsInit)
return &bls12381FqParams
}
// bls12381FqArithmetic is a struct with all the methods needed for working
// in mod q
type bls12381FqArithmetic struct{}
// ToMontgomery converts this field to montgomery form
func (f bls12381FqArithmetic) ToMontgomery(out, arg *[native.FieldLimbs]uint64) {
// arg.R^0 * R^2 / R = arg.R
f.Mul(out, arg, &getBls12381FqParams().R2)
}
// FromMontgomery converts this field from montgomery form
func (f bls12381FqArithmetic) FromMontgomery(out, arg *[native.FieldLimbs]uint64) {
// Mul by 1 is division by 2^256 mod q
// f.Mul(out, arg, &[native.FieldLimbs]uint64{1, 0, 0, 0})
f.montReduce(out, &[native.FieldLimbs * 2]uint64{arg[0], arg[1], arg[2], arg[3], 0, 0, 0, 0})
}
// Neg performs modular negation
func (f bls12381FqArithmetic) Neg(out, arg *[native.FieldLimbs]uint64) {
// Subtract `arg` from `fqModulus`. Ignore final borrow
// since it can't underflow.
var t [native.FieldLimbs]uint64
var borrow uint64
t[0], borrow = sbb(fqModulus[0], arg[0], 0)
t[1], borrow = sbb(fqModulus[1], arg[1], borrow)
t[2], borrow = sbb(fqModulus[2], arg[2], borrow)
t[3], _ = sbb(fqModulus[3], arg[3], borrow)
// t could be `fqModulus` if `arg`=0. Set mask=0 if self=0
// and 0xff..ff if `arg`!=0
mask := t[0] | t[1] | t[2] | t[3]
mask = -((mask | -mask) >> 63)
out[0] = t[0] & mask
out[1] = t[1] & mask
out[2] = t[2] & mask
out[3] = t[3] & mask
}
// Square performs modular square
func (f bls12381FqArithmetic) Square(out, arg *[native.FieldLimbs]uint64) {
var r [2 * native.FieldLimbs]uint64
var carry uint64
r[1], carry = mac(0, arg[0], arg[1], 0)
r[2], carry = mac(0, arg[0], arg[2], carry)
r[3], r[4] = mac(0, arg[0], arg[3], carry)
r[3], carry = mac(r[3], arg[1], arg[2], 0)
r[4], r[5] = mac(r[4], arg[1], arg[3], carry)
r[5], r[6] = mac(r[5], arg[2], arg[3], 0)
r[7] = r[6] >> 63
r[6] = (r[6] << 1) | r[5]>>63
r[5] = (r[5] << 1) | r[4]>>63
r[4] = (r[4] << 1) | r[3]>>63
r[3] = (r[3] << 1) | r[2]>>63
r[2] = (r[2] << 1) | r[1]>>63
r[1] = r[1] << 1
r[0], carry = mac(0, arg[0], arg[0], 0)
r[1], carry = adc(0, r[1], carry)
r[2], carry = mac(r[2], arg[1], arg[1], carry)
r[3], carry = adc(0, r[3], carry)
r[4], carry = mac(r[4], arg[2], arg[2], carry)
r[5], carry = adc(0, r[5], carry)
r[6], carry = mac(r[6], arg[3], arg[3], carry)
r[7], _ = adc(0, r[7], carry)
f.montReduce(out, &r)
}
// Mul performs modular multiplication
func (f bls12381FqArithmetic) Mul(out, arg1, arg2 *[native.FieldLimbs]uint64) {
// Schoolbook multiplication
var r [2 * native.FieldLimbs]uint64
var carry uint64
r[0], carry = mac(0, arg1[0], arg2[0], 0)
r[1], carry = mac(0, arg1[0], arg2[1], carry)
r[2], carry = mac(0, arg1[0], arg2[2], carry)
r[3], r[4] = mac(0, arg1[0], arg2[3], carry)
r[1], carry = mac(r[1], arg1[1], arg2[0], 0)
r[2], carry = mac(r[2], arg1[1], arg2[1], carry)
r[3], carry = mac(r[3], arg1[1], arg2[2], carry)
r[4], r[5] = mac(r[4], arg1[1], arg2[3], carry)
r[2], carry = mac(r[2], arg1[2], arg2[0], 0)
r[3], carry = mac(r[3], arg1[2], arg2[1], carry)
r[4], carry = mac(r[4], arg1[2], arg2[2], carry)
r[5], r[6] = mac(r[5], arg1[2], arg2[3], carry)
r[3], carry = mac(r[3], arg1[3], arg2[0], 0)
r[4], carry = mac(r[4], arg1[3], arg2[1], carry)
r[5], carry = mac(r[5], arg1[3], arg2[2], carry)
r[6], r[7] = mac(r[6], arg1[3], arg2[3], carry)
f.montReduce(out, &r)
}
// Add performs modular addition
func (f bls12381FqArithmetic) Add(out, arg1, arg2 *[native.FieldLimbs]uint64) {
var t [native.FieldLimbs]uint64
var carry uint64
t[0], carry = adc(arg1[0], arg2[0], 0)
t[1], carry = adc(arg1[1], arg2[1], carry)
t[2], carry = adc(arg1[2], arg2[2], carry)
t[3], _ = adc(arg1[3], arg2[3], carry)
// Subtract the fqModulus to ensure the value
// is smaller.
f.Sub(out, &t, &fqModulus)
}
// Sub performs modular subtraction
func (f bls12381FqArithmetic) Sub(out, arg1, arg2 *[native.FieldLimbs]uint64) {
d0, borrow := sbb(arg1[0], arg2[0], 0)
d1, borrow := sbb(arg1[1], arg2[1], borrow)
d2, borrow := sbb(arg1[2], arg2[2], borrow)
d3, borrow := sbb(arg1[3], arg2[3], borrow)
// If underflow occurred on the final limb, borrow 0xff...ff, otherwise
// borrow = 0x00...00. Conditionally mask to add the fqModulus
borrow = -borrow
d0, carry := adc(d0, fqModulus[0]&borrow, 0)
d1, carry = adc(d1, fqModulus[1]&borrow, carry)
d2, carry = adc(d2, fqModulus[2]&borrow, carry)
d3, _ = adc(d3, fqModulus[3]&borrow, carry)
out[0] = d0
out[1] = d1
out[2] = d2
out[3] = d3
}
// Sqrt performs modular square root
func (f bls12381FqArithmetic) Sqrt(wasSquare *int, out, arg *[native.FieldLimbs]uint64) {
// See sqrt_ts_ct at
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.4
// c1 := fqS
// c2 := (q - 1) / (2^c1)
c2 := [4]uint64{
0xfffe5bfeffffffff,
0x09a1d80553bda402,
0x299d7d483339d808,
0x0000000073eda753,
}
// c3 := (c2 - 1) / 2
c3 := [native.FieldLimbs]uint64{
0x7fff2dff7fffffff,
0x04d0ec02a9ded201,
0x94cebea4199cec04,
0x0000000039f6d3a9,
}
// c4 := fqGenerator
var c5 [native.FieldLimbs]uint64
native.Pow(&c5, &fqGenerator, &c2, getBls12381FqParams(), f)
// c5 := [native.FieldLimbs]uint64{0x1015708f7e368fe1, 0x31c6c5456ecc4511, 0x5281fe8998a19ea1, 0x0279089e10c63fe8}
var z, t, b, c, tv [native.FieldLimbs]uint64
native.Pow(&z, arg, &c3, getBls12381FqParams(), f)
f.Square(&t, &z)
f.Mul(&t, &t, arg)
f.Mul(&z, &z, arg)
copy(b[:], t[:])
copy(c[:], c5[:])
for i := fqS; i >= 2; i-- {
for j := 1; j <= i-2; j++ {
f.Square(&b, &b)
}
// if b == 1 flag = 0 else flag = 1
flag := -(&native.Field{
Value: b,
Params: getBls12381FqParams(),
Arithmetic: f,
}).IsOne() + 1
f.Mul(&tv, &z, &c)
f.Selectznz(&z, &z, &tv, flag)
f.Square(&c, &c)
f.Mul(&tv, &t, &c)
f.Selectznz(&t, &t, &tv, flag)
copy(b[:], t[:])
}
f.Square(&c, &z)
*wasSquare = (&native.Field{
Value: c,
Params: getBls12381FqParams(),
Arithmetic: f,
}).Equal(&native.Field{
Value: *arg,
Params: getBls12381FqParams(),
Arithmetic: f,
})
f.Selectznz(out, out, &z, *wasSquare)
}
// Invert performs modular inverse
func (f bls12381FqArithmetic) Invert(wasInverted *int, out, arg *[native.FieldLimbs]uint64) {
// Using an addition chain from
// https://github.com/kwantam/addchain
var t0, t1, t2, t3, t4, t5, t6, t7, t8 [native.FieldLimbs]uint64
var t9, t11, t12, t13, t14, t15, t16, t17 [native.FieldLimbs]uint64
f.Square(&t0, arg)
f.Mul(&t1, &t0, arg)
f.Square(&t16, &t0)
f.Square(&t6, &t16)
f.Mul(&t5, &t6, &t0)
f.Mul(&t0, &t6, &t16)
f.Mul(&t12, &t5, &t16)
f.Square(&t2, &t6)
f.Mul(&t7, &t5, &t6)
f.Mul(&t15, &t0, &t5)
f.Square(&t17, &t12)
f.Mul(&t1, &t1, &t17)
f.Mul(&t3, &t7, &t2)
f.Mul(&t8, &t1, &t17)
f.Mul(&t4, &t8, &t2)
f.Mul(&t9, &t8, &t7)
f.Mul(&t7, &t4, &t5)
f.Mul(&t11, &t4, &t17)
f.Mul(&t5, &t9, &t17)
f.Mul(&t14, &t7, &t15)
f.Mul(&t13, &t11, &t12)
f.Mul(&t12, &t11, &t17)
f.Mul(&t15, &t15, &t12)
f.Mul(&t16, &t16, &t15)
f.Mul(&t3, &t3, &t16)
f.Mul(&t17, &t17, &t3)
f.Mul(&t0, &t0, &t17)
f.Mul(&t6, &t6, &t0)
f.Mul(&t2, &t2, &t6)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t17)
native.Pow2k(&t0, &t0, 9, f)
f.Mul(&t0, &t0, &t16)
native.Pow2k(&t0, &t0, 9, f)
f.Mul(&t0, &t0, &t15)
native.Pow2k(&t0, &t0, 9, f)
f.Mul(&t0, &t0, &t15)
native.Pow2k(&t0, &t0, 7, f)
f.Mul(&t0, &t0, &t14)
native.Pow2k(&t0, &t0, 7, f)
f.Mul(&t0, &t0, &t13)
native.Pow2k(&t0, &t0, 10, f)
f.Mul(&t0, &t0, &t12)
native.Pow2k(&t0, &t0, 9, f)
f.Mul(&t0, &t0, &t11)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t8)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, arg)
native.Pow2k(&t0, &t0, 14, f)
f.Mul(&t0, &t0, &t9)
native.Pow2k(&t0, &t0, 10, f)
f.Mul(&t0, &t0, &t8)
native.Pow2k(&t0, &t0, 15, f)
f.Mul(&t0, &t0, &t7)
native.Pow2k(&t0, &t0, 10, f)
f.Mul(&t0, &t0, &t6)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t5)
native.Pow2k(&t0, &t0, 16, f)
f.Mul(&t0, &t0, &t3)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 7, f)
f.Mul(&t0, &t0, &t4)
native.Pow2k(&t0, &t0, 9, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t3)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t3)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 8, f)
f.Mul(&t0, &t0, &t2)
native.Pow2k(&t0, &t0, 5, f)
f.Mul(&t0, &t0, &t1)
native.Pow2k(&t0, &t0, 5, f)
f.Mul(&t0, &t0, &t1)
*wasInverted = (&native.Field{
Value: *arg,
Params: getBls12381FqParams(),
Arithmetic: f,
}).IsNonZero()
f.Selectznz(out, out, &t0, *wasInverted)
}
// FromBytes converts a little endian byte array into a field element
func (f bls12381FqArithmetic) FromBytes(
out *[native.FieldLimbs]uint64,
arg *[native.FieldBytes]byte,
) {
out[0] = binary.LittleEndian.Uint64(arg[:8])
out[1] = binary.LittleEndian.Uint64(arg[8:16])
out[2] = binary.LittleEndian.Uint64(arg[16:24])
out[3] = binary.LittleEndian.Uint64(arg[24:])
}
// ToBytes converts a field element to a little endian byte array
func (f bls12381FqArithmetic) ToBytes(
out *[native.FieldBytes]byte,
arg *[native.FieldLimbs]uint64,
) {
binary.LittleEndian.PutUint64(out[:8], arg[0])
binary.LittleEndian.PutUint64(out[8:16], arg[1])
binary.LittleEndian.PutUint64(out[16:24], arg[2])
binary.LittleEndian.PutUint64(out[24:], arg[3])
}
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f bls12381FqArithmetic) Selectznz(out, arg1, arg2 *[native.FieldLimbs]uint64, choice int) {
b := uint64(-choice)
out[0] = arg1[0] ^ ((arg1[0] ^ arg2[0]) & b)
out[1] = arg1[1] ^ ((arg1[1] ^ arg2[1]) & b)
out[2] = arg1[2] ^ ((arg1[2] ^ arg2[2]) & b)
out[3] = arg1[3] ^ ((arg1[3] ^ arg2[3]) & b)
}
func (f bls12381FqArithmetic) montReduce(
out *[native.FieldLimbs]uint64,
r *[2 * native.FieldLimbs]uint64,
) {
// Taken from Algorithm 14.32 in Handbook of Applied Cryptography
var r1, r2, r3, r4, r5, r6, carry, carry2, k uint64
var rr [native.FieldLimbs]uint64
k = r[0] * qInv
_, carry = mac(r[0], k, fqModulus[0], 0)
r1, carry = mac(r[1], k, fqModulus[1], carry)
r2, carry = mac(r[2], k, fqModulus[2], carry)
r3, carry = mac(r[3], k, fqModulus[3], carry)
r4, carry2 = adc(r[4], 0, carry)
k = r1 * qInv
_, carry = mac(r1, k, fqModulus[0], 0)
r2, carry = mac(r2, k, fqModulus[1], carry)
r3, carry = mac(r3, k, fqModulus[2], carry)
r4, carry = mac(r4, k, fqModulus[3], carry)
r5, carry2 = adc(r[5], carry2, carry)
k = r2 * qInv
_, carry = mac(r2, k, fqModulus[0], 0)
r3, carry = mac(r3, k, fqModulus[1], carry)
r4, carry = mac(r4, k, fqModulus[2], carry)
r5, carry = mac(r5, k, fqModulus[3], carry)
r6, carry2 = adc(r[6], carry2, carry)
k = r3 * qInv
_, carry = mac(r3, k, fqModulus[0], 0)
rr[0], carry = mac(r4, k, fqModulus[1], carry)
rr[1], carry = mac(r5, k, fqModulus[2], carry)
rr[2], carry = mac(r6, k, fqModulus[3], carry)
rr[3], _ = adc(r[7], carry2, carry)
f.Sub(out, &rr, &fqModulus)
}
+353
View File
@@ -0,0 +1,353 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package bls12381
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/internal"
)
func TestFqSetOne(t *testing.T) {
fq := Bls12381FqNew().SetOne()
require.NotNil(t, fq)
require.Equal(t, fq.Value, getBls12381FqParams().R)
}
func TestFqSetUint64(t *testing.T) {
act := Bls12381FqNew().SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act.Value[0], uint64(0xbc98da2820121c89))
}
func TestFqAdd(t *testing.T) {
lhs := Bls12381FqNew().SetOne()
rhs := Bls12381FqNew().SetOne()
exp := Bls12381FqNew().SetUint64(2)
res := Bls12381FqNew().Add(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := Bls12381FqNew().Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqSub(t *testing.T) {
lhs := Bls12381FqNew().SetOne()
rhs := Bls12381FqNew().SetOne()
exp := Bls12381FqNew().SetZero()
res := Bls12381FqNew().Sub(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := Bls12381FqNew().Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqMul(t *testing.T) {
lhs := Bls12381FqNew().SetOne()
rhs := Bls12381FqNew().SetOne()
exp := Bls12381FqNew().SetOne()
res := Bls12381FqNew().Mul(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := Bls12381FqNew().Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqDouble(t *testing.T) {
a := Bls12381FqNew().SetUint64(2)
e := Bls12381FqNew().SetUint64(4)
require.Equal(t, e, Bls12381FqNew().Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = Bls12381FqNew().SetUint64(uint64(tv))
e = Bls12381FqNew().SetUint64(ttv)
require.Equal(t, e, Bls12381FqNew().Double(a))
}
}
func TestFqSquare(t *testing.T) {
a := Bls12381FqNew().SetUint64(4)
e := Bls12381FqNew().SetUint64(16)
require.Equal(t, 1, e.Equal(a.Square(a)))
a.SetUint64(2854263694)
e.SetUint64(8146821234886525636)
require.Equal(t, 1, e.Equal(a.Square(a)))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, 1, e.Equal(a.Square(a)), "exp = %d, j = %d", exp, j)
}
}
func TestFqNeg(t *testing.T) {
g := Bls12381FqNew().SetRaw(&fqGenerator)
a := Bls12381FqNew().SetOne()
a.Neg(a)
e := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0xfffffffd00000003, 0xfb38ec08fffb13fc, 0x99ad88181ce5880f, 0x5bc8f5f97cd877d8})
require.Equal(t, 1, e.Equal(a))
a.Neg(g)
e = Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0xfffffff000000010, 0x3bda402fffe5bfef, 0x339d80809a1d8055, 0x3eda753299d7d483})
require.Equal(t, e, a)
}
func TestFqExp(t *testing.T) {
e := Bls12381FqNew().SetUint64(8)
a := Bls12381FqNew().SetUint64(2)
by := Bls12381FqNew().SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFqSqrt(t *testing.T) {
t1 := Bls12381FqNew().SetUint64(2)
t2 := Bls12381FqNew().Neg(t1)
t3 := Bls12381FqNew().Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.Equal(t, 1, t1.Equal(t3)|t2.Equal(t3))
t1.SetUint64(5)
_, wasSquare = Bls12381FqNew().Sqrt(t1)
require.False(t, wasSquare)
}
func TestFqInvert(t *testing.T) {
twoInv := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0xffffffff, 0xac425bfd0001a401, 0xccc627f7f65e27fa, 0xc1258acd66282b7})
two := Bls12381FqNew().SetUint64(2)
a, inverted := Bls12381FqNew().Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnity := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0xb9b58d8c5f0e466a, 0x5b1b4c801819d7ec, 0x0af53ae352a31e64, 0x5bf3adda19e9b27b})
rootOfUnityInv := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0x4256481adcf3219a, 0x45f37b7f96b6cad3, 0xf9c3f1d75f7a3b27, 0x2d2fc049658afd43})
a, inverted = Bls12381FqNew().Invert(rootOfUnity)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := Bls12381FqNew().SetUint64(9)
rhs := Bls12381FqNew().SetUint64(3)
rhsInv, inverted := Bls12381FqNew().Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, Bls12381FqNew().Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = Bls12381FqNew().Invert(rhs)
require.False(t, inverted)
}
func TestFqCMove(t *testing.T) {
t1 := Bls12381FqNew().SetUint64(5)
t2 := Bls12381FqNew().SetUint64(10)
require.Equal(t, t1, Bls12381FqNew().CMove(t1, t2, 0))
require.Equal(t, t2, Bls12381FqNew().CMove(t1, t2, 1))
}
func TestFqBytes(t *testing.T) {
t1 := Bls12381FqNew().SetUint64(99)
seq := t1.Bytes()
t2, err := Bls12381FqNew().SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFqCmp(t *testing.T) {
tests := []struct {
a *native.Field
b *native.Field
e int
}{
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{2731658267414164836, 14655288906067898431, 6537465423330262322, 8306191141697566219}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{6472764012681988529, 10848812988401906064, 2961825807536828898, 4282183981941645679}),
e: 1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{8023004109510539223, 4652004072850285717, 1877219145646046927, 383214385093921911}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{10099384440823804262, 16139476942229308465, 8636966320777393798, 5435928725024696785}),
e: -1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{3741840066202388211, 12165774400417314871, 16619312580230515379, 16195032234110087705}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{3905865991286066744, 543690822309071825, 17963103015950210055, 3745476720756119742}),
e: 1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{16660853697936147788, 7799793619412111108, 13515141085171033220, 2641079731236069032}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{17790588295388238399, 571847801379669440, 14537208974498222469, 12792570372087452754}),
e: -1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{3912839285384959186, 2701177075110484070, 6453856448115499033, 6475797457962597458}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{1282566391665688512, 13503640416992806563, 2962240104675990153, 3374904770947067689}),
e: 1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{5716631803409360103, 7859567470082614154, 12747956220853330146, 18434584096087315020}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{16317076441459028418, 12854146980376319601, 2258436689269031143, 9531877130792223752}),
e: 1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{17955191469941083403, 10350326247207200880, 17263512235150705075, 12700328451238078022}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{6767595547459644695, 7146403825494928147, 12269344038346710612, 9122477829383225603}),
e: 1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{17099388671847024438, 6426264987820696548, 10641143464957227405, 7709745403700754098}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{10799154372990268556, 17178492485719929374, 5705777922258988797, 8051037767683567782}),
e: -1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{4567139260680454325, 1629385880182139061, 16607020832317899145, 1261011562621553200}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{13487234491304534488, 17872642955936089265, 17651026784972590233, 9468934643333871559}),
e: -1,
},
{
a: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{18071070103467571798, 11787850505799426140, 10631355976141928593, 4867785203635092610}),
b: Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{12596443599426461624, 10176122686151524591, 17075755296887483439, 6726169532695070719}),
e: -1,
},
}
for _, test := range tests {
require.Equal(t, test.e, test.a.Cmp(test.b))
require.Equal(t, -test.e, test.b.Cmp(test.a))
require.Equal(t, 0, test.a.Cmp(test.a))
require.Equal(t, 0, test.b.Cmp(test.b))
}
}
func TestFqBigInt(t *testing.T) {
t1 := Bls12381FqNew().SetBigInt(big.NewInt(9999))
t2 := Bls12381FqNew().SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0x673053fc60e06500, 0x86e6d480b4f76ada, 0x7fc68f9fefa23291, 0x3fb17f49bdda126d})
b := new(
big.Int,
).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Neg(e)
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFqSetBytesWide(t *testing.T) {
e := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{0xc759fba87ff8c5a6, 0x9ef5194839e7df44, 0x21375d22b678bf0e, 0x38b105387033fd57})
a := Bls12381FqNew().SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
func TestFqSetBytesWideBigInt(t *testing.T) {
params := getBls12381FqParams()
var tv2 [64]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, params.BiModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a := Bls12381FqNew().SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
func TestFqToMontgomery(t *testing.T) {
v := Bls12381FqNew().SetUint64(2)
require.Equal(
t,
[native.FieldLimbs]uint64{
0x3fffffffc,
0xb1096ff400069004,
0x33189fdfd9789fea,
0x304962b3598a0adf,
},
v.Value,
)
}
func TestFqFromMontgomery(t *testing.T) {
e := [native.FieldLimbs]uint64{2, 0, 0, 0}
a := [native.FieldLimbs]uint64{0, 0, 0, 0}
v := Bls12381FqNew().SetUint64(2)
v.Arithmetic.FromMontgomery(&a, &v.Value)
require.Equal(t, e, a)
}
File diff suppressed because it is too large Load Diff
+431
View File
@@ -0,0 +1,431 @@
package bls12381
import (
crand "crypto/rand"
"encoding/hex"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves/native"
)
func TestG1IsOnCurve(t *testing.T) {
require.Equal(t, 1, new(G1).Identity().IsOnCurve())
require.Equal(t, 1, new(G1).Generator().IsOnCurve())
z := fp{
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
}
gen := new(G1).Generator()
test := G1{
x: *(gen.x.Mul(&gen.x, &z)),
y: *(gen.y.Mul(&gen.y, &z)),
z: z,
}
require.Equal(t, 1, test.IsOnCurve())
test.x = z
require.Equal(t, 0, test.IsOnCurve())
}
func TestG1Equality(t *testing.T) {
a := new(G1).Generator()
b := new(G1).Identity()
require.Equal(t, 1, a.Equal(a))
require.Equal(t, 1, b.Equal(b))
require.Equal(t, 0, a.Equal(b))
require.Equal(t, 0, b.Equal(a))
z := fp{
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
}
c := G1{}
c.x.Mul(&a.x, &z)
c.y.Mul(&a.y, &z)
c.z.Set(&z)
require.Equal(t, 1, c.IsOnCurve())
require.Equal(t, 1, a.Equal(&c))
require.Equal(t, 0, b.Equal(&c))
c.y.Neg(&c.y)
require.Equal(t, 1, c.IsOnCurve())
require.Equal(t, 0, a.Equal(&c))
c.y.Neg(&c.y)
c.x.Set(&z)
require.Equal(t, 0, c.IsOnCurve())
}
func TestG1Double(t *testing.T) {
t0 := new(G1).Identity()
t0.Double(t0)
require.Equal(t, 1, t0.IsIdentity())
require.Equal(t, 1, t0.IsOnCurve())
t0.Double(t0.Generator())
require.Equal(t, 0, t0.IsIdentity())
require.Equal(t, 1, t0.IsOnCurve())
e := G1{
x: fp{
0x53e978ce58a9ba3c,
0x3ea0583c4f3d65f9,
0x4d20bb47f0012960,
0xa54c664ae5b2b5d9,
0x26b552a39d7eb21f,
0x0008895d26e68785,
},
y: fp{
0x70110b3298293940,
0xda33c5393f1f6afc,
0xb86edfd16a5aa785,
0xaec6d1c9e7b1c895,
0x25cfc2b522d11720,
0x06361c83f8d09b15,
},
z: r,
}
require.Equal(t, 1, e.Equal(t0))
}
func TestG1Add(t *testing.T) {
g := new(G1).Generator()
a := new(G1).Identity()
b := new(G1).Identity()
c := new(G1).Add(a, b)
require.Equal(t, 1, c.IsIdentity())
require.Equal(t, 1, c.IsOnCurve())
b.Generator()
z := fp{
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
}
b.x.Mul(&b.x, &z)
b.y.Mul(&b.y, &z)
b.z.Set(&z)
c.Add(a, b)
require.Equal(t, 0, c.IsIdentity())
require.Equal(t, 1, g.Equal(c))
a.Generator()
a.Double(a)
a.Double(a)
b.Generator()
b.Double(b)
c.Add(a, b)
d := new(G1).Generator()
for i := 0; i < 5; i++ {
d.Add(d, g)
}
require.Equal(t, 0, c.IsIdentity())
require.Equal(t, 1, c.IsOnCurve())
require.Equal(t, 0, d.IsIdentity())
require.Equal(t, 1, d.IsOnCurve())
require.Equal(t, 1, c.Equal(d))
beta := fp{
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x03f97d6e83d050d2,
0x18f0206554638741,
}
beta.Square(&beta)
a.Generator()
a.Double(a)
a.Double(a)
b.x.Mul(&a.x, &beta)
b.y.Neg(&a.y)
b.z.Set(&a.z)
require.Equal(t, 1, a.IsOnCurve())
require.Equal(t, 1, b.IsOnCurve())
c.Add(a, b)
d.x.Set(&fp{
0x29e1e987ef68f2d0,
0xc5f3ec531db03233,
0xacd6c4b6ca19730f,
0x18ad9e827bc2bab7,
0x46e3b2c5785cc7a9,
0x07e571d42d22ddd6,
})
d.y.Set(&fp{
0x94d117a7e5a539e7,
0x8e17ef673d4b5d22,
0x9d746aaf508a33ea,
0x8c6d883d2516c9a2,
0x0bc3b8d5fb0447f7,
0x07bfa4c7210f4f44,
})
d.z.SetOne()
require.Equal(t, 1, c.Equal(d))
}
func TestG1Sub(t *testing.T) {
a := new(G1).Generator()
b := new(G1).Generator()
require.Equal(t, 1, a.Sub(a, b).IsIdentity())
b.Double(b)
a.Generator()
require.Equal(t, 1, b.Sub(b, a).Equal(a))
}
func TestG1Mul(t *testing.T) {
g := new(G1).Generator()
a := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{
0x2b568297a56da71c,
0xd8c39ecb0ef375d1,
0x435c38da67bfbf96,
0x8088a05026b659b2,
})
b := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{
0x785fdd9b26ef8b85,
0xc997f25837695c18,
0x4c8dbc39e7b756c1,
0x70d9b6cc6d87df20,
})
c := Bls12381FqNew().Mul(a, b)
t1 := new(G1).Generator()
t1.Mul(t1, a)
t1.Mul(t1, b)
require.Equal(t, 1, t1.Equal(g.Mul(g, c)))
}
func TestG1Neg(t *testing.T) {
a := new(G1).Generator()
b := new(G1).Neg(a)
require.Equal(t, 1, new(G1).Add(a, b).IsIdentity())
require.Equal(t, 1, new(G1).Sub(a, b.Neg(b)).IsIdentity())
a.Identity()
require.Equal(t, 1, a.Neg(a).IsIdentity())
}
func TestG1InCorrectSubgroup(t *testing.T) {
// ZCash test vector
a := G1{
x: fp{
0x0abaf895b97e43c8,
0xba4c6432eb9b61b0,
0x12506f52adfe307f,
0x75028c3439336b72,
0x84744f05b8e9bd71,
0x113d554fb09554f7,
},
y: fp{
0x73e90e88f5cf01c0,
0x37007b65dd3197e2,
0x5cf9a1992f0d7c78,
0x4f83c10b9eb3330d,
0xf6a63f6f07f60961,
0x0c53b5b97e634df3,
},
z: *(new(fp).SetOne()),
}
require.Equal(t, 0, a.InCorrectSubgroup())
require.Equal(t, 1, new(G1).Identity().InCorrectSubgroup())
require.Equal(t, 1, new(G1).Generator().InCorrectSubgroup())
}
func TestG1MulByX(t *testing.T) {
// multiplying by `x` a point in G1 is the same as multiplying by
// the equivalent scalar.
generator := new(G1).Generator()
x := Bls12381FqNew().SetUint64(paramX)
x.Neg(x)
lhs := new(G1).Mul(generator, x)
rhs := new(G1).MulByX(generator)
require.Equal(t, 1, lhs.Equal(rhs))
pt := new(G1).Generator()
s := Bls12381FqNew().SetUint64(42)
pt.Mul(pt, s)
lhs.Mul(pt, x)
rhs.MulByX(pt)
require.Equal(t, 1, lhs.Equal(rhs))
}
func TestG1ClearCofactor(t *testing.T) {
// the generator (and the identity) are always on the curve,
// even after clearing the cofactor
generator := new(G1).Generator()
generator.ClearCofactor(generator)
require.Equal(t, 1, generator.IsOnCurve())
id := new(G1).Identity()
id.ClearCofactor(id)
require.Equal(t, 1, id.IsOnCurve())
z := fp{
0x3d2d1c670671394e,
0x0ee3a800a2f7c1ca,
0x270f4f21da2e5050,
0xe02840a53f1be768,
0x55debeb597512690,
0x08bd25353dc8f791,
}
point := G1{
x: fp{
0x48af5ff540c817f0,
0xd73893acaf379d5a,
0xe6c43584e18e023c,
0x1eda39c30f188b3e,
0xf618c6d3ccc0f8d8,
0x0073542cd671e16c,
},
y: fp{
0x57bf8be79461d0ba,
0xfc61459cee3547c3,
0x0d23567df1ef147b,
0x0ee187bcce1d9b64,
0xb0c8cfbe9dc8fdc1,
0x1328661767ef368b,
},
z: *(&fp{}).Set(&z),
}
point.x.Mul(&point.x, &z)
point.z.Square(&z)
point.z.Mul(&point.z, &z)
require.Equal(t, 1, point.IsOnCurve())
require.Equal(t, 0, point.InCorrectSubgroup())
clearedPoint := new(G1).ClearCofactor(&point)
require.Equal(t, 1, clearedPoint.IsOnCurve())
require.Equal(t, 1, clearedPoint.InCorrectSubgroup())
// in BLS12-381 the cofactor in G1 can be
// cleared multiplying by (1-x)
hEff := Bls12381FqNew().SetOne()
hEff.Add(hEff, Bls12381FqNew().SetUint64(paramX))
point.Mul(&point, hEff)
require.Equal(t, 1, clearedPoint.Equal(&point))
}
func TestG1Hash(t *testing.T) {
dst := []byte("QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_")
tests := []struct {
input, expected string
}{
{
"",
"052926add2207b76ca4fa57a8734416c8dc95e24501772c814278700eed6d1e4e8cf62d9c09db0fac349612b759e79a108ba738453bfed09cb546dbb0783dbb3a5f1f566ed67bb6be0e8c67e2e81a4cc68ee29813bb7994998f3eae0c9c6a265",
},
{
"abc",
"03567bc5ef9c690c2ab2ecdf6a96ef1c139cc0b2f284dca0a9a7943388a49a3aee664ba5379a7655d3c68900be2f69030b9c15f3fe6e5cf4211f346271d7b01c8f3b28be689c8429c85b67af215533311f0b8dfaaa154fa6b88176c229f2885d",
},
{
"abcdef0123456789",
"11e0b079dea29a68f0383ee94fed1b940995272407e3bb916bbf268c263ddd57a6a27200a784cbc248e84f357ce82d9803a87ae2caf14e8ee52e51fa2ed8eefe80f02457004ba4d486d6aa1f517c0889501dc7413753f9599b099ebcbbd2d709",
},
{
"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"15f68eaa693b95ccb85215dc65fa81038d69629f70aeee0d0f677cf22285e7bf58d7cb86eefe8f2e9bc3f8cb84fac4881807a1d50c29f430b8cafc4f8638dfeeadf51211e1602a5f184443076715f91bb90a48ba1e370edce6ae1062f5e6dd38",
},
{
"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"082aabae8b7dedb0e78aeb619ad3bfd9277a2f77ba7fad20ef6aabdc6c31d19ba5a6d12283553294c1825c4b3ca2dcfe05b84ae5a942248eea39e1d91030458c40153f3b654ab7872d779ad1e942856a20c438e8d99bc8abfbf74729ce1f7ac8",
},
}
pt := new(G1).Identity()
ept := new(G1).Identity()
var b [WideFieldBytes]byte
for _, tst := range tests {
i := []byte(tst.input)
e, _ := hex.DecodeString(tst.expected)
copy(b[:], e)
_, _ = ept.FromUncompressed(&b)
pt.Hash(native.EllipticPointHasherSha256(), i, dst)
require.Equal(t, 1, pt.Equal(ept))
}
}
func TestSerialization(t *testing.T) {
a := new(
G1,
).Hash(native.EllipticPointHasherSha256(), []byte("a"), []byte("BLS12381G1_XMD:SHA-256_SSWU_RO_"))
b := new(
G1,
).Hash(native.EllipticPointHasherSha256(), []byte("b"), []byte("BLS12381G1_XMD:SHA-256_SSWU_RO_"))
aBytes := a.ToCompressed()
bBytes := b.ToCompressed()
aa, err := new(G1).FromCompressed(&aBytes)
require.NoError(t, err)
require.Equal(t, 1, a.Equal(aa))
bb, err := new(G1).FromCompressed(&bBytes)
require.NoError(t, err)
require.Equal(t, 1, b.Equal(bb))
auBytes := a.ToUncompressed()
buBytes := b.ToUncompressed()
_, err = aa.FromUncompressed(&auBytes)
require.NoError(t, err)
require.Equal(t, 1, a.Equal(aa))
_, err = bb.FromUncompressed(&buBytes)
require.NoError(t, err)
require.Equal(t, 1, b.Equal(bb))
bBytes = a.ToCompressed()
a.Neg(a)
aBytes = a.ToCompressed()
_, err = aa.FromCompressed(&aBytes)
require.NoError(t, err)
require.Equal(t, 1, a.Equal(aa))
_, err = aa.FromCompressed(&bBytes)
require.NoError(t, err)
require.Equal(t, 0, a.Equal(aa))
require.Equal(t, 1, aa.Equal(a.Neg(a)))
}
func TestSumOfProducts(t *testing.T) {
var b [64]byte
h0, _ := new(G1).Random(crand.Reader)
_, _ = crand.Read(b[:])
s := Bls12381FqNew().SetBytesWide(&b)
_, _ = crand.Read(b[:])
sTilde := Bls12381FqNew().SetBytesWide(&b)
_, _ = crand.Read(b[:])
c := Bls12381FqNew().SetBytesWide(&b)
lhs := new(G1).Mul(h0, s)
rhs, _ := new(G1).SumOfProducts([]*G1{h0}, []*native.Field{s})
require.Equal(t, 1, lhs.Equal(rhs))
u := new(G1).Mul(h0, s)
uTilde := new(G1).Mul(h0, sTilde)
sHat := Bls12381FqNew().Mul(c, s)
sHat.Sub(sTilde, sHat)
rhs.Mul(u, c)
rhs.Add(rhs, new(G1).Mul(h0, sHat))
require.Equal(t, 1, uTilde.Equal(rhs))
_, _ = rhs.SumOfProducts([]*G1{u, h0}, []*native.Field{c, sHat})
require.Equal(t, 1, uTilde.Equal(rhs))
}
File diff suppressed because it is too large Load Diff
+569
View File
@@ -0,0 +1,569 @@
package bls12381
import (
crand "crypto/rand"
"encoding/hex"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves/native"
)
func TestG2IsOnCurve(t *testing.T) {
require.Equal(t, 1, new(G2).Identity().IsOnCurve())
require.Equal(t, 1, new(G2).Generator().IsOnCurve())
z := fp2{
A: fp{
0xba7a_fa1f_9a6f_e250,
0xfa0f_5b59_5eaf_e731,
0x3bdc_4776_94c3_06e7,
0x2149_be4b_3949_fa24,
0x64aa_6e06_49b2_078c,
0x12b1_08ac_3364_3c3e,
},
B: fp{
0x1253_25df_3d35_b5a8,
0xdc46_9ef5_555d_7fe3,
0x02d7_16d2_4431_06a9,
0x05a1_db59_a6ff_37d0,
0x7cf7_784e_5300_bb8f,
0x16a8_8922_c7a5_e844,
},
}
test := new(G2).Generator()
test.x.Mul(&test.x, &z)
test.y.Mul(&test.y, &z)
test.z.Set(&z)
require.Equal(t, 1, test.IsOnCurve())
test.x.Set(&z)
require.Equal(t, 0, test.IsOnCurve())
}
func TestG2Equal(t *testing.T) {
a := new(G2).Generator()
b := new(G2).Identity()
require.Equal(t, 1, a.Equal(a))
require.Equal(t, 0, a.Equal(b))
require.Equal(t, 1, b.Equal(b))
}
func TestG2ToAffine(t *testing.T) {
a := new(G2).Generator()
z := fp2{
A: fp{
0xba7afa1f9a6fe250,
0xfa0f5b595eafe731,
0x3bdc477694c306e7,
0x2149be4b3949fa24,
0x64aa6e0649b2078c,
0x12b108ac33643c3e,
},
B: fp{
0x125325df3d35b5a8,
0xdc469ef5555d7fe3,
0x02d716d2443106a9,
0x05a1db59a6ff37d0,
0x7cf7784e5300bb8f,
0x16a88922c7a5e844,
},
}
a.x.Mul(&a.x, &z)
a.y.Mul(&a.y, &z)
a.z.Set(&z)
require.Equal(t, 1, a.ToAffine(a).Equal(new(G2).Generator()))
}
func TestG2Double(t *testing.T) {
a := new(G2).Identity()
require.Equal(t, 1, a.Double(a).IsIdentity())
a.Generator()
a.Double(a)
e := G2{
x: fp2{
A: fp{
0xe9d9e2da9620f98b,
0x54f1199346b97f36,
0x3db3b820376bed27,
0xcfdb31c9b0b64f4c,
0x41d7c12786354493,
0x05710794c255c064,
},
B: fp{
0xd6c1d3ca6ea0d06e,
0xda0cbd905595489f,
0x4f5352d43479221d,
0x8ade5d736f8c97e0,
0x48cc8433925ef70e,
0x08d7ea71ea91ef81,
},
},
y: fp2{
A: fp{
0x15ba26eb4b0d186f,
0x0d086d64b7e9e01e,
0xc8b848dd652f4c78,
0xeecf46a6123bae4f,
0x255e8dd8b6dc812a,
0x164142af21dcf93f,
},
B: fp{
0xf9b4a1a895984db4,
0xd417b114cccff748,
0x6856301fc89f086e,
0x41c777878931e3da,
0x3556b155066a2105,
0x00acf7d325cb89cf,
},
},
z: *((&fp2{}).SetOne()),
}
require.Equal(t, 1, e.Equal(a))
}
func TestG2Add(t *testing.T) {
a := new(G2).Identity()
b := new(G2).Identity()
c := new(G2).Add(a, b)
require.Equal(t, 1, c.IsIdentity())
b.Generator()
c.Add(a, b)
require.Equal(t, 1, c.Equal(b))
a.Generator()
a.Double(a)
a.Double(a)
b.Double(b)
c.Add(a, b)
d := new(G2).Generator()
e := new(G2).Generator()
for i := 0; i < 5; i++ {
e.Add(e, d)
}
require.Equal(t, 1, e.Equal(c))
// Degenerate case
beta := fp2{
A: fp{
0xcd03c9e48671f071,
0x5dab22461fcda5d2,
0x587042afd3851b95,
0x8eb60ebe01bacb9e,
0x03f97d6e83d050d2,
0x18f0206554638741,
},
B: fp{},
}
beta.Square(&beta)
b.x.Mul(&a.x, &beta)
b.y.Neg(&a.y)
b.z.Set(&a.z)
require.Equal(t, 1, b.IsOnCurve())
c.Add(a, b)
e.x.Set(&fp2{
A: fp{
0x705abc799ca773d3,
0xfe132292c1d4bf08,
0xf37ece3e07b2b466,
0x887e1c43f447e301,
0x1e0970d033bc77e8,
0x1985c81e20a693f2,
},
B: fp{
0x1d79b25db36ab924,
0x23948e4d529639d3,
0x471ba7fb0d006297,
0x2c36d4b4465dc4c0,
0x82bbc3cfec67f538,
0x051d2728b67bf952,
},
})
e.y.Set(&fp2{
A: fp{
0x41b1bbf6576c0abf,
0xb6cc93713f7a0f9a,
0x6b65b43e48f3f01f,
0xfb7a4cfcaf81be4f,
0x3e32dadc6ec22cb6,
0x0bb0fc49d79807e3,
},
B: fp{
0x7d1397788f5f2ddf,
0xab2907144ff0d8e8,
0x5b7573e0cdb91f92,
0x4cb8932dd31daf28,
0x62bbfac6db052a54,
0x11f95c16d14c3bbe,
},
})
e.z.SetOne()
require.Equal(t, 1, e.Equal(c))
}
func TestG2Neg(t *testing.T) {
a := new(G2).Generator()
b := new(G2).Neg(a)
require.Equal(t, 1, new(G2).Add(a, b).IsIdentity())
require.Equal(t, 1, new(G2).Sub(a, b.Neg(b)).IsIdentity())
a.Identity()
require.Equal(t, 1, a.Neg(a).IsIdentity())
}
func TestG2Mul(t *testing.T) {
g := new(G2).Generator()
a := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{
0x2b56_8297_a56d_a71c,
0xd8c3_9ecb_0ef3_75d1,
0x435c_38da_67bf_bf96,
0x8088_a050_26b6_59b2,
})
b := Bls12381FqNew().SetRaw(&[native.FieldLimbs]uint64{
0x785f_dd9b_26ef_8b85,
0xc997_f258_3769_5c18,
0x4c8d_bc39_e7b7_56c1,
0x70d9_b6cc_6d87_df20,
})
c := Bls12381FqNew().Mul(a, b)
t1 := new(G2).Generator()
t1.Mul(t1, a)
t1.Mul(t1, b)
require.Equal(t, 1, t1.Equal(g.Mul(g, c)))
}
func TestG2InCorrectSubgroup(t *testing.T) {
a := G2{
x: fp2{
A: fp{
0x89f550c813db6431,
0xa50be8c456cd8a1a,
0xa45b374114cae851,
0xbb6190f5bf7fff63,
0x970ca02c3ba80bc7,
0x02b85d24e840fbac,
},
B: fp{
0x6888bc53d70716dc,
0x3dea6b4117682d70,
0xd8f5f930500ca354,
0x6b5ecb6556f5c155,
0xc96bef0434778ab0,
0x05081505515006ad,
},
},
y: fp2{
A: fp{
0x3cf1ea0d434b0f40,
0x1a0dc610e603e333,
0x7f89956160c72fa0,
0x25ee03decf6431c5,
0xeee8e206ec0fe137,
0x097592b226dfef28,
},
B: fp{
0x71e8bb5f29247367,
0xa5fe049e211831ce,
0x0ce6b354502a3896,
0x93b012000997314e,
0x6759f3b6aa5b42ac,
0x156944c4dfe92bbb,
},
},
z: *(&fp2{}).SetOne(),
}
require.Equal(t, 0, a.InCorrectSubgroup())
require.Equal(t, 1, new(G2).Identity().InCorrectSubgroup())
require.Equal(t, 1, new(G2).Generator().InCorrectSubgroup())
}
func TestG2MulByX(t *testing.T) {
// multiplying by `x` a point in G2 is the same as multiplying by
// the equivalent scalar.
x := Bls12381FqNew().SetUint64(paramX)
x.Neg(x)
t1 := new(G2).Generator()
t1.MulByX(t1)
t2 := new(G2).Generator()
t2.Mul(t2, x)
require.Equal(t, 1, t1.Equal(t2))
point := new(G2).Generator()
a := Bls12381FqNew().SetUint64(42)
point.Mul(point, a)
t1.MulByX(point)
t2.Mul(point, x)
require.Equal(t, 1, t1.Equal(t2))
}
func TestG2Psi(t *testing.T) {
generator := new(G2).Generator()
z := fp2{
A: fp{
0x0ef2ddffab187c0a,
0x2424522b7d5ecbfc,
0xc6f341a3398054f4,
0x5523ddf409502df0,
0xd55c0b5a88e0dd97,
0x066428d704923e52,
},
B: fp{
0x538bbe0c95b4878d,
0xad04a50379522881,
0x6d5c05bf5c12fb64,
0x4ce4a069a2d34787,
0x59ea6c8d0dffaeaf,
0x0d42a083a75bd6f3,
},
}
// `point` is a random point in the curve
point := G2{
x: fp2{
A: fp{
0xee4c8cb7c047eaf2,
0x44ca22eee036b604,
0x33b3affb2aefe101,
0x15d3e45bbafaeb02,
0x7bfc2154cd7419a4,
0x0a2d0c2b756e5edc,
},
B: fp{
0xfc224361029a8777,
0x4cbf2baab8740924,
0xc5008c6ec6592c89,
0xecc2c57b472a9c2d,
0x8613eafd9d81ffb1,
0x10fe54daa2d3d495,
},
},
y: fp2{
A: fp{
0x7de7edc43953b75c,
0x58be1d2de35e87dc,
0x5731d30b0e337b40,
0xbe93b60cfeaae4c9,
0x8b22c203764bedca,
0x01616c8d1033b771,
},
B: fp{
0xea126fe476b5733b,
0x85cee68b5dae1652,
0x98247779f7272b04,
0xa649c8b468c6e808,
0xb5b9a62dff0c4e45,
0x1555b67fc7bbe73d,
},
},
z: *(&fp2{}).Set(&z),
}
point.x.Mul(&point.x, &z)
point.z.Square(&point.z)
point.z.Mul(&point.z, &z)
require.Equal(t, 1, point.IsOnCurve())
// psi2(P) = psi(psi(P))
tv1 := new(G2).psi2(generator)
tv2 := new(G2).psi(generator)
tv2.psi(tv2)
require.Equal(t, 1, tv1.Equal(tv2))
tv1.psi2(&point)
tv2.psi(&point)
tv2.psi(tv2)
require.Equal(t, 1, tv1.Equal(tv2))
// psi(P) is a morphism
tv1.Double(generator)
tv1.psi(tv1)
tv2.psi(generator)
tv2.Double(tv2)
require.Equal(t, 1, tv1.Equal(tv2))
tv1.psi(&point)
tv2.psi(generator)
tv1.Add(tv1, tv2)
tv2.Set(&point)
tv3 := new(G2).Generator()
tv2.Add(tv2, tv3)
tv2.psi(tv2)
require.Equal(t, 1, tv1.Equal(tv2))
}
func TestG2ClearCofactor(t *testing.T) {
z := fp2{
A: fp{
0x0ef2ddffab187c0a,
0x2424522b7d5ecbfc,
0xc6f341a3398054f4,
0x5523ddf409502df0,
0xd55c0b5a88e0dd97,
0x066428d704923e52,
},
B: fp{
0x538bbe0c95b4878d,
0xad04a50379522881,
0x6d5c05bf5c12fb64,
0x4ce4a069a2d34787,
0x59ea6c8d0dffaeaf,
0x0d42a083a75bd6f3,
},
}
// `point` is a random point in the curve
point := G2{
x: fp2{
A: fp{
0xee4c8cb7c047eaf2,
0x44ca22eee036b604,
0x33b3affb2aefe101,
0x15d3e45bbafaeb02,
0x7bfc2154cd7419a4,
0x0a2d0c2b756e5edc,
},
B: fp{
0xfc224361029a8777,
0x4cbf2baab8740924,
0xc5008c6ec6592c89,
0xecc2c57b472a9c2d,
0x8613eafd9d81ffb1,
0x10fe54daa2d3d495,
},
},
y: fp2{
A: fp{
0x7de7edc43953b75c,
0x58be1d2de35e87dc,
0x5731d30b0e337b40,
0xbe93b60cfeaae4c9,
0x8b22c203764bedca,
0x01616c8d1033b771,
},
B: fp{
0xea126fe476b5733b,
0x85cee68b5dae1652,
0x98247779f7272b04,
0xa649c8b468c6e808,
0xb5b9a62dff0c4e45,
0x1555b67fc7bbe73d,
},
},
z: fp2{},
}
point.x.Mul(&point.x, &z)
point.z.Square(&z)
point.z.Mul(&point.z, &z)
require.Equal(t, 1, point.IsOnCurve())
require.Equal(t, 0, point.InCorrectSubgroup())
clearedPoint := new(G2).ClearCofactor(&point)
require.Equal(t, 1, clearedPoint.IsOnCurve())
require.Equal(t, 1, clearedPoint.InCorrectSubgroup())
// the generator (and the identity) are always on the curve,
// even after clearing the cofactor
generator := new(G2).Generator()
generator.ClearCofactor(generator)
require.Equal(t, 1, generator.InCorrectSubgroup())
id := new(G2).Identity()
id.ClearCofactor(id)
require.Equal(t, 1, id.InCorrectSubgroup())
// test the effect on q-torsion points multiplying by h_eff modulo q
// h_eff % q = 0x2b116900400069009a40200040001ffff
hEffModq := [native.FieldBytes]byte{
0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, 0x16,
0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
}
generator.Generator()
generator.multiply(generator, &hEffModq)
point.Generator().ClearCofactor(&point)
require.Equal(t, 1, point.Equal(generator))
point.ClearCofactor(clearedPoint)
require.Equal(t, 1, point.Equal(clearedPoint.multiply(clearedPoint, &hEffModq)))
}
func TestG2Hash(t *testing.T) {
dst := []byte("QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_")
tests := []struct {
input, expected string
}{
{
"",
"05cb8437535e20ecffaef7752baddf98034139c38452458baeefab379ba13dff5bf5dd71b72418717047f5b0f37da03d0141ebfbdca40eb85b87142e130ab689c673cf60f1a3e98d69335266f30d9b8d4ac44c1038e9dcdd5393faf5c41fb78a12424ac32561493f3fe3c260708a12b7c620e7be00099a974e259ddc7d1f6395c3c811cdd19f1e8dbf3e9ecfdcbab8d60503921d7f6a12805e72940b963c0cf3471c7b2a524950ca195d11062ee75ec076daf2d4bc358c4b190c0c98064fdd92",
},
{
"abc",
"139cddbccdc5e91b9623efd38c49f81a6f83f175e80b06fc374de9eb4b41dfe4ca3a230ed250fbe3a2acf73a41177fd802c2d18e033b960562aae3cab37a27ce00d80ccd5ba4b7fe0e7a210245129dbec7780ccc7954725f4168aff2787776e600aa65dae3c8d732d10ecd2c50f8a1baf3001578f71c694e03866e9f3d49ac1e1ce70dd94a733534f106d4cec0eddd161787327b68159716a37440985269cf584bcb1e621d3a7202be6ea05c4cfe244aeb197642555a0645fb87bf7466b2ba48",
},
{
"abcdef0123456789",
"190d119345b94fbd15497bcba94ecf7db2cbfd1e1fe7da034d26cbba169fb3968288b3fafb265f9ebd380512a71c3f2c121982811d2491fde9ba7ed31ef9ca474f0e1501297f68c298e9f4c0028add35aea8bb83d53c08cfc007c1e005723cd00bb5e7572275c567462d91807de765611490205a941a5a6af3b1691bfe596c31225d3aabdf15faff860cb4ef17c7c3be05571a0f8d3c08d094576981f4a3b8eda0a8e771fcdcc8ecceaf1356a6acf17574518acb506e435b639353c2e14827c8",
},
{
"q128_qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
"0934aba516a52d8ae479939a91998299c76d39cc0c035cd18813bec433f587e2d7a4fef038260eef0cef4d02aae3eb9119a84dd7248a1066f737cc34502ee5555bd3c19f2ecdb3c7d9e24dc65d4e25e50d83f0f77105e955d78f4762d33c17da09bcccfa036b4847c9950780733633f13619994394c23ff0b32fa6b795844f4a0673e20282d07bc69641cee04f5e566214f81cd421617428bc3b9fe25afbb751d934a00493524bc4e065635b0555084dd54679df1536101b2c979c0152d09192",
},
{
"a512_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"11fca2ff525572795a801eed17eb12785887c7b63fb77a42be46ce4a34131d71f7a73e95fee3f812aea3de78b4d0156901a6ba2f9a11fa5598b2d8ace0fbe0a0eacb65deceb476fbbcb64fd24557c2f4b18ecfc5663e54ae16a84f5ab7f6253403a47f8e6d1763ba0cad63d6114c0accbef65707825a511b251a660a9b3994249ae4e63fac38b23da0c398689ee2ab520b6798718c8aed24bc19cb27f866f1c9effcdbf92397ad6448b5c9db90d2b9da6cbabf48adc1adf59a1a28344e79d57e",
},
}
pt := new(G2).Identity()
ept := new(G2).Identity()
var b [DoubleWideFieldBytes]byte
for _, tst := range tests {
i := []byte(tst.input)
e, _ := hex.DecodeString(tst.expected)
copy(b[:], e)
_, _ = ept.FromUncompressed(&b)
pt.Hash(native.EllipticPointHasherSha256(), i, dst)
require.Equal(t, 1, pt.Equal(ept))
}
}
func TestG2SumOfProducts(t *testing.T) {
var b [64]byte
h0, _ := new(G2).Random(crand.Reader)
_, _ = crand.Read(b[:])
s := Bls12381FqNew().SetBytesWide(&b)
_, _ = crand.Read(b[:])
sTilde := Bls12381FqNew().SetBytesWide(&b)
_, _ = crand.Read(b[:])
c := Bls12381FqNew().SetBytesWide(&b)
lhs := new(G2).Mul(h0, s)
rhs, _ := new(G2).SumOfProducts([]*G2{h0}, []*native.Field{s})
require.Equal(t, 1, lhs.Equal(rhs))
u := new(G2).Mul(h0, s)
uTilde := new(G2).Mul(h0, sTilde)
sHat := Bls12381FqNew().Mul(c, s)
sHat.Sub(sTilde, sHat)
rhs.Mul(u, c)
rhs.Add(rhs, new(G2).Mul(h0, sHat))
require.Equal(t, 1, uTilde.Equal(rhs))
_, _ = rhs.SumOfProducts([]*G2{u, h0}, []*native.Field{c, sHat})
require.Equal(t, 1, uTilde.Equal(rhs))
}
+434
View File
@@ -0,0 +1,434 @@
package bls12381
import (
"io"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/internal"
)
// GtFieldBytes is the number of bytes needed to represent this field
const GtFieldBytes = 576
// Gt is the target group
type Gt fp12
// Random generates a random field element
func (gt *Gt) Random(reader io.Reader) (*Gt, error) {
_, err := (*fp12)(gt).Random(reader)
return gt, err
}
// FinalExponentiation performs a "final exponentiation" routine to convert the result
// of a Miller loop into an element of `Gt` with help of efficient squaring
// operation in the so-called `cyclotomic subgroup` of `Fq6` so that
// it can be compared with other elements of `Gt`.
func (gt *Gt) FinalExponentiation(a *Gt) *Gt {
var t0, t1, t2, t3, t4, t5, t6, t fp12
t0.FrobeniusMap((*fp12)(a))
t0.FrobeniusMap(&t0)
t0.FrobeniusMap(&t0)
t0.FrobeniusMap(&t0)
t0.FrobeniusMap(&t0)
t0.FrobeniusMap(&t0)
// Shouldn't happen since we enforce `a` to be non-zero but just in case
_, wasInverted := t1.Invert((*fp12)(a))
t2.Mul(&t0, &t1)
t1.Set(&t2)
t2.FrobeniusMap(&t2)
t2.FrobeniusMap(&t2)
t2.Mul(&t2, &t1)
t1.cyclotomicSquare(&t2)
t1.Conjugate(&t1)
t3.cyclotomicExp(&t2)
t4.cyclotomicSquare(&t3)
t5.Mul(&t1, &t3)
t1.cyclotomicExp(&t5)
t0.cyclotomicExp(&t1)
t6.cyclotomicExp(&t0)
t6.Mul(&t6, &t4)
t4.cyclotomicExp(&t6)
t5.Conjugate(&t5)
t4.Mul(&t4, &t5)
t4.Mul(&t4, &t2)
t5.Conjugate(&t2)
t1.Mul(&t1, &t2)
t1.FrobeniusMap(&t1)
t1.FrobeniusMap(&t1)
t1.FrobeniusMap(&t1)
t6.Mul(&t6, &t5)
t6.FrobeniusMap(&t6)
t3.Mul(&t3, &t0)
t3.FrobeniusMap(&t3)
t3.FrobeniusMap(&t3)
t3.Mul(&t3, &t1)
t3.Mul(&t3, &t6)
t.Mul(&t3, &t4)
(*fp12)(gt).CMove((*fp12)(gt), &t, wasInverted)
return gt
}
// IsZero returns 1 if gt == 0, 0 otherwise
func (gt *Gt) IsZero() int {
return (*fp12)(gt).IsZero()
}
// IsOne returns 1 if gt == 1, 0 otherwise
func (gt *Gt) IsOne() int {
return (*fp12)(gt).IsOne()
}
// SetOne gt = one
func (gt *Gt) SetOne() *Gt {
(*fp12)(gt).SetOne()
return gt
}
// Set copies a into gt
func (gt *Gt) Set(a *Gt) *Gt {
gt.A.Set(&a.A)
gt.B.Set(&a.B)
return gt
}
// Bytes returns the Gt field byte representation
func (gt *Gt) Bytes() [GtFieldBytes]byte {
var out [GtFieldBytes]byte
t := gt.A.A.A.Bytes()
copy(out[:FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.A.A.B.Bytes()
copy(out[FieldBytes:2*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.A.B.A.Bytes()
copy(out[2*FieldBytes:3*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.A.B.B.Bytes()
copy(out[3*FieldBytes:4*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.A.C.A.Bytes()
copy(out[4*FieldBytes:5*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.A.C.B.Bytes()
copy(out[5*FieldBytes:6*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.A.A.Bytes()
copy(out[6*FieldBytes:7*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.A.B.Bytes()
copy(out[7*FieldBytes:8*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.B.A.Bytes()
copy(out[8*FieldBytes:9*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.B.B.Bytes()
copy(out[9*FieldBytes:10*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.C.A.Bytes()
copy(out[10*FieldBytes:11*FieldBytes], internal.ReverseScalarBytes(t[:]))
t = gt.B.C.B.Bytes()
copy(out[11*FieldBytes:12*FieldBytes], internal.ReverseScalarBytes(t[:]))
return out
}
// SetBytes attempts to convert a big-endian byte representation of
// a scalar into a `Gt`, failing if the input is not canonical.
func (gt *Gt) SetBytes(input *[GtFieldBytes]byte) (*Gt, int) {
var t [FieldBytes]byte
var valid [12]int
copy(t[:], internal.ReverseScalarBytes(input[:FieldBytes]))
_, valid[0] = gt.A.A.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[FieldBytes:2*FieldBytes]))
_, valid[1] = gt.A.A.B.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[2*FieldBytes:3*FieldBytes]))
_, valid[2] = gt.A.B.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[3*FieldBytes:4*FieldBytes]))
_, valid[3] = gt.A.B.B.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[4*FieldBytes:5*FieldBytes]))
_, valid[4] = gt.A.C.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[5*FieldBytes:6*FieldBytes]))
_, valid[5] = gt.A.C.B.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[6*FieldBytes:7*FieldBytes]))
_, valid[6] = gt.B.A.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[7*FieldBytes:8*FieldBytes]))
_, valid[7] = gt.B.A.B.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[8*FieldBytes:9*FieldBytes]))
_, valid[8] = gt.B.B.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[9*FieldBytes:10*FieldBytes]))
_, valid[9] = gt.B.B.B.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[10*FieldBytes:11*FieldBytes]))
_, valid[10] = gt.B.C.A.SetBytes(&t)
copy(t[:], internal.ReverseScalarBytes(input[11*FieldBytes:12*FieldBytes]))
_, valid[11] = gt.B.C.B.SetBytes(&t)
return gt, valid[0] & valid[1] &
valid[2] & valid[3] &
valid[4] & valid[5] &
valid[6] & valid[7] &
valid[8] & valid[9] &
valid[10] & valid[11]
}
// Equal returns 1 if gt == rhs, 0 otherwise
func (gt *Gt) Equal(rhs *Gt) int {
return (*fp12)(gt).Equal((*fp12)(rhs))
}
// Generator returns the base point
func (gt *Gt) Generator() *Gt {
// pairing(&G1::generator(), &G2::generator())
gt.Set((*Gt)(&fp12{
A: fp6{
A: fp2{
A: fp{
0x1972e433a01f85c5,
0x97d32b76fd772538,
0xc8ce546fc96bcdf9,
0xcef63e7366d40614,
0xa611342781843780,
0x13f3448a3fc6d825,
},
B: fp{
0xd26331b02e9d6995,
0x9d68a482f7797e7d,
0x9c9b29248d39ea92,
0xf4801ca2e13107aa,
0xa16c0732bdbcb066,
0x083ca4afba360478,
},
},
B: fp2{
A: fp{
0x59e261db0916b641,
0x2716b6f4b23e960d,
0xc8e55b10a0bd9c45,
0x0bdb0bd99c4deda8,
0x8cf89ebf57fdaac5,
0x12d6b7929e777a5e,
},
B: fp{
0x5fc85188b0e15f35,
0x34a06e3a8f096365,
0xdb3126a6e02ad62c,
0xfc6f5aa97d9a990b,
0xa12f55f5eb89c210,
0x1723703a926f8889,
},
},
C: fp2{
A: fp{
0x93588f2971828778,
0x43f65b8611ab7585,
0x3183aaf5ec279fdf,
0xfa73d7e18ac99df6,
0x64e176a6a64c99b0,
0x179fa78c58388f1f,
},
B: fp{
0x672a0a11ca2aef12,
0x0d11b9b52aa3f16b,
0xa44412d0699d056e,
0xc01d0177221a5ba5,
0x66e0cede6c735529,
0x05f5a71e9fddc339,
},
},
},
B: fp6{
A: fp2{
A: fp{
0xd30a88a1b062c679,
0x5ac56a5d35fc8304,
0xd0c834a6a81f290d,
0xcd5430c2da3707c7,
0xf0c27ff780500af0,
0x09245da6e2d72eae,
},
B: fp{
0x9f2e0676791b5156,
0xe2d1c8234918fe13,
0x4c9e459f3c561bf4,
0xa3e85e53b9d3e3c1,
0x820a121e21a70020,
0x15af618341c59acc,
},
},
B: fp2{
A: fp{
0x7c95658c24993ab1,
0x73eb38721ca886b9,
0x5256d749477434bc,
0x8ba41902ea504a8b,
0x04a3d3f80c86ce6d,
0x18a64a87fb686eaa,
},
B: fp{
0xbb83e71bb920cf26,
0x2a5277ac92a73945,
0xfc0ee59f94f046a0,
0x7158cdf3786058f7,
0x7cc1061b82f945f6,
0x03f847aa9fdbe567,
},
},
C: fp2{
A: fp{
0x8078dba56134e657,
0x1cd7ec9a43998a6e,
0xb1aa599a1a993766,
0xc9a0f62f0842ee44,
0x8e159be3b605dffa,
0x0c86ba0d4af13fc2,
},
B: fp{
0xe80ff2a06a52ffb1,
0x7694ca48721a906c,
0x7583183e03b08514,
0xf567afdd40cee4e2,
0x9a6d96d2e526a5fc,
0x197e9f49861f2242,
},
},
},
}))
return gt
}
// Add adds this value to another value.
func (gt *Gt) Add(arg1, arg2 *Gt) *Gt {
(*fp12)(gt).Mul((*fp12)(arg1), (*fp12)(arg2))
return gt
}
// Double this value
func (gt *Gt) Double(a *Gt) *Gt {
(*fp12)(gt).Square((*fp12)(a))
return gt
}
// Sub subtracts the two values
func (gt *Gt) Sub(arg1, arg2 *Gt) *Gt {
var t fp12
t.Conjugate((*fp12)(arg2))
(*fp12)(gt).Mul((*fp12)(arg1), &t)
return gt
}
// Neg negates this value
func (gt *Gt) Neg(a *Gt) *Gt {
(*fp12)(gt).Conjugate((*fp12)(a))
return gt
}
// Mul multiplies this value by the input scalar
func (gt *Gt) Mul(a *Gt, s *native.Field) *Gt {
var f, p fp12
f.Set((*fp12)(a))
bytes := s.Bytes()
precomputed := [16]fp12{}
precomputed[1].Set(&f)
for i := 2; i < 16; i += 2 {
precomputed[i].Square(&precomputed[i>>1])
precomputed[i+1].Mul(&precomputed[i], &f)
}
for i := 0; i < 256; i += 4 {
// Brouwer / windowing method. window size of 4.
for j := 0; j < 4; j++ {
p.Square(&p)
}
window := bytes[32-1-i>>3] >> (4 - i&0x04) & 0x0F
p.Mul(&p, &precomputed[window])
}
(*fp12)(gt).Set(&p)
return gt
}
// Square this value
func (gt *Gt) Square(a *Gt) *Gt {
(*fp12)(gt).cyclotomicSquare((*fp12)(a))
return gt
}
// Invert this value
func (gt *Gt) Invert(a *Gt) (*Gt, int) {
_, wasInverted := (*fp12)(gt).Invert((*fp12)(a))
return gt, wasInverted
}
func fp4Square(a, b, arg1, arg2 *fp2) {
var t0, t1, t2 fp2
t0.Square(arg1)
t1.Square(arg2)
t2.MulByNonResidue(&t1)
a.Add(&t2, &t0)
t2.Add(arg1, arg2)
t2.Square(&t2)
t2.Sub(&t2, &t0)
b.Sub(&t2, &t1)
}
func (f *fp12) cyclotomicSquare(a *fp12) *fp12 {
// Adaptation of Algorithm 5.5.4, Guide to Pairing-Based Cryptography
// Faster Squaring in the Cyclotomic Subgroup of Sixth Degree Extensions
// https://eprint.iacr.org/2009/565.pdf
var z0, z1, z2, z3, z4, z5, t0, t1, t2, t3 fp2
z0.Set(&a.A.A)
z4.Set(&a.A.B)
z3.Set(&a.A.C)
z2.Set(&a.B.A)
z1.Set(&a.B.B)
z5.Set(&a.B.C)
fp4Square(&t0, &t1, &z0, &z1)
z0.Sub(&t0, &z0)
z0.Double(&z0)
z0.Add(&z0, &t0)
z1.Add(&t1, &z1)
z1.Double(&z1)
z1.Add(&z1, &t1)
fp4Square(&t0, &t1, &z2, &z3)
fp4Square(&t2, &t3, &z4, &z5)
z4.Sub(&t0, &z4)
z4.Double(&z4)
z4.Add(&z4, &t0)
z5.Add(&z5, &t1)
z5.Double(&z5)
z5.Add(&z5, &t1)
t0.MulByNonResidue(&t3)
z2.Add(&z2, &t0)
z2.Double(&z2)
z2.Add(&z2, &t0)
z3.Sub(&t2, &z3)
z3.Double(&z3)
z3.Add(&z3, &t2)
f.A.A.Set(&z0)
f.A.B.Set(&z4)
f.A.C.Set(&z3)
f.B.A.Set(&z2)
f.B.B.Set(&z1)
f.B.C.Set(&z5)
return f
}
func (f *fp12) cyclotomicExp(a *fp12) *fp12 {
var t fp12
t.SetOne()
foundOne := 0
for i := 63; i >= 0; i-- {
b := int((paramX >> i) & 1)
if foundOne == 1 {
t.cyclotomicSquare(&t)
} else {
foundOne = b
}
if b == 1 {
t.Mul(&t, a)
}
}
f.Conjugate(&t)
return f
}
+254
View File
@@ -0,0 +1,254 @@
package bls12381
const coefficientsG2 = 68
type Engine struct {
pairs []pair
}
type pair struct {
g1 G1
g2 G2
}
type g2Prepared struct {
identity int
coefficients []coefficients
}
type coefficients struct {
a, b, c fp2
}
func (c *coefficients) CMove(arg1, arg2 *coefficients, choice int) *coefficients {
c.a.CMove(&arg1.a, &arg2.a, choice)
c.b.CMove(&arg1.b, &arg2.b, choice)
c.c.CMove(&arg1.c, &arg2.c, choice)
return c
}
// AddPair adds a pair of points to be paired
func (e *Engine) AddPair(g1 *G1, g2 *G2) *Engine {
var p pair
p.g1.ToAffine(g1)
p.g2.ToAffine(g2)
if p.g1.IsIdentity()|p.g2.IsIdentity() == 0 {
e.pairs = append(e.pairs, p)
}
return e
}
// AddPairInvG1 adds a pair of points to be paired. G1 point is negated
func (e *Engine) AddPairInvG1(g1 *G1, g2 *G2) *Engine {
var p G1
p.Neg(g1)
return e.AddPair(&p, g2)
}
// AddPairInvG2 adds a pair of points to be paired. G2 point is negated
func (e *Engine) AddPairInvG2(g1 *G1, g2 *G2) *Engine {
var p G2
p.Neg(g2)
return e.AddPair(g1, &p)
}
func (e *Engine) Reset() *Engine {
e.pairs = []pair{}
return e
}
func (e *Engine) Check() bool {
return e.pairing().IsOne() == 1
}
func (e *Engine) Result() *Gt {
return e.pairing()
}
func (e *Engine) pairing() *Gt {
f := new(Gt).SetOne()
if len(e.pairs) == 0 {
return f
}
coeffs := e.computeCoeffs()
e.millerLoop((*fp12)(f), coeffs)
return f.FinalExponentiation(f)
}
func (e *Engine) millerLoop(f *fp12, coeffs []g2Prepared) {
newF := new(fp12).SetZero()
found := 0
cIdx := 0
for i := 63; i >= 0; i-- {
x := int(((paramX >> 1) >> i) & 1)
if found == 0 {
found |= x
continue
}
// doubling
for j, terms := range coeffs {
identity := e.pairs[j].g1.IsIdentity() | terms.identity
newF.Set(f)
ell(newF, terms.coefficients[cIdx], &e.pairs[j].g1)
f.CMove(newF, f, identity)
}
cIdx++
if x == 1 {
// adding
for j, terms := range coeffs {
identity := e.pairs[j].g1.IsIdentity() | terms.identity
newF.Set(f)
ell(newF, terms.coefficients[cIdx], &e.pairs[j].g1)
f.CMove(newF, f, identity)
}
cIdx++
}
f.Square(f)
}
for j, terms := range coeffs {
identity := e.pairs[j].g1.IsIdentity() | terms.identity
newF.Set(f)
ell(newF, terms.coefficients[cIdx], &e.pairs[j].g1)
f.CMove(newF, f, identity)
}
f.Conjugate(f)
}
func (e *Engine) computeCoeffs() []g2Prepared {
coeffs := make([]g2Prepared, len(e.pairs))
for i, p := range e.pairs {
identity := p.g2.IsIdentity()
q := new(G2).Generator()
q.CMove(&p.g2, q, identity)
c := new(G2).Set(q)
cfs := make([]coefficients, coefficientsG2)
found := 0
k := 0
for j := 63; j >= 0; j-- {
x := int(((paramX >> 1) >> j) & 1)
if found == 0 {
found |= x
continue
}
cfs[k] = doublingStep(c)
k++
if x == 1 {
cfs[k] = additionStep(c, q)
k++
}
}
cfs[k] = doublingStep(c)
coeffs[i] = g2Prepared{
coefficients: cfs, identity: identity,
}
}
return coeffs
}
func ell(f *fp12, coeffs coefficients, p *G1) {
var x, y fp2
x.A.Mul(&coeffs.a.A, &p.y)
x.B.Mul(&coeffs.a.B, &p.y)
y.A.Mul(&coeffs.b.A, &p.x)
y.B.Mul(&coeffs.b.B, &p.x)
f.MulByABD(f, &coeffs.c, &y, &x)
}
func doublingStep(p *G2) coefficients {
// Adaptation of Algorithm 26, https://eprint.iacr.org/2010/354.pdf
var t0, t1, t2, t3, t4, t5, t6, zsqr fp2
t0.Square(&p.x)
t1.Square(&p.y)
t2.Square(&t1)
t3.Add(&t1, &p.x)
t3.Square(&t3)
t3.Sub(&t3, &t0)
t3.Sub(&t3, &t2)
t3.Double(&t3)
t4.Double(&t0)
t4.Add(&t4, &t0)
t6.Add(&p.x, &t4)
t5.Square(&t4)
zsqr.Square(&p.z)
p.x.Sub(&t5, &t3)
p.x.Sub(&p.x, &t3)
p.z.Add(&p.z, &p.y)
p.z.Square(&p.z)
p.z.Sub(&p.z, &t1)
p.z.Sub(&p.z, &zsqr)
p.y.Sub(&t3, &p.x)
p.y.Mul(&p.y, &t4)
t2.Double(&t2)
t2.Double(&t2)
t2.Double(&t2)
p.y.Sub(&p.y, &t2)
t3.Mul(&t4, &zsqr)
t3.Double(&t3)
t3.Neg(&t3)
t6.Square(&t6)
t6.Sub(&t6, &t0)
t6.Sub(&t6, &t5)
t1.Double(&t1)
t1.Double(&t1)
t6.Sub(&t6, &t1)
t0.Mul(&p.z, &zsqr)
t0.Double(&t0)
return coefficients{
a: t0, b: t3, c: t6,
}
}
func additionStep(r, q *G2) coefficients {
// Adaptation of Algorithm 27, https://eprint.iacr.org/2010/354.pdf
var zsqr, ysqr fp2
var t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10 fp2
zsqr.Square(&r.z)
ysqr.Square(&q.y)
t0.Mul(&zsqr, &q.x)
t1.Add(&q.y, &r.z)
t1.Square(&t1)
t1.Sub(&t1, &ysqr)
t1.Sub(&t1, &zsqr)
t1.Mul(&t1, &zsqr)
t2.Sub(&t0, &r.x)
t3.Square(&t2)
t4.Double(&t3)
t4.Double(&t4)
t5.Mul(&t4, &t2)
t6.Sub(&t1, &r.y)
t6.Sub(&t6, &r.y)
t9.Mul(&t6, &q.x)
t7.Mul(&t4, &r.x)
r.x.Square(&t6)
r.x.Sub(&r.x, &t5)
r.x.Sub(&r.x, &t7)
r.x.Sub(&r.x, &t7)
r.z.Add(&r.z, &t2)
r.z.Square(&r.z)
r.z.Sub(&r.z, &zsqr)
r.z.Sub(&r.z, &t3)
t10.Add(&q.y, &r.z)
t8.Sub(&t7, &r.x)
t8.Mul(&t8, &t6)
t0.Mul(&r.y, &t5)
t0.Double(&t0)
r.y.Sub(&t8, &t0)
t10.Square(&t10)
t10.Sub(&t10, &ysqr)
zsqr.Square(&r.z)
t10.Sub(&t10, &zsqr)
t9.Double(&t9)
t9.Sub(&t9, &t10)
t10.Double(&r.z)
t6.Neg(&t6)
t1.Double(&t6)
return coefficients{
a: t10, b: t1, c: t9,
}
}
@@ -0,0 +1,64 @@
package bls12381
import (
crand "crypto/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves/native"
)
func TestSinglePairing(t *testing.T) {
g := new(G1).Generator()
h := new(G2).Generator()
e := new(Engine)
e.AddPair(g, h)
p := e.Result()
p.Neg(p)
e.Reset()
e.AddPairInvG2(g, h)
q := e.Result()
e.Reset()
e.AddPairInvG1(g, h)
r := e.Result()
require.Equal(t, 1, p.Equal(q))
require.Equal(t, 1, q.Equal(r))
}
func TestMultiPairing(t *testing.T) {
const Tests = 10
e1 := new(Engine)
e2 := new(Engine)
g1s := make([]*G1, Tests)
g2s := make([]*G2, Tests)
sc := make([]*native.Field, Tests)
res := make([]*Gt, Tests)
expected := new(Gt).SetOne()
for i := 0; i < Tests; i++ {
var bytes [64]byte
g1s[i] = new(G1).Generator()
g2s[i] = new(G2).Generator()
sc[i] = Bls12381FqNew()
_, _ = crand.Read(bytes[:])
sc[i].SetBytesWide(&bytes)
if i&1 == 0 {
g1s[i].Mul(g1s[i], sc[i])
} else {
g2s[i].Mul(g2s[i], sc[i])
}
e1.AddPair(g1s[i], g2s[i])
e2.AddPair(g1s[i], g2s[i])
res[i] = e1.Result()
e1.Reset()
expected.Add(expected, res[i])
}
actual := e2.Result()
require.Equal(t, 1, expected.Equal(actual))
}
+388
View File
@@ -0,0 +1,388 @@
package native
import (
"encoding/binary"
"fmt"
"math/big"
"github.com/sonr-io/sonr/crypto/internal"
)
// FieldLimbs is the number of limbs needed to represent this field
const FieldLimbs = 4
// FieldBytes is the number of bytes needed to represent this field
const FieldBytes = 32
// WideFieldBytes is the number of bytes needed for safe conversion
// to this field to avoid bias when reduced
const WideFieldBytes = 64
// Field represents a field element
type Field struct {
// Value is the field elements value
Value [FieldLimbs]uint64
// Params are the field parameters
Params *FieldParams
// Arithmetic are the field methods
Arithmetic FieldArithmetic
}
// FieldParams are the field parameters
type FieldParams struct {
// R is 2^256 mod Modulus
R [FieldLimbs]uint64
// R2 is 2^512 mod Modulus
R2 [FieldLimbs]uint64
// R3 is 2^768 mod Modulus
R3 [FieldLimbs]uint64
// Modulus of the field
Modulus [FieldLimbs]uint64
// Modulus as big.Int
BiModulus *big.Int
}
// FieldArithmetic are the methods that can be done on a field
type FieldArithmetic interface {
// ToMontgomery converts this field to montgomery form
ToMontgomery(out, arg *[FieldLimbs]uint64)
// FromMontgomery converts this field from montgomery form
FromMontgomery(out, arg *[FieldLimbs]uint64)
// Neg performs modular negation
Neg(out, arg *[FieldLimbs]uint64)
// Square performs modular square
Square(out, arg *[FieldLimbs]uint64)
// Mul performs modular multiplication
Mul(out, arg1, arg2 *[FieldLimbs]uint64)
// Add performs modular addition
Add(out, arg1, arg2 *[FieldLimbs]uint64)
// Sub performs modular subtraction
Sub(out, arg1, arg2 *[FieldLimbs]uint64)
// Sqrt performs modular square root
Sqrt(wasSquare *int, out, arg *[FieldLimbs]uint64)
// Invert performs modular inverse
Invert(wasInverted *int, out, arg *[FieldLimbs]uint64)
// FromBytes converts a little endian byte array into a field element
FromBytes(out *[FieldLimbs]uint64, arg *[FieldBytes]byte)
// ToBytes converts a field element to a little endian byte array
ToBytes(out *[FieldBytes]byte, arg *[FieldLimbs]uint64)
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
Selectznz(out, arg1, arg2 *[FieldLimbs]uint64, choice int)
}
// Cmp returns -1 if f < rhs
// 0 if f == rhs
// 1 if f > rhs
func (f *Field) Cmp(rhs *Field) int {
return cmpHelper(&f.Value, &rhs.Value)
}
// cmpHelper returns -1 if lhs < rhs
// -1 if lhs == rhs
// 1 if lhs > rhs
// Public only for convenience for some internal implementations
func cmpHelper(lhs, rhs *[FieldLimbs]uint64) int {
gt := uint64(0)
lt := uint64(0)
for i := 3; i >= 0; i-- {
// convert to two 64-bit numbers where
// the leading bits are zeros and hold no meaning
// so rhs - fp actually means gt
// and fp - rhs actually means lt.
rhsH := rhs[i] >> 32
rhsL := rhs[i] & 0xffffffff
lhsH := lhs[i] >> 32
lhsL := lhs[i] & 0xffffffff
// Check the leading bit
// if negative then fp > rhs
// if positive then fp < rhs
gt |= (rhsH - lhsH) >> 32 & 1 &^ lt
lt |= (lhsH - rhsH) >> 32 & 1 &^ gt
gt |= (rhsL - lhsL) >> 32 & 1 &^ lt
lt |= (lhsL - rhsL) >> 32 & 1 &^ gt
}
// Make the result -1 for <, 0 for =, 1 for >
return int(gt) - int(lt)
}
// Equal returns 1 if f == rhs, 0 otherwise
func (f *Field) Equal(rhs *Field) int {
return equalHelper(&f.Value, &rhs.Value)
}
func equalHelper(lhs, rhs *[FieldLimbs]uint64) int {
t := lhs[0] ^ rhs[0]
t |= lhs[1] ^ rhs[1]
t |= lhs[2] ^ rhs[2]
t |= lhs[3] ^ rhs[3]
return int(((int64(t) | int64(-t)) >> 63) + 1)
}
// IsZero returns 1 if f == 0, 0 otherwise
func (f *Field) IsZero() int {
t := f.Value[0]
t |= f.Value[1]
t |= f.Value[2]
t |= f.Value[3]
return int(((int64(t) | int64(-t)) >> 63) + 1)
}
// IsNonZero returns 1 if f != 0, 0 otherwise
func (f *Field) IsNonZero() int {
t := f.Value[0]
t |= f.Value[1]
t |= f.Value[2]
t |= f.Value[3]
return int(-((int64(t) | int64(-t)) >> 63))
}
// IsOne returns 1 if f == 1, 0 otherwise
func (f *Field) IsOne() int {
return equalHelper(&f.Value, &f.Params.R)
}
// Set f = rhs
func (f *Field) Set(rhs *Field) *Field {
f.Value[0] = rhs.Value[0]
f.Value[1] = rhs.Value[1]
f.Value[2] = rhs.Value[2]
f.Value[3] = rhs.Value[3]
f.Params = rhs.Params
f.Arithmetic = rhs.Arithmetic
return f
}
// SetUint64 f = rhs
func (f *Field) SetUint64(rhs uint64) *Field {
t := &[FieldLimbs]uint64{rhs, 0, 0, 0}
f.Arithmetic.ToMontgomery(&f.Value, t)
return f
}
// SetOne f = r
func (f *Field) SetOne() *Field {
f.Value[0] = f.Params.R[0]
f.Value[1] = f.Params.R[1]
f.Value[2] = f.Params.R[2]
f.Value[3] = f.Params.R[3]
return f
}
// SetZero f = 0
func (f *Field) SetZero() *Field {
f.Value[0] = 0
f.Value[1] = 0
f.Value[2] = 0
f.Value[3] = 0
return f
}
// SetBytesWide takes 64 bytes as input and treats them as a 512-bit number.
// Attributed to https://github.com/zcash/pasta_curves/blob/main/src/fields/Fp.rs#L255
// We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits
// with the higher bits multiplied by 2^256. Thus, we perform two reductions
//
// 1. the lower bits are multiplied by r^2, as normal
// 2. the upper bits are multiplied by r^2 * 2^256 = r^3
//
// and computing their sum in the field. It remains to see that arbitrary 256-bit
// numbers can be placed into Montgomery form safely using the reduction. The
// reduction works so long as the product is less than r=2^256 multiplied by
// the modulus. This holds because for any `c` smaller than the modulus, we have
// that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the
// reduction always works so long as `c` is in the field; in this case it is either the
// constant `r2` or `r3`.
func (f *Field) SetBytesWide(input *[WideFieldBytes]byte) *Field {
d0 := [FieldLimbs]uint64{
binary.LittleEndian.Uint64(input[:8]),
binary.LittleEndian.Uint64(input[8:16]),
binary.LittleEndian.Uint64(input[16:24]),
binary.LittleEndian.Uint64(input[24:32]),
}
d1 := [FieldLimbs]uint64{
binary.LittleEndian.Uint64(input[32:40]),
binary.LittleEndian.Uint64(input[40:48]),
binary.LittleEndian.Uint64(input[48:56]),
binary.LittleEndian.Uint64(input[56:64]),
}
// f.Arithmetic.ToMontgomery(&d0, &d0)
// f.Arithmetic.Mul(&d1, &d1, &f.Params.R2)
// f.Arithmetic.Add(&f.Value, &d0, &d0)
// Convert to Montgomery form
tv1 := &[FieldLimbs]uint64{}
tv2 := &[FieldLimbs]uint64{}
// d0*r2 + d1*r3
f.Arithmetic.Mul(tv1, &d0, &f.Params.R2)
f.Arithmetic.Mul(tv2, &d1, &f.Params.R3)
f.Arithmetic.Add(&f.Value, tv1, tv2)
return f
}
// SetBytes attempts to convert a little endian byte representation
// of a scalar into a `Fp`, failing if input is not canonical
func (f *Field) SetBytes(input *[FieldBytes]byte) (*Field, error) {
d0 := [FieldLimbs]uint64{0, 0, 0, 0}
f.Arithmetic.FromBytes(&d0, input)
if cmpHelper(&d0, &f.Params.Modulus) != -1 {
return nil, fmt.Errorf("invalid byte sequence")
}
return f.SetLimbs(&d0), nil
}
// SetBigInt initializes an element from big.Int
// The value is reduced by the modulus
func (f *Field) SetBigInt(bi *big.Int) *Field {
var buffer [FieldBytes]byte
t := new(big.Int).Set(bi)
t.Mod(t, f.Params.BiModulus)
t.FillBytes(buffer[:])
copy(buffer[:], internal.ReverseScalarBytes(buffer[:]))
_, _ = f.SetBytes(&buffer)
return f
}
// SetRaw converts a raw array into a field element
// Assumes input is already in montgomery form
func (f *Field) SetRaw(input *[FieldLimbs]uint64) *Field {
f.Value[0] = input[0]
f.Value[1] = input[1]
f.Value[2] = input[2]
f.Value[3] = input[3]
return f
}
// SetLimbs converts an array into a field element
// by converting to montgomery form
func (f *Field) SetLimbs(input *[FieldLimbs]uint64) *Field {
f.Arithmetic.ToMontgomery(&f.Value, input)
return f
}
// Bytes converts this element into a byte representation
// in little endian byte order
func (f *Field) Bytes() [FieldBytes]byte {
var output [FieldBytes]byte
tv := &[FieldLimbs]uint64{}
f.Arithmetic.FromMontgomery(tv, &f.Value)
f.Arithmetic.ToBytes(&output, tv)
return output
}
// BigInt converts this element into the big.Int struct
func (f *Field) BigInt() *big.Int {
buffer := f.Bytes()
return new(big.Int).SetBytes(internal.ReverseScalarBytes(buffer[:]))
}
// Raw converts this element into the a [FieldLimbs]uint64
func (f *Field) Raw() [FieldLimbs]uint64 {
res := &[FieldLimbs]uint64{}
f.Arithmetic.FromMontgomery(res, &f.Value)
return *res
}
// Double this element
func (f *Field) Double(a *Field) *Field {
f.Arithmetic.Add(&f.Value, &a.Value, &a.Value)
return f
}
// Square this element
func (f *Field) Square(a *Field) *Field {
f.Arithmetic.Square(&f.Value, &a.Value)
return f
}
// Sqrt this element, if it exists. If true, then value
// is a square root. If false, value is a QNR
func (f *Field) Sqrt(a *Field) (*Field, bool) {
wasSquare := 0
f.Arithmetic.Sqrt(&wasSquare, &f.Value, &a.Value)
return f, wasSquare == 1
}
// Invert this element i.e. compute the multiplicative inverse
// return false, zero if this element is zero.
func (f *Field) Invert(a *Field) (*Field, bool) {
wasInverted := 0
f.Arithmetic.Invert(&wasInverted, &f.Value, &a.Value)
return f, wasInverted == 1
}
// Mul returns the result from multiplying this element by rhs
func (f *Field) Mul(lhs, rhs *Field) *Field {
f.Arithmetic.Mul(&f.Value, &lhs.Value, &rhs.Value)
return f
}
// Sub returns the result from subtracting rhs from this element
func (f *Field) Sub(lhs, rhs *Field) *Field {
f.Arithmetic.Sub(&f.Value, &lhs.Value, &rhs.Value)
return f
}
// Add returns the result from adding rhs to this element
func (f *Field) Add(lhs, rhs *Field) *Field {
f.Arithmetic.Add(&f.Value, &lhs.Value, &rhs.Value)
return f
}
// Neg returns negation of this element
func (f *Field) Neg(input *Field) *Field {
f.Arithmetic.Neg(&f.Value, &input.Value)
return f
}
// Exp raises base^exp
func (f *Field) Exp(base, exp *Field) *Field {
e := [FieldLimbs]uint64{}
f.Arithmetic.FromMontgomery(&e, &exp.Value)
Pow(&f.Value, &base.Value, &e, f.Params, f.Arithmetic)
return f
}
// CMove sets f = lhs if choice == 0 and f = rhs if choice == 1
func (f *Field) CMove(lhs, rhs *Field, choice int) *Field {
f.Arithmetic.Selectznz(&f.Value, &lhs.Value, &rhs.Value, choice)
return f
}
// Pow raises base^exp. The result is written to out.
// Public only for convenience for some internal implementations
func Pow(out, base, exp *[FieldLimbs]uint64, params *FieldParams, arithmetic FieldArithmetic) {
res := [FieldLimbs]uint64{params.R[0], params.R[1], params.R[2], params.R[3]}
tmp := [FieldLimbs]uint64{}
for i := len(exp) - 1; i >= 0; i-- {
for j := 63; j >= 0; j-- {
arithmetic.Square(&res, &res)
arithmetic.Mul(&tmp, &res, base)
arithmetic.Selectznz(&res, &res, &tmp, int(exp[i]>>j)&1)
}
}
out[0] = res[0]
out[1] = res[1]
out[2] = res[2]
out[3] = res[3]
}
// Pow2k raises arg to the power `2^k`. This result is written to out.
// Public only for convenience for some internal implementations
func Pow2k(out, arg *[FieldLimbs]uint64, k int, arithmetic FieldArithmetic) {
var t [FieldLimbs]uint64
t[0] = arg[0]
t[1] = arg[1]
t[2] = arg[2]
t[3] = arg[3]
for i := 0; i < k; i++ {
arithmetic.Square(&t, &t)
}
out[0] = t[0]
out[1] = t[1]
out[2] = t[2]
out[3] = t[3]
}
+107
View File
@@ -0,0 +1,107 @@
package native
import (
"hash"
"golang.org/x/crypto/sha3"
)
// OversizeDstSalt is the salt used to hash a dst over MaxDstLen
var OversizeDstSalt = []byte("H2C-OVERSIZE-DST-")
// MaxDstLen the max size for dst in hash to curve
const MaxDstLen = 255
func getDomainXmd(h hash.Hash, domain []byte) []byte {
var out []byte
if len(domain) > MaxDstLen {
h.Reset()
_, _ = h.Write(OversizeDstSalt)
_, _ = h.Write(domain)
out = h.Sum(nil)
} else {
out = domain
}
return out
}
func getDomainXof(h sha3.ShakeHash, domain []byte) []byte {
var out []byte
if len(domain) > MaxDstLen {
h.Reset()
_, _ = h.Write(OversizeDstSalt)
_, _ = h.Write(domain)
var tv [64]byte
_, _ = h.Read(tv[:])
out = tv[:]
} else {
out = domain
}
return out
}
// ExpandMsgXmd expands the msg with the domain to output a byte array
// with outLen in size using a fixed size hash.
// See https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-13#section-5.4.1
func ExpandMsgXmd(h *EllipticPointHasher, msg, domain []byte, outLen int) []byte {
domain = getDomainXmd(h.xmd, domain)
domainLen := byte(len(domain))
h.xmd.Reset()
// DST_prime = DST || I2OSP(len(DST), 1)
// b_0 = H(Z_pad || msg || l_i_b_str || I2OSP(0, 1) || DST_prime)
_, _ = h.xmd.Write(make([]byte, h.xmd.BlockSize()))
_, _ = h.xmd.Write(msg)
_, _ = h.xmd.Write([]byte{uint8(outLen >> 8), uint8(outLen)})
_, _ = h.xmd.Write([]byte{0})
_, _ = h.xmd.Write(domain)
_, _ = h.xmd.Write([]byte{domainLen})
b0 := h.xmd.Sum(nil)
// b_1 = H(b_0 || I2OSP(1, 1) || DST_prime)
h.xmd.Reset()
_, _ = h.xmd.Write(b0)
_, _ = h.xmd.Write([]byte{1})
_, _ = h.xmd.Write(domain)
_, _ = h.xmd.Write([]byte{domainLen})
b1 := h.xmd.Sum(nil)
// b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
ell := (outLen + h.xmd.Size() - 1) / h.xmd.Size()
bi := b1
out := make([]byte, outLen)
for i := 1; i < ell; i++ {
h.xmd.Reset()
// b_i = H(strxor(b_0, b_(i - 1)) || I2OSP(i, 1) || DST_prime)
tmp := make([]byte, h.xmd.Size())
for j := 0; j < h.xmd.Size(); j++ {
tmp[j] = b0[j] ^ bi[j]
}
_, _ = h.xmd.Write(tmp)
_, _ = h.xmd.Write([]byte{1 + uint8(i)})
_, _ = h.xmd.Write(domain)
_, _ = h.xmd.Write([]byte{domainLen})
// b_1 || ... || b_(ell - 1)
copy(out[(i-1)*h.xmd.Size():i*h.xmd.Size()], bi[:])
bi = h.xmd.Sum(nil)
}
// b_ell
copy(out[(ell-1)*h.xmd.Size():], bi[:])
return out[:outLen]
}
// ExpandMsgXof expands the msg with the domain to output a byte array
// with outLen in size using a xof hash
// See https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-13#section-5.4.2
func ExpandMsgXof(h *EllipticPointHasher, msg, domain []byte, outLen int) []byte {
domain = getDomainXof(h.xof, domain)
domainLen := byte(len(domain))
h.xof.Reset()
_, _ = h.xof.Write(msg)
_, _ = h.xof.Write([]byte{uint8(outLen >> 8), uint8(outLen)})
_, _ = h.xof.Write(domain)
_, _ = h.xof.Write([]byte{domainLen})
out := make([]byte, outLen)
_, _ = h.xof.Read(out)
return out
}
+62
View File
@@ -0,0 +1,62 @@
package native
// IsogenyParams are the parameters needed to map from an isogeny to the main curve
type IsogenyParams struct {
XNum [][FieldLimbs]uint64
XDen [][FieldLimbs]uint64
YNum [][FieldLimbs]uint64
YDen [][FieldLimbs]uint64
}
// Map from the isogeny curve to the main curve using the parameters
func (p *IsogenyParams) Map(xIn, yIn *Field) (x, y *Field) {
var xNum, xDen, yNum, yDen, tv [FieldLimbs]uint64
var wasInverted int
xnumL := len(p.XNum)
xdenL := len(p.XDen)
ynumL := len(p.YNum)
ydenL := len(p.YDen)
degree := 0
for _, i := range []int{xnumL, xdenL, ynumL, ydenL} {
if degree < i {
degree = i
}
}
xs := make([][FieldLimbs]uint64, degree)
xs[0] = xIn.Params.R // x[0] = x^0
xs[1] = xIn.Value // x[1] = x^1
xIn.Arithmetic.Square(&xs[2], &xIn.Value) // x[2] = x^2
for i := 3; i < degree; i++ {
// x[i] = x^i
xIn.Arithmetic.Mul(&xs[i], &xs[i-1], &xIn.Value)
}
computeIsoK(&xNum, &xs, &p.XNum, xIn.Arithmetic)
computeIsoK(&xDen, &xs, &p.XDen, xIn.Arithmetic)
computeIsoK(&yNum, &xs, &p.YNum, xIn.Arithmetic)
computeIsoK(&yDen, &xs, &p.YDen, xIn.Arithmetic)
xIn.Arithmetic.Invert(&wasInverted, &xDen, &xDen)
x = new(Field).Set(xIn)
xIn.Arithmetic.Mul(&tv, &xNum, &xDen)
xIn.Arithmetic.Selectznz(&x.Value, &x.Value, &tv, wasInverted)
yIn.Arithmetic.Invert(&wasInverted, &yDen, &yDen)
y = new(Field).Set(yIn)
yIn.Arithmetic.Mul(&tv, &yNum, &yDen)
yIn.Arithmetic.Selectznz(&y.Value, &y.Value, &tv, wasInverted)
yIn.Arithmetic.Mul(&y.Value, &y.Value, &yIn.Value)
return x, y
}
func computeIsoK(out *[FieldLimbs]uint64, xxs, k *[][FieldLimbs]uint64, f FieldArithmetic) {
var tv [FieldLimbs]uint64
for i := range *k {
f.Mul(&tv, &(*xxs)[i], &(*k)[i])
f.Add(out, out, &tv)
}
}
+207
View File
@@ -0,0 +1,207 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
"math/big"
"sync"
"github.com/sonr-io/sonr/crypto/core/curves/native"
)
var (
k256FpInitonce sync.Once
k256FpParams native.FieldParams
)
func K256FpNew() *native.Field {
return &native.Field{
Value: [native.FieldLimbs]uint64{},
Params: getK256FpParams(),
Arithmetic: k256FpArithmetic{},
}
}
func k256FpParamsInit() {
k256FpParams = native.FieldParams{
R: [native.FieldLimbs]uint64{
0x00000001000003d1,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
R2: [native.FieldLimbs]uint64{
0x000007a2000e90a1,
0x0000000000000001,
0x0000000000000000,
0x0000000000000000,
},
R3: [native.FieldLimbs]uint64{
0x002bb1e33795f671,
0x0000000100000b73,
0x0000000000000000,
0x0000000000000000,
},
Modulus: [native.FieldLimbs]uint64{
0xfffffffefffffc2f,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
},
BiModulus: new(big.Int).SetBytes([]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2f,
}),
}
}
func getK256FpParams() *native.FieldParams {
k256FpInitonce.Do(k256FpParamsInit)
return &k256FpParams
}
// k256FpArithmetic is a struct with all the methods needed for working
// in mod p
type k256FpArithmetic struct{}
// ToMontgomery converts this field to montgomery form
func (f k256FpArithmetic) ToMontgomery(out, arg *[native.FieldLimbs]uint64) {
ToMontgomery((*MontgomeryDomainFieldElement)(out), (*NonMontgomeryDomainFieldElement)(arg))
}
// FromMontgomery converts this field from montgomery form
func (f k256FpArithmetic) FromMontgomery(out, arg *[native.FieldLimbs]uint64) {
FromMontgomery((*NonMontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Neg performs modular negation
func (f k256FpArithmetic) Neg(out, arg *[native.FieldLimbs]uint64) {
Opp((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Square performs modular square
func (f k256FpArithmetic) Square(out, arg *[native.FieldLimbs]uint64) {
Square((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Mul performs modular multiplication
func (f k256FpArithmetic) Mul(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Mul(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Add performs modular addition
func (f k256FpArithmetic) Add(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Add(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Sub performs modular subtraction
func (f k256FpArithmetic) Sub(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Sub(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Sqrt performs modular square root
func (f k256FpArithmetic) Sqrt(wasSquare *int, out, arg *[native.FieldLimbs]uint64) {
// p is congruent to 3 mod 4 we can compute
// sqrt using elem^(p+1)/4 mod p
// 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffff0c
var s, t [native.FieldLimbs]uint64
params := getK256FpParams()
native.Pow(&s, arg, &[native.FieldLimbs]uint64{
0xffffffffbfffff0c,
0xffffffffffffffff,
0xffffffffffffffff,
0x3fffffffffffffff,
}, params, f)
f.Square(&t, &s)
tv1 := &native.Field{Value: t, Params: params, Arithmetic: f}
tv2 := &native.Field{Value: *arg, Params: params, Arithmetic: f}
*wasSquare = tv1.Equal(tv2)
f.Selectznz(out, out, &s, *wasSquare)
}
// Invert performs modular inverse
func (f k256FpArithmetic) Invert(wasInverted *int, out, arg *[native.FieldLimbs]uint64) {
// The binary representation of (p - 2) has 5 groups of 1s, with lengths in
// { 1, 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each group:
// [1], [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223]
var s, x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223 [native.FieldLimbs]uint64
native.Pow2k(&x2, arg, 1, f)
f.Mul(&x2, &x2, arg)
native.Pow2k(&x3, &x2, 1, f)
f.Mul(&x3, &x3, arg)
native.Pow2k(&x6, &x3, 3, f)
f.Mul(&x6, &x6, &x3)
native.Pow2k(&x9, &x6, 3, f)
f.Mul(&x9, &x9, &x3)
native.Pow2k(&x11, &x9, 2, f)
f.Mul(&x11, &x11, &x2)
native.Pow2k(&x22, &x11, 11, f)
f.Mul(&x22, &x22, &x11)
native.Pow2k(&x44, &x22, 22, f)
f.Mul(&x44, &x44, &x22)
native.Pow2k(&x88, &x44, 44, f)
f.Mul(&x88, &x88, &x44)
native.Pow2k(&x176, &x88, 88, f)
f.Mul(&x176, &x176, &x88)
native.Pow2k(&x220, &x176, 44, f)
f.Mul(&x220, &x220, &x44)
native.Pow2k(&x223, &x220, 3, f)
f.Mul(&x223, &x223, &x3)
// Use sliding window over the group
native.Pow2k(&s, &x223, 23, f)
f.Mul(&s, &s, &x22)
native.Pow2k(&s, &s, 5, f)
f.Mul(&s, &s, arg)
native.Pow2k(&s, &s, 3, f)
f.Mul(&s, &s, &x2)
native.Pow2k(&s, &s, 2, f)
f.Mul(&s, &s, arg)
tv := &native.Field{Value: *arg, Params: getK256FpParams(), Arithmetic: f}
*wasInverted = tv.IsNonZero()
f.Selectznz(out, out, &s, *wasInverted)
}
// FromBytes converts a little endian byte array into a field element
func (f k256FpArithmetic) FromBytes(out *[native.FieldLimbs]uint64, arg *[native.FieldBytes]byte) {
FromBytes(out, arg)
}
// ToBytes converts a field element to a little endian byte array
func (f k256FpArithmetic) ToBytes(out *[native.FieldBytes]byte, arg *[native.FieldLimbs]uint64) {
ToBytes(out, arg)
}
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f k256FpArithmetic) Selectznz(out, arg1, arg2 *[native.FieldLimbs]uint64, choice int) {
Selectznz(out, uint1(choice), arg1, arg2)
}
+330
View File
@@ -0,0 +1,330 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/internal"
)
func TestFpSetOne(t *testing.T) {
fp := K256FpNew().SetOne()
require.NotNil(t, fp)
require.Equal(t, fp.Value, getK256FpParams().R)
}
func TestFpSetUint64(t *testing.T) {
act := K256FpNew().SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act.Value[0], uint64(0x1000000000000000))
}
func TestFpAdd(t *testing.T) {
lhs := K256FpNew().SetOne()
rhs := K256FpNew().SetOne()
exp := K256FpNew().SetUint64(2)
res := K256FpNew().Add(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, res.Equal(exp), 1)
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := K256FpNew().Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpSub(t *testing.T) {
lhs := K256FpNew().SetOne()
rhs := K256FpNew().SetOne()
exp := K256FpNew().SetZero()
res := K256FpNew().Sub(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := K256FpNew().Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpMul(t *testing.T) {
lhs := K256FpNew().SetOne()
rhs := K256FpNew().SetOne()
exp := K256FpNew().SetOne()
res := K256FpNew().Mul(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := K256FpNew().Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpDouble(t *testing.T) {
a := K256FpNew().SetUint64(2)
e := K256FpNew().SetUint64(4)
require.Equal(t, e, K256FpNew().Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = K256FpNew().SetUint64(uint64(tv))
e = K256FpNew().SetUint64(ttv)
require.Equal(t, e, K256FpNew().Double(a))
}
}
func TestFpSquare(t *testing.T) {
a := K256FpNew().SetUint64(4)
e := K256FpNew().SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFpNeg(t *testing.T) {
a := K256FpNew().SetOne()
a.Neg(a)
e := K256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xfffffffdfffff85e, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff})
require.Equal(t, e, a)
}
func TestFpExp(t *testing.T) {
e := K256FpNew().SetUint64(8)
a := K256FpNew().SetUint64(2)
by := K256FpNew().SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFpSqrt(t *testing.T) {
t1 := K256FpNew().SetUint64(2)
t2 := K256FpNew().Neg(t1)
t3 := K256FpNew().Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.Equal(t, 1, t1.Equal(t3)|t2.Equal(t3))
t1.SetUint64(5)
_, wasSquare = K256FpNew().Sqrt(t1)
require.False(t, wasSquare)
}
func TestFpInvert(t *testing.T) {
twoInv := K256FpNew().SetLimbs(&[native.FieldLimbs]uint64{
0xffffffff7ffffe18,
0xffffffffffffffff,
0xffffffffffffffff,
0x7fffffffffffffff,
})
two := K256FpNew().SetUint64(2)
a, inverted := K256FpNew().Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
seven := K256FpNew().SetUint64(7)
sevenInv := K256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xdb6db6dab6db6afd, 0x6db6db6db6db6db6, 0xb6db6db6db6db6db, 0xdb6db6db6db6db6d})
a, inverted = K256FpNew().Invert(seven)
require.True(t, inverted)
require.Equal(t, a, sevenInv)
lhs := K256FpNew().SetUint64(9)
rhs := K256FpNew().SetUint64(3)
rhsInv, inverted := K256FpNew().Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, K256FpNew().Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = K256FpNew().Invert(rhs)
require.False(t, inverted)
}
func TestFpCMove(t *testing.T) {
t1 := K256FpNew().SetUint64(5)
t2 := K256FpNew().SetUint64(10)
require.Equal(t, t1, K256FpNew().CMove(t1, t2, 0))
require.Equal(t, t2, K256FpNew().CMove(t1, t2, 1))
}
func TestFpBytes(t *testing.T) {
t1 := K256FpNew().SetUint64(99)
seq := t1.Bytes()
t2, err := K256FpNew().SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFpCmp(t *testing.T) {
tests := []struct {
a *native.Field
b *native.Field
e int
}{
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{2731658267414164836, 14655288906067898431, 6537465423330262322, 8306191141697566219}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{6472764012681988529, 10848812988401906064, 2961825807536828898, 4282183981941645679}),
e: 1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{8023004109510539223, 4652004072850285717, 1877219145646046927, 383214385093921911}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{10099384440823804262, 16139476942229308465, 8636966320777393798, 5435928725024696785}),
e: -1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{3741840066202388211, 12165774400417314871, 16619312580230515379, 16195032234110087705}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{3905865991286066744, 543690822309071825, 17963103015950210055, 3745476720756119742}),
e: 1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{16660853697936147788, 7799793619412111108, 13515141085171033220, 2641079731236069032}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{17790588295388238399, 571847801379669440, 14537208974498222469, 12792570372087452754}),
e: -1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{3912839285384959186, 2701177075110484070, 6453856448115499033, 6475797457962597458}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{1282566391665688512, 13503640416992806563, 2962240104675990153, 3374904770947067689}),
e: 1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{5716631803409360103, 7859567470082614154, 12747956220853330146, 18434584096087315020}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{16317076441459028418, 12854146980376319601, 2258436689269031143, 9531877130792223752}),
e: 1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{17955191469941083403, 10350326247207200880, 17263512235150705075, 12700328451238078022}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{6767595547459644695, 7146403825494928147, 12269344038346710612, 9122477829383225603}),
e: 1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{17099388671847024438, 6426264987820696548, 10641143464957227405, 7709745403700754098}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{10799154372990268556, 17178492485719929374, 5705777922258988797, 8051037767683567782}),
e: -1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{4567139260680454325, 1629385880182139061, 16607020832317899145, 1261011562621553200}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{13487234491304534488, 17872642955936089265, 17651026784972590233, 9468934643333871559}),
e: -1,
},
{
a: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{18071070103467571798, 11787850505799426140, 10631355976141928593, 4867785203635092610}),
b: K256FpNew().SetRaw(&[native.FieldLimbs]uint64{12596443599426461624, 10176122686151524591, 17075755296887483439, 6726169532695070719}),
e: -1,
},
}
for _, test := range tests {
require.Equal(t, test.e, test.a.Cmp(test.b))
require.Equal(t, -test.e, test.b.Cmp(test.a))
require.Equal(t, 0, test.a.Cmp(test.a))
require.Equal(t, 0, test.b.Cmp(test.b))
}
}
func TestFpBigInt(t *testing.T) {
t1 := K256FpNew().SetBigInt(big.NewInt(9999))
t2 := K256FpNew().SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := K256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xc6c6c6c63939371d, 0xc6c6c6c6c6c6c6c6, 0x8d8d8dd28485081d, 0x8484848484848484})
b := new(
big.Int,
).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Value[0] = 0x39393938c6c6c512
e.Value[1] = 0x3939393939393939
e.Value[2] = 0x7272722d7b7af7e2
e.Value[3] = 0x7b7b7b7b7b7b7b7b
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFpSetBytesWide(t *testing.T) {
e := K256FpNew().SetRaw(&[native.FieldLimbs]uint64{0x6aa784623e2d641e, 0x7c40617d755bae27, 0x206b7be66ed7b71b, 0x6d1e4fc581e19dc2})
a := K256FpNew().SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
func TestFpSetBytesWideBigInt(t *testing.T) {
params := getK256FpParams()
var tv2 [64]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, params.BiModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a := K256FpNew().SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
File diff suppressed because it is too large Load Diff
+493
View File
@@ -0,0 +1,493 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
"math/big"
"sync"
"github.com/sonr-io/sonr/crypto/core/curves/native"
)
var (
k256FqInitonce sync.Once
k256FqParams native.FieldParams
)
func K256FqNew() *native.Field {
return &native.Field{
Value: [native.FieldLimbs]uint64{},
Params: getK256FqParams(),
Arithmetic: k256FqArithmetic{},
}
}
func k256FqParamsInit() {
k256FqParams = native.FieldParams{
R: [native.FieldLimbs]uint64{
0x402da1732fc9bebf,
0x4551231950b75fc4,
0x0000000000000001,
0x0000000000000000,
},
R2: [native.FieldLimbs]uint64{
0x896cf21467d7d140,
0x741496c20e7cf878,
0xe697f5e45bcd07c6,
0x9d671cd581c69bc5,
},
R3: [native.FieldLimbs]uint64{
0x7bc0cfe0e9ff41ed,
0x0017648444d4322c,
0xb1b31347f1d0b2da,
0x555d800c18ef116d,
},
Modulus: [native.FieldLimbs]uint64{
0xbfd25e8cd0364141,
0xbaaedce6af48a03b,
0xfffffffffffffffe,
0xffffffffffffffff,
},
BiModulus: new(big.Int).SetBytes([]byte{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41,
},
),
}
}
func getK256FqParams() *native.FieldParams {
k256FqInitonce.Do(k256FqParamsInit)
return &k256FqParams
}
// k256FqArithmetic is a struct with all the methods needed for working
// in mod q
type k256FqArithmetic struct{}
// ToMontgomery converts this field to montgomery form
func (f k256FqArithmetic) ToMontgomery(out, arg *[native.FieldLimbs]uint64) {
ToMontgomery((*MontgomeryDomainFieldElement)(out), (*NonMontgomeryDomainFieldElement)(arg))
}
// FromMontgomery converts this field from montgomery form
func (f k256FqArithmetic) FromMontgomery(out, arg *[native.FieldLimbs]uint64) {
FromMontgomery((*NonMontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Neg performs modular negation
func (f k256FqArithmetic) Neg(out, arg *[native.FieldLimbs]uint64) {
Opp((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Square performs modular square
func (f k256FqArithmetic) Square(out, arg *[native.FieldLimbs]uint64) {
Square((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Mul performs modular multiplication
func (f k256FqArithmetic) Mul(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Mul(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Add performs modular addition
func (f k256FqArithmetic) Add(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Add(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Sub performs modular subtraction
func (f k256FqArithmetic) Sub(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Sub(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Sqrt performs modular square root
func (f k256FqArithmetic) Sqrt(wasSquare *int, out, arg *[native.FieldLimbs]uint64) {
// See sqrt_ts_ct at
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.4
// c1 := 6
// c2 := (q - 1) / (2^c1)
// c2 := [4]uint64{
// 0xeeff497a3340d905,
// 0xfaeabb739abd2280,
// 0xffffffffffffffff,
// 0x03ffffffffffffff,
//}
// c3 := (c2 - 1) / 2
c3 := [native.FieldLimbs]uint64{
0x777fa4bd19a06c82,
0xfd755db9cd5e9140,
0xffffffffffffffff,
0x01ffffffffffffff,
}
// c4 := generator
// c5 := new(Fq).pow(generator, c2)
c5 := [native.FieldLimbs]uint64{
0x944cf2a220910e04,
0x815c829c780589f4,
0x55980b07bc222113,
0xc702b0d248825b36,
}
var z, t, b, c, tv [native.FieldLimbs]uint64
native.Pow(&z, arg, &c3, getK256FqParams(), f)
Square((*MontgomeryDomainFieldElement)(&t), (*MontgomeryDomainFieldElement)(&z))
Mul(
(*MontgomeryDomainFieldElement)(&t),
(*MontgomeryDomainFieldElement)(&t),
(*MontgomeryDomainFieldElement)(arg),
)
Mul(
(*MontgomeryDomainFieldElement)(&z),
(*MontgomeryDomainFieldElement)(&z),
(*MontgomeryDomainFieldElement)(arg),
)
copy(b[:], t[:])
copy(c[:], c5[:])
for i := s; i >= 2; i-- {
for j := 1; j <= i-2; j++ {
Square((*MontgomeryDomainFieldElement)(&b), (*MontgomeryDomainFieldElement)(&b))
}
// if b == 1 flag = 0 else flag = 1
flag := -(&native.Field{
Value: b,
Params: getK256FqParams(),
Arithmetic: f,
}).IsOne() + 1
Mul(
(*MontgomeryDomainFieldElement)(&tv),
(*MontgomeryDomainFieldElement)(&z),
(*MontgomeryDomainFieldElement)(&c),
)
Selectznz(&z, uint1(flag), &z, &tv)
Square((*MontgomeryDomainFieldElement)(&c), (*MontgomeryDomainFieldElement)(&c))
Mul(
(*MontgomeryDomainFieldElement)(&tv),
(*MontgomeryDomainFieldElement)(&t),
(*MontgomeryDomainFieldElement)(&c),
)
Selectznz(&t, uint1(flag), &t, &tv)
copy(b[:], t[:])
}
Square((*MontgomeryDomainFieldElement)(&c), (*MontgomeryDomainFieldElement)(&z))
*wasSquare = (&native.Field{
Value: c,
Params: getK256FqParams(),
Arithmetic: f,
}).Equal(&native.Field{
Value: *arg,
Params: getK256FqParams(),
Arithmetic: f,
})
Selectznz(out, uint1(*wasSquare), out, &z)
}
// Invert performs modular inverse
func (f k256FqArithmetic) Invert(wasInverted *int, out, arg *[native.FieldLimbs]uint64) {
// Using an addition chain from
// https://briansmith.org/ecc-inversion-addition-chains-01#secp256k1_scalar_inversion
var x1, x10, x11, x101, x111, x1001, x1011, x1101 [native.FieldLimbs]uint64
var x6, x8, x14, x28, x56, tmp [native.FieldLimbs]uint64
copy(x1[:], arg[:])
native.Pow2k(&x10, arg, 1, f)
Mul(
(*MontgomeryDomainFieldElement)(&x11),
(*MontgomeryDomainFieldElement)(&x10),
(*MontgomeryDomainFieldElement)(&x1),
)
Mul(
(*MontgomeryDomainFieldElement)(&x101),
(*MontgomeryDomainFieldElement)(&x10),
(*MontgomeryDomainFieldElement)(&x11),
)
Mul(
(*MontgomeryDomainFieldElement)(&x111),
(*MontgomeryDomainFieldElement)(&x10),
(*MontgomeryDomainFieldElement)(&x101),
)
Mul(
(*MontgomeryDomainFieldElement)(&x1001),
(*MontgomeryDomainFieldElement)(&x10),
(*MontgomeryDomainFieldElement)(&x111),
)
Mul(
(*MontgomeryDomainFieldElement)(&x1011),
(*MontgomeryDomainFieldElement)(&x10),
(*MontgomeryDomainFieldElement)(&x1001),
)
Mul(
(*MontgomeryDomainFieldElement)(&x1101),
(*MontgomeryDomainFieldElement)(&x10),
(*MontgomeryDomainFieldElement)(&x1011),
)
native.Pow2k(&x6, &x1101, 2, f)
Mul(
(*MontgomeryDomainFieldElement)(&x6),
(*MontgomeryDomainFieldElement)(&x6),
(*MontgomeryDomainFieldElement)(&x1011),
)
native.Pow2k(&x8, &x6, 2, f)
Mul(
(*MontgomeryDomainFieldElement)(&x8),
(*MontgomeryDomainFieldElement)(&x8),
(*MontgomeryDomainFieldElement)(&x11),
)
native.Pow2k(&x14, &x8, 6, f)
Mul(
(*MontgomeryDomainFieldElement)(&x14),
(*MontgomeryDomainFieldElement)(&x14),
(*MontgomeryDomainFieldElement)(&x6),
)
native.Pow2k(&x28, &x14, 14, f)
Mul(
(*MontgomeryDomainFieldElement)(&x28),
(*MontgomeryDomainFieldElement)(&x28),
(*MontgomeryDomainFieldElement)(&x14),
)
native.Pow2k(&x56, &x28, 28, f)
Mul(
(*MontgomeryDomainFieldElement)(&x56),
(*MontgomeryDomainFieldElement)(&x56),
(*MontgomeryDomainFieldElement)(&x28),
)
native.Pow2k(&tmp, &x56, 56, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x56),
)
native.Pow2k(&tmp, &tmp, 14, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x14),
)
native.Pow2k(&tmp, &tmp, 3, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x101),
)
native.Pow2k(&tmp, &tmp, 4, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x111),
)
native.Pow2k(&tmp, &tmp, 4, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x101),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1011),
)
native.Pow2k(&tmp, &tmp, 4, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1011),
)
native.Pow2k(&tmp, &tmp, 4, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x111),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x111),
)
native.Pow2k(&tmp, &tmp, 6, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1101),
)
native.Pow2k(&tmp, &tmp, 4, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x101),
)
native.Pow2k(&tmp, &tmp, 3, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x111),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1001),
)
native.Pow2k(&tmp, &tmp, 6, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x101),
)
native.Pow2k(&tmp, &tmp, 10, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x111),
)
native.Pow2k(&tmp, &tmp, 4, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x111),
)
native.Pow2k(&tmp, &tmp, 9, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x8),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1001),
)
native.Pow2k(&tmp, &tmp, 6, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1011),
)
native.Pow2k(&tmp, &tmp, 4, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1101),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x11),
)
native.Pow2k(&tmp, &tmp, 6, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1101),
)
native.Pow2k(&tmp, &tmp, 10, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1101),
)
native.Pow2k(&tmp, &tmp, 4, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1001),
)
native.Pow2k(&tmp, &tmp, 6, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1),
)
native.Pow2k(&tmp, &tmp, 8, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x6),
)
*wasInverted = (&native.Field{
Value: *arg,
Params: getK256FqParams(),
Arithmetic: f,
}).IsNonZero()
Selectznz(out, uint1(*wasInverted), out, &tmp)
}
// FromBytes converts a little endian byte array into a field element
func (f k256FqArithmetic) FromBytes(out *[native.FieldLimbs]uint64, arg *[native.FieldBytes]byte) {
FromBytes(out, arg)
}
// ToBytes converts a field element to a little endian byte array
func (f k256FqArithmetic) ToBytes(out *[native.FieldBytes]byte, arg *[native.FieldLimbs]uint64) {
ToBytes(out, arg)
}
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f k256FqArithmetic) Selectznz(out, arg1, arg2 *[native.FieldLimbs]uint64, choice int) {
Selectznz(out, uint1(choice), arg1, arg2)
}
// generator = 7 mod q is a generator of the `q - 1` order multiplicative
// subgroup, or in other words a primitive element of the field.
// generator^t where t * 2^s + 1 = q
var generator = &[native.FieldLimbs]uint64{
0xc13f6a264e843739,
0xe537f5b135039e5d,
0x0000000000000008,
0x0000000000000000,
}
// s satisfies the equation 2^s * t = q - 1 with t odd.
var s = 6
+330
View File
@@ -0,0 +1,330 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/internal"
)
func TestFqSetOne(t *testing.T) {
fq := K256FqNew().SetOne()
require.NotNil(t, fq)
require.Equal(t, fq.Value, getK256FqParams().R)
}
func TestFqSetUint64(t *testing.T) {
act := K256FqNew().SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act.Value[0], uint64(0xF000000000000000))
}
func TestFqAdd(t *testing.T) {
lhs := K256FqNew().SetOne()
rhs := K256FqNew().SetOne()
exp := K256FqNew().SetUint64(2)
res := K256FqNew().Add(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := K256FqNew().Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqSub(t *testing.T) {
lhs := K256FqNew().SetOne()
rhs := K256FqNew().SetOne()
exp := K256FqNew().SetZero()
res := K256FqNew().Sub(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := K256FqNew().Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqMul(t *testing.T) {
lhs := K256FqNew().SetOne()
rhs := K256FqNew().SetOne()
exp := K256FqNew().SetOne()
res := K256FqNew().Mul(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := K256FqNew().Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqDouble(t *testing.T) {
a := K256FqNew().SetUint64(2)
e := K256FqNew().SetUint64(4)
require.Equal(t, e, K256FqNew().Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = K256FqNew().SetUint64(uint64(tv))
e = K256FqNew().SetUint64(ttv)
require.Equal(t, e, K256FqNew().Double(a))
}
}
func TestFqSquare(t *testing.T) {
a := K256FqNew().SetUint64(4)
e := K256FqNew().SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFqNeg(t *testing.T) {
g := K256FqNew().SetRaw(generator)
a := K256FqNew().SetOne()
a.Neg(a)
e := K256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x7fa4bd19a06c8282, 0x755db9cd5e914077, 0xfffffffffffffffd, 0xffffffffffffffff})
require.Equal(t, e, a)
a.Neg(g)
e = K256FqNew().SetRaw(&[native.FieldLimbs]uint64{0xfe92f46681b20a08, 0xd576e7357a4501dd, 0xfffffffffffffff5, 0xffffffffffffffff})
require.Equal(t, e, a)
}
func TestFqExp(t *testing.T) {
e := K256FqNew().SetUint64(8)
a := K256FqNew().SetUint64(2)
by := K256FqNew().SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFqSqrt(t *testing.T) {
t1 := K256FqNew().SetUint64(2)
t2 := K256FqNew().Neg(t1)
t3 := K256FqNew().Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.Equal(t, 1, t1.Equal(t3)|t2.Equal(t3))
t1.SetUint64(5)
_, wasSquare = K256FqNew().Sqrt(t1)
require.False(t, wasSquare)
}
func TestFqInvert(t *testing.T) {
twoInv := K256FqNew().SetLimbs(&[native.FieldLimbs]uint64{0xdfe92f46681b20a1, 0x5d576e7357a4501d, 0xffffffffffffffff, 0x7fffffffffffffff})
two := K256FqNew().SetUint64(2)
a, inverted := K256FqNew().Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnity := K256FqNew().SetLimbs(&[native.FieldLimbs]uint64{0x8619a9e760c01d0c, 0xa883c4fba37998df, 0x45607580b6eabd98, 0xf252b002544b2f99})
rootOfUnityInv := K256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x7d99f8e21447e314, 0x5b60c477e7728d4c, 0xd78befc191f58654, 0x6897e5ff7824360f})
a, inverted = K256FqNew().Invert(rootOfUnity)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := K256FqNew().SetUint64(9)
rhs := K256FqNew().SetUint64(3)
rhsInv, inverted := K256FqNew().Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, K256FqNew().Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = K256FqNew().Invert(rhs)
require.False(t, inverted)
}
func TestFqCMove(t *testing.T) {
t1 := K256FqNew().SetUint64(5)
t2 := K256FqNew().SetUint64(10)
require.Equal(t, t1, K256FqNew().CMove(t1, t2, 0))
require.Equal(t, t2, K256FqNew().CMove(t1, t2, 1))
}
func TestFqBytes(t *testing.T) {
t1 := K256FqNew().SetUint64(99)
seq := t1.Bytes()
t2, err := K256FqNew().SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFqCmp(t *testing.T) {
tests := []struct {
a *native.Field
b *native.Field
e int
}{
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{2731658267414164836, 14655288906067898431, 6537465423330262322, 8306191141697566219}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{6472764012681988529, 10848812988401906064, 2961825807536828898, 4282183981941645679}),
e: 1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{8023004109510539223, 4652004072850285717, 1877219145646046927, 383214385093921911}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{10099384440823804262, 16139476942229308465, 8636966320777393798, 5435928725024696785}),
e: -1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{3741840066202388211, 12165774400417314871, 16619312580230515379, 16195032234110087705}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{3905865991286066744, 543690822309071825, 17963103015950210055, 3745476720756119742}),
e: 1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{16660853697936147788, 7799793619412111108, 13515141085171033220, 2641079731236069032}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{17790588295388238399, 571847801379669440, 14537208974498222469, 12792570372087452754}),
e: -1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{3912839285384959186, 2701177075110484070, 6453856448115499033, 6475797457962597458}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{1282566391665688512, 13503640416992806563, 2962240104675990153, 3374904770947067689}),
e: 1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{5716631803409360103, 7859567470082614154, 12747956220853330146, 18434584096087315020}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{16317076441459028418, 12854146980376319601, 2258436689269031143, 9531877130792223752}),
e: 1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{17955191469941083403, 10350326247207200880, 17263512235150705075, 12700328451238078022}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{6767595547459644695, 7146403825494928147, 12269344038346710612, 9122477829383225603}),
e: 1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{17099388671847024438, 6426264987820696548, 10641143464957227405, 7709745403700754098}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{10799154372990268556, 17178492485719929374, 5705777922258988797, 8051037767683567782}),
e: -1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{4567139260680454325, 1629385880182139061, 16607020832317899145, 1261011562621553200}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{13487234491304534488, 17872642955936089265, 17651026784972590233, 9468934643333871559}),
e: -1,
},
{
a: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{18071070103467571798, 11787850505799426140, 10631355976141928593, 4867785203635092610}),
b: K256FqNew().SetRaw(&[native.FieldLimbs]uint64{12596443599426461624, 10176122686151524591, 17075755296887483439, 6726169532695070719}),
e: -1,
},
}
for _, test := range tests {
require.Equal(t, test.e, test.a.Cmp(test.b))
require.Equal(t, -test.e, test.b.Cmp(test.a))
require.Equal(t, 0, test.a.Cmp(test.a))
require.Equal(t, 0, test.b.Cmp(test.b))
}
}
func TestFqBigInt(t *testing.T) {
t1 := K256FqNew().SetBigInt(big.NewInt(9999))
t2 := K256FqNew().SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := K256FqNew().SetRaw(&[native.FieldLimbs]uint64{0xa764d3f6f152f222, 0x3b5dc8aacb9297b7, 0xb015fa9d2b3efdc6, 0x567360cef000f24a})
b := new(
big.Int,
).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Value[0] = 0x186d8a95dee34f1f
e.Value[1] = 0x7f51143be3b60884
e.Value[2] = 0x4fea0562d4c10238
e.Value[3] = 0xa98c9f310fff0db5
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFqSetBytesWide(t *testing.T) {
e := K256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x70620a92b4f2eb7a, 0xd04588eb9c228a3a, 0xccb71ae40a10491c, 0x61cf39d70a8b33b7})
a := K256FqNew().SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
func TestFpSetBytesWideBigInt(t *testing.T) {
params := getK256FqParams()
var tv2 [64]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, params.BiModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a := K256FqNew().SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
File diff suppressed because it is too large Load Diff
+475
View File
@@ -0,0 +1,475 @@
package k256
import (
"sync"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/core/curves/native/k256/fp"
"github.com/sonr-io/sonr/crypto/internal"
)
var (
k256PointInitonce sync.Once
k256PointParams native.EllipticPointParams
k256PointSswuInitOnce sync.Once
k256PointSswuParams native.SswuParams
k256PointIsogenyInitOnce sync.Once
k256PointIsogenyParams native.IsogenyParams
)
func K256PointNew() *native.EllipticPoint {
return &native.EllipticPoint{
X: fp.K256FpNew(),
Y: fp.K256FpNew(),
Z: fp.K256FpNew(),
Params: getK256PointParams(),
Arithmetic: &k256PointArithmetic{},
}
}
func k256PointParamsInit() {
k256PointParams = native.EllipticPointParams{
A: fp.K256FpNew(),
B: fp.K256FpNew().SetUint64(7),
Gx: fp.K256FpNew().SetLimbs(&[native.FieldLimbs]uint64{
0x59f2815b16f81798,
0x029bfcdb2dce28d9,
0x55a06295ce870b07,
0x79be667ef9dcbbac,
}),
Gy: fp.K256FpNew().SetLimbs(&[native.FieldLimbs]uint64{
0x9c47d08ffb10d4b8,
0xfd17b448a6855419,
0x5da4fbfc0e1108a8,
0x483ada7726a3c465,
}),
BitSize: 256,
Name: "secp256k1",
}
}
func getK256PointParams() *native.EllipticPointParams {
k256PointInitonce.Do(k256PointParamsInit)
return &k256PointParams
}
func getK256PointSswuParams() *native.SswuParams {
k256PointSswuInitOnce.Do(k256PointSswuParamsInit)
return &k256PointSswuParams
}
func k256PointSswuParamsInit() {
// Taken from https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#section-8.7
//params := btcec.S256().Params()
//
//// c1 = (q - 3) / 4
//c1 := new(big.Int).Set(params.P)
//c1.Sub(c1, big.NewInt(3))
//c1.Rsh(c1, 2)
//
//a, _ := new(big.Int).SetString("3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533", 16)
//b := big.NewInt(1771)
//z := big.NewInt(-11)
//z.Mod(z, params.P)
//// sqrt(-z^3)
//zTmp := new(big.Int).Exp(z, big.NewInt(3), nil)
//zTmp = zTmp.Neg(zTmp)
//zTmp.Mod(zTmp, params.P)
//c2 := new(big.Int).ModSqrt(zTmp, params.P)
//
//var tBytes [32]byte
//c1.FillBytes(tBytes[:])
//newC1 := [native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(tBytes[24:32]),
// binary.BigEndian.Uint64(tBytes[16:24]),
// binary.BigEndian.Uint64(tBytes[8:16]),
// binary.BigEndian.Uint64(tBytes[:8]),
//}
//fp.K256FpNew().Arithmetic.ToMontgomery(&newC1, &newC1)
//c2.FillBytes(tBytes[:])
//newC2 := [native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(tBytes[24:32]),
// binary.BigEndian.Uint64(tBytes[16:24]),
// binary.BigEndian.Uint64(tBytes[8:16]),
// binary.BigEndian.Uint64(tBytes[:8]),
//}
//fp.K256FpNew().Arithmetic.ToMontgomery(&newC2, &newC2)
//a.FillBytes(tBytes[:])
//newA := [native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(tBytes[24:32]),
// binary.BigEndian.Uint64(tBytes[16:24]),
// binary.BigEndian.Uint64(tBytes[8:16]),
// binary.BigEndian.Uint64(tBytes[:8]),
//}
//fp.K256FpNew().Arithmetic.ToMontgomery(&newA, &newA)
//b.FillBytes(tBytes[:])
//newB := [native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(tBytes[24:32]),
// binary.BigEndian.Uint64(tBytes[16:24]),
// binary.BigEndian.Uint64(tBytes[8:16]),
// binary.BigEndian.Uint64(tBytes[:8]),
//}
//fp.K256FpNew().Arithmetic.ToMontgomery(&newB, &newB)
//z.FillBytes(tBytes[:])
//newZ := [native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(tBytes[24:32]),
// binary.BigEndian.Uint64(tBytes[16:24]),
// binary.BigEndian.Uint64(tBytes[8:16]),
// binary.BigEndian.Uint64(tBytes[:8]),
//}
//fp.K256FpNew().Arithmetic.ToMontgomery(&newZ, &newZ)
k256PointSswuParams = native.SswuParams{
// (q -3) // 4
C1: [native.FieldLimbs]uint64{
0xffffffffbfffff0b,
0xffffffffffffffff,
0xffffffffffffffff,
0x3fffffffffffffff,
},
// sqrt(-z^3)
C2: [native.FieldLimbs]uint64{
0x5b57ba53a30d1520,
0x908f7cef34a762eb,
0x190b0ffe068460c8,
0x98a9828e8f00ff62,
},
// 0x3f8731abdd661adca08a5558f0f5d272e953d363cb6f0e5d405447c01a444533
A: [native.FieldLimbs]uint64{
0xdb714ce7b18444a1,
0x4458ce38a32a19a2,
0xa0e58ae2837bfbf0,
0x505aabc49336d959,
},
// 1771
B: [native.FieldLimbs]uint64{
0x000006eb001a66db,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
// -11
Z: [native.FieldLimbs]uint64{
0xfffffff3ffffd234,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
},
}
}
func k256PointIsogenyInit() {
k256PointIsogenyParams = native.IsogenyParams{
XNum: [][native.FieldLimbs]uint64{
{
0x0000003b1c72a8b4,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
{
0xd5bd51a17b2edf46,
0x2cc06f7c86b86bcd,
0x50b37e74f3294a00,
0xeb32314a9da73679,
},
{
0x48c18b1b0d2191bd,
0x5a3f74c29bfccce3,
0xbe55a02e5e8bd357,
0x09bf218d11fff905,
},
{
0x000000001c71c789,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
},
XDen: [][native.FieldLimbs]uint64{
{
0x8af79c1ffdf1e7fa,
0xb84bc22235735eb5,
0x82ee5655a55ace04,
0xce4b32dea0a2becb,
},
{
0x8ecde3f3762e1fa5,
0x2c3b1ad77be333fd,
0xb102a1a152ea6e12,
0x57b82df5a1ffc133,
},
{
0x00000001000003d1,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
},
YNum: [][native.FieldLimbs]uint64{
{
0xffffffce425e12c3,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
},
{
0xba60d5fd6e56922e,
0x4ec198c898a435f2,
0x27e77a577b9764ab,
0xb3b80a1197651d12,
},
{
0xa460c58d0690c6f6,
0xad1fba614dfe6671,
0xdf2ad0172f45e9ab,
0x84df90c688fffc82,
},
{
0x00000000097b4283,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
},
YDen: [][native.FieldLimbs]uint64{
{
0xfffffd0afff4b6fb,
0xffffffffffffffff,
0xffffffffffffffff,
0xffffffffffffffff,
},
{
0xa0e6d461f9d5bf90,
0x28e34666a05a1c20,
0x88cb0300f0106a0e,
0x6ae1989be1e83c62,
},
{
0x5634d5edb1453160,
0x4258a84339d4cdfc,
0x8983f271fc5fa51b,
0x039444f072ffa1cd,
},
{
0x00000001000003d1,
0x0000000000000000,
0x0000000000000000,
0x0000000000000000,
},
},
}
}
func getK256PointIsogenyParams() *native.IsogenyParams {
k256PointIsogenyInitOnce.Do(k256PointIsogenyInit)
return &k256PointIsogenyParams
}
type k256PointArithmetic struct{}
func (k k256PointArithmetic) Hash(
out *native.EllipticPoint,
hash *native.EllipticPointHasher,
msg, dst []byte,
) error {
var u []byte
sswuParams := getK256PointSswuParams()
isoParams := getK256PointIsogenyParams()
switch hash.Type() {
case native.XMD:
u = native.ExpandMsgXmd(hash, msg, dst, 96)
case native.XOF:
u = native.ExpandMsgXof(hash, msg, dst, 96)
}
var buf [64]byte
copy(buf[:48], internal.ReverseScalarBytes(u[:48]))
u0 := fp.K256FpNew().SetBytesWide(&buf)
copy(buf[:48], internal.ReverseScalarBytes(u[48:]))
u1 := fp.K256FpNew().SetBytesWide(&buf)
r0x, r0y := sswuParams.Osswu3mod4(u0)
r1x, r1y := sswuParams.Osswu3mod4(u1)
q0x, q0y := isoParams.Map(r0x, r0y)
q1x, q1y := isoParams.Map(r1x, r1y)
out.X = q0x
out.Y = q0y
out.Z.SetOne()
tv := &native.EllipticPoint{
X: q1x,
Y: q1y,
Z: fp.K256FpNew().SetOne(),
}
k.Add(out, out, tv)
return nil
}
func (k k256PointArithmetic) Double(out, arg *native.EllipticPoint) {
// Addition formula from Renes-Costello-Batina 2015
// (https://eprint.iacr.org/2015/1060 Algorithm 9)
var yy, zz, xy2, bzz, bzz3, bzz9 [native.FieldLimbs]uint64
var yyMBzz9, yyPBzz3, yyzz, yyzz8, t [native.FieldLimbs]uint64
var x, y, z [native.FieldLimbs]uint64
f := arg.X.Arithmetic
f.Square(&yy, &arg.Y.Value)
f.Square(&zz, &arg.Z.Value)
f.Mul(&xy2, &arg.X.Value, &arg.Y.Value)
f.Add(&xy2, &xy2, &xy2)
f.Mul(&bzz, &zz, &arg.Params.B.Value)
f.Add(&bzz3, &bzz, &bzz)
f.Add(&bzz3, &bzz3, &bzz)
f.Add(&bzz9, &bzz3, &bzz3)
f.Add(&bzz9, &bzz9, &bzz3)
f.Neg(&yyMBzz9, &bzz9)
f.Add(&yyMBzz9, &yyMBzz9, &yy)
f.Add(&yyPBzz3, &yy, &bzz3)
f.Mul(&yyzz, &yy, &zz)
f.Add(&yyzz8, &yyzz, &yyzz)
f.Add(&yyzz8, &yyzz8, &yyzz8)
f.Add(&yyzz8, &yyzz8, &yyzz8)
f.Add(&t, &yyzz8, &yyzz8)
f.Add(&t, &t, &yyzz8)
f.Mul(&t, &t, &arg.Params.B.Value)
f.Mul(&x, &xy2, &yyMBzz9)
f.Mul(&y, &yyMBzz9, &yyPBzz3)
f.Add(&y, &y, &t)
f.Mul(&z, &yy, &arg.Y.Value)
f.Mul(&z, &z, &arg.Z.Value)
f.Add(&z, &z, &z)
f.Add(&z, &z, &z)
f.Add(&z, &z, &z)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
}
func (k k256PointArithmetic) Add(out, arg1, arg2 *native.EllipticPoint) {
// Addition formula from Renes-Costello-Batina 2015
// (https://eprint.iacr.org/2015/1060 Algorithm 7).
var xx, yy, zz, nXxYy, nYyZz, nXxZz [native.FieldLimbs]uint64
var tv1, tv2, xyPairs, yzPairs, xzPairs [native.FieldLimbs]uint64
var bzz, bzz3, yyMBzz3, yyPBzz3, byz [native.FieldLimbs]uint64
var byz3, xx3, bxx9, x, y, z [native.FieldLimbs]uint64
f := arg1.X.Arithmetic
f.Mul(&xx, &arg1.X.Value, &arg2.X.Value)
f.Mul(&yy, &arg1.Y.Value, &arg2.Y.Value)
f.Mul(&zz, &arg1.Z.Value, &arg2.Z.Value)
f.Add(&nXxYy, &xx, &yy)
f.Neg(&nXxYy, &nXxYy)
f.Add(&nYyZz, &yy, &zz)
f.Neg(&nYyZz, &nYyZz)
f.Add(&nXxZz, &xx, &zz)
f.Neg(&nXxZz, &nXxZz)
f.Add(&tv1, &arg1.X.Value, &arg1.Y.Value)
f.Add(&tv2, &arg2.X.Value, &arg2.Y.Value)
f.Mul(&xyPairs, &tv1, &tv2)
f.Add(&xyPairs, &xyPairs, &nXxYy)
f.Add(&tv1, &arg1.Y.Value, &arg1.Z.Value)
f.Add(&tv2, &arg2.Y.Value, &arg2.Z.Value)
f.Mul(&yzPairs, &tv1, &tv2)
f.Add(&yzPairs, &yzPairs, &nYyZz)
f.Add(&tv1, &arg1.X.Value, &arg1.Z.Value)
f.Add(&tv2, &arg2.X.Value, &arg2.Z.Value)
f.Mul(&xzPairs, &tv1, &tv2)
f.Add(&xzPairs, &xzPairs, &nXxZz)
f.Mul(&bzz, &zz, &arg1.Params.B.Value)
f.Add(&bzz3, &bzz, &bzz)
f.Add(&bzz3, &bzz3, &bzz)
f.Neg(&yyMBzz3, &bzz3)
f.Add(&yyMBzz3, &yyMBzz3, &yy)
f.Add(&yyPBzz3, &yy, &bzz3)
f.Mul(&byz, &yzPairs, &arg1.Params.B.Value)
f.Add(&byz3, &byz, &byz)
f.Add(&byz3, &byz3, &byz)
f.Add(&xx3, &xx, &xx)
f.Add(&xx3, &xx3, &xx)
f.Add(&bxx9, &xx3, &xx3)
f.Add(&bxx9, &bxx9, &xx3)
f.Mul(&bxx9, &bxx9, &arg1.Params.B.Value)
f.Mul(&tv1, &xyPairs, &yyMBzz3)
f.Mul(&tv2, &byz3, &xzPairs)
f.Neg(&tv2, &tv2)
f.Add(&x, &tv1, &tv2)
f.Mul(&tv1, &yyPBzz3, &yyMBzz3)
f.Mul(&tv2, &bxx9, &xzPairs)
f.Add(&y, &tv1, &tv2)
f.Mul(&tv1, &yzPairs, &yyPBzz3)
f.Mul(&tv2, &xx3, &xyPairs)
f.Add(&z, &tv1, &tv2)
e1 := arg1.Z.IsZero()
e2 := arg2.Z.IsZero()
// If arg1 is identity set it to arg2
f.Selectznz(&z, &z, &arg2.Z.Value, e1)
f.Selectznz(&y, &y, &arg2.Y.Value, e1)
f.Selectznz(&x, &x, &arg2.X.Value, e1)
// If arg2 is identity set it to arg1
f.Selectznz(&z, &z, &arg1.Z.Value, e2)
f.Selectznz(&y, &y, &arg1.Y.Value, e2)
f.Selectznz(&x, &x, &arg1.X.Value, e2)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
}
func (k k256PointArithmetic) IsOnCurve(arg *native.EllipticPoint) bool {
affine := K256PointNew()
k.ToAffine(affine, arg)
lhs := fp.K256FpNew().Square(affine.Y)
rhs := fp.K256FpNew()
k.RhsEq(rhs, affine.X)
return lhs.Equal(rhs) == 1
}
func (k k256PointArithmetic) ToAffine(out, arg *native.EllipticPoint) {
var wasInverted int
var zero, x, y, z [native.FieldLimbs]uint64
f := arg.X.Arithmetic
f.Invert(&wasInverted, &z, &arg.Z.Value)
f.Mul(&x, &arg.X.Value, &z)
f.Mul(&y, &arg.Y.Value, &z)
out.Z.SetOne()
// If point at infinity this does nothing
f.Selectznz(&x, &zero, &x, wasInverted)
f.Selectznz(&y, &zero, &y, wasInverted)
f.Selectznz(&z, &zero, &out.Z.Value, wasInverted)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
out.Params = arg.Params
out.Arithmetic = arg.Arithmetic
}
func (k k256PointArithmetic) RhsEq(out, x *native.Field) {
// Elliptic curve equation for secp256k1 is: y^2 = x^3 + 7
out.Square(x)
out.Mul(out, x)
out.Add(out, getK256PointParams().B)
}
+19
View File
@@ -0,0 +1,19 @@
package k256_test
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/core/curves/native/k256"
)
func TestK256PointArithmetic_Hash(t *testing.T) {
var b [32]byte
sc, err := k256.K256PointNew().Hash(b[:], native.EllipticPointHasherSha256())
require.NoError(t, err)
require.True(t, !sc.IsIdentity())
require.True(t, sc.IsOnCurve())
}
+82
View File
@@ -0,0 +1,82 @@
package native
// SswuParams for computing the Simplified SWU mapping
// for hash to curve implementations
type SswuParams struct {
C1, C2, A, B, Z [FieldLimbs]uint64
}
// Osswu3mod4 computes the simplified map optmized for 3 mod 4 primes
// https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-11#appendix-G.2.1
func (p *SswuParams) Osswu3mod4(u *Field) (x, y *Field) {
var tv1, tv2, tv3, tv4, xd, x1n, x2n, gxd, gx1, aNeg, zA, y1, y2 [FieldLimbs]uint64
var wasInverted int
u.Arithmetic.Mul(&tv1, &u.Value, &u.Value) // tv1 = u^2
u.Arithmetic.Mul(&tv3, &p.Z, &tv1) // tv3 = z * tv1
u.Arithmetic.Square(&tv2, &tv3) // tv2 = tv3^2
u.Arithmetic.Add(&xd, &tv2, &tv3) // xd = tv2 + tv3
u.Arithmetic.Add(&x1n, &u.Params.R, &xd) // x1n = (xd + 1)
u.Arithmetic.Mul(&x1n, &x1n, &p.B) // x1n * B
u.Arithmetic.Neg(&aNeg, &p.A)
u.Arithmetic.Mul(&xd, &xd, &aNeg) // xd = -A * xd
xdIsZero := (&Field{
Value: xd,
}).IsZero()
u.Arithmetic.Mul(&zA, &p.Z, &p.A)
u.Arithmetic.Selectznz(&xd, &xd, &zA, xdIsZero) // xd = z * A if xd == 0
u.Arithmetic.Square(&tv2, &xd) // tv2 = xd^2
u.Arithmetic.Mul(&gxd, &tv2, &xd) // gxd = tv2 * xd
u.Arithmetic.Mul(&tv2, &tv2, &p.A) // tv2 = A * tv2
u.Arithmetic.Square(&gx1, &x1n) // gx1 = x1n^2
u.Arithmetic.Add(&gx1, &gx1, &tv2) // gx1 = gx1 + tv2
u.Arithmetic.Mul(&gx1, &gx1, &x1n) // gx1 = gx1 * x1n
u.Arithmetic.Mul(&tv2, &gxd, &p.B) // tv2 = B * gxd
u.Arithmetic.Add(&gx1, &gx1, &tv2) // gx1 = gx1 + tv2
u.Arithmetic.Square(&tv4, &gxd) // tv4 = gxd^2
u.Arithmetic.Mul(&tv2, &gx1, &gxd) // tv2 = gx1 * gxd
u.Arithmetic.Mul(&tv4, &tv4, &tv2) // tv4 = tv4 * tv2
Pow(&y1, &tv4, &p.C1, u.Params, u.Arithmetic) // y1 = tv4^C1
u.Arithmetic.Mul(&y1, &y1, &tv2) // y1 = y1 * tv2
u.Arithmetic.Mul(&x2n, &tv3, &x1n) // x2n = tv3 * x1n
u.Arithmetic.Mul(&y2, &y1, &p.C2) // y2 = y1 * c2
u.Arithmetic.Mul(&y2, &y2, &tv1) // y2 = y2 * tv1
u.Arithmetic.Mul(&y2, &y2, &u.Value) // y2 = y2 * u
u.Arithmetic.Square(&tv2, &y1) // tv2 = y1^2
u.Arithmetic.Mul(&tv2, &tv2, &gxd) // tv2 = tv2 * gxd
e2 := (&Field{Value: tv2}).Equal(&Field{Value: gx1})
x = new(Field).Set(u)
y = new(Field).Set(u)
// If e2, x = x1, else x = x2
u.Arithmetic.Selectznz(&x.Value, &x2n, &x1n, e2)
// xn / xd
u.Arithmetic.Invert(&wasInverted, &tv1, &xd)
u.Arithmetic.Mul(&tv1, &x.Value, &tv1)
u.Arithmetic.Selectznz(&x.Value, &x.Value, &tv1, wasInverted)
// If e2, y = y1, else y = y2
u.Arithmetic.Selectznz(&y.Value, &y2, &y1, e2)
uBytes := u.Bytes()
yBytes := y.Bytes()
usign := uBytes[0] & 1
ysign := yBytes[0] & 1
// Fix sign of y
if usign != ysign {
y.Neg(y)
}
return x, y
}
+206
View File
@@ -0,0 +1,206 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
"math/big"
"sync"
"github.com/sonr-io/sonr/crypto/core/curves/native"
)
var (
p256FpInitonce sync.Once
p256FpParams native.FieldParams
)
func P256FpNew() *native.Field {
return &native.Field{
Value: [native.FieldLimbs]uint64{},
Params: getP256FpParams(),
Arithmetic: p256FpArithmetic{},
}
}
func p256FpParamsInit() {
// See FIPS 186-3, section D.2.3
p256FpParams = native.FieldParams{
R: [native.FieldLimbs]uint64{
0x0000000000000001,
0xffffffff00000000,
0xffffffffffffffff,
0x00000000fffffffe,
},
R2: [native.FieldLimbs]uint64{
0x0000000000000003,
0xfffffffbffffffff,
0xfffffffffffffffe,
0x00000004fffffffd,
},
R3: [native.FieldLimbs]uint64{
0xfffffffd0000000a,
0xffffffedfffffff7,
0x00000005fffffffc,
0x0000001800000001,
},
Modulus: [native.FieldLimbs]uint64{
0xffffffffffffffff,
0x00000000ffffffff,
0x0000000000000000,
0xffffffff00000001,
},
BiModulus: new(big.Int).SetBytes([]byte{
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
}),
}
}
func getP256FpParams() *native.FieldParams {
p256FpInitonce.Do(p256FpParamsInit)
return &p256FpParams
}
// p256FpArithmetic is a struct with all the methods needed for working
// in mod q
type p256FpArithmetic struct{}
// ToMontgomery converts this field to montgomery form
func (f p256FpArithmetic) ToMontgomery(out, arg *[native.FieldLimbs]uint64) {
ToMontgomery((*MontgomeryDomainFieldElement)(out), (*NonMontgomeryDomainFieldElement)(arg))
}
// FromMontgomery converts this field from montgomery form
func (f p256FpArithmetic) FromMontgomery(out, arg *[native.FieldLimbs]uint64) {
FromMontgomery((*NonMontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Neg performs modular negation
func (f p256FpArithmetic) Neg(out, arg *[native.FieldLimbs]uint64) {
Opp((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Square performs modular square
func (f p256FpArithmetic) Square(out, arg *[native.FieldLimbs]uint64) {
Square((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Mul performs modular multiplication
func (f p256FpArithmetic) Mul(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Mul(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Add performs modular addition
func (f p256FpArithmetic) Add(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Add(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Sub performs modular subtraction
func (f p256FpArithmetic) Sub(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Sub(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Sqrt performs modular square root
func (f p256FpArithmetic) Sqrt(wasSquare *int, out, arg *[native.FieldLimbs]uint64) {
// Use p = 3 mod 4 by Euler's criterion means
// arg^((p+1)/4 mod p
var t, c [native.FieldLimbs]uint64
c1 := [native.FieldLimbs]uint64{
0x0000_0000_0000_0000,
0x0000_0000_4000_0000,
0x4000_0000_0000_0000,
0x3fff_ffff_c000_0000,
}
native.Pow(&t, arg, &c1, getP256FpParams(), f)
Square((*MontgomeryDomainFieldElement)(&c), (*MontgomeryDomainFieldElement)(&t))
*wasSquare = (&native.Field{Value: c, Params: getP256FpParams(), Arithmetic: f}).Equal(
&native.Field{
Value: *arg, Params: getP256FpParams(), Arithmetic: f,
},
)
Selectznz(out, uint1(*wasSquare), out, &t)
}
// Invert performs modular inverse
func (f p256FpArithmetic) Invert(wasInverted *int, out, arg *[native.FieldLimbs]uint64) {
// Fermat's Little Theorem
// a ^ (p - 2) mod p
//
// The exponent pattern (from high to low) is:
// - 32 bits of value 1
// - 31 bits of value 0
// - 1 bit of value 1
// - 96 bits of value 0
// - 94 bits of value 1
// - 1 bit of value 0
// - 1 bit of value 1
// To speed up the square-and-multiply algorithm, precompute a^(2^31-1).
//
// Courtesy of Thomas Pornin
//
var t, r [native.FieldLimbs]uint64
copy(t[:], arg[:])
for i := 0; i < 30; i++ {
f.Square(&t, &t)
f.Mul(&t, &t, arg)
}
copy(r[:], t[:])
for i := 224; i >= 0; i-- {
f.Square(&r, &r)
switch i {
case 0:
fallthrough
case 2:
fallthrough
case 192:
fallthrough
case 224:
f.Mul(&r, &r, arg)
case 3:
fallthrough
case 34:
fallthrough
case 65:
f.Mul(&r, &r, &t)
}
}
*wasInverted = (&native.Field{
Value: *arg,
Params: getP256FpParams(),
Arithmetic: f,
}).IsNonZero()
Selectznz(out, uint1(*wasInverted), out, &r)
}
// FromBytes converts a little endian byte array into a field element
func (f p256FpArithmetic) FromBytes(out *[native.FieldLimbs]uint64, arg *[native.FieldBytes]byte) {
FromBytes(out, arg)
}
// ToBytes converts a field element to a little endian byte array
func (f p256FpArithmetic) ToBytes(out *[native.FieldBytes]byte, arg *[native.FieldLimbs]uint64) {
ToBytes(out, arg)
}
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f p256FpArithmetic) Selectznz(out, arg1, arg2 *[native.FieldLimbs]uint64, choice int) {
Selectznz(out, uint1(choice), arg1, arg2)
}
+330
View File
@@ -0,0 +1,330 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/internal"
)
func TestFpSetOne(t *testing.T) {
fq := P256FpNew().SetOne()
require.NotNil(t, fq)
require.Equal(t, fq.Value, getP256FpParams().R)
}
func TestFpSetUint64(t *testing.T) {
act := P256FpNew().SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act.Value[0], uint64(0x100000000fffffff))
}
func TestFpAdd(t *testing.T) {
lhs := P256FpNew().SetOne()
rhs := P256FpNew().SetOne()
exp := P256FpNew().SetUint64(2)
res := P256FpNew().Add(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := P256FpNew().Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpSub(t *testing.T) {
lhs := P256FpNew().SetOne()
rhs := P256FpNew().SetOne()
exp := P256FpNew().SetZero()
res := P256FpNew().Sub(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := P256FpNew().Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpMul(t *testing.T) {
lhs := P256FpNew().SetOne()
rhs := P256FpNew().SetOne()
exp := P256FpNew().SetOne()
res := P256FpNew().Mul(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := P256FpNew().Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpDouble(t *testing.T) {
a := P256FpNew().SetUint64(2)
e := P256FpNew().SetUint64(4)
require.Equal(t, e, P256FpNew().Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = P256FpNew().SetUint64(uint64(tv))
e = P256FpNew().SetUint64(ttv)
require.Equal(t, e, P256FpNew().Double(a))
}
}
func TestFpSquare(t *testing.T) {
a := P256FpNew().SetUint64(4)
e := P256FpNew().SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFpNeg(t *testing.T) {
g := P256FpNew().SetUint64(7)
a := P256FpNew().SetOne()
a.Neg(a)
e := P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xfffffffffffffffe, 0x00000001ffffffff, 0x0000000000000000, 0xfffffffe00000002})
require.Equal(t, e, a)
a.Neg(g)
e = P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xfffffffffffffff8, 0x00000007ffffffff, 0x0000000000000000, 0xfffffff800000008})
require.Equal(t, e, a)
}
func TestFpExp(t *testing.T) {
e := P256FpNew().SetUint64(8)
a := P256FpNew().SetUint64(2)
by := P256FpNew().SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFpSqrt(t *testing.T) {
t1 := P256FpNew().SetUint64(2)
t2 := P256FpNew().Neg(t1)
t3 := P256FpNew().Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.Equal(t, 1, t1.Equal(t3)|t2.Equal(t3))
t1.SetUint64(3)
_, wasSquare = P256FpNew().Sqrt(t1)
require.False(t, wasSquare)
}
func TestFpInvert(t *testing.T) {
twoInv := P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x8000000000000000})
two := P256FpNew().SetUint64(2)
a, inverted := P256FpNew().Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnity := P256FpNew().SetLimbs(&[native.FieldLimbs]uint64{0x8619a9e760c01d0c, 0xa883c4fba37998df, 0x45607580b6eabd98, 0xf252b002544b2f99})
rootOfUnityInv := P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0x2609d8ab477f96d1, 0x6e5f128fb20a0f24, 0xe4d636874d99643b, 0x376080cc1f2a3735})
a, inverted = P256FpNew().Invert(rootOfUnity)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := P256FpNew().SetUint64(9)
rhs := P256FpNew().SetUint64(3)
rhsInv, inverted := P256FpNew().Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, P256FpNew().Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = P256FpNew().Invert(rhs)
require.False(t, inverted)
}
func TestFpCMove(t *testing.T) {
t1 := P256FpNew().SetUint64(5)
t2 := P256FpNew().SetUint64(10)
require.Equal(t, t1, P256FpNew().CMove(t1, t2, 0))
require.Equal(t, t2, P256FpNew().CMove(t1, t2, 1))
}
func TestFpBytes(t *testing.T) {
t1 := P256FpNew().SetUint64(99)
seq := t1.Bytes()
t2, err := P256FpNew().SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFpCmp(t *testing.T) {
tests := []struct {
a *native.Field
b *native.Field
e int
}{
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{2731658267414164836, 14655288906067898431, 6537465423330262322, 8306191141697566219}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{6472764012681988529, 10848812988401906064, 2961825807536828898, 4282183981941645679}),
e: 1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{8023004109510539223, 4652004072850285717, 1877219145646046927, 383214385093921911}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{10099384440823804262, 16139476942229308465, 8636966320777393798, 5435928725024696785}),
e: -1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{3741840066202388211, 12165774400417314871, 16619312580230515379, 16195032234110087705}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{3905865991286066744, 543690822309071825, 17963103015950210055, 3745476720756119742}),
e: 1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{16660853697936147788, 7799793619412111108, 13515141085171033220, 2641079731236069032}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{17790588295388238399, 571847801379669440, 14537208974498222469, 12792570372087452754}),
e: -1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{3912839285384959186, 2701177075110484070, 6453856448115499033, 6475797457962597458}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{1282566391665688512, 13503640416992806563, 2962240104675990153, 3374904770947067689}),
e: 1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{5716631803409360103, 7859567470082614154, 12747956220853330146, 18434584096087315020}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{16317076441459028418, 12854146980376319601, 2258436689269031143, 9531877130792223752}),
e: 1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{17955191469941083403, 10350326247207200880, 17263512235150705075, 12700328451238078022}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{6767595547459644695, 7146403825494928147, 12269344038346710612, 9122477829383225603}),
e: 1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{17099388671847024438, 6426264987820696548, 10641143464957227405, 7709745403700754098}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{10799154372990268556, 17178492485719929374, 5705777922258988797, 8051037767683567782}),
e: -1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{4567139260680454325, 1629385880182139061, 16607020832317899145, 1261011562621553200}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{13487234491304534488, 17872642955936089265, 17651026784972590233, 9468934643333871559}),
e: -1,
},
{
a: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{18071070103467571798, 11787850505799426140, 10631355976141928593, 4867785203635092610}),
b: P256FpNew().SetRaw(&[native.FieldLimbs]uint64{12596443599426461624, 10176122686151524591, 17075755296887483439, 6726169532695070719}),
e: -1,
},
}
for _, test := range tests {
require.Equal(t, test.e, test.a.Cmp(test.b))
require.Equal(t, -test.e, test.b.Cmp(test.a))
require.Equal(t, 0, test.a.Cmp(test.a))
require.Equal(t, 0, test.b.Cmp(test.b))
}
}
func TestFpBigInt(t *testing.T) {
t1 := P256FpNew().SetBigInt(big.NewInt(9999))
t2 := P256FpNew().SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0x515151513f3f3f3e, 0xc9c9c9cb36363636, 0xb7b7b7b79c9c9c9c, 0xfffffffeaeaeaeaf})
b := new(
big.Int,
).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Value[0] = 0xaeaeaeaec0c0c0c1
e.Value[1] = 0x36363635c9c9c9c9
e.Value[2] = 0x4848484863636363
e.Value[3] = 0x0000000051515151
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFpSetBytesWide(t *testing.T) {
e := P256FpNew().SetRaw(&[native.FieldLimbs]uint64{0xccdefd48c77805bc, 0xe935dc2db86364d6, 0xca8ee6e5870a020e, 0x4c94bf4467f3b5bf})
a := P256FpNew().SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
func TestFpSetBytesWideBigInt(t *testing.T) {
params := getP256FpParams()
var tv2 [64]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, params.BiModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a := P256FpNew().SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
+1463
View File
File diff suppressed because it is too large Load Diff
+510
View File
@@ -0,0 +1,510 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
"math/big"
"sync"
"github.com/sonr-io/sonr/crypto/core/curves/native"
)
var (
p256FqInitonce sync.Once
p256FqParams native.FieldParams
)
func P256FqNew() *native.Field {
return &native.Field{
Value: [native.FieldLimbs]uint64{},
Params: getP256FqParams(),
Arithmetic: p256FqArithmetic{},
}
}
func p256FqParamsInit() {
// See FIPS 186-3, section D.2.3
p256FqParams = native.FieldParams{
R: [native.FieldLimbs]uint64{
0x0c46353d039cdaaf,
0x4319055258e8617b,
0x0000000000000000,
0x00000000ffffffff,
},
R2: [native.FieldLimbs]uint64{
0x83244c95be79eea2,
0x4699799c49bd6fa6,
0x2845b2392b6bec59,
0x66e12d94f3d95620,
},
R3: [native.FieldLimbs]uint64{
0xac8ebec90b65a624,
0x111f28ae0c0555c9,
0x2543b9246ba5e93f,
0x503a54e76407be65,
},
Modulus: [native.FieldLimbs]uint64{
0xf3b9cac2fc632551,
0xbce6faada7179e84,
0xffffffffffffffff,
0xffffffff00000000,
},
BiModulus: new(big.Int).SetBytes([]byte{
0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51,
}),
}
}
func getP256FqParams() *native.FieldParams {
p256FqInitonce.Do(p256FqParamsInit)
return &p256FqParams
}
// p256FqArithmetic is a struct with all the methods needed for working
// in mod q
type p256FqArithmetic struct{}
// ToMontgomery converts this field to montgomery form
func (f p256FqArithmetic) ToMontgomery(out, arg *[native.FieldLimbs]uint64) {
ToMontgomery((*MontgomeryDomainFieldElement)(out), (*NonMontgomeryDomainFieldElement)(arg))
}
// FromMontgomery converts this field from montgomery form
func (f p256FqArithmetic) FromMontgomery(out, arg *[native.FieldLimbs]uint64) {
FromMontgomery((*NonMontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Neg performs modular negation
func (f p256FqArithmetic) Neg(out, arg *[native.FieldLimbs]uint64) {
Opp((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Square performs modular square
func (f p256FqArithmetic) Square(out, arg *[native.FieldLimbs]uint64) {
Square((*MontgomeryDomainFieldElement)(out), (*MontgomeryDomainFieldElement)(arg))
}
// Mul performs modular multiplication
func (f p256FqArithmetic) Mul(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Mul(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Add performs modular addition
func (f p256FqArithmetic) Add(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Add(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Sub performs modular subtraction
func (f p256FqArithmetic) Sub(out, arg1, arg2 *[native.FieldLimbs]uint64) {
Sub(
(*MontgomeryDomainFieldElement)(out),
(*MontgomeryDomainFieldElement)(arg1),
(*MontgomeryDomainFieldElement)(arg2),
)
}
// Sqrt performs modular square root
func (f p256FqArithmetic) Sqrt(wasSquare *int, out, arg *[native.FieldLimbs]uint64) {
// See sqrt_ts_ct at
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.4
// c1 := s
// c2 := (q - 1) / (2^c1)
// c2 := [4]uint64{
// 0x4f3b9cac2fc63255,
// 0xfbce6faada7179e8,
// 0x0fffffffffffffff,
// 0x0ffffffff0000000,
// }
// c3 := (c2 - 1) / 2
c3 := [native.FieldLimbs]uint64{
0x279dce5617e3192a,
0xfde737d56d38bcf4,
0x07ffffffffffffff,
0x07fffffff8000000,
}
// c4 := generator
// c5 := new(Fq).pow(generator, c2)
c5 := [native.FieldLimbs]uint64{
0x1015708f7e368fe1,
0x31c6c5456ecc4511,
0x5281fe8998a19ea1,
0x0279089e10c63fe8,
}
var z, t, b, c, tv [native.FieldLimbs]uint64
native.Pow(&z, arg, &c3, getP256FqParams(), f)
Square((*MontgomeryDomainFieldElement)(&t), (*MontgomeryDomainFieldElement)(&z))
Mul(
(*MontgomeryDomainFieldElement)(&t),
(*MontgomeryDomainFieldElement)(&t),
(*MontgomeryDomainFieldElement)(arg),
)
Mul(
(*MontgomeryDomainFieldElement)(&z),
(*MontgomeryDomainFieldElement)(&z),
(*MontgomeryDomainFieldElement)(arg),
)
copy(b[:], t[:])
copy(c[:], c5[:])
for i := s; i >= 2; i-- {
for j := 1; j <= i-2; j++ {
Square((*MontgomeryDomainFieldElement)(&b), (*MontgomeryDomainFieldElement)(&b))
}
// if b == 1 flag = 0 else flag = 1
flag := -(&native.Field{
Value: b,
Params: getP256FqParams(),
Arithmetic: f,
}).IsOne() + 1
Mul(
(*MontgomeryDomainFieldElement)(&tv),
(*MontgomeryDomainFieldElement)(&z),
(*MontgomeryDomainFieldElement)(&c),
)
Selectznz(&z, uint1(flag), &z, &tv)
Square((*MontgomeryDomainFieldElement)(&c), (*MontgomeryDomainFieldElement)(&c))
Mul(
(*MontgomeryDomainFieldElement)(&tv),
(*MontgomeryDomainFieldElement)(&t),
(*MontgomeryDomainFieldElement)(&c),
)
Selectznz(&t, uint1(flag), &t, &tv)
copy(b[:], t[:])
}
Square((*MontgomeryDomainFieldElement)(&c), (*MontgomeryDomainFieldElement)(&z))
*wasSquare = (&native.Field{
Value: c,
Params: getP256FqParams(),
Arithmetic: f,
}).Equal(&native.Field{
Value: *arg,
Params: getP256FqParams(),
Arithmetic: f,
})
Selectznz(out, uint1(*wasSquare), out, &z)
}
// Invert performs modular inverse
func (f p256FqArithmetic) Invert(wasInverted *int, out, arg *[native.FieldLimbs]uint64) {
// Using an addition chain from
// https://briansmith.org/ecc-inversion-addition-chains-01#p256_field_inversion
var x1, x10, x11, x101, x111, x1010, x1111, x10101, x101010, x101111 [native.FieldLimbs]uint64
var x6, x8, x16, x32, tmp [native.FieldLimbs]uint64
copy(x1[:], arg[:])
native.Pow2k(&x10, arg, 1, f)
Mul(
(*MontgomeryDomainFieldElement)(&x11),
(*MontgomeryDomainFieldElement)(&x10),
(*MontgomeryDomainFieldElement)(&x1),
)
Mul(
(*MontgomeryDomainFieldElement)(&x101),
(*MontgomeryDomainFieldElement)(&x10),
(*MontgomeryDomainFieldElement)(&x11),
)
Mul(
(*MontgomeryDomainFieldElement)(&x111),
(*MontgomeryDomainFieldElement)(&x10),
(*MontgomeryDomainFieldElement)(&x101),
)
native.Pow2k(&x1010, &x101, 1, f)
Mul(
(*MontgomeryDomainFieldElement)(&x1111),
(*MontgomeryDomainFieldElement)(&x101),
(*MontgomeryDomainFieldElement)(&x1010),
)
native.Pow2k(&x10101, &x1010, 1, f)
Mul(
(*MontgomeryDomainFieldElement)(&x10101),
(*MontgomeryDomainFieldElement)(&x10101),
(*MontgomeryDomainFieldElement)(&x1),
)
native.Pow2k(&x101010, &x10101, 1, f)
Mul(
(*MontgomeryDomainFieldElement)(&x101111),
(*MontgomeryDomainFieldElement)(&x101),
(*MontgomeryDomainFieldElement)(&x101010),
)
Mul(
(*MontgomeryDomainFieldElement)(&x6),
(*MontgomeryDomainFieldElement)(&x10101),
(*MontgomeryDomainFieldElement)(&x101010),
)
native.Pow2k(&x8, &x6, 2, f)
Mul(
(*MontgomeryDomainFieldElement)(&x8),
(*MontgomeryDomainFieldElement)(&x8),
(*MontgomeryDomainFieldElement)(&x11),
)
native.Pow2k(&x16, &x8, 8, f)
Mul(
(*MontgomeryDomainFieldElement)(&x16),
(*MontgomeryDomainFieldElement)(&x16),
(*MontgomeryDomainFieldElement)(&x8),
)
native.Pow2k(&x32, &x16, 16, f)
Mul(
(*MontgomeryDomainFieldElement)(&x32),
(*MontgomeryDomainFieldElement)(&x32),
(*MontgomeryDomainFieldElement)(&x16),
)
native.Pow2k(&tmp, &x32, 64, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x32),
)
native.Pow2k(&tmp, &tmp, 32, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x32),
)
native.Pow2k(&tmp, &tmp, 6, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x101111),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x111),
)
native.Pow2k(&tmp, &tmp, 4, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x11),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1111),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x10101),
)
native.Pow2k(&tmp, &tmp, 4, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x101),
)
native.Pow2k(&tmp, &tmp, 3, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x101),
)
native.Pow2k(&tmp, &tmp, 3, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x101),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x111),
)
native.Pow2k(&tmp, &tmp, 9, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x101111),
)
native.Pow2k(&tmp, &tmp, 6, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1111),
)
native.Pow2k(&tmp, &tmp, 2, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1),
)
native.Pow2k(&tmp, &tmp, 6, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1111),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x111),
)
native.Pow2k(&tmp, &tmp, 4, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x111),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x111),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x101),
)
native.Pow2k(&tmp, &tmp, 3, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x11),
)
native.Pow2k(&tmp, &tmp, 10, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x101111),
)
native.Pow2k(&tmp, &tmp, 2, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x11),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x11),
)
native.Pow2k(&tmp, &tmp, 5, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x11),
)
native.Pow2k(&tmp, &tmp, 3, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1),
)
native.Pow2k(&tmp, &tmp, 7, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x10101),
)
native.Pow2k(&tmp, &tmp, 6, f)
Mul(
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&tmp),
(*MontgomeryDomainFieldElement)(&x1111),
)
*wasInverted = (&native.Field{
Value: *arg,
Params: getP256FqParams(),
Arithmetic: f,
}).IsNonZero()
Selectznz(out, uint1(*wasInverted), out, &tmp)
}
// FromBytes converts a little endian byte array into a field element
func (f p256FqArithmetic) FromBytes(out *[native.FieldLimbs]uint64, arg *[native.FieldBytes]byte) {
FromBytes(out, arg)
}
// ToBytes converts a field element to a little endian byte array
func (f p256FqArithmetic) ToBytes(out *[native.FieldBytes]byte, arg *[native.FieldLimbs]uint64) {
ToBytes(out, arg)
}
// Selectznz performs conditional select.
// selects arg1 if choice == 0 and arg2 if choice == 1
func (f p256FqArithmetic) Selectznz(out, arg1, arg2 *[native.FieldLimbs]uint64, choice int) {
Selectznz(out, uint1(choice), arg1, arg2)
}
// generator = 7 mod q is a generator of the `q - 1` order multiplicative
// subgroup, or in other words a primitive element of the field.
// generator^t where t * 2^s + 1 = q
var generator = &[native.FieldLimbs]uint64{
0x55eb74ab1949fac9,
0xd5af25406e5aaa5d,
0x0000000000000001,
0x00000006fffffff9,
}
// s satisfies the equation 2^s * t = q - 1 with t odd.
var s = 4
// rootOfUnity
var rootOfUnity = &[native.FieldLimbs]uint64{
0x0592d7fbb41e6602,
0x1546cad004378daf,
0xba807ace842a3dfc,
0xffc97f062a770992,
}
+330
View File
@@ -0,0 +1,330 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
crand "crypto/rand"
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/internal"
)
func TestFqSetOne(t *testing.T) {
fq := P256FqNew().SetOne()
require.NotNil(t, fq)
require.Equal(t, fq.Value, getP256FqParams().R)
}
func TestFqSetUint64(t *testing.T) {
act := P256FqNew().SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, act.Value[0], uint64(0xb3f3986dec632551))
}
func TestFqAdd(t *testing.T) {
lhs := P256FqNew().SetOne()
rhs := P256FqNew().SetOne()
exp := P256FqNew().SetUint64(2)
res := P256FqNew().Add(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := P256FqNew().Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqSub(t *testing.T) {
lhs := P256FqNew().SetOne()
rhs := P256FqNew().SetOne()
exp := P256FqNew().SetZero()
res := P256FqNew().Sub(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := P256FqNew().Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqMul(t *testing.T) {
lhs := P256FqNew().SetOne()
rhs := P256FqNew().SetOne()
exp := P256FqNew().SetOne()
res := P256FqNew().Mul(lhs, rhs)
require.NotNil(t, res)
require.Equal(t, 1, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := P256FqNew().Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqDouble(t *testing.T) {
a := P256FqNew().SetUint64(2)
e := P256FqNew().SetUint64(4)
require.Equal(t, e, P256FqNew().Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = P256FqNew().SetUint64(uint64(tv))
e = P256FqNew().SetUint64(ttv)
require.Equal(t, e, P256FqNew().Double(a))
}
}
func TestFqSquare(t *testing.T) {
a := P256FqNew().SetUint64(4)
e := P256FqNew().SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFqNeg(t *testing.T) {
g := P256FqNew().SetRaw(generator)
a := P256FqNew().SetOne()
a.Neg(a)
e := P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0xe7739585f8c64aa2, 0x79cdf55b4e2f3d09, 0xffffffffffffffff, 0xfffffffe00000001})
require.Equal(t, e, a)
a.Neg(g)
e = P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x9dce5617e3192a88, 0xe737d56d38bcf427, 0xfffffffffffffffd, 0xfffffff800000007})
require.Equal(t, e, a)
}
func TestFqExp(t *testing.T) {
e := P256FqNew().SetUint64(8)
a := P256FqNew().SetUint64(2)
by := P256FqNew().SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFqSqrt(t *testing.T) {
t1 := P256FqNew().SetUint64(2)
t2 := P256FqNew().Neg(t1)
t3 := P256FqNew().Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.Equal(t, 1, t1.Equal(t3)|t2.Equal(t3))
t1.SetUint64(7)
_, wasSquare = t3.Sqrt(t1)
require.False(t, wasSquare)
}
func TestFqInvert(t *testing.T) {
twoInv := P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x0000000000000000, 0x0000000000000000, 0x0000000000000000, 0x8000000000000000})
two := P256FqNew().SetUint64(2)
a, inverted := P256FqNew().Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnityInv := P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0xcfbf53b9618acf96, 0xf17e5c39df7bd05b, 0xc7acb1f83e3ad9ad, 0x4659a42b394ff7df})
rootOU := P256FqNew().SetRaw(rootOfUnity)
a, inverted = P256FqNew().Invert(rootOU)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := P256FqNew().SetUint64(9)
rhs := P256FqNew().SetUint64(3)
rhsInv, inverted := P256FqNew().Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, P256FqNew().Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = P256FqNew().Invert(rhs)
require.False(t, inverted)
}
func TestFqCMove(t *testing.T) {
t1 := P256FqNew().SetUint64(5)
t2 := P256FqNew().SetUint64(10)
require.Equal(t, t1, P256FqNew().CMove(t1, t2, 0))
require.Equal(t, t2, P256FqNew().CMove(t1, t2, 1))
}
func TestFqBytes(t *testing.T) {
t1 := P256FqNew().SetUint64(99)
seq := t1.Bytes()
t2, err := P256FqNew().SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFqCmp(t *testing.T) {
tests := []struct {
a *native.Field
b *native.Field
e int
}{
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{2731658267414164836, 14655288906067898431, 6537465423330262322, 8306191141697566219}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{6472764012681988529, 10848812988401906064, 2961825807536828898, 4282183981941645679}),
e: 1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{8023004109510539223, 4652004072850285717, 1877219145646046927, 383214385093921911}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{10099384440823804262, 16139476942229308465, 8636966320777393798, 5435928725024696785}),
e: -1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{3741840066202388211, 12165774400417314871, 16619312580230515379, 16195032234110087705}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{3905865991286066744, 543690822309071825, 17963103015950210055, 3745476720756119742}),
e: 1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{16660853697936147788, 7799793619412111108, 13515141085171033220, 2641079731236069032}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{17790588295388238399, 571847801379669440, 14537208974498222469, 12792570372087452754}),
e: -1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{3912839285384959186, 2701177075110484070, 6453856448115499033, 6475797457962597458}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{1282566391665688512, 13503640416992806563, 2962240104675990153, 3374904770947067689}),
e: 1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{5716631803409360103, 7859567470082614154, 12747956220853330146, 18434584096087315020}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{16317076441459028418, 12854146980376319601, 2258436689269031143, 9531877130792223752}),
e: 1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{17955191469941083403, 10350326247207200880, 17263512235150705075, 12700328451238078022}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{6767595547459644695, 7146403825494928147, 12269344038346710612, 9122477829383225603}),
e: 1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{17099388671847024438, 6426264987820696548, 10641143464957227405, 7709745403700754098}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{10799154372990268556, 17178492485719929374, 5705777922258988797, 8051037767683567782}),
e: -1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{4567139260680454325, 1629385880182139061, 16607020832317899145, 1261011562621553200}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{13487234491304534488, 17872642955936089265, 17651026784972590233, 9468934643333871559}),
e: -1,
},
{
a: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{18071070103467571798, 11787850505799426140, 10631355976141928593, 4867785203635092610}),
b: P256FqNew().SetRaw(&[native.FieldLimbs]uint64{12596443599426461624, 10176122686151524591, 17075755296887483439, 6726169532695070719}),
e: -1,
},
}
for _, test := range tests {
require.Equal(t, test.e, test.a.Cmp(test.b))
require.Equal(t, -test.e, test.b.Cmp(test.a))
require.Equal(t, 0, test.a.Cmp(test.a))
require.Equal(t, 0, test.b.Cmp(test.b))
}
}
func TestFqBigInt(t *testing.T) {
t1 := P256FqNew().SetBigInt(big.NewInt(9999))
t2 := P256FqNew().SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0x21dcaaadf1cb6aa0, 0x568de5f5990a98d7, 0x354f43b0d837fac5, 0x3e02532cb23f481a})
b := new(
big.Int,
).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e.Value[0] = 0xd1dd20150a97bab1
e.Value[1] = 0x665914b80e0d05ad
e.Value[2] = 0xcab0bc4f27c8053a
e.Value[3] = 0xc1fdacd24dc0b7e6
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFqSetBytesWide(t *testing.T) {
e := P256FqNew().SetRaw(&[native.FieldLimbs]uint64{0xe2b8d4b0e576c8fa, 0x9d2b215f85d3bdf7, 0xf6070a872442640c, 0xcf15d1e49c990b88})
a := P256FqNew().SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
func TestFpSetBytesWideBigInt(t *testing.T) {
params := getP256FqParams()
var tv2 [64]byte
for i := 0; i < 25; i++ {
_, _ = crand.Read(tv2[:])
e := new(big.Int).SetBytes(tv2[:])
e.Mod(e, params.BiModulus)
tv := internal.ReverseScalarBytes(tv2[:])
copy(tv2[:], tv)
a := P256FqNew().SetBytesWide(&tv2)
require.Equal(t, 0, e.Cmp(a.BigInt()))
}
}
+1651
View File
File diff suppressed because it is too large Load Diff
+350
View File
@@ -0,0 +1,350 @@
package p256
import (
"sync"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/core/curves/native/p256/fp"
"github.com/sonr-io/sonr/crypto/internal"
)
var (
p256PointInitonce sync.Once
p256PointParams native.EllipticPointParams
p256PointSswuInitOnce sync.Once
p256PointSswuParams native.SswuParams
)
func P256PointNew() *native.EllipticPoint {
return &native.EllipticPoint{
X: fp.P256FpNew(),
Y: fp.P256FpNew(),
Z: fp.P256FpNew(),
Params: getP256PointParams(),
Arithmetic: &p256PointArithmetic{},
}
}
func p256PointParamsInit() {
// How these values were derived
// left for informational purposes
// params := elliptic.P256().Params()
// a := big.NewInt(-3)
// a.Mod(a, params.P)
// capA := fp.P256FpNew().SetBigInt(a)
// capB := fp.P256FpNew().SetBigInt(params.B)
// gx := fp.P256FpNew().SetBigInt(params.Gx)
// gy := fp.P256FpNew().SetBigInt(params.Gy)
p256PointParams = native.EllipticPointParams{
A: fp.P256FpNew().
SetRaw(&[native.FieldLimbs]uint64{0xfffffffffffffffc, 0x00000003ffffffff, 0x0000000000000000, 0xfffffffc00000004}),
B: fp.P256FpNew().
SetRaw(&[native.FieldLimbs]uint64{0xd89cdf6229c4bddf, 0xacf005cd78843090, 0xe5a220abf7212ed6, 0xdc30061d04874834}),
Gx: fp.P256FpNew().
SetRaw(&[native.FieldLimbs]uint64{0x79e730d418a9143c, 0x75ba95fc5fedb601, 0x79fb732b77622510, 0x18905f76a53755c6}),
Gy: fp.P256FpNew().
SetRaw(&[native.FieldLimbs]uint64{0xddf25357ce95560a, 0x8b4ab8e4ba19e45c, 0xd2e88688dd21f325, 0x8571ff1825885d85}),
BitSize: 256,
Name: "P256",
}
}
func getP256PointParams() *native.EllipticPointParams {
p256PointInitonce.Do(p256PointParamsInit)
return &p256PointParams
}
func getP256PointSswuParams() *native.SswuParams {
p256PointSswuInitOnce.Do(p256PointSswuParamsInit)
return &p256PointSswuParams
}
func p256PointSswuParamsInit() {
// How these values were derived
// left for informational purposes
//params := elliptic.P256().Params()
//
//// c1 = (q - 3) / 4
//c1 := new(big.Int).Set(params.P)
//c1.Sub(c1, big.NewInt(3))
//c1.Rsh(c1, 2)
//
//a := big.NewInt(-3)
//a.Mod(a, params.P)
//b := new(big.Int).Set(params.B)
//z := big.NewInt(-10)
//z.Mod(z, params.P)
//// sqrt(-Z^3)
//zTmp := new(big.Int).Exp(z, big.NewInt(3), nil)
//zTmp = zTmp.Neg(zTmp)
//zTmp.Mod(zTmp, params.P)
//c2 := new(big.Int).ModSqrt(zTmp, params.P)
//
//var capC1Bytes [32]byte
//c1.FillBytes(capC1Bytes[:])
//capC1 := fp.P256FpNew().SetRaw(&[native.FieldLimbs]uint64{
// binary.BigEndian.Uint64(capC1Bytes[24:]),
// binary.BigEndian.Uint64(capC1Bytes[16:24]),
// binary.BigEndian.Uint64(capC1Bytes[8:16]),
// binary.BigEndian.Uint64(capC1Bytes[:8]),
//})
//capC2 := fp.P256FpNew().SetBigInt(c2)
//capA := fp.P256FpNew().SetBigInt(a)
//capB := fp.P256FpNew().SetBigInt(b)
//capZ := fp.P256FpNew().SetBigInt(z)
p256PointSswuParams = native.SswuParams{
C1: [native.FieldLimbs]uint64{
0xffffffffffffffff,
0x000000003fffffff,
0x4000000000000000,
0x3fffffffc0000000,
},
C2: [native.FieldLimbs]uint64{
0x53e43951f64fdbe7,
0xb2806c63966a1a66,
0x1ac5d59c3298bf50,
0xa3323851ba997e27,
},
A: [native.FieldLimbs]uint64{
0xfffffffffffffffc,
0x00000003ffffffff,
0x0000000000000000,
0xfffffffc00000004,
},
B: [native.FieldLimbs]uint64{
0xd89cdf6229c4bddf,
0xacf005cd78843090,
0xe5a220abf7212ed6,
0xdc30061d04874834,
},
Z: [native.FieldLimbs]uint64{
0xfffffffffffffff5,
0x0000000affffffff,
0x0000000000000000,
0xfffffff50000000b,
},
}
}
type p256PointArithmetic struct{}
func (k p256PointArithmetic) Hash(
out *native.EllipticPoint,
hash *native.EllipticPointHasher,
msg, dst []byte,
) error {
var u []byte
sswuParams := getP256PointSswuParams()
switch hash.Type() {
case native.XMD:
u = native.ExpandMsgXmd(hash, msg, dst, 96)
case native.XOF:
u = native.ExpandMsgXof(hash, msg, dst, 96)
}
var buf [64]byte
copy(buf[:48], internal.ReverseScalarBytes(u[:48]))
u0 := fp.P256FpNew().SetBytesWide(&buf)
copy(buf[:48], internal.ReverseScalarBytes(u[48:]))
u1 := fp.P256FpNew().SetBytesWide(&buf)
q0x, q0y := sswuParams.Osswu3mod4(u0)
q1x, q1y := sswuParams.Osswu3mod4(u1)
out.X = q0x
out.Y = q0y
out.Z.SetOne()
tv := &native.EllipticPoint{
X: q1x,
Y: q1y,
Z: fp.P256FpNew().SetOne(),
}
k.Add(out, out, tv)
return nil
}
func (k p256PointArithmetic) Double(out, arg *native.EllipticPoint) {
// Addition formula from Renes-Costello-Batina 2015
// (https://eprint.iacr.org/2015/1060 Algorithm 6)
var xx, yy, zz, xy2, yz2, xz2, bzz, bzz3 [native.FieldLimbs]uint64
var yyMBzz3, yyPBzz3, yFrag, xFrag, zz3 [native.FieldLimbs]uint64
var bxz2, bxz6, xx3Mzz3, x, y, z [native.FieldLimbs]uint64
b := getP256PointParams().B.Value
f := arg.X.Arithmetic
f.Square(&xx, &arg.X.Value)
f.Square(&yy, &arg.Y.Value)
f.Square(&zz, &arg.Z.Value)
f.Mul(&xy2, &arg.X.Value, &arg.Y.Value)
f.Add(&xy2, &xy2, &xy2)
f.Mul(&yz2, &arg.Y.Value, &arg.Z.Value)
f.Add(&yz2, &yz2, &yz2)
f.Mul(&xz2, &arg.X.Value, &arg.Z.Value)
f.Add(&xz2, &xz2, &xz2)
f.Mul(&bzz, &b, &zz)
f.Sub(&bzz, &bzz, &xz2)
f.Add(&bzz3, &bzz, &bzz)
f.Add(&bzz3, &bzz3, &bzz)
f.Sub(&yyMBzz3, &yy, &bzz3)
f.Add(&yyPBzz3, &yy, &bzz3)
f.Mul(&yFrag, &yyPBzz3, &yyMBzz3)
f.Mul(&xFrag, &yyMBzz3, &xy2)
f.Add(&zz3, &zz, &zz)
f.Add(&zz3, &zz3, &zz)
f.Mul(&bxz2, &b, &xz2)
f.Sub(&bxz2, &bxz2, &zz3)
f.Sub(&bxz2, &bxz2, &xx)
f.Add(&bxz6, &bxz2, &bxz2)
f.Add(&bxz6, &bxz6, &bxz2)
f.Add(&xx3Mzz3, &xx, &xx)
f.Add(&xx3Mzz3, &xx3Mzz3, &xx)
f.Sub(&xx3Mzz3, &xx3Mzz3, &zz3)
f.Mul(&x, &bxz6, &yz2)
f.Sub(&x, &xFrag, &x)
f.Mul(&y, &xx3Mzz3, &bxz6)
f.Add(&y, &yFrag, &y)
f.Mul(&z, &yz2, &yy)
f.Add(&z, &z, &z)
f.Add(&z, &z, &z)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
}
func (k p256PointArithmetic) Add(out, arg1, arg2 *native.EllipticPoint) {
// Addition formula from Renes-Costello-Batina 2015
// (https://eprint.iacr.org/2015/1060 Algorithm 4).
var xx, yy, zz, zz3, bxz, bxz3 [native.FieldLimbs]uint64
var tv1, xyPairs, yzPairs, xzPairs [native.FieldLimbs]uint64
var bzz, bzz3, yyMBzz3, yyPBzz3 [native.FieldLimbs]uint64
var xx3Mzz3, x, y, z [native.FieldLimbs]uint64
f := arg1.X.Arithmetic
b := getP256PointParams().B.Value
f.Mul(&xx, &arg1.X.Value, &arg2.X.Value)
f.Mul(&yy, &arg1.Y.Value, &arg2.Y.Value)
f.Mul(&zz, &arg1.Z.Value, &arg2.Z.Value)
f.Add(&tv1, &arg2.X.Value, &arg2.Y.Value)
f.Add(&xyPairs, &arg1.X.Value, &arg1.Y.Value)
f.Mul(&xyPairs, &xyPairs, &tv1)
f.Sub(&xyPairs, &xyPairs, &xx)
f.Sub(&xyPairs, &xyPairs, &yy)
f.Add(&tv1, &arg2.Y.Value, &arg2.Z.Value)
f.Add(&yzPairs, &arg1.Y.Value, &arg1.Z.Value)
f.Mul(&yzPairs, &yzPairs, &tv1)
f.Sub(&yzPairs, &yzPairs, &yy)
f.Sub(&yzPairs, &yzPairs, &zz)
f.Add(&tv1, &arg2.X.Value, &arg2.Z.Value)
f.Add(&xzPairs, &arg1.X.Value, &arg1.Z.Value)
f.Mul(&xzPairs, &xzPairs, &tv1)
f.Sub(&xzPairs, &xzPairs, &xx)
f.Sub(&xzPairs, &xzPairs, &zz)
f.Mul(&bzz, &b, &zz)
f.Sub(&bzz, &xzPairs, &bzz)
f.Add(&bzz3, &bzz, &bzz)
f.Add(&bzz3, &bzz3, &bzz)
f.Sub(&yyMBzz3, &yy, &bzz3)
f.Add(&yyPBzz3, &yy, &bzz3)
f.Add(&zz3, &zz, &zz)
f.Add(&zz3, &zz3, &zz)
f.Mul(&bxz, &b, &xzPairs)
f.Sub(&bxz, &bxz, &zz3)
f.Sub(&bxz, &bxz, &xx)
f.Add(&bxz3, &bxz, &bxz)
f.Add(&bxz3, &bxz3, &bxz)
f.Add(&xx3Mzz3, &xx, &xx)
f.Add(&xx3Mzz3, &xx3Mzz3, &xx)
f.Sub(&xx3Mzz3, &xx3Mzz3, &zz3)
f.Mul(&tv1, &yzPairs, &bxz3)
f.Mul(&x, &yyPBzz3, &xyPairs)
f.Sub(&x, &x, &tv1)
f.Mul(&tv1, &xx3Mzz3, &bxz3)
f.Mul(&y, &yyPBzz3, &yyMBzz3)
f.Add(&y, &y, &tv1)
f.Mul(&tv1, &xyPairs, &xx3Mzz3)
f.Mul(&z, &yyMBzz3, &yzPairs)
f.Add(&z, &z, &tv1)
e1 := arg1.Z.IsZero()
e2 := arg2.Z.IsZero()
// If arg1 is identity set it to arg2
f.Selectznz(&z, &z, &arg2.Z.Value, e1)
f.Selectznz(&y, &y, &arg2.Y.Value, e1)
f.Selectznz(&x, &x, &arg2.X.Value, e1)
// If arg2 is identity set it to arg1
f.Selectznz(&z, &z, &arg1.Z.Value, e2)
f.Selectznz(&y, &y, &arg1.Y.Value, e2)
f.Selectznz(&x, &x, &arg1.X.Value, e2)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
}
func (k p256PointArithmetic) IsOnCurve(arg *native.EllipticPoint) bool {
affine := P256PointNew()
k.ToAffine(affine, arg)
lhs := fp.P256FpNew().Square(affine.Y)
rhs := fp.P256FpNew()
k.RhsEq(rhs, affine.X)
return lhs.Equal(rhs) == 1
}
func (k p256PointArithmetic) ToAffine(out, arg *native.EllipticPoint) {
var wasInverted int
var zero, x, y, z [native.FieldLimbs]uint64
f := arg.X.Arithmetic
f.Invert(&wasInverted, &z, &arg.Z.Value)
f.Mul(&x, &arg.X.Value, &z)
f.Mul(&y, &arg.Y.Value, &z)
out.Z.SetOne()
// If point at infinity this does nothing
f.Selectznz(&x, &zero, &x, wasInverted)
f.Selectznz(&y, &zero, &y, wasInverted)
f.Selectznz(&z, &zero, &out.Z.Value, wasInverted)
out.X.Value = x
out.Y.Value = y
out.Z.Value = z
out.Params = arg.Params
out.Arithmetic = arg.Arithmetic
}
func (k p256PointArithmetic) RhsEq(out, x *native.Field) {
// Elliptic curve equation for p256 is: y^2 = x^3 ax + b
out.Square(x)
out.Mul(out, x)
out.Add(out, getP256PointParams().B)
out.Add(out, fp.P256FpNew().Mul(getP256PointParams().A, x))
}
+38
View File
@@ -0,0 +1,38 @@
package p256_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/sonr-io/sonr/crypto/core/curves"
"github.com/sonr-io/sonr/crypto/core/curves/native"
"github.com/sonr-io/sonr/crypto/core/curves/native/p256"
"github.com/sonr-io/sonr/crypto/core/curves/native/p256/fp"
)
func TestP256PointArithmetic_Double(t *testing.T) {
g := p256.P256PointNew().Generator()
pt1 := p256.P256PointNew().Double(g)
pt2 := p256.P256PointNew().Add(g, g)
pt3 := p256.P256PointNew().Mul(g, fp.P256FpNew().SetUint64(2))
e1 := pt1.Equal(pt2)
e2 := pt1.Equal(pt3)
e3 := pt2.Equal(pt3)
require.Equal(t, 1, e1)
require.Equal(t, 1, e2)
require.Equal(t, 1, e3)
}
func TestP256PointArithmetic_Hash(t *testing.T) {
var b [32]byte
sc, err := p256.P256PointNew().Hash(b[:], native.EllipticPointHasherSha256())
sc1 := curves.P256().NewIdentityPoint().Hash(b[:])
fmt.Printf("%v\n", sc1)
require.NoError(t, err)
require.True(t, !sc.IsIdentity())
require.True(t, sc.IsOnCurve())
}
+10
View File
@@ -0,0 +1,10 @@
---
aliases: [README]
tags: []
title: README
linter-yaml-title-alias: README
date created: Wednesday, April 17th 2024, 4:11:40 pm
date modified: Thursday, April 18th 2024, 8:19:25 am
---
## Pallas and Pasta Curve
+375
View File
@@ -0,0 +1,375 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
"encoding/binary"
"fmt"
"math/big"
"github.com/sonr-io/sonr/crypto/internal"
)
type Fp fiat_pasta_fp_montgomery_domain_field_element
// r = 2^256 mod p
var r = &Fp{0x34786d38fffffffd, 0x992c350be41914ad, 0xffffffffffffffff, 0x3fffffffffffffff}
// r2 = 2^512 mod p
var r2 = &Fp{0x8c78ecb30000000f, 0xd7d30dbd8b0de0e7, 0x7797a99bc3c95d18, 0x096d41af7b9cb714}
// r3 = 2^768 mod p
var r3 = &Fp{0xf185a5993a9e10f9, 0xf6a68f3b6ac5b1d1, 0xdf8d1014353fd42c, 0x2ae309222d2d9910}
// generator = 5 mod p is a generator of the `p - 1` order multiplicative
// subgroup, or in other words a primitive element of the field.
var generator = &Fp{0xa1a55e68ffffffed, 0x74c2a54b4f4982f3, 0xfffffffffffffffd, 0x3fffffffffffffff}
var s = 32
// modulus representation
// p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
var modulus = &Fp{0x992d30ed00000001, 0x224698fc094cf91b, 0x0000000000000000, 0x4000000000000000}
var biModulus = new(big.Int).SetBytes([]byte{
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x22, 0x46, 0x98, 0xfc, 0x09, 0x4c, 0xf9, 0x1b,
0x99, 0x2d, 0x30, 0xed, 0x00, 0x00, 0x00, 0x01,
})
// Cmp returns -1 if fp < rhs
// 0 if fp == rhs
// 1 if fp > rhs
func (fp *Fp) Cmp(rhs *Fp) int {
gt := 0
lt := 0
for i := len(fp) - 1; i >= 0; i-- {
gt |= int((rhs[i]-fp[i])>>63) &^ lt
lt |= int((fp[i]-rhs[i])>>63) &^ gt
}
return gt - lt
}
// Equal returns true if fp == rhs
func (fp *Fp) Equal(rhs *Fp) bool {
t := fp[0] ^ rhs[0]
t |= fp[1] ^ rhs[1]
t |= fp[2] ^ rhs[2]
t |= fp[3] ^ rhs[3]
return t == 0
}
// IsZero returns true if fp == 0
func (fp *Fp) IsZero() bool {
t := fp[0]
t |= fp[1]
t |= fp[2]
t |= fp[3]
return t == 0
}
// IsOne returns true if fp == R
func (fp *Fp) IsOne() bool {
return fp.Equal(r)
}
func (fp *Fp) IsOdd() bool {
tv := new(fiat_pasta_fp_non_montgomery_domain_field_element)
fiat_pasta_fp_from_montgomery(tv, (*fiat_pasta_fp_montgomery_domain_field_element)(fp))
return tv[0]&0x01 == 0x01
}
// Set fp == rhs
func (fp *Fp) Set(rhs *Fp) *Fp {
fp[0] = rhs[0]
fp[1] = rhs[1]
fp[2] = rhs[2]
fp[3] = rhs[3]
return fp
}
// SetUint64 sets fp == rhs
func (fp *Fp) SetUint64(rhs uint64) *Fp {
r := &fiat_pasta_fp_non_montgomery_domain_field_element{rhs, 0, 0, 0}
fiat_pasta_fp_to_montgomery((*fiat_pasta_fp_montgomery_domain_field_element)(fp), r)
return fp
}
func (fp *Fp) SetBool(rhs bool) *Fp {
if rhs {
fp.SetOne()
} else {
fp.SetZero()
}
return fp
}
// SetOne fp == R
func (fp *Fp) SetOne() *Fp {
return fp.Set(r)
}
// SetZero fp == 0
func (fp *Fp) SetZero() *Fp {
fp[0] = 0
fp[1] = 0
fp[2] = 0
fp[3] = 0
return fp
}
// SetBytesWide takes 64 bytes as input and treats them as a 512-bit number.
// Attributed to https://github.com/zcash/pasta_curves/blob/main/src/fields/fp.rs#L255
// We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits
// with the higher bits multiplied by 2^256. Thus, we perform two reductions
//
// 1. the lower bits are multiplied by R^2, as normal
// 2. the upper bits are multiplied by R^2 * 2^256 = R^3
//
// and computing their sum in the field. It remains to see that arbitrary 256-bit
// numbers can be placed into Montgomery form safely using the reduction. The
// reduction works so long as the product is less than R=2^256 multiplied by
// the modulus. This holds because for any `c` smaller than the modulus, we have
// that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the
// reduction always works so long as `c` is in the field; in this case it is either the
// constant `r2` or `r3`.
func (fp *Fp) SetBytesWide(input *[64]byte) *Fp {
d0 := fiat_pasta_fp_montgomery_domain_field_element{
binary.LittleEndian.Uint64(input[:8]),
binary.LittleEndian.Uint64(input[8:16]),
binary.LittleEndian.Uint64(input[16:24]),
binary.LittleEndian.Uint64(input[24:32]),
}
d1 := fiat_pasta_fp_montgomery_domain_field_element{
binary.LittleEndian.Uint64(input[32:40]),
binary.LittleEndian.Uint64(input[40:48]),
binary.LittleEndian.Uint64(input[48:56]),
binary.LittleEndian.Uint64(input[56:64]),
}
// Convert to Montgomery form
tv1 := new(fiat_pasta_fp_montgomery_domain_field_element)
tv2 := new(fiat_pasta_fp_montgomery_domain_field_element)
// d0 * r2 + d1 * r3
fiat_pasta_fp_mul(tv1, &d0, (*fiat_pasta_fp_montgomery_domain_field_element)(r2))
fiat_pasta_fp_mul(tv2, &d1, (*fiat_pasta_fp_montgomery_domain_field_element)(r3))
fiat_pasta_fp_add((*fiat_pasta_fp_montgomery_domain_field_element)(fp), tv1, tv2)
return fp
}
// SetBytes attempts to convert a little endian byte representation
// of a scalar into a `Fp`, failing if input is not canonical
func (fp *Fp) SetBytes(input *[32]byte) (*Fp, error) {
d0 := &Fp{
binary.LittleEndian.Uint64(input[:8]),
binary.LittleEndian.Uint64(input[8:16]),
binary.LittleEndian.Uint64(input[16:24]),
binary.LittleEndian.Uint64(input[24:32]),
}
if d0.Cmp(modulus) != -1 {
return nil, fmt.Errorf("invalid byte sequence")
}
fiat_pasta_fp_from_bytes((*[4]uint64)(fp), input)
fiat_pasta_fp_to_montgomery(
(*fiat_pasta_fp_montgomery_domain_field_element)(fp),
(*fiat_pasta_fp_non_montgomery_domain_field_element)(fp),
)
return fp, nil
}
// SetBigInt initializes an element from big.Int
// The value is reduced by the modulus
func (fp *Fp) SetBigInt(bi *big.Int) *Fp {
var buffer [32]byte
r := new(big.Int).Set(bi)
r.Mod(r, biModulus)
r.FillBytes(buffer[:])
copy(buffer[:], internal.ReverseScalarBytes(buffer[:]))
_, _ = fp.SetBytes(&buffer)
return fp
}
// SetRaw converts a raw array into a field element
func (fp *Fp) SetRaw(array *[4]uint64) *Fp {
fiat_pasta_fp_to_montgomery(
(*fiat_pasta_fp_montgomery_domain_field_element)(fp),
(*fiat_pasta_fp_non_montgomery_domain_field_element)(array),
)
return fp
}
// Bytes converts this element into a byte representation
// in little endian byte order
func (fp *Fp) Bytes() [32]byte {
var output [32]byte
tv := new(fiat_pasta_fp_non_montgomery_domain_field_element)
fiat_pasta_fp_from_montgomery(tv, (*fiat_pasta_fp_montgomery_domain_field_element)(fp))
fiat_pasta_fp_to_bytes(&output, (*[4]uint64)(tv))
return output
}
// BigInt converts this element into the big.Int struct
func (fp *Fp) BigInt() *big.Int {
buffer := fp.Bytes()
return new(big.Int).SetBytes(internal.ReverseScalarBytes(buffer[:]))
}
// Double this element
func (fp *Fp) Double(elem *Fp) *Fp {
delem := (*fiat_pasta_fp_montgomery_domain_field_element)(elem)
fiat_pasta_fp_add((*fiat_pasta_fp_montgomery_domain_field_element)(fp), delem, delem)
return fp
}
// Square this element
func (fp *Fp) Square(elem *Fp) *Fp {
delem := (*fiat_pasta_fp_montgomery_domain_field_element)(elem)
fiat_pasta_fp_square((*fiat_pasta_fp_montgomery_domain_field_element)(fp), delem)
return fp
}
// Sqrt this element, if it exists. If true, then value
// is a square root. If false, value is a QNR
func (fp *Fp) Sqrt(elem *Fp) (*Fp, bool) {
return fp.tonelliShanks(elem)
}
// See sqrt_ts_ct at
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.4
func (fp *Fp) tonelliShanks(elem *Fp) (*Fp, bool) {
// c1 := 32
// c2 := (q - 1) / (2^c1)
// c2 := [4]uint64{
// 0x094cf91b992d30ed,
// 0x00000000224698fc,
// 0x0000000000000000,
// 0x0000000040000000,
// }
// c3 := (c2 - 1) / 2
c3 := [4]uint64{
0x04a67c8dcc969876,
0x0000000011234c7e,
0x0000000000000000,
0x0000000020000000,
}
// c4 := generator
// c5 := new(Fp).pow(&generator, c2)
c5 := &Fp{
0xa28db849bad6dbf0,
0x9083cd03d3b539df,
0xfba6b9ca9dc8448e,
0x3ec928747b89c6da,
}
z := new(Fp).pow(elem, c3)
t := new(Fp).Square(z)
t.Mul(t, elem)
z.Mul(z, elem)
b := new(Fp).Set(t)
c := new(Fp).Set(c5)
flags := map[bool]int{
true: 1,
false: 0,
}
for i := s; i >= 2; i-- {
for j := 1; j <= i-2; j++ {
b.Square(b)
}
z.CMove(z, new(Fp).Mul(z, c), flags[!b.IsOne()])
c.Square(c)
t.CMove(t, new(Fp).Mul(t, c), flags[!b.IsOne()])
b.Set(t)
}
wasSquare := c.Square(z).Equal(elem)
return fp.Set(z), wasSquare
}
// Invert this element i.e. compute the multiplicative inverse
// return false, zero if this element is zero
func (fp *Fp) Invert(elem *Fp) (*Fp, bool) {
// computes elem^(p - 2) mod p
exp := [4]uint64{
0x992d30ecffffffff,
0x224698fc094cf91b,
0x0000000000000000,
0x4000000000000000,
}
return fp.pow(elem, exp), !elem.IsZero()
}
// Mul returns the result from multiplying this element by rhs
func (fp *Fp) Mul(lhs, rhs *Fp) *Fp {
dlhs := (*fiat_pasta_fp_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fp_montgomery_domain_field_element)(rhs)
fiat_pasta_fp_mul((*fiat_pasta_fp_montgomery_domain_field_element)(fp), dlhs, drhs)
return fp
}
// Sub returns the result from subtracting rhs from this element
func (fp *Fp) Sub(lhs, rhs *Fp) *Fp {
dlhs := (*fiat_pasta_fp_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fp_montgomery_domain_field_element)(rhs)
fiat_pasta_fp_sub((*fiat_pasta_fp_montgomery_domain_field_element)(fp), dlhs, drhs)
return fp
}
// Add returns the result from adding rhs to this element
func (fp *Fp) Add(lhs, rhs *Fp) *Fp {
dlhs := (*fiat_pasta_fp_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fp_montgomery_domain_field_element)(rhs)
fiat_pasta_fp_add((*fiat_pasta_fp_montgomery_domain_field_element)(fp), dlhs, drhs)
return fp
}
// Neg returns negation of this element
func (fp *Fp) Neg(elem *Fp) *Fp {
delem := (*fiat_pasta_fp_montgomery_domain_field_element)(elem)
fiat_pasta_fp_opp((*fiat_pasta_fp_montgomery_domain_field_element)(fp), delem)
return fp
}
// Exp exponentiates this element by exp
func (fp *Fp) Exp(base, exp *Fp) *Fp {
// convert exponent to integer form
tv := &fiat_pasta_fp_non_montgomery_domain_field_element{}
fiat_pasta_fp_from_montgomery(tv, (*fiat_pasta_fp_montgomery_domain_field_element)(exp))
e := (*[4]uint64)(tv)
return fp.pow(base, *e)
}
func (fp *Fp) pow(base *Fp, exp [4]uint64) *Fp {
res := new(Fp).SetOne()
tmp := new(Fp)
for i := len(exp) - 1; i >= 0; i-- {
for j := 63; j >= 0; j-- {
res.Square(res)
tmp.Mul(res, base)
res.CMove(res, tmp, int(exp[i]>>j)&1)
}
}
return fp.Set(res)
}
// CMove selects lhs if choice == 0 and rhs if choice == 1
func (fp *Fp) CMove(lhs, rhs *Fp, choice int) *Fp {
dlhs := (*[4]uint64)(lhs)
drhs := (*[4]uint64)(rhs)
fiat_pasta_fp_selectznz((*[4]uint64)(fp), fiat_pasta_fp_uint1(choice), dlhs, drhs)
return fp
}
// ToRaw converts this element into the a [4]uint64
func (fp *Fp) ToRaw() [4]uint64 {
res := &fiat_pasta_fp_non_montgomery_domain_field_element{}
fiat_pasta_fp_from_montgomery(res, (*fiat_pasta_fp_montgomery_domain_field_element)(fp))
return *(*[4]uint64)(res)
}
+273
View File
@@ -0,0 +1,273 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fp
import (
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
)
func TestFpSetOne(t *testing.T) {
fp := new(Fp).SetOne()
require.NotNil(t, fp)
require.True(t, fp.Equal(r))
}
func TestFpSetUint64(t *testing.T) {
act := new(Fp).SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, int(act[0]), 0x592d30ed00000001)
}
func TestFpAdd(t *testing.T) {
lhs := new(Fp).SetOne()
rhs := new(Fp).SetOne()
exp := new(Fp).SetUint64(2)
res := new(Fp).Add(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := new(Fp).Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpSub(t *testing.T) {
lhs := new(Fp).SetOne()
rhs := new(Fp).SetOne()
exp := new(Fp).SetZero()
res := new(Fp).Sub(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := new(Fp).Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpMul(t *testing.T) {
lhs := new(Fp).SetOne()
rhs := new(Fp).SetOne()
exp := new(Fp).SetOne()
res := new(Fp).Mul(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := new(Fp).Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFpDouble(t *testing.T) {
a := new(Fp).SetUint64(2)
e := new(Fp).SetUint64(4)
require.Equal(t, e, new(Fp).Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = new(Fp).SetUint64(uint64(tv))
e = new(Fp).SetUint64(ttv)
require.Equal(t, e, new(Fp).Double(a))
}
}
func TestFpSquare(t *testing.T) {
a := new(Fp).SetUint64(4)
e := new(Fp).SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFpNeg(t *testing.T) {
a := new(Fp).SetOne()
a.Neg(a)
e := &Fp{7256640077462241284, 9879318615658062958, 0, 0}
require.Equal(t, e, a)
a.Neg(generator)
e = &Fp{0xf787d28400000014, 0xad83f3b0ba037627, 0x2, 0x0}
require.Equal(t, e, a)
}
func TestFpExp(t *testing.T) {
e := new(Fp).SetUint64(8)
a := new(Fp).SetUint64(2)
by := new(Fp).SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFpSqrt(t *testing.T) {
t1 := new(Fp).SetUint64(2)
t2 := new(Fp).Neg(t1)
t3 := new(Fp).Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.True(t, t1.Equal(t3) || t2.Equal(t3))
t1.SetUint64(5)
_, wasSquare = new(Fp).Sqrt(t1)
require.False(t, wasSquare)
}
func TestFpInvert(t *testing.T) {
twoInv := &Fp{0xcc96987680000001, 0x11234c7e04a67c8d, 0x0000000000000000, 0x2000000000000000}
fiat_pasta_fp_to_montgomery(
(*fiat_pasta_fp_montgomery_domain_field_element)(twoInv),
(*fiat_pasta_fp_non_montgomery_domain_field_element)(twoInv),
)
two := new(Fp).SetUint64(2)
a, inverted := new(Fp).Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnity := &Fp{
0xbdad6fabd87ea32f,
0xea322bf2b7bb7584,
0x362120830561f81a,
0x2bce74deac30ebda,
}
fiat_pasta_fp_to_montgomery(
(*fiat_pasta_fp_montgomery_domain_field_element)(rootOfUnity),
(*fiat_pasta_fp_non_montgomery_domain_field_element)(rootOfUnity),
)
rootOfUnityInv := &Fp{
0xf0b87c7db2ce91f6,
0x84a0a1d8859f066f,
0xb4ed8e647196dad1,
0x2cd5282c53116b5c,
}
fiat_pasta_fp_to_montgomery(
(*fiat_pasta_fp_montgomery_domain_field_element)(rootOfUnityInv),
(*fiat_pasta_fp_non_montgomery_domain_field_element)(rootOfUnityInv),
)
a, inverted = new(Fp).Invert(rootOfUnity)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := new(Fp).SetUint64(9)
rhs := new(Fp).SetUint64(3)
rhsInv, inverted := new(Fp).Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, new(Fp).Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = new(Fp).Invert(rhs)
require.False(t, inverted)
}
func TestFpCMove(t *testing.T) {
t1 := new(Fp).SetUint64(5)
t2 := new(Fp).SetUint64(10)
require.Equal(t, t1, new(Fp).CMove(t1, t2, 0))
require.Equal(t, t2, new(Fp).CMove(t1, t2, 1))
}
func TestFpBytes(t *testing.T) {
t1 := new(Fp).SetUint64(99)
seq := t1.Bytes()
t2, err := new(Fp).SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFpBigInt(t *testing.T) {
t1 := new(Fp).SetBigInt(big.NewInt(9999))
t2 := new(Fp).SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := &Fp{0x8c6bc70550c87761, 0xce2c6c48e7063731, 0xf1275fd1e4607cd6, 0x3e6762e63501edbd}
b := new(
big.Int,
).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e[0] = 0xcc169e7af3788a0
e[1] = 0x541a2cb32246c1ea
e[2] = 0xed8a02e1b9f8329
e[3] = 0x1989d19cafe1242
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFpSetBool(t *testing.T) {
require.Equal(t, new(Fp).SetOne(), new(Fp).SetBool(true))
require.Equal(t, new(Fp).SetZero(), new(Fp).SetBool(false))
}
func TestFpSetBytesWide(t *testing.T) {
e := &Fp{0x3daec14d565241d9, 0x0b7af45b6073944b, 0xea5b8bd611a5bd4c, 0x150160330625db3d}
fiat_pasta_fp_to_montgomery(
(*fiat_pasta_fp_montgomery_domain_field_element)(e),
(*fiat_pasta_fp_non_montgomery_domain_field_element)(e),
)
a := new(Fp).SetBytesWide(&[64]byte{
0xa1, 0x78, 0x76, 0x29, 0x41, 0x56, 0x15, 0xee,
0x65, 0xbe, 0xfd, 0xdb, 0x6b, 0x15, 0x3e, 0xd8,
0xb5, 0xa0, 0x8b, 0xc6, 0x34, 0xd8, 0xcc, 0xd9,
0x58, 0x27, 0x27, 0x12, 0xe3, 0xed, 0x08, 0xf5,
0x89, 0x8e, 0x22, 0xf8, 0xcb, 0xf7, 0x8d, 0x03,
0x41, 0x4b, 0xc7, 0xa3, 0xe4, 0xa1, 0x05, 0x35,
0xb3, 0x2d, 0xb8, 0x5e, 0x77, 0x6f, 0xa4, 0xbf,
0x1d, 0x47, 0x2f, 0x26, 0x7e, 0xe2, 0xeb, 0x26,
})
require.Equal(t, e, a)
}
File diff suppressed because it is too large Load Diff
+369
View File
@@ -0,0 +1,369 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
"encoding/binary"
"fmt"
"math/big"
"github.com/sonr-io/sonr/crypto/internal"
)
type Fq fiat_pasta_fq_montgomery_domain_field_element
// r = 2^256 mod p
var r = &Fq{0x5b2b3e9cfffffffd, 0x992c350be3420567, 0xffffffffffffffff, 0x3fffffffffffffff}
// r2 = 2^512 mod p
var r2 = &Fq{0xfc9678ff0000000f, 0x67bb433d891a16e3, 0x7fae231004ccf590, 0x096d41af7ccfdaa9}
// r3 = 2^768 mod p
var r3 = &Fq{0x008b421c249dae4c, 0xe13bda50dba41326, 0x88fececb8e15cb63, 0x07dd97a06e6792c8}
// generator = 5 mod p is a generator of the `p - 1` order multiplicative
// subgroup, or in other words a primitive element of the field.
var generator = &Fq{0x96bc8c8cffffffed, 0x74c2a54b49f7778e, 0xfffffffffffffffd, 0x3fffffffffffffff}
var s = 32
// modulus representation
// p = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
var modulus = &Fq{0x8c46eb2100000001, 0x224698fc0994a8dd, 0x0000000000000000, 0x4000000000000000}
var BiModulus = new(big.Int).SetBytes([]byte{
0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x22, 0x46, 0x98, 0xfc, 0x09, 0x94, 0xa8, 0xdd,
0x8c, 0x46, 0xeb, 0x21, 0x00, 0x00, 0x00, 0x01,
})
// Cmp returns -1 if fp < rhs
// 0 if fp == rhs
// 1 if fp > rhs
func (fq *Fq) Cmp(rhs *Fq) int {
gt := 0
lt := 0
for i := len(fq) - 1; i >= 0; i-- {
gt |= int((rhs[i]-fq[i])>>63) &^ lt
lt |= int((fq[i]-rhs[i])>>63) &^ gt
}
return gt - lt
}
// Equal returns true if fp == rhs
func (fq *Fq) Equal(rhs *Fq) bool {
t := fq[0] ^ rhs[0]
t |= fq[1] ^ rhs[1]
t |= fq[2] ^ rhs[2]
t |= fq[3] ^ rhs[3]
return t == 0
}
// IsZero returns true if fp == 0
func (fq *Fq) IsZero() bool {
t := fq[0]
t |= fq[1]
t |= fq[2]
t |= fq[3]
return t == 0
}
// IsOne returns true if fp == r
func (fq *Fq) IsOne() bool {
return fq.Equal(r)
}
// Set fp == rhs
func (fq *Fq) Set(rhs *Fq) *Fq {
fq[0] = rhs[0]
fq[1] = rhs[1]
fq[2] = rhs[2]
fq[3] = rhs[3]
return fq
}
// SetUint64 sets fp == rhs
func (fq *Fq) SetUint64(rhs uint64) *Fq {
r := &fiat_pasta_fq_non_montgomery_domain_field_element{rhs, 0, 0, 0}
fiat_pasta_fq_to_montgomery((*fiat_pasta_fq_montgomery_domain_field_element)(fq), r)
return fq
}
func (fq *Fq) SetBool(rhs bool) *Fq {
if rhs {
fq.SetOne()
} else {
fq.SetZero()
}
return fq
}
// SetOne fp == r
func (fq *Fq) SetOne() *Fq {
return fq.Set(r)
}
// SetZero fp == 0
func (fq *Fq) SetZero() *Fq {
fq[0] = 0
fq[1] = 0
fq[2] = 0
fq[3] = 0
return fq
}
// SetBytesWide takes 64 bytes as input and treats them as a 512-bit number.
// Attributed to https://github.com/zcash/pasta_curves/blob/main/src/fields/fq.rs#L255
// We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits
// with the higher bits multiplied by 2^256. Thus, we perform two reductions
//
// 1. the lower bits are multiplied by r^2, as normal
// 2. the upper bits are multiplied by r^2 * 2^256 = r^3
//
// and computing their sum in the field. It remains to see that arbitrary 256-bit
// numbers can be placed into Montgomery form safely using the reduction. The
// reduction works so long as the product is less than r=2^256 multiplied by
// the modulus. This holds because for any `c` smaller than the modulus, we have
// that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the
// reduction always works so long as `c` is in the field; in this case it is either the
// constant `r2` or `r3`.
func (fq *Fq) SetBytesWide(input *[64]byte) *Fq {
d0 := fiat_pasta_fq_montgomery_domain_field_element{
binary.LittleEndian.Uint64(input[:8]),
binary.LittleEndian.Uint64(input[8:16]),
binary.LittleEndian.Uint64(input[16:24]),
binary.LittleEndian.Uint64(input[24:32]),
}
d1 := fiat_pasta_fq_montgomery_domain_field_element{
binary.LittleEndian.Uint64(input[32:40]),
binary.LittleEndian.Uint64(input[40:48]),
binary.LittleEndian.Uint64(input[48:56]),
binary.LittleEndian.Uint64(input[56:64]),
}
// Convert to Montgomery form
tv1 := &fiat_pasta_fq_montgomery_domain_field_element{}
tv2 := &fiat_pasta_fq_montgomery_domain_field_element{}
// d0 * r2 + d1 * r3
fiat_pasta_fq_mul(tv1, &d0, (*fiat_pasta_fq_montgomery_domain_field_element)(r2))
fiat_pasta_fq_mul(tv2, &d1, (*fiat_pasta_fq_montgomery_domain_field_element)(r3))
fiat_pasta_fq_add((*fiat_pasta_fq_montgomery_domain_field_element)(fq), tv1, tv2)
return fq
}
// SetBytes attempts to convert a little endian byte representation
// of a scalar into a `Fq`, failing if input is not canonical
func (fq *Fq) SetBytes(input *[32]byte) (*Fq, error) {
d0 := &Fq{
binary.LittleEndian.Uint64(input[:8]),
binary.LittleEndian.Uint64(input[8:16]),
binary.LittleEndian.Uint64(input[16:24]),
binary.LittleEndian.Uint64(input[24:32]),
}
if d0.Cmp(modulus) != -1 {
return nil, fmt.Errorf("invalid byte sequence")
}
fiat_pasta_fq_from_bytes((*[4]uint64)(fq), input)
fiat_pasta_fq_to_montgomery(
(*fiat_pasta_fq_montgomery_domain_field_element)(fq),
(*fiat_pasta_fq_non_montgomery_domain_field_element)(fq),
)
return fq, nil
}
// SetBigInt initializes an element from big.Int
// The value is reduced by the modulus
func (fq *Fq) SetBigInt(bi *big.Int) *Fq {
var buffer [32]byte
r := new(big.Int).Set(bi)
r.Mod(r, BiModulus)
r.FillBytes(buffer[:])
copy(buffer[:], internal.ReverseScalarBytes(buffer[:]))
_, _ = fq.SetBytes(&buffer)
return fq
}
// SetRaw converts a raw array into a field element
func (fq *Fq) SetRaw(array *[4]uint64) *Fq {
fiat_pasta_fq_to_montgomery(
(*fiat_pasta_fq_montgomery_domain_field_element)(fq),
(*fiat_pasta_fq_non_montgomery_domain_field_element)(array),
)
return fq
}
// Bytes converts this element into a byte representation
// in little endian byte order
func (fq *Fq) Bytes() [32]byte {
var output [32]byte
tv := &fiat_pasta_fq_non_montgomery_domain_field_element{}
fiat_pasta_fq_from_montgomery(tv, (*fiat_pasta_fq_montgomery_domain_field_element)(fq))
fiat_pasta_fq_to_bytes(&output, (*[4]uint64)(tv))
return output
}
// BigInt converts this element into the big.Int struct
func (fq *Fq) BigInt() *big.Int {
buffer := fq.Bytes()
return new(big.Int).SetBytes(internal.ReverseScalarBytes(buffer[:]))
}
// Double this element
func (fq *Fq) Double(elem *Fq) *Fq {
delem := (*fiat_pasta_fq_montgomery_domain_field_element)(elem)
fiat_pasta_fq_add((*fiat_pasta_fq_montgomery_domain_field_element)(fq), delem, delem)
return fq
}
// Square this element
func (fq *Fq) Square(elem *Fq) *Fq {
delem := (*fiat_pasta_fq_montgomery_domain_field_element)(elem)
fiat_pasta_fq_square((*fiat_pasta_fq_montgomery_domain_field_element)(fq), delem)
return fq
}
// Sqrt this element, if it exists. If true, then value
// is a square root. If false, value is a QNR
func (fq *Fq) Sqrt(elem *Fq) (*Fq, bool) {
return fq.tonelliShanks(elem)
}
// See sqrt_ts_ct at
// https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-hash-to-curve-11#appendix-I.4
func (fq *Fq) tonelliShanks(elem *Fq) (*Fq, bool) {
// c1 := 32
// c2 := (q - 1) / (2^c1)
//c2 := [4]uint64{
// 0x0994a8dd8c46eb21,
// 0x00000000224698fc,
// 0x0000000000000000,
// 0x0000000040000000,
//}
// c3 := (c2 - 1) / 2
c3 := [4]uint64{
0x04ca546ec6237590,
0x0000000011234c7e,
0x0000000000000000,
0x0000000020000000,
}
// c4 := generator
// c5 := new(Fq).pow(&generator, c2)
c5 := &Fq{
0x218077428c9942de,
0xcc49578921b60494,
0xac2e5d27b2efbee2,
0xb79fa897f2db056,
}
z := new(Fq).pow(elem, c3)
t := new(Fq).Square(z)
t.Mul(t, elem)
z.Mul(z, elem)
b := new(Fq).Set(t)
c := new(Fq).Set(c5)
flags := map[bool]int{
true: 1,
false: 0,
}
for i := s; i >= 2; i-- {
for j := 1; j <= i-2; j++ {
b.Square(b)
}
z.CMove(z, new(Fq).Mul(z, c), flags[!b.IsOne()])
c.Square(c)
t.CMove(t, new(Fq).Mul(t, c), flags[!b.IsOne()])
b.Set(t)
}
wasSquare := c.Square(z).Equal(elem)
return fq.Set(z), wasSquare
}
// Invert this element i.e. compute the multiplicative inverse
// return false, zero if this element is zero
func (fq *Fq) Invert(elem *Fq) (*Fq, bool) {
// computes elem^(p - 2) mod p
exp := [4]uint64{
0x8c46eb20ffffffff,
0x224698fc0994a8dd,
0x0000000000000000,
0x4000000000000000,
}
return fq.pow(elem, exp), !elem.IsZero()
}
// Mul returns the result from multiplying this element by rhs
func (fq *Fq) Mul(lhs, rhs *Fq) *Fq {
dlhs := (*fiat_pasta_fq_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fq_montgomery_domain_field_element)(rhs)
fiat_pasta_fq_mul((*fiat_pasta_fq_montgomery_domain_field_element)(fq), dlhs, drhs)
return fq
}
// Sub returns the result from subtracting rhs from this element
func (fq *Fq) Sub(lhs, rhs *Fq) *Fq {
dlhs := (*fiat_pasta_fq_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fq_montgomery_domain_field_element)(rhs)
fiat_pasta_fq_sub((*fiat_pasta_fq_montgomery_domain_field_element)(fq), dlhs, drhs)
return fq
}
// Add returns the result from adding rhs to this element
func (fq *Fq) Add(lhs, rhs *Fq) *Fq {
dlhs := (*fiat_pasta_fq_montgomery_domain_field_element)(lhs)
drhs := (*fiat_pasta_fq_montgomery_domain_field_element)(rhs)
fiat_pasta_fq_add((*fiat_pasta_fq_montgomery_domain_field_element)(fq), dlhs, drhs)
return fq
}
// Neg returns negation of this element
func (fq *Fq) Neg(elem *Fq) *Fq {
delem := (*fiat_pasta_fq_montgomery_domain_field_element)(elem)
fiat_pasta_fq_opp((*fiat_pasta_fq_montgomery_domain_field_element)(fq), delem)
return fq
}
// Exp exponentiates this element by exp
func (fq *Fq) Exp(base, exp *Fq) *Fq {
// convert exponent to integer form
tv := &fiat_pasta_fq_non_montgomery_domain_field_element{}
fiat_pasta_fq_from_montgomery(tv, (*fiat_pasta_fq_montgomery_domain_field_element)(exp))
e := (*[4]uint64)(tv)
return fq.pow(base, *e)
}
func (fq *Fq) pow(base *Fq, exp [4]uint64) *Fq {
res := new(Fq).SetOne()
tmp := new(Fq)
for i := len(exp) - 1; i >= 0; i-- {
for j := 63; j >= 0; j-- {
res.Square(res)
tmp.Mul(res, base)
res.CMove(res, tmp, int(exp[i]>>j)&1)
}
}
return fq.Set(res)
}
// CMove selects lhs if choice == 0 and rhs if choice == 1
func (fq *Fq) CMove(lhs, rhs *Fq, choice int) *Fq {
dlhs := (*[4]uint64)(lhs)
drhs := (*[4]uint64)(rhs)
fiat_pasta_fq_selectznz((*[4]uint64)(fq), fiat_pasta_fq_uint1(choice), dlhs, drhs)
return fq
}
// ToRaw converts this element into the a [4]uint64
func (fq *Fq) ToRaw() [4]uint64 {
res := &fiat_pasta_fq_non_montgomery_domain_field_element{}
fiat_pasta_fq_from_montgomery(res, (*fiat_pasta_fq_montgomery_domain_field_element)(fq))
return *(*[4]uint64)(res)
}
+273
View File
@@ -0,0 +1,273 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package fq
import (
"math/big"
"math/rand"
"testing"
"github.com/stretchr/testify/require"
)
func TestFqSetOne(t *testing.T) {
fq := new(Fq).SetOne()
require.NotNil(t, fq)
require.True(t, fq.Equal(r))
}
func TestFqSetUint64(t *testing.T) {
act := new(Fq).SetUint64(1 << 60)
require.NotNil(t, act)
// Remember it will be in montgomery form
require.Equal(t, int(act[0]), 0x4c46eb2100000001)
}
func TestFqAdd(t *testing.T) {
lhs := new(Fq).SetOne()
rhs := new(Fq).SetOne()
exp := new(Fq).SetUint64(2)
res := new(Fq).Add(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
e := l + r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := new(Fq).Add(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqSub(t *testing.T) {
lhs := new(Fq).SetOne()
rhs := new(Fq).SetOne()
exp := new(Fq).SetZero()
res := new(Fq).Sub(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint64() >> 2
r := rand.Uint64() >> 2
if l < r {
l, r = r, l
}
e := l - r
lhs.SetUint64(l)
rhs.SetUint64(r)
exp.SetUint64(e)
a := new(Fq).Sub(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqMul(t *testing.T) {
lhs := new(Fq).SetOne()
rhs := new(Fq).SetOne()
exp := new(Fq).SetOne()
res := new(Fq).Mul(lhs, rhs)
require.NotNil(t, res)
require.True(t, res.Equal(exp))
// Fuzz test
for i := 0; i < 25; i++ {
// Divide by 4 to prevent overflow false errors
l := rand.Uint32()
r := rand.Uint32()
e := uint64(l) * uint64(r)
lhs.SetUint64(uint64(l))
rhs.SetUint64(uint64(r))
exp.SetUint64(e)
a := new(Fq).Mul(lhs, rhs)
require.NotNil(t, a)
require.Equal(t, exp, a)
}
}
func TestFqDouble(t *testing.T) {
a := new(Fq).SetUint64(2)
e := new(Fq).SetUint64(4)
require.Equal(t, e, new(Fq).Double(a))
for i := 0; i < 25; i++ {
tv := rand.Uint32()
ttv := uint64(tv) * 2
a = new(Fq).SetUint64(uint64(tv))
e = new(Fq).SetUint64(ttv)
require.Equal(t, e, new(Fq).Double(a))
}
}
func TestFqSquare(t *testing.T) {
a := new(Fq).SetUint64(4)
e := new(Fq).SetUint64(16)
require.Equal(t, e, a.Square(a))
for i := 0; i < 25; i++ {
j := rand.Uint32()
exp := uint64(j) * uint64(j)
e.SetUint64(exp)
a.SetUint64(uint64(j))
require.Equal(t, e, a.Square(a))
}
}
func TestFqNeg(t *testing.T) {
a := new(Fq).SetOne()
a.Neg(a)
e := &Fq{0x311bac8400000004, 0x891a63f02652a376, 0, 0}
require.Equal(t, e, a)
a.Neg(generator)
e = &Fq{0xf58a5e9400000014, 0xad83f3b0bf9d314e, 0x2, 0x0}
require.Equal(t, e, a)
}
func TestFqExp(t *testing.T) {
e := new(Fq).SetUint64(8)
a := new(Fq).SetUint64(2)
by := new(Fq).SetUint64(3)
require.Equal(t, e, a.Exp(a, by))
}
func TestFqSqrt(t *testing.T) {
t1 := new(Fq).SetUint64(2)
t2 := new(Fq).Neg(t1)
t3 := new(Fq).Square(t1)
_, wasSquare := t3.Sqrt(t3)
require.True(t, wasSquare)
require.True(t, t1.Equal(t3) || t2.Equal(t3))
t1.SetUint64(5)
_, wasSquare = new(Fq).Sqrt(t1)
require.False(t, wasSquare)
}
func TestFqInvert(t *testing.T) {
twoInv := &Fq{0xc623759080000001, 0x11234c7e04ca546e, 0x0000000000000000, 0x2000000000000000}
fiat_pasta_fq_to_montgomery(
(*fiat_pasta_fq_montgomery_domain_field_element)(twoInv),
(*fiat_pasta_fq_non_montgomery_domain_field_element)(twoInv),
)
two := new(Fq).SetUint64(2)
a, inverted := new(Fq).Invert(two)
require.True(t, inverted)
require.Equal(t, a, twoInv)
rootOfUnity := &Fq{
0xa70e2c1102b6d05f,
0x9bb97ea3c106f049,
0x9e5c4dfd492ae26e,
0x2de6a9b8746d3f58,
}
fiat_pasta_fq_to_montgomery(
(*fiat_pasta_fq_montgomery_domain_field_element)(rootOfUnity),
(*fiat_pasta_fq_non_montgomery_domain_field_element)(rootOfUnity),
)
rootOfUnityInv := &Fq{
0x57eecda0a84b6836,
0x4ad38b9084b8a80c,
0xf4c8f353124086c1,
0x2235e1a7415bf936,
}
fiat_pasta_fq_to_montgomery(
(*fiat_pasta_fq_montgomery_domain_field_element)(rootOfUnityInv),
(*fiat_pasta_fq_non_montgomery_domain_field_element)(rootOfUnityInv),
)
a, inverted = new(Fq).Invert(rootOfUnity)
require.True(t, inverted)
require.Equal(t, a, rootOfUnityInv)
lhs := new(Fq).SetUint64(9)
rhs := new(Fq).SetUint64(3)
rhsInv, inverted := new(Fq).Invert(rhs)
require.True(t, inverted)
require.Equal(t, rhs, new(Fq).Mul(lhs, rhsInv))
rhs.SetZero()
_, inverted = new(Fq).Invert(rhs)
require.False(t, inverted)
}
func TestFqCMove(t *testing.T) {
t1 := new(Fq).SetUint64(5)
t2 := new(Fq).SetUint64(10)
require.Equal(t, t1, new(Fq).CMove(t1, t2, 0))
require.Equal(t, t2, new(Fq).CMove(t1, t2, 1))
}
func TestFqBytes(t *testing.T) {
t1 := new(Fq).SetUint64(99)
seq := t1.Bytes()
t2, err := new(Fq).SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
for i := 0; i < 25; i++ {
t1.SetUint64(rand.Uint64())
seq = t1.Bytes()
_, err = t2.SetBytes(&seq)
require.NoError(t, err)
require.Equal(t, t1, t2)
}
}
func TestFqBigInt(t *testing.T) {
t1 := new(Fq).SetBigInt(big.NewInt(9999))
t2 := new(Fq).SetBigInt(t1.BigInt())
require.Equal(t, t1, t2)
e := &Fq{0x7bb1416dea3d6ae3, 0x62f9108a340aa525, 0x303b3f30fcaa477f, 0x11c9ef5422d80a4d}
b := new(
big.Int,
).SetBytes([]byte{9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9})
t1.SetBigInt(b)
require.Equal(t, e, t1)
e[0] = 0x1095a9b315c2951e
e[1] = 0xbf4d8871d58a03b8
e[2] = 0xcfc4c0cf0355b880
e[3] = 0x2e3610abdd27f5b2
b.Neg(b)
t1.SetBigInt(b)
require.Equal(t, e, t1)
}
func TestFqSetBool(t *testing.T) {
require.Equal(t, new(Fq).SetOne(), new(Fq).SetBool(true))
require.Equal(t, new(Fq).SetZero(), new(Fq).SetBool(false))
}
func TestFqSetBytesWide(t *testing.T) {
e := &Fq{0xe22bd0d1b22cc43e, 0x6b84e5b52490a7c8, 0x264262941ac9e229, 0x27dcfdf361ce4254}
fiat_pasta_fq_to_montgomery(
(*fiat_pasta_fq_montgomery_domain_field_element)(e),
(*fiat_pasta_fq_non_montgomery_domain_field_element)(e),
)
a := new(Fq).SetBytesWide(&[64]byte{
0x69, 0x23, 0x5a, 0x0b, 0xce, 0x0c, 0xa8, 0x64,
0x3c, 0x78, 0xbc, 0x01, 0x05, 0xef, 0xf2, 0x84,
0xde, 0xbb, 0x6b, 0xc8, 0x63, 0x5e, 0x6e, 0x69,
0x62, 0xcc, 0xc6, 0x2d, 0xf5, 0x72, 0x40, 0x92,
0x28, 0x11, 0xd6, 0xc8, 0x07, 0xa5, 0x88, 0x82,
0xfe, 0xe3, 0x97, 0xf6, 0x1e, 0xfb, 0x2e, 0x3b,
0x27, 0x5f, 0x85, 0x06, 0x8d, 0x99, 0xa4, 0x75,
0xc0, 0x2c, 0x71, 0x69, 0x9e, 0x58, 0xea, 0x52,
})
require.Equal(t, e, a)
}
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
//
// Copyright Coinbase, Inc. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
package pasta
+471
View File
@@ -0,0 +1,471 @@
package native
import (
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
"io"
"math/big"
"github.com/pkg/errors"
"golang.org/x/crypto/blake2b"
"golang.org/x/crypto/sha3"
)
// EllipticPointHashType is to indicate which expand operation is used
// for hash to curve operations
type EllipticPointHashType uint
// EllipticPointHashName is to indicate the hash function is used
// for hash to curve operations
type EllipticPointHashName uint
const (
// XMD - use ExpandMsgXmd
XMD EllipticPointHashType = iota
// XOF - use ExpandMsgXof
XOF
)
const (
SHA256 EllipticPointHashName = iota
SHA512
SHA3_256
SHA3_384
SHA3_512
BLAKE2B
SHAKE128
SHAKE256
)
// EllipticPoint represents a Weierstrauss elliptic curve point
type EllipticPoint struct {
X *Field
Y *Field
Z *Field
Params *EllipticPointParams
Arithmetic EllipticPointArithmetic
}
// EllipticPointParams are the Weierstrauss curve parameters
// such as the name, the coefficients the generator point,
// and the prime bit size
type EllipticPointParams struct {
Name string
A *Field
B *Field
Gx *Field
Gy *Field
BitSize int
}
// EllipticPointHasher is the type of hashing methods for
// hashing byte sequences to curve point.
type EllipticPointHasher struct {
name EllipticPointHashName
hashType EllipticPointHashType
xmd hash.Hash
xof sha3.ShakeHash
}
// Name returns the hash name for this hasher
func (e *EllipticPointHasher) Name() string {
return e.name.String()
}
// Type returns the hash type for this hasher
func (e *EllipticPointHasher) Type() EllipticPointHashType {
return e.hashType
}
// Xmd returns the hash method for ExpandMsgXmd
func (e *EllipticPointHasher) Xmd() hash.Hash {
return e.xmd
}
// Xof returns the hash method for ExpandMsgXof
func (e *EllipticPointHasher) Xof() sha3.ShakeHash {
return e.xof
}
// EllipticPointHasherSha256 creates a point hasher that uses Sha256
func EllipticPointHasherSha256() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHA256,
hashType: XMD,
xmd: sha256.New(),
}
}
// EllipticPointHasherSha512 creates a point hasher that uses Sha512
func EllipticPointHasherSha512() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHA512,
hashType: XMD,
xmd: sha512.New(),
}
}
// EllipticPointHasherSha3256 creates a point hasher that uses Sha3256
func EllipticPointHasherSha3256() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHA3_256,
hashType: XMD,
xmd: sha3.New256(),
}
}
// EllipticPointHasherSha3384 creates a point hasher that uses Sha3384
func EllipticPointHasherSha3384() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHA3_384,
hashType: XMD,
xmd: sha3.New384(),
}
}
// EllipticPointHasherSha3512 creates a point hasher that uses Sha3512
func EllipticPointHasherSha3512() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHA3_512,
hashType: XMD,
xmd: sha3.New512(),
}
}
// EllipticPointHasherBlake2b creates a point hasher that uses Blake2b
func EllipticPointHasherBlake2b() *EllipticPointHasher {
h, _ := blake2b.New(64, []byte{})
return &EllipticPointHasher{
name: BLAKE2B,
hashType: XMD,
xmd: h,
}
}
// EllipticPointHasherShake128 creates a point hasher that uses Shake128
func EllipticPointHasherShake128() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHAKE128,
hashType: XOF,
xof: sha3.NewShake128(),
}
}
// EllipticPointHasherShake256 creates a point hasher that uses Shake256
func EllipticPointHasherShake256() *EllipticPointHasher {
return &EllipticPointHasher{
name: SHAKE128,
hashType: XOF,
xof: sha3.NewShake256(),
}
}
// EllipticPointArithmetic are the methods that specific curves
// need to implement for higher abstractions to wrap the point
type EllipticPointArithmetic interface {
// Hash a byte sequence to the curve using the specified hasher
// and dst and store the result in out
Hash(out *EllipticPoint, hasher *EllipticPointHasher, bytes, dst []byte) error
// Double arg and store the result in out
Double(out, arg *EllipticPoint)
// Add arg1 with arg2 and store the result in out
Add(out, arg1, arg2 *EllipticPoint)
// IsOnCurve tests arg if it represents a valid point on the curve
IsOnCurve(arg *EllipticPoint) bool
// ToAffine converts arg to affine coordinates storing the result in out
ToAffine(out, arg *EllipticPoint)
// RhsEq computes the right-hand side of the ecc equation
RhsEq(out, x *Field)
}
func (t EllipticPointHashType) String() string {
switch t {
case XMD:
return "XMD"
case XOF:
return "XOF"
}
return "unknown"
}
func (n EllipticPointHashName) String() string {
switch n {
case SHA256:
return "SHA-256"
case SHA512:
return "SHA-512"
case SHA3_256:
return "SHA3-256"
case SHA3_384:
return "SHA3-384"
case SHA3_512:
return "SHA3-512"
case BLAKE2B:
return "BLAKE2b"
case SHAKE128:
return "SHAKE-128"
case SHAKE256:
return "SHAKE-256"
}
return "unknown"
}
// Random creates a random point on the curve
// from the specified reader
func (p *EllipticPoint) Random(reader io.Reader) (*EllipticPoint, error) {
var seed [WideFieldBytes]byte
n, err := reader.Read(seed[:])
if err != nil {
return nil, errors.Wrap(err, "random could not read from stream")
}
if n != WideFieldBytes {
return nil, fmt.Errorf("insufficient bytes read %d when %d are needed", n, WideFieldBytes)
}
dst := []byte(fmt.Sprintf("%s_XMD:SHA-256_SSWU_RO_", p.Params.Name))
err = p.Arithmetic.Hash(p, EllipticPointHasherSha256(), seed[:], dst)
if err != nil {
return nil, errors.Wrap(err, "ecc hash failed")
}
return p, nil
}
// Hash uses the hasher to map bytes to a valid point
func (p *EllipticPoint) Hash(bytes []byte, hasher *EllipticPointHasher) (*EllipticPoint, error) {
dst := []byte(fmt.Sprintf("%s_%s:%s_SSWU_RO_", p.Params.Name, hasher.hashType, hasher.name))
err := p.Arithmetic.Hash(p, hasher, bytes, dst)
if err != nil {
return nil, errors.Wrap(err, "hash failed")
}
return p, nil
}
// Identity returns the identity point
func (p *EllipticPoint) Identity() *EllipticPoint {
p.X.SetZero()
p.Y.SetZero()
p.Z.SetZero()
return p
}
// Generator returns the base point for the curve
func (p *EllipticPoint) Generator() *EllipticPoint {
p.X.Set(p.Params.Gx)
p.Y.Set(p.Params.Gy)
p.Z.SetOne()
return p
}
// IsIdentity returns true if this point is at infinity
func (p *EllipticPoint) IsIdentity() bool {
return p.Z.IsZero() == 1
}
// Double this point
func (p *EllipticPoint) Double(point *EllipticPoint) *EllipticPoint {
p.Set(point)
p.Arithmetic.Double(p, point)
return p
}
// Neg negates this point
func (p *EllipticPoint) Neg(point *EllipticPoint) *EllipticPoint {
p.Set(point)
p.Y.Neg(p.Y)
return p
}
// Add adds the two points
func (p *EllipticPoint) Add(lhs, rhs *EllipticPoint) *EllipticPoint {
p.Set(lhs)
p.Arithmetic.Add(p, lhs, rhs)
return p
}
// Sub subtracts the two points
func (p *EllipticPoint) Sub(lhs, rhs *EllipticPoint) *EllipticPoint {
p.Set(lhs)
p.Arithmetic.Add(p, lhs, new(EllipticPoint).Neg(rhs))
return p
}
// Mul multiplies this point by the input scalar
func (p *EllipticPoint) Mul(point *EllipticPoint, scalar *Field) *EllipticPoint {
bytes := scalar.Bytes()
precomputed := [16]*EllipticPoint{}
precomputed[0] = new(EllipticPoint).Set(point).Identity()
precomputed[1] = new(EllipticPoint).Set(point)
for i := 2; i < 16; i += 2 {
precomputed[i] = new(EllipticPoint).Set(point).Double(precomputed[i>>1])
precomputed[i+1] = new(EllipticPoint).Set(point).Add(precomputed[i], point)
}
p.Identity()
for i := 0; i < 256; i += 4 {
// Brouwer / windowing method. window size of 4.
for j := 0; j < 4; j++ {
p.Double(p)
}
window := bytes[32-1-i>>3] >> (4 - i&0x04) & 0x0F
p.Add(p, precomputed[window])
}
return p
}
// Equal returns 1 if the two points are equal 0 otherwise.
func (p *EllipticPoint) Equal(rhs *EllipticPoint) int {
var x1, x2, y1, y2 Field
x1.Arithmetic = p.X.Arithmetic
x2.Arithmetic = p.X.Arithmetic
y1.Arithmetic = p.Y.Arithmetic
y2.Arithmetic = p.Y.Arithmetic
x1.Mul(p.X, rhs.Z)
x2.Mul(rhs.X, p.Z)
y1.Mul(p.Y, rhs.Z)
y2.Mul(rhs.Y, p.Z)
e1 := p.Z.IsZero()
e2 := rhs.Z.IsZero()
// Both at infinity or coordinates are the same
return (e1 & e2) | (^e1 & ^e2)&x1.Equal(&x2)&y1.Equal(&y2)
}
// Set copies clone into p
func (p *EllipticPoint) Set(clone *EllipticPoint) *EllipticPoint {
p.X = new(Field).Set(clone.X)
p.Y = new(Field).Set(clone.Y)
p.Z = new(Field).Set(clone.Z)
p.Params = clone.Params
p.Arithmetic = clone.Arithmetic
return p
}
// BigInt returns the x and y as big.Ints in affine
func (p *EllipticPoint) BigInt() (x, y *big.Int) {
t := new(EllipticPoint).Set(p)
p.Arithmetic.ToAffine(t, p)
x = t.X.BigInt()
y = t.Y.BigInt()
return x, y
}
// SetBigInt creates a point from affine x, y
// and returns the point if it is on the curve
func (p *EllipticPoint) SetBigInt(x, y *big.Int) (*EllipticPoint, error) {
xx := &Field{
Params: p.Params.Gx.Params,
Arithmetic: p.Params.Gx.Arithmetic,
}
xx.SetBigInt(x)
yy := &Field{
Params: p.Params.Gx.Params,
Arithmetic: p.Params.Gx.Arithmetic,
}
yy.SetBigInt(y)
pp := new(EllipticPoint).Set(p)
zero := new(Field).Set(xx).SetZero()
one := new(Field).Set(xx).SetOne()
isIdentity := xx.IsZero() & yy.IsZero()
pp.X = xx.CMove(xx, zero, isIdentity)
pp.Y = yy.CMove(yy, zero, isIdentity)
pp.Z = one.CMove(one, zero, isIdentity)
if !p.Arithmetic.IsOnCurve(pp) && isIdentity == 0 {
return nil, fmt.Errorf("invalid coordinates")
}
return p.Set(pp), nil
}
// GetX returns the affine X coordinate
func (p *EllipticPoint) GetX() *Field {
t := new(EllipticPoint).Set(p)
p.Arithmetic.ToAffine(t, p)
return t.X
}
// GetY returns the affine Y coordinate
func (p *EllipticPoint) GetY() *Field {
t := new(EllipticPoint).Set(p)
p.Arithmetic.ToAffine(t, p)
return t.Y
}
// IsOnCurve determines if this point represents a valid curve point
func (p *EllipticPoint) IsOnCurve() bool {
return p.Arithmetic.IsOnCurve(p)
}
// ToAffine converts the point into affine coordinates
func (p *EllipticPoint) ToAffine(clone *EllipticPoint) *EllipticPoint {
p.Arithmetic.ToAffine(p, clone)
return p
}
// SumOfProducts computes the multi-exponentiation for the specified
// points and scalars and stores the result in `p`.
// Returns an error if the lengths of the arguments is not equal.
func (p *EllipticPoint) SumOfProducts(
points []*EllipticPoint,
scalars []*Field,
) (*EllipticPoint, error) {
const Upper = 256
const W = 4
const Windows = Upper / W // careful--use ceiling division in case this doesn't divide evenly
if len(points) != len(scalars) {
return nil, fmt.Errorf("length mismatch")
}
bucketSize := 1 << W
windows := make([]*EllipticPoint, Windows)
bytes := make([][32]byte, len(scalars))
buckets := make([]*EllipticPoint, bucketSize)
for i, scalar := range scalars {
bytes[i] = scalar.Bytes()
}
for i := range windows {
windows[i] = new(EllipticPoint).Set(p).Identity()
}
for i := 0; i < bucketSize; i++ {
buckets[i] = new(EllipticPoint).Set(p).Identity()
}
sum := new(EllipticPoint).Set(p)
for j := 0; j < len(windows); j++ {
for i := 0; i < bucketSize; i++ {
buckets[i].Identity()
}
for i := 0; i < len(scalars); i++ {
// j*W to get the nibble
// >> 3 to convert to byte, / 8
// (W * j & W) gets the nibble, mod W
// 1 << W - 1 to get the offset
index := bytes[i][j*W>>3] >> (W * j & W) & (1<<W - 1) // little-endian
buckets[index].Add(buckets[index], points[i])
}
sum.Identity()
for i := bucketSize - 1; i > 0; i-- {
sum.Add(sum, buckets[i])
windows[j].Add(windows[j], sum)
}
}
p.Identity()
for i := len(windows) - 1; i >= 0; i-- {
for j := 0; j < W; j++ {
p.Double(p)
}
p.Add(p, windows[i])
}
return p, nil
}