mirror of
https://github.com/discountry/ritmex-bot.git
synced 2026-09-11 09:18:08 +00:00
feat: 添加 Lighter 适配器及相关功能,支持 trailing stops 和新的交易逻辑
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
package ecgfp5
|
||||
|
||||
import (
|
||||
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
|
||||
)
|
||||
|
||||
// A curve point in affine (x,u) coordinates. This is used internally
|
||||
// to make "windows" that speed up point multiplications.
|
||||
type AffinePoint struct {
|
||||
x, u gFp5.Element
|
||||
}
|
||||
|
||||
var AFFINE_NEUTRAL = AffinePoint{
|
||||
x: gFp5.FP5_ZERO,
|
||||
u: gFp5.FP5_ZERO,
|
||||
}
|
||||
|
||||
func (p AffinePoint) ToPoint() ECgFp5Point {
|
||||
return ECgFp5Point{
|
||||
x: p.x,
|
||||
z: gFp5.FP5_ONE,
|
||||
u: p.u,
|
||||
t: gFp5.FP5_ONE,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *AffinePoint) SetNeg() {
|
||||
p.u = gFp5.Neg(p.u)
|
||||
}
|
||||
|
||||
// Lookup a point in a window. The win[] slice must contain values
|
||||
// i*P for i = 1 to n (win[0] contains P, win[1] contains 2*P, and
|
||||
// so on). Index value k is an integer in the -n to n range; returned
|
||||
// point is k*P.
|
||||
func (p *AffinePoint) SetLookup(win []AffinePoint, k int32) {
|
||||
// sign = 0xFFFFFFFF if k < 0, 0x00000000 otherwise
|
||||
sign := uint32(k >> 31)
|
||||
// ka = abs(k)
|
||||
ka := (uint32(k) ^ sign) - sign
|
||||
// km1 = ka - 1
|
||||
km1 := ka - 1
|
||||
|
||||
x := gFp5.FP5_ZERO
|
||||
u := gFp5.FP5_ZERO
|
||||
for i := 0; i < len(win); i++ {
|
||||
m := km1 - uint32(i)
|
||||
c_1 := (m | (^m + 1)) >> 31
|
||||
c := uint64(c_1) - 1
|
||||
if c != 0 {
|
||||
x = win[i].x
|
||||
u = win[i].u
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If k < 0, then we must negate the point.
|
||||
c := uint64(sign) | (uint64(sign) << 32)
|
||||
p.x = x
|
||||
p.u = u
|
||||
|
||||
if c != 0 {
|
||||
p.u = gFp5.Neg(p.u)
|
||||
}
|
||||
}
|
||||
|
||||
func Lookup(win []AffinePoint, k int32) AffinePoint {
|
||||
r := AFFINE_NEUTRAL
|
||||
r.SetLookup(win, k)
|
||||
return r
|
||||
}
|
||||
|
||||
// Same as lookup(), except this implementation is variable-time.
|
||||
func LookupVarTime(win []AffinePoint, k int32) AffinePoint {
|
||||
if k == 0 {
|
||||
return AFFINE_NEUTRAL
|
||||
} else if k > 0 {
|
||||
return win[k-1]
|
||||
} else {
|
||||
res := win[-k-1]
|
||||
res.SetNeg()
|
||||
return res
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,441 @@
|
||||
package ecgfp5
|
||||
|
||||
import (
|
||||
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
|
||||
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
|
||||
)
|
||||
|
||||
// A curve point.
|
||||
type ECgFp5Point struct {
|
||||
// Internally, we use the (x,u) fractional coordinates: for curve
|
||||
// point (x,y), we have (x,u) = (x,x/y) = (X/Z,U/T) (for the neutral
|
||||
// N, the u coordinate is 0).
|
||||
x, z, u, t gFp5.Element
|
||||
}
|
||||
|
||||
// Constants for ECgFp5Point
|
||||
var (
|
||||
A_ECgFp5Point = gFp5.FromUint64Array([5]uint64{2, 0, 0, 0, 0})
|
||||
|
||||
B1 = uint64(263)
|
||||
B_ECgFp5Point = gFp5.FromUint64Array([5]uint64{0, B1, 0, 0, 0})
|
||||
B_MUL2_ECgFp5Point = gFp5.FromUint64Array([5]uint64{0, 2 * B1, 0, 0, 0})
|
||||
B_MUL4_ECgFp5Point = gFp5.FromUint64Array([5]uint64{0, 4 * B1, 0, 0, 0})
|
||||
B_MUL16_ECgFp5Point = gFp5.FromUint64Array([5]uint64{0, 16 * B1, 0, 0, 0})
|
||||
|
||||
NEUTRAL_ECgFp5Point = ECgFp5Point{
|
||||
x: gFp5.FP5_ZERO,
|
||||
z: gFp5.FP5_ONE,
|
||||
u: gFp5.FP5_ZERO,
|
||||
t: gFp5.FP5_ONE,
|
||||
}
|
||||
|
||||
GENERATOR_ECgFp5Point = ECgFp5Point{
|
||||
x: gFp5.FromUint64Array([5]uint64{
|
||||
12883135586176881569,
|
||||
4356519642755055268,
|
||||
5248930565894896907,
|
||||
2165973894480315022,
|
||||
2448410071095648785,
|
||||
},
|
||||
),
|
||||
z: gFp5.FP5_ONE,
|
||||
u: gFp5.FP5_ONE,
|
||||
t: gFp5.FromUint64Array([5]uint64{4, 0, 0, 0, 0}),
|
||||
}
|
||||
)
|
||||
|
||||
func (p ECgFp5Point) Equals(rhs ECgFp5Point) bool {
|
||||
return gFp5.Equals(
|
||||
gFp5.Mul(p.u, rhs.t),
|
||||
gFp5.Mul(rhs.u, p.t),
|
||||
)
|
||||
}
|
||||
|
||||
func CanBeDecodedIntoPoint(w gFp5.Element) bool {
|
||||
// Value w can be decoded if and only if it is zero, or
|
||||
// (w^2 - a)^2 - 4*b is a quadratic residue.
|
||||
e := gFp5.Sub(gFp5.Square(w), A_ECgFp5Point)
|
||||
delta := gFp5.Sub(gFp5.Square(e), B_MUL4_ECgFp5Point)
|
||||
deltaLegendre := gFp5.Legendre(delta)
|
||||
return gFp5.IsZero(w) || deltaLegendre.IsOne()
|
||||
}
|
||||
|
||||
func (p ECgFp5Point) Encode() gFp5.Element {
|
||||
return gFp5.Mul(p.t, gFp5.InverseOrZero(p.u))
|
||||
}
|
||||
|
||||
// Attempt to decode a point from an gFp5 element
|
||||
func Decode(w gFp5.Element) (ECgFp5Point, bool) {
|
||||
// Curve equation is y^2 = x*(x^2 + a*x + b); encoded value
|
||||
// is w = y/x. Dividing by x, we get the equation:
|
||||
// x^2 - (w^2 - a)*x + b = 0
|
||||
// We solve for x and keep the solution which is not itself a
|
||||
// square (if there are solutions, exactly one of them will be
|
||||
// a square, and the other will not be a square).
|
||||
|
||||
e := gFp5.Sub(gFp5.Square(w), A_ECgFp5Point)
|
||||
delta := gFp5.Sub(gFp5.Square(e), B_MUL4_ECgFp5Point)
|
||||
r, c := gFp5.CanonicalSqrt(delta)
|
||||
if !c {
|
||||
r = gFp5.FP5_ZERO
|
||||
}
|
||||
|
||||
x1 := gFp5.Div(gFp5.Add(e, r), gFp5.FP5_TWO)
|
||||
x2 := gFp5.Div(gFp5.Sub(e, r), gFp5.FP5_TWO)
|
||||
x := x2
|
||||
|
||||
x1Legendre := gFp5.Legendre(x1)
|
||||
one := g.One()
|
||||
if !one.Equal(&x1Legendre) {
|
||||
x = x1
|
||||
}
|
||||
|
||||
// If c == true (delta is not a sqrt) then we want to get the neutral here; note that if
|
||||
// w == 0, then delta = a^2 - 4*b, which is not a square, and
|
||||
// thus we also get c == 0.
|
||||
if !c {
|
||||
x = gFp5.FP5_ZERO
|
||||
}
|
||||
z := gFp5.FP5_ONE
|
||||
u := gFp5.FP5_ONE
|
||||
if !c {
|
||||
u = gFp5.FP5_ZERO
|
||||
}
|
||||
t := w
|
||||
if !c {
|
||||
t = gFp5.FP5_ONE
|
||||
}
|
||||
|
||||
// If w == 0 then this is in fact a success.
|
||||
if c || gFp5.IsZero(w) {
|
||||
return ECgFp5Point{x: x, z: z, u: u, t: t}, true
|
||||
}
|
||||
|
||||
return ECgFp5Point{}, false
|
||||
}
|
||||
|
||||
func (p ECgFp5Point) IsNeutral() bool {
|
||||
return gFp5.IsZero(p.u)
|
||||
}
|
||||
|
||||
// General point addition. formulas are complete (no special case).
|
||||
func (p ECgFp5Point) Add(rhs ECgFp5Point) ECgFp5Point {
|
||||
// cost: 10M
|
||||
|
||||
x1 := p.x
|
||||
z1 := p.z
|
||||
u1 := p.u
|
||||
_t1 := p.t
|
||||
|
||||
x2 := rhs.x
|
||||
z2 := rhs.z
|
||||
u2 := rhs.u
|
||||
_t2 := rhs.t
|
||||
|
||||
// let t1 = x1 * x2;
|
||||
t1 := gFp5.Mul(x1, x2)
|
||||
// let t2 = z1 * z2;
|
||||
t2 := gFp5.Mul(z1, z2)
|
||||
// let t3 = u1 * u2;
|
||||
t3 := gFp5.Mul(u1, u2)
|
||||
// let t4 = _t1 * _t2;
|
||||
t4 := gFp5.Mul(_t1, _t2)
|
||||
// let t5 = (x1 + z1) * (x2 + z2) - t1 - t2;
|
||||
t5 := gFp5.Sub(
|
||||
gFp5.Mul(gFp5.Add(x1, z1), gFp5.Add(x2, z2)),
|
||||
gFp5.Add(t1, t2),
|
||||
)
|
||||
// let t6 = (u1 + _t1) * (u2 + _t2) - t3 - t4;
|
||||
t6 := gFp5.Sub(
|
||||
gFp5.Mul(gFp5.Add(u1, _t1), gFp5.Add(u2, _t2)),
|
||||
gFp5.Add(t3, t4),
|
||||
)
|
||||
// let t7 = t1 + t2 * Self::B;
|
||||
t7 := gFp5.Add(t1, gFp5.Mul(t2, B_ECgFp5Point))
|
||||
// let t8 = t4 * t7;
|
||||
t8 := gFp5.Mul(t4, t7)
|
||||
// let t9 = t3 * (t5 * Self::B_MUL2 + t7.double());
|
||||
t9 := gFp5.Mul(
|
||||
t3,
|
||||
gFp5.Add(gFp5.Mul(t5, B_MUL2_ECgFp5Point), gFp5.Double(t7)),
|
||||
)
|
||||
// let t10 = (t4 + t3.double()) * (t5 + t7);
|
||||
t10 := gFp5.Mul(
|
||||
gFp5.Add(t4, gFp5.Double(t3)),
|
||||
gFp5.Add(t5, t7),
|
||||
)
|
||||
|
||||
xNew := gFp5.Mul(gFp5.Sub(t10, t8), B_ECgFp5Point)
|
||||
zNew := gFp5.Sub(t8, t9)
|
||||
uNew := gFp5.Mul(t6, gFp5.Sub(gFp5.Mul(t2, B_ECgFp5Point), t1))
|
||||
tNew := gFp5.Add(t8, t9)
|
||||
|
||||
return ECgFp5Point{x: xNew, z: zNew, u: uNew, t: tNew}
|
||||
}
|
||||
|
||||
func (p ECgFp5Point) Double() ECgFp5Point {
|
||||
newPoint := p
|
||||
newPoint.SetDouble()
|
||||
return newPoint
|
||||
}
|
||||
|
||||
func (p *ECgFp5Point) SetDouble() {
|
||||
// cost: 4M+5S
|
||||
x := p.x
|
||||
z := p.z
|
||||
u := p.u
|
||||
t := p.t
|
||||
|
||||
t1 := gFp5.Mul(z, t)
|
||||
t2 := gFp5.Mul(t1, t)
|
||||
x1 := gFp5.Square(t2)
|
||||
z1 := gFp5.Mul(t1, u)
|
||||
t3 := gFp5.Square(u)
|
||||
w1 := gFp5.Sub(
|
||||
t2,
|
||||
gFp5.Mul(
|
||||
t3,
|
||||
gFp5.Double(gFp5.Add(x, z)),
|
||||
),
|
||||
)
|
||||
t4 := gFp5.Square(z1)
|
||||
|
||||
xNew := gFp5.Mul(t4, B_MUL4_ECgFp5Point)
|
||||
zNew := gFp5.Square(w1)
|
||||
uNew := gFp5.Sub(
|
||||
gFp5.Square(gFp5.Add(w1, z1)),
|
||||
gFp5.Add(t4, zNew),
|
||||
)
|
||||
tNew := gFp5.Sub(
|
||||
gFp5.Double(x1),
|
||||
gFp5.Add(
|
||||
gFp5.Mul(t4, gFp5.FromUint64Array([5]uint64{4, 0, 0, 0, 0})),
|
||||
zNew,
|
||||
),
|
||||
)
|
||||
|
||||
p.x = xNew
|
||||
p.z = zNew
|
||||
p.u = uNew
|
||||
p.t = tNew
|
||||
}
|
||||
|
||||
func (p *ECgFp5Point) MDouble(n uint32) ECgFp5Point {
|
||||
newPoint := ECgFp5Point{x: p.x, z: p.z, u: p.u, t: p.t}
|
||||
newPoint.SetMDouble(n)
|
||||
return newPoint
|
||||
}
|
||||
|
||||
func (p *ECgFp5Point) SetMDouble(n uint32) {
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
if n == 1 {
|
||||
p.SetDouble()
|
||||
return
|
||||
}
|
||||
|
||||
// cost: n*(2M+5S) + 2M+1S
|
||||
x0 := p.x
|
||||
z0 := p.z
|
||||
u0 := p.u
|
||||
t0 := p.t
|
||||
|
||||
t1 := gFp5.Mul(z0, t0)
|
||||
t2 := gFp5.Mul(t1, t0)
|
||||
x1 := gFp5.Square(t2)
|
||||
z1 := gFp5.Mul(t1, u0)
|
||||
t3 := gFp5.Square(u0)
|
||||
w1 := gFp5.Sub(
|
||||
t2,
|
||||
gFp5.Mul(
|
||||
gFp5.Double(gFp5.Add(x0, z0)),
|
||||
t3,
|
||||
),
|
||||
)
|
||||
t4 := gFp5.Square(w1)
|
||||
t5 := gFp5.Square(z1)
|
||||
x := gFp5.Mul(gFp5.Square(t5), B_MUL16_ECgFp5Point)
|
||||
w := gFp5.Sub(
|
||||
gFp5.Double(x1),
|
||||
gFp5.Add(
|
||||
gFp5.Mul(t5, gFp5.FromUint64Array([5]uint64{4, 0, 0, 0, 0})),
|
||||
t4,
|
||||
),
|
||||
)
|
||||
z := gFp5.Sub(
|
||||
gFp5.Square(gFp5.Add(w1, z1)),
|
||||
gFp5.Add(t4, t5),
|
||||
)
|
||||
|
||||
for i := 2; i < int(n); i++ {
|
||||
t1 = gFp5.Square(z)
|
||||
t2 = gFp5.Square(t1)
|
||||
t3 = gFp5.Square(w)
|
||||
t4 = gFp5.Square(t3)
|
||||
t5 = gFp5.Sub(
|
||||
gFp5.Square(gFp5.Add(w, z)),
|
||||
gFp5.Add(t1, t3),
|
||||
)
|
||||
z = gFp5.Mul(
|
||||
t5,
|
||||
gFp5.Sub(
|
||||
gFp5.Double(gFp5.Add(x, t1)),
|
||||
t3,
|
||||
),
|
||||
)
|
||||
x = gFp5.Mul(gFp5.Mul(t2, t4), B_MUL16_ECgFp5Point)
|
||||
w = gFp5.Neg(
|
||||
gFp5.Add(
|
||||
t4,
|
||||
gFp5.Mul(
|
||||
t2,
|
||||
gFp5.Sub(
|
||||
B_MUL4_ECgFp5Point,
|
||||
gFp5.FromUint64Array([5]uint64{4, 0, 0, 0, 0}),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
t1 = gFp5.Square(w)
|
||||
t2 = gFp5.Square(z)
|
||||
t3 = gFp5.Sub(
|
||||
gFp5.Square(gFp5.Add(w, z)),
|
||||
gFp5.Add(t1, t2),
|
||||
)
|
||||
w1 = gFp5.Sub(
|
||||
t1,
|
||||
gFp5.Double(gFp5.Add(x, t2)),
|
||||
)
|
||||
|
||||
p.x = gFp5.Mul(gFp5.Square(t3), B_ECgFp5Point)
|
||||
p.z = gFp5.Square(w1)
|
||||
p.u = gFp5.Mul(t3, w1)
|
||||
p.t = gFp5.Sub(
|
||||
gFp5.Mul(
|
||||
gFp5.Double(t1),
|
||||
gFp5.Sub(t1, gFp5.Double(t2)),
|
||||
),
|
||||
p.z,
|
||||
)
|
||||
}
|
||||
|
||||
// Add a point in affine coordinates to this one.
|
||||
func (p ECgFp5Point) AddAffine(rhs AffinePoint) ECgFp5Point {
|
||||
// cost: 8M
|
||||
x1, z1, u1, _t1 := p.x, p.z, p.u, p.t
|
||||
x2, u2 := rhs.x, rhs.u
|
||||
|
||||
t1 := gFp5.Mul(x1, x2)
|
||||
t2 := z1
|
||||
t3 := gFp5.Mul(u1, u2)
|
||||
t4 := _t1
|
||||
t5 := gFp5.Add(x1, gFp5.Mul(x2, z1))
|
||||
t6 := gFp5.Add(u1, gFp5.Mul(u2, _t1))
|
||||
t7 := gFp5.Add(t1, gFp5.Mul(t2, B_ECgFp5Point))
|
||||
t8 := gFp5.Mul(t4, t7)
|
||||
t9 := gFp5.Mul(t3, gFp5.Add(gFp5.Mul(t5, B_MUL2_ECgFp5Point), gFp5.Double(t7)))
|
||||
t10 := gFp5.Mul(gFp5.Add(t4, gFp5.Double(t3)), gFp5.Add(t5, t7))
|
||||
|
||||
return ECgFp5Point{
|
||||
x: gFp5.Mul(gFp5.Sub(t10, t8), B_ECgFp5Point),
|
||||
u: gFp5.Mul(t6, gFp5.Sub(gFp5.Mul(t2, B_ECgFp5Point), t1)),
|
||||
z: gFp5.Sub(t8, t9),
|
||||
t: gFp5.Add(t8, t9),
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
WINDOW = 5
|
||||
WIN_SIZE = 1 << (WINDOW - 1)
|
||||
)
|
||||
|
||||
// Convert points to affine coordinates.
|
||||
func BatchToAffine(src []ECgFp5Point) []AffinePoint {
|
||||
// We use a trick due to Montgomery: to compute the inverse of
|
||||
// x and of y, a single inversion suffices, with:
|
||||
// 1/x = y*(1/(x*y))
|
||||
// 1/y = x*(1/(x*y))
|
||||
// This extends to the case of inverting n values, with a total
|
||||
// cost of 1 inversion and 3*(n-1) multiplications.
|
||||
n := len(src)
|
||||
if n == 0 {
|
||||
return []AffinePoint{}
|
||||
}
|
||||
if n == 1 {
|
||||
p := src[0]
|
||||
m1 := gFp5.InverseOrZero(gFp5.Mul(p.z, p.t))
|
||||
return []AffinePoint{
|
||||
{
|
||||
x: gFp5.Mul(gFp5.Mul(p.x, p.t), m1),
|
||||
u: gFp5.Mul(gFp5.Mul(p.u, p.z), m1),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
res := make([]AffinePoint, n)
|
||||
// Compute product of all values to invert, and invert it.
|
||||
// We also use the x and u coordinates of the points in the
|
||||
// destination slice to keep track of the partial products.
|
||||
m := gFp5.Mul(src[0].z, src[0].t)
|
||||
for i := 1; i < n; i++ {
|
||||
x := m
|
||||
m = gFp5.Mul(m, src[i].z)
|
||||
u := m
|
||||
m = gFp5.Mul(m, src[i].t)
|
||||
|
||||
res[i] = AffinePoint{x: x, u: u}
|
||||
}
|
||||
|
||||
m = gFp5.InverseOrZero(m)
|
||||
|
||||
// Propagate back inverses.
|
||||
for i := n - 1; i > 0; i-- {
|
||||
res[i].u = gFp5.Mul(gFp5.Mul(src[i].u, res[i].u), m)
|
||||
m = gFp5.Mul(m, src[i].t)
|
||||
res[i].x = gFp5.Mul(gFp5.Mul(src[i].x, res[i].x), m)
|
||||
m = gFp5.Mul(m, src[i].z)
|
||||
}
|
||||
res[0].u = gFp5.Mul(gFp5.Mul(src[0].u, src[0].z), m)
|
||||
m = gFp5.Mul(m, src[0].t)
|
||||
res[0].x = gFp5.Mul(src[0].x, m)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (p ECgFp5Point) MakeWindowAffine() []AffinePoint {
|
||||
tmp := make([]ECgFp5Point, WIN_SIZE)
|
||||
tmp[0] = p
|
||||
for i := 1; i < WIN_SIZE; i++ {
|
||||
if (i & 1) == 0 {
|
||||
tmp[i] = tmp[i-1].Add(p)
|
||||
} else {
|
||||
tmp[i] = tmp[i>>1].Double()
|
||||
}
|
||||
}
|
||||
return BatchToAffine(tmp)
|
||||
}
|
||||
|
||||
// Multiply this point by a scalar.
|
||||
func (p *ECgFp5Point) SetMul(s *ECgFp5Scalar) {
|
||||
// Make a window with affine points.
|
||||
win := p.MakeWindowAffine()
|
||||
digits := make([]int32, (319+WINDOW)/WINDOW)
|
||||
s.RecodeSigned(digits, int32(WINDOW))
|
||||
|
||||
*p = LookupVarTime(win, digits[len(digits)-1]).ToPoint()
|
||||
for i := len(digits) - 2; i >= 0; i-- {
|
||||
p.SetMDouble(uint32(WINDOW))
|
||||
lookup := Lookup(win, digits[i])
|
||||
*p = p.AddAffine(lookup)
|
||||
}
|
||||
}
|
||||
|
||||
func (p ECgFp5Point) Mul(s *ECgFp5Scalar) ECgFp5Point {
|
||||
newPoint := p
|
||||
newPoint.SetMul(s)
|
||||
return newPoint
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,729 @@
|
||||
package ecgfp5
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
|
||||
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
|
||||
)
|
||||
|
||||
func TestSerdes(t *testing.T) {
|
||||
scalar := ECgFp5Scalar{
|
||||
6950590877883398434,
|
||||
17178336263794770543,
|
||||
11012823478139181320,
|
||||
16445091359523510936,
|
||||
5882925226143600273,
|
||||
}
|
||||
|
||||
leBytes := scalar.ToLittleEndianBytes()
|
||||
result := ScalarElementFromLittleEndianBytes(leBytes)
|
||||
|
||||
if !scalar.Equals(&result) {
|
||||
t.Fatalf("Expected %v, but got %v", scalar, result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitTo4LimbBits(t *testing.T) {
|
||||
scalar := ECgFp5Scalar{
|
||||
6950590877883398434,
|
||||
17178336263794770543,
|
||||
11012823478139181320,
|
||||
16445091359523510936,
|
||||
5882925226143600273,
|
||||
}
|
||||
|
||||
limbs := scalar.SplitTo4BitLimbs()
|
||||
|
||||
expectedValues := map[int]uint8{
|
||||
0: 2, 1: 2, 2: 9, 3: 7, 4: 15, 5: 4, 6: 15, 7: 13,
|
||||
8: 3, 9: 9, 10: 5, 11: 7, 12: 5, 13: 7, 14: 0, 15: 6,
|
||||
16: 15, 17: 6, 18: 2, 19: 12, 20: 2, 21: 11, 22: 3, 23: 3,
|
||||
24: 1, 25: 13, 26: 5, 27: 11, 28: 5, 29: 6, 30: 14, 31: 14,
|
||||
32: 8, 33: 0, 34: 9, 35: 5, 36: 1, 37: 9, 38: 12, 39: 13,
|
||||
40: 10, 41: 9, 42: 8, 43: 6, 44: 5, 45: 13, 46: 8, 47: 9,
|
||||
48: 8, 49: 9, 50: 10, 51: 9, 52: 14, 53: 3, 54: 15, 55: 2,
|
||||
56: 6, 57: 7, 58: 3, 59: 11, 60: 8, 61: 3, 62: 4, 63: 14,
|
||||
64: 1, 65: 9, 66: 14, 67: 4, 68: 9, 69: 7, 70: 8, 71: 15,
|
||||
72: 2, 73: 5, 74: 9, 75: 5, 76: 4, 77: 10, 78: 1, 79: 5,
|
||||
}
|
||||
|
||||
for i, expected := range expectedValues {
|
||||
if limbs[i] != expected {
|
||||
t.Fatalf("Expected limbs[%d] to be %d, but got %d", i, expected, limbs[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddInner(t *testing.T) {
|
||||
scalar1 := ECgFp5Scalar{
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
}
|
||||
|
||||
scalar2 := ECgFp5Scalar{
|
||||
0xFFFFFFFFFeeFFF,
|
||||
12312321312,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFacdFFFFF,
|
||||
0xbcaFFFFFFFFFFFFF,
|
||||
}
|
||||
|
||||
result := scalar1.AddInner(scalar2)
|
||||
expectedValues := ECgFp5Scalar{
|
||||
0xfffffffffeeffe,
|
||||
0x2dddf1d20,
|
||||
0xffffffffffffffff,
|
||||
0xffffffacdfffff,
|
||||
0xbcafffffffffffff,
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if result[i] != expectedValues[i] {
|
||||
t.Fatalf("Expected %v but got %v at index %d", expectedValues[i], result[i], i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubInner(t *testing.T) {
|
||||
scalar1 := ECgFp5Scalar{0, 0, 0, 0, 0}
|
||||
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
|
||||
|
||||
result, carry := scalar1.SubInner(&scalar2)
|
||||
expectedValues := ECgFp5Scalar{1, 0, 0, 0, 0}
|
||||
expectedCarry := uint64(18446744073709551615)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if result[i] != expectedValues[i] {
|
||||
t.Fatalf("Expected result[%d] to be %d, but got %d", i, expectedValues[i], result[i])
|
||||
}
|
||||
}
|
||||
if carry != expectedCarry {
|
||||
t.Fatalf("Expected carry to be %d, but got %d", expectedCarry, carry)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddScalar(t *testing.T) {
|
||||
scalar1 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
|
||||
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
|
||||
|
||||
result := scalar1.AddInner(scalar2)
|
||||
expectedValues := ECgFp5Scalar{0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if result[i] != expectedValues[i] {
|
||||
t.Fatalf("Expected %v but got %v at index %d", expectedValues[i], result[i], i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSub(t *testing.T) {
|
||||
scalar1 := ECgFp5Scalar{1, 2, 0, 0, 0}
|
||||
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
|
||||
|
||||
result := scalar1.Sub(scalar2)
|
||||
expectedValues := ECgFp5Scalar{0xe80fd996948bffe3, 0xe8885c39d724a09e, 0x7fffffe6cfb80639, 0x7ffffff100000016, 0x7ffffffd80000007}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if result[i] != expectedValues[i] {
|
||||
t.Fatalf("Expected result[%d] to be %d, but got %d", i, expectedValues[i], result[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelect(t *testing.T) {
|
||||
a0 := ECgFp5Scalar{1, 2, 3, 4, 5}
|
||||
a1 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFE, 0xFFFFFFFFFFFFFFFD, 0xFFFFFFFFFFFFFFFC, 0xFFFFFFFFFFFFFFFB}
|
||||
|
||||
result := Select(uint64(0), &a0, &a1)
|
||||
for i := 0; i < 5; i++ {
|
||||
if result[i] != a0[i] {
|
||||
t.Fatalf("Expected result[%d] to be %d, but got %d", i, a0[i], result[i])
|
||||
}
|
||||
}
|
||||
|
||||
result = Select(uint64(0xFFFFFFFFFFFFFFFF), &a0, &a1)
|
||||
for i := 0; i < 5; i++ {
|
||||
if result[i] != a1[i] {
|
||||
t.Fatalf("Expected result[%d] to be %d, but got %d", i, a1[i], result[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMontyMul(t *testing.T) {
|
||||
scalar1 := ECgFp5Scalar{1, 2, 3, 4, 5}
|
||||
scalar2 := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
|
||||
|
||||
result := scalar1.MontyMul(&scalar2)
|
||||
expectedValues := ECgFp5Scalar{10974894505036100890, 7458803775930281466, 744239893213209819, 3396127080529349464, 5979369289905897562}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if result[i] != expectedValues[i] {
|
||||
t.Fatalf("Expected result[%d] to be %d, but got %d", i, expectedValues[i], result[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMul(t *testing.T) {
|
||||
scalar := ECgFp5Scalar{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF}
|
||||
|
||||
result := scalar.Mul(&scalar)
|
||||
expectedValues := ECgFp5Scalar{471447996674510360, 3520142298321118626, 17240611161823899731, 5610669884293437850, 1193611606749909414}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if result[i] != expectedValues[i] {
|
||||
t.Fatalf("Expected result[%d] to be %d, but got %d", i, expectedValues[i], result[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecodeSigned(t *testing.T) {
|
||||
var ss [50]int32
|
||||
scalar := ECgFp5Scalar{
|
||||
g.Modulus() - 1,
|
||||
g.Modulus() - 2,
|
||||
g.Modulus() - 3,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
g.Modulus() - 5,
|
||||
}
|
||||
|
||||
scalar.RecodeSigned(ss[:], 5)
|
||||
|
||||
expectedValues := map[int]int32{
|
||||
6: -4,
|
||||
19: -2,
|
||||
25: -8,
|
||||
32: -1,
|
||||
}
|
||||
|
||||
for i, elem := range ss {
|
||||
if expected, exists := expectedValues[i]; exists {
|
||||
if elem != expected {
|
||||
t.Fatalf("Expected ss[%d] to be %d, but got %d", i, expected, elem)
|
||||
}
|
||||
} else if elem != 0 {
|
||||
t.Fatalf("Expected ss[%d] to be 0, but got %d", i, elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromQuinticExtension(t *testing.T) {
|
||||
scalar := FromGfp5(gFp5.Element{*g.NegOne(), *g.NegOne(), *g.NegOne(), *g.NegOne(), *g.NegOne()})
|
||||
|
||||
expectedValues := ECgFp5Scalar{
|
||||
3449841778703204414,
|
||||
3382000508875488967,
|
||||
212073444237,
|
||||
124554051540,
|
||||
17179869170,
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if scalar[i] != expectedValues[i] {
|
||||
t.Fatalf("Expected scalar[%d] to be %d, but got %d", i, expectedValues[i], scalar[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddShiftedSmall161(t *testing.T) {
|
||||
scalar := Signed161{
|
||||
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
}
|
||||
|
||||
scalar.AddShiftedSmall([]uint64{1, 0xFFFFFFFFFFFFDDBB, 0xFFFFAACFFFFFDDBB}, 1231233)
|
||||
expectedValues := [3]uint64{
|
||||
1,
|
||||
18446744073709534070,
|
||||
18446556744415951735,
|
||||
}
|
||||
|
||||
for i, elem := range scalar {
|
||||
if elem != expectedValues[i] {
|
||||
t.Fatalf("Expected scalar[%d] to be %d, but got %d", i, expectedValues[i], elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd161(t *testing.T) {
|
||||
scalar := Signed161{
|
||||
|
||||
0x10FFFFabcdFF1213,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
}
|
||||
|
||||
scalar.Add([]uint64{0x10FFFFabcdFF1213, 0xFFFFFFFFFFFFDDBB, 0xFFFFAACFFFFFDDBB})
|
||||
|
||||
expectedValues := [3]uint64{
|
||||
2449957474057200678,
|
||||
18446744073709542842,
|
||||
18446650409062751675,
|
||||
}
|
||||
|
||||
for i, elem := range scalar {
|
||||
if elem != expectedValues[i] {
|
||||
t.Fatalf("Expected scalar[%d] to be %d, but got %d", i, expectedValues[i], elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddShifted161(t *testing.T) {
|
||||
scalar1 := Signed161{
|
||||
|
||||
0x10FFFFabcdFF1213,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
}
|
||||
scalar2 := Signed161{
|
||||
0xabcdabcdabcdabcd,
|
||||
0xdef0def0def0def0,
|
||||
0x1234123412341234,
|
||||
}
|
||||
scalar1.AddShifted(&scalar2, 21423423)
|
||||
|
||||
expectedValues := [3]uint64{
|
||||
1224978737028600339,
|
||||
18446744073709551615,
|
||||
18446744073709551615,
|
||||
}
|
||||
for i, elem := range scalar1 {
|
||||
if elem != expectedValues[i] {
|
||||
t.Fatalf("Expected scalar1[%d] to be %d, but got %d", i, expectedValues[i], elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecodeSigned5_161(t *testing.T) {
|
||||
scalar1 := Signed161{
|
||||
0x1234567890abcdef,
|
||||
0xfedcba0987654321,
|
||||
0x0fedcba987654321,
|
||||
}
|
||||
|
||||
expectedValues := [33]int32{
|
||||
15, 15, -13, -8, 11, 8, 2, 15, -10, 3, 13, 4, -15, -15, 13, 8, 5, -5, 2, -13, 1, -3, -13, -4, -1, 16, 8, 6, -12, -13, -2, -15, 0,
|
||||
}
|
||||
for i, elem := range scalar1.RecodeSigned5() {
|
||||
if elem != expectedValues[i] {
|
||||
t.Fatalf("Expected ss[%d] to be %d, but got %d", i, expectedValues[i], elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSub161(t *testing.T) {
|
||||
scalar := Signed161{
|
||||
|
||||
0x1010111112121313,
|
||||
0x10FFFFabcdFF1213,
|
||||
0xabcdef12345abcde,
|
||||
}
|
||||
|
||||
scalar.Sub([]uint64{0xFFFFFFFFFFFFFFFF, 0x10FFFFabcdFF1213, 0xFFFFFFFFFFFFFFFF})
|
||||
|
||||
expectedValues := [3]uint64{
|
||||
1157443869249508116,
|
||||
18446744073709551615,
|
||||
12379813812178173150,
|
||||
}
|
||||
|
||||
for i, elem := range scalar {
|
||||
if elem != expectedValues[i] {
|
||||
t.Fatalf("Expected scalar[%d] to be %d, but got %d", i, expectedValues[i], elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubShiftedSmall161(t *testing.T) {
|
||||
scalar := Signed161{
|
||||
|
||||
0x1010111112121313,
|
||||
0x10FFFFabcdFF1213,
|
||||
0xabcdef12345abcde,
|
||||
}
|
||||
|
||||
scalar.SubShiftedSmall([]uint64{0xFFFFFFFFFFFFFFFF, 0x10FFFFabcdFF1213, 0xFFFFFFFFFFFFFFFF}, 5123142)
|
||||
|
||||
expectedValues := [3]uint64{
|
||||
1157443869249508179,
|
||||
15060059935745936659,
|
||||
12379813812178173209,
|
||||
}
|
||||
|
||||
for i, elem := range scalar {
|
||||
if elem != expectedValues[i] {
|
||||
t.Fatalf("Expected scalar[%d] to be %d, but got %d", i, expectedValues[i], elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubShifted161(t *testing.T) {
|
||||
scalar1 := Signed161{
|
||||
|
||||
0x1010111112121313,
|
||||
0x10FFFFabcdFF1213,
|
||||
0xabcdef12345abcde,
|
||||
}
|
||||
|
||||
scalar2 := Signed161{
|
||||
|
||||
0xabcdabcdabcdabcd,
|
||||
0xdef0def0def0def0,
|
||||
0x1234123412341234,
|
||||
}
|
||||
|
||||
scalar1.SubShifted(&scalar2, 12315523)
|
||||
|
||||
expectedValues := [3]uint64{
|
||||
1157443869249508115,
|
||||
1224978737028600339,
|
||||
12379813812178173150,
|
||||
}
|
||||
|
||||
for i, elem := range scalar1 {
|
||||
if elem != expectedValues[i] {
|
||||
t.Fatalf("Expected scalar1[%d] to be %d, but got %d", i, expectedValues[i], elem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromNsquared(t *testing.T) {
|
||||
expected := Signed640{
|
||||
10262430419493848001,
|
||||
781583365610726095,
|
||||
1685487855950207164,
|
||||
0x90465B4214B27B1C,
|
||||
0xD308FECCB1878B88,
|
||||
0x3CC55EB2EAC07502,
|
||||
0x59F038FB784335CE,
|
||||
0xBFFFFE954FB808EA,
|
||||
13835057829796380825,
|
||||
4611686007689969677,
|
||||
}
|
||||
|
||||
for i, limb := range FromNsquared() {
|
||||
if limb != expected[i] {
|
||||
t.Fatalf("Expected limb %d to be %x, but got %x", i, expected[i], limb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromMulScalars(t *testing.T) {
|
||||
a := ECgFp5Scalar{
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
}
|
||||
b := ECgFp5Scalar{
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
}
|
||||
result := FromMulScalars(&a, &b)
|
||||
|
||||
expectedValues := [10]uint64{
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
18446744073709551614,
|
||||
18446744073709551615,
|
||||
18446744073709551615,
|
||||
18446744073709551615,
|
||||
18446744073709551615,
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
if result[i] != expectedValues[i] {
|
||||
t.Fatalf("Expected result[%d] to be %d, but got %d", i, expectedValues[i], result[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestAdd1_640(t *testing.T) {
|
||||
a := Signed640{
|
||||
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
}
|
||||
|
||||
a.Add1()
|
||||
|
||||
expected := Signed640{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
|
||||
for i, limb := range a {
|
||||
if limb != expected[i] {
|
||||
t.Fatalf("Test case 2: Expected limb %d to be %x, but got %x", i, expected[i], limb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsNonnegative640(t *testing.T) {
|
||||
nonnegativeTest := Signed640{
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0x7FFFFFFFFFFFFFFF,
|
||||
}
|
||||
|
||||
if !nonnegativeTest.IsNonnegative() {
|
||||
t.Fatalf("Expected nonnegativeTest to be nonnegative, but it is not")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLtUnsigned640(t *testing.T) {
|
||||
ltTest1 := Signed640{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
ltTest2 := Signed640{
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
}
|
||||
|
||||
if !ltTest1.LtUnsigned(<Test2) {
|
||||
t.Fatalf("Expected ltTest1 to be less than ltTest2, but it is not")
|
||||
}
|
||||
if ltTest2.LtUnsigned(<Test1) {
|
||||
t.Fatalf("Expected ltTest2 to be greater than ltTest1, but it is not")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBitlength(t *testing.T) {
|
||||
bitlengthTest := Signed640{
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0x8000000000000000,
|
||||
}
|
||||
|
||||
bitlength := bitlengthTest.Bitlength()
|
||||
|
||||
expectedBitlength := int32(639)
|
||||
if bitlength != expectedBitlength {
|
||||
t.Fatalf("Expected bit length to be %d, but got %d", expectedBitlength, bitlength)
|
||||
}
|
||||
|
||||
bitlengthTest = Signed640{
|
||||
|
||||
0xFFFFFFFFFFFFFaFF,
|
||||
0xFFFFFFFFFFFFFaFF,
|
||||
0xFFFFFFFFFFFFFaFF,
|
||||
0xFFFFFFFFFFFFFaFF,
|
||||
0xFFFFFFFFFFFFFaFF,
|
||||
0xFFFFFFFFFFFFFaFF,
|
||||
0xFFFFFFFFFFFFFaFF,
|
||||
0xFFFFFFFFFFFFFaFF,
|
||||
0xFFFFFFFFFFFFFaFF,
|
||||
0xFFFFFFFFFFFFFaFF,
|
||||
}
|
||||
|
||||
bitlength = bitlengthTest.Bitlength()
|
||||
|
||||
expectedBitlength = int32(587)
|
||||
if bitlength != expectedBitlength {
|
||||
t.Fatalf("Expected bit length to be %d, but got %d", expectedBitlength, bitlength)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd640(t *testing.T) {
|
||||
a := Signed640{
|
||||
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFaaaF,
|
||||
}
|
||||
|
||||
b := Signed640{
|
||||
|
||||
0xFFFFFabcdFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFF1234FFFFF,
|
||||
0,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFF1234FFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFF1234FFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
}
|
||||
|
||||
a.AddShifted(&b, 5543242)
|
||||
|
||||
expected := Signed640{
|
||||
|
||||
18446744073709551615,
|
||||
0,
|
||||
18446744073709551615,
|
||||
0,
|
||||
18446744073709551615,
|
||||
0,
|
||||
18446744073709551615,
|
||||
0,
|
||||
18446744073709551615,
|
||||
18446744073709529775,
|
||||
}
|
||||
|
||||
for i, limb := range a {
|
||||
if limb != expected[i] {
|
||||
t.Fatalf("Expected limb %d to be %x, but got %x", i, expected[i], limb)
|
||||
}
|
||||
}
|
||||
|
||||
a.AddShifted(&b, 63)
|
||||
|
||||
expected = Signed640{
|
||||
|
||||
9223372036854775807,
|
||||
18446741180780642304,
|
||||
18446744073709551614,
|
||||
0,
|
||||
9223372004938743807,
|
||||
9223372036854775809,
|
||||
18446744073709551614,
|
||||
18446744041793519616,
|
||||
18446744073709551614,
|
||||
18446744041793497775,
|
||||
}
|
||||
|
||||
for i, limb := range a {
|
||||
if limb != expected[i] {
|
||||
t.Fatalf("Expected limb %d to be %x, but got %x", i, expected[i], limb)
|
||||
}
|
||||
}
|
||||
|
||||
a.AddShifted(&b, 0)
|
||||
|
||||
expected = Signed640{
|
||||
|
||||
9223366250996957182,
|
||||
18446741180780642304,
|
||||
18446744073709551614,
|
||||
18446744009877487616,
|
||||
9223372004938743807,
|
||||
9223372036854775808,
|
||||
18446744009877487614,
|
||||
18446744041793519616,
|
||||
18446744009877487614,
|
||||
18446744041793497775,
|
||||
}
|
||||
|
||||
for i, limb := range a {
|
||||
if limb != expected[i] {
|
||||
t.Fatalf("Expected limb %d to be %x, but got %x", i, expected[i], limb)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubShifted640(t *testing.T) {
|
||||
// Create two Signed640 instances for the test
|
||||
a := Signed640{
|
||||
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFaaaF,
|
||||
}
|
||||
|
||||
b := Signed640{
|
||||
|
||||
0xFFFFFabcdFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFF1234FFFFF,
|
||||
0,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFF1234FFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
0xFFFFFFF1234FFFFF,
|
||||
0xFFFFFFFFFFFFFFFF,
|
||||
}
|
||||
|
||||
a.SubShifted(&b, 313)
|
||||
expectedLargeShift := Signed640{
|
||||
|
||||
18446744073709551615,
|
||||
0,
|
||||
18446744073709551615,
|
||||
0,
|
||||
18446744073709551615,
|
||||
144115188075855872,
|
||||
18446744073709551615,
|
||||
498688000,
|
||||
18446744073709551615,
|
||||
498666159,
|
||||
}
|
||||
|
||||
for i, limb := range a {
|
||||
if limb != expectedLargeShift[i] {
|
||||
t.Fatalf("sub_shifted (large shift): Expected limb %d to be %x, but got %x", i, expectedLargeShift[i], limb)
|
||||
}
|
||||
}
|
||||
|
||||
a.SubShifted(&b, 63)
|
||||
expectedSmallShift := Signed640{
|
||||
|
||||
9223372036854775807,
|
||||
2892928909313,
|
||||
18446744073709551615,
|
||||
0,
|
||||
9223372068770807807,
|
||||
9367487224930631680,
|
||||
18446744073709551615,
|
||||
32414720000,
|
||||
18446744073709551615,
|
||||
32414698159,
|
||||
}
|
||||
|
||||
for i, limb := range a {
|
||||
if limb != expectedSmallShift[i] {
|
||||
t.Fatalf("sub_shifted (small shift): Expected limb %d to be %x, but got %x", i, expectedSmallShift[i], limb)
|
||||
}
|
||||
}
|
||||
|
||||
a.SubShifted(&b, 0)
|
||||
expectedZeroShift := Signed640{
|
||||
|
||||
9223377822712594432,
|
||||
2892928909313,
|
||||
18446744073709551615,
|
||||
63832064000,
|
||||
9223372068770807806,
|
||||
9367487224930631681,
|
||||
63832063999,
|
||||
32414720001,
|
||||
63832063999,
|
||||
32414698160,
|
||||
}
|
||||
|
||||
for i, limb := range a {
|
||||
if limb != expectedZeroShift[i] {
|
||||
t.Fatalf("sub: Expected limb %d to be %x, but got %x", i, expectedZeroShift[i], limb)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package ecgfp5
|
||||
|
||||
// A custom 161-bit integer type; used for splitting a scalar into a
|
||||
// fraction. Negative values use two's complement notation; the value
|
||||
// is truncated to 161 bits (upper bits in the top limb are ignored).
|
||||
// Elements are mutable containers.
|
||||
// WARNING: everything in here is vartime; do not use on secret values.
|
||||
type Signed161 [3]uint64
|
||||
|
||||
// Export this value as a 192-bit integer (three 64-bit limbs, in little-endian order).
|
||||
func (s Signed161) ToU192() [3]uint64 {
|
||||
x := s[2] & 0x00000001FFFFFFFF
|
||||
x |= (^(x >> 32) + 1) << 33
|
||||
return [3]uint64{s[0], s[1], x}
|
||||
}
|
||||
|
||||
// Recode this integer into 33 signed digits for a 5-bit window.
|
||||
func (s Signed161) RecodeSigned5() [33]int32 {
|
||||
// We first sign-extend the value to 192 bits, then add
|
||||
// 2^160 to get a nonnegative value in the 0 to 2^161-1
|
||||
// range. We then recode that value; and finally we fix
|
||||
// the result by subtracting 1 from the top digit.
|
||||
tmp := s.ToU192()
|
||||
tmp[2] += 0x0000000100000000
|
||||
var ss [33]int32
|
||||
RecodeSignedFromLimbs(tmp[:], ss[:], 5)
|
||||
ss[32] -= 1
|
||||
return ss
|
||||
}
|
||||
|
||||
// Add v*2^s to this value.
|
||||
func (s *Signed161) AddShifted(v *Signed161, shift int32) {
|
||||
if shift == 0 {
|
||||
s.Add(v[:])
|
||||
} else if shift < 64 {
|
||||
s.AddShiftedSmall(v[:], shift)
|
||||
} else if shift < 161 {
|
||||
s.AddShiftedSmall(v[(shift>>6):], shift&63)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Signed161) AddShiftedSmall(v []uint64, shift int32) {
|
||||
cc, vbits, j := uint64(0), uint64(0), 3-len(v)
|
||||
for i := j; i < 3; i++ {
|
||||
vw := v[i-j]
|
||||
|
||||
vws := (vw << (uint32(shift) % 64)) | vbits
|
||||
vbits = vw >> ((64 - uint32(shift)) % 64)
|
||||
|
||||
z := U128From64(s[i]).Add64(vws).Add64(cc)
|
||||
s[i] = z.Lo
|
||||
cc = z.Hi
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Signed161) Add(v []uint64) {
|
||||
cc, j := uint64(0), 3-len(v)
|
||||
for i := j; i < 3; i++ {
|
||||
z := U128From64(s[i]).Add64(v[i-j]).Add64(cc)
|
||||
s[i] = z.Lo
|
||||
cc = z.Hi
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract v*2^s from this value.
|
||||
func (s *Signed161) SubShifted(v *Signed161, shift int32) {
|
||||
if shift == 0 {
|
||||
s.Sub(v[:])
|
||||
} else if shift < 64 {
|
||||
s.SubShiftedSmall(v[:], shift)
|
||||
} else if shift < 161 {
|
||||
s.SubShiftedSmall(v[(shift>>6):], shift&63)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Signed161) SubShiftedSmall(v []uint64, shift int32) {
|
||||
cc, vbits, j := uint64(0), uint64(0), 3-len(v)
|
||||
for i := j; i < 3; i++ {
|
||||
vw := v[i-j]
|
||||
vws := (vw << (uint32(shift) % 64)) | vbits
|
||||
vbits = vw >> ((64 - uint32(shift)) % 64)
|
||||
|
||||
z := U128From64(s[i]).Sub64(vws).Sub64(cc)
|
||||
s[i] = z.Lo
|
||||
cc = z.Hi & 1
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Signed161) Sub(v []uint64) {
|
||||
cc, j := uint64(0), 3-len(v)
|
||||
for i := j; i < 3; i++ {
|
||||
z := U128From64(s[i]).Sub64(v[i-j]).Sub64(cc)
|
||||
s[i] = z.Lo
|
||||
cc = z.Hi & 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package ecgfp5
|
||||
|
||||
// A custom 640-bit integer type (signed).
|
||||
// Elements are mutable containers.
|
||||
// WARNING: everything in here is vartime; do not use on secret values.
|
||||
type Signed640 [10]uint64
|
||||
|
||||
// Obtain an instance containing n^2.
|
||||
func FromNsquared() *Signed640 {
|
||||
return &Signed640{
|
||||
0x8E6B7A18061803C1,
|
||||
0x0AD8BDEE1594E2CF,
|
||||
0x17640E465F2598BC,
|
||||
0x90465B4214B27B1C,
|
||||
0xD308FECCB1878B88,
|
||||
0x3CC55EB2EAC07502,
|
||||
0x59F038FB784335CE,
|
||||
0xBFFFFE954FB808EA,
|
||||
0xBFFFFFCB80000099,
|
||||
0x3FFFFFFD8000000D,
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain an instance containing a*b (both a and b are interpreted
|
||||
// as integers in the 0..n-1 range).
|
||||
func FromMulScalars(a, b *ECgFp5Scalar) *Signed640 {
|
||||
var r Signed640
|
||||
for i := 0; i < 5; i++ {
|
||||
aw := a[i]
|
||||
cc := uint64(0)
|
||||
for j := 0; j < 5; j++ {
|
||||
z := U128From64(aw).Mul64(b[j]).Add64(r[i+j]).Add64(cc)
|
||||
r[i+j] = z.Lo
|
||||
cc = z.Hi
|
||||
}
|
||||
r[i+5] = cc
|
||||
}
|
||||
return &r
|
||||
}
|
||||
|
||||
// Add 1 to this instance.
|
||||
func (s *Signed640) Add1() {
|
||||
for i := 0; i < 10; i++ {
|
||||
s[i]++
|
||||
if s[i] != 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Signed640) IsNonnegative() bool {
|
||||
return (s[9] >> 63) == 0
|
||||
}
|
||||
|
||||
func (s *Signed640) LtUnsigned(rhs *Signed640) bool {
|
||||
for i := 9; i >= 0; i-- {
|
||||
aw := s[i]
|
||||
bw := rhs[i]
|
||||
if aw < bw {
|
||||
return true
|
||||
}
|
||||
if aw > bw {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Get the bit length of this value. The bit length is defined as the
|
||||
// minimal size of the binary representation in two's complement,
|
||||
// _excluding_ the sign bit (thus, -2^k has bit length k, whereas +2^k
|
||||
// has bit length k+1).
|
||||
func (s *Signed640) Bitlength() int32 {
|
||||
sm := (^(s[9] >> 63) + 1)
|
||||
for i := 9; i >= 0; i-- {
|
||||
w := s[i] ^ sm
|
||||
if w != 0 {
|
||||
return (int32(i) << 6) + U64Bitlength(w)
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func U64Bitlength(w uint64) int32 {
|
||||
// We use here a portable algorithm; some architectures have
|
||||
// dedicated opcodes that could speed up this operation
|
||||
// greatly (e.g. lzcnt on recent x86).
|
||||
var x = w
|
||||
var r int32
|
||||
if x > 0xFFFFFFFF {
|
||||
x >>= 32
|
||||
r += 32
|
||||
}
|
||||
if x > 0x0000FFFF {
|
||||
x >>= 16
|
||||
r += 16
|
||||
}
|
||||
if x > 0x000000FF {
|
||||
x >>= 8
|
||||
r += 8
|
||||
}
|
||||
if x > 0x0000000F {
|
||||
x >>= 4
|
||||
r += 4
|
||||
}
|
||||
if x > 0x00000003 {
|
||||
x >>= 2
|
||||
r += 2
|
||||
}
|
||||
return r + int32(x) - int32((x+1)>>2)
|
||||
}
|
||||
|
||||
// Add v*2^s to this instance.
|
||||
func (s *Signed640) AddShifted(v *Signed640, shift int32) {
|
||||
if shift == 0 {
|
||||
s.Add(v[:])
|
||||
} else if shift < 64 {
|
||||
s.AddShiftedSmall(v[:], shift)
|
||||
} else if shift < 640 {
|
||||
s.AddShiftedSmall(v[(shift>>6):], shift&63)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Signed640) AddShiftedSmall(v []uint64, shift int32) {
|
||||
cc := uint64(0)
|
||||
j := 10 - len(v)
|
||||
vbits := uint64(0)
|
||||
for i := j; i < 10; i++ {
|
||||
vw := v[i-j]
|
||||
|
||||
vws := (vw << (uint32(shift) % 64)) | vbits
|
||||
vbits = vw >> ((64 - uint32(shift)) % 64)
|
||||
|
||||
z := U128From64(s[i]).Add64(vws).Add64(cc)
|
||||
s[i] = z.Lo
|
||||
cc = z.Hi
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Signed640) Add(v []uint64) {
|
||||
cc := uint64(0)
|
||||
j := 10 - len(v)
|
||||
for i := j; i < 10; i++ {
|
||||
z := U128From64(s[i]).Add64(v[i-j]).Add64(cc)
|
||||
s[i] = z.Lo
|
||||
cc = z.Hi
|
||||
}
|
||||
}
|
||||
|
||||
// Subtract v*2^s from this instance.
|
||||
func (s *Signed640) SubShifted(v *Signed640, shift int32) {
|
||||
if shift == 0 {
|
||||
s.Sub(v[:])
|
||||
} else if shift < 64 {
|
||||
s.SubShiftedSmall(v[:], shift)
|
||||
} else if shift < 640 {
|
||||
s.SubShiftedSmall(v[(shift>>6):], shift&63)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Signed640) SubShiftedSmall(v []uint64, shift int32) {
|
||||
cc, vbits, j := uint64(0), uint64(0), 10-len(v)
|
||||
for i := j; i < 10; i++ {
|
||||
vw := v[i-j]
|
||||
|
||||
vws := (vw << (uint32(shift) % 64)) | vbits
|
||||
vbits = vw >> ((64 - uint32(shift)) % 64)
|
||||
|
||||
z := U128From64(s[i]).Sub64(vws).Sub64(cc)
|
||||
s[i] = z.Lo
|
||||
cc = z.Hi & 1
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Signed640) Sub(v []uint64) {
|
||||
cc, j := uint64(0), 10-len(v)
|
||||
for i := j; i < 10; i++ {
|
||||
z := U128From64(s[i]).Sub64(v[i-j]).Sub64(cc)
|
||||
s[i] = z.Lo
|
||||
cc = z.Hi & 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package ecgfp5
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
)
|
||||
|
||||
type U128 struct{ Hi, Lo uint64 }
|
||||
|
||||
func U128From64(v uint64) U128 { return U128{Lo: v} }
|
||||
|
||||
func (u U128) Add64(n uint64) (v U128) {
|
||||
var carry uint64
|
||||
v.Lo, carry = bits.Add64(u.Lo, n, 0)
|
||||
v.Hi = u.Hi + carry
|
||||
return v
|
||||
}
|
||||
|
||||
func (u U128) Sub64(n uint64) (v U128) {
|
||||
var borrowed uint64
|
||||
v.Lo, borrowed = bits.Sub64(u.Lo, n, 0)
|
||||
v.Hi = u.Hi - borrowed
|
||||
return v
|
||||
}
|
||||
|
||||
func (u U128) Mul64(n uint64) (dest U128) {
|
||||
dest.Hi, dest.Lo = bits.Mul64(u.Lo, n)
|
||||
dest.Hi += u.Hi * n
|
||||
return dest
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package ecgfp5
|
||||
|
||||
import (
|
||||
g "github.com/elliottech/poseidon_crypto/field/goldilocks"
|
||||
gFp5 "github.com/elliottech/poseidon_crypto/field/goldilocks_quintic_extension"
|
||||
)
|
||||
|
||||
// A curve point in short Weirstrass form (x, y). This is used by the in-circuit representation
|
||||
type WeierstrassPoint struct {
|
||||
X gFp5.Element
|
||||
Y gFp5.Element
|
||||
IsInf bool
|
||||
}
|
||||
|
||||
var (
|
||||
GENERATOR_WEIERSTRASS = WeierstrassPoint{
|
||||
X: gFp5.Element{
|
||||
g.FromUint64(11712523173042564207),
|
||||
g.FromUint64(14090224426659529053),
|
||||
g.FromUint64(13197813503519687414),
|
||||
g.FromUint64(16280770174934269299),
|
||||
g.FromUint64(15998333998318935536),
|
||||
},
|
||||
Y: gFp5.Element{
|
||||
g.FromUint64(14639054205878357578),
|
||||
g.FromUint64(17426078571020221072),
|
||||
g.FromUint64(2548978194165003307),
|
||||
g.FromUint64(8663895577921260088),
|
||||
g.FromUint64(9793640284382595140),
|
||||
},
|
||||
IsInf: false,
|
||||
}
|
||||
|
||||
A_WEIERSTRASS = gFp5.Element{
|
||||
g.FromUint64(6148914689804861439),
|
||||
g.FromUint64(263),
|
||||
g.FromUint64(0),
|
||||
g.FromUint64(0),
|
||||
g.FromUint64(0),
|
||||
}
|
||||
|
||||
NEUTRAL_WEIERSTRASS = WeierstrassPoint{
|
||||
X: gFp5.FP5_ZERO,
|
||||
Y: gFp5.FP5_ZERO,
|
||||
IsInf: true,
|
||||
}
|
||||
)
|
||||
|
||||
func (p WeierstrassPoint) Equals(q WeierstrassPoint) bool {
|
||||
if p.IsInf && q.IsInf {
|
||||
return true
|
||||
}
|
||||
return gFp5.Equals(p.X, q.X) && gFp5.Equals(p.Y, q.Y)
|
||||
}
|
||||
|
||||
func (p WeierstrassPoint) Encode() gFp5.Element {
|
||||
return gFp5.Div(p.Y, gFp5.Sub(gFp5.Div(A_ECgFp5Point, gFp5.FromUint64(3)), p.X))
|
||||
}
|
||||
|
||||
func DecodeFp5AsWeierstrass(w gFp5.Element) (WeierstrassPoint, bool) {
|
||||
e := gFp5.Sub(gFp5.Square(w), A_ECgFp5Point)
|
||||
delta := gFp5.Sub(gFp5.Square(e), B_MUL4_ECgFp5Point)
|
||||
r, success := gFp5.CanonicalSqrt(delta)
|
||||
if !success {
|
||||
r = gFp5.FP5_ZERO
|
||||
}
|
||||
|
||||
x1 := gFp5.Div(gFp5.Add(e, r), gFp5.FP5_TWO)
|
||||
x2 := gFp5.Div(gFp5.Sub(e, r), gFp5.FP5_TWO)
|
||||
|
||||
x := x1
|
||||
x1Legendre := gFp5.Legendre(x1)
|
||||
if !x1Legendre.IsOne() {
|
||||
x = x2
|
||||
}
|
||||
|
||||
y := gFp5.Neg(gFp5.Mul(w, x))
|
||||
if success {
|
||||
x = gFp5.Add(x, gFp5.Div(A_ECgFp5Point, gFp5.FromUint64(3)))
|
||||
} else {
|
||||
x = gFp5.FP5_ZERO
|
||||
}
|
||||
|
||||
isInf := !success
|
||||
|
||||
// If w == 0 then this is in fact a success.
|
||||
if success || gFp5.IsZero(w) {
|
||||
return WeierstrassPoint{X: x, Y: y, IsInf: isInf}, true
|
||||
}
|
||||
return WeierstrassPoint{}, false
|
||||
}
|
||||
|
||||
func (p WeierstrassPoint) Add(q WeierstrassPoint) WeierstrassPoint {
|
||||
if p.IsInf {
|
||||
return q
|
||||
}
|
||||
if q.IsInf {
|
||||
return p
|
||||
}
|
||||
|
||||
x1, y1 := p.X, p.Y
|
||||
x2, y2 := q.X, q.Y
|
||||
|
||||
// note: paper has a typo. sx == 1 when x1 != x2, not when x1 == x2
|
||||
xSame := gFp5.Equals(x1, x2)
|
||||
yDiff := !gFp5.Equals(y1, y2)
|
||||
|
||||
var lambda0, lambda1 gFp5.Element
|
||||
if xSame {
|
||||
lambda0 = gFp5.Add(gFp5.Triple(gFp5.Square(x1)), A_WEIERSTRASS)
|
||||
lambda1 = gFp5.Double(y1)
|
||||
} else {
|
||||
lambda0 = gFp5.Sub(y2, y1)
|
||||
lambda1 = gFp5.Sub(x2, x1)
|
||||
}
|
||||
lambda := gFp5.Div(lambda0, lambda1)
|
||||
|
||||
x3 := gFp5.Sub(gFp5.Sub(gFp5.Square(lambda), x1), x2)
|
||||
y3 := gFp5.Sub(gFp5.Mul(lambda, gFp5.Sub(x1, x3)), y1)
|
||||
|
||||
return WeierstrassPoint{X: x3, Y: y3, IsInf: xSame && yDiff}
|
||||
}
|
||||
|
||||
func (p WeierstrassPoint) Double() WeierstrassPoint {
|
||||
x := p.X
|
||||
y := p.Y
|
||||
is_inf := p.IsInf
|
||||
|
||||
if is_inf {
|
||||
return p
|
||||
}
|
||||
|
||||
lambda0 := gFp5.Square(x)
|
||||
lambda0 = gFp5.Triple(lambda0)
|
||||
lambda0 = gFp5.Add(lambda0, A_WEIERSTRASS)
|
||||
|
||||
lambda1 := gFp5.Double(y)
|
||||
|
||||
lambda := gFp5.Div(lambda0, lambda1)
|
||||
|
||||
x2 := gFp5.Square(lambda)
|
||||
two_x := gFp5.Double(x)
|
||||
x2 = gFp5.Sub(x2, two_x)
|
||||
|
||||
y2 := gFp5.Sub(x, x2)
|
||||
y2 = gFp5.Mul(lambda, y2)
|
||||
y2 = gFp5.Sub(y2, y)
|
||||
|
||||
return WeierstrassPoint{X: x2, Y: y2, IsInf: is_inf}
|
||||
}
|
||||
|
||||
func (p WeierstrassPoint) PrecomputeWindow(windowBits uint32) []WeierstrassPoint {
|
||||
if windowBits < 2 {
|
||||
panic("windowBits in PrecomputeWindow for WeierstrassPoint must be at least 2")
|
||||
}
|
||||
multiples := []WeierstrassPoint{NEUTRAL_WEIERSTRASS, p, p.Double()}
|
||||
for i := 3; i < 1<<windowBits; i++ {
|
||||
multiples = append(multiples, p.Add(multiples[len(multiples)-1]))
|
||||
}
|
||||
return multiples
|
||||
}
|
||||
|
||||
func MulAdd2(a, b WeierstrassPoint, scalarA, scalarB ECgFp5Scalar) WeierstrassPoint {
|
||||
aWindow := a.PrecomputeWindow(4)
|
||||
aFourBitLimbs := scalarA.SplitTo4BitLimbs()
|
||||
|
||||
bWindow := b.PrecomputeWindow(4)
|
||||
bFourBitLimbs := scalarB.SplitTo4BitLimbs()
|
||||
|
||||
numLimbs := len(aFourBitLimbs)
|
||||
|
||||
res := aWindow[aFourBitLimbs[numLimbs-1]].Add(bWindow[bFourBitLimbs[numLimbs-1]])
|
||||
for i := numLimbs - 2; i >= 0; i-- {
|
||||
for j := 0; j < 4; j++ {
|
||||
res = res.Double()
|
||||
}
|
||||
res = res.Add(aWindow[aFourBitLimbs[i]].Add(bWindow[bFourBitLimbs[i]]))
|
||||
}
|
||||
return res
|
||||
}
|
||||
Reference in New Issue
Block a user