From b6eb459f56689694d090d1f17609235ab8db4a95 Mon Sep 17 00:00:00 2001 From: George Tankersley Date: Sun, 3 Mar 2019 02:53:09 +0000 Subject: [PATCH] ristretto255: implement Encode and Decode --- fe.go | 18 ++++++ ristretto255.go | 134 +++++++++++++++++++++++++++++++++++++++++++ ristretto255_test.go | 13 +++++ 3 files changed, 165 insertions(+) create mode 100644 ristretto255_test.go diff --git a/fe.go b/fe.go index bd94255..6d94733 100644 --- a/fe.go +++ b/fe.go @@ -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 +} diff --git a/ristretto255.go b/ristretto255.go index 7a16a1b..b0c9730 100644 --- a/ristretto255.go +++ b/ristretto255.go @@ -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 +} diff --git a/ristretto255_test.go b/ristretto255_test.go new file mode 100644 index 0000000..c5ec046 --- /dev/null +++ b/ristretto255_test.go @@ -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) + } +}