Skip to content

Commit f5957e7

Browse files
committed
Updating Decaf documentation.
1 parent 6f573e6 commit f5957e7

File tree

3 files changed

+69
-75
lines changed

3 files changed

+69
-75
lines changed

ecc/decaf/constants.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ const EncodingSize = fp.Size
1313
var ErrInvalidDecoding = errors.New("invalid decoding")
1414

1515
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{
16+
// aMinusD is paramA-paramD = (-1)-(-39082) = 39081.
17+
aMinusD = fp.Elt{0xa9, 0x98}
18+
// sqrtAMinusD is the smallest root of sqrt(paramA-paramD) = sqrt(39081).
19+
sqrtAMinusD = fp.Elt{
2020
0x36, 0x27, 0x57, 0x45, 0x0f, 0xef, 0x42, 0x96,
2121
0x52, 0xce, 0x20, 0xaa, 0xf6, 0x7b, 0x33, 0x60,
2222
0xd2, 0xde, 0x6e, 0xfd, 0xf4, 0x66, 0x9a, 0x83,

ecc/decaf/decaf.go

+64-71
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
//
66
// Decaf (3) is a prime-order group constructed as a quotient of groups. A Decaf
77
// element can be represented by any point in the coset P+J[2], where J is a
8-
// Jacobi quartic and J[2] are its 2-torsion points.
8+
// Jacobi quartic curve and J[2] are its 2-torsion points.
99
// Since P+J[2] has four points, Decaf specifies rules to choose one canonical
1010
// representative, which has a unique encoding. Two representations are
1111
// equivalent if they belong to the same coset.
@@ -15,17 +15,9 @@
1515
//
1616
// Version
1717
//
18-
// This implementation uses Decaf v1.0 of the encoding (see (4) for a complete
18+
// This implementation uses Decaf v1.0 of the encoding (see (4,5) for a complete
1919
// specification).
2020
//
21-
// Internals
22-
//
23-
// Decaf uses as internal representation the curve
24-
// ted448: ax^2+y^2 = 1 + dx^2y^2, where a=-1 and d=-39082.
25-
// This curve is 4-degree isogeneous to the Goldilocks curve, and 2-degree
26-
// isogeneous to the Jacobi quartic. The ted448 curve was chosen because it
27-
// provides faster arithmetic operations.
28-
//
2921
// References
3022
//
3123
// (1) https://www.shiftleft.org/papers/goldilocks
@@ -35,6 +27,8 @@
3527
// (3) https://doi.org/10.1007/978-3-662-47989-6_34 and https://www.shiftleft.org/papers/decaf
3628
//
3729
// (4) https://sourceforge.net/p/ed448goldilocks/code/ci/v1.0/tree/
30+
//
31+
// (5) https://mailarchive.ietf.org/arch/msg/cfrg/S4YUTt_5eD4kwYbDuhEK0tXT1aM/
3832
package decaf
3933

4034
import (
@@ -96,8 +90,8 @@ func (e *Elt) IsEqual(a *Elt) bool {
9690
return fp.IsZero(l)
9791
}
9892

99-
// UnmarshalBinary if succeeds returns a Decaf element by decoding the first
100-
// DecafEncodingSize bytes of data.
93+
// UnmarshalBinary interprets the first EncodingSize bytes passed in data, and
94+
// returns a Decaf element.
10195
func (e *Elt) UnmarshalBinary(data []byte) error {
10296
if len(data) < EncodingSize {
10397
return ErrInvalidDecoding
@@ -109,35 +103,34 @@ func (e *Elt) UnmarshalBinary(data []byte) error {
109103
isLessThanP := isLessThan(s[:], p[:])
110104
isPositiveS := fp.Parity(s) == 0
111105

112-
s2, den, num := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
113-
isr, altx := &fp.Elt{}, &fp.Elt{}
114-
t0, t1 := &fp.Elt{}, &fp.Elt{}
106+
den, num := &fp.Elt{}, &fp.Elt{}
107+
isr, altx, t0 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
115108
x, y := &fp.Elt{}, &fp.Elt{}
116109
one := fp.One()
117-
fp.Sqr(s2, s) // s2 = s^2
118-
fp.Sub(den, &one, s2) // den = 1 + a*s^2
119-
fp.Mul(t1, s2, &ted448.ParamD) // t1 = d*s^2
120-
fp.Add(t1, t1, t1) // t1 = 2*d*s^2
121-
fp.Add(t1, t1, t1) // t1 = 4*d*s^2
122-
fp.Sqr(t0, den) // num = (1 + a*s^2)^2
123-
fp.Sub(num, t0, t1) // num = (1 + a*s^2)^2 - 4*d*s^2
124-
fp.Mul(t0, t0, num) // t0 = num*den^2
125-
isQR := fp.InvSqrt(isr, &one, t0) // v = 1/sqrt(num*den^2)
126-
fp.Mul(t1, den, isr) // altx = isr*den
127-
fp.Mul(t1, t1, s) // altx = s*isr*den
128-
fp.Add(t1, t1, t1) // t1 = 2*s*isr*den
129-
fp.Mul(altx, t1, &sqrtAMinusDTwist) // altx = 2*s*isr*den*sqrt(A-D)
130-
isNegX := fp.Parity(altx) // isNeg = sgn(altx)
131-
fp.Neg(t0, isr) // t0 = -isr
132-
fp.Cmov(isr, t0, uint(isNegX)) // if altx is negative then isr = -isr
133-
fp.Sqr(x, isr) // x = isr^2
134-
fp.Mul(x, x, den) // x = isr^2*den
135-
fp.Mul(x, x, num) // x = isr^2*den*num
136-
fp.Mul(x, x, s) // x = s*isr^2*den*num
137-
fp.Add(x, x, x) // x = 2*s*isr^2*den*num
138-
fp.Mul(y, isr, den) // y = isr*den
139-
fp.Add(t0, &one, s2) // t0 = 1 - a*s^2
140-
fp.Mul(y, y, t0) // y = (1 - a*s^2)*isr*den
110+
paramD := ted448.ParamD()
111+
fp.Sqr(t0, s) // t0 = s^2
112+
fp.Sub(den, &one, t0) // den = 1 + a*s^2
113+
fp.Add(y, &one, t0) // y = 1 - a*s^2
114+
fp.Mul(num, t0, &paramD) // num = d*s^2
115+
fp.Add(num, num, num) // = 2*d*s^2
116+
fp.Add(num, num, num) // = 4*d*s^2
117+
fp.Sqr(t0, den) // t0 = den^2 = (1 + a*s^2)^2
118+
fp.Sub(num, t0, num) // num = den^2 - 4*d*s^2
119+
fp.Mul(t0, t0, num) // t0 = den^2*num
120+
isQR := fp.InvSqrt(isr, &one, t0) // isr = 1/(den*sqrt(num))
121+
fp.Mul(altx, isr, den) // altx = isr*den
122+
fp.Mul(altx, altx, s) // = s*isr*den
123+
fp.Add(altx, altx, altx) // = 2*s*isr*den
124+
fp.Mul(altx, altx, &sqrtAMinusD) // = 2*s*isr*den*sqrt(A-D)
125+
isNegX := fp.Parity(altx) // isNeg = sgn(altx)
126+
fp.Neg(t0, isr) // t0 = -isr
127+
fp.Cmov(isr, t0, uint(isNegX)) // if altx is negative then isr = -isr
128+
fp.Mul(t0, isr, den) // t0 = isr*den
129+
fp.Mul(x, t0, isr) // x = isr^2*den
130+
fp.Mul(x, x, num) // x = isr^2*den*num
131+
fp.Mul(x, x, s) // x = s*isr^2*den*num
132+
fp.Add(x, x, x) // x = 2*s*isr^2*den*num
133+
fp.Mul(y, y, t0) // y = (1 - a*s^2)*isr*den
141134

142135
isValid := isPositiveS && isLessThanP && isQR
143136
b := uint(*((*byte)(unsafe.Pointer(&isValid))))
@@ -146,45 +139,45 @@ func (e *Elt) UnmarshalBinary(data []byte) error {
146139
fp.Cmov(&e.p.Ta, x, b)
147140
fp.Cmov(&e.p.Tb, y, b)
148141
fp.Cmov(&e.p.Z, &one, b)
149-
var err error
150142
if !isValid {
151-
err = ErrInvalidDecoding
143+
return ErrInvalidDecoding
152144
}
153-
return err
145+
return nil
154146
}
155147

156148
// MarshalBinary returns a unique encoding of the element e.
157149
func (e *Elt) MarshalBinary() ([]byte, error) {
158-
x, ta, tb, z := &e.p.X, &e.p.Ta, &e.p.Tb, &e.p.Z
159-
one, t, t2, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
160-
fp.SetOne(one)
161-
fp.Mul(t, ta, tb) // t = ta*tb
162-
t0, t1 := *x, *t // (t0,t1) = (x,t)
163-
fp.Sqr(t2, x) // t2 = x^2
164-
fp.AddSub(&t0, &t1) // (t0,t1) = (x+t,x-t)
165-
fp.Mul(&t1, &t0, &t1) // t1 = (x+t)*(x-t)
166-
fp.Mul(&t0, &t1, &aMinusDTwist) // t0 = (a-d)*(x+t)*(x-t)
167-
fp.Mul(&t0, &t0, t2) // t0 = x^2*(a-d)*(x+t)*(x-t)
168-
fp.InvSqrt(&t0, one, &t0) // t0 = 1/sqrt( x^2*(a-d)*(z+y)*(z-y) )
169-
fp.Mul(&t1, &t1, &t0) // t1 = (z+y)*(z-y)/sqrt( x^2*(a-d)*(z+y)*(z-y) )
170-
fp.Mul(t2, &t1, &sqrtAMinusDTwist) // t2 = sqrt( (z+y)*(z-y) )/z
171-
isNeg := fp.Parity(t2) // isNeg = sgn(t2)
172-
fp.Neg(t2, &t1) // t2 = -t1
173-
fp.Cmov(&t1, t2, uint(isNeg)) // if t2 is negative then t1 = -t1
174-
fp.Mul(s, &t1, z) // s = t1*z
175-
fp.Sub(s, s, t) // s = t1*z - t
176-
fp.Mul(s, s, x) // s = x*(t1*z - t)
177-
fp.Mul(s, s, &t0) // s = isr*x*(t1*z - t)
178-
fp.Mul(s, s, &aMinusDTwist) // s = (a-d)*isr*x*(t1*z - t)
179-
isNeg = fp.Parity(s) // isNeg = sgn(s)
180-
fp.Neg(&t0, s) // t0 = -s
181-
fp.Cmov(s, &t0, uint(isNeg)) // if s is negative then s = -s
182-
183150
var encS [EncodingSize]byte
184-
if err := fp.ToBytes(encS[:], s); err != nil {
185-
return nil, err
186-
}
187-
return encS[:], nil
151+
err := e.marshalBinary(encS[:])
152+
return encS[:], err
153+
}
154+
155+
func (e *Elt) marshalBinary(enc []byte) error {
156+
x, ta, tb, z := &e.p.X, &e.p.Ta, &e.p.Tb, &e.p.Z
157+
t, t2, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
158+
one := fp.One()
159+
fp.Mul(t, ta, tb) // t = ta*tb
160+
t0, t1 := *x, *t // (t0,t1) = (x,t)
161+
fp.AddSub(&t0, &t1) // (t0,t1) = (x+t,x-t)
162+
fp.Mul(&t1, &t0, &t1) // t1 = num = (x+t)*(x-t) = x^2*(z^2-y^2)/z^2
163+
fp.Mul(&t0, &t1, &aMinusD) // t0 = (a-d)*(x+t)*(x-t) = (a-d)*x^2*(z^2-y^2)/z^2
164+
fp.Sqr(t2, x) // t2 = x^2
165+
fp.Mul(&t0, &t0, t2) // t0 = x^2*(a-d)*(x+t)*(x-t) = (a-d)*x^4*(z^2-y^2)/z^2
166+
fp.InvSqrt(&t0, &one, &t0) // t0 = isr = z/(x^2*sqrt((a-d)*(z^2-y^2)))
167+
fp.Mul(&t1, &t1, &t0) // t1 = ratio = (z^2-y^2)/(z*sqrt((a-d)*(z^2-y^2)))
168+
fp.Mul(t2, &t1, &sqrtAMinusD) // t2 = altx = sqrt((z^2-y^2))/z
169+
isNeg := fp.Parity(t2) // isNeg = sgn(t2)
170+
fp.Neg(t2, &t1) // t2 = -t1
171+
fp.Cmov(&t1, t2, uint(isNeg)) // if t2 is negative then t1 = -t1
172+
fp.Mul(s, &t1, z) // s = t1*z
173+
fp.Sub(s, s, t) // s = t1*z - t
174+
fp.Mul(s, s, x) // s = x*(t1*z - t)
175+
fp.Mul(s, s, &t0) // s = isr*x*(t1*z - t)
176+
fp.Mul(s, s, &aMinusD) // s = (a-d)*isr*x*(t1*z - t)
177+
isNeg = fp.Parity(s) // isNeg = sgn(s)
178+
fp.Neg(&t0, s) // t0 = -s
179+
fp.Cmov(s, &t0, uint(isNeg)) // if s is negative then s = -s
180+
return fp.ToBytes(enc[:], s)
188181
}
189182

190183
// isLessThan returns true if 0 <= x < y, and assumes that slices are of the

ecc/decaf/decaf_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ func TestPointNeg(t *testing.T) {
164164
}
165165
}
166166
}
167+
167168
func TestDecafOrder(t *testing.T) {
168169
const testTimes = 1 << 10
169170
Q := &decaf.Elt{}

0 commit comments

Comments
 (0)