mirror of
https://github.com/sonr-io/crypto.git
synced 2026-08-02 15:31:38 +00:00
No commit suggestions generated
This commit is contained in:
Executable
+10
@@ -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
|
||||
@@ -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)
|
||||
}
|
||||
Executable
+273
@@ -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)
|
||||
}
|
||||
Executable
+1518
File diff suppressed because it is too large
Load Diff
@@ -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)
|
||||
}
|
||||
Executable
+273
@@ -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)
|
||||
}
|
||||
Executable
+1518
File diff suppressed because it is too large
Load Diff
Executable
+7
@@ -0,0 +1,7 @@
|
||||
//
|
||||
// Copyright Coinbase, Inc. All Rights Reserved.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
package pasta
|
||||
Reference in New Issue
Block a user