diff --git a/unsafe.go b/unsafe.go new file mode 100644 index 0000000..b37be58 --- /dev/null +++ b/unsafe.go @@ -0,0 +1,81 @@ +package edwards25519 + +// UnsafeVarTimeScalarMult sets v = x * q, and returns v., and returns v. Execution time depends on the inputs. +// Deprecated: Unsafe for private operations +func (v *Point) UnsafeVarTimeScalarMult(x *Scalar, q *Point) *Point { + checkInitialized(q) + + // Build lookup table for point q + var table nafLookupTable5 + table.FromP3(q) + // Compute a NAF for scalar x + naf := x.nonAdjacentForm(5) + + multiple := &projCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + tmp2.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) + if naf[i] > 0 { + v.fromP1xP1(tmp1) + table.SelectInto(multiple, naf[i]) + tmp1.Add(v, multiple) + } else if naf[i] < 0 { + v.fromP1xP1(tmp1) + table.SelectInto(multiple, -naf[i]) + tmp1.Sub(v, multiple) + } + + tmp2.FromP1xP1(tmp1) + } + + v.fromP2(tmp2) + return v +} + +// UnsafeVarTimeScalarBaseMult sets v = x * B, where B is the canonical generator, and returns v. Execution time depends on the inputs. +// Deprecated: Unsafe for private operations +// This is not faster than ScalarBaseMult +func (v *Point) UnsafeVarTimeScalarBaseMult(x *Scalar) *Point { + basepointNafTable := basepointNafTable() + // Because the basepoint is fixed, we can use a wider NAF + // corresponding to a bigger table. + naf := x.nonAdjacentForm(8) + + multiple := &affineCached{} + tmp1 := &projP1xP1{} + tmp2 := &projP2{} + tmp2.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) + if naf[i] > 0 { + v.fromP1xP1(tmp1) + basepointNafTable.SelectInto(multiple, naf[i]) + tmp1.AddAffine(v, multiple) + } else if naf[i] < 0 { + v.fromP1xP1(tmp1) + basepointNafTable.SelectInto(multiple, -naf[i]) + tmp1.SubAffine(v, multiple) + } + + tmp2.FromP1xP1(tmp1) + } + + v.fromP2(tmp2) + return v +} diff --git a/unsafe_test.go b/unsafe_test.go new file mode 100644 index 0000000..a63510e --- /dev/null +++ b/unsafe_test.go @@ -0,0 +1,100 @@ +package edwards25519 + +import ( + "testing" + "testing/quick" +) + +func TestUnsafeVarTimeScalarMultMatchesScalarBaseMult(t *testing.T) { + unsafeVarTimeScalarMultMatchesScalarMult := func(x Scalar) bool { + var p, check Point + + p.UnsafeVarTimeScalarMult(&x, B) + + check.ScalarBaseMult(&x) + + checkOnCurve(t, &p, &check) + return p.Equal(&check) == 1 + } + + if err := quick.Check(unsafeVarTimeScalarMultMatchesScalarMult, quickCheckConfig32); err != nil { + t.Error(err) + } +} + +func TestUnsafeVarTimeScalarBaseMultMatchesScalarBaseMult(t *testing.T) { + unsafeVarTimeScalarBaseMultMatchesScalarMult := func(x Scalar) bool { + var p, check Point + + p.UnsafeVarTimeScalarBaseMult(&x) + + check.ScalarBaseMult(&x) + + checkOnCurve(t, &p, &check) + return p.Equal(&check) == 1 + } + + if err := quick.Check(unsafeVarTimeScalarBaseMultMatchesScalarMult, quickCheckConfig32); err != nil { + t.Error(err) + } +} + +func TestUnsafeVarTimeScalarMultNonIdentityPoint(t *testing.T) { + // Check whether p.ScalarMult and q.ScalaBaseMult give the same, + // when p and q are originally set to the base point. + + unsafeVarTimeScalarMultNonIdentityPoint := func(x Scalar) bool { + var p, q Point + p.Set(B) + q.Set(B) + + p.UnsafeVarTimeScalarMult(&x, B) + q.ScalarBaseMult(&x) + + checkOnCurve(t, &p, &q) + + return p.Equal(&q) == 1 + } + + if err := quick.Check(unsafeVarTimeScalarMultNonIdentityPoint, quickCheckConfig32); err != nil { + t.Error(err) + } +} + +func TestUnsafeVarTimeScalarBaseMultNonIdentityPoint(t *testing.T) { + // Check whether p.ScalarMult and q.ScalaBaseMult give the same, + // when p and q are originally set to the base point. + + unsafeVarTimeScalarBaseMultNonIdentityPoint := func(x Scalar) bool { + var p, q Point + p.Set(B) + q.Set(B) + + p.UnsafeVarTimeScalarBaseMult(&x) + q.ScalarBaseMult(&x) + + checkOnCurve(t, &p, &q) + + return p.Equal(&q) == 1 + } + + if err := quick.Check(unsafeVarTimeScalarBaseMultNonIdentityPoint, quickCheckConfig32); err != nil { + t.Error(err) + } +} + +func BenchmarkUnsafeVarTimeScalarMult(b *testing.B) { + var p Point + + for i := 0; i < b.N; i++ { + p.UnsafeVarTimeScalarMult(dalekScalar, B) + } +} + +func BenchmarkUnsafeVarTimeScalarBaseMult(b *testing.B) { + var p Point + + for i := 0; i < b.N; i++ { + p.UnsafeVarTimeScalarBaseMult(dalekScalar) + } +}