123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- // Copyright (c) 2021 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package edwards25519
- // This file contains additional functionality that is not included in the
- // upstream crypto/internal/edwards25519 package.
- import (
- "errors"
- "filippo.io/edwards25519/field"
- )
- // ExtendedCoordinates returns v in extended coordinates (X:Y:Z:T) where
- // x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
- func (v *Point) ExtendedCoordinates() (X, Y, Z, T *field.Element) {
- // This function is outlined to make the allocations inline in the caller
- // rather than happen on the heap. Don't change the style without making
- // sure it doesn't increase the inliner cost.
- var e [4]field.Element
- X, Y, Z, T = v.extendedCoordinates(&e)
- return
- }
- func (v *Point) extendedCoordinates(e *[4]field.Element) (X, Y, Z, T *field.Element) {
- checkInitialized(v)
- X = e[0].Set(&v.x)
- Y = e[1].Set(&v.y)
- Z = e[2].Set(&v.z)
- T = e[3].Set(&v.t)
- return
- }
- // SetExtendedCoordinates sets v = (X:Y:Z:T) in extended coordinates where
- // x = X/Z, y = Y/Z, and xy = T/Z as in https://eprint.iacr.org/2008/522.
- //
- // If the coordinates are invalid or don't represent a valid point on the curve,
- // SetExtendedCoordinates returns nil and an error and the receiver is
- // unchanged. Otherwise, SetExtendedCoordinates returns v.
- func (v *Point) SetExtendedCoordinates(X, Y, Z, T *field.Element) (*Point, error) {
- if !isOnCurve(X, Y, Z, T) {
- return nil, errors.New("edwards25519: invalid point coordinates")
- }
- v.x.Set(X)
- v.y.Set(Y)
- v.z.Set(Z)
- v.t.Set(T)
- return v, nil
- }
- func isOnCurve(X, Y, Z, T *field.Element) bool {
- var lhs, rhs field.Element
- XX := new(field.Element).Square(X)
- YY := new(field.Element).Square(Y)
- ZZ := new(field.Element).Square(Z)
- TT := new(field.Element).Square(T)
- // -x² + y² = 1 + dx²y²
- // -(X/Z)² + (Y/Z)² = 1 + d(T/Z)²
- // -X² + Y² = Z² + dT²
- lhs.Subtract(YY, XX)
- rhs.Multiply(d, TT).Add(&rhs, ZZ)
- if lhs.Equal(&rhs) != 1 {
- return false
- }
- // xy = T/Z
- // XY/Z² = T/Z
- // XY = TZ
- lhs.Multiply(X, Y)
- rhs.Multiply(T, Z)
- return lhs.Equal(&rhs) == 1
- }
- // BytesMontgomery converts v to a point on the birationally-equivalent
- // Curve25519 Montgomery curve, and returns its canonical 32 bytes encoding
- // according to RFC 7748.
- //
- // Note that BytesMontgomery only encodes the u-coordinate, so v and -v encode
- // to the same value. If v is the identity point, BytesMontgomery returns 32
- // zero bytes, analogously to the X25519 function.
- //
- // The lack of an inverse operation (such as SetMontgomeryBytes) is deliberate:
- // while every valid edwards25519 point has a unique u-coordinate Montgomery
- // encoding, X25519 accepts inputs on the quadratic twist, which don't correspond
- // to any edwards25519 point, and every other X25519 input corresponds to two
- // edwards25519 points.
- func (v *Point) BytesMontgomery() []byte {
- // This function is outlined to make the allocations inline in the caller
- // rather than happen on the heap.
- var buf [32]byte
- return v.bytesMontgomery(&buf)
- }
- func (v *Point) bytesMontgomery(buf *[32]byte) []byte {
- checkInitialized(v)
- // RFC 7748, Section 4.1 provides the bilinear map to calculate the
- // Montgomery u-coordinate
- //
- // u = (1 + y) / (1 - y)
- //
- // where y = Y / Z.
- var y, recip, u field.Element
- y.Multiply(&v.y, y.Invert(&v.z)) // y = Y / Z
- recip.Invert(recip.Subtract(feOne, &y)) // r = 1/(1 - y)
- u.Multiply(u.Add(feOne, &y), &recip) // u = (1 + y)*r
- return copyFieldElement(buf, &u)
- }
- // MultByCofactor sets v = 8 * p, and returns v.
- func (v *Point) MultByCofactor(p *Point) *Point {
- checkInitialized(p)
- result := projP1xP1{}
- pp := (&projP2{}).FromP3(p)
- result.Double(pp)
- pp.FromP1xP1(&result)
- result.Double(pp)
- pp.FromP1xP1(&result)
- result.Double(pp)
- return v.fromP1xP1(&result)
- }
- // Given k > 0, set s = s**(2*i).
- func (s *Scalar) pow2k(k int) {
- for i := 0; i < k; i++ {
- s.Multiply(s, s)
- }
- }
- // Invert sets s to the inverse of a nonzero scalar v, and returns s.
- //
- // If t is zero, Invert returns zero.
- func (s *Scalar) Invert(t *Scalar) *Scalar {
- // Uses a hardcoded sliding window of width 4.
- var table [8]Scalar
- var tt Scalar
- tt.Multiply(t, t)
- table[0] = *t
- for i := 0; i < 7; i++ {
- table[i+1].Multiply(&table[i], &tt)
- }
- // Now table = [t**1, t**3, t**5, t**7, t**9, t**11, t**13, t**15]
- // so t**k = t[k/2] for odd k
- // To compute the sliding window digits, use the following Sage script:
- // sage: import itertools
- // sage: def sliding_window(w,k):
- // ....: digits = []
- // ....: while k > 0:
- // ....: if k % 2 == 1:
- // ....: kmod = k % (2**w)
- // ....: digits.append(kmod)
- // ....: k = k - kmod
- // ....: else:
- // ....: digits.append(0)
- // ....: k = k // 2
- // ....: return digits
- // Now we can compute s roughly as follows:
- // sage: s = 1
- // sage: for coeff in reversed(sliding_window(4,l-2)):
- // ....: s = s*s
- // ....: if coeff > 0 :
- // ....: s = s*t**coeff
- // This works on one bit at a time, with many runs of zeros.
- // The digits can be collapsed into [(count, coeff)] as follows:
- // sage: [(len(list(group)),d) for d,group in itertools.groupby(sliding_window(4,l-2))]
- // Entries of the form (k, 0) turn into pow2k(k)
- // Entries of the form (1, coeff) turn into a squaring and then a table lookup.
- // We can fold the squaring into the previous pow2k(k) as pow2k(k+1).
- *s = table[1/2]
- s.pow2k(127 + 1)
- s.Multiply(s, &table[1/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[9/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[11/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[13/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[15/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[7/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[15/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[5/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[1/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[15/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[15/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[7/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[3/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[11/2])
- s.pow2k(5 + 1)
- s.Multiply(s, &table[11/2])
- s.pow2k(9 + 1)
- s.Multiply(s, &table[9/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[3/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[3/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[3/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[9/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[7/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[3/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[13/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[7/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[9/2])
- s.pow2k(3 + 1)
- s.Multiply(s, &table[15/2])
- s.pow2k(4 + 1)
- s.Multiply(s, &table[11/2])
- return s
- }
- // MultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v.
- //
- // Execution time depends only on the lengths of the two slices, which must match.
- func (v *Point) MultiScalarMult(scalars []*Scalar, points []*Point) *Point {
- if len(scalars) != len(points) {
- panic("edwards25519: called MultiScalarMult with different size inputs")
- }
- checkInitialized(points...)
- // Proceed as in the single-base case, but share doublings
- // between each point in the multiscalar equation.
- // Build lookup tables for each point
- tables := make([]projLookupTable, len(points))
- for i := range tables {
- tables[i].FromP3(points[i])
- }
- // Compute signed radix-16 digits for each scalar
- digits := make([][64]int8, len(scalars))
- for i := range digits {
- digits[i] = scalars[i].signedRadix16()
- }
- // Unwrap first loop iteration to save computing 16*identity
- multiple := &projCached{}
- tmp1 := &projP1xP1{}
- tmp2 := &projP2{}
- // Lookup-and-add the appropriate multiple of each input point
- for j := range tables {
- tables[j].SelectInto(multiple, digits[j][63])
- tmp1.Add(v, multiple) // tmp1 = v + x_(j,63)*Q in P1xP1 coords
- v.fromP1xP1(tmp1) // update v
- }
- tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration
- for i := 62; i >= 0; i-- {
- tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords
- tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords
- tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords
- tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords
- tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords
- tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords
- tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords
- v.fromP1xP1(tmp1) // v = 16*(prev) in P3 coords
- // Lookup-and-add the appropriate multiple of each input point
- for j := range tables {
- tables[j].SelectInto(multiple, digits[j][i])
- tmp1.Add(v, multiple) // tmp1 = v + x_(j,i)*Q in P1xP1 coords
- v.fromP1xP1(tmp1) // update v
- }
- tmp2.FromP3(v) // set up tmp2 = v in P2 coords for next iteration
- }
- return v
- }
- // VarTimeMultiScalarMult sets v = sum(scalars[i] * points[i]), and returns v.
- //
- // Execution time depends on the inputs.
- func (v *Point) VarTimeMultiScalarMult(scalars []*Scalar, points []*Point) *Point {
- if len(scalars) != len(points) {
- panic("edwards25519: called VarTimeMultiScalarMult with different size inputs")
- }
- checkInitialized(points...)
- // Generalize double-base NAF computation to arbitrary sizes.
- // Here all the points are dynamic, so we only use the smaller
- // tables.
- // Build lookup tables for each point
- tables := make([]nafLookupTable5, len(points))
- for i := range tables {
- tables[i].FromP3(points[i])
- }
- // Compute a NAF for each scalar
- nafs := make([][256]int8, len(scalars))
- for i := range nafs {
- nafs[i] = scalars[i].nonAdjacentForm(5)
- }
- multiple := &projCached{}
- tmp1 := &projP1xP1{}
- tmp2 := &projP2{}
- tmp2.Zero()
- // Move from high to low bits, doubling the accumulator
- // at each iteration and checking whether there is a nonzero
- // coefficient to look up a multiple of.
- //
- // Skip trying to find the first nonzero coefficent, because
- // searching might be more work than a few extra doublings.
- for i := 255; i >= 0; i-- {
- tmp1.Double(tmp2)
- for j := range nafs {
- if nafs[j][i] > 0 {
- v.fromP1xP1(tmp1)
- tables[j].SelectInto(multiple, nafs[j][i])
- tmp1.Add(v, multiple)
- } else if nafs[j][i] < 0 {
- v.fromP1xP1(tmp1)
- tables[j].SelectInto(multiple, -nafs[j][i])
- tmp1.Sub(v, multiple)
- }
- }
- tmp2.FromP1xP1(tmp1)
- }
- v.fromP2(tmp2)
- return v
- }
|