mirror of https://github.com/gtank/ristretto255
internal/ed25519: add lookup tables for scalar mul.
This commit is contained in:
parent
2312dea95d
commit
26af03f7b3
|
@ -281,3 +281,36 @@ func (v *ProjP3) Equal(u *ProjP3) int {
|
||||||
|
|
||||||
return t1.Equal(&t2) & t3.Equal(&t4)
|
return t1.Equal(&t2) & t3.Equal(&t4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Constant-time operations
|
||||||
|
|
||||||
|
// Select sets v to a if cond == 1 and to b if cond == 0.
|
||||||
|
func (v *ProjCached) Select(a, b *ProjCached, cond int) *ProjCached {
|
||||||
|
v.YplusX.Select(&a.YplusX, &b.YplusX, cond)
|
||||||
|
v.YminusX.Select(&a.YminusX, &b.YminusX, cond)
|
||||||
|
v.Z.Select(&a.Z, &b.Z, cond)
|
||||||
|
v.T2d.Select(&a.T2d, &b.T2d, cond)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select sets v to a if cond == 1 and to b if cond == 0.
|
||||||
|
func (v *AffineCached) Select(a, b *AffineCached, cond int) *AffineCached {
|
||||||
|
v.YplusX.Select(&a.YplusX, &b.YplusX, cond)
|
||||||
|
v.YminusX.Select(&a.YminusX, &b.YminusX, cond)
|
||||||
|
v.T2d.Select(&a.T2d, &b.T2d, cond)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
||||||
|
func (v *ProjCached) CondNeg(cond int) *ProjCached {
|
||||||
|
radix51.CondSwap(&v.YplusX, &v.YminusX, cond)
|
||||||
|
v.T2d.CondNeg(&v.T2d, cond)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// CondNeg negates v if cond == 1 and leaves it unchanged if cond == 0.
|
||||||
|
func (v *AffineCached) CondNeg(cond int) *AffineCached {
|
||||||
|
radix51.CondSwap(&v.YplusX, &v.YminusX, cond)
|
||||||
|
v.T2d.CondNeg(&v.T2d, cond)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
// Copyright (c) 2019 Henry de Valence.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A dynamic lookup table for variable-base, constant-time scalar muls.
|
||||||
|
type ProjLookupTable struct {
|
||||||
|
points [8]ProjCached
|
||||||
|
}
|
||||||
|
|
||||||
|
// A precomputed lookup table for fixed-base, constant-time scalar muls.
|
||||||
|
type AffineLookupTable struct {
|
||||||
|
points [8]AffineCached
|
||||||
|
}
|
||||||
|
|
||||||
|
// A dynamic lookup table for variable-base, variable-time scalar muls.
|
||||||
|
type NafLookupTable5 struct {
|
||||||
|
points [8]ProjCached
|
||||||
|
}
|
||||||
|
|
||||||
|
// A precomputed lookup table for fixed-base, variable-time scalar muls.
|
||||||
|
type NafLookupTable8 struct {
|
||||||
|
points [64]AffineCached
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constructors.
|
||||||
|
|
||||||
|
// Builds a lookup table at runtime. Fast.
|
||||||
|
func (v *ProjLookupTable) FromP3(q *ProjP3) {
|
||||||
|
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
||||||
|
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
||||||
|
v.points[0].FromP3(q)
|
||||||
|
tmpP3 := ProjP3{}
|
||||||
|
tmpP1xP1 := ProjP1xP1{}
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
// Compute (i+1)*Q as Q + i*Q and convert to a ProjCached
|
||||||
|
// This is needlessly complicated because the API has explicit
|
||||||
|
// recievers instead of creating stack objects and relying on RVO
|
||||||
|
v.points[i+1].FromP3(tmpP3.FromP1xP1(tmpP1xP1.Add(q, &v.points[i])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not optimised for speed; affine tables should be precomputed.
|
||||||
|
func (v *AffineLookupTable) FromP3(q *ProjP3) {
|
||||||
|
// Goal: v.points[i] = (i+1)*Q, i.e., Q, 2Q, ..., 8Q
|
||||||
|
// This allows lookup of -8Q, ..., -Q, 0, Q, ..., 8Q
|
||||||
|
v.points[0].FromP3(q)
|
||||||
|
tmpP3 := ProjP3{}
|
||||||
|
tmpP1xP1 := ProjP1xP1{}
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
// Compute (i+1)*Q as Q + i*Q and convert to AffineCached
|
||||||
|
v.points[i+1].FromP3(tmpP3.FromP1xP1(tmpP1xP1.AddAffine(q, &v.points[i])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds a lookup table at runtime. Fast.
|
||||||
|
func (v *NafLookupTable5) FromP3(q *ProjP3) {
|
||||||
|
// Goal: v.points[i] = (2*i+1)*Q, i.e., Q, 3Q, 5Q, ..., 15Q
|
||||||
|
// This allows lookup of -15Q, ..., -3Q, -Q, 0, Q, 3Q, ..., 15Q
|
||||||
|
v.points[0].FromP3(q)
|
||||||
|
q2 := ProjP3{}
|
||||||
|
q2.Add(q, q)
|
||||||
|
tmpP3 := ProjP3{}
|
||||||
|
tmpP1xP1 := ProjP1xP1{}
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
v.points[i+1].FromP3(tmpP3.FromP1xP1(tmpP1xP1.Add(&q2, &v.points[i])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not optimised for speed; affine tables should be precomputed.
|
||||||
|
func (v *NafLookupTable8) FromP3(q *ProjP3) {
|
||||||
|
v.points[0].FromP3(q)
|
||||||
|
q2 := ProjP3{}
|
||||||
|
q2.Add(q, q)
|
||||||
|
tmpP3 := ProjP3{}
|
||||||
|
tmpP1xP1 := ProjP1xP1{}
|
||||||
|
for i := 0; i < 63; i++ {
|
||||||
|
v.points[i+1].FromP3(tmpP3.FromP1xP1(tmpP1xP1.AddAffine(&q2, &v.points[i])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Selectors.
|
||||||
|
|
||||||
|
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
||||||
|
func (v *ProjLookupTable) SelectInto(dest *ProjCached, x int8) {
|
||||||
|
// Compute xabs = |x|
|
||||||
|
xmask := x >> 7
|
||||||
|
xabs := uint8((x + xmask) ^ xmask)
|
||||||
|
|
||||||
|
dest.Zero()
|
||||||
|
for j := 1; j <= 8; j++ {
|
||||||
|
// Set dest = j*Q if |x| = j
|
||||||
|
cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
|
||||||
|
dest.Select(&v.points[j-1], dest, cond)
|
||||||
|
}
|
||||||
|
// Now dest = |x|*Q, conditionally negate to get x*Q
|
||||||
|
dest.CondNeg(int(xmask & 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set dest to x*Q, where -8 <= x <= 8, in constant time.
|
||||||
|
func (v *AffineLookupTable) SelectInto(dest *AffineCached, x int8) {
|
||||||
|
// Compute xabs = |x|
|
||||||
|
xmask := x >> 7
|
||||||
|
xabs := uint8((x + xmask) ^ xmask)
|
||||||
|
|
||||||
|
dest.Zero()
|
||||||
|
for j := 1; j <= 8; j++ {
|
||||||
|
// Set dest = j*Q if |x| = j
|
||||||
|
cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
|
||||||
|
dest.Select(&v.points[j-1], dest, cond)
|
||||||
|
}
|
||||||
|
// Now dest = |x|*Q, conditionally negate to get x*Q
|
||||||
|
dest.CondNeg(int(xmask & 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given odd x with 0 < x < 2^4, return x*Q (in variable time).
|
||||||
|
func (v *NafLookupTable5) SelectInto(dest *ProjCached, x int8) {
|
||||||
|
*dest = v.points[x/2]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given odd x with 0 < x < 2^7, return x*Q (in variable time).
|
||||||
|
func (v *NafLookupTable8) SelectInto(dest *AffineCached, x int8) {
|
||||||
|
*dest = v.points[x/2]
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gtank/ristretto255/internal/radix51"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
B = ProjP3{
|
||||||
|
X: radix51.FieldElement([5]uint64{426475514619346, 2063872706840040, 14628272888959, 107677749330612, 288339085807592}),
|
||||||
|
Y: radix51.FieldElement([5]uint64{1934594822876571, 2049809580636559, 1991994783322914, 1758681962032007, 380046701118659}),
|
||||||
|
Z: radix51.FieldElement([5]uint64{1, 0, 0, 0, 0}),
|
||||||
|
T: radix51.FieldElement([5]uint64{410445769351754, 2235400917701188, 1495825632738689, 1351628537510093, 430502003771208}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestProjLookupTable(t *testing.T) {
|
||||||
|
var table ProjLookupTable
|
||||||
|
table.FromP3(&B)
|
||||||
|
|
||||||
|
var tmp1, tmp2, tmp3 ProjCached
|
||||||
|
table.SelectInto(&tmp1, 6)
|
||||||
|
table.SelectInto(&tmp2, -2)
|
||||||
|
table.SelectInto(&tmp3, -4)
|
||||||
|
// Expect T1 + T2 + T3 = identity
|
||||||
|
|
||||||
|
var accP1xP1 ProjP1xP1
|
||||||
|
var accP3, check ProjP3
|
||||||
|
accP3.Zero()
|
||||||
|
check.Zero()
|
||||||
|
|
||||||
|
accP1xP1.Add(&accP3, &tmp1)
|
||||||
|
accP3.FromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.Add(&accP3, &tmp2)
|
||||||
|
accP3.FromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.Add(&accP3, &tmp3)
|
||||||
|
accP3.FromP1xP1(&accP1xP1)
|
||||||
|
|
||||||
|
if accP3.Equal(&check) != 1 {
|
||||||
|
t.Errorf("Sanity check on ProjLookupTable.SelectInto failed! %x %x %x", tmp1, tmp2, tmp3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAffineLookupTable(t *testing.T) {
|
||||||
|
var table AffineLookupTable
|
||||||
|
table.FromP3(&B)
|
||||||
|
|
||||||
|
var tmp1, tmp2, tmp3 AffineCached
|
||||||
|
table.SelectInto(&tmp1, 3)
|
||||||
|
table.SelectInto(&tmp2, -7)
|
||||||
|
table.SelectInto(&tmp3, 4)
|
||||||
|
// Expect T1 + T2 + T3 = identity
|
||||||
|
|
||||||
|
var accP1xP1 ProjP1xP1
|
||||||
|
var accP3, check ProjP3
|
||||||
|
accP3.Zero()
|
||||||
|
check.Zero()
|
||||||
|
|
||||||
|
accP1xP1.AddAffine(&accP3, &tmp1)
|
||||||
|
accP3.FromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.AddAffine(&accP3, &tmp2)
|
||||||
|
accP3.FromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.AddAffine(&accP3, &tmp3)
|
||||||
|
accP3.FromP1xP1(&accP1xP1)
|
||||||
|
|
||||||
|
if accP3.Equal(&check) != 1 {
|
||||||
|
t.Errorf("Sanity check on ProjLookupTable.SelectInto failed! %x %x %x", tmp1, tmp2, tmp3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNafLookupTable5(t *testing.T) {
|
||||||
|
var table NafLookupTable5
|
||||||
|
table.FromP3(&B)
|
||||||
|
|
||||||
|
var tmp1, tmp2, tmp3, tmp4 ProjCached
|
||||||
|
table.SelectInto(&tmp1, 9)
|
||||||
|
table.SelectInto(&tmp2, 11)
|
||||||
|
table.SelectInto(&tmp3, 7)
|
||||||
|
table.SelectInto(&tmp4, 13)
|
||||||
|
// Expect T1 + T2 = T3 + T4
|
||||||
|
|
||||||
|
var accP1xP1 ProjP1xP1
|
||||||
|
var lhs, rhs ProjP3
|
||||||
|
lhs.Zero()
|
||||||
|
rhs.Zero()
|
||||||
|
|
||||||
|
accP1xP1.Add(&lhs, &tmp1)
|
||||||
|
lhs.FromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.Add(&lhs, &tmp2)
|
||||||
|
lhs.FromP1xP1(&accP1xP1)
|
||||||
|
|
||||||
|
accP1xP1.Add(&rhs, &tmp3)
|
||||||
|
rhs.FromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.Add(&rhs, &tmp4)
|
||||||
|
rhs.FromP1xP1(&accP1xP1)
|
||||||
|
|
||||||
|
if lhs.Equal(&rhs) != 1 {
|
||||||
|
t.Errorf("Sanity check on NafLookupTable5 failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNafLookupTable8(t *testing.T) {
|
||||||
|
var table NafLookupTable8
|
||||||
|
table.FromP3(&B)
|
||||||
|
|
||||||
|
var tmp1, tmp2, tmp3, tmp4 AffineCached
|
||||||
|
table.SelectInto(&tmp1, 49)
|
||||||
|
table.SelectInto(&tmp2, 11)
|
||||||
|
table.SelectInto(&tmp3, 35)
|
||||||
|
table.SelectInto(&tmp4, 25)
|
||||||
|
// Expect T1 + T2 = T3 + T4
|
||||||
|
|
||||||
|
var accP1xP1 ProjP1xP1
|
||||||
|
var lhs, rhs ProjP3
|
||||||
|
lhs.Zero()
|
||||||
|
rhs.Zero()
|
||||||
|
|
||||||
|
accP1xP1.AddAffine(&lhs, &tmp1)
|
||||||
|
lhs.FromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.AddAffine(&lhs, &tmp2)
|
||||||
|
lhs.FromP1xP1(&accP1xP1)
|
||||||
|
|
||||||
|
accP1xP1.AddAffine(&rhs, &tmp3)
|
||||||
|
rhs.FromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.AddAffine(&rhs, &tmp4)
|
||||||
|
rhs.FromP1xP1(&accP1xP1)
|
||||||
|
|
||||||
|
if lhs.Equal(&rhs) != 1 {
|
||||||
|
t.Errorf("Sanity check on NafLookupTable8 failed")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue