Skip to content

Commit 87b9a72

Browse files
committed
Adds decaf prime-order group.
Squashed commit of the following: commit ec347f5 Author: armfazh <armfazh@cloudflare.com> Date: Fri Jul 24 15:28:34 2020 -0700 Adding decaf group. commit 796b37e Author: armfazh <armfazh@cloudflare.com> Date: Wed Jul 22 19:44:07 2020 -0700 Updates internal packages of Ed448. commit f5957e7 Author: armfazh <armfazh@cloudflare.com> Date: Wed Jul 22 19:11:15 2020 -0700 Updating Decaf documentation. commit 6f573e6 Author: armfazh <armfazh@cloudflare.com> Date: Wed Jul 22 13:56:12 2020 -0700 Updating documentation of ted448 internal package. commit 7e80bfc Author: armfazh <armfazh@cloudflare.com> Date: Wed Jun 10 01:56:31 2020 -0700 Adapting ed448 for using the internal ted448 package. commit 44ce6c5 Author: armfazh <armfazh@cloudflare.com> Date: Tue Jun 9 23:36:17 2020 -0700 ted448 point public fields. commit 13dd30d Author: armfazh <armfazh@cloudflare.com> Date: Tue Jun 9 20:32:32 2020 -0700 Moving twist implementation to an internal package. commit 4cdcb71 Author: armfazh <armfazh@cloudflare.com> Date: Mon Jun 1 01:30:22 2020 -0700 Solving scalar constant-time operations. commit 26b9ea4 Author: armfazh <armfazh@cloudflare.com> Date: Thu May 21 09:21:36 2020 -0700 Review comments on formulas. commit ff821b5 Author: armfazh <armfazh@cloudflare.com> Date: Tue May 19 16:54:36 2020 -0700 Adding tests for detecting decaf/point invalid encodings. commit 3881ea8 Author: armfazh <armfazh@cloudflare.com> Date: Fri May 15 15:13:53 2020 -0700 One test for decaf, unmarshaling straight-line code, and check for errors. commit 9b048c0 Author: armfazh <armfazh@cloudflare.com> Date: Thu May 14 18:12:57 2020 -0700 Updating interface for decaf and curve. commit a99153c Author: armfazh <armfazh@cloudflare.com> Date: Thu May 14 16:10:04 2020 -0700 Adding goldilocks documentation. commit a62d6bd Author: armfazh <armfazh@cloudflare.com> Date: Thu May 14 14:15:02 2020 -0700 Adding decaf v1.1 and kat tests. commit c72bdfa Author: armfazh <armfazh@cloudflare.com> Date: Wed May 13 13:30:11 2020 -0700 Removing non-used fp functions. commit d4fc865 Author: armfazh <armfazh@cloudflare.com> Date: Tue May 12 11:47:57 2020 -0700 Adding some helper functions. commit 6173c83 Author: armfazh <armfazh@cloudflare.com> Date: Tue May 12 05:15:55 2020 -0700 Decaf decoding working. More tests needed. commit daa36c1 Author: armfazh <armfazh@cloudflare.com> Date: Mon May 11 16:16:16 2020 -0700 Decaf encoding is working, except by the choice of generator. commit 8933f6c Author: armfazh <armfazh@cloudflare.com> Date: Thu May 7 01:19:47 2020 -0700 Decaf encoding requires cannon sqrt. commit 9479b45 Author: armfazh <armfazh@cloudflare.com> Date: Wed Apr 29 14:51:21 2020 -0700 Adding support for decaf quotient group.
1 parent d004263 commit 87b9a72

31 files changed

+1769
-1138
lines changed

README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ Version numbers are [Semvers](https://semver.org/). We release a minor version f
3535
| PQ Key Exchange | SIDH | SIDH provide key exchange mechanisms using ephemeral keys. | Post-quantum key exchange in TLS |
3636
| PQ Key Exchange | cSIDH | Isogeny based drop-in replacement for Diffie–Hellman | Post-Quantum Key exchange. |
3737
| PQ KEM | SIKE | SIKE is a key encapsulation mechanism (KEM). | Post-quantum key exchange in TLS |
38+
| PQ Digital Signatures | Dilithium, Hybrid modes | Lattice (Module LWE) based signature scheme | Post-Quantum PKI |
3839
| Key Exchange | X25519, X448 | RFC-7748 provides new key exchange mechanisms based on Montgomery elliptic curves. | TLS 1.3. Secure Shell. |
3940
| Key Exchange | FourQ | One of the fastest elliptic curves at 128-bit security level. | Experimental for key agreement and digital signatures. |
4041
| Key Exchange / Digital signatures | P-384 | Our optimizations reduce the burden when moving from P-256 to P-384. | ECDSA and ECDH using Suite B at top secret level. |
4142
| Digital Signatures | Ed25519, Ed448 | RFC-8032 provides new signature schemes based on Edwards curves. | Digital certificates and authentication. |
42-
| PQ Digital Signatures | Dilithium, Hybrid modes | Lattice (Module LWE) based signature scheme | Post-Quantum PKI |
43+
| Groups | Decaf | Prime-order groups. | Protocols based on the Discrete Logarithm Problem. |
4344

4445
### Work in Progress
4546

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 = (-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{
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

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
// Package decaf provides a prime-order group derived from a quotient of
2+
// Edwards curves.
3+
//
4+
// Decaf Group
5+
//
6+
// Decaf (3) is a prime-order group constructed as a quotient of groups. A Decaf
7+
// element can be represented by any point in the coset P+J[2], where J is a
8+
// Jacobi quartic curve and J[2] are its 2-torsion points.
9+
// Since P+J[2] has four points, Decaf specifies rules to choose one canonical
10+
// representative, which has a unique encoding. Two representations are
11+
// equivalent if they belong to the same coset.
12+
//
13+
// The types Elt and Scalar provide methods to perform arithmetic operations on
14+
// the Decaf group.
15+
//
16+
// Version
17+
//
18+
// This implementation uses Decaf v1.0 of the encoding (see (4,5) for a complete
19+
// specification).
20+
//
21+
// References
22+
//
23+
// (1) https://www.shiftleft.org/papers/goldilocks
24+
//
25+
// (2) https://tools.ietf.org/html/rfc7748
26+
//
27+
// (3) https://doi.org/10.1007/978-3-662-47989-6_34 and https://www.shiftleft.org/papers/decaf
28+
//
29+
// (4) https://sourceforge.net/p/ed448goldilocks/code/ci/v1.0/tree/
30+
//
31+
// (5) https://mailarchive.ietf.org/arch/msg/cfrg/S4YUTt_5eD4kwYbDuhEK0tXT1aM/
32+
package decaf
33+
34+
import (
35+
"unsafe"
36+
37+
"github.com/cloudflare/circl/internal/ted448"
38+
fp "github.com/cloudflare/circl/math/fp448"
39+
)
40+
41+
// Decaf v1.0 of the encoding.
42+
const Version = "v1.0"
43+
44+
// Elt is an element of the Decaf group. It must be always initialized using
45+
// one of the Decaf functions.
46+
type Elt struct{ p ted448.Point }
47+
48+
// Scalar represents a positive integer stored in little-endian order.
49+
type Scalar = ted448.Scalar
50+
51+
func (e Elt) String() string { return e.p.String() }
52+
53+
// IsValid returns True if a is a valid element of the group.
54+
func IsValid(a *Elt) bool { return ted448.IsOnCurve(&a.p) }
55+
56+
// Identity returns the identity element of the group.
57+
func Identity() *Elt { return &Elt{ted448.Identity()} }
58+
59+
// Generator returns the generator element of the group.
60+
func Generator() *Elt { return &Elt{ted448.Generator()} }
61+
62+
// Order returns a scalar with the order of the group.
63+
func Order() Scalar { return ted448.Order() }
64+
65+
// Neg calculates c=-a, where - is the inverse of the group operation.
66+
func Neg(c, a *Elt) { c.p = a.p; c.p.Neg() }
67+
68+
// Add calculates c=a+b, where + is the group operation.
69+
func Add(c, a, b *Elt) { q := a.p; q.Add(&b.p); c.p = q }
70+
71+
// Double calculates c=a+a, where + is the group operation.
72+
func Double(c, a *Elt) { c.p = a.p; c.p.Double() }
73+
74+
// Mul calculates c=n*a, where * is scalar multiplication on the group.
75+
func Mul(c *Elt, n *Scalar, a *Elt) { ted448.ScalarMult(&c.p, n, &a.p) }
76+
77+
// MulGen calculates c=n*g, where * is scalar multiplication on the group,
78+
// and g is the generator of the group.
79+
func MulGen(c *Elt, n *Scalar) { ted448.ScalarBaseMult(&c.p, n) }
80+
81+
// IsIdentity returns True if e is the identity of the group.
82+
func (e *Elt) IsIdentity() bool { return fp.IsZero(&e.p.X) && !fp.IsZero(&e.p.Y) && !fp.IsZero(&e.p.Z) }
83+
84+
// IsEqual returns True if e=a, where = is an equivalence relation.
85+
func (e *Elt) IsEqual(a *Elt) bool {
86+
l, r := &fp.Elt{}, &fp.Elt{}
87+
fp.Mul(l, &e.p.X, &a.p.Y)
88+
fp.Mul(r, &a.p.X, &e.p.Y)
89+
fp.Sub(l, l, r)
90+
return fp.IsZero(l)
91+
}
92+
93+
// UnmarshalBinary interprets the first EncodingSize bytes passed in data, and
94+
// returns a Decaf element.
95+
func (e *Elt) UnmarshalBinary(data []byte) error {
96+
if len(data) < EncodingSize {
97+
return ErrInvalidDecoding
98+
}
99+
100+
s := &fp.Elt{}
101+
copy(s[:], data[:EncodingSize])
102+
p := fp.P()
103+
isLessThanP := isLessThan(s[:], p[:])
104+
isPositiveS := fp.Parity(s) == 0
105+
106+
den, num := &fp.Elt{}, &fp.Elt{}
107+
isr, altx, t0 := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}
108+
x, y := &fp.Elt{}, &fp.Elt{}
109+
one := fp.One()
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
134+
135+
isValid := isPositiveS && isLessThanP && isQR
136+
b := uint(*((*byte)(unsafe.Pointer(&isValid))))
137+
fp.Cmov(&e.p.X, x, b)
138+
fp.Cmov(&e.p.Y, y, b)
139+
fp.Cmov(&e.p.Ta, x, b)
140+
fp.Cmov(&e.p.Tb, y, b)
141+
fp.Cmov(&e.p.Z, &one, b)
142+
if !isValid {
143+
return ErrInvalidDecoding
144+
}
145+
return nil
146+
}
147+
148+
// MarshalBinary returns a unique encoding of the element e.
149+
func (e *Elt) MarshalBinary() ([]byte, error) {
150+
var encS [EncodingSize]byte
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)
181+
}
182+
183+
// isLessThan returns true if 0 <= x < y, and assumes that slices are of the
184+
// same length and are interpreted in little-endian order.
185+
func isLessThan(x, y []byte) bool {
186+
i := len(x) - 1
187+
for i > 0 && x[i] == y[i] {
188+
i--
189+
}
190+
return x[i] < y[i]
191+
}

0 commit comments

Comments
 (0)