mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-11 09:18:08 +00:00
350 lines
8.2 KiB
Go
350 lines
8.2 KiB
Go
package ecgfp5
|
|
|
|
import (
|
|
cryptorand "crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"math/big"
|
|
"math/rand"
|
|
|
|
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
|
|
)
|
|
|
|
// ECgFp5Scalar represents the scalar field of the ECgFP5 elliptic curve where
|
|
// p = 1067993516717146951041484916571792702745057740581727230159139685185762082554198619328292418486241
|
|
type ECgFp5Scalar [5]uint64
|
|
|
|
func (s *ECgFp5Scalar) DeepCopy() ECgFp5Scalar {
|
|
return ECgFp5Scalar{s[0], s[1], s[2], s[3], s[4]}
|
|
}
|
|
|
|
func (s ECgFp5Scalar) ToLittleEndianBytes() []byte {
|
|
var result [40]byte
|
|
for i := 0; i < 5; i++ {
|
|
binary.LittleEndian.PutUint64(result[i*8:], s[i])
|
|
}
|
|
return result[:]
|
|
}
|
|
|
|
func ScalarElementFromLittleEndianBytes(data []byte) ECgFp5Scalar {
|
|
if len(data) != 40 {
|
|
panic("invalid length")
|
|
}
|
|
|
|
var value ECgFp5Scalar
|
|
for i := 0; i < 5; i++ {
|
|
value[i] = binary.LittleEndian.Uint64(data[i*8:])
|
|
}
|
|
return value
|
|
}
|
|
|
|
func (s ECgFp5Scalar) SplitTo4BitLimbs() [80]uint8 {
|
|
limbs := s[:]
|
|
var result [80]uint8
|
|
for i := 0; i < 5; i++ {
|
|
for j := 0; j < 16; j++ {
|
|
result[i*16+j] = uint8((limbs[i] >> uint(j*4)) & 0xF)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func SampleScalarCrypto() ECgFp5Scalar {
|
|
rng, err := cryptorand.Int(cryptorand.Reader, ORDER)
|
|
if err != nil {
|
|
panic("failed to read random bytes into buffer")
|
|
}
|
|
return FromNonCanonicalBigInt(rng)
|
|
}
|
|
|
|
func SampleScalar(seed *string) ECgFp5Scalar {
|
|
var rng *rand.Rand
|
|
if seed == nil {
|
|
return SampleScalarCrypto()
|
|
}
|
|
|
|
hash := sha256.Sum256([]byte(*seed))
|
|
var intSeed int64
|
|
for _, b := range hash[:8] {
|
|
intSeed = (intSeed << 8) | int64(b)
|
|
}
|
|
rng = rand.New(rand.NewSource(intSeed))
|
|
|
|
return FromNonCanonicalBigInt(new(big.Int).Rand(rng, ORDER))
|
|
}
|
|
|
|
var (
|
|
ORDER, _ = new(big.Int).SetString("1067993516717146951041484916571792702745057740581727230159139685185762082554198619328292418486241", 10)
|
|
ZERO = ECgFp5Scalar{}
|
|
ONE = ECgFp5Scalar{1, 0, 0, 0, 0}
|
|
TWO = ECgFp5Scalar{2, 0, 0, 0, 0}
|
|
NEG_ONE = ECgFp5Scalar{
|
|
0xE80FD996948BFFE0,
|
|
0xE8885C39D724A09C,
|
|
0x7FFFFFE6CFB80639,
|
|
0x7FFFFFF100000016,
|
|
0x7FFFFFFD80000007,
|
|
}
|
|
)
|
|
|
|
func (s ECgFp5Scalar) Order() *big.Int {
|
|
return ORDER
|
|
}
|
|
|
|
var (
|
|
// Group order n is slightly below 2^319. We store values over five
|
|
// 64-bit limbs. We use Montgomery multiplication to perform
|
|
// computations; however, we keep the limbs in normal
|
|
// (non-Montgomery) representation, so that operations that do not
|
|
// require any multiplication of scalars, just encoding and
|
|
// decoding, are fastest.
|
|
|
|
// The modulus itself, stored in a Scalar structure (which
|
|
// contravenes to the rules of a Scalar; this constant MUST NOT leak
|
|
// outside the API).
|
|
N = ECgFp5Scalar{
|
|
0xE80FD996948BFFE1,
|
|
0xE8885C39D724A09C,
|
|
0x7FFFFFE6CFB80639,
|
|
0x7FFFFFF100000016,
|
|
0x7FFFFFFD80000007,
|
|
}
|
|
// -1/N[0] mod 2^64
|
|
N0I = uint64(0xD78BEF72057B7BDF)
|
|
// 2^640 mod n
|
|
R2 = ECgFp5Scalar{
|
|
0xA01001DCE33DC739,
|
|
0x6C3228D33F62ACCF,
|
|
0xD1D796CC91CF8525,
|
|
0xAADFFF5D1574C1D8,
|
|
0x4ACA13B28CA251F5,
|
|
}
|
|
// 2^632 mod n
|
|
T632 = ECgFp5Scalar{
|
|
0x2B0266F317CA91B3,
|
|
0xEC1D26528E984773,
|
|
0x8651D7865E12DB94,
|
|
0xDA2ADFF5941574D0,
|
|
0x53CACA12110CA256,
|
|
}
|
|
)
|
|
|
|
func (s *ECgFp5Scalar) IsZero() bool {
|
|
for i := 0; i < 5; i++ {
|
|
if s[i] != 0 {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (s *ECgFp5Scalar) Equals(rhs *ECgFp5Scalar) bool {
|
|
for i := 0; i < 5; i++ {
|
|
if s[i] != rhs[i] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// raw addition (no reduction)
|
|
func (s ECgFp5Scalar) AddInner(a ECgFp5Scalar) ECgFp5Scalar {
|
|
var r ECgFp5Scalar
|
|
var c uint64 = 0
|
|
for i := 0; i < 5; i++ {
|
|
z := U128From64(s[i]).Add64(a[i]).Add64(c)
|
|
|
|
r[i] = z.Lo
|
|
c = z.Hi
|
|
}
|
|
return r
|
|
}
|
|
|
|
// raw subtraction (no reduction)
|
|
// Final borrow is returned (0xFFFFFFFFFFFFFFFF if borrow, 0 otherwise).
|
|
func (s *ECgFp5Scalar) SubInner(a *ECgFp5Scalar) (*ECgFp5Scalar, uint64) {
|
|
r := new(ECgFp5Scalar)
|
|
c := uint64(0)
|
|
|
|
for i := 0; i < 5; i++ {
|
|
z := U128From64(s[i]).Sub64(a[i]).Sub64(c)
|
|
r[i] = z.Lo
|
|
c = z.Hi & 1
|
|
}
|
|
|
|
if c != 0 {
|
|
return r, 0xFFFFFFFFFFFFFFFF
|
|
}
|
|
return r, 0
|
|
}
|
|
|
|
// If c == 0, return a0.
|
|
// If c == 0xFFFFFFFFFFFFFFFF, return a1.
|
|
// c MUST be equal to 0 or 0xFFFFFFFFFFFFFFFF.
|
|
func Select(c uint64, a0, a1 *ECgFp5Scalar) *ECgFp5Scalar {
|
|
return &ECgFp5Scalar{
|
|
a0[0] ^ (c & (a0[0] ^ a1[0])),
|
|
a0[1] ^ (c & (a0[1] ^ a1[1])),
|
|
a0[2] ^ (c & (a0[2] ^ a1[2])),
|
|
a0[3] ^ (c & (a0[3] ^ a1[3])),
|
|
a0[4] ^ (c & (a0[4] ^ a1[4])),
|
|
}
|
|
}
|
|
|
|
func (s ECgFp5Scalar) Add(rhs ECgFp5Scalar) ECgFp5Scalar {
|
|
r0 := s.AddInner(rhs)
|
|
r1, c := r0.SubInner(&N)
|
|
return *Select(c, r1, &r0)
|
|
}
|
|
|
|
func (s ECgFp5Scalar) Sub(rhs ECgFp5Scalar) ECgFp5Scalar {
|
|
r0, c := s.SubInner(&rhs)
|
|
r1 := r0.AddInner(N)
|
|
return *Select(c, r0, &r1)
|
|
}
|
|
|
|
func (s ECgFp5Scalar) Neg() ECgFp5Scalar {
|
|
return ZERO.Sub(s)
|
|
}
|
|
|
|
func (s *ECgFp5Scalar) Mul(rhs *ECgFp5Scalar) *ECgFp5Scalar {
|
|
res := s.MontyMul(&R2).MontyMul(rhs)
|
|
return res
|
|
}
|
|
|
|
func (s *ECgFp5Scalar) Square() *ECgFp5Scalar {
|
|
return s.Mul(s)
|
|
}
|
|
|
|
// Montgomery multiplication.
|
|
// Returns (self*rhs)/2^320 mod n.
|
|
// 'self' MUST be less than n (the other operand can be up to 2^320-1).
|
|
func (s *ECgFp5Scalar) MontyMul(rhs *ECgFp5Scalar) *ECgFp5Scalar {
|
|
r := new(ECgFp5Scalar)
|
|
for i := 0; i < 5; i++ {
|
|
// Iteration i computes r <- (r + self*rhs_i + f*n)/2^64.
|
|
// Factor f is at most 2^64-1 and set so that the division
|
|
// is exact.
|
|
// On input:
|
|
// r <= 2^320 - 1
|
|
// self <= n - 1
|
|
// rhs_i <= 2^64 - 1
|
|
// f <= 2^64 - 1
|
|
// Therefore:
|
|
// r + self*rhs_i + f*n <= 2^320-1 + (2^64 - 1) * (n - 1)
|
|
// + (2^64 - 1) * n
|
|
// < 2^384
|
|
// Thus, the new r fits on 320 bits.
|
|
m := rhs[i]
|
|
f := (s[0]*m + r[0]) * N0I
|
|
|
|
cc1, cc2 := uint64(0), uint64(0)
|
|
for j := 0; j < 5; j++ {
|
|
z := U128From64(s[j]).Mul64(m).Add64(r[j]).Add64(cc1)
|
|
cc1 = z.Hi
|
|
z = U128From64(f).Mul64(N[j]).Add64(z.Lo).Add64(cc2)
|
|
cc2 = z.Hi
|
|
if j > 0 {
|
|
r[j-1] = z.Lo
|
|
}
|
|
}
|
|
// No overflow here since the new r fits on 320 bits.
|
|
r[4] = cc1 + cc2
|
|
}
|
|
// We computed (self*rhs + ff*n) / 2^320, with:
|
|
// self < n
|
|
// rhs < 2^320
|
|
// ff < 2^320
|
|
// Thus, the value we obtained is lower than 2*n. Subtracting n
|
|
// once (conditionally) is sufficient to achieve full reduction.
|
|
r2, c := r.SubInner(&N)
|
|
return Select(c, r2, r)
|
|
}
|
|
|
|
func (s ECgFp5Scalar) expPowerOf2(exp int) ECgFp5Scalar {
|
|
result := s
|
|
for i := 0; i < exp; i++ {
|
|
result = *result.Square()
|
|
}
|
|
return result
|
|
}
|
|
|
|
func FromGfp5(fp5 gFp5.Element) ECgFp5Scalar {
|
|
return FromNonCanonicalBigInt(BigIntFromArray([5]uint64{
|
|
fp5[0].Uint64(), fp5[1].Uint64(), fp5[2].Uint64(), fp5[3].Uint64(), fp5[4].Uint64(),
|
|
}))
|
|
}
|
|
|
|
func BigIntFromArray(arr [5]uint64) *big.Int {
|
|
result := new(big.Int)
|
|
for i := 4; i >= 0; i-- {
|
|
result.Lsh(result, 64)
|
|
result.Or(result, new(big.Int).SetUint64(arr[i]))
|
|
}
|
|
return result
|
|
}
|
|
|
|
func FromNonCanonicalBigInt(val *big.Int) ECgFp5Scalar {
|
|
limbs := new(big.Int).Mod(val, ORDER).Bits()
|
|
if len(limbs) < 5 {
|
|
limbs = append(limbs, 0)
|
|
}
|
|
return ECgFp5Scalar{uint64(limbs[0]), uint64(limbs[1]), uint64(limbs[2]), uint64(limbs[3]), uint64(limbs[4])}
|
|
}
|
|
|
|
func (s ECgFp5Scalar) ToCanonicalBigInt() *big.Int {
|
|
result := BigIntFromArray(s)
|
|
|
|
order := ORDER
|
|
if result.Cmp(order) >= 0 {
|
|
result.Sub(result, order)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// Recode a scalar into signed integers. For a window width of w
|
|
// bits, returned integers are in the -(2^w-1) to +2^w range. The
|
|
// provided slice is filled; if w*len(ss) >= 320, then the output
|
|
// encodes the complete scalar value, and the top (last) signed
|
|
// integer is nonnegative.
|
|
// Window width MUST be between 2 and 10.
|
|
func (s ECgFp5Scalar) RecodeSigned(ss []int32, w int32) {
|
|
RecodeSignedFromLimbs(s[:], ss, w)
|
|
}
|
|
|
|
func RecodeSignedFromLimbs(limbs []uint64, ss []int32, w int32) {
|
|
var acc uint64 = 0
|
|
var accLen int32 = 0
|
|
var j int = 0
|
|
mw := (uint32(1) << w) - 1
|
|
hw := uint32(1) << (w - 1)
|
|
var cc uint32 = 0
|
|
for i := 0; i < len(ss); i++ {
|
|
// Get next w-bit chunk in bb.
|
|
var bb uint32
|
|
if accLen < w {
|
|
if j < len(limbs) {
|
|
nl := limbs[j]
|
|
j++
|
|
bb = (uint32(acc | (nl << accLen))) & mw
|
|
acc = nl >> (w - accLen)
|
|
} else {
|
|
bb = uint32(acc) & mw
|
|
acc = 0
|
|
}
|
|
accLen += 64 - w
|
|
} else {
|
|
bb = uint32(acc) & mw
|
|
accLen -= w
|
|
acc >>= w
|
|
}
|
|
|
|
// If bb is greater than 2^(w-1), subtract 2^w and propagate a carry.
|
|
bb += cc
|
|
|
|
cc = (hw - bb) >> 31
|
|
ss[i] = int32(bb) - int32(cc<<w)
|
|
}
|
|
}
|