Skip to content

Commit f329f01

Browse files
committed
Moving twist implementation to an internal package.
1 parent 6240da6 commit f329f01

13 files changed

+1788
-5
lines changed

.etc/golangci.yml

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
---
12
linters:
23
enable-all: true
34
disable:
4-
- testpackage # it can be improved
5-
- lll # it can be improved
6-
- gocritic # it can be improved
7-
- gocognit # it can be improved
5+
- testpackage # it can be improved
6+
- lll # it can be improved
7+
- gocritic # it can be improved
8+
- gocognit # it can be improved
89
- gochecknoglobals
910
- gochecknoinits
1011
- gomnd
@@ -17,7 +18,8 @@ linters-settings:
1718
statements: 80
1819
nestif:
1920
min-complexity: 6
20-
21+
dogsled:
22+
max-blank-identifiers: 3
2123
issues:
2224
max-issues-per-linter: 0
2325
max-same-issues: 0

ecc/decaf/constants.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package decaf
2+
3+
import (
4+
"errors"
5+
6+
fp "github.com/cloudflare/circl/math/fp448"
7+
)
8+
9+
// DecafEncodingSize is the size (in bytes) of an encoded Decaf element.
10+
const EncodingSize = fp.Size
11+
12+
// ErrInvalidDecoding alerts of an error during decoding a point.
13+
var ErrInvalidDecoding = errors.New("invalid decoding")
14+
15+
var (
16+
// aMinusD is paramA-paramD used in Decaf.
17+
aMinusDTwist = fp.Elt{0xa9, 0x98}
18+
// sqrtAMinusDTwist is the smallest sqrt(paramATwist-paramDTwist) used in Decaf.
19+
sqrtAMinusDTwist = fp.Elt{
20+
0x36, 0x27, 0x57, 0x45, 0x0f, 0xef, 0x42, 0x96,
21+
0x52, 0xce, 0x20, 0xaa, 0xf6, 0x7b, 0x33, 0x60,
22+
0xd2, 0xde, 0x6e, 0xfd, 0xf4, 0x66, 0x9a, 0x83,
23+
0xba, 0x14, 0x8c, 0x96, 0x80, 0xd7, 0xa2, 0x64,
24+
0x4b, 0xd5, 0xb8, 0xa5, 0xb8, 0xa7, 0xf1, 0xa1,
25+
0xa0, 0x6a, 0xa2, 0x2f, 0x72, 0x8d, 0xf6, 0x3b,
26+
0x68, 0xf7, 0x24, 0xeb, 0xfb, 0x62, 0xd9, 0x22,
27+
}
28+
)

ecc/decaf/decaf.go

+169
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Package decaf provides operations on a prime-order group derived from the goldilocks curve.
2+
//
3+
// Its internal implementation uses the twist of the goldilocks curve.
4+
// This implementation uses Decaf v1.0 of the encoding.
5+
//
6+
// References:
7+
// - https://www.shiftleft.org/papers/decaf/
8+
// - https://www.shiftleft.org/papers/goldilocks
9+
// - https://sourceforge.net/p/ed448goldilocks/code/ci/v1.0/tree/
10+
package decaf
11+
12+
import (
13+
"github.com/cloudflare/circl/internal/ted448"
14+
fp "github.com/cloudflare/circl/math/fp448"
15+
)
16+
17+
// Version targets Decaf v1.0 of the encoding. As implemented in https://sourceforge.net/p/ed448goldilocks/code/ci/v1.0/tree/.
18+
const Version = "v1.0"
19+
20+
// Elt is an element of the Decaf group. It must be always initialized using
21+
// one of the Decaf functions.
22+
type Elt struct{ p ted448.Point }
23+
24+
// Scalar represents a positive integer stored in little-endian order.
25+
type Scalar = ted448.Scalar
26+
27+
func (e Elt) String() string { return e.p.String() }
28+
29+
// IsValid returns True if a is a valid element of the group.
30+
func IsValid(a *Elt) bool { return ted448.IsOnCurve(&a.p) }
31+
32+
// Identity returns the identity element of the group.
33+
func Identity() *Elt { return &Elt{ted448.Identity()} }
34+
35+
// Generator returns the generator element of the group.
36+
func Generator() *Elt { return &Elt{ted448.Generator()} }
37+
38+
// Order returns a scalar with the order of the group.
39+
func Order() Scalar { return ted448.Order() }
40+
41+
// Neg calculates c=-a, where - is the inverse of the group operation.
42+
func Neg(c, a *Elt) { c.p = a.p; c.p.Neg() }
43+
44+
// Add calculates c=a+b, where + is the group operation.
45+
func Add(c, a, b *Elt) { q := a.p; q.Add(&b.p); c.p = q }
46+
47+
// Double calculates c=a+a, where + is the group operation.
48+
func Double(c, a *Elt) { c.p = a.p; c.p.Double() }
49+
50+
// Mul calculates c=n*a, where * is scalar multiplication on the group.
51+
func Mul(c *Elt, n *Scalar, a *Elt) { ted448.ScalarMult(&c.p, n, &a.p) }
52+
53+
// MulGen calculates c=n*g, where * is scalar multiplication on the group,
54+
// and g is the generator of the group.
55+
func MulGen(c *Elt, n *Scalar) { ted448.ScalarBaseMult(&c.p, n) }
56+
57+
// IsIdentity returns True if e is the identity of the group.
58+
func (e *Elt) IsIdentity() bool {
59+
x, y, _, _, z := e.p.Coordinates()
60+
return fp.IsZero(&x) && !fp.IsZero(&y) && !fp.IsZero(&z)
61+
}
62+
63+
// IsEqual returns True if e=a, where = is an equivalence relation.
64+
func (e *Elt) IsEqual(a *Elt) bool {
65+
x1, y1, _, _, _ := e.p.Coordinates()
66+
x2, y2, _, _, _ := a.p.Coordinates()
67+
68+
l, r := &fp.Elt{}, &fp.Elt{}
69+
fp.Mul(l, &x1, &y2)
70+
fp.Mul(r, &x2, &y1)
71+
fp.Sub(l, l, r)
72+
return fp.IsZero(l)
73+
}
74+
75+
// UnmarshalBinary if succeeds returns a Decaf element by decoding the first
76+
// DecafEncodingSize bytes of data.
77+
func (e *Elt) UnmarshalBinary(data []byte) error {
78+
if len(data) < EncodingSize {
79+
return ErrInvalidDecoding
80+
}
81+
82+
s := &fp.Elt{}
83+
copy(s[:], data[:EncodingSize])
84+
p := fp.P()
85+
isLessThanP := isLessThan(s[:], p[:])
86+
isPositiveS := fp.Parity(s) == 0
87+
88+
s2, den, num := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
89+
isr, altx := &fp.Elt{}, &fp.Elt{}
90+
t0, t1 := &fp.Elt{}, &fp.Elt{}
91+
x, y := &fp.Elt{}, &fp.Elt{}
92+
one := fp.One()
93+
fp.Sqr(s2, s) // s2 = s^2
94+
fp.Sub(den, &one, s2) // den = 1 + a*s^2
95+
fp.Mul(t1, s2, &ted448.ParamD) // t1 = d*s^2
96+
fp.Add(t1, t1, t1) // t1 = 2*d*s^2
97+
fp.Add(t1, t1, t1) // t1 = 4*d*s^2
98+
fp.Sqr(t0, den) // num = (1 + a*s^2)^2
99+
fp.Sub(num, t0, t1) // num = (1 + a*s^2)^2 - 4*d*s^2
100+
fp.Mul(t0, t0, num) // t0 = num*den^2
101+
isQR := fp.InvSqrt(isr, &one, t0) // v = 1/sqrt(num*den^2)
102+
fp.Mul(t1, den, isr) // altx = isr*den
103+
fp.Mul(t1, t1, s) // altx = s*isr*den
104+
fp.Add(t1, t1, t1) // t1 = 2*s*isr*den
105+
fp.Mul(altx, t1, &sqrtAMinusDTwist) // altx = 2*s*isr*den*sqrt(A-D)
106+
isNegX := fp.Parity(altx) // isNeg = sgn(altx)
107+
fp.Neg(t0, isr) // t0 = -isr
108+
fp.Cmov(isr, t0, uint(isNegX)) // if altx is negative then isr = -isr
109+
fp.Sqr(x, isr) // x = isr^2
110+
fp.Mul(x, x, den) // x = isr^2*den
111+
fp.Mul(x, x, num) // x = isr^2*den*num
112+
fp.Mul(x, x, s) // x = s*isr^2*den*num
113+
fp.Add(x, x, x) // x = 2*s*isr^2*den*num
114+
fp.Mul(y, isr, den) // y = isr*den
115+
fp.Add(t0, &one, s2) // t0 = 1 - a*s^2
116+
fp.Mul(y, y, t0) // y = (1 - a*s^2)*isr*den
117+
118+
isValid := isPositiveS && isLessThanP && isQR
119+
P, err := ted448.NewPoint(x, y)
120+
if !isValid || err != nil {
121+
return ErrInvalidDecoding
122+
}
123+
e.p = *P
124+
return nil
125+
}
126+
127+
// MarshalBinary returns a unique encoding of the element e.
128+
func (e *Elt) MarshalBinary() ([]byte, error) {
129+
x, _, ta, tb, z := e.p.Coordinates()
130+
one, t, t2, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
131+
fp.SetOne(one)
132+
fp.Mul(t, &ta, &tb) // t = ta*tb
133+
t0, t1 := x, *t // (t0,t1) = (x,t)
134+
fp.Sqr(t2, &x) // t2 = x^2
135+
fp.AddSub(&t0, &t1) // (t0,t1) = (x+t,x-t)
136+
fp.Mul(&t1, &t0, &t1) // t1 = (x+t)*(x-t)
137+
fp.Mul(&t0, &t1, &aMinusDTwist) // t0 = (a-d)*(x+t)*(x-t)
138+
fp.Mul(&t0, &t0, t2) // t0 = x^2*(a-d)*(x+t)*(x-t)
139+
fp.InvSqrt(&t0, one, &t0) // t0 = 1/sqrt( x^2*(a-d)*(z+y)*(z-y) )
140+
fp.Mul(&t1, &t1, &t0) // t1 = (z+y)*(z-y)/sqrt( x^2*(a-d)*(z+y)*(z-y) )
141+
fp.Mul(t2, &t1, &sqrtAMinusDTwist) // t2 = sqrt( (z+y)*(z-y) )/z
142+
isNeg := fp.Parity(t2) // isNeg = sgn(t2)
143+
fp.Neg(t2, &t1) // t2 = -t1
144+
fp.Cmov(&t1, t2, uint(isNeg)) // if t2 is negative then t1 = -t1
145+
fp.Mul(s, &t1, &z) // s = t1*z
146+
fp.Sub(s, s, t) // s = t1*z - t
147+
fp.Mul(s, s, &x) // s = x*(t1*z - t)
148+
fp.Mul(s, s, &t0) // s = isr*x*(t1*z - t)
149+
fp.Mul(s, s, &aMinusDTwist) // s = (a-d)*isr*x*(t1*z - t)
150+
isNeg = fp.Parity(s) // isNeg = sgn(s)
151+
fp.Neg(&t0, s) // t0 = -s
152+
fp.Cmov(s, &t0, uint(isNeg)) // if s is negative then s = -s
153+
154+
var encS [EncodingSize]byte
155+
if err := fp.ToBytes(encS[:], s); err != nil {
156+
return nil, err
157+
}
158+
return encS[:], nil
159+
}
160+
161+
// isLessThan returns true if 0 <= x < y, and assumes that slices are of the
162+
// same length and are interpreted in little-endian order.
163+
func isLessThan(x, y []byte) bool {
164+
i := len(x) - 1
165+
for i > 0 && x[i] == y[i] {
166+
i--
167+
}
168+
return x[i] < y[i]
169+
}

0 commit comments

Comments
 (0)