|
| 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, ¶mD) // 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