-
Notifications
You must be signed in to change notification settings - Fork 136
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Moving twist implementation to an internal package.
- Loading branch information
Showing
12 changed files
with
1,781 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package decaf | ||
|
||
import ( | ||
"errors" | ||
|
||
fp "github.com/cloudflare/circl/math/fp448" | ||
) | ||
|
||
// DecafEncodingSize is the size (in bytes) of an encoded Decaf element. | ||
const EncodingSize = fp.Size | ||
|
||
// ErrInvalidDecoding alerts of an error during decoding a point. | ||
var ErrInvalidDecoding = errors.New("invalid decoding") | ||
|
||
var ( | ||
// aMinusD is paramA-paramD used in Decaf. | ||
aMinusDTwist = fp.Elt{0xa9, 0x98} | ||
// sqrtAMinusDTwist is the smallest sqrt(paramATwist-paramDTwist) used in Decaf. | ||
sqrtAMinusDTwist = fp.Elt{ | ||
0x36, 0x27, 0x57, 0x45, 0x0f, 0xef, 0x42, 0x96, | ||
0x52, 0xce, 0x20, 0xaa, 0xf6, 0x7b, 0x33, 0x60, | ||
0xd2, 0xde, 0x6e, 0xfd, 0xf4, 0x66, 0x9a, 0x83, | ||
0xba, 0x14, 0x8c, 0x96, 0x80, 0xd7, 0xa2, 0x64, | ||
0x4b, 0xd5, 0xb8, 0xa5, 0xb8, 0xa7, 0xf1, 0xa1, | ||
0xa0, 0x6a, 0xa2, 0x2f, 0x72, 0x8d, 0xf6, 0x3b, | ||
0x68, 0xf7, 0x24, 0xeb, 0xfb, 0x62, 0xd9, 0x22, | ||
} | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// Package decaf provides operations on a prime-order group derived from the goldilocks curve. | ||
// | ||
// Its internal implementation uses the twist of the goldilocks curve. | ||
// This implementation uses Decaf v1.0 of the encoding. | ||
// | ||
// References: | ||
// - https://www.shiftleft.org/papers/decaf/ | ||
// - https://www.shiftleft.org/papers/goldilocks | ||
// - https://sourceforge.net/p/ed448goldilocks/code/ci/v1.0/tree/ | ||
package decaf | ||
|
||
import ( | ||
"github.com/cloudflare/circl/internal/ted448" | ||
fp "github.com/cloudflare/circl/math/fp448" | ||
) | ||
|
||
// Version targets Decaf v1.0 of the encoding. As implemented in https://sourceforge.net/p/ed448goldilocks/code/ci/v1.0/tree/. | ||
const Version = "v1.0" | ||
|
||
// Elt is an element of the Decaf group. It must be always initialized using | ||
// one of the Decaf functions. | ||
type Elt struct{ p ted448.Point } | ||
|
||
// Scalar represents a positive integer stored in little-endian order. | ||
type Scalar = ted448.Scalar | ||
|
||
func (e Elt) String() string { return e.p.String() } | ||
|
||
// IsValid returns True if a is a valid element of the group. | ||
func IsValid(a *Elt) bool { return ted448.IsOnCurve(&a.p) } | ||
|
||
// Identity returns the identity element of the group. | ||
func Identity() *Elt { return &Elt{ted448.Identity()} } | ||
|
||
// Generator returns the generator element of the group. | ||
func Generator() *Elt { return &Elt{ted448.Generator()} } | ||
|
||
// Order returns a scalar with the order of the group. | ||
func Order() Scalar { return ted448.Order() } | ||
|
||
// Neg calculates c=-a, where - is the inverse of the group operation. | ||
func Neg(c, a *Elt) { c.p = a.p; c.p.Neg() } | ||
|
||
// Add calculates c=a+b, where + is the group operation. | ||
func Add(c, a, b *Elt) { q := a.p; q.Add(&b.p); c.p = q } | ||
|
||
// Double calculates c=a+a, where + is the group operation. | ||
func Double(c, a *Elt) { c.p = a.p; c.p.Double() } | ||
|
||
// Mul calculates c=n*a, where * is scalar multiplication on the group. | ||
func Mul(c *Elt, n *Scalar, a *Elt) { ted448.ScalarMult(&c.p, n, &a.p) } | ||
|
||
// MulGen calculates c=n*g, where * is scalar multiplication on the group, | ||
// and g is the generator of the group. | ||
func MulGen(c *Elt, n *Scalar) { ted448.ScalarBaseMult(&c.p, n) } | ||
|
||
// IsIdentity returns True if e is the identity of the group. | ||
func (e *Elt) IsIdentity() bool { | ||
x, y, _, _, z := e.p.Coordinates() | ||
return fp.IsZero(&x) && !fp.IsZero(&y) && !fp.IsZero(&z) | ||
} | ||
|
||
// IsEqual returns True if e=a, where = is an equivalence relation. | ||
func (e *Elt) IsEqual(a *Elt) bool { | ||
x1, y1, _, _, _ := e.p.Coordinates() | ||
x2, y2, _, _, _ := a.p.Coordinates() | ||
|
||
l, r := &fp.Elt{}, &fp.Elt{} | ||
fp.Mul(l, &x1, &y2) | ||
fp.Mul(r, &x2, &y1) | ||
fp.Sub(l, l, r) | ||
return fp.IsZero(l) | ||
} | ||
|
||
// UnmarshalBinary if succeeds returns a Decaf element by decoding the first | ||
// DecafEncodingSize bytes of data. | ||
func (e *Elt) UnmarshalBinary(data []byte) error { | ||
if len(data) < EncodingSize { | ||
return ErrInvalidDecoding | ||
} | ||
|
||
s := &fp.Elt{} | ||
copy(s[:], data[:EncodingSize]) | ||
p := fp.P() | ||
isLessThanP := isLessThan(s[:], p[:]) | ||
isPositiveS := fp.Parity(s) == 0 | ||
|
||
s2, den, num := &fp.Elt{}, &fp.Elt{}, &fp.Elt{} | ||
isr, altx := &fp.Elt{}, &fp.Elt{} | ||
t0, t1 := &fp.Elt{}, &fp.Elt{} | ||
x, y := &fp.Elt{}, &fp.Elt{} | ||
one := fp.One() | ||
fp.Sqr(s2, s) // s2 = s^2 | ||
fp.Sub(den, &one, s2) // den = 1 + a*s^2 | ||
fp.Mul(t1, s2, &ted448.ParamD) // t1 = d*s^2 | ||
fp.Add(t1, t1, t1) // t1 = 2*d*s^2 | ||
fp.Add(t1, t1, t1) // t1 = 4*d*s^2 | ||
fp.Sqr(t0, den) // num = (1 + a*s^2)^2 | ||
fp.Sub(num, t0, t1) // num = (1 + a*s^2)^2 - 4*d*s^2 | ||
fp.Mul(t0, t0, num) // t0 = num*den^2 | ||
isQR := fp.InvSqrt(isr, &one, t0) // v = 1/sqrt(num*den^2) | ||
fp.Mul(t1, den, isr) // altx = isr*den | ||
fp.Mul(t1, t1, s) // altx = s*isr*den | ||
fp.Add(t1, t1, t1) // t1 = 2*s*isr*den | ||
fp.Mul(altx, t1, &sqrtAMinusDTwist) // altx = 2*s*isr*den*sqrt(A-D) | ||
isNegX := fp.Parity(altx) // isNeg = sgn(altx) | ||
fp.Neg(t0, isr) // t0 = -isr | ||
fp.Cmov(isr, t0, uint(isNegX)) // if altx is negative then isr = -isr | ||
fp.Sqr(x, isr) // x = isr^2 | ||
fp.Mul(x, x, den) // x = isr^2*den | ||
fp.Mul(x, x, num) // x = isr^2*den*num | ||
fp.Mul(x, x, s) // x = s*isr^2*den*num | ||
fp.Add(x, x, x) // x = 2*s*isr^2*den*num | ||
fp.Mul(y, isr, den) // y = isr*den | ||
fp.Add(t0, &one, s2) // t0 = 1 - a*s^2 | ||
fp.Mul(y, y, t0) // y = (1 - a*s^2)*isr*den | ||
|
||
isValid := isPositiveS && isLessThanP && isQR | ||
P, err := ted448.NewPoint(x, y) | ||
if !isValid || err != nil { | ||
return ErrInvalidDecoding | ||
} | ||
e.p = *P | ||
return nil | ||
} | ||
|
||
// MarshalBinary returns a unique encoding of the element e. | ||
func (e *Elt) MarshalBinary() ([]byte, error) { | ||
x, _, ta, tb, z := e.p.Coordinates() | ||
one, t, t2, s := &fp.Elt{}, &fp.Elt{}, &fp.Elt{}, &fp.Elt{} | ||
fp.SetOne(one) | ||
fp.Mul(t, &ta, &tb) // t = ta*tb | ||
t0, t1 := x, *t // (t0,t1) = (x,t) | ||
fp.Sqr(t2, &x) // t2 = x^2 | ||
fp.AddSub(&t0, &t1) // (t0,t1) = (x+t,x-t) | ||
fp.Mul(&t1, &t0, &t1) // t1 = (x+t)*(x-t) | ||
fp.Mul(&t0, &t1, &aMinusDTwist) // t0 = (a-d)*(x+t)*(x-t) | ||
fp.Mul(&t0, &t0, t2) // t0 = x^2*(a-d)*(x+t)*(x-t) | ||
fp.InvSqrt(&t0, one, &t0) // t0 = 1/sqrt( x^2*(a-d)*(z+y)*(z-y) ) | ||
fp.Mul(&t1, &t1, &t0) // t1 = (z+y)*(z-y)/sqrt( x^2*(a-d)*(z+y)*(z-y) ) | ||
fp.Mul(t2, &t1, &sqrtAMinusDTwist) // t2 = sqrt( (z+y)*(z-y) )/z | ||
isNeg := fp.Parity(t2) // isNeg = sgn(t2) | ||
fp.Neg(t2, &t1) // t2 = -t1 | ||
fp.Cmov(&t1, t2, uint(isNeg)) // if t2 is negative then t1 = -t1 | ||
fp.Mul(s, &t1, &z) // s = t1*z | ||
fp.Sub(s, s, t) // s = t1*z - t | ||
fp.Mul(s, s, &x) // s = x*(t1*z - t) | ||
fp.Mul(s, s, &t0) // s = isr*x*(t1*z - t) | ||
fp.Mul(s, s, &aMinusDTwist) // s = (a-d)*isr*x*(t1*z - t) | ||
isNeg = fp.Parity(s) // isNeg = sgn(s) | ||
fp.Neg(&t0, s) // t0 = -s | ||
fp.Cmov(s, &t0, uint(isNeg)) // if s is negative then s = -s | ||
|
||
var encS [EncodingSize]byte | ||
if err := fp.ToBytes(encS[:], s); err != nil { | ||
return nil, err | ||
} | ||
return encS[:], nil | ||
} | ||
|
||
// isLessThan returns true if 0 <= x < y, and assumes that slices are of the | ||
// same length and are interpreted in little-endian order. | ||
func isLessThan(x, y []byte) bool { | ||
i := len(x) - 1 | ||
for i > 0 && x[i] == y[i] { | ||
i-- | ||
} | ||
return x[i] < y[i] | ||
} |
Oops, something went wrong.