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)
|
||||
}
|
||||
|
||||
// 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