diff --git a/internal/edwards25519/scalarMul.go b/internal/edwards25519/scalarMul.go new file mode 100644 index 0000000..79a0179 --- /dev/null +++ b/internal/edwards25519/scalarMul.go @@ -0,0 +1,86 @@ +// 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 ( + "github.com/gtank/ristretto255/internal/scalar" +) + +// Set v to x*B, where B is the Ed25519 basepoint, and return v. +// +// The scalar multiplication is done in constant time. +func (v *ProjP3) BasepointMul(x *scalar.Scalar) *ProjP3 { + panic("unimplemented") + return v +} + +// Set v to x*Q, and return v. v and q may alias. +// +// The scalar multiplication is done in constant time. +func (v *ProjP3) ScalarMul(x *scalar.Scalar, q *ProjP3) *ProjP3 { + var table ProjLookupTable + table.FromP3(q) + // v and q could alias, but once the table is built we can clobber v. + v.Zero() + + // Write x = sum(x_i * 16^i) + // so x*Q = sum( Q*x_i*16^i ) + // = Q*x_0 + 16*(Q*x_1 + 16*( ... + Q*x_63) ... ) + // <------compute inside out--------- + // + // We use the lookup table to get the x_i*Q values + // and do four doublings to compute 16*Q + digits := x.SignedRadix16() + + // Unwrap first loop iteration to save computing 16*identity + multiple := &ProjCached{} + tmp1 := &ProjP1xP1{} + tmp2 := &ProjP2{} + table.SelectInto(multiple, digits[63]) + tmp1.Add(v, multiple) // tmp1 = x_63*Q in P1xP1 coords + for i := 62; i >= 0; i-- { + tmp2.FromP1xP1(tmp1) // tmp2 = (prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 2*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 2*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 4*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 4*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 8*(prev) in P1xP1 coords + tmp2.FromP1xP1(tmp1) // tmp2 = 8*(prev) in P2 coords + tmp1.Double(tmp2) // tmp1 = 16*(prev) in P1xP1 coords + v.FromP1xP1(tmp1) // v = 16*(prev) in P3 coords + table.SelectInto(multiple, digits[i]) + tmp1.Add(v, multiple) // tmp1 = x_i*Q + 16*(prev) in P1xP1 coords + } + v.FromP1xP1(tmp1) + 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]). +// +// The multiscalar multiplication is performed in constant time. +func (v *ProjP3) MultiscalarMul(scalars []scalar.Scalar, points []*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]). +// +// The multiscalar multiplication is performed in variable time. +func (v *ProjP3) VartimeMultiscalarMul(scalars []scalar.Scalar, points []*ProjP3) *ProjP3 { + panic("unimplemented") + return v +} diff --git a/internal/edwards25519/scalarMul_test.go b/internal/edwards25519/scalarMul_test.go new file mode 100644 index 0000000..b244d84 --- /dev/null +++ b/internal/edwards25519/scalarMul_test.go @@ -0,0 +1,70 @@ +package edwards25519 + +import ( + "github.com/gtank/ristretto255/internal/radix51" + "github.com/gtank/ristretto255/internal/scalar" + "testing" + "testing/quick" +) + +// quickCheckConfig will make each quickcheck test run (1024 * -quickchecks) +// times. The default value of -quickchecks is 100. +var ( + quickCheckConfig = &quick.Config{MaxCountScale: 1 << 10} +) + +func TestScalarMulSmallScalars(t *testing.T) { + var z scalar.Scalar + var p, check ProjP3 + p.ScalarMul(&z, &B) + check.Zero() + if check.Equal(&p) != 1 { + t.Error("0*B != 0") + } + + z = scalar.Scalar([32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}) + p.ScalarMul(&z, &B) + check.Set(&B) + if check.Equal(&p) != 1 { + t.Error("1*B != 1") + } +} + +func TestScalarMulVsDalek(t *testing.T) { + expected := ProjP3{ + X: radix51.FieldElement([5]uint64{778774234987948, 1589187156384239, 1213330452914652, 186161118421127, 2186284806803213}), + Y: radix51.FieldElement([5]uint64{1241255309069369, 1115278942994853, 1016511918109334, 1303231926552315, 1801448517689873}), + Z: radix51.FieldElement([5]uint64{353337085654440, 1327844406437681, 2207296012811921, 707394926933424, 917408459573183}), + T: radix51.FieldElement([5]uint64{585487439439725, 1792815221887900, 946062846079052, 1954901232609667, 1418300670001780}), + } + z := scalar.Scalar([32]byte{219, 106, 114, 9, 174, 249, 155, 89, 69, 203, 201, 93, 92, 116, 234, 187, 78, 115, 103, 172, 182, 98, 62, 103, 187, 136, 13, 100, 248, 110, 12, 4}) + + var p ProjP3 + p.ScalarMul(&z, &B) + if expected.Equal(&p) != 1 { + t.Error("Scalar mul does not match dalek") + } +} + +func TestScalarMulDistributesOverAdd(t *testing.T) { + scalarMulDistributesOverAdd := func(x, y scalar.Scalar) bool { + // The quickcheck generation strategy chooses a random + // 32-byte array, but we require that the high bit is + // unset. FIXME: make Scalar opaque. Until then, + // mask the high bits: + x[31] &= 127 + y[31] &= 127 + var z scalar.Scalar + z.Add(&x, &y) + var p, q, r, check ProjP3 + p.ScalarMul(&x, &B) + q.ScalarMul(&y, &B) + r.ScalarMul(&z, &B) + check.Add(&p, &q) + return check.Equal(&r) == 1 + } + + if err := quick.Check(scalarMulDistributesOverAdd, quickCheckConfig); err != nil { + t.Error(err) + } +}