Compare commits

...

15 commits

Author SHA1 Message Date
DataHoarder a67b1e9d0a
Faster UnsafeVarTimeScalarBaseMult 2024-04-05 10:16:06 +02:00
DataHoarder b67b282d40
Added unsafe VarTime methods for ScalarMult, ScalarBaseMult 2024-04-05 09:03:06 +02:00
DataHoarder f0596f127a
Replace package name with git.gammaspectra.live/P2Pool/edwards25519 2024-04-05 09:03:06 +02:00
Daniel Bourdrez a7dfd8e4e6
extra: rectify pow2k function comment (#35) 2023-12-10 14:26:02 -05:00
Filippo Valsorda 325f520de7 all: update Go version 2023-12-10 20:13:24 +01:00
Dmitri Shuralyov c0501e42ed all: drop old +build lines
Running 'go fix' on the cmd+std packages handled much of this change.

Also update code generators to use only the new go:build lines,
not the old +build ones.

For golang/go#41184.
For golang/go#60268.

Change-Id: If35532abe3012e7357b02c79d5992ff5ac37ca23
Cq-Include-Trybots: luci.golang.try:gotip-linux-386-longtest,gotip-linux-amd64-longtest,gotip-windows-amd64-longtest
Reviewed-on: https://go-review.googlesource.com/c/go/+/536237
Reviewed-by: Ian Lance Taylor <iant@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
2023-12-10 20:10:48 +01:00
Jes Cok 23384ff85b all: use the indefinite article an in comments
This is a follow up of CL 530120.

Change-Id: Ifa0bd1c3bb9bb1202568eaae27500bcea376f56b
GitHub-Last-Rev: b4154fa1fc205a6a1af050ab49a4738f73b3c32a
GitHub-Pull-Request: golang/go#63228
Reviewed-on: https://go-review.googlesource.com/c/go/+/531136
Auto-Submit: Bryan Mills <bcmills@google.com>
Reviewed-by: Bryan Mills <bcmills@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
2023-12-10 20:10:48 +01:00
Bryan C. Mills 16197b4051 crypto/internal/edwards25519: shorten quick.Check tests in short mode
The edwards25519 tests can be quite slow on platforms without a
well-optimized implementation, especially if the race detector is also
enabled. Since these tests aren't checking for specific inputs anyway,
the extra coverage of a more aggressive quick.Config does not seem
worth wasting extra time on slow CI builders and TryBots.

For #60109.

Change-Id: I530e75a0b76725585df5a2f5ded6705ab1b9da51
Reviewed-on: https://go-review.googlesource.com/c/go/+/522715
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Auto-Submit: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Bryan Mills <bcmills@google.com>
Reviewed-by: Joedian Reid <joedian@golang.org>
2023-12-10 20:10:48 +01:00
cui fliter 6387a56aa6 all: fix misuses of "a" vs "an"
Fixes the misuse of "a" vs "an", according to English grammatical
expectations and using https://www.a-or-an.com/

Change-Id: I53ac724070e3ff3d33c304483fe72c023c7cda47
Reviewed-on: https://go-review.googlesource.com/c/go/+/480536
Run-TryBot: shuang cui <imcusg@gmail.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
2023-12-10 20:10:48 +01:00
Jorropo c901e5ec3c crypto/internal/edwards25519: reduce Point size by reordering fields
Updates #58483

Tested on Linux amd64:
  type Element struct {
    l0, l1, l2, l3, l4 uint64
  }

  type PointAfter struct {
    x, y, z, t Element
    _          incomparable
  }

  type PointBefore struct {
    _          incomparable
    x, y, z, t Element
  }

  type incomparable [0]func()

  func main() {
    fmt.Println(unsafe.Sizeof(PointAfter{})) // 168
    fmt.Println(unsafe.Sizeof(PointBefore{})) // 160
  }

Change-Id: I6c4fcb586bbf3febf62b6e54608496ff81685e43
Reviewed-on: https://go-review.googlesource.com/c/go/+/467616
Reviewed-by: Roland Shoemaker <roland@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Damien Neil <dneil@google.com>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
2023-12-10 20:10:48 +01:00
cui fliter daffb31912 all: fix problematic comments
Change-Id: If092ae7c72b66f172ae32fa6c7294a7ac250362e
Reviewed-on: https://go-review.googlesource.com/c/go/+/463995
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Run-TryBot: Than McIntosh <thanm@google.com>
2023-12-10 20:10:48 +01:00
Michael Pratt 5caf132ffa all: give nested modules fully-qualified names
The two crypto modules are both named "asm". If both are included in a
single go.work (e.g., from `go work use -r .` in the repo), builds break
from "module asm appears multiple times in workspace".

Give these modules fully-qualified names to avoid conflicts. While we
are here, also expand the name of two other testdata modules. Those
modules don't currently conflict, but they have vague names at risk of
future conflicts.

Fixes golang/go#57769.

Change-Id: I2bd8a505051e92348d49560ec698ed921f2c81be
Reviewed-on: https://go-review.googlesource.com/c/go/+/461896
Reviewed-by: Bryan Mills <bcmills@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Run-TryBot: Michael Pratt <mpratt@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Auto-Submit: Michael Pratt <mpratt@google.com>
2023-12-10 20:10:48 +01:00
Filippo Valsorda 4bafd0bab4 edwards25519: gofmt scalar_fiat.go 2023-12-10 20:04:13 +01:00
Dan Kortschak 40637db7b9 all: fix comment typos
Change-Id: Ic16824482142d4de4d0b949459e36505ee944ff7
Reviewed-on: https://go-review.googlesource.com/c/go/+/448175
Reviewed-by: Robert Griesemer <gri@google.com>
Run-TryBot: Dan Kortschak <dan@kortschak.io>
Auto-Submit: Robert Griesemer <gri@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Auto-Submit: Dan Kortschak <dan@kortschak.io>
Auto-Submit: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
Reviewed-by: Ian Lance Taylor <iant@google.com>
2023-12-10 20:00:32 +01:00
Cuong Manh Le f25ca2c5f0 edwards25519: drop Go builders noopt check 2023-12-10 20:00:03 +01:00
19 changed files with 369 additions and 104 deletions

View file

@ -6,12 +6,12 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
go: [ '1.17', '1.x' ]
go: [ '1.20', '1.x' ]
steps:
- uses: actions/setup-go@v2
with: { go-version: "${{ matrix.go }}" }
- uses: actions/checkout@v2
- run: go test -quickchecks 1 ./...
- run: go test -quickchecks 1 -tags purego ./...
- run: go test -short ./...
- run: go test -short -tags purego ./...
- run: GOARCH=arm64 go test -c
- run: GOARCH=arm go test -c

View file

@ -7,7 +7,7 @@ package edwards25519
import (
"errors"
"filippo.io/edwards25519/field"
"git.gammaspectra.live/P2Pool/edwards25519/field"
)
// Point types.
@ -27,13 +27,13 @@ type projP2 struct {
//
// The zero value is NOT valid, and it may be used only as a receiver.
type Point struct {
// The point is internally represented in extended coordinates (X, Y, Z, T)
// where x = X/Z, y = Y/Z, and xy = T/Z per https://eprint.iacr.org/2008/522.
x, y, z, t field.Element
// Make the type not comparable (i.e. used with == or as a map key), as
// equivalent points can be represented by different Go values.
_ incomparable
// The point is internally represented in extended coordinates (X, Y, Z, T)
// where x = X/Z, y = Y/Z, and xy = T/Z per https://eprint.iacr.org/2008/522.
x, y, z, t field.Element
}
type incomparable [0]func()

View file

@ -6,12 +6,10 @@ package edwards25519
import (
"encoding/hex"
"os"
"reflect"
"strings"
"testing"
"filippo.io/edwards25519/field"
"git.gammaspectra.live/P2Pool/edwards25519/field"
)
var B = NewGeneratorPoint()
@ -282,9 +280,6 @@ func TestNonCanonicalPoints(t *testing.T) {
var testAllocationsSink byte
func TestAllocations(t *testing.T) {
if strings.HasSuffix(os.Getenv("GO_BUILDER_NAME"), "-noopt") {
t.Skip("skipping allocations test without relevant optimizations")
}
if allocs := testing.AllocsPerRun(100, func() {
p := NewIdentityPoint()
p.Add(p, NewGeneratorPoint())

View file

@ -10,7 +10,7 @@ package edwards25519
import (
"errors"
"filippo.io/edwards25519/field"
"git.gammaspectra.live/P2Pool/edwards25519/field"
)
// ExtendedCoordinates returns v in extended coordinates (X:Y:Z:T) where
@ -124,7 +124,7 @@ func (v *Point) MultByCofactor(p *Point) *Point {
return v.fromP1xP1(&result)
}
// Given k > 0, set s = s**(2*i).
// Given k > 0, set s = s**(2*k).
func (s *Scalar) pow2k(k int) {
for i := 0; i < k; i++ {
s.Multiply(s, s)

View file

@ -110,7 +110,7 @@ func TestScalarInvert(t *testing.T) {
return check.Equal(scOne) == 1 && isReduced(xInv.Bytes())
}
if err := quick.Check(invertWorks, quickCheckConfig32); err != nil {
if err := quick.Check(invertWorks, quickCheckConfig(32)); err != nil {
t.Error(err)
}
@ -144,7 +144,7 @@ func TestMultiScalarMultMatchesBaseMult(t *testing.T) {
return p.Equal(&check) == 1
}
if err := quick.Check(multiScalarMultMatchesBaseMult, quickCheckConfig32); err != nil {
if err := quick.Check(multiScalarMultMatchesBaseMult, quickCheckConfig(32)); err != nil {
t.Error(err)
}
}
@ -164,7 +164,7 @@ func TestVarTimeMultiScalarMultMatchesBaseMult(t *testing.T) {
return p.Equal(&check) == 1
}
if err := quick.Check(varTimeMultiScalarMultMatchesBaseMult, quickCheckConfig32); err != nil {
if err := quick.Check(varTimeMultiScalarMultMatchesBaseMult, quickCheckConfig(32)); err != nil {
t.Error(err)
}
}

View file

@ -1,6 +1,6 @@
module asm
module std/crypto/internal/edwards25519/field/_asm
go 1.18
go 1.20
require (
filippo.io/edwards25519 v0.0.0

View file

@ -129,9 +129,9 @@ func TestAliasing(t *testing.T) {
var err error
switch {
case tt.oneArgF != nil:
err = quick.Check(checkAliasingOneArg(tt.oneArgF), &quick.Config{MaxCountScale: 1 << 8})
err = quick.Check(checkAliasingOneArg(tt.oneArgF), quickCheckConfig(256))
case tt.twoArgsF != nil:
err = quick.Check(checkAliasingTwoArgs(tt.twoArgsF), &quick.Config{MaxCountScale: 1 << 8})
err = quick.Check(checkAliasingTwoArgs(tt.twoArgsF), quickCheckConfig(256))
}
if err != nil {
t.Errorf("%v: %v", tt.name, err)

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build arm64,gc,!purego
//go:build arm64 && gc && !purego
#include "textflag.h"

View file

@ -156,7 +156,7 @@ func feMulGeneric(v, a, b *Element) {
rr4 := r4.lo&maskLow51Bits + c3
// Now all coefficients fit into 64-bit registers but are still too large to
// be passed around as a Element. We therefore do one last carry chain,
// be passed around as an Element. We therefore do one last carry chain,
// where the carries will be small enough to fit in the wiggle room above 2⁵¹.
*v = Element{rr0, rr1, rr2, rr3, rr4}
v.carryPropagate()
@ -245,7 +245,7 @@ func feSquareGeneric(v, a *Element) {
v.carryPropagate()
}
// carryPropagate brings the limbs below 52 bits by applying the reduction
// carryPropagateGeneric brings the limbs below 52 bits by applying the reduction
// identity (a * 2²⁵⁵ + b = a * 19 + b) to the l4 carry.
func (v *Element) carryPropagateGeneric() *Element {
c0 := v.l0 >> 51

View file

@ -21,9 +21,15 @@ func (v Element) String() string {
return hex.EncodeToString(v.Bytes())
}
// quickCheckConfig1024 will make each quickcheck test run (1024 * -quickchecks)
// times. The default value of -quickchecks is 100.
var quickCheckConfig1024 = &quick.Config{MaxCountScale: 1 << 10}
// quickCheckConfig returns a quick.Config that scales the max count by the
// given factor if the -short flag is not set.
func quickCheckConfig(slowScale int) *quick.Config {
cfg := new(quick.Config)
if !testing.Short() {
cfg.MaxCountScale = float64(slowScale)
}
return cfg
}
func generateFieldElement(rand *mathrand.Rand) Element {
const maskLow52Bits = (1 << 52) - 1
@ -114,7 +120,7 @@ func TestMultiplyDistributesOverAdd(t *testing.T) {
return t1.Equal(t2) == 1 && isInBounds(t1) && isInBounds(t2)
}
if err := quick.Check(multiplyDistributesOverAdd, quickCheckConfig1024); err != nil {
if err := quick.Check(multiplyDistributesOverAdd, quickCheckConfig(1024)); err != nil {
t.Error(err)
}
}
@ -419,7 +425,7 @@ func TestMult32(t *testing.T) {
return t1.Equal(t2) == 1 && isInBounds(t1) && isInBounds(t2)
}
if err := quick.Check(mult32EquivalentToMul, quickCheckConfig1024); err != nil {
if err := quick.Check(mult32EquivalentToMul, quickCheckConfig(1024)); err != nil {
t.Error(err)
}
}
@ -498,7 +504,7 @@ func TestCarryPropagate(t *testing.T) {
return *t1 == *t2 && isInBounds(t2)
}
if err := quick.Check(asmLikeGeneric, quickCheckConfig1024); err != nil {
if err := quick.Check(asmLikeGeneric, quickCheckConfig(1024)); err != nil {
t.Error(err)
}
@ -522,7 +528,7 @@ func TestFeSquare(t *testing.T) {
return t1 == t2 && isInBounds(&t2)
}
if err := quick.Check(asmLikeGeneric, quickCheckConfig1024); err != nil {
if err := quick.Check(asmLikeGeneric, quickCheckConfig(1024)); err != nil {
t.Error(err)
}
}
@ -546,7 +552,7 @@ func TestFeMul(t *testing.T) {
b1 == b2 && isInBounds(&b2)
}
if err := quick.Check(asmLikeGeneric, quickCheckConfig1024); err != nil {
if err := quick.Check(asmLikeGeneric, quickCheckConfig(1024)); err != nil {
t.Error(err)
}
}

4
go.mod
View file

@ -1,3 +1,3 @@
module filippo.io/edwards25519
module git.gammaspectra.live/P2Pool/edwards25519
go 1.17
go 1.20

View file

@ -104,7 +104,7 @@ func (s *Scalar) Set(x *Scalar) *Scalar {
// If x is not of the right length, SetUniformBytes returns nil and an error,
// and the receiver is unchanged.
//
// SetUniformBytes can be used to set s to an uniformly distributed value given
// SetUniformBytes can be used to set s to a uniformly distributed value given
// 64 uniformly distributed random bytes.
func (s *Scalar) SetUniformBytes(x []byte) (*Scalar, error) {
if len(x) != 64 {

View file

@ -103,7 +103,7 @@ func TestScalarAliasing(t *testing.T) {
}, v, x, y)
},
} {
err := quick.Check(f, &quick.Config{MaxCountScale: 1 << 5})
err := quick.Check(f, quickCheckConfig(32))
if err != nil {
t.Errorf("%v: %v", name, err)
}

View file

@ -41,7 +41,7 @@ package edwards25519
import "math/bits"
type fiatScalarUint1 uint64 // We use uint64 instead of a more narrow type for performance reasons; see https://github.com/mit-plv/fiat-crypto/pull/1006#issuecomment-892625927
type fiatScalarInt1 int64 // We use uint64 instead of a more narrow type for performance reasons; see https://github.com/mit-plv/fiat-crypto/pull/1006#issuecomment-892625927
type fiatScalarInt1 int64 // We use uint64 instead of a more narrow type for performance reasons; see https://github.com/mit-plv/fiat-crypto/pull/1006#issuecomment-892625927
// The type fiatScalarMontgomeryDomainFieldElement is a field element in the Montgomery domain.
//
@ -56,14 +56,18 @@ type fiatScalarNonMontgomeryDomainFieldElement [4]uint64
// fiatScalarCmovznzU64 is a single-word conditional move.
//
// Postconditions:
// out1 = (if arg1 = 0 then arg2 else arg3)
//
// out1 = (if arg1 = 0 then arg2 else arg3)
//
// Input Bounds:
// arg1: [0x0 ~> 0x1]
// arg2: [0x0 ~> 0xffffffffffffffff]
// arg3: [0x0 ~> 0xffffffffffffffff]
//
// arg1: [0x0 ~> 0x1]
// arg2: [0x0 ~> 0xffffffffffffffff]
// arg3: [0x0 ~> 0xffffffffffffffff]
//
// Output Bounds:
// out1: [0x0 ~> 0xffffffffffffffff]
//
// out1: [0x0 ~> 0xffffffffffffffff]
func fiatScalarCmovznzU64(out1 *uint64, arg1 fiatScalarUint1, arg2 uint64, arg3 uint64) {
x1 := (uint64(arg1) * 0xffffffffffffffff)
x2 := ((x1 & arg3) | ((^x1) & arg2))
@ -73,12 +77,14 @@ func fiatScalarCmovznzU64(out1 *uint64, arg1 fiatScalarUint1, arg2 uint64, arg3
// fiatScalarMul multiplies two field elements in the Montgomery domain.
//
// Preconditions:
// 0 ≤ eval arg1 < m
// 0 ≤ eval arg2 < m
// Postconditions:
// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m
// 0 ≤ eval out1 < m
//
// 0 ≤ eval arg1 < m
// 0 ≤ eval arg2 < m
//
// Postconditions:
//
// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) * eval (from_montgomery arg2)) mod m
// 0 ≤ eval out1 < m
func fiatScalarMul(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement, arg2 *fiatScalarMontgomeryDomainFieldElement) {
x1 := arg1[1]
x2 := arg1[2]
@ -367,12 +373,14 @@ func fiatScalarMul(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScala
// fiatScalarAdd adds two field elements in the Montgomery domain.
//
// Preconditions:
// 0 ≤ eval arg1 < m
// 0 ≤ eval arg2 < m
// Postconditions:
// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m
// 0 ≤ eval out1 < m
//
// 0 ≤ eval arg1 < m
// 0 ≤ eval arg2 < m
//
// Postconditions:
//
// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) + eval (from_montgomery arg2)) mod m
// 0 ≤ eval out1 < m
func fiatScalarAdd(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement, arg2 *fiatScalarMontgomeryDomainFieldElement) {
var x1 uint64
var x2 uint64
@ -417,12 +425,14 @@ func fiatScalarAdd(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScala
// fiatScalarSub subtracts two field elements in the Montgomery domain.
//
// Preconditions:
// 0 ≤ eval arg1 < m
// 0 ≤ eval arg2 < m
// Postconditions:
// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m
// 0 ≤ eval out1 < m
//
// 0 ≤ eval arg1 < m
// 0 ≤ eval arg2 < m
//
// Postconditions:
//
// eval (from_montgomery out1) mod m = (eval (from_montgomery arg1) - eval (from_montgomery arg2)) mod m
// 0 ≤ eval out1 < m
func fiatScalarSub(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement, arg2 *fiatScalarMontgomeryDomainFieldElement) {
var x1 uint64
var x2 uint64
@ -458,11 +468,13 @@ func fiatScalarSub(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScala
// fiatScalarOpp negates a field element in the Montgomery domain.
//
// Preconditions:
// 0 ≤ eval arg1 < m
// Postconditions:
// eval (from_montgomery out1) mod m = -eval (from_montgomery arg1) mod m
// 0 ≤ eval out1 < m
//
// 0 ≤ eval arg1 < m
//
// Postconditions:
//
// eval (from_montgomery out1) mod m = -eval (from_montgomery arg1) mod m
// 0 ≤ eval out1 < m
func fiatScalarOpp(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement) {
var x1 uint64
var x2 uint64
@ -498,14 +510,20 @@ func fiatScalarOpp(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScala
// fiatScalarNonzero outputs a single non-zero word if the input is non-zero and zero otherwise.
//
// Preconditions:
// 0 ≤ eval arg1 < m
//
// 0 ≤ eval arg1 < m
//
// Postconditions:
// out1 = 0 ↔ eval (from_montgomery arg1) mod m = 0
//
// out1 = 0 ↔ eval (from_montgomery arg1) mod m = 0
//
// Input Bounds:
// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
//
// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff]]
//
// Output Bounds:
// out1: [0x0 ~> 0xffffffffffffffff]
//
// out1: [0x0 ~> 0xffffffffffffffff]
func fiatScalarNonzero(out1 *uint64, arg1 *[4]uint64) {
x1 := (arg1[0] | (arg1[1] | (arg1[2] | arg1[3])))
*out1 = x1
@ -514,11 +532,13 @@ func fiatScalarNonzero(out1 *uint64, arg1 *[4]uint64) {
// fiatScalarFromMontgomery translates a field element out of the Montgomery domain.
//
// Preconditions:
// 0 ≤ eval arg1 < m
// Postconditions:
// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^4) mod m
// 0 ≤ eval out1 < m
//
// 0 ≤ eval arg1 < m
//
// Postconditions:
//
// eval out1 mod m = (eval arg1 * ((2^64)⁻¹ mod m)^4) mod m
// 0 ≤ eval out1 < m
func fiatScalarFromMontgomery(out1 *fiatScalarNonMontgomeryDomainFieldElement, arg1 *fiatScalarMontgomeryDomainFieldElement) {
x1 := arg1[0]
var x2 uint64
@ -668,11 +688,13 @@ func fiatScalarFromMontgomery(out1 *fiatScalarNonMontgomeryDomainFieldElement, a
// fiatScalarToMontgomery translates a field element into the Montgomery domain.
//
// Preconditions:
// 0 ≤ eval arg1 < m
// Postconditions:
// eval (from_montgomery out1) mod m = eval arg1 mod m
// 0 ≤ eval out1 < m
//
// 0 ≤ eval arg1 < m
//
// Postconditions:
//
// eval (from_montgomery out1) mod m = eval arg1 mod m
// 0 ≤ eval out1 < m
func fiatScalarToMontgomery(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *fiatScalarNonMontgomeryDomainFieldElement) {
x1 := arg1[1]
x2 := arg1[2]
@ -930,14 +952,20 @@ func fiatScalarToMontgomery(out1 *fiatScalarMontgomeryDomainFieldElement, arg1 *
// fiatScalarToBytes serializes a field element NOT in the Montgomery domain to bytes in little-endian order.
//
// Preconditions:
// 0 ≤ eval arg1 < m
//
// 0 ≤ eval arg1 < m
//
// Postconditions:
// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..31]
//
// out1 = map (λ x, ⌊((eval arg1 mod m) mod 2^(8 * (x + 1))) / 2^(8 * x)⌋) [0..31]
//
// Input Bounds:
// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1fffffffffffffff]]
//
// arg1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1fffffffffffffff]]
//
// Output Bounds:
// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1f]]
//
// out1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1f]]
func fiatScalarToBytes(out1 *[32]uint8, arg1 *[4]uint64) {
x1 := arg1[3]
x2 := arg1[2]
@ -1036,15 +1064,21 @@ func fiatScalarToBytes(out1 *[32]uint8, arg1 *[4]uint64) {
// fiatScalarFromBytes deserializes a field element NOT in the Montgomery domain from bytes in little-endian order.
//
// Preconditions:
// 0 ≤ bytes_eval arg1 < m
//
// 0 ≤ bytes_eval arg1 < m
//
// Postconditions:
// eval out1 mod m = bytes_eval arg1 mod m
// 0 ≤ eval out1 < m
//
// eval out1 mod m = bytes_eval arg1 mod m
// 0 ≤ eval out1 < m
//
// Input Bounds:
// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1f]]
//
// arg1: [[0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0xff], [0x0 ~> 0x1f]]
//
// Output Bounds:
// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1fffffffffffffff]]
//
// out1: [[0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0xffffffffffffffff], [0x0 ~> 0x1fffffffffffffff]]
func fiatScalarFromBytes(out1 *[4]uint64, arg1 *[32]uint8) {
x1 := (uint64(arg1[31]) << 56)
x2 := (uint64(arg1[30]) << 48)

View file

@ -14,6 +14,16 @@ import (
"testing/quick"
)
// quickCheckConfig returns a quick.Config that scales the max count by the
// given factor if the -short flag is not set.
func quickCheckConfig(slowScale int) *quick.Config {
cfg := new(quick.Config)
if !testing.Short() {
cfg.MaxCountScale = float64(slowScale)
}
return cfg
}
var scOneBytes = [32]byte{1}
var scOne, _ = new(Scalar).SetCanonicalBytes(scOneBytes[:])
var scMinusOne, _ = new(Scalar).SetCanonicalBytes(scalarMinusOneBytes[:])
@ -53,15 +63,11 @@ func (Scalar) Generate(rand *mathrand.Rand, size int) reflect.Value {
return reflect.ValueOf(val)
}
// quickCheckConfig1024 will make each quickcheck test run (1024 * -quickchecks)
// times. The default value of -quickchecks is 100.
var quickCheckConfig1024 = &quick.Config{MaxCountScale: 1 << 10}
func TestScalarGenerate(t *testing.T) {
f := func(sc Scalar) bool {
return isReduced(sc.Bytes())
}
if err := quick.Check(f, quickCheckConfig1024); err != nil {
if err := quick.Check(f, quickCheckConfig(1024)); err != nil {
t.Errorf("generated unreduced scalar: %v", err)
}
}
@ -76,7 +82,7 @@ func TestScalarSetCanonicalBytes(t *testing.T) {
repr := sc.Bytes()
return bytes.Equal(in[:], repr) && isReduced(repr)
}
if err := quick.Check(f1, quickCheckConfig1024); err != nil {
if err := quick.Check(f1, quickCheckConfig(1024)); err != nil {
t.Errorf("failed bytes->scalar->bytes round-trip: %v", err)
}
@ -86,7 +92,7 @@ func TestScalarSetCanonicalBytes(t *testing.T) {
}
return sc1 == sc2
}
if err := quick.Check(f2, quickCheckConfig1024); err != nil {
if err := quick.Check(f2, quickCheckConfig(1024)); err != nil {
t.Errorf("failed scalar->bytes->scalar round-trip: %v", err)
}
@ -115,7 +121,7 @@ func TestScalarSetUniformBytes(t *testing.T) {
inBig := bigIntFromLittleEndianBytes(in[:])
return inBig.Mod(inBig, mod).Cmp(scBig) == 0
}
if err := quick.Check(f, quickCheckConfig1024); err != nil {
if err := quick.Check(f, quickCheckConfig(1024)); err != nil {
t.Error(err)
}
}
@ -175,7 +181,7 @@ func TestScalarMultiplyDistributesOverAdd(t *testing.T) {
return t1 == t2 && isReduced(reprT1) && isReduced(reprT2)
}
if err := quick.Check(multiplyDistributesOverAdd, quickCheckConfig1024); err != nil {
if err := quick.Check(multiplyDistributesOverAdd, quickCheckConfig(1024)); err != nil {
t.Error(err)
}
}
@ -194,7 +200,7 @@ func TestScalarAddLikeSubNeg(t *testing.T) {
return t1 == t2 && isReduced(t1.Bytes())
}
if err := quick.Check(addLikeSubNeg, quickCheckConfig1024); err != nil {
if err := quick.Check(addLikeSubNeg, quickCheckConfig(1024)); err != nil {
t.Error(err)
}
}

View file

@ -10,10 +10,6 @@ import (
)
var (
// quickCheckConfig32 will make each quickcheck test run (32 * -quickchecks)
// times. The default value of -quickchecks is 100.
quickCheckConfig32 = &quick.Config{MaxCountScale: 1 << 5}
// a random scalar generated using dalek.
dalekScalar, _ = (&Scalar{}).SetCanonicalBytes([]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})
// the above, times the edwards25519 basepoint.
@ -83,7 +79,7 @@ func TestScalarMultDistributesOverAdd(t *testing.T) {
return check.Equal(&r) == 1
}
if err := quick.Check(scalarMultDistributesOverAdd, quickCheckConfig32); err != nil {
if err := quick.Check(scalarMultDistributesOverAdd, quickCheckConfig(32)); err != nil {
t.Error(err)
}
}
@ -105,7 +101,7 @@ func TestScalarMultNonIdentityPoint(t *testing.T) {
return p.Equal(&q) == 1
}
if err := quick.Check(scalarMultNonIdentityPoint, quickCheckConfig32); err != nil {
if err := quick.Check(scalarMultNonIdentityPoint, quickCheckConfig(32)); err != nil {
t.Error(err)
}
}
@ -149,7 +145,7 @@ func TestScalarMultMatchesBaseMult(t *testing.T) {
return p.Equal(&q) == 1
}
if err := quick.Check(scalarMultMatchesBaseMult, quickCheckConfig32); err != nil {
if err := quick.Check(scalarMultMatchesBaseMult, quickCheckConfig(32)); err != nil {
t.Error(err)
}
}
@ -177,7 +173,7 @@ func TestVarTimeDoubleBaseMultMatchesBaseMult(t *testing.T) {
return p.Equal(&check) == 1
}
if err := quick.Check(varTimeDoubleBaseMultMatchesBaseMult, quickCheckConfig32); err != nil {
if err := quick.Check(varTimeDoubleBaseMultMatchesBaseMult, quickCheckConfig(32)); err != nil {
t.Error(err)
}
}

View file

@ -38,7 +38,7 @@ func (v *projLookupTable) FromP3(q *Point) {
tmpP3 := Point{}
tmpP1xP1 := projP1xP1{}
for i := 0; i < 7; i++ {
// Compute (i+1)*Q as Q + i*Q and convert to a ProjCached
// Compute (i+1)*Q as Q + i*Q and convert to a projCached
// This is needlessly complicated because the API has explicit
// receivers instead of creating stack objects and relying on RVO
v.points[i+1].FromP3(tmpP3.fromP1xP1(tmpP1xP1.Add(q, &v.points[i])))
@ -53,7 +53,7 @@ func (v *affineLookupTable) FromP3(q *Point) {
tmpP3 := Point{}
tmpP1xP1 := projP1xP1{}
for i := 0; i < 7; i++ {
// Compute (i+1)*Q as Q + i*Q and convert to AffineCached
// 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])))
}
}

128
unsafe.go Normal file
View file

@ -0,0 +1,128 @@
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()
// Skip the trailing zeroes
i := 255
for ; i >= 0; i-- {
if naf[i] != 0 {
break
}
}
if i == -1 {
v.fromP2(tmp2)
return v
}
// 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 >= 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
func (v *Point) UnsafeVarTimeScalarBaseMult(x *Scalar) *Point {
basepointTable := basepointTable()
// Write x = sum(x_i * 16^i) so x*B = sum( B*x_i*16^i )
// as described in the Ed25519 paper
//
// Group even and odd coefficients
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
// + x_1*16^1*B + x_3*16^3*B + ... + x_63*16^63*B
// x*B = x_0*16^0*B + x_2*16^2*B + ... + x_62*16^62*B
// + 16*( x_1*16^0*B + x_3*16^2*B + ... + x_63*16^62*B)
//
// We use a lookup table for each i to get x_i*16^(2*i)*B
// and do four doublings to multiply by 16.
digits := x.signedRadix16()
multiple := &affineCached{}
tmp1 := &projP1xP1{}
tmp2 := &projP2{}
// Accumulate the odd components first
v.Set(NewIdentityPoint())
for i := 1; i < 64; i += 2 {
basepointTable[i/2].UnsafeVarTimeSelectInto(multiple, digits[i])
tmp1.AddAffine(v, multiple)
v.fromP1xP1(tmp1)
}
// Multiply by 16
tmp2.FromP3(v) // tmp2 = v in P2 coords
tmp1.Double(tmp2) // tmp1 = 2*v in P1xP1 coords
tmp2.FromP1xP1(tmp1) // tmp2 = 2*v in P2 coords
tmp1.Double(tmp2) // tmp1 = 4*v in P1xP1 coords
tmp2.FromP1xP1(tmp1) // tmp2 = 4*v in P2 coords
tmp1.Double(tmp2) // tmp1 = 8*v in P1xP1 coords
tmp2.FromP1xP1(tmp1) // tmp2 = 8*v in P2 coords
tmp1.Double(tmp2) // tmp1 = 16*v in P1xP1 coords
v.fromP1xP1(tmp1) // now v = 16*(odd components)
// Accumulate the even components
for i := 0; i < 64; i += 2 {
basepointTable[i/2].UnsafeVarTimeSelectInto(multiple, digits[i])
tmp1.AddAffine(v, multiple)
v.fromP1xP1(tmp1)
}
return v
}
// Set dest to x*Q, where -8 <= x <= 8, in variable time.
// Deprecated: Unsafe for private operations
func (v *affineLookupTable) UnsafeVarTimeSelectInto(dest *affineCached, x int8) {
if x == 0 {
dest.Zero()
return
}
// Compute xabs = |x|
xmask := x >> 7
xabs := uint8((x + xmask) ^ xmask)
// Set dest = j*Q
*dest = v.points[xabs-1]
// Now dest = |x|*Q, conditionally negate to get x*Q
if (xmask & 1) > 0 {
dest.YplusX, dest.YminusX = dest.YminusX, dest.YplusX
dest.T2d.Negate(&dest.T2d)
}
}

100
unsafe_test.go Normal file
View file

@ -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, quickCheckConfig(32)); 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, quickCheckConfig(32)); 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, quickCheckConfig(32)); 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, quickCheckConfig(32)); 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)
}
}