From 620415daa4f07457fa06e1030f05d6a1a8dc4366 Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Sat, 30 Mar 2019 22:01:30 -0400 Subject: [PATCH] internal/radix51: make reduction an invariant and unexport Reduce Now every operation returns a light-reduced value, so the reduction is an invariant, and there's no need to ever explicitly call Reduce. Safety! --- ed25519.go | 1 - internal/radix51/alias_test.go | 2 +- internal/radix51/fe.go | 76 ++++++++++++++-------------------- internal/radix51/fe_mul.go | 43 ++++++------------- internal/radix51/fe_square.go | 31 ++++---------- internal/radix51/fe_test.go | 10 ++--- 6 files changed, 56 insertions(+), 107 deletions(-) diff --git a/ed25519.go b/ed25519.go index 4a2ecfa..820b77e 100644 --- a/ed25519.go +++ b/ed25519.go @@ -74,7 +74,6 @@ func (curve ed25519Curve) IsOnCurve(x, y *big.Int) bool { lh.Neg(&lh) // -x^2 lh.Add(&lh, &y2) // -x^2 + y^2 lh.Sub(&lh, &rh) // -x^2 + y^2 - 1 - dx^2y^2 - lh.Reduce(&lh) // mod p return lh.Equal(radix51.Zero) == 1 } diff --git a/internal/radix51/alias_test.go b/internal/radix51/alias_test.go index 1309cb3..0ac0521 100644 --- a/internal/radix51/alias_test.go +++ b/internal/radix51/alias_test.go @@ -84,7 +84,7 @@ func TestAliasing(t *testing.T) { {name: "Abs", oneArgF: (*FieldElement).Abs}, {name: "Invert", oneArgF: (*FieldElement).Invert}, {name: "Neg", oneArgF: (*FieldElement).Neg}, - {name: "Reduce", oneArgF: (*FieldElement).Reduce}, + {name: "reduce", oneArgF: (*FieldElement).reduce}, {name: "Set", oneArgF: (*FieldElement).Set}, {name: "Square", oneArgF: (*FieldElement).Square}, { diff --git a/internal/radix51/fe.go b/internal/radix51/fe.go index 1c09245..00b0d65 100644 --- a/internal/radix51/fe.go +++ b/internal/radix51/fe.go @@ -35,51 +35,53 @@ var ( // Zero sets v = 0 and returns v. func (v *FieldElement) Zero() *FieldElement { - v[0] = 0 - v[1] = 0 - v[2] = 0 - v[3] = 0 - v[4] = 0 + *v = *Zero return v } // One sets v = 1 and returns v. func (v *FieldElement) One() *FieldElement { - v[0] = 1 - v[1] = 0 - v[2] = 0 - v[3] = 0 - v[4] = 0 + *v = *One return v } -// Reduce reduces v modulo 2^255 - 19 and returns it. -func (v *FieldElement) Reduce(u *FieldElement) *FieldElement { - v.Set(u) - - // Lev v = v[0] + v[1]*2^51 + v[2]*2^102 + v[3]*2^153 + v[4]*2^204 - // Reduce each limb below 2^51, propagating carries. +// lightReduce brings the limbs below 52, 51, 51, 51, 51 bits. It is split in +// two because the inliner works actively against us. The two functions MUST be +// called one after the other. +func (v *FieldElement) lightReduce1() *FieldElement { v[1] += v[0] >> 51 v[0] = v[0] & maskLow51Bits v[2] += v[1] >> 51 v[1] = v[1] & maskLow51Bits v[3] += v[2] >> 51 + return v +} +func (v *FieldElement) lightReduce2() *FieldElement { v[2] = v[2] & maskLow51Bits v[4] += v[3] >> 51 v[3] = v[3] & maskLow51Bits v[0] += (v[4] >> 51) * 19 v[4] = v[4] & maskLow51Bits + return v +} - // We now hate a field element v < 2^255, but need v <= 2^255-19 - // TODO Document why this works. It's the elaborate comment about r = h-pq etc etc. +// reduce reduces v modulo 2^255 - 19 and returns it. +func (v *FieldElement) reduce(u *FieldElement) *FieldElement { + v.Set(u).lightReduce1().lightReduce2() - // Get the carry bit + // After the light reduction we now have a field element representation + // v < 2^255 + 2^13 * 19, but need v < 2^255 - 19. + + // If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1, + // generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise. c := (v[0] + 19) >> 51 c = (v[1] + c) >> 51 c = (v[2] + c) >> 51 c = (v[3] + c) >> 51 c = (v[4] + c) >> 51 + // If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's + // effectively applying the reduction identity to the carry. v[0] += 19 * c v[1] += v[0] >> 51 @@ -107,35 +109,19 @@ func (v *FieldElement) Add(a, b *FieldElement) *FieldElement { v[2] = a[2] + b[2] v[3] = a[3] + b[3] v[4] = a[4] + b[4] - return v + return v.lightReduce1().lightReduce2() } // Sub sets v = a - b and returns v. func (v *FieldElement) Sub(a, b *FieldElement) *FieldElement { - t := *b - - // Reduce each limb below 2^51, propagating carries. Ensures that results - // fit within the limbs. This would not be required for reduced input. - t[1] += t[0] >> 51 - t[0] = t[0] & maskLow51Bits - t[2] += t[1] >> 51 - t[1] = t[1] & maskLow51Bits - t[3] += t[2] >> 51 - t[2] = t[2] & maskLow51Bits - t[4] += t[3] >> 51 - t[3] = t[3] & maskLow51Bits - t[0] += (t[4] >> 51) * 19 - t[4] = t[4] & maskLow51Bits - - // This is slightly more complicated. Because we use unsigned coefficients, we - // first add a multiple of p and then subtract. - v[0] = (a[0] + 0xFFFFFFFFFFFDA) - t[0] - v[1] = (a[1] + 0xFFFFFFFFFFFFE) - t[1] - v[2] = (a[2] + 0xFFFFFFFFFFFFE) - t[2] - v[3] = (a[3] + 0xFFFFFFFFFFFFE) - t[3] - v[4] = (a[4] + 0xFFFFFFFFFFFFE) - t[4] - - return v + // We first add 2 * p, to guarantee the subtraction won't underflow, and + // then subtract b (which can be up to 2^255 + 2^13 * 19). + v[0] = (a[0] + 0xFFFFFFFFFFFDA) - b[0] + v[1] = (a[1] + 0xFFFFFFFFFFFFE) - b[1] + v[2] = (a[2] + 0xFFFFFFFFFFFFE) - b[2] + v[3] = (a[3] + 0xFFFFFFFFFFFFE) - b[3] + v[4] = (a[4] + 0xFFFFFFFFFFFFE) - b[4] + return v.lightReduce1().lightReduce2() } // Neg sets v = -a and returns v. @@ -241,7 +227,7 @@ func (v *FieldElement) FromBytes(x []byte) *FieldElement { // Bytes appends a 32 bytes little-endian encoding of v to b. func (v *FieldElement) Bytes(b []byte) []byte { - t := new(FieldElement).Reduce(v) + t := new(FieldElement).reduce(v) res, out := sliceForAppend(b, 32) for i := range out { diff --git a/internal/radix51/fe_mul.go b/internal/radix51/fe_mul.go index 3ea249f..c5bb94e 100644 --- a/internal/radix51/fe_mul.go +++ b/internal/radix51/fe_mul.go @@ -8,20 +8,17 @@ package radix51 // Mul sets v = x * y and returns v. func (v *FieldElement) Mul(x, y *FieldElement) *FieldElement { - var x0, x1, x2, x3, x4 uint64 - var y0, y1, y2, y3, y4 uint64 + x0 := x[0] + x1 := x[1] + x2 := x[2] + x3 := x[3] + x4 := x[4] - x0 = x[0] - x1 = x[1] - x2 = x[2] - x3 = x[3] - x4 = x[4] - - y0 = y[0] - y1 = y[1] - y2 = y[2] - y3 = y[3] - y4 = y[4] + y0 := y[0] + y1 := y[1] + y2 := y[2] + y3 := y[3] + y4 := y[4] // Reduction can be carried out simultaneously to multiplication. For // example, we do not compute a coefficient r_5 . Whenever the result of a @@ -106,22 +103,6 @@ func (v *FieldElement) Mul(x, y *FieldElement) *FieldElement { // r_0 to r_1 , from r_1 to r_2 , from r_2 to r_3 , from r_3 to r_4 , and // finally from r_4 to r_0 . Each of these carries is done as one copy, one // right shift by 51, one logical and with 2^51 − 1, and one addition. - - r10 += r00 >> 51 - r00 &= maskLow51Bits - r20 += r10 >> 51 - r10 &= maskLow51Bits - r30 += r20 >> 51 - r20 &= maskLow51Bits - r40 += r30 >> 51 - r30 &= maskLow51Bits - r00 += (r40 >> 51) * 19 - r40 &= maskLow51Bits - - v[0] = r00 - v[1] = r10 - v[2] = r20 - v[3] = r30 - v[4] = r40 - return v + *v = FieldElement{r00, r10, r20, r30, r40} + return v.lightReduce1().lightReduce2() } diff --git a/internal/radix51/fe_square.go b/internal/radix51/fe_square.go index eb9a565..c04c72a 100644 --- a/internal/radix51/fe_square.go +++ b/internal/radix51/fe_square.go @@ -12,13 +12,11 @@ func (v *FieldElement) Square(x *FieldElement) *FieldElement { // this is combined with multiplication by 19 where possible. The coefficient // reduction after squaring is the same as for multiplication. - var x0, x1, x2, x3, x4 uint64 - - x0 = x[0] - x1 = x[1] - x2 = x[2] - x3 = x[3] - x4 = x[4] + x0 := x[0] + x1 := x[1] + x2 := x[2] + x3 := x[3] + x4 := x[4] x0_2 := x0 << 1 x1_2 := x1 << 1 @@ -79,21 +77,6 @@ func (v *FieldElement) Square(x *FieldElement) *FieldElement { r41 *= 19 r00 += r41 - r10 += r00 >> 51 - r00 &= maskLow51Bits - r20 += r10 >> 51 - r10 &= maskLow51Bits - r30 += r20 >> 51 - r20 &= maskLow51Bits - r40 += r30 >> 51 - r30 &= maskLow51Bits - r00 += (r40 >> 51) * 19 - r40 &= maskLow51Bits - - v[0] = r00 - v[1] = r10 - v[2] = r20 - v[3] = r30 - v[4] = r40 - return v + *v = FieldElement{r00, r10, r20, r30, r40} + return v.lightReduce1().lightReduce2() } diff --git a/internal/radix51/fe_test.go b/internal/radix51/fe_test.go index 3b52e38..6b9424c 100644 --- a/internal/radix51/fe_test.go +++ b/internal/radix51/fe_test.go @@ -145,10 +145,10 @@ func TestFromBytesRoundTrip(t *testing.T) { r.FromBytes(out[:]) // Intentionally not using Equal not to go through Bytes again. - // Calling Reduce because both Generate and FromBytes can produce + // Calling reduce because both Generate and FromBytes can produce // non-canonical representations. - fe.Reduce(&fe) - r.Reduce(&r) + fe.reduce(&fe) + r.reduce(&r) return fe == r } if err := quick.Check(f2, nil); err != nil { @@ -287,7 +287,7 @@ func TestFeInvert(t *testing.T) { xinv.Invert(&x) r.Mul(&x, &xinv) - r.Reduce(&r) + r.reduce(&r) if !vartimeEqual(one, r) { t.Errorf("inversion identity failed, got: %x", r) @@ -303,7 +303,7 @@ func TestFeInvert(t *testing.T) { xinv.Invert(&x) r.Mul(&x, &xinv) - r.Reduce(&r) + r.reduce(&r) if !vartimeEqual(one, r) { t.Errorf("random inversion identity failed, got: %x for field element %x", r, x)