internal/ed25519: add vartime double-base scmul

This commit is contained in:
Henry de Valence 2019-05-08 23:46:54 -07:00 committed by Filippo Valsorda
parent 7b8b390b63
commit 4ba8cc9326
2 changed files with 109 additions and 8 deletions

View File

@ -99,14 +99,6 @@ func (v *ProjP3) ScalarMul(x *scalar.Scalar, q *ProjP3) *ProjP3 {
return v
}
// Set v to a*A + b*B, where B is the Ed25519 basepoint, and return v.
//
// The scalar multiplication is done in variable time.
func (v *ProjP3) VartimeDoubleBaseMul(a, b *scalar.Scalar, A *ProjP3) *ProjP3 {
panic("unimplemented")
return v
}
// Set v to the result of a multiscalar multiplication and return v.
//
// The multiscalar multiplication is sum(scalars[i]*points[i]).
@ -162,6 +154,80 @@ func (v *ProjP3) MultiscalarMul(scalars []scalar.Scalar, points []*ProjP3) *Proj
return v
}
// Set v to a*A + b*B, where B is the Ed25519 basepoint, and return v.
//
// The scalar multiplication is done in variable time.
func (v *ProjP3) VartimeDoubleBaseMul(a, b *scalar.Scalar, A *ProjP3) *ProjP3 {
// Similarly to the single variable-base approach, we compute
// digits and use them with a lookup table. However, because
// we are allowed to do variable-time operations, we don't
// need constant-time lookups or constant-time digit
// computations.
//
// So we use a non-adjacent form of some width w instead of
// radix 16. This is like a binary representation (one digit
// for each binary place) but we allow the digits to grow in
// magnitude up to 2^{w-1} so that the nonzero digits are as
// sparse as possible. Intuitively, this "condenses" the
// "mass" of the scalar onto sparse coefficients (meaning
// fewer additions).
var aTable NafLookupTable5
aTable.FromP3(A)
// Because the basepoint is fixed, we can use a wider NAF
// corresponding to a bigger table.
aNaf := a.NonAdjacentForm(5)
bNaf := b.NonAdjacentForm(8)
// Find the first nonzero coefficient.
i := 255
for j := i; j >= 0; j-- {
if aNaf[j] != 0 || bNaf[j] != 0 {
break
}
}
multA := &ProjCached{}
multB := &AffineCached{}
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.
for ; i >= 0; i-- {
tmp1.Double(tmp2)
// Only update v if we have a nonzero coeff to add in.
if aNaf[i] > 0 {
v.FromP1xP1(tmp1)
aTable.SelectInto(multA, aNaf[i])
tmp1.Add(v, multA)
} else if aNaf[i] < 0 {
v.FromP1xP1(tmp1)
aTable.SelectInto(multA, -aNaf[i])
tmp1.Sub(v, multA)
}
if bNaf[i] > 0 {
v.FromP1xP1(tmp1)
basepointNafTable.SelectInto(multB, bNaf[i])
tmp1.AddAffine(v, multB)
} else if bNaf[i] < 0 {
v.FromP1xP1(tmp1)
basepointNafTable.SelectInto(multB, -bNaf[i])
tmp1.SubAffine(v, multB)
}
tmp2.FromP1xP1(tmp1)
}
v.FromP2(tmp2)
return v
}
// Set v to the result of a multiscalar multiplication and return v.
//
// The multiscalar multiplication is sum(scalars[i]*points[i]).

View File

@ -56,6 +56,19 @@ func TestBasepointMulVsDalek(t *testing.T) {
}
}
func TestVartimeDoubleBaseMulVsDalek(t *testing.T) {
var p ProjP3
var z scalar.Scalar
p.VartimeDoubleBaseMul(&dalekScalar, &z, &B)
if dalekScalarBasepoint.Equal(&p) != 1 {
t.Error("VartimeDoubleBaseMul fails with b=0")
}
p.VartimeDoubleBaseMul(&z, &dalekScalar, &B)
if dalekScalarBasepoint.Equal(&p) != 1 {
t.Error("VartimeDoubleBaseMul fails with a=0")
}
}
func TestScalarMulDistributesOverAdd(t *testing.T) {
scalarMulDistributesOverAdd := func(x, y scalar.Scalar) bool {
// The quickcheck generation strategy chooses a random
@ -154,3 +167,25 @@ func TestBasepointNafTableGeneration(t *testing.T) {
t.Error("BasepointNafTable does not match")
}
}
func TestVartimeDoubleBaseMulMatchesBasepointMul(t *testing.T) {
vartimeDoubleBaseMulMatchesBasepointMul := func(x, y scalar.Scalar) bool {
// FIXME opaque scalars
x[31] &= 127
y[31] &= 127
var p, q1, q2, check ProjP3
p.VartimeDoubleBaseMul(&x, &y, &B)
q1.BasepointMul(&x)
q2.BasepointMul(&y)
check.Zero()
check.Add(&q1, &q2)
return p.Equal(&check) == 1
}
if err := quick.Check(vartimeDoubleBaseMulMatchesBasepointMul, quickCheckConfig); err != nil {
t.Error(err)
}
}