|
| 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