From 8186dbd6e1a946d15493de0434a25989305d87d0 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Thu, 9 May 2019 00:05:08 -0700 Subject: [PATCH] internal/ed25519: add variable-time multiscalar mul --- internal/edwards25519/scalarMul.go | 51 ++++++++++++++++++++++++- internal/edwards25519/scalarMul_test.go | 24 ++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/internal/edwards25519/scalarMul.go b/internal/edwards25519/scalarMul.go index db872f8..e560610 100644 --- a/internal/edwards25519/scalarMul.go +++ b/internal/edwards25519/scalarMul.go @@ -234,6 +234,55 @@ func (v *ProjP3) VartimeDoubleBaseMul(a, b *scalar.Scalar, A *ProjP3) *ProjP3 { // // The multiscalar multiplication is performed in variable time. func (v *ProjP3) VartimeMultiscalarMul(scalars []scalar.Scalar, points []*ProjP3) *ProjP3 { - panic("unimplemented") + if len(scalars) != len(points) { + panic("called MultiscalarMul with different size inputs") + } + + // 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() + v.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 } diff --git a/internal/edwards25519/scalarMul_test.go b/internal/edwards25519/scalarMul_test.go index ef3dca1..22d9c83 100644 --- a/internal/edwards25519/scalarMul_test.go +++ b/internal/edwards25519/scalarMul_test.go @@ -189,3 +189,27 @@ func TestVartimeDoubleBaseMulMatchesBasepointMul(t *testing.T) { t.Error(err) } } + +func TestVartimeMultiScalarMulMatchesBasepointMul(t *testing.T) { + vartimeMultiScalarMulMatchesBasepointMul := func(x, y, z scalar.Scalar) bool { + // FIXME opaque scalars + x[31] &= 127 + y[31] &= 127 + z[31] &= 127 + var p, q1, q2, q3, check ProjP3 + + p.VartimeMultiscalarMul([]scalar.Scalar{x, y, z}, []*ProjP3{&B, &B, &B}) + + q1.BasepointMul(&x) + q2.BasepointMul(&y) + q3.BasepointMul(&z) + check.Zero() + check.Add(&q1, &q2).Add(&check, &q3) + + return p.Equal(&check) == 1 + } + + if err := quick.Check(vartimeMultiScalarMulMatchesBasepointMul, quickCheckConfig); err != nil { + t.Error(err) + } +}