ristretto255: implement Encode and Decode

This commit is contained in:
George Tankersley 2019-03-03 02:53:09 +00:00 committed by Filippo Valsorda
parent 88aa823cd0
commit b6eb459f56
3 changed files with 165 additions and 0 deletions

18
fe.go
View File

@ -109,3 +109,21 @@ func fieldElementFromDecimal(s string) *radix51.FieldElement {
}
return new(radix51.FieldElement).FromBig(n)
}
// The order of the field, 2^255 - 19, in 51-bit little endian form.
var fieldOrder = [5]uint64{0x7ffffffffffed, 0x7ffffffffffff, 0x7ffffffffffff, 0x7ffffffffffff, 0x7ffffffffffff}
// feMinimal returns true if the given field element is less than the order of the field.
func feMinimal(fe *radix51.FieldElement) bool {
for i := 4; ; i-- {
v := fe[i]
if v > fieldOrder[i] {
return false
} else if v < fieldOrder[i] {
break
} else if i == 0 {
return false
}
}
return true
}

View File

@ -8,6 +8,9 @@
package ristretto255
import (
"encoding/hex"
"errors"
"github.com/gtank/ristretto255/internal/edwards25519"
"github.com/gtank/ristretto255/internal/radix51"
)
@ -23,6 +26,13 @@ var (
"1159843021668779879193775521855586647937357759715417654439879720876111806838")
dMinusOneSQ = fieldElementFromDecimal(
"40440834346308536858101042469323190826248399146238708352240133220865137265952")
// The encoding of the Ristretto element that can be represented internally
// by the Curve25519 base point.
encodedBasepoint, _ = hex.DecodeString(
"e2f2ae0a6abc4e71a884a961c500515f58e30b6aa582dd8db6a65945e08d2d76")
errInvalidEncoding = errors.New("invalid Ristretto encoding")
)
// Element is an element of the ristretto255 prime-order group.
@ -127,3 +137,127 @@ func mapToPoint(out *edwards25519.ExtendedGroupElement, t *radix51.FieldElement)
out.Z.Mul(w1, w3)
out.T.Mul(w0, w2)
}
// Encode encodes a Ristretto group element to its canonical bytestring.
func (e *Element) Encode(ee *Element) []byte {
tmp := &radix51.FieldElement{}
// u1 = (z0 + y0) * (z0 - y0)
u1 := &radix51.FieldElement{}
u1.Add(&ee.r.Z, &ee.r.Y).Mul(u1, tmp.Sub(&ee.r.Z, &ee.r.Y))
// u2 = x0 * y0
u2 := &radix51.FieldElement{}
u2.Mul(&ee.r.X, &ee.r.Y)
// Ignore was_square since this is always square
// (_, invsqrt) = SQRT_RATIO_M1(1, u1 * u2^2)
invSqrt := &radix51.FieldElement{}
_ = feSqrtRatio(invSqrt, u1, tmp.Square(u2))
// den1 = invsqrt * u1
// den2 = invsqrt * u2
// z_inv = den1 * den2 * t0
den1, den2 := &radix51.FieldElement{}, &radix51.FieldElement{}
zInv := &radix51.FieldElement{}
den1.Mul(invSqrt, u1)
den2.Mul(invSqrt, u2)
zInv.Mul(den1, den2).Mul(zInv, &ee.r.T)
// ix0 = x0 * SQRT_M1
// iy0 = y0 * SQRT_M1
// enchanted_denominator = den1 * INVSQRT_A_MINUS_D
ix0, iy0 := &radix51.FieldElement{}, &radix51.FieldElement{}
enchantedDenominator := &radix51.FieldElement{}
ix0.Mul(&ee.r.X, sqrtM1)
iy0.Mul(&ee.r.Y, sqrtM1)
enchantedDenominator.Mul(den1, invSqrtAMinusD)
// rotate = IS_NEGATIVE(t0 * z_inv)
rotate := tmp.Mul(&ee.r.T, zInv).IsNegative()
// x = CT_SELECT(iy0 IF rotate ELSE x0)
// y = CT_SELECT(ix0 IF rotate ELSE y0)
// z = z0
// den_inv = CT_SELECT(enchanted_denominator IF rotate ELSE den2)
x, y := &radix51.FieldElement{}, &radix51.FieldElement{}
denInv := &radix51.FieldElement{}
x.Select(iy0, &ee.r.X, rotate)
y.Select(ix0, &ee.r.Y, rotate)
z := &ee.r.Z
denInv.Select(enchantedDenominator, den2, rotate)
// y = CT_NEG(y, IS_NEGATIVE(x * z_inv))
y.CondNeg(y, tmp.Mul(x, zInv).IsNegative())
// s = CT_ABS(den_inv * (z - y))
s := tmp.Mul(denInv, tmp.Sub(z, y)).Abs(tmp)
// Return the canonical little-endian encoding of s.
return s.Bytes(nil)
}
// Decode decodes the canonical bytestring encoding of an element into a Ristretto element.
// Returns nil on success.
func (e *Element) Decode(in []byte) error {
if len(in) != 32 {
return errInvalidEncoding
}
// First, interpret the string as an integer s in little-endian representation.
s := &radix51.FieldElement{}
s.FromBytes(in)
// If the resulting value is >= p, decoding fails.
// If IS_NEGATIVE(s) returns TRUE, decoding fails.
if !feMinimal(s) || s.IsNegative() == 1 {
return errInvalidEncoding
}
// ss = s^2
sSqr := &radix51.FieldElement{}
sSqr.Square(s)
// u1 = 1 - ss
u1 := &radix51.FieldElement{}
u1.Sub(radix51.One, sSqr)
// u2 = 1 + ss
u2 := &radix51.FieldElement{}
u2.Add(radix51.One, sSqr)
// u2_sqr = u2^2
u2Sqr := &radix51.FieldElement{}
u2Sqr.Square(u2)
// v = -(D * u1^2) - u2_sqr
v := &radix51.FieldElement{}
v.Square(u1).Mul(v, edwards25519.D).Neg(v).Sub(v, u2Sqr)
// (was_square, invsqrt) = SQRT_RATIO_M1(1, v * u2_sqr)
invSqrt, tmp := &radix51.FieldElement{}, &radix51.FieldElement{}
wasSquare := feSqrtRatio(invSqrt, radix51.One, tmp.Mul(v, u2Sqr))
// den_x = invsqrt * u2
// den_y = invsqrt * den_x * v
denX, denY := &radix51.FieldElement{}, &radix51.FieldElement{}
denX.Mul(invSqrt, u2)
denY.Mul(invSqrt, denX).Mul(denY, v)
// x = CT_ABS(2 * s * den_x)
// y = u1 * den_y
// t = x * y
out := &e.r
out.X.Mul(radix51.Two, s).Mul(&out.X, denX).Abs(&out.X)
out.Y.Mul(u1, denY)
out.Z.One()
out.T.Mul(&out.X, &out.Y)
// If was_square is FALSE, or IS_NEGATIVE(t) returns TRUE, or y = 0, decoding fails.
if wasSquare == 0 || out.T.IsNegative() == 1 || out.Y.Equal(radix51.Zero) == 1 {
return errInvalidEncoding
}
// Otherwise, return the internal representation in extended coordinates (x, y, 1, t).
return nil
}

13
ristretto255_test.go Normal file
View File

@ -0,0 +1,13 @@
package ristretto255
import (
"testing"
)
func TestRistrettoBasepointDecode(t *testing.T) {
extendedBasepoint := &Element{}
err := extendedBasepoint.Decode(encodedBasepoint)
if err != nil {
t.Fatal(err)
}
}