mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
crypto/ed25519: replace internal/edwards25519 with filippo.io/edwards25519
This change replaces the crypto/ed25519/internal/edwards25519 package with code from filippo.io/edwards25519, a significantly faster, safer, well tested (over 1600 lines of new tests, 99% test coverage), and better documented (600 lines of new comments) implementation. Some highlights: * an unsaturated 51-bit limb field implementation optimized for 64-bit architectures and math/bits.Mul64 intrinsics * more efficient variable time scalar multiplication using multi-width non-adjacent form with a larger lookup table for fixed-base * a safe math/big.Int-like API for the Scalar, Point, and field.Element types with fully abstracted reduction invariants * a test suite including a testing/quick fuzzer that explores edge case values that would be impossible to hit randomly, and systematic tests for arguments and receiver aliasing * point decoding rules that strictly match the original logic of crypto/ed25519/internal/edwards25519, to avoid consensus issues * AssemblyPolicy-compliant assembly cores for arm64 and amd64, the former under 20 lines, and the latter generated by a program based on github.com/mmcloughlin/avo that can be reviewed line-by-line against the generic implementation Intel(R) Core(TM) i5-7400 CPU @ 3.00GHz name old time/op new time/op delta KeyGeneration-4 59.5µs ± 1% 26.1µs ± 1% -56.20% (p=0.000 n=10+10) NewKeyFromSeed-4 59.3µs ± 1% 25.8µs ± 1% -56.48% (p=0.000 n=9+10) Signing-4 60.4µs ± 1% 31.4µs ± 1% -48.05% (p=0.000 n=10+10) Verification-4 169µs ± 1% 73µs ± 2% -56.55% (p=0.000 n=10+10) Apple M1 name old time/op new time/op delta KeyGeneration-8 35.1µs ± 0% 20.2µs ± 2% -42.46% (p=0.000 n=8+10) NewKeyFromSeed-8 35.1µs ± 0% 20.0µs ± 1% -42.93% (p=0.000 n=8+9) Signing-8 36.2µs ± 0% 25.6µs ± 1% -29.25% (p=0.000 n=8+9) Verification-8 96.1µs ± 0% 57.6µs ± 1% -40.14% (p=0.000 n=10+10) The code in this CL is a copy of the filippo.io/edwards25519 module at version v1.0.0-beta.3.0.20210405211453-c6be47d67779 with only the following functions removed as irrelevant to crypto/ed25519: - (*Point).BytesMontgomery() - (*Point).MultByCofactor() - (*Scalar).Invert() - (*Point).MultiScalarMult() - (*Point).VarTimeMultiScalarMult() This codebase took a long journey outside the standard library before making its way back here. Its oldest parts started as a faster field implementation rewrite by George Tankersley almost four years ago, eventually submitted as CL 71950 but never merged. That code was then merged into github.com/gtank/ristretto255, which also started as an internal/edwards25519 fork. There it was worked on by me, George, and Henry de Valence as a backend for our Go ristretto255 implementation. Finally, I extracted the edwards25519 code into a reusable package as filippo.io/edwards25519. Now, we're ready for the standard library to become the source of truth for this code again, while filippo.io/edwards25519 will become a re-packaged and extended version for external use, since we don't want to expose unsafe curve operations in x/crypto or the standard library. Submitted under the Google CLA on behalf of: - Henry de Valence https://github.com/gtank/ristretto255/issues/34 - George Tankersley https://golang.org/cl/71950 https://github.com/gtank/ristretto255-private/issues/28 - Luke Champine https://github.com/FiloSottile/edwards25519/pull/7 - Adrian Hamelink https://github.com/FiloSottile/edwards25519/pull/12 Changes 32506b5 and 18c803c are trivial and don't require a CLA. The full history of this code since diverging from internal/edwards25519 is available at https://github.com/FiloSottile/edwards25519, and summarized below. + c6be47d - edwards25519: update TestScalarSetBytesWithClamping <Filippo Valsorda> + c882e8e - edwards25519: rewrite amd64 assembly with avo <Filippo Valsorda> + 8eb02eb - edwards25519: refactor feMulGeneric and feSquareGeneric <Filippo Valsorda> + 8afd860 - edwards25519: remove Go 1.12 compatibility hack <Filippo Valsorda> + 1765c13 - edwards25519: don't clobber BP in amd64 assembly <Filippo Valsorda> + b73a7c8 - edwards25519: fix ScalarMult when receiver is not the identity (FiloSottile/edwards25519#12) <Adrian Hamelink> + 32a46d7 - edwards25519: document why this can't implement X25519 <Filippo Valsorda> + c547797 - edwards25519: make SqrtRatio slightly more efficient <Filippo Valsorda> + 700f4f4 - edwards25519: panic if an uninitialized Point is used <Filippo Valsorda> + d791cf8 - edwards25519: use testing.AllocsPerRun for TestAllocations <Filippo Valsorda> + 8cc8037 - edwards25519: smooth a couple test coverage rough edges <Filippo Valsorda> + 9063a14 - edwards25519: test that operations cause zero heap allocations <Filippo Valsorda> + 6944ac7 - edwards25519: relax the limb schedule slightly <Filippo Valsorda> + 21ebdac - edwards25519: rewrite carryPropagate in arm64 assembly <Filippo Valsorda> + a260082 - edwards25519: merge carryPropagate[12] <Filippo Valsorda> + dbe1792 - edwards25519: add TestScalarSetBytesWithClamping <Filippo Valsorda> + c1fe95a - edwards25519: add MultByCofactor <Filippo Valsorda> + 132d95c - edwards25519: sprinkle on-curve checks around tests <Filippo Valsorda> + ffb3e31 - edwards25519: specify the behavior of Invert(0) and I.BytesMontgomery() <Filippo Valsorda> + 9e6a931 - edwards25519: add (*Scalar).MultiplyAdd <lukechampine> + 3b045f3 - edwards25519: outline (*Point).Bytes (FiloSottile/edwards25519#6) <Luke Champine> + ec6f8a6 - edwards25519: make (*Scalar).SetCanonicalBytes return the receiver <Filippo Valsorda> + 77d7b31 - edwards25519: add (*Point).BytesMontgomery <Filippo Valsorda> + 6e8d645 - edwards25519: implement (*Point).Bytes and (*Point).SetBytes <Filippo Valsorda> + 1c833da - edwards25519: clarify ScalarBaseMult docs <Filippo Valsorda> + 3a13cf1 - edwards25519: apply gc build tag <Filippo Valsorda> + 90c35a7 - edwards25519: hide FieldElement and (*Point).ExtendedCoords <Filippo Valsorda> + 498fb1e - edwards25519: replace FillBytes with Bytes, again <Filippo Valsorda> + 9c7303a - edwards25519: remove (*Point).Identity and (*Point).Generator <Filippo Valsorda> + 2e52ce2 - edwards25519: drop unused (*Scalar).Zero <Filippo Valsorda> + 7c14a36 - edwards25519: rename FromBytes to SetBytes <Filippo Valsorda> + e3d0e45 - edwards25519: ensure only test files import math/big <Filippo Valsorda> + daa2507 - edwards25519: minor doc and string touch-ups <Filippo Valsorda> + e8698cd - edwards25519: implement (*Scalar).FromBytesWithClamping <Filippo Valsorda> + f28d75a - edwards25519: change constructors <Filippo Valsorda> + 36d8598 - edwards25519: test the invariant that Scalars are always reduced <Filippo Valsorda> + feed48c - edwards25519: cleanup the FieldElement API <Filippo Valsorda> + f6ee187 - edwards25519: make Point opaque <Filippo Valsorda> + 176388b - edwards25519: cleanup Scalar API to match ristretto255 <Filippo Valsorda> + c5c2e9e - edwards25519: rename ProjP3 to Point and unexport other point types <Filippo Valsorda> + 8542076 - edwards25519: add Scalar aliasing test <Filippo Valsorda> + 1a86a9c - edwards25519: make Scalar opaque <Filippo Valsorda> + 07a7683 - edwards25519: hide some more exposed symbols <Filippo Valsorda> + d3569cb - all: flatten the package and make FieldElement opaque <Filippo Valsorda> + 6f5f582 - all: expose edwards25519, base, and scalar packages <Filippo Valsorda> + 7ab4a68 - all: ensure compatibility with older Go versions <Filippo Valsorda> + e9b8baa - internal/radix51: implement (*FieldElement).Mul32 <Filippo Valsorda> + eac4de5 - internal/radix51: restructure according to golang.org/wiki/TargetSpecific <Filippo Valsorda> + 32506b5 - internal/radix51: fix !amd64 build (lightReduce -> carryPropagate) (gtank/ristretto255#29) <Sunny Aggarwal> + d64d989 - internal/scalar: fix FromUniformBytes <Filippo Valsorda> + 044bb44 - internal/scalar: address review comments <Filippo Valsorda> + 7dba54f - all: apply suggestions from code review <Filippo Valsorda> + 94bd1d9 - ristretto255: expose scalar multiplication APIs <Filippo Valsorda> + 5bd5476 - internal/edwards25519: fix shadowing of B in TestAddSubNegOnBasePoint <Filippo Valsorda> + 66bf647 - internal/scalar: replace FromBytes/IsCanonical with FromUniformBytes/FromCanonicalBytes <Filippo Valsorda> + 024f3f7 - internal/edwards25519,internal/scalar: apply some Go style touches <Filippo Valsorda> + 5e0c5c6 - internal/scalar: add scalar inversion <Henry de Valence> + 74fd625 - internal/ed25519: rearrange VartimeDoubleBaseMul args <Henry de Valence> + 81ae7ea - internal/ed25519: add benchmarks for scalar mul <Henry de Valence> + 9f1f939 - internal/ed25519: add variable-time multiscalar mul <Henry de Valence> + 7a96974 - internal/ed25519: add vartime double-base scmul <Henry de Valence> + 2bc256c - internal/ed25519: add precomputed NAF table for basepoint <Henry de Valence> + a0f0b96 - internal/ed25519: lower quickcheck size for point ops <Henry de Valence> + 2f385a1 - internal/ed25519: implement MultiscalarMul <Henry de Valence> + 8ae211b - internal/ed25519: implement BasepointMul <Henry de Valence> + 7b4858d - internal/ed25519: extract common test variables <Henry de Valence> + 16e7c48 - internal/ed25519: add a basepoint multiple table. <Henry de Valence> + 988e521 - internal/ed25519: add constant-time variable-base scmul. <Henry de Valence> + b695f6b - internal/ed25519: move basepoint constant & correct it <Henry de Valence> + ddd014e - internal/scalar: fix high bit check <Henry de Valence> + c88ea89 - internal/scalar: make casts clearer <Henry de Valence> + b75f989 - internal/scalar: add invariant checks on Scalar digits <Henry de Valence> + 36216ca - internal/scalar: use one scMulAdd for Sub <Henry de Valence> + 8bf40f3 - internal/scalar: fix constant-time signed radix 16 implementation <Henry de Valence> + e6d9ef6 - Update internal/radix51/fe_test.go <Filippo Valsorda> + 3aa63de - Update internal/radix51/fe_test.go <Filippo Valsorda> + 3e66ff0 - Update internal/radix51/fe_test.go <Filippo Valsorda> + 94e6c15 - internal/ed25519: add TODO note and doc ref <Henry de Valence> + 3647548 - internal/ed25519: rename twoD to D2 <Henry de Valence> + 1cf853c - internal/ed25519: add lookup tables for scalar mul. <Henry de Valence> + 3af304a - internal/radix51: add a conditional swap <Henry de Valence> + 4673217 - ristretto255: use multi-model arithmetic <Henry de Valence> + cca757a - internal/ed25519: remove single-model code <Henry de Valence> + d26e77b - internal/ed25519: add addition for Edwards points <Henry de Valence> + e0fbb35 - internal/ed25519: use twoD <Henry de Valence> + fd9b37b - internal/ed25519: add tests for multi-model point types. <Henry de Valence> + dacabb0 - internal/ed25519: add multi-model point types. <Henry de Valence> + dddc72e - internal/scalar: add constant-time signed radix 16 <Henry de Valence> + 92cdb35 - internal/scalar: add non-adjacent form <Henry de Valence> + d147963 - internal/scalar: don't zero memory that is about to be copied over <George Tankersley> + 8da186c - internal/scalar: add scalar field implementation <George Tankersley> + f38e583 - internal/radix51: add a "weird" testing/quick generation strategy <Filippo Valsorda> + 6454f61 - Move comment inside function <Henry de Valence> + 1983365 - implement Add, Sub, Neg for ed25519 and ristretto255 points. <Henry de Valence> + 9f25562 - internal/group: rename to internal/edwards25519 <Filippo Valsorda> + 48e66d3 - internal/group: restore ScalarMult code <Filippo Valsorda> + 0078d66 - internal/radix51: rename lightReduce to carryPropagate and touch up docs <Filippo Valsorda> + 05f4107 - internal/radix51: add benchmarks <Filippo Valsorda> + fd36334 - internal/radix51: test that operations don't exceed bounds <Filippo Valsorda> + 703421d - internal/radix51: make Generate produce random light-reduced elements <Filippo Valsorda> + f8d8297 - internal/radix51: simplify lightReduce <Filippo Valsorda> + 413120f - internal/radix51: minor tests cleanup <Filippo Valsorda> + abc8c5a - internal/radix51: make reduction an invariant and unexport Reduce <Filippo Valsorda> + 4fd198d - internal/radix51: actually apply go:noescape <Filippo Valsorda> + 18c803c - all: fix typos <Dimitris Apostolou> + bbfe059 - internal/radix51: test field encoding roundtrip with fixed vectors <George Tankersley> + c428b18 - internal/radix51: rename AppendBytes to Bytes <Filippo Valsorda> + c59bc1a - internal/radix51: rewrite FromBytes and AppendBytes with encoding/binary <Filippo Valsorda> + 57c0cd5 - internal/radix51: add docs and some light readability refactors <Filippo Valsorda> + cb1b734 - internal/radix51: remove unused (and a bit broken) SetInt <Filippo Valsorda> + beb8abd - internal/radix51: refactor ToBig and FromBig <Filippo Valsorda> + 87c0a53 - internal/radix51: replace ToBytes with AppendBytes <Filippo Valsorda> + b7e1e45 - internal/radix51: fix aliasing bug in CondNeg (gtank/ristretto255#21) <George Tankersley> + ed3748d - internal/radix51: actually, uhm, check the result of TestAliasing <Filippo Valsorda> + ec0e293 - radix51: change API of FromBytes and ToBytes to use slices <George Tankersley> + 29f6815 - internal/radix51: test all combinations of argument and receiver aliasing <Filippo Valsorda> + cd53d90 - internal/radix51: add property-based tests that multiplication distributes over addition <Henry de Valence> + c3bc45f - radix51: use go1.12 intrinsics for 128-bit multiplications <George Tankersley> + 7e7043e - internal/radix51: define a mask64Bits constant <Filippo Valsorda> + 4fdd06d - internal/group: set Z to 1, not 0 in FromAffine <Filippo Valsorda> + ffa7be7 - internal/group: fix typo <Filippo Valsorda> + 1f452ac - internal/group: derive twoD from D <Filippo Valsorda> + 2424c78 - internal/radix51: add MinusOne <Filippo Valsorda> + 76978fc - internal/group: make conversion APIs caller-allocated <Filippo Valsorda> + d17d202 - internal/group: rewrite DoubleZ1 because stack is cheaper than mental state <Filippo Valsorda> + 72b97c1 - internal: make all APIs chainable <Filippo Valsorda> + 993d979 - internal/radix51: make all APIs not consider the receiver an input <Filippo Valsorda> + b2a1d7d - all: refactor field API to be methods based <Filippo Valsorda> + cdf9b90 - internal/radix51: add constant time field operations <Filippo Valsorda> + e490a48 - internal/radix51: remove FeEqual <Filippo Valsorda> + 2de114c - internal/radix51: remove FeCSwap <Filippo Valsorda> + 08b80c1 - make things more generally presentable <George Tankersley> + 2178536 - Cache the field representation of d <George Tankersley> + 4135059 - Remove 32-bit code and update license. <George Tankersley> + 5d95cb3 - Use Bits() for FeToBig. <George Tankersley> + 146e33c - Implement ScalarMult using Montgomery pattern and dedicated extended-coordinates doubling. This will be slow. <George Tankersley> + 12a673a - use faster FeFromBig & a horrible assortment of other random changes <George Tankersley> + 901f40c - group logic WIP <George Tankersley> + a9c89cd - add equality for field elements <George Tankersley> + 214873b - Add radix51 FieldElement implementation <George Tankersley> + 8fd5cae - Implement an elliptic.Curve for ed25519 <George Tankersley> Change-Id: Ifbcdd13e8b6304f9906c0ef2b73f1fdc493a7dfa Co-authored-by: George Tankersley <george.tankersley@gmail.com> Co-authored-by: Henry de Valence <hdevalence@hdevalence.ca> Reviewed-on: https://go-review.googlesource.com/c/go/+/276272 Run-TryBot: Filippo Valsorda <filippo@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Trust: Filippo Valsorda <filippo@golang.org> Trust: Katie Hockman <katie@golang.org> Reviewed-by: Katie Hockman <katie@golang.org>
This commit is contained in:
parent
543e098320
commit
b0c49ae9f5
31 changed files with 5009 additions and 3267 deletions
|
|
@ -301,6 +301,13 @@ func (w *walker) walk(dir string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip submodules, which might not be vendored
|
||||||
|
for _, f := range files {
|
||||||
|
if f.Name() == "go.mod" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// apply pkgh to the files in directory dir
|
// apply pkgh to the files in directory dir
|
||||||
// but ignore files directly under $GOROOT/src (might be temporary test files).
|
// but ignore files directly under $GOROOT/src (might be temporary test files).
|
||||||
if dir != filepath.Join(runtime.GOROOT(), "src") {
|
if dir != filepath.Join(runtime.GOROOT(), "src") {
|
||||||
|
|
|
||||||
|
|
@ -422,6 +422,12 @@ func findGorootModules(t *testing.T) []gorootModule {
|
||||||
// running time of this test anyway.)
|
// running time of this test anyway.)
|
||||||
return filepath.SkipDir
|
return filepath.SkipDir
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(info.Name(), "_") || strings.HasPrefix(info.Name(), ".") {
|
||||||
|
// _ and . prefixed directories can be used for internal modules
|
||||||
|
// without a vendor directory that don't contribute to the build
|
||||||
|
// but might be used for example as code generators.
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
if info.IsDir() || info.Name() != "go.mod" {
|
if info.IsDir() || info.Name() != "go.mod" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,6 @@
|
||||||
// 8032 private key as the “seed”.
|
// 8032 private key as the “seed”.
|
||||||
package ed25519
|
package ed25519
|
||||||
|
|
||||||
// This code is a port of the public domain, “ref10” implementation of ed25519
|
|
||||||
// from SUPERCOP.
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
|
|
@ -128,20 +125,14 @@ func newKeyFromSeed(privateKey, seed []byte) {
|
||||||
panic("ed25519: bad seed length: " + strconv.Itoa(l))
|
panic("ed25519: bad seed length: " + strconv.Itoa(l))
|
||||||
}
|
}
|
||||||
|
|
||||||
digest := sha512.Sum512(seed)
|
h := sha512.Sum512(seed)
|
||||||
digest[0] &= 248
|
s := edwards25519.NewScalar().SetBytesWithClamping(h[:32])
|
||||||
digest[31] &= 127
|
A := (&edwards25519.Point{}).ScalarBaseMult(s)
|
||||||
digest[31] |= 64
|
|
||||||
|
|
||||||
var A edwards25519.ExtendedGroupElement
|
publicKey := A.Bytes()
|
||||||
var hBytes [32]byte
|
|
||||||
copy(hBytes[:], digest[:])
|
|
||||||
edwards25519.GeScalarMultBase(&A, &hBytes)
|
|
||||||
var publicKeyBytes [32]byte
|
|
||||||
A.ToBytes(&publicKeyBytes)
|
|
||||||
|
|
||||||
copy(privateKey, seed)
|
copy(privateKey, seed)
|
||||||
copy(privateKey[32:], publicKeyBytes[:])
|
copy(privateKey[32:], publicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign signs the message with privateKey and returns a signature. It will
|
// Sign signs the message with privateKey and returns a signature. It will
|
||||||
|
|
@ -158,44 +149,33 @@ func sign(signature, privateKey, message []byte) {
|
||||||
if l := len(privateKey); l != PrivateKeySize {
|
if l := len(privateKey); l != PrivateKeySize {
|
||||||
panic("ed25519: bad private key length: " + strconv.Itoa(l))
|
panic("ed25519: bad private key length: " + strconv.Itoa(l))
|
||||||
}
|
}
|
||||||
|
seed, publicKey := privateKey[:SeedSize], privateKey[SeedSize:]
|
||||||
|
|
||||||
h := sha512.New()
|
h := sha512.Sum512(seed)
|
||||||
h.Write(privateKey[:32])
|
s := edwards25519.NewScalar().SetBytesWithClamping(h[:32])
|
||||||
|
prefix := h[32:]
|
||||||
|
|
||||||
var digest1, messageDigest, hramDigest [64]byte
|
mh := sha512.New()
|
||||||
var expandedSecretKey [32]byte
|
mh.Write(prefix)
|
||||||
h.Sum(digest1[:0])
|
mh.Write(message)
|
||||||
copy(expandedSecretKey[:], digest1[:])
|
messageDigest := make([]byte, 0, sha512.Size)
|
||||||
expandedSecretKey[0] &= 248
|
messageDigest = mh.Sum(messageDigest)
|
||||||
expandedSecretKey[31] &= 63
|
r := edwards25519.NewScalar().SetUniformBytes(messageDigest)
|
||||||
expandedSecretKey[31] |= 64
|
|
||||||
|
|
||||||
h.Reset()
|
R := (&edwards25519.Point{}).ScalarBaseMult(r)
|
||||||
h.Write(digest1[32:])
|
|
||||||
h.Write(message)
|
|
||||||
h.Sum(messageDigest[:0])
|
|
||||||
|
|
||||||
var messageDigestReduced [32]byte
|
kh := sha512.New()
|
||||||
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
|
kh.Write(R.Bytes())
|
||||||
var R edwards25519.ExtendedGroupElement
|
kh.Write(publicKey)
|
||||||
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
|
kh.Write(message)
|
||||||
|
hramDigest := make([]byte, 0, sha512.Size)
|
||||||
|
hramDigest = kh.Sum(hramDigest)
|
||||||
|
k := edwards25519.NewScalar().SetUniformBytes(hramDigest)
|
||||||
|
|
||||||
var encodedR [32]byte
|
S := edwards25519.NewScalar().MultiplyAdd(k, s, r)
|
||||||
R.ToBytes(&encodedR)
|
|
||||||
|
|
||||||
h.Reset()
|
copy(signature[:32], R.Bytes())
|
||||||
h.Write(encodedR[:])
|
copy(signature[32:], S.Bytes())
|
||||||
h.Write(privateKey[32:])
|
|
||||||
h.Write(message)
|
|
||||||
h.Sum(hramDigest[:0])
|
|
||||||
var hramDigestReduced [32]byte
|
|
||||||
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
|
|
||||||
|
|
||||||
var s [32]byte
|
|
||||||
edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
|
|
||||||
|
|
||||||
copy(signature[:], encodedR[:])
|
|
||||||
copy(signature[32:], s[:])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify reports whether sig is a valid signature of message by publicKey. It
|
// Verify reports whether sig is a valid signature of message by publicKey. It
|
||||||
|
|
@ -209,38 +189,27 @@ func Verify(publicKey PublicKey, message, sig []byte) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var A edwards25519.ExtendedGroupElement
|
A, err := (&edwards25519.Point{}).SetBytes(publicKey)
|
||||||
var publicKeyBytes [32]byte
|
if err != nil {
|
||||||
copy(publicKeyBytes[:], publicKey)
|
|
||||||
if !A.FromBytes(&publicKeyBytes) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
edwards25519.FeNeg(&A.X, &A.X)
|
|
||||||
edwards25519.FeNeg(&A.T, &A.T)
|
|
||||||
|
|
||||||
h := sha512.New()
|
|
||||||
h.Write(sig[:32])
|
|
||||||
h.Write(publicKey[:])
|
|
||||||
h.Write(message)
|
|
||||||
var digest [64]byte
|
|
||||||
h.Sum(digest[:0])
|
|
||||||
|
|
||||||
var hReduced [32]byte
|
|
||||||
edwards25519.ScReduce(&hReduced, &digest)
|
|
||||||
|
|
||||||
var R edwards25519.ProjectiveGroupElement
|
|
||||||
var s [32]byte
|
|
||||||
copy(s[:], sig[32:])
|
|
||||||
|
|
||||||
// https://tools.ietf.org/html/rfc8032#section-5.1.7 requires that s be in
|
|
||||||
// the range [0, order) in order to prevent signature malleability.
|
|
||||||
if !edwards25519.ScMinimal(&s) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &s)
|
kh := sha512.New()
|
||||||
|
kh.Write(sig[:32])
|
||||||
|
kh.Write(publicKey)
|
||||||
|
kh.Write(message)
|
||||||
|
hramDigest := make([]byte, 0, sha512.Size)
|
||||||
|
hramDigest = kh.Sum(hramDigest)
|
||||||
|
k := edwards25519.NewScalar().SetUniformBytes(hramDigest)
|
||||||
|
|
||||||
var checkR [32]byte
|
S, err := edwards25519.NewScalar().SetCanonicalBytes(sig[32:])
|
||||||
R.ToBytes(&checkR)
|
if err != nil {
|
||||||
return bytes.Equal(sig[:32], checkR[:])
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// [S]B = R + [k]A --> [k](-A) + [S]B = R
|
||||||
|
minusA := (&edwards25519.Point{}).Negate(A)
|
||||||
|
R := (&edwards25519.Point{}).VarTimeDoubleScalarBaseMult(k, minusA, S)
|
||||||
|
|
||||||
|
return bytes.Equal(sig[:32], R.Bytes())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/gzip"
|
"compress/gzip"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ed25519/internal/edwards25519"
|
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"os"
|
"os"
|
||||||
|
|
@ -26,24 +25,6 @@ func (zeroReader) Read(buf []byte) (int, error) {
|
||||||
return len(buf), nil
|
return len(buf), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalMarshal(t *testing.T) {
|
|
||||||
pub, _, _ := GenerateKey(rand.Reader)
|
|
||||||
|
|
||||||
var A edwards25519.ExtendedGroupElement
|
|
||||||
var pubBytes [32]byte
|
|
||||||
copy(pubBytes[:], pub)
|
|
||||||
if !A.FromBytes(&pubBytes) {
|
|
||||||
t.Fatalf("ExtendedGroupElement.FromBytes failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
var pub2 [32]byte
|
|
||||||
A.ToBytes(&pub2)
|
|
||||||
|
|
||||||
if pubBytes != pub2 {
|
|
||||||
t.Errorf("FromBytes(%v)->ToBytes does not round-trip, got %x\n", pubBytes, pub2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSignVerify(t *testing.T) {
|
func TestSignVerify(t *testing.T) {
|
||||||
var zero zeroReader
|
var zero zeroReader
|
||||||
public, private, _ := GenerateKey(zero)
|
public, private, _ := GenerateKey(zero)
|
||||||
|
|
@ -204,6 +185,21 @@ func TestMalleability(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAllocations(t *testing.T) {
|
||||||
|
if allocs := testing.AllocsPerRun(100, func() {
|
||||||
|
seed := make([]byte, SeedSize)
|
||||||
|
message := []byte("Hello, world!")
|
||||||
|
priv := NewKeyFromSeed(seed)
|
||||||
|
pub := priv.Public().(PublicKey)
|
||||||
|
signature := Sign(priv, message)
|
||||||
|
if !Verify(pub, message, signature) {
|
||||||
|
t.Fatal("signature didn't verify")
|
||||||
|
}
|
||||||
|
}); allocs > 0 {
|
||||||
|
t.Errorf("expected zero allocations, got %0.1v", allocs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkKeyGeneration(b *testing.B) {
|
func BenchmarkKeyGeneration(b *testing.B) {
|
||||||
var zero zeroReader
|
var zero zeroReader
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
|
@ -215,7 +211,6 @@ func BenchmarkKeyGeneration(b *testing.B) {
|
||||||
|
|
||||||
func BenchmarkNewKeyFromSeed(b *testing.B) {
|
func BenchmarkNewKeyFromSeed(b *testing.B) {
|
||||||
seed := make([]byte, SeedSize)
|
seed := make([]byte, SeedSize)
|
||||||
b.ReportAllocs()
|
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
_ = NewKeyFromSeed(seed)
|
_ = NewKeyFromSeed(seed)
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +223,6 @@ func BenchmarkSigning(b *testing.B) {
|
||||||
b.Fatal(err)
|
b.Fatal(err)
|
||||||
}
|
}
|
||||||
message := []byte("Hello, world!")
|
message := []byte("Hello, world!")
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
Sign(priv, message)
|
Sign(priv, message)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
22
src/crypto/ed25519/internal/edwards25519/doc.go
Normal file
22
src/crypto/ed25519/internal/edwards25519/doc.go
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package edwards25519 implements group logic for the twisted Edwards curve
|
||||||
|
//
|
||||||
|
// -x^2 + y^2 = 1 + -(121665/121666)*x^2*y^2
|
||||||
|
//
|
||||||
|
// This is better known as the Edwards curve equivalent to Curve25519, and is
|
||||||
|
// the curve used by the Ed25519 signature scheme.
|
||||||
|
//
|
||||||
|
// Most users don't need this package, and should instead use crypto/ed25519 for
|
||||||
|
// signatures, golang.org/x/crypto/curve25519 for Diffie-Hellman, or
|
||||||
|
// github.com/gtank/ristretto255 for prime order group logic.
|
||||||
|
//
|
||||||
|
// However, developers who do need to interact with low-level edwards25519
|
||||||
|
// operations can use filippo.io/edwards25519, an extended version of this
|
||||||
|
// package repackaged as an importable module.
|
||||||
|
//
|
||||||
|
// (Note that filippo.io/edwards25519 and github.com/gtank/ristretto255 are not
|
||||||
|
// maintained by the Go team and are not covered by the Go 1 Compatibility Promise.)
|
||||||
|
package edwards25519
|
||||||
File diff suppressed because it is too large
Load diff
299
src/crypto/ed25519/internal/edwards25519/edwards25519_test.go
Normal file
299
src/crypto/ed25519/internal/edwards25519/edwards25519_test.go
Normal file
|
|
@ -0,0 +1,299 @@
|
||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ed25519/internal/edwards25519/field"
|
||||||
|
"encoding/hex"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var B = NewGeneratorPoint()
|
||||||
|
var I = NewIdentityPoint()
|
||||||
|
|
||||||
|
func checkOnCurve(t *testing.T, points ...*Point) {
|
||||||
|
t.Helper()
|
||||||
|
for i, p := range points {
|
||||||
|
var XX, YY, ZZ, ZZZZ field.Element
|
||||||
|
XX.Square(&p.x)
|
||||||
|
YY.Square(&p.y)
|
||||||
|
ZZ.Square(&p.z)
|
||||||
|
ZZZZ.Square(&ZZ)
|
||||||
|
// -x² + y² = 1 + dx²y²
|
||||||
|
// -(X/Z)² + (Y/Z)² = 1 + d(X/Z)²(Y/Z)²
|
||||||
|
// (-X² + Y²)/Z² = 1 + (dX²Y²)/Z⁴
|
||||||
|
// (-X² + Y²)*Z² = Z⁴ + dX²Y²
|
||||||
|
var lhs, rhs field.Element
|
||||||
|
lhs.Subtract(&YY, &XX).Multiply(&lhs, &ZZ)
|
||||||
|
rhs.Multiply(d, &XX).Multiply(&rhs, &YY).Add(&rhs, &ZZZZ)
|
||||||
|
if lhs.Equal(&rhs) != 1 {
|
||||||
|
t.Errorf("X, Y, and Z do not specify a point on the curve\nX = %v\nY = %v\nZ = %v", p.x, p.y, p.z)
|
||||||
|
}
|
||||||
|
// xy = T/Z
|
||||||
|
lhs.Multiply(&p.x, &p.y)
|
||||||
|
rhs.Multiply(&p.z, &p.t)
|
||||||
|
if lhs.Equal(&rhs) != 1 {
|
||||||
|
t.Errorf("point %d is not valid\nX = %v\nY = %v\nZ = %v", i, p.x, p.y, p.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerator(t *testing.T) {
|
||||||
|
// These are the coordinates of B from RFC 8032, Section 5.1, converted to
|
||||||
|
// little endian hex.
|
||||||
|
x := "1ad5258f602d56c9b2a7259560c72c695cdcd6fd31e2a4c0fe536ecdd3366921"
|
||||||
|
y := "5866666666666666666666666666666666666666666666666666666666666666"
|
||||||
|
if got := hex.EncodeToString(B.x.Bytes()); got != x {
|
||||||
|
t.Errorf("wrong B.x: got %s, expected %s", got, x)
|
||||||
|
}
|
||||||
|
if got := hex.EncodeToString(B.y.Bytes()); got != y {
|
||||||
|
t.Errorf("wrong B.y: got %s, expected %s", got, y)
|
||||||
|
}
|
||||||
|
if B.z.Equal(feOne) != 1 {
|
||||||
|
t.Errorf("wrong B.z: got %v, expected 1", B.z)
|
||||||
|
}
|
||||||
|
// Check that t is correct.
|
||||||
|
checkOnCurve(t, B)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddSubNegOnBasePoint(t *testing.T) {
|
||||||
|
checkLhs, checkRhs := &Point{}, &Point{}
|
||||||
|
|
||||||
|
checkLhs.Add(B, B)
|
||||||
|
tmpP2 := new(projP2).FromP3(B)
|
||||||
|
tmpP1xP1 := new(projP1xP1).Double(tmpP2)
|
||||||
|
checkRhs.fromP1xP1(tmpP1xP1)
|
||||||
|
if checkLhs.Equal(checkRhs) != 1 {
|
||||||
|
t.Error("B + B != [2]B")
|
||||||
|
}
|
||||||
|
checkOnCurve(t, checkLhs, checkRhs)
|
||||||
|
|
||||||
|
checkLhs.Subtract(B, B)
|
||||||
|
Bneg := new(Point).Negate(B)
|
||||||
|
checkRhs.Add(B, Bneg)
|
||||||
|
if checkLhs.Equal(checkRhs) != 1 {
|
||||||
|
t.Error("B - B != B + (-B)")
|
||||||
|
}
|
||||||
|
if I.Equal(checkLhs) != 1 {
|
||||||
|
t.Error("B - B != 0")
|
||||||
|
}
|
||||||
|
if I.Equal(checkRhs) != 1 {
|
||||||
|
t.Error("B + (-B) != 0")
|
||||||
|
}
|
||||||
|
checkOnCurve(t, checkLhs, checkRhs, Bneg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComparable(t *testing.T) {
|
||||||
|
if reflect.TypeOf(Point{}).Comparable() {
|
||||||
|
t.Error("Point is unexpectedly comparable")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidEncodings(t *testing.T) {
|
||||||
|
// An invalid point, that also happens to have y > p.
|
||||||
|
invalid := "efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"
|
||||||
|
p := NewGeneratorPoint()
|
||||||
|
if out, err := p.SetBytes(decodeHex(invalid)); err == nil {
|
||||||
|
t.Error("expected error for invalid point")
|
||||||
|
} else if out != nil {
|
||||||
|
t.Error("SetBytes did not return nil on an invalid encoding")
|
||||||
|
} else if p.Equal(B) != 1 {
|
||||||
|
t.Error("the Point was modified while decoding an invalid encoding")
|
||||||
|
}
|
||||||
|
checkOnCurve(t, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNonCanonicalPoints(t *testing.T) {
|
||||||
|
type test struct {
|
||||||
|
name string
|
||||||
|
encoding, canonical string
|
||||||
|
}
|
||||||
|
tests := []test{
|
||||||
|
// Points with x = 0 and the sign bit set. With x = 0 the curve equation
|
||||||
|
// gives y² = 1, so y = ±1. 1 has two valid encodings.
|
||||||
|
{
|
||||||
|
"y=1,sign-",
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+1,sign-",
|
||||||
|
"eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p-1,sign-",
|
||||||
|
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Non-canonical y encodings with values 2²⁵⁵-19 (p) to 2²⁵⁵-1 (p+18).
|
||||||
|
{
|
||||||
|
"y=p,sign+",
|
||||||
|
"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p,sign-",
|
||||||
|
"edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+1,sign+",
|
||||||
|
"eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
// "y=p+1,sign-" is already tested above.
|
||||||
|
// p+2 is not a valid y-coordinate.
|
||||||
|
{
|
||||||
|
"y=p+3,sign+",
|
||||||
|
"f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+3,sign-",
|
||||||
|
"f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"0300000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+4,sign+",
|
||||||
|
"f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+4,sign-",
|
||||||
|
"f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+5,sign+",
|
||||||
|
"f2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"0500000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+5,sign-",
|
||||||
|
"f2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"0500000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+6,sign+",
|
||||||
|
"f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"0600000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+6,sign-",
|
||||||
|
"f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"0600000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
},
|
||||||
|
// p+7 is not a valid y-coordinate.
|
||||||
|
// p+8 is not a valid y-coordinate.
|
||||||
|
{
|
||||||
|
"y=p+9,sign+",
|
||||||
|
"f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"0900000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+9,sign-",
|
||||||
|
"f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"0900000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+10,sign+",
|
||||||
|
"f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"0a00000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+10,sign-",
|
||||||
|
"f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"0a00000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
},
|
||||||
|
// p+11 is not a valid y-coordinate.
|
||||||
|
// p+12 is not a valid y-coordinate.
|
||||||
|
// p+13 is not a valid y-coordinate.
|
||||||
|
{
|
||||||
|
"y=p+14,sign+",
|
||||||
|
"fbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"0e00000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+14,sign-",
|
||||||
|
"fbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"0e00000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+15,sign+",
|
||||||
|
"fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"0f00000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+15,sign-",
|
||||||
|
"fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"0f00000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+16,sign+",
|
||||||
|
"fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"1000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+16,sign-",
|
||||||
|
"fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"1000000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
},
|
||||||
|
// p+17 is not a valid y-coordinate.
|
||||||
|
{
|
||||||
|
"y=p+18,sign+",
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
|
||||||
|
"1200000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"y=p+18,sign-",
|
||||||
|
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
|
||||||
|
"1200000000000000000000000000000000000000000000000000000000000080",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
p1, err := new(Point).SetBytes(decodeHex(tt.encoding))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error decoding non-canonical point: %v", err)
|
||||||
|
}
|
||||||
|
p2, err := new(Point).SetBytes(decodeHex(tt.canonical))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error decoding canonical point: %v", err)
|
||||||
|
}
|
||||||
|
if p1.Equal(p2) != 1 {
|
||||||
|
t.Errorf("equivalent points are not equal: %v, %v", p1, p2)
|
||||||
|
}
|
||||||
|
if encoding := hex.EncodeToString(p1.Bytes()); encoding != tt.canonical {
|
||||||
|
t.Errorf("re-encoding does not match canonical; got %q, expected %q", encoding, tt.canonical)
|
||||||
|
}
|
||||||
|
checkOnCurve(t, p1, p2)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var testAllocationsSink byte
|
||||||
|
|
||||||
|
func TestAllocations(t *testing.T) {
|
||||||
|
if allocs := testing.AllocsPerRun(100, func() {
|
||||||
|
p := NewIdentityPoint()
|
||||||
|
p.Add(p, NewGeneratorPoint())
|
||||||
|
s := NewScalar()
|
||||||
|
testAllocationsSink ^= s.Bytes()[0]
|
||||||
|
testAllocationsSink ^= p.Bytes()[0]
|
||||||
|
}); allocs > 0 {
|
||||||
|
t.Errorf("expected zero allocations, got %0.1v", allocs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeHex(s string) []byte {
|
||||||
|
b, err := hex.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,294 @@
|
||||||
|
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
. "github.com/mmcloughlin/avo/build"
|
||||||
|
. "github.com/mmcloughlin/avo/gotypes"
|
||||||
|
. "github.com/mmcloughlin/avo/operand"
|
||||||
|
. "github.com/mmcloughlin/avo/reg"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate go run . -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
Package("crypto/ed25519/internal/edwards25519/field")
|
||||||
|
ConstraintExpr("amd64,gc,!purego")
|
||||||
|
feMul()
|
||||||
|
feSquare()
|
||||||
|
Generate()
|
||||||
|
}
|
||||||
|
|
||||||
|
type namedComponent struct {
|
||||||
|
Component
|
||||||
|
name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c namedComponent) String() string { return c.name }
|
||||||
|
|
||||||
|
type uint128 struct {
|
||||||
|
name string
|
||||||
|
hi, lo GPVirtual
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c uint128) String() string { return c.name }
|
||||||
|
|
||||||
|
func feSquare() {
|
||||||
|
TEXT("feSquare", NOSPLIT, "func(out, a *Element)")
|
||||||
|
Doc("feSquare sets out = a * a. It works like feSquareGeneric.")
|
||||||
|
Pragma("noescape")
|
||||||
|
|
||||||
|
a := Dereference(Param("a"))
|
||||||
|
l0 := namedComponent{a.Field("l0"), "l0"}
|
||||||
|
l1 := namedComponent{a.Field("l1"), "l1"}
|
||||||
|
l2 := namedComponent{a.Field("l2"), "l2"}
|
||||||
|
l3 := namedComponent{a.Field("l3"), "l3"}
|
||||||
|
l4 := namedComponent{a.Field("l4"), "l4"}
|
||||||
|
|
||||||
|
// r0 = l0×l0 + 19×2×(l1×l4 + l2×l3)
|
||||||
|
r0 := uint128{"r0", GP64(), GP64()}
|
||||||
|
mul64(r0, 1, l0, l0)
|
||||||
|
addMul64(r0, 38, l1, l4)
|
||||||
|
addMul64(r0, 38, l2, l3)
|
||||||
|
|
||||||
|
// r1 = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3
|
||||||
|
r1 := uint128{"r1", GP64(), GP64()}
|
||||||
|
mul64(r1, 2, l0, l1)
|
||||||
|
addMul64(r1, 38, l2, l4)
|
||||||
|
addMul64(r1, 19, l3, l3)
|
||||||
|
|
||||||
|
// r2 = = 2×l0×l2 + l1×l1 + 19×2×l3×l4
|
||||||
|
r2 := uint128{"r2", GP64(), GP64()}
|
||||||
|
mul64(r2, 2, l0, l2)
|
||||||
|
addMul64(r2, 1, l1, l1)
|
||||||
|
addMul64(r2, 38, l3, l4)
|
||||||
|
|
||||||
|
// r3 = = 2×l0×l3 + 2×l1×l2 + 19×l4×l4
|
||||||
|
r3 := uint128{"r3", GP64(), GP64()}
|
||||||
|
mul64(r3, 2, l0, l3)
|
||||||
|
addMul64(r3, 2, l1, l2)
|
||||||
|
addMul64(r3, 19, l4, l4)
|
||||||
|
|
||||||
|
// r4 = = 2×l0×l4 + 2×l1×l3 + l2×l2
|
||||||
|
r4 := uint128{"r4", GP64(), GP64()}
|
||||||
|
mul64(r4, 2, l0, l4)
|
||||||
|
addMul64(r4, 2, l1, l3)
|
||||||
|
addMul64(r4, 1, l2, l2)
|
||||||
|
|
||||||
|
Comment("First reduction chain")
|
||||||
|
maskLow51Bits := GP64()
|
||||||
|
MOVQ(Imm((1<<51)-1), maskLow51Bits)
|
||||||
|
c0, r0lo := shiftRightBy51(&r0)
|
||||||
|
c1, r1lo := shiftRightBy51(&r1)
|
||||||
|
c2, r2lo := shiftRightBy51(&r2)
|
||||||
|
c3, r3lo := shiftRightBy51(&r3)
|
||||||
|
c4, r4lo := shiftRightBy51(&r4)
|
||||||
|
maskAndAdd(r0lo, maskLow51Bits, c4, 19)
|
||||||
|
maskAndAdd(r1lo, maskLow51Bits, c0, 1)
|
||||||
|
maskAndAdd(r2lo, maskLow51Bits, c1, 1)
|
||||||
|
maskAndAdd(r3lo, maskLow51Bits, c2, 1)
|
||||||
|
maskAndAdd(r4lo, maskLow51Bits, c3, 1)
|
||||||
|
|
||||||
|
Comment("Second reduction chain (carryPropagate)")
|
||||||
|
// c0 = r0 >> 51
|
||||||
|
MOVQ(r0lo, c0)
|
||||||
|
SHRQ(Imm(51), c0)
|
||||||
|
// c1 = r1 >> 51
|
||||||
|
MOVQ(r1lo, c1)
|
||||||
|
SHRQ(Imm(51), c1)
|
||||||
|
// c2 = r2 >> 51
|
||||||
|
MOVQ(r2lo, c2)
|
||||||
|
SHRQ(Imm(51), c2)
|
||||||
|
// c3 = r3 >> 51
|
||||||
|
MOVQ(r3lo, c3)
|
||||||
|
SHRQ(Imm(51), c3)
|
||||||
|
// c4 = r4 >> 51
|
||||||
|
MOVQ(r4lo, c4)
|
||||||
|
SHRQ(Imm(51), c4)
|
||||||
|
maskAndAdd(r0lo, maskLow51Bits, c4, 19)
|
||||||
|
maskAndAdd(r1lo, maskLow51Bits, c0, 1)
|
||||||
|
maskAndAdd(r2lo, maskLow51Bits, c1, 1)
|
||||||
|
maskAndAdd(r3lo, maskLow51Bits, c2, 1)
|
||||||
|
maskAndAdd(r4lo, maskLow51Bits, c3, 1)
|
||||||
|
|
||||||
|
Comment("Store output")
|
||||||
|
out := Dereference(Param("out"))
|
||||||
|
Store(r0lo, out.Field("l0"))
|
||||||
|
Store(r1lo, out.Field("l1"))
|
||||||
|
Store(r2lo, out.Field("l2"))
|
||||||
|
Store(r3lo, out.Field("l3"))
|
||||||
|
Store(r4lo, out.Field("l4"))
|
||||||
|
|
||||||
|
RET()
|
||||||
|
}
|
||||||
|
|
||||||
|
func feMul() {
|
||||||
|
TEXT("feMul", NOSPLIT, "func(out, a, b *Element)")
|
||||||
|
Doc("feMul sets out = a * b. It works like feMulGeneric.")
|
||||||
|
Pragma("noescape")
|
||||||
|
|
||||||
|
a := Dereference(Param("a"))
|
||||||
|
a0 := namedComponent{a.Field("l0"), "a0"}
|
||||||
|
a1 := namedComponent{a.Field("l1"), "a1"}
|
||||||
|
a2 := namedComponent{a.Field("l2"), "a2"}
|
||||||
|
a3 := namedComponent{a.Field("l3"), "a3"}
|
||||||
|
a4 := namedComponent{a.Field("l4"), "a4"}
|
||||||
|
|
||||||
|
b := Dereference(Param("b"))
|
||||||
|
b0 := namedComponent{b.Field("l0"), "b0"}
|
||||||
|
b1 := namedComponent{b.Field("l1"), "b1"}
|
||||||
|
b2 := namedComponent{b.Field("l2"), "b2"}
|
||||||
|
b3 := namedComponent{b.Field("l3"), "b3"}
|
||||||
|
b4 := namedComponent{b.Field("l4"), "b4"}
|
||||||
|
|
||||||
|
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||||
|
r0 := uint128{"r0", GP64(), GP64()}
|
||||||
|
mul64(r0, 1, a0, b0)
|
||||||
|
addMul64(r0, 19, a1, b4)
|
||||||
|
addMul64(r0, 19, a2, b3)
|
||||||
|
addMul64(r0, 19, a3, b2)
|
||||||
|
addMul64(r0, 19, a4, b1)
|
||||||
|
|
||||||
|
// r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2)
|
||||||
|
r1 := uint128{"r1", GP64(), GP64()}
|
||||||
|
mul64(r1, 1, a0, b1)
|
||||||
|
addMul64(r1, 1, a1, b0)
|
||||||
|
addMul64(r1, 19, a2, b4)
|
||||||
|
addMul64(r1, 19, a3, b3)
|
||||||
|
addMul64(r1, 19, a4, b2)
|
||||||
|
|
||||||
|
// r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3)
|
||||||
|
r2 := uint128{"r2", GP64(), GP64()}
|
||||||
|
mul64(r2, 1, a0, b2)
|
||||||
|
addMul64(r2, 1, a1, b1)
|
||||||
|
addMul64(r2, 1, a2, b0)
|
||||||
|
addMul64(r2, 19, a3, b4)
|
||||||
|
addMul64(r2, 19, a4, b3)
|
||||||
|
|
||||||
|
// r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4
|
||||||
|
r3 := uint128{"r3", GP64(), GP64()}
|
||||||
|
mul64(r3, 1, a0, b3)
|
||||||
|
addMul64(r3, 1, a1, b2)
|
||||||
|
addMul64(r3, 1, a2, b1)
|
||||||
|
addMul64(r3, 1, a3, b0)
|
||||||
|
addMul64(r3, 19, a4, b4)
|
||||||
|
|
||||||
|
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||||
|
r4 := uint128{"r4", GP64(), GP64()}
|
||||||
|
mul64(r4, 1, a0, b4)
|
||||||
|
addMul64(r4, 1, a1, b3)
|
||||||
|
addMul64(r4, 1, a2, b2)
|
||||||
|
addMul64(r4, 1, a3, b1)
|
||||||
|
addMul64(r4, 1, a4, b0)
|
||||||
|
|
||||||
|
Comment("First reduction chain")
|
||||||
|
maskLow51Bits := GP64()
|
||||||
|
MOVQ(Imm((1<<51)-1), maskLow51Bits)
|
||||||
|
c0, r0lo := shiftRightBy51(&r0)
|
||||||
|
c1, r1lo := shiftRightBy51(&r1)
|
||||||
|
c2, r2lo := shiftRightBy51(&r2)
|
||||||
|
c3, r3lo := shiftRightBy51(&r3)
|
||||||
|
c4, r4lo := shiftRightBy51(&r4)
|
||||||
|
maskAndAdd(r0lo, maskLow51Bits, c4, 19)
|
||||||
|
maskAndAdd(r1lo, maskLow51Bits, c0, 1)
|
||||||
|
maskAndAdd(r2lo, maskLow51Bits, c1, 1)
|
||||||
|
maskAndAdd(r3lo, maskLow51Bits, c2, 1)
|
||||||
|
maskAndAdd(r4lo, maskLow51Bits, c3, 1)
|
||||||
|
|
||||||
|
Comment("Second reduction chain (carryPropagate)")
|
||||||
|
// c0 = r0 >> 51
|
||||||
|
MOVQ(r0lo, c0)
|
||||||
|
SHRQ(Imm(51), c0)
|
||||||
|
// c1 = r1 >> 51
|
||||||
|
MOVQ(r1lo, c1)
|
||||||
|
SHRQ(Imm(51), c1)
|
||||||
|
// c2 = r2 >> 51
|
||||||
|
MOVQ(r2lo, c2)
|
||||||
|
SHRQ(Imm(51), c2)
|
||||||
|
// c3 = r3 >> 51
|
||||||
|
MOVQ(r3lo, c3)
|
||||||
|
SHRQ(Imm(51), c3)
|
||||||
|
// c4 = r4 >> 51
|
||||||
|
MOVQ(r4lo, c4)
|
||||||
|
SHRQ(Imm(51), c4)
|
||||||
|
maskAndAdd(r0lo, maskLow51Bits, c4, 19)
|
||||||
|
maskAndAdd(r1lo, maskLow51Bits, c0, 1)
|
||||||
|
maskAndAdd(r2lo, maskLow51Bits, c1, 1)
|
||||||
|
maskAndAdd(r3lo, maskLow51Bits, c2, 1)
|
||||||
|
maskAndAdd(r4lo, maskLow51Bits, c3, 1)
|
||||||
|
|
||||||
|
Comment("Store output")
|
||||||
|
out := Dereference(Param("out"))
|
||||||
|
Store(r0lo, out.Field("l0"))
|
||||||
|
Store(r1lo, out.Field("l1"))
|
||||||
|
Store(r2lo, out.Field("l2"))
|
||||||
|
Store(r3lo, out.Field("l3"))
|
||||||
|
Store(r4lo, out.Field("l4"))
|
||||||
|
|
||||||
|
RET()
|
||||||
|
}
|
||||||
|
|
||||||
|
// mul64 sets r to i * aX * bX.
|
||||||
|
func mul64(r uint128, i int, aX, bX namedComponent) {
|
||||||
|
switch i {
|
||||||
|
case 1:
|
||||||
|
Comment(fmt.Sprintf("%s = %s×%s", r, aX, bX))
|
||||||
|
Load(aX, RAX)
|
||||||
|
case 2:
|
||||||
|
Comment(fmt.Sprintf("%s = 2×%s×%s", r, aX, bX))
|
||||||
|
Load(aX, RAX)
|
||||||
|
SHLQ(Imm(1), RAX)
|
||||||
|
default:
|
||||||
|
panic("unsupported i value")
|
||||||
|
}
|
||||||
|
MULQ(mustAddr(bX)) // RDX, RAX = RAX * bX
|
||||||
|
MOVQ(RAX, r.lo)
|
||||||
|
MOVQ(RDX, r.hi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addMul64 sets r to r + i * aX * bX.
|
||||||
|
func addMul64(r uint128, i uint64, aX, bX namedComponent) {
|
||||||
|
switch i {
|
||||||
|
case 1:
|
||||||
|
Comment(fmt.Sprintf("%s += %s×%s", r, aX, bX))
|
||||||
|
Load(aX, RAX)
|
||||||
|
default:
|
||||||
|
Comment(fmt.Sprintf("%s += %d×%s×%s", r, i, aX, bX))
|
||||||
|
IMUL3Q(Imm(i), Load(aX, GP64()), RAX)
|
||||||
|
}
|
||||||
|
MULQ(mustAddr(bX)) // RDX, RAX = RAX * bX
|
||||||
|
ADDQ(RAX, r.lo)
|
||||||
|
ADCQ(RDX, r.hi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// shiftRightBy51 returns r >> 51 and r.lo.
|
||||||
|
//
|
||||||
|
// After this function is called, the uint128 may not be used anymore.
|
||||||
|
func shiftRightBy51(r *uint128) (out, lo GPVirtual) {
|
||||||
|
out = r.hi
|
||||||
|
lo = r.lo
|
||||||
|
SHLQ(Imm(64-51), r.lo, r.hi)
|
||||||
|
r.lo, r.hi = nil, nil // make sure the uint128 is unusable
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// maskAndAdd sets r = r&mask + c*i.
|
||||||
|
func maskAndAdd(r, mask, c GPVirtual, i uint64) {
|
||||||
|
ANDQ(mask, r)
|
||||||
|
if i != 1 {
|
||||||
|
IMUL3Q(Imm(i), c, c)
|
||||||
|
}
|
||||||
|
ADDQ(c, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustAddr(c Component) Op {
|
||||||
|
b, err := c.Resolve()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b.Addr
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
module asm
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require github.com/mmcloughlin/avo v0.2.0
|
||||||
31
src/crypto/ed25519/internal/edwards25519/field/_asm/go.sum
Normal file
31
src/crypto/ed25519/internal/edwards25519/field/_asm/go.sum
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
github.com/mmcloughlin/avo v0.2.0 h1:6vhoSaKtxb6f4RiH+LK2qL6GSMpFzhEwJYTTSZNy09w=
|
||||||
|
github.com/mmcloughlin/avo v0.2.0/go.mod h1:5tidO2Z9Z7N6X7UMcGg+1KTj51O8OxYDCMHxCZTVpEA=
|
||||||
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
|
golang.org/x/arch v0.0.0-20210405154355-08b684f594a5/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
|
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||||
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
|
||||||
|
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||||
|
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||||
416
src/crypto/ed25519/internal/edwards25519/field/fe.go
Normal file
416
src/crypto/ed25519/internal/edwards25519/field/fe.go
Normal file
|
|
@ -0,0 +1,416 @@
|
||||||
|
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package field implements fast arithmetic modulo 2^255-19.
|
||||||
|
package field
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/subtle"
|
||||||
|
"encoding/binary"
|
||||||
|
"math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Element represents an element of the field GF(2^255-19). Note that this
|
||||||
|
// is not a cryptographically secure group, and should only be used to interact
|
||||||
|
// with edwards25519.Point coordinates.
|
||||||
|
//
|
||||||
|
// This type works similarly to math/big.Int, and all arguments and receivers
|
||||||
|
// are allowed to alias.
|
||||||
|
//
|
||||||
|
// The zero value is a valid zero element.
|
||||||
|
type Element struct {
|
||||||
|
// An element t represents the integer
|
||||||
|
// t.l0 + t.l1*2^51 + t.l2*2^102 + t.l3*2^153 + t.l4*2^204
|
||||||
|
//
|
||||||
|
// Between operations, all limbs are expected to be lower than 2^52.
|
||||||
|
l0 uint64
|
||||||
|
l1 uint64
|
||||||
|
l2 uint64
|
||||||
|
l3 uint64
|
||||||
|
l4 uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
const maskLow51Bits uint64 = (1 << 51) - 1
|
||||||
|
|
||||||
|
var feZero = &Element{0, 0, 0, 0, 0}
|
||||||
|
|
||||||
|
// Zero sets v = 0, and returns v.
|
||||||
|
func (v *Element) Zero() *Element {
|
||||||
|
*v = *feZero
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
var feOne = &Element{1, 0, 0, 0, 0}
|
||||||
|
|
||||||
|
// One sets v = 1, and returns v.
|
||||||
|
func (v *Element) One() *Element {
|
||||||
|
*v = *feOne
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// reduce reduces v modulo 2^255 - 19 and returns it.
|
||||||
|
func (v *Element) reduce() *Element {
|
||||||
|
v.carryPropagate()
|
||||||
|
|
||||||
|
// After the light reduction we now have a field element representation
|
||||||
|
// v < 2^255 + 2^13 * 19, but need v < 2^255 - 19.
|
||||||
|
|
||||||
|
// If v >= 2^255 - 19, then v + 19 >= 2^255, which would overflow 2^255 - 1,
|
||||||
|
// generating a carry. That is, c will be 0 if v < 2^255 - 19, and 1 otherwise.
|
||||||
|
c := (v.l0 + 19) >> 51
|
||||||
|
c = (v.l1 + c) >> 51
|
||||||
|
c = (v.l2 + c) >> 51
|
||||||
|
c = (v.l3 + c) >> 51
|
||||||
|
c = (v.l4 + c) >> 51
|
||||||
|
|
||||||
|
// If v < 2^255 - 19 and c = 0, this will be a no-op. Otherwise, it's
|
||||||
|
// effectively applying the reduction identity to the carry.
|
||||||
|
v.l0 += 19 * c
|
||||||
|
|
||||||
|
v.l1 += v.l0 >> 51
|
||||||
|
v.l0 = v.l0 & maskLow51Bits
|
||||||
|
v.l2 += v.l1 >> 51
|
||||||
|
v.l1 = v.l1 & maskLow51Bits
|
||||||
|
v.l3 += v.l2 >> 51
|
||||||
|
v.l2 = v.l2 & maskLow51Bits
|
||||||
|
v.l4 += v.l3 >> 51
|
||||||
|
v.l3 = v.l3 & maskLow51Bits
|
||||||
|
// no additional carry
|
||||||
|
v.l4 = v.l4 & maskLow51Bits
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sets v = a + b, and returns v.
|
||||||
|
func (v *Element) Add(a, b *Element) *Element {
|
||||||
|
v.l0 = a.l0 + b.l0
|
||||||
|
v.l1 = a.l1 + b.l1
|
||||||
|
v.l2 = a.l2 + b.l2
|
||||||
|
v.l3 = a.l3 + b.l3
|
||||||
|
v.l4 = a.l4 + b.l4
|
||||||
|
// Using the generic implementation here is actually faster than the
|
||||||
|
// assembly. Probably because the body of this function is so simple that
|
||||||
|
// the compiler can figure out better optimizations by inlining the carry
|
||||||
|
// propagation.
|
||||||
|
return v.carryPropagateGeneric()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract sets v = a - b, and returns v.
|
||||||
|
func (v *Element) Subtract(a, b *Element) *Element {
|
||||||
|
// We first add 2 * p, to guarantee the subtraction won't underflow, and
|
||||||
|
// then subtract b (which can be up to 2^255 + 2^13 * 19).
|
||||||
|
v.l0 = (a.l0 + 0xFFFFFFFFFFFDA) - b.l0
|
||||||
|
v.l1 = (a.l1 + 0xFFFFFFFFFFFFE) - b.l1
|
||||||
|
v.l2 = (a.l2 + 0xFFFFFFFFFFFFE) - b.l2
|
||||||
|
v.l3 = (a.l3 + 0xFFFFFFFFFFFFE) - b.l3
|
||||||
|
v.l4 = (a.l4 + 0xFFFFFFFFFFFFE) - b.l4
|
||||||
|
return v.carryPropagate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Negate sets v = -a, and returns v.
|
||||||
|
func (v *Element) Negate(a *Element) *Element {
|
||||||
|
return v.Subtract(feZero, a)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invert sets v = 1/z mod p, and returns v.
|
||||||
|
//
|
||||||
|
// If z == 0, Invert returns v = 0.
|
||||||
|
func (v *Element) Invert(z *Element) *Element {
|
||||||
|
// Inversion is implemented as exponentiation with exponent p − 2. It uses the
|
||||||
|
// same sequence of 255 squarings and 11 multiplications as [Curve25519].
|
||||||
|
var z2, z9, z11, z2_5_0, z2_10_0, z2_20_0, z2_50_0, z2_100_0, t Element
|
||||||
|
|
||||||
|
z2.Square(z) // 2
|
||||||
|
t.Square(&z2) // 4
|
||||||
|
t.Square(&t) // 8
|
||||||
|
z9.Multiply(&t, z) // 9
|
||||||
|
z11.Multiply(&z9, &z2) // 11
|
||||||
|
t.Square(&z11) // 22
|
||||||
|
z2_5_0.Multiply(&t, &z9) // 31 = 2^5 - 2^0
|
||||||
|
|
||||||
|
t.Square(&z2_5_0) // 2^6 - 2^1
|
||||||
|
for i := 0; i < 4; i++ {
|
||||||
|
t.Square(&t) // 2^10 - 2^5
|
||||||
|
}
|
||||||
|
z2_10_0.Multiply(&t, &z2_5_0) // 2^10 - 2^0
|
||||||
|
|
||||||
|
t.Square(&z2_10_0) // 2^11 - 2^1
|
||||||
|
for i := 0; i < 9; i++ {
|
||||||
|
t.Square(&t) // 2^20 - 2^10
|
||||||
|
}
|
||||||
|
z2_20_0.Multiply(&t, &z2_10_0) // 2^20 - 2^0
|
||||||
|
|
||||||
|
t.Square(&z2_20_0) // 2^21 - 2^1
|
||||||
|
for i := 0; i < 19; i++ {
|
||||||
|
t.Square(&t) // 2^40 - 2^20
|
||||||
|
}
|
||||||
|
t.Multiply(&t, &z2_20_0) // 2^40 - 2^0
|
||||||
|
|
||||||
|
t.Square(&t) // 2^41 - 2^1
|
||||||
|
for i := 0; i < 9; i++ {
|
||||||
|
t.Square(&t) // 2^50 - 2^10
|
||||||
|
}
|
||||||
|
z2_50_0.Multiply(&t, &z2_10_0) // 2^50 - 2^0
|
||||||
|
|
||||||
|
t.Square(&z2_50_0) // 2^51 - 2^1
|
||||||
|
for i := 0; i < 49; i++ {
|
||||||
|
t.Square(&t) // 2^100 - 2^50
|
||||||
|
}
|
||||||
|
z2_100_0.Multiply(&t, &z2_50_0) // 2^100 - 2^0
|
||||||
|
|
||||||
|
t.Square(&z2_100_0) // 2^101 - 2^1
|
||||||
|
for i := 0; i < 99; i++ {
|
||||||
|
t.Square(&t) // 2^200 - 2^100
|
||||||
|
}
|
||||||
|
t.Multiply(&t, &z2_100_0) // 2^200 - 2^0
|
||||||
|
|
||||||
|
t.Square(&t) // 2^201 - 2^1
|
||||||
|
for i := 0; i < 49; i++ {
|
||||||
|
t.Square(&t) // 2^250 - 2^50
|
||||||
|
}
|
||||||
|
t.Multiply(&t, &z2_50_0) // 2^250 - 2^0
|
||||||
|
|
||||||
|
t.Square(&t) // 2^251 - 2^1
|
||||||
|
t.Square(&t) // 2^252 - 2^2
|
||||||
|
t.Square(&t) // 2^253 - 2^3
|
||||||
|
t.Square(&t) // 2^254 - 2^4
|
||||||
|
t.Square(&t) // 2^255 - 2^5
|
||||||
|
|
||||||
|
return v.Multiply(&t, &z11) // 2^255 - 21
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set sets v = a, and returns v.
|
||||||
|
func (v *Element) Set(a *Element) *Element {
|
||||||
|
*v = *a
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBytes sets v to x, which must be a 32-byte little-endian encoding.
|
||||||
|
//
|
||||||
|
// Consistent with RFC 7748, the most significant bit (the high bit of the
|
||||||
|
// last byte) is ignored, and non-canonical values (2^255-19 through 2^255-1)
|
||||||
|
// are accepted. Note that this is laxer than specified by RFC 8032.
|
||||||
|
func (v *Element) SetBytes(x []byte) *Element {
|
||||||
|
if len(x) != 32 {
|
||||||
|
panic("edwards25519: invalid field element input size")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bits 0:51 (bytes 0:8, bits 0:64, shift 0, mask 51).
|
||||||
|
v.l0 = binary.LittleEndian.Uint64(x[0:8])
|
||||||
|
v.l0 &= maskLow51Bits
|
||||||
|
// Bits 51:102 (bytes 6:14, bits 48:112, shift 3, mask 51).
|
||||||
|
v.l1 = binary.LittleEndian.Uint64(x[6:14]) >> 3
|
||||||
|
v.l1 &= maskLow51Bits
|
||||||
|
// Bits 102:153 (bytes 12:20, bits 96:160, shift 6, mask 51).
|
||||||
|
v.l2 = binary.LittleEndian.Uint64(x[12:20]) >> 6
|
||||||
|
v.l2 &= maskLow51Bits
|
||||||
|
// Bits 153:204 (bytes 19:27, bits 152:216, shift 1, mask 51).
|
||||||
|
v.l3 = binary.LittleEndian.Uint64(x[19:27]) >> 1
|
||||||
|
v.l3 &= maskLow51Bits
|
||||||
|
// Bits 204:251 (bytes 24:32, bits 192:256, shift 12, mask 51).
|
||||||
|
// Note: not bytes 25:33, shift 4, to avoid overread.
|
||||||
|
v.l4 = binary.LittleEndian.Uint64(x[24:32]) >> 12
|
||||||
|
v.l4 &= maskLow51Bits
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the canonical 32-byte little-endian encoding of v.
|
||||||
|
func (v *Element) Bytes() []byte {
|
||||||
|
// This function is outlined to make the allocations inline in the caller
|
||||||
|
// rather than happen on the heap.
|
||||||
|
var out [32]byte
|
||||||
|
return v.bytes(&out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Element) bytes(out *[32]byte) []byte {
|
||||||
|
t := *v
|
||||||
|
t.reduce()
|
||||||
|
|
||||||
|
var buf [8]byte
|
||||||
|
for i, l := range [5]uint64{t.l0, t.l1, t.l2, t.l3, t.l4} {
|
||||||
|
bitsOffset := i * 51
|
||||||
|
binary.LittleEndian.PutUint64(buf[:], l<<uint(bitsOffset%8))
|
||||||
|
for i, bb := range buf {
|
||||||
|
off := bitsOffset/8 + i
|
||||||
|
if off >= len(out) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
out[off] |= bb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns 1 if v and u are equal, and 0 otherwise.
|
||||||
|
func (v *Element) Equal(u *Element) int {
|
||||||
|
sa, sv := u.Bytes(), v.Bytes()
|
||||||
|
return subtle.ConstantTimeCompare(sa, sv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mask64Bits returns 0xffffffff if cond is 1, and 0 otherwise.
|
||||||
|
func mask64Bits(cond int) uint64 { return ^(uint64(cond) - 1) }
|
||||||
|
|
||||||
|
// Select sets v to a if cond == 1, and to b if cond == 0.
|
||||||
|
func (v *Element) Select(a, b *Element, cond int) *Element {
|
||||||
|
m := mask64Bits(cond)
|
||||||
|
v.l0 = (m & a.l0) | (^m & b.l0)
|
||||||
|
v.l1 = (m & a.l1) | (^m & b.l1)
|
||||||
|
v.l2 = (m & a.l2) | (^m & b.l2)
|
||||||
|
v.l3 = (m & a.l3) | (^m & b.l3)
|
||||||
|
v.l4 = (m & a.l4) | (^m & b.l4)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap swaps v and u if cond == 1 or leaves them unchanged if cond == 0, and returns v.
|
||||||
|
func (v *Element) Swap(u *Element, cond int) {
|
||||||
|
m := mask64Bits(cond)
|
||||||
|
t := m & (v.l0 ^ u.l0)
|
||||||
|
v.l0 ^= t
|
||||||
|
u.l0 ^= t
|
||||||
|
t = m & (v.l1 ^ u.l1)
|
||||||
|
v.l1 ^= t
|
||||||
|
u.l1 ^= t
|
||||||
|
t = m & (v.l2 ^ u.l2)
|
||||||
|
v.l2 ^= t
|
||||||
|
u.l2 ^= t
|
||||||
|
t = m & (v.l3 ^ u.l3)
|
||||||
|
v.l3 ^= t
|
||||||
|
u.l3 ^= t
|
||||||
|
t = m & (v.l4 ^ u.l4)
|
||||||
|
v.l4 ^= t
|
||||||
|
u.l4 ^= t
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNegative returns 1 if v is negative, and 0 otherwise.
|
||||||
|
func (v *Element) IsNegative() int {
|
||||||
|
return int(v.Bytes()[0] & 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Absolute sets v to |u|, and returns v.
|
||||||
|
func (v *Element) Absolute(u *Element) *Element {
|
||||||
|
return v.Select(new(Element).Negate(u), u, u.IsNegative())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiply sets v = x * y, and returns v.
|
||||||
|
func (v *Element) Multiply(x, y *Element) *Element {
|
||||||
|
feMul(v, x, y)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Square sets v = x * x, and returns v.
|
||||||
|
func (v *Element) Square(x *Element) *Element {
|
||||||
|
feSquare(v, x)
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mult32 sets v = x * y, and returns v.
|
||||||
|
func (v *Element) Mult32(x *Element, y uint32) *Element {
|
||||||
|
x0lo, x0hi := mul51(x.l0, y)
|
||||||
|
x1lo, x1hi := mul51(x.l1, y)
|
||||||
|
x2lo, x2hi := mul51(x.l2, y)
|
||||||
|
x3lo, x3hi := mul51(x.l3, y)
|
||||||
|
x4lo, x4hi := mul51(x.l4, y)
|
||||||
|
v.l0 = x0lo + 19*x4hi // carried over per the reduction identity
|
||||||
|
v.l1 = x1lo + x0hi
|
||||||
|
v.l2 = x2lo + x1hi
|
||||||
|
v.l3 = x3lo + x2hi
|
||||||
|
v.l4 = x4lo + x3hi
|
||||||
|
// The hi portions are going to be only 32 bits, plus any previous excess,
|
||||||
|
// so we can skip the carry propagation.
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// mul51 returns lo + hi * 2⁵¹ = a * b.
|
||||||
|
func mul51(a uint64, b uint32) (lo uint64, hi uint64) {
|
||||||
|
mh, ml := bits.Mul64(a, uint64(b))
|
||||||
|
lo = ml & maskLow51Bits
|
||||||
|
hi = (mh << 13) | (ml >> 51)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pow22523 set v = x^((p-5)/8), and returns v. (p-5)/8 is 2^252-3.
|
||||||
|
func (v *Element) Pow22523(x *Element) *Element {
|
||||||
|
var t0, t1, t2 Element
|
||||||
|
|
||||||
|
t0.Square(x) // x^2
|
||||||
|
t1.Square(&t0) // x^4
|
||||||
|
t1.Square(&t1) // x^8
|
||||||
|
t1.Multiply(x, &t1) // x^9
|
||||||
|
t0.Multiply(&t0, &t1) // x^11
|
||||||
|
t0.Square(&t0) // x^22
|
||||||
|
t0.Multiply(&t1, &t0) // x^31
|
||||||
|
t1.Square(&t0) // x^62
|
||||||
|
for i := 1; i < 5; i++ { // x^992
|
||||||
|
t1.Square(&t1)
|
||||||
|
}
|
||||||
|
t0.Multiply(&t1, &t0) // x^1023 -> 1023 = 2^10 - 1
|
||||||
|
t1.Square(&t0) // 2^11 - 2
|
||||||
|
for i := 1; i < 10; i++ { // 2^20 - 2^10
|
||||||
|
t1.Square(&t1)
|
||||||
|
}
|
||||||
|
t1.Multiply(&t1, &t0) // 2^20 - 1
|
||||||
|
t2.Square(&t1) // 2^21 - 2
|
||||||
|
for i := 1; i < 20; i++ { // 2^40 - 2^20
|
||||||
|
t2.Square(&t2)
|
||||||
|
}
|
||||||
|
t1.Multiply(&t2, &t1) // 2^40 - 1
|
||||||
|
t1.Square(&t1) // 2^41 - 2
|
||||||
|
for i := 1; i < 10; i++ { // 2^50 - 2^10
|
||||||
|
t1.Square(&t1)
|
||||||
|
}
|
||||||
|
t0.Multiply(&t1, &t0) // 2^50 - 1
|
||||||
|
t1.Square(&t0) // 2^51 - 2
|
||||||
|
for i := 1; i < 50; i++ { // 2^100 - 2^50
|
||||||
|
t1.Square(&t1)
|
||||||
|
}
|
||||||
|
t1.Multiply(&t1, &t0) // 2^100 - 1
|
||||||
|
t2.Square(&t1) // 2^101 - 2
|
||||||
|
for i := 1; i < 100; i++ { // 2^200 - 2^100
|
||||||
|
t2.Square(&t2)
|
||||||
|
}
|
||||||
|
t1.Multiply(&t2, &t1) // 2^200 - 1
|
||||||
|
t1.Square(&t1) // 2^201 - 2
|
||||||
|
for i := 1; i < 50; i++ { // 2^250 - 2^50
|
||||||
|
t1.Square(&t1)
|
||||||
|
}
|
||||||
|
t0.Multiply(&t1, &t0) // 2^250 - 1
|
||||||
|
t0.Square(&t0) // 2^251 - 2
|
||||||
|
t0.Square(&t0) // 2^252 - 4
|
||||||
|
return v.Multiply(&t0, x) // 2^252 - 3 -> x^(2^252-3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sqrtM1 is 2^((p-1)/4), which squared is equal to -1 by Euler's Criterion.
|
||||||
|
var sqrtM1 = &Element{1718705420411056, 234908883556509,
|
||||||
|
2233514472574048, 2117202627021982, 765476049583133}
|
||||||
|
|
||||||
|
// SqrtRatio sets r to the non-negative square root of the ratio of u and v.
|
||||||
|
//
|
||||||
|
// If u/v is square, SqrtRatio returns r and 1. If u/v is not square, SqrtRatio
|
||||||
|
// sets r according to Section 4.3 of draft-irtf-cfrg-ristretto255-decaf448-00,
|
||||||
|
// and returns r and 0.
|
||||||
|
func (r *Element) SqrtRatio(u, v *Element) (rr *Element, wasSquare int) {
|
||||||
|
var a, b Element
|
||||||
|
|
||||||
|
// r = (u * v3) * (u * v7)^((p-5)/8)
|
||||||
|
v2 := a.Square(v)
|
||||||
|
uv3 := b.Multiply(u, b.Multiply(v2, v))
|
||||||
|
uv7 := a.Multiply(uv3, a.Square(v2))
|
||||||
|
r.Multiply(uv3, r.Pow22523(uv7))
|
||||||
|
|
||||||
|
check := a.Multiply(v, a.Square(r)) // check = v * r^2
|
||||||
|
|
||||||
|
uNeg := b.Negate(u)
|
||||||
|
correctSignSqrt := check.Equal(u)
|
||||||
|
flippedSignSqrt := check.Equal(uNeg)
|
||||||
|
flippedSignSqrtI := check.Equal(uNeg.Multiply(uNeg, sqrtM1))
|
||||||
|
|
||||||
|
rPrime := b.Multiply(r, sqrtM1) // r_prime = SQRT_M1 * r
|
||||||
|
// r = CT_SELECT(r_prime IF flipped_sign_sqrt | flipped_sign_sqrt_i ELSE r)
|
||||||
|
r.Select(rPrime, r, flippedSignSqrt|flippedSignSqrtI)
|
||||||
|
|
||||||
|
r.Absolute(r) // Choose the nonnegative square root.
|
||||||
|
return r, correctSignSqrt | flippedSignSqrt
|
||||||
|
}
|
||||||
126
src/crypto/ed25519/internal/edwards25519/field/fe_alias_test.go
Normal file
126
src/crypto/ed25519/internal/edwards25519/field/fe_alias_test.go
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"testing/quick"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkAliasingOneArg(f func(v, x *Element) *Element) func(v, x Element) bool {
|
||||||
|
return func(v, x Element) bool {
|
||||||
|
x1, v1 := x, x
|
||||||
|
|
||||||
|
// Calculate a reference f(x) without aliasing.
|
||||||
|
if out := f(&v, &x); out != &v && isInBounds(out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test aliasing the argument and the receiver.
|
||||||
|
if out := f(&v1, &v1); out != &v1 || v1 != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the arguments was not modified.
|
||||||
|
return x == x1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkAliasingTwoArgs(f func(v, x, y *Element) *Element) func(v, x, y Element) bool {
|
||||||
|
return func(v, x, y Element) bool {
|
||||||
|
x1, y1, v1 := x, y, Element{}
|
||||||
|
|
||||||
|
// Calculate a reference f(x, y) without aliasing.
|
||||||
|
if out := f(&v, &x, &y); out != &v && isInBounds(out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test aliasing the first argument and the receiver.
|
||||||
|
v1 = x
|
||||||
|
if out := f(&v1, &v1, &y); out != &v1 || v1 != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Test aliasing the second argument and the receiver.
|
||||||
|
v1 = y
|
||||||
|
if out := f(&v1, &x, &v1); out != &v1 || v1 != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate a reference f(x, x) without aliasing.
|
||||||
|
if out := f(&v, &x, &x); out != &v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test aliasing the first argument and the receiver.
|
||||||
|
v1 = x
|
||||||
|
if out := f(&v1, &v1, &x); out != &v1 || v1 != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Test aliasing the second argument and the receiver.
|
||||||
|
v1 = x
|
||||||
|
if out := f(&v1, &x, &v1); out != &v1 || v1 != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Test aliasing both arguments and the receiver.
|
||||||
|
v1 = x
|
||||||
|
if out := f(&v1, &v1, &v1); out != &v1 || v1 != v {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the arguments were not modified.
|
||||||
|
return x == x1 && y == y1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestAliasing checks that receivers and arguments can alias each other without
|
||||||
|
// leading to incorrect results. That is, it ensures that it's safe to write
|
||||||
|
//
|
||||||
|
// v.Invert(v)
|
||||||
|
//
|
||||||
|
// or
|
||||||
|
//
|
||||||
|
// v.Add(v, v)
|
||||||
|
//
|
||||||
|
// without any of the inputs getting clobbered by the output being written.
|
||||||
|
func TestAliasing(t *testing.T) {
|
||||||
|
type target struct {
|
||||||
|
name string
|
||||||
|
oneArgF func(v, x *Element) *Element
|
||||||
|
twoArgsF func(v, x, y *Element) *Element
|
||||||
|
}
|
||||||
|
for _, tt := range []target{
|
||||||
|
{name: "Absolute", oneArgF: (*Element).Absolute},
|
||||||
|
{name: "Invert", oneArgF: (*Element).Invert},
|
||||||
|
{name: "Negate", oneArgF: (*Element).Negate},
|
||||||
|
{name: "Set", oneArgF: (*Element).Set},
|
||||||
|
{name: "Square", oneArgF: (*Element).Square},
|
||||||
|
{name: "Multiply", twoArgsF: (*Element).Multiply},
|
||||||
|
{name: "Add", twoArgsF: (*Element).Add},
|
||||||
|
{name: "Subtract", twoArgsF: (*Element).Subtract},
|
||||||
|
{
|
||||||
|
name: "Select0",
|
||||||
|
twoArgsF: func(v, x, y *Element) *Element {
|
||||||
|
return (*Element).Select(v, x, y, 0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Select1",
|
||||||
|
twoArgsF: func(v, x, y *Element) *Element {
|
||||||
|
return (*Element).Select(v, x, y, 1)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
var err error
|
||||||
|
switch {
|
||||||
|
case tt.oneArgF != nil:
|
||||||
|
err = quick.Check(checkAliasingOneArg(tt.oneArgF), &quick.Config{MaxCountScale: 1 << 8})
|
||||||
|
case tt.twoArgsF != nil:
|
||||||
|
err = quick.Check(checkAliasingTwoArgs(tt.twoArgsF), &quick.Config{MaxCountScale: 1 << 8})
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v: %v", tt.name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/crypto/ed25519/internal/edwards25519/field/fe_amd64.go
Normal file
13
src/crypto/ed25519/internal/edwards25519/field/fe_amd64.go
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||||
|
|
||||||
|
// +build amd64,gc,!purego
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
// feMul sets out = a * b. It works like feMulGeneric.
|
||||||
|
//go:noescape
|
||||||
|
func feMul(out *Element, a *Element, b *Element)
|
||||||
|
|
||||||
|
// feSquare sets out = a * a. It works like feSquareGeneric.
|
||||||
|
//go:noescape
|
||||||
|
func feSquare(out *Element, a *Element)
|
||||||
378
src/crypto/ed25519/internal/edwards25519/field/fe_amd64.s
Normal file
378
src/crypto/ed25519/internal/edwards25519/field/fe_amd64.s
Normal file
|
|
@ -0,0 +1,378 @@
|
||||||
|
// Code generated by command: go run fe_amd64_asm.go -out ../fe_amd64.s -stubs ../fe_amd64.go -pkg field. DO NOT EDIT.
|
||||||
|
|
||||||
|
// +build amd64,gc,!purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// func feMul(out *Element, a *Element, b *Element)
|
||||||
|
TEXT ·feMul(SB), NOSPLIT, $0-24
|
||||||
|
MOVQ a+8(FP), CX
|
||||||
|
MOVQ b+16(FP), BX
|
||||||
|
|
||||||
|
// r0 = a0×b0
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ (BX)
|
||||||
|
MOVQ AX, DI
|
||||||
|
MOVQ DX, SI
|
||||||
|
|
||||||
|
// r0 += 19×a1×b4
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 32(BX)
|
||||||
|
ADDQ AX, DI
|
||||||
|
ADCQ DX, SI
|
||||||
|
|
||||||
|
// r0 += 19×a2×b3
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 24(BX)
|
||||||
|
ADDQ AX, DI
|
||||||
|
ADCQ DX, SI
|
||||||
|
|
||||||
|
// r0 += 19×a3×b2
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 16(BX)
|
||||||
|
ADDQ AX, DI
|
||||||
|
ADCQ DX, SI
|
||||||
|
|
||||||
|
// r0 += 19×a4×b1
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 8(BX)
|
||||||
|
ADDQ AX, DI
|
||||||
|
ADCQ DX, SI
|
||||||
|
|
||||||
|
// r1 = a0×b1
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ 8(BX)
|
||||||
|
MOVQ AX, R9
|
||||||
|
MOVQ DX, R8
|
||||||
|
|
||||||
|
// r1 += a1×b0
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
MULQ (BX)
|
||||||
|
ADDQ AX, R9
|
||||||
|
ADCQ DX, R8
|
||||||
|
|
||||||
|
// r1 += 19×a2×b4
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 32(BX)
|
||||||
|
ADDQ AX, R9
|
||||||
|
ADCQ DX, R8
|
||||||
|
|
||||||
|
// r1 += 19×a3×b3
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 24(BX)
|
||||||
|
ADDQ AX, R9
|
||||||
|
ADCQ DX, R8
|
||||||
|
|
||||||
|
// r1 += 19×a4×b2
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 16(BX)
|
||||||
|
ADDQ AX, R9
|
||||||
|
ADCQ DX, R8
|
||||||
|
|
||||||
|
// r2 = a0×b2
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ 16(BX)
|
||||||
|
MOVQ AX, R11
|
||||||
|
MOVQ DX, R10
|
||||||
|
|
||||||
|
// r2 += a1×b1
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
MULQ 8(BX)
|
||||||
|
ADDQ AX, R11
|
||||||
|
ADCQ DX, R10
|
||||||
|
|
||||||
|
// r2 += a2×b0
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
MULQ (BX)
|
||||||
|
ADDQ AX, R11
|
||||||
|
ADCQ DX, R10
|
||||||
|
|
||||||
|
// r2 += 19×a3×b4
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 32(BX)
|
||||||
|
ADDQ AX, R11
|
||||||
|
ADCQ DX, R10
|
||||||
|
|
||||||
|
// r2 += 19×a4×b3
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 24(BX)
|
||||||
|
ADDQ AX, R11
|
||||||
|
ADCQ DX, R10
|
||||||
|
|
||||||
|
// r3 = a0×b3
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ 24(BX)
|
||||||
|
MOVQ AX, R13
|
||||||
|
MOVQ DX, R12
|
||||||
|
|
||||||
|
// r3 += a1×b2
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
MULQ 16(BX)
|
||||||
|
ADDQ AX, R13
|
||||||
|
ADCQ DX, R12
|
||||||
|
|
||||||
|
// r3 += a2×b1
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
MULQ 8(BX)
|
||||||
|
ADDQ AX, R13
|
||||||
|
ADCQ DX, R12
|
||||||
|
|
||||||
|
// r3 += a3×b0
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
MULQ (BX)
|
||||||
|
ADDQ AX, R13
|
||||||
|
ADCQ DX, R12
|
||||||
|
|
||||||
|
// r3 += 19×a4×b4
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 32(BX)
|
||||||
|
ADDQ AX, R13
|
||||||
|
ADCQ DX, R12
|
||||||
|
|
||||||
|
// r4 = a0×b4
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ 32(BX)
|
||||||
|
MOVQ AX, R15
|
||||||
|
MOVQ DX, R14
|
||||||
|
|
||||||
|
// r4 += a1×b3
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
MULQ 24(BX)
|
||||||
|
ADDQ AX, R15
|
||||||
|
ADCQ DX, R14
|
||||||
|
|
||||||
|
// r4 += a2×b2
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
MULQ 16(BX)
|
||||||
|
ADDQ AX, R15
|
||||||
|
ADCQ DX, R14
|
||||||
|
|
||||||
|
// r4 += a3×b1
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
MULQ 8(BX)
|
||||||
|
ADDQ AX, R15
|
||||||
|
ADCQ DX, R14
|
||||||
|
|
||||||
|
// r4 += a4×b0
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
MULQ (BX)
|
||||||
|
ADDQ AX, R15
|
||||||
|
ADCQ DX, R14
|
||||||
|
|
||||||
|
// First reduction chain
|
||||||
|
MOVQ $0x0007ffffffffffff, AX
|
||||||
|
SHLQ $0x0d, DI, SI
|
||||||
|
SHLQ $0x0d, R9, R8
|
||||||
|
SHLQ $0x0d, R11, R10
|
||||||
|
SHLQ $0x0d, R13, R12
|
||||||
|
SHLQ $0x0d, R15, R14
|
||||||
|
ANDQ AX, DI
|
||||||
|
IMUL3Q $0x13, R14, R14
|
||||||
|
ADDQ R14, DI
|
||||||
|
ANDQ AX, R9
|
||||||
|
ADDQ SI, R9
|
||||||
|
ANDQ AX, R11
|
||||||
|
ADDQ R8, R11
|
||||||
|
ANDQ AX, R13
|
||||||
|
ADDQ R10, R13
|
||||||
|
ANDQ AX, R15
|
||||||
|
ADDQ R12, R15
|
||||||
|
|
||||||
|
// Second reduction chain (carryPropagate)
|
||||||
|
MOVQ DI, SI
|
||||||
|
SHRQ $0x33, SI
|
||||||
|
MOVQ R9, R8
|
||||||
|
SHRQ $0x33, R8
|
||||||
|
MOVQ R11, R10
|
||||||
|
SHRQ $0x33, R10
|
||||||
|
MOVQ R13, R12
|
||||||
|
SHRQ $0x33, R12
|
||||||
|
MOVQ R15, R14
|
||||||
|
SHRQ $0x33, R14
|
||||||
|
ANDQ AX, DI
|
||||||
|
IMUL3Q $0x13, R14, R14
|
||||||
|
ADDQ R14, DI
|
||||||
|
ANDQ AX, R9
|
||||||
|
ADDQ SI, R9
|
||||||
|
ANDQ AX, R11
|
||||||
|
ADDQ R8, R11
|
||||||
|
ANDQ AX, R13
|
||||||
|
ADDQ R10, R13
|
||||||
|
ANDQ AX, R15
|
||||||
|
ADDQ R12, R15
|
||||||
|
|
||||||
|
// Store output
|
||||||
|
MOVQ out+0(FP), AX
|
||||||
|
MOVQ DI, (AX)
|
||||||
|
MOVQ R9, 8(AX)
|
||||||
|
MOVQ R11, 16(AX)
|
||||||
|
MOVQ R13, 24(AX)
|
||||||
|
MOVQ R15, 32(AX)
|
||||||
|
RET
|
||||||
|
|
||||||
|
// func feSquare(out *Element, a *Element)
|
||||||
|
TEXT ·feSquare(SB), NOSPLIT, $0-16
|
||||||
|
MOVQ a+8(FP), CX
|
||||||
|
|
||||||
|
// r0 = l0×l0
|
||||||
|
MOVQ (CX), AX
|
||||||
|
MULQ (CX)
|
||||||
|
MOVQ AX, SI
|
||||||
|
MOVQ DX, BX
|
||||||
|
|
||||||
|
// r0 += 38×l1×l4
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
IMUL3Q $0x26, AX, AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX, SI
|
||||||
|
ADCQ DX, BX
|
||||||
|
|
||||||
|
// r0 += 38×l2×l3
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
IMUL3Q $0x26, AX, AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
ADDQ AX, SI
|
||||||
|
ADCQ DX, BX
|
||||||
|
|
||||||
|
// r1 = 2×l0×l1
|
||||||
|
MOVQ (CX), AX
|
||||||
|
SHLQ $0x01, AX
|
||||||
|
MULQ 8(CX)
|
||||||
|
MOVQ AX, R8
|
||||||
|
MOVQ DX, DI
|
||||||
|
|
||||||
|
// r1 += 38×l2×l4
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
IMUL3Q $0x26, AX, AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX, R8
|
||||||
|
ADCQ DX, DI
|
||||||
|
|
||||||
|
// r1 += 19×l3×l3
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
ADDQ AX, R8
|
||||||
|
ADCQ DX, DI
|
||||||
|
|
||||||
|
// r2 = 2×l0×l2
|
||||||
|
MOVQ (CX), AX
|
||||||
|
SHLQ $0x01, AX
|
||||||
|
MULQ 16(CX)
|
||||||
|
MOVQ AX, R10
|
||||||
|
MOVQ DX, R9
|
||||||
|
|
||||||
|
// r2 += l1×l1
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
MULQ 8(CX)
|
||||||
|
ADDQ AX, R10
|
||||||
|
ADCQ DX, R9
|
||||||
|
|
||||||
|
// r2 += 38×l3×l4
|
||||||
|
MOVQ 24(CX), AX
|
||||||
|
IMUL3Q $0x26, AX, AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX, R10
|
||||||
|
ADCQ DX, R9
|
||||||
|
|
||||||
|
// r3 = 2×l0×l3
|
||||||
|
MOVQ (CX), AX
|
||||||
|
SHLQ $0x01, AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
MOVQ AX, R12
|
||||||
|
MOVQ DX, R11
|
||||||
|
|
||||||
|
// r3 += 2×l1×l2
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
IMUL3Q $0x02, AX, AX
|
||||||
|
MULQ 16(CX)
|
||||||
|
ADDQ AX, R12
|
||||||
|
ADCQ DX, R11
|
||||||
|
|
||||||
|
// r3 += 19×l4×l4
|
||||||
|
MOVQ 32(CX), AX
|
||||||
|
IMUL3Q $0x13, AX, AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
ADDQ AX, R12
|
||||||
|
ADCQ DX, R11
|
||||||
|
|
||||||
|
// r4 = 2×l0×l4
|
||||||
|
MOVQ (CX), AX
|
||||||
|
SHLQ $0x01, AX
|
||||||
|
MULQ 32(CX)
|
||||||
|
MOVQ AX, R14
|
||||||
|
MOVQ DX, R13
|
||||||
|
|
||||||
|
// r4 += 2×l1×l3
|
||||||
|
MOVQ 8(CX), AX
|
||||||
|
IMUL3Q $0x02, AX, AX
|
||||||
|
MULQ 24(CX)
|
||||||
|
ADDQ AX, R14
|
||||||
|
ADCQ DX, R13
|
||||||
|
|
||||||
|
// r4 += l2×l2
|
||||||
|
MOVQ 16(CX), AX
|
||||||
|
MULQ 16(CX)
|
||||||
|
ADDQ AX, R14
|
||||||
|
ADCQ DX, R13
|
||||||
|
|
||||||
|
// First reduction chain
|
||||||
|
MOVQ $0x0007ffffffffffff, AX
|
||||||
|
SHLQ $0x0d, SI, BX
|
||||||
|
SHLQ $0x0d, R8, DI
|
||||||
|
SHLQ $0x0d, R10, R9
|
||||||
|
SHLQ $0x0d, R12, R11
|
||||||
|
SHLQ $0x0d, R14, R13
|
||||||
|
ANDQ AX, SI
|
||||||
|
IMUL3Q $0x13, R13, R13
|
||||||
|
ADDQ R13, SI
|
||||||
|
ANDQ AX, R8
|
||||||
|
ADDQ BX, R8
|
||||||
|
ANDQ AX, R10
|
||||||
|
ADDQ DI, R10
|
||||||
|
ANDQ AX, R12
|
||||||
|
ADDQ R9, R12
|
||||||
|
ANDQ AX, R14
|
||||||
|
ADDQ R11, R14
|
||||||
|
|
||||||
|
// Second reduction chain (carryPropagate)
|
||||||
|
MOVQ SI, BX
|
||||||
|
SHRQ $0x33, BX
|
||||||
|
MOVQ R8, DI
|
||||||
|
SHRQ $0x33, DI
|
||||||
|
MOVQ R10, R9
|
||||||
|
SHRQ $0x33, R9
|
||||||
|
MOVQ R12, R11
|
||||||
|
SHRQ $0x33, R11
|
||||||
|
MOVQ R14, R13
|
||||||
|
SHRQ $0x33, R13
|
||||||
|
ANDQ AX, SI
|
||||||
|
IMUL3Q $0x13, R13, R13
|
||||||
|
ADDQ R13, SI
|
||||||
|
ANDQ AX, R8
|
||||||
|
ADDQ BX, R8
|
||||||
|
ANDQ AX, R10
|
||||||
|
ADDQ DI, R10
|
||||||
|
ANDQ AX, R12
|
||||||
|
ADDQ R9, R12
|
||||||
|
ANDQ AX, R14
|
||||||
|
ADDQ R11, R14
|
||||||
|
|
||||||
|
// Store output
|
||||||
|
MOVQ out+0(FP), AX
|
||||||
|
MOVQ SI, (AX)
|
||||||
|
MOVQ R8, 8(AX)
|
||||||
|
MOVQ R10, 16(AX)
|
||||||
|
MOVQ R12, 24(AX)
|
||||||
|
MOVQ R14, 32(AX)
|
||||||
|
RET
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !amd64 || !gc || purego
|
||||||
|
// +build !amd64 !gc purego
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
func feMul(v, x, y *Element) { feMulGeneric(v, x, y) }
|
||||||
|
|
||||||
|
func feSquare(v, x *Element) { feSquareGeneric(v, x) }
|
||||||
16
src/crypto/ed25519/internal/edwards25519/field/fe_arm64.go
Normal file
16
src/crypto/ed25519/internal/edwards25519/field/fe_arm64.go
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build arm64 && gc && !purego
|
||||||
|
// +build arm64,gc,!purego
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
//go:noescape
|
||||||
|
func carryPropagate(v *Element)
|
||||||
|
|
||||||
|
func (v *Element) carryPropagate() *Element {
|
||||||
|
carryPropagate(v)
|
||||||
|
return v
|
||||||
|
}
|
||||||
42
src/crypto/ed25519/internal/edwards25519/field/fe_arm64.s
Normal file
42
src/crypto/ed25519/internal/edwards25519/field/fe_arm64.s
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
// Copyright (c) 2020 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// +build arm64,gc,!purego
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// carryPropagate works exactly like carryPropagateGeneric and uses the
|
||||||
|
// same AND, ADD, and LSR+MADD instructions emitted by the compiler, but
|
||||||
|
// avoids loading R0-R4 twice and uses LDP and STP.
|
||||||
|
//
|
||||||
|
// See https://golang.org/issues/43145 for the main compiler issue.
|
||||||
|
//
|
||||||
|
// func carryPropagate(v *Element)
|
||||||
|
TEXT ·carryPropagate(SB),NOFRAME|NOSPLIT,$0-8
|
||||||
|
MOVD v+0(FP), R20
|
||||||
|
|
||||||
|
LDP 0(R20), (R0, R1)
|
||||||
|
LDP 16(R20), (R2, R3)
|
||||||
|
MOVD 32(R20), R4
|
||||||
|
|
||||||
|
AND $0x7ffffffffffff, R0, R10
|
||||||
|
AND $0x7ffffffffffff, R1, R11
|
||||||
|
AND $0x7ffffffffffff, R2, R12
|
||||||
|
AND $0x7ffffffffffff, R3, R13
|
||||||
|
AND $0x7ffffffffffff, R4, R14
|
||||||
|
|
||||||
|
ADD R0>>51, R11, R11
|
||||||
|
ADD R1>>51, R12, R12
|
||||||
|
ADD R2>>51, R13, R13
|
||||||
|
ADD R3>>51, R14, R14
|
||||||
|
// R4>>51 * 19 + R10 -> R10
|
||||||
|
LSR $51, R4, R21
|
||||||
|
MOVD $19, R22
|
||||||
|
MADD R22, R10, R21, R10
|
||||||
|
|
||||||
|
STP (R10, R11), 0(R20)
|
||||||
|
STP (R12, R13), 16(R20)
|
||||||
|
MOVD R14, 32(R20)
|
||||||
|
|
||||||
|
RET
|
||||||
|
|
@ -0,0 +1,12 @@
|
||||||
|
// Copyright (c) 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !arm64 || !gc || purego
|
||||||
|
// +build !arm64 !gc purego
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
func (v *Element) carryPropagate() *Element {
|
||||||
|
return v.carryPropagateGeneric()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func BenchmarkAdd(b *testing.B) {
|
||||||
|
var x, y Element
|
||||||
|
x.One()
|
||||||
|
y.Add(feOne, feOne)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
x.Add(&x, &y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMultiply(b *testing.B) {
|
||||||
|
var x, y Element
|
||||||
|
x.One()
|
||||||
|
y.Add(feOne, feOne)
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
x.Multiply(&x, &y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMult32(b *testing.B) {
|
||||||
|
var x Element
|
||||||
|
x.One()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
x.Mult32(&x, 0xaa42aa42)
|
||||||
|
}
|
||||||
|
}
|
||||||
264
src/crypto/ed25519/internal/edwards25519/field/fe_generic.go
Normal file
264
src/crypto/ed25519/internal/edwards25519/field/fe_generic.go
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
import "math/bits"
|
||||||
|
|
||||||
|
// uint128 holds a 128-bit number as two 64-bit limbs, for use with the
|
||||||
|
// bits.Mul64 and bits.Add64 intrinsics.
|
||||||
|
type uint128 struct {
|
||||||
|
lo, hi uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// mul64 returns a * b.
|
||||||
|
func mul64(a, b uint64) uint128 {
|
||||||
|
hi, lo := bits.Mul64(a, b)
|
||||||
|
return uint128{lo, hi}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addMul64 returns v + a * b.
|
||||||
|
func addMul64(v uint128, a, b uint64) uint128 {
|
||||||
|
hi, lo := bits.Mul64(a, b)
|
||||||
|
lo, c := bits.Add64(lo, v.lo, 0)
|
||||||
|
hi, _ = bits.Add64(hi, v.hi, c)
|
||||||
|
return uint128{lo, hi}
|
||||||
|
}
|
||||||
|
|
||||||
|
// shiftRightBy51 returns a >> 51. a is assumed to be at most 115 bits.
|
||||||
|
func shiftRightBy51(a uint128) uint64 {
|
||||||
|
return (a.hi << (64 - 51)) | (a.lo >> 51)
|
||||||
|
}
|
||||||
|
|
||||||
|
func feMulGeneric(v, a, b *Element) {
|
||||||
|
a0 := a.l0
|
||||||
|
a1 := a.l1
|
||||||
|
a2 := a.l2
|
||||||
|
a3 := a.l3
|
||||||
|
a4 := a.l4
|
||||||
|
|
||||||
|
b0 := b.l0
|
||||||
|
b1 := b.l1
|
||||||
|
b2 := b.l2
|
||||||
|
b3 := b.l3
|
||||||
|
b4 := b.l4
|
||||||
|
|
||||||
|
// Limb multiplication works like pen-and-paper columnar multiplication, but
|
||||||
|
// with 51-bit limbs instead of digits.
|
||||||
|
//
|
||||||
|
// a4 a3 a2 a1 a0 x
|
||||||
|
// b4 b3 b2 b1 b0 =
|
||||||
|
// ------------------------
|
||||||
|
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||||
|
// a4b1 a3b1 a2b1 a1b1 a0b1 +
|
||||||
|
// a4b2 a3b2 a2b2 a1b2 a0b2 +
|
||||||
|
// a4b3 a3b3 a2b3 a1b3 a0b3 +
|
||||||
|
// a4b4 a3b4 a2b4 a1b4 a0b4 =
|
||||||
|
// ----------------------------------------------
|
||||||
|
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||||
|
//
|
||||||
|
// We can then use the reduction identity (a * 2²⁵⁵ + b = a * 19 + b) to
|
||||||
|
// reduce the limbs that would overflow 255 bits. r5 * 2²⁵⁵ becomes 19 * r5,
|
||||||
|
// r6 * 2³⁰⁶ becomes 19 * r6 * 2⁵¹, etc.
|
||||||
|
//
|
||||||
|
// Reduction can be carried out simultaneously to multiplication. For
|
||||||
|
// example, we do not compute r5: whenever the result of a multiplication
|
||||||
|
// belongs to r5, like a1b4, we multiply it by 19 and add the result to r0.
|
||||||
|
//
|
||||||
|
// a4b0 a3b0 a2b0 a1b0 a0b0 +
|
||||||
|
// a3b1 a2b1 a1b1 a0b1 19×a4b1 +
|
||||||
|
// a2b2 a1b2 a0b2 19×a4b2 19×a3b2 +
|
||||||
|
// a1b3 a0b3 19×a4b3 19×a3b3 19×a2b3 +
|
||||||
|
// a0b4 19×a4b4 19×a3b4 19×a2b4 19×a1b4 =
|
||||||
|
// --------------------------------------
|
||||||
|
// r4 r3 r2 r1 r0
|
||||||
|
//
|
||||||
|
// Finally we add up the columns into wide, overlapping limbs.
|
||||||
|
|
||||||
|
a1_19 := a1 * 19
|
||||||
|
a2_19 := a2 * 19
|
||||||
|
a3_19 := a3 * 19
|
||||||
|
a4_19 := a4 * 19
|
||||||
|
|
||||||
|
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||||
|
r0 := mul64(a0, b0)
|
||||||
|
r0 = addMul64(r0, a1_19, b4)
|
||||||
|
r0 = addMul64(r0, a2_19, b3)
|
||||||
|
r0 = addMul64(r0, a3_19, b2)
|
||||||
|
r0 = addMul64(r0, a4_19, b1)
|
||||||
|
|
||||||
|
// r1 = a0×b1 + a1×b0 + 19×(a2×b4 + a3×b3 + a4×b2)
|
||||||
|
r1 := mul64(a0, b1)
|
||||||
|
r1 = addMul64(r1, a1, b0)
|
||||||
|
r1 = addMul64(r1, a2_19, b4)
|
||||||
|
r1 = addMul64(r1, a3_19, b3)
|
||||||
|
r1 = addMul64(r1, a4_19, b2)
|
||||||
|
|
||||||
|
// r2 = a0×b2 + a1×b1 + a2×b0 + 19×(a3×b4 + a4×b3)
|
||||||
|
r2 := mul64(a0, b2)
|
||||||
|
r2 = addMul64(r2, a1, b1)
|
||||||
|
r2 = addMul64(r2, a2, b0)
|
||||||
|
r2 = addMul64(r2, a3_19, b4)
|
||||||
|
r2 = addMul64(r2, a4_19, b3)
|
||||||
|
|
||||||
|
// r3 = a0×b3 + a1×b2 + a2×b1 + a3×b0 + 19×a4×b4
|
||||||
|
r3 := mul64(a0, b3)
|
||||||
|
r3 = addMul64(r3, a1, b2)
|
||||||
|
r3 = addMul64(r3, a2, b1)
|
||||||
|
r3 = addMul64(r3, a3, b0)
|
||||||
|
r3 = addMul64(r3, a4_19, b4)
|
||||||
|
|
||||||
|
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||||
|
r4 := mul64(a0, b4)
|
||||||
|
r4 = addMul64(r4, a1, b3)
|
||||||
|
r4 = addMul64(r4, a2, b2)
|
||||||
|
r4 = addMul64(r4, a3, b1)
|
||||||
|
r4 = addMul64(r4, a4, b0)
|
||||||
|
|
||||||
|
// After the multiplication, we need to reduce (carry) the five coefficients
|
||||||
|
// to obtain a result with limbs that are at most slightly larger than 2⁵¹,
|
||||||
|
// to respect the Element invariant.
|
||||||
|
//
|
||||||
|
// Overall, the reduction works the same as carryPropagate, except with
|
||||||
|
// wider inputs: we take the carry for each coefficient by shifting it right
|
||||||
|
// by 51, and add it to the limb above it. The top carry is multiplied by 19
|
||||||
|
// according to the reduction identity and added to the lowest limb.
|
||||||
|
//
|
||||||
|
// The largest coefficient (r0) will be at most 111 bits, which guarantees
|
||||||
|
// that all carries are at most 111 - 51 = 60 bits, which fits in a uint64.
|
||||||
|
//
|
||||||
|
// r0 = a0×b0 + 19×(a1×b4 + a2×b3 + a3×b2 + a4×b1)
|
||||||
|
// r0 < 2⁵²×2⁵² + 19×(2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵² + 2⁵²×2⁵²)
|
||||||
|
// r0 < (1 + 19 × 4) × 2⁵² × 2⁵²
|
||||||
|
// r0 < 2⁷ × 2⁵² × 2⁵²
|
||||||
|
// r0 < 2¹¹¹
|
||||||
|
//
|
||||||
|
// Moreover, the top coefficient (r4) is at most 107 bits, so c4 is at most
|
||||||
|
// 56 bits, and c4 * 19 is at most 61 bits, which again fits in a uint64 and
|
||||||
|
// allows us to easily apply the reduction identity.
|
||||||
|
//
|
||||||
|
// r4 = a0×b4 + a1×b3 + a2×b2 + a3×b1 + a4×b0
|
||||||
|
// r4 < 5 × 2⁵² × 2⁵²
|
||||||
|
// r4 < 2¹⁰⁷
|
||||||
|
//
|
||||||
|
|
||||||
|
c0 := shiftRightBy51(r0)
|
||||||
|
c1 := shiftRightBy51(r1)
|
||||||
|
c2 := shiftRightBy51(r2)
|
||||||
|
c3 := shiftRightBy51(r3)
|
||||||
|
c4 := shiftRightBy51(r4)
|
||||||
|
|
||||||
|
rr0 := r0.lo&maskLow51Bits + c4*19
|
||||||
|
rr1 := r1.lo&maskLow51Bits + c0
|
||||||
|
rr2 := r2.lo&maskLow51Bits + c1
|
||||||
|
rr3 := r3.lo&maskLow51Bits + c2
|
||||||
|
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,
|
||||||
|
// where the carries will be small enough to fit in the wiggle room above 2⁵¹.
|
||||||
|
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
||||||
|
v.carryPropagate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func feSquareGeneric(v, a *Element) {
|
||||||
|
l0 := a.l0
|
||||||
|
l1 := a.l1
|
||||||
|
l2 := a.l2
|
||||||
|
l3 := a.l3
|
||||||
|
l4 := a.l4
|
||||||
|
|
||||||
|
// Squaring works precisely like multiplication above, but thanks to its
|
||||||
|
// symmetry we get to group a few terms together.
|
||||||
|
//
|
||||||
|
// l4 l3 l2 l1 l0 x
|
||||||
|
// l4 l3 l2 l1 l0 =
|
||||||
|
// ------------------------
|
||||||
|
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||||
|
// l4l1 l3l1 l2l1 l1l1 l0l1 +
|
||||||
|
// l4l2 l3l2 l2l2 l1l2 l0l2 +
|
||||||
|
// l4l3 l3l3 l2l3 l1l3 l0l3 +
|
||||||
|
// l4l4 l3l4 l2l4 l1l4 l0l4 =
|
||||||
|
// ----------------------------------------------
|
||||||
|
// r8 r7 r6 r5 r4 r3 r2 r1 r0
|
||||||
|
//
|
||||||
|
// l4l0 l3l0 l2l0 l1l0 l0l0 +
|
||||||
|
// l3l1 l2l1 l1l1 l0l1 19×l4l1 +
|
||||||
|
// l2l2 l1l2 l0l2 19×l4l2 19×l3l2 +
|
||||||
|
// l1l3 l0l3 19×l4l3 19×l3l3 19×l2l3 +
|
||||||
|
// l0l4 19×l4l4 19×l3l4 19×l2l4 19×l1l4 =
|
||||||
|
// --------------------------------------
|
||||||
|
// r4 r3 r2 r1 r0
|
||||||
|
//
|
||||||
|
// With precomputed 2×, 19×, and 2×19× terms, we can compute each limb with
|
||||||
|
// only three Mul64 and four Add64, instead of five and eight.
|
||||||
|
|
||||||
|
l0_2 := l0 * 2
|
||||||
|
l1_2 := l1 * 2
|
||||||
|
|
||||||
|
l1_38 := l1 * 38
|
||||||
|
l2_38 := l2 * 38
|
||||||
|
l3_38 := l3 * 38
|
||||||
|
|
||||||
|
l3_19 := l3 * 19
|
||||||
|
l4_19 := l4 * 19
|
||||||
|
|
||||||
|
// r0 = l0×l0 + 19×(l1×l4 + l2×l3 + l3×l2 + l4×l1) = l0×l0 + 19×2×(l1×l4 + l2×l3)
|
||||||
|
r0 := mul64(l0, l0)
|
||||||
|
r0 = addMul64(r0, l1_38, l4)
|
||||||
|
r0 = addMul64(r0, l2_38, l3)
|
||||||
|
|
||||||
|
// r1 = l0×l1 + l1×l0 + 19×(l2×l4 + l3×l3 + l4×l2) = 2×l0×l1 + 19×2×l2×l4 + 19×l3×l3
|
||||||
|
r1 := mul64(l0_2, l1)
|
||||||
|
r1 = addMul64(r1, l2_38, l4)
|
||||||
|
r1 = addMul64(r1, l3_19, l3)
|
||||||
|
|
||||||
|
// r2 = l0×l2 + l1×l1 + l2×l0 + 19×(l3×l4 + l4×l3) = 2×l0×l2 + l1×l1 + 19×2×l3×l4
|
||||||
|
r2 := mul64(l0_2, l2)
|
||||||
|
r2 = addMul64(r2, l1, l1)
|
||||||
|
r2 = addMul64(r2, l3_38, l4)
|
||||||
|
|
||||||
|
// r3 = l0×l3 + l1×l2 + l2×l1 + l3×l0 + 19×l4×l4 = 2×l0×l3 + 2×l1×l2 + 19×l4×l4
|
||||||
|
r3 := mul64(l0_2, l3)
|
||||||
|
r3 = addMul64(r3, l1_2, l2)
|
||||||
|
r3 = addMul64(r3, l4_19, l4)
|
||||||
|
|
||||||
|
// r4 = l0×l4 + l1×l3 + l2×l2 + l3×l1 + l4×l0 = 2×l0×l4 + 2×l1×l3 + l2×l2
|
||||||
|
r4 := mul64(l0_2, l4)
|
||||||
|
r4 = addMul64(r4, l1_2, l3)
|
||||||
|
r4 = addMul64(r4, l2, l2)
|
||||||
|
|
||||||
|
c0 := shiftRightBy51(r0)
|
||||||
|
c1 := shiftRightBy51(r1)
|
||||||
|
c2 := shiftRightBy51(r2)
|
||||||
|
c3 := shiftRightBy51(r3)
|
||||||
|
c4 := shiftRightBy51(r4)
|
||||||
|
|
||||||
|
rr0 := r0.lo&maskLow51Bits + c4*19
|
||||||
|
rr1 := r1.lo&maskLow51Bits + c0
|
||||||
|
rr2 := r2.lo&maskLow51Bits + c1
|
||||||
|
rr3 := r3.lo&maskLow51Bits + c2
|
||||||
|
rr4 := r4.lo&maskLow51Bits + c3
|
||||||
|
|
||||||
|
*v = Element{rr0, rr1, rr2, rr3, rr4}
|
||||||
|
v.carryPropagate()
|
||||||
|
}
|
||||||
|
|
||||||
|
// carryPropagate 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
|
||||||
|
c1 := v.l1 >> 51
|
||||||
|
c2 := v.l2 >> 51
|
||||||
|
c3 := v.l3 >> 51
|
||||||
|
c4 := v.l4 >> 51
|
||||||
|
|
||||||
|
v.l0 = v.l0&maskLow51Bits + c4*19
|
||||||
|
v.l1 = v.l1&maskLow51Bits + c0
|
||||||
|
v.l2 = v.l2&maskLow51Bits + c1
|
||||||
|
v.l3 = v.l3&maskLow51Bits + c2
|
||||||
|
v.l4 = v.l4&maskLow51Bits + c3
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
558
src/crypto/ed25519/internal/edwards25519/field/fe_test.go
Normal file
558
src/crypto/ed25519/internal/edwards25519/field/fe_test.go
Normal file
|
|
@ -0,0 +1,558 @@
|
||||||
|
// Copyright (c) 2017 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package field
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"io"
|
||||||
|
"math/big"
|
||||||
|
"math/bits"
|
||||||
|
mathrand "math/rand"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"testing/quick"
|
||||||
|
)
|
||||||
|
|
||||||
|
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}
|
||||||
|
|
||||||
|
func generateFieldElement(rand *mathrand.Rand) Element {
|
||||||
|
const maskLow52Bits = (1 << 52) - 1
|
||||||
|
return Element{
|
||||||
|
rand.Uint64() & maskLow52Bits,
|
||||||
|
rand.Uint64() & maskLow52Bits,
|
||||||
|
rand.Uint64() & maskLow52Bits,
|
||||||
|
rand.Uint64() & maskLow52Bits,
|
||||||
|
rand.Uint64() & maskLow52Bits,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// weirdLimbs can be combined to generate a range of edge-case field elements.
|
||||||
|
// 0 and -1 are intentionally more weighted, as they combine well.
|
||||||
|
var (
|
||||||
|
weirdLimbs51 = []uint64{
|
||||||
|
0, 0, 0, 0,
|
||||||
|
1,
|
||||||
|
19 - 1,
|
||||||
|
19,
|
||||||
|
0x2aaaaaaaaaaaa,
|
||||||
|
0x5555555555555,
|
||||||
|
(1 << 51) - 20,
|
||||||
|
(1 << 51) - 19,
|
||||||
|
(1 << 51) - 1, (1 << 51) - 1,
|
||||||
|
(1 << 51) - 1, (1 << 51) - 1,
|
||||||
|
}
|
||||||
|
weirdLimbs52 = []uint64{
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
1,
|
||||||
|
19 - 1,
|
||||||
|
19,
|
||||||
|
0x2aaaaaaaaaaaa,
|
||||||
|
0x5555555555555,
|
||||||
|
(1 << 51) - 20,
|
||||||
|
(1 << 51) - 19,
|
||||||
|
(1 << 51) - 1, (1 << 51) - 1,
|
||||||
|
(1 << 51) - 1, (1 << 51) - 1,
|
||||||
|
(1 << 51) - 1, (1 << 51) - 1,
|
||||||
|
1 << 51,
|
||||||
|
(1 << 51) + 1,
|
||||||
|
(1 << 52) - 19,
|
||||||
|
(1 << 52) - 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func generateWeirdFieldElement(rand *mathrand.Rand) Element {
|
||||||
|
return Element{
|
||||||
|
weirdLimbs52[rand.Intn(len(weirdLimbs52))],
|
||||||
|
weirdLimbs51[rand.Intn(len(weirdLimbs51))],
|
||||||
|
weirdLimbs51[rand.Intn(len(weirdLimbs51))],
|
||||||
|
weirdLimbs51[rand.Intn(len(weirdLimbs51))],
|
||||||
|
weirdLimbs51[rand.Intn(len(weirdLimbs51))],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (Element) Generate(rand *mathrand.Rand, size int) reflect.Value {
|
||||||
|
if rand.Intn(2) == 0 {
|
||||||
|
return reflect.ValueOf(generateWeirdFieldElement(rand))
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(generateFieldElement(rand))
|
||||||
|
}
|
||||||
|
|
||||||
|
// isInBounds returns whether the element is within the expected bit size bounds
|
||||||
|
// after a light reduction.
|
||||||
|
func isInBounds(x *Element) bool {
|
||||||
|
return bits.Len64(x.l0) <= 52 &&
|
||||||
|
bits.Len64(x.l1) <= 52 &&
|
||||||
|
bits.Len64(x.l2) <= 52 &&
|
||||||
|
bits.Len64(x.l3) <= 52 &&
|
||||||
|
bits.Len64(x.l4) <= 52
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiplyDistributesOverAdd(t *testing.T) {
|
||||||
|
multiplyDistributesOverAdd := func(x, y, z Element) bool {
|
||||||
|
// Compute t1 = (x+y)*z
|
||||||
|
t1 := new(Element)
|
||||||
|
t1.Add(&x, &y)
|
||||||
|
t1.Multiply(t1, &z)
|
||||||
|
|
||||||
|
// Compute t2 = x*z + y*z
|
||||||
|
t2 := new(Element)
|
||||||
|
t3 := new(Element)
|
||||||
|
t2.Multiply(&x, &z)
|
||||||
|
t3.Multiply(&y, &z)
|
||||||
|
t2.Add(t2, t3)
|
||||||
|
|
||||||
|
return t1.Equal(t2) == 1 && isInBounds(t1) && isInBounds(t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quick.Check(multiplyDistributesOverAdd, quickCheckConfig1024); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMul64to128(t *testing.T) {
|
||||||
|
a := uint64(5)
|
||||||
|
b := uint64(5)
|
||||||
|
r := mul64(a, b)
|
||||||
|
if r.lo != 0x19 || r.hi != 0 {
|
||||||
|
t.Errorf("lo-range wide mult failed, got %d + %d*(2**64)", r.lo, r.hi)
|
||||||
|
}
|
||||||
|
|
||||||
|
a = uint64(18014398509481983) // 2^54 - 1
|
||||||
|
b = uint64(18014398509481983) // 2^54 - 1
|
||||||
|
r = mul64(a, b)
|
||||||
|
if r.lo != 0xff80000000000001 || r.hi != 0xfffffffffff {
|
||||||
|
t.Errorf("hi-range wide mult failed, got %d + %d*(2**64)", r.lo, r.hi)
|
||||||
|
}
|
||||||
|
|
||||||
|
a = uint64(1125899906842661)
|
||||||
|
b = uint64(2097155)
|
||||||
|
r = mul64(a, b)
|
||||||
|
r = addMul64(r, a, b)
|
||||||
|
r = addMul64(r, a, b)
|
||||||
|
r = addMul64(r, a, b)
|
||||||
|
r = addMul64(r, a, b)
|
||||||
|
if r.lo != 16888498990613035 || r.hi != 640 {
|
||||||
|
t.Errorf("wrong answer: %d + %d*(2**64)", r.lo, r.hi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetBytesRoundTrip(t *testing.T) {
|
||||||
|
f1 := func(in [32]byte, fe Element) bool {
|
||||||
|
fe.SetBytes(in[:])
|
||||||
|
|
||||||
|
// Mask the most significant bit as it's ignored by SetBytes. (Now
|
||||||
|
// instead of earlier so we check the masking in SetBytes is working.)
|
||||||
|
in[len(in)-1] &= (1 << 7) - 1
|
||||||
|
|
||||||
|
return bytes.Equal(in[:], fe.Bytes()) && isInBounds(&fe)
|
||||||
|
}
|
||||||
|
if err := quick.Check(f1, nil); err != nil {
|
||||||
|
t.Errorf("failed bytes->FE->bytes round-trip: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f2 := func(fe, r Element) bool {
|
||||||
|
r.SetBytes(fe.Bytes())
|
||||||
|
|
||||||
|
// Intentionally not using Equal not to go through Bytes again.
|
||||||
|
// Calling reduce because both Generate and SetBytes can produce
|
||||||
|
// non-canonical representations.
|
||||||
|
fe.reduce()
|
||||||
|
r.reduce()
|
||||||
|
return fe == r
|
||||||
|
}
|
||||||
|
if err := quick.Check(f2, nil); err != nil {
|
||||||
|
t.Errorf("failed FE->bytes->FE round-trip: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check some fixed vectors from dalek
|
||||||
|
type feRTTest struct {
|
||||||
|
fe Element
|
||||||
|
b []byte
|
||||||
|
}
|
||||||
|
var tests = []feRTTest{
|
||||||
|
{
|
||||||
|
fe: Element{358744748052810, 1691584618240980, 977650209285361, 1429865912637724, 560044844278676},
|
||||||
|
b: []byte{74, 209, 69, 197, 70, 70, 161, 222, 56, 226, 229, 19, 112, 60, 25, 92, 187, 74, 222, 56, 50, 153, 51, 233, 40, 74, 57, 6, 160, 185, 213, 31},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fe: Element{84926274344903, 473620666599931, 365590438845504, 1028470286882429, 2146499180330972},
|
||||||
|
b: []byte{199, 23, 106, 112, 61, 77, 216, 79, 186, 60, 11, 118, 13, 16, 103, 15, 42, 32, 83, 250, 44, 57, 204, 198, 78, 199, 253, 119, 146, 172, 3, 122},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
b := tt.fe.Bytes()
|
||||||
|
if !bytes.Equal(b, tt.b) || new(Element).SetBytes(tt.b).Equal(&tt.fe) != 1 {
|
||||||
|
t.Errorf("Failed fixed roundtrip: %v", tt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func swapEndianness(buf []byte) []byte {
|
||||||
|
for i := 0; i < len(buf)/2; i++ {
|
||||||
|
buf[i], buf[len(buf)-i-1] = buf[len(buf)-i-1], buf[i]
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBytesBigEquivalence(t *testing.T) {
|
||||||
|
f1 := func(in [32]byte, fe, fe1 Element) bool {
|
||||||
|
fe.SetBytes(in[:])
|
||||||
|
|
||||||
|
in[len(in)-1] &= (1 << 7) - 1 // mask the most significant bit
|
||||||
|
b := new(big.Int).SetBytes(swapEndianness(in[:]))
|
||||||
|
fe1.fromBig(b)
|
||||||
|
|
||||||
|
if fe != fe1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 32) // pad with zeroes
|
||||||
|
copy(buf, swapEndianness(fe1.toBig().Bytes()))
|
||||||
|
|
||||||
|
return bytes.Equal(fe.Bytes(), buf) && isInBounds(&fe) && isInBounds(&fe1)
|
||||||
|
}
|
||||||
|
if err := quick.Check(f1, nil); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fromBig sets v = n, and returns v. The bit length of n must not exceed 256.
|
||||||
|
func (v *Element) fromBig(n *big.Int) *Element {
|
||||||
|
if n.BitLen() > 32*8 {
|
||||||
|
panic("edwards25519: invalid field element input size")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 0, 32)
|
||||||
|
for _, word := range n.Bits() {
|
||||||
|
for i := 0; i < bits.UintSize; i += 8 {
|
||||||
|
if len(buf) >= cap(buf) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
buf = append(buf, byte(word))
|
||||||
|
word >>= 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.SetBytes(buf[:32])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Element) fromDecimal(s string) *Element {
|
||||||
|
n, ok := new(big.Int).SetString(s, 10)
|
||||||
|
if !ok {
|
||||||
|
panic("not a valid decimal: " + s)
|
||||||
|
}
|
||||||
|
return v.fromBig(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// toBig returns v as a big.Int.
|
||||||
|
func (v *Element) toBig() *big.Int {
|
||||||
|
buf := v.Bytes()
|
||||||
|
|
||||||
|
words := make([]big.Word, 32*8/bits.UintSize)
|
||||||
|
for n := range words {
|
||||||
|
for i := 0; i < bits.UintSize; i += 8 {
|
||||||
|
if len(buf) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
words[n] |= big.Word(buf[0]) << big.Word(i)
|
||||||
|
buf = buf[1:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new(big.Int).SetBits(words)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecimalConstants(t *testing.T) {
|
||||||
|
sqrtM1String := "19681161376707505956807079304988542015446066515923890162744021073123829784752"
|
||||||
|
if exp := new(Element).fromDecimal(sqrtM1String); sqrtM1.Equal(exp) != 1 {
|
||||||
|
t.Errorf("sqrtM1 is %v, expected %v", sqrtM1, exp)
|
||||||
|
}
|
||||||
|
// d is in the parent package, and we don't want to expose d or fromDecimal.
|
||||||
|
// dString := "37095705934669439343138083508754565189542113879843219016388785533085940283555"
|
||||||
|
// if exp := new(Element).fromDecimal(dString); d.Equal(exp) != 1 {
|
||||||
|
// t.Errorf("d is %v, expected %v", d, exp)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetBytesRoundTripEdgeCases(t *testing.T) {
|
||||||
|
// TODO: values close to 0, close to 2^255-19, between 2^255-19 and 2^255-1,
|
||||||
|
// and between 2^255 and 2^256-1. Test both the documented SetBytes
|
||||||
|
// behavior, and that Bytes reduces them.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests self-consistency between Multiply and Square.
|
||||||
|
func TestConsistency(t *testing.T) {
|
||||||
|
var x Element
|
||||||
|
var x2, x2sq Element
|
||||||
|
|
||||||
|
x = Element{1, 1, 1, 1, 1}
|
||||||
|
x2.Multiply(&x, &x)
|
||||||
|
x2sq.Square(&x)
|
||||||
|
|
||||||
|
if x2 != x2sq {
|
||||||
|
t.Fatalf("all ones failed\nmul: %x\nsqr: %x\n", x2, x2sq)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes [32]byte
|
||||||
|
|
||||||
|
_, err := io.ReadFull(rand.Reader, bytes[:])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
x.SetBytes(bytes[:])
|
||||||
|
|
||||||
|
x2.Multiply(&x, &x)
|
||||||
|
x2sq.Square(&x)
|
||||||
|
|
||||||
|
if x2 != x2sq {
|
||||||
|
t.Fatalf("all ones failed\nmul: %x\nsqr: %x\n", x2, x2sq)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqual(t *testing.T) {
|
||||||
|
x := Element{1, 1, 1, 1, 1}
|
||||||
|
y := Element{5, 4, 3, 2, 1}
|
||||||
|
|
||||||
|
eq := x.Equal(&x)
|
||||||
|
if eq != 1 {
|
||||||
|
t.Errorf("wrong about equality")
|
||||||
|
}
|
||||||
|
|
||||||
|
eq = x.Equal(&y)
|
||||||
|
if eq != 0 {
|
||||||
|
t.Errorf("wrong about inequality")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvert(t *testing.T) {
|
||||||
|
x := Element{1, 1, 1, 1, 1}
|
||||||
|
one := Element{1, 0, 0, 0, 0}
|
||||||
|
var xinv, r Element
|
||||||
|
|
||||||
|
xinv.Invert(&x)
|
||||||
|
r.Multiply(&x, &xinv)
|
||||||
|
r.reduce()
|
||||||
|
|
||||||
|
if one != r {
|
||||||
|
t.Errorf("inversion identity failed, got: %x", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes [32]byte
|
||||||
|
|
||||||
|
_, err := io.ReadFull(rand.Reader, bytes[:])
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
x.SetBytes(bytes[:])
|
||||||
|
|
||||||
|
xinv.Invert(&x)
|
||||||
|
r.Multiply(&x, &xinv)
|
||||||
|
r.reduce()
|
||||||
|
|
||||||
|
if one != r {
|
||||||
|
t.Errorf("random inversion identity failed, got: %x for field element %x", r, x)
|
||||||
|
}
|
||||||
|
|
||||||
|
zero := Element{}
|
||||||
|
x.Set(&zero)
|
||||||
|
if xx := xinv.Invert(&x); xx != &xinv {
|
||||||
|
t.Errorf("inverting zero did not return the receiver")
|
||||||
|
} else if xinv.Equal(&zero) != 1 {
|
||||||
|
t.Errorf("inverting zero did not return zero")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSelectSwap(t *testing.T) {
|
||||||
|
a := Element{358744748052810, 1691584618240980, 977650209285361, 1429865912637724, 560044844278676}
|
||||||
|
b := Element{84926274344903, 473620666599931, 365590438845504, 1028470286882429, 2146499180330972}
|
||||||
|
|
||||||
|
var c, d Element
|
||||||
|
|
||||||
|
c.Select(&a, &b, 1)
|
||||||
|
d.Select(&a, &b, 0)
|
||||||
|
|
||||||
|
if c.Equal(&a) != 1 || d.Equal(&b) != 1 {
|
||||||
|
t.Errorf("Select failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Swap(&d, 0)
|
||||||
|
|
||||||
|
if c.Equal(&a) != 1 || d.Equal(&b) != 1 {
|
||||||
|
t.Errorf("Swap failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Swap(&d, 1)
|
||||||
|
|
||||||
|
if c.Equal(&b) != 1 || d.Equal(&a) != 1 {
|
||||||
|
t.Errorf("Swap failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMult32(t *testing.T) {
|
||||||
|
mult32EquivalentToMul := func(x Element, y uint32) bool {
|
||||||
|
t1 := new(Element)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
t1.Mult32(&x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
ty := new(Element)
|
||||||
|
ty.l0 = uint64(y)
|
||||||
|
|
||||||
|
t2 := new(Element)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
t2.Multiply(&x, ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
return t1.Equal(t2) == 1 && isInBounds(t1) && isInBounds(t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quick.Check(mult32EquivalentToMul, quickCheckConfig1024); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSqrtRatio(t *testing.T) {
|
||||||
|
// From draft-irtf-cfrg-ristretto255-decaf448-00, Appendix A.4.
|
||||||
|
type test struct {
|
||||||
|
u, v string
|
||||||
|
wasSquare int
|
||||||
|
r string
|
||||||
|
}
|
||||||
|
var tests = []test{
|
||||||
|
// If u is 0, the function is defined to return (0, TRUE), even if v
|
||||||
|
// is zero. Note that where used in this package, the denominator v
|
||||||
|
// is never zero.
|
||||||
|
{
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
1, "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
// 0/1 == 0²
|
||||||
|
{
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
1, "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
// If u is non-zero and v is zero, defined to return (0, FALSE).
|
||||||
|
{
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
0, "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
// 2/1 is not square in this field.
|
||||||
|
{
|
||||||
|
"0200000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
0, "3c5ff1b5d8e4113b871bd052f9e7bcd0582804c266ffb2d4f4203eb07fdb7c54",
|
||||||
|
},
|
||||||
|
// 4/1 == 2²
|
||||||
|
{
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
1, "0200000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
},
|
||||||
|
// 1/4 == (2⁻¹)² == (2^(p-2))² per Euler's theorem
|
||||||
|
{
|
||||||
|
"0100000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"0400000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
1, "f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tt := range tests {
|
||||||
|
u := new(Element).SetBytes(decodeHex(tt.u))
|
||||||
|
v := new(Element).SetBytes(decodeHex(tt.v))
|
||||||
|
want := new(Element).SetBytes(decodeHex(tt.r))
|
||||||
|
got, wasSquare := new(Element).SqrtRatio(u, v)
|
||||||
|
if got.Equal(want) == 0 || wasSquare != tt.wasSquare {
|
||||||
|
t.Errorf("%d: got (%v, %v), want (%v, %v)", i, got, wasSquare, want, tt.wasSquare)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCarryPropagate(t *testing.T) {
|
||||||
|
asmLikeGeneric := func(a [5]uint64) bool {
|
||||||
|
t1 := &Element{a[0], a[1], a[2], a[3], a[4]}
|
||||||
|
t2 := &Element{a[0], a[1], a[2], a[3], a[4]}
|
||||||
|
|
||||||
|
t1.carryPropagate()
|
||||||
|
t2.carryPropagateGeneric()
|
||||||
|
|
||||||
|
if *t1 != *t2 {
|
||||||
|
t.Logf("got: %#v,\nexpected: %#v", t1, t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
return *t1 == *t2 && isInBounds(t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quick.Check(asmLikeGeneric, quickCheckConfig1024); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !asmLikeGeneric([5]uint64{0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}) {
|
||||||
|
t.Errorf("failed for {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFeSquare(t *testing.T) {
|
||||||
|
asmLikeGeneric := func(a Element) bool {
|
||||||
|
t1 := a
|
||||||
|
t2 := a
|
||||||
|
|
||||||
|
feSquareGeneric(&t1, &t1)
|
||||||
|
feSquare(&t2, &t2)
|
||||||
|
|
||||||
|
if t1 != t2 {
|
||||||
|
t.Logf("got: %#v,\nexpected: %#v", t1, t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
return t1 == t2 && isInBounds(&t2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quick.Check(asmLikeGeneric, quickCheckConfig1024); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFeMul(t *testing.T) {
|
||||||
|
asmLikeGeneric := func(a, b Element) bool {
|
||||||
|
a1 := a
|
||||||
|
a2 := a
|
||||||
|
b1 := b
|
||||||
|
b2 := b
|
||||||
|
|
||||||
|
feMulGeneric(&a1, &a1, &b1)
|
||||||
|
feMul(&a2, &a2, &b2)
|
||||||
|
|
||||||
|
if a1 != a2 || b1 != b2 {
|
||||||
|
t.Logf("got: %#v,\nexpected: %#v", a1, a2)
|
||||||
|
t.Logf("got: %#v,\nexpected: %#v", b1, b2)
|
||||||
|
}
|
||||||
|
|
||||||
|
return a1 == a2 && isInBounds(&a2) &&
|
||||||
|
b1 == b2 && isInBounds(&b2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quick.Check(asmLikeGeneric, quickCheckConfig1024); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeHex(s string) []byte {
|
||||||
|
b, err := hex.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
1025
src/crypto/ed25519/internal/edwards25519/scalar.go
Normal file
1025
src/crypto/ed25519/internal/edwards25519/scalar.go
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,93 @@
|
||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"testing/quick"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScalarAliasing(t *testing.T) {
|
||||||
|
checkAliasingOneArg := func(f func(v, x *Scalar) *Scalar, v, x Scalar) bool {
|
||||||
|
x1, v1 := x, x
|
||||||
|
|
||||||
|
// Calculate a reference f(x) without aliasing.
|
||||||
|
if out := f(&v, &x); out != &v || !isReduced(out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test aliasing the argument and the receiver.
|
||||||
|
if out := f(&v1, &v1); out != &v1 || v1 != v || !isReduced(out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the arguments was not modified.
|
||||||
|
return x == x1
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAliasingTwoArgs := func(f func(v, x, y *Scalar) *Scalar, v, x, y Scalar) bool {
|
||||||
|
x1, y1, v1 := x, y, Scalar{}
|
||||||
|
|
||||||
|
// Calculate a reference f(x, y) without aliasing.
|
||||||
|
if out := f(&v, &x, &y); out != &v || !isReduced(out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test aliasing the first argument and the receiver.
|
||||||
|
v1 = x
|
||||||
|
if out := f(&v1, &v1, &y); out != &v1 || v1 != v || !isReduced(out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Test aliasing the second argument and the receiver.
|
||||||
|
v1 = y
|
||||||
|
if out := f(&v1, &x, &v1); out != &v1 || v1 != v || !isReduced(out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate a reference f(x, x) without aliasing.
|
||||||
|
if out := f(&v, &x, &x); out != &v || !isReduced(out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test aliasing the first argument and the receiver.
|
||||||
|
v1 = x
|
||||||
|
if out := f(&v1, &v1, &x); out != &v1 || v1 != v || !isReduced(out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Test aliasing the second argument and the receiver.
|
||||||
|
v1 = x
|
||||||
|
if out := f(&v1, &x, &v1); out != &v1 || v1 != v || !isReduced(out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Test aliasing both arguments and the receiver.
|
||||||
|
v1 = x
|
||||||
|
if out := f(&v1, &v1, &v1); out != &v1 || v1 != v || !isReduced(out) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the arguments were not modified.
|
||||||
|
return x == x1 && y == y1
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, f := range map[string]interface{}{
|
||||||
|
"Negate": func(v, x Scalar) bool {
|
||||||
|
return checkAliasingOneArg((*Scalar).Negate, v, x)
|
||||||
|
},
|
||||||
|
"Multiply": func(v, x, y Scalar) bool {
|
||||||
|
return checkAliasingTwoArgs((*Scalar).Multiply, v, x, y)
|
||||||
|
},
|
||||||
|
"Add": func(v, x, y Scalar) bool {
|
||||||
|
return checkAliasingTwoArgs((*Scalar).Add, v, x, y)
|
||||||
|
},
|
||||||
|
"Subtract": func(v, x, y Scalar) bool {
|
||||||
|
return checkAliasingTwoArgs((*Scalar).Subtract, v, x, y)
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
err := quick.Check(f, &quick.Config{MaxCountScale: 1 << 5})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v: %v", name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
233
src/crypto/ed25519/internal/edwards25519/scalar_test.go
Normal file
233
src/crypto/ed25519/internal/edwards25519/scalar_test.go
Normal file
|
|
@ -0,0 +1,233 @@
|
||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"math/big"
|
||||||
|
mathrand "math/rand"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"testing/quick"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generate returns a valid (reduced modulo l) Scalar with a distribution
|
||||||
|
// weighted towards high, low, and edge values.
|
||||||
|
func (Scalar) Generate(rand *mathrand.Rand, size int) reflect.Value {
|
||||||
|
s := scZero
|
||||||
|
diceRoll := rand.Intn(100)
|
||||||
|
switch {
|
||||||
|
case diceRoll == 0:
|
||||||
|
case diceRoll == 1:
|
||||||
|
s = scOne
|
||||||
|
case diceRoll == 2:
|
||||||
|
s = scMinusOne
|
||||||
|
case diceRoll < 5:
|
||||||
|
// Generate a low scalar in [0, 2^125).
|
||||||
|
rand.Read(s.s[:16])
|
||||||
|
s.s[15] &= (1 << 5) - 1
|
||||||
|
case diceRoll < 10:
|
||||||
|
// Generate a high scalar in [2^252, 2^252 + 2^124).
|
||||||
|
s.s[31] = 1 << 4
|
||||||
|
rand.Read(s.s[:16])
|
||||||
|
s.s[15] &= (1 << 4) - 1
|
||||||
|
default:
|
||||||
|
// Generate a valid scalar in [0, l) by returning [0, 2^252) which has a
|
||||||
|
// negligibly different distribution (the former has a 2^-127.6 chance
|
||||||
|
// of being out of the latter range).
|
||||||
|
rand.Read(s.s[:])
|
||||||
|
s.s[31] &= (1 << 4) - 1
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
if err := quick.Check(f, quickCheckConfig1024); err != nil {
|
||||||
|
t.Errorf("generated unreduced scalar: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarSetCanonicalBytes(t *testing.T) {
|
||||||
|
f1 := func(in [32]byte, sc Scalar) bool {
|
||||||
|
// Mask out top 4 bits to guarantee value falls in [0, l).
|
||||||
|
in[len(in)-1] &= (1 << 4) - 1
|
||||||
|
if _, err := sc.SetCanonicalBytes(in[:]); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return bytes.Equal(in[:], sc.Bytes()) && isReduced(&sc)
|
||||||
|
}
|
||||||
|
if err := quick.Check(f1, quickCheckConfig1024); err != nil {
|
||||||
|
t.Errorf("failed bytes->scalar->bytes round-trip: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f2 := func(sc1, sc2 Scalar) bool {
|
||||||
|
if _, err := sc2.SetCanonicalBytes(sc1.Bytes()); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return sc1 == sc2
|
||||||
|
}
|
||||||
|
if err := quick.Check(f2, quickCheckConfig1024); err != nil {
|
||||||
|
t.Errorf("failed scalar->bytes->scalar round-trip: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := scMinusOne.s
|
||||||
|
b[31] += 1
|
||||||
|
s := scOne
|
||||||
|
if out, err := s.SetCanonicalBytes(b[:]); err == nil {
|
||||||
|
t.Errorf("SetCanonicalBytes worked on a non-canonical value")
|
||||||
|
} else if s != scOne {
|
||||||
|
t.Errorf("SetCanonicalBytes modified its receiver")
|
||||||
|
} else if out != nil {
|
||||||
|
t.Errorf("SetCanonicalBytes did not return nil with an error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarSetUniformBytes(t *testing.T) {
|
||||||
|
mod, _ := new(big.Int).SetString("27742317777372353535851937790883648493", 10)
|
||||||
|
mod.Add(mod, new(big.Int).Lsh(big.NewInt(1), 252))
|
||||||
|
f := func(in [64]byte, sc Scalar) bool {
|
||||||
|
sc.SetUniformBytes(in[:])
|
||||||
|
if !isReduced(&sc) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
scBig := bigIntFromLittleEndianBytes(sc.s[:])
|
||||||
|
inBig := bigIntFromLittleEndianBytes(in[:])
|
||||||
|
return inBig.Mod(inBig, mod).Cmp(scBig) == 0
|
||||||
|
}
|
||||||
|
if err := quick.Check(f, quickCheckConfig1024); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarSetBytesWithClamping(t *testing.T) {
|
||||||
|
// Generated with libsodium.js 1.0.18 crypto_scalarmult_ed25519_base.
|
||||||
|
|
||||||
|
random := "633d368491364dc9cd4c1bf891b1d59460face1644813240a313e61f2c88216e"
|
||||||
|
s := new(Scalar).SetBytesWithClamping(decodeHex(random))
|
||||||
|
p := new(Point).ScalarBaseMult(s)
|
||||||
|
want := "1d87a9026fd0126a5736fe1628c95dd419172b5b618457e041c9c861b2494a94"
|
||||||
|
if got := hex.EncodeToString(p.Bytes()); got != want {
|
||||||
|
t.Errorf("random: got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
zero := "0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
s = new(Scalar).SetBytesWithClamping(decodeHex(zero))
|
||||||
|
p = new(Point).ScalarBaseMult(s)
|
||||||
|
want = "693e47972caf527c7883ad1b39822f026f47db2ab0e1919955b8993aa04411d1"
|
||||||
|
if got := hex.EncodeToString(p.Bytes()); got != want {
|
||||||
|
t.Errorf("zero: got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
one := "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
|
||||||
|
s = new(Scalar).SetBytesWithClamping(decodeHex(one))
|
||||||
|
p = new(Point).ScalarBaseMult(s)
|
||||||
|
want = "12e9a68b73fd5aacdbcaf3e88c46fea6ebedb1aa84eed1842f07f8edab65e3a7"
|
||||||
|
if got := hex.EncodeToString(p.Bytes()); got != want {
|
||||||
|
t.Errorf("one: got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bigIntFromLittleEndianBytes(b []byte) *big.Int {
|
||||||
|
bb := make([]byte, len(b))
|
||||||
|
for i := range b {
|
||||||
|
bb[i] = b[len(b)-i-1]
|
||||||
|
}
|
||||||
|
return new(big.Int).SetBytes(bb)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarMultiplyDistributesOverAdd(t *testing.T) {
|
||||||
|
multiplyDistributesOverAdd := func(x, y, z Scalar) bool {
|
||||||
|
// Compute t1 = (x+y)*z
|
||||||
|
var t1 Scalar
|
||||||
|
t1.Add(&x, &y)
|
||||||
|
t1.Multiply(&t1, &z)
|
||||||
|
|
||||||
|
// Compute t2 = x*z + y*z
|
||||||
|
var t2 Scalar
|
||||||
|
var t3 Scalar
|
||||||
|
t2.Multiply(&x, &z)
|
||||||
|
t3.Multiply(&y, &z)
|
||||||
|
t2.Add(&t2, &t3)
|
||||||
|
|
||||||
|
return t1 == t2 && isReduced(&t1) && isReduced(&t3)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quick.Check(multiplyDistributesOverAdd, quickCheckConfig1024); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarAddLikeSubNeg(t *testing.T) {
|
||||||
|
addLikeSubNeg := func(x, y Scalar) bool {
|
||||||
|
// Compute t1 = x - y
|
||||||
|
var t1 Scalar
|
||||||
|
t1.Subtract(&x, &y)
|
||||||
|
|
||||||
|
// Compute t2 = -y + x
|
||||||
|
var t2 Scalar
|
||||||
|
t2.Negate(&y)
|
||||||
|
t2.Add(&t2, &x)
|
||||||
|
|
||||||
|
return t1 == t2 && isReduced(&t1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quick.Check(addLikeSubNeg, quickCheckConfig1024); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarNonAdjacentForm(t *testing.T) {
|
||||||
|
s := Scalar{[32]byte{
|
||||||
|
0x1a, 0x0e, 0x97, 0x8a, 0x90, 0xf6, 0x62, 0x2d,
|
||||||
|
0x37, 0x47, 0x02, 0x3f, 0x8a, 0xd8, 0x26, 0x4d,
|
||||||
|
0xa7, 0x58, 0xaa, 0x1b, 0x88, 0xe0, 0x40, 0xd1,
|
||||||
|
0x58, 0x9e, 0x7b, 0x7f, 0x23, 0x76, 0xef, 0x09,
|
||||||
|
}}
|
||||||
|
expectedNaf := [256]int8{
|
||||||
|
0, 13, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, -11, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1,
|
||||||
|
0, 0, 0, 0, 9, 0, 0, 0, 0, -5, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 11, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0,
|
||||||
|
-9, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 9, 0,
|
||||||
|
0, 0, 0, -15, 0, 0, 0, 0, -7, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 13, 0, 0, 0, 0, 0, -3, 0,
|
||||||
|
0, 0, 0, -11, 0, 0, 0, 0, -7, 0, 0, 0, 0, -13, 0, 0, 0, 0, 11, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 1, 0, 0,
|
||||||
|
0, 0, 0, -15, 0, 0, 0, 0, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 13, 0, 0, 0,
|
||||||
|
0, 0, 0, 11, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, -9, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 7,
|
||||||
|
0, 0, 0, 0, 0, -15, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 15, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
sNaf := s.nonAdjacentForm(5)
|
||||||
|
|
||||||
|
for i := 0; i < 256; i++ {
|
||||||
|
if expectedNaf[i] != sNaf[i] {
|
||||||
|
t.Errorf("Wrong digit at position %d, got %d, expected %d", i, sNaf[i], expectedNaf[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type notZeroScalar Scalar
|
||||||
|
|
||||||
|
func (notZeroScalar) Generate(rand *mathrand.Rand, size int) reflect.Value {
|
||||||
|
var s Scalar
|
||||||
|
for s == scZero {
|
||||||
|
s = Scalar{}.Generate(rand, size).Interface().(Scalar)
|
||||||
|
}
|
||||||
|
return reflect.ValueOf(notZeroScalar(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarEqual(t *testing.T) {
|
||||||
|
if scOne.Equal(&scMinusOne) == 1 {
|
||||||
|
t.Errorf("scOne.Equal(&scMinusOne) is true")
|
||||||
|
}
|
||||||
|
if scMinusOne.Equal(&scMinusOne) == 0 {
|
||||||
|
t.Errorf("scMinusOne.Equal(&scMinusOne) is false")
|
||||||
|
}
|
||||||
|
}
|
||||||
214
src/crypto/ed25519/internal/edwards25519/scalarmult.go
Normal file
214
src/crypto/ed25519/internal/edwards25519/scalarmult.go
Normal file
|
|
@ -0,0 +1,214 @@
|
||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// basepointTable is a set of 32 affineLookupTables, where table i is generated
|
||||||
|
// from 256i * basepoint. It is precomputed the first time it's used.
|
||||||
|
func basepointTable() *[32]affineLookupTable {
|
||||||
|
basepointTablePrecomp.initOnce.Do(func() {
|
||||||
|
p := NewGeneratorPoint()
|
||||||
|
for i := 0; i < 32; i++ {
|
||||||
|
basepointTablePrecomp.table[i].FromP3(p)
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
p.Add(p, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return &basepointTablePrecomp.table
|
||||||
|
}
|
||||||
|
|
||||||
|
var basepointTablePrecomp struct {
|
||||||
|
table [32]affineLookupTable
|
||||||
|
initOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScalarBaseMult sets v = x * B, where B is the canonical generator, and
|
||||||
|
// returns v.
|
||||||
|
//
|
||||||
|
// The scalar multiplication is done in constant time.
|
||||||
|
func (v *Point) ScalarBaseMult(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].SelectInto(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].SelectInto(multiple, digits[i])
|
||||||
|
tmp1.AddAffine(v, multiple)
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScalarMult sets v = x * q, and returns v.
|
||||||
|
//
|
||||||
|
// The scalar multiplication is done in constant time.
|
||||||
|
func (v *Point) ScalarMult(x *Scalar, q *Point) *Point {
|
||||||
|
checkInitialized(q)
|
||||||
|
|
||||||
|
var table projLookupTable
|
||||||
|
table.FromP3(q)
|
||||||
|
|
||||||
|
// 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])
|
||||||
|
|
||||||
|
v.Set(NewIdentityPoint())
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// basepointNafTable is the nafLookupTable8 for the basepoint.
|
||||||
|
// It is precomputed the first time it's used.
|
||||||
|
func basepointNafTable() *nafLookupTable8 {
|
||||||
|
basepointNafTablePrecomp.initOnce.Do(func() {
|
||||||
|
basepointNafTablePrecomp.table.FromP3(NewGeneratorPoint())
|
||||||
|
})
|
||||||
|
return &basepointNafTablePrecomp.table
|
||||||
|
}
|
||||||
|
|
||||||
|
var basepointNafTablePrecomp struct {
|
||||||
|
table nafLookupTable8
|
||||||
|
initOnce sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
// VarTimeDoubleScalarBaseMult sets v = a * A + b * B, where B is the canonical
|
||||||
|
// generator, and returns v.
|
||||||
|
//
|
||||||
|
// Execution time depends on the inputs.
|
||||||
|
func (v *Point) VarTimeDoubleScalarBaseMult(a *Scalar, A *Point, b *Scalar) *Point {
|
||||||
|
checkInitialized(A)
|
||||||
|
|
||||||
|
// Similarly to the single variable-base approach, we compute
|
||||||
|
// digits and use them with a lookup table. However, because
|
||||||
|
// we are allowed to do variable-time operations, we don't
|
||||||
|
// need constant-time lookups or constant-time digit
|
||||||
|
// computations.
|
||||||
|
//
|
||||||
|
// So we use a non-adjacent form of some width w instead of
|
||||||
|
// radix 16. This is like a binary representation (one digit
|
||||||
|
// for each binary place) but we allow the digits to grow in
|
||||||
|
// magnitude up to 2^{w-1} so that the nonzero digits are as
|
||||||
|
// sparse as possible. Intuitively, this "condenses" the
|
||||||
|
// "mass" of the scalar onto sparse coefficients (meaning
|
||||||
|
// fewer additions).
|
||||||
|
|
||||||
|
basepointNafTable := basepointNafTable()
|
||||||
|
var aTable nafLookupTable5
|
||||||
|
aTable.FromP3(A)
|
||||||
|
// Because the basepoint is fixed, we can use a wider NAF
|
||||||
|
// corresponding to a bigger table.
|
||||||
|
aNaf := a.nonAdjacentForm(5)
|
||||||
|
bNaf := b.nonAdjacentForm(8)
|
||||||
|
|
||||||
|
// Find the first nonzero coefficient.
|
||||||
|
i := 255
|
||||||
|
for j := i; j >= 0; j-- {
|
||||||
|
if aNaf[j] != 0 || bNaf[j] != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
multA := &projCached{}
|
||||||
|
multB := &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.
|
||||||
|
for ; i >= 0; i-- {
|
||||||
|
tmp1.Double(tmp2)
|
||||||
|
|
||||||
|
// Only update v if we have a nonzero coeff to add in.
|
||||||
|
if aNaf[i] > 0 {
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
aTable.SelectInto(multA, aNaf[i])
|
||||||
|
tmp1.Add(v, multA)
|
||||||
|
} else if aNaf[i] < 0 {
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
aTable.SelectInto(multA, -aNaf[i])
|
||||||
|
tmp1.Sub(v, multA)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bNaf[i] > 0 {
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
basepointNafTable.SelectInto(multB, bNaf[i])
|
||||||
|
tmp1.AddAffine(v, multB)
|
||||||
|
} else if bNaf[i] < 0 {
|
||||||
|
v.fromP1xP1(tmp1)
|
||||||
|
basepointNafTable.SelectInto(multB, -bNaf[i])
|
||||||
|
tmp1.SubAffine(v, multB)
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp2.FromP1xP1(tmp1)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.fromP2(tmp2)
|
||||||
|
return v
|
||||||
|
}
|
||||||
209
src/crypto/ed25519/internal/edwards25519/scalarmult_test.go
Normal file
209
src/crypto/ed25519/internal/edwards25519/scalarmult_test.go
Normal file
|
|
@ -0,0 +1,209 @@
|
||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"testing/quick"
|
||||||
|
)
|
||||||
|
|
||||||
|
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{[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}}
|
||||||
|
// the above, times the edwards25519 basepoint.
|
||||||
|
dalekScalarBasepoint, _ = new(Point).SetBytes([]byte{0xf4, 0xef, 0x7c, 0xa, 0x34, 0x55, 0x7b, 0x9f, 0x72, 0x3b, 0xb6, 0x1e, 0xf9, 0x46, 0x9, 0x91, 0x1c, 0xb9, 0xc0, 0x6c, 0x17, 0x28, 0x2d, 0x8b, 0x43, 0x2b, 0x5, 0x18, 0x6a, 0x54, 0x3e, 0x48})
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestScalarMultSmallScalars(t *testing.T) {
|
||||||
|
var z Scalar
|
||||||
|
var p Point
|
||||||
|
p.ScalarMult(&z, B)
|
||||||
|
if I.Equal(&p) != 1 {
|
||||||
|
t.Error("0*B != 0")
|
||||||
|
}
|
||||||
|
checkOnCurve(t, &p)
|
||||||
|
|
||||||
|
z = 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.ScalarMult(&z, B)
|
||||||
|
if B.Equal(&p) != 1 {
|
||||||
|
t.Error("1*B != 1")
|
||||||
|
}
|
||||||
|
checkOnCurve(t, &p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarMultVsDalek(t *testing.T) {
|
||||||
|
var p Point
|
||||||
|
p.ScalarMult(&dalekScalar, B)
|
||||||
|
if dalekScalarBasepoint.Equal(&p) != 1 {
|
||||||
|
t.Error("Scalar mul does not match dalek")
|
||||||
|
}
|
||||||
|
checkOnCurve(t, &p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBaseMultVsDalek(t *testing.T) {
|
||||||
|
var p Point
|
||||||
|
p.ScalarBaseMult(&dalekScalar)
|
||||||
|
if dalekScalarBasepoint.Equal(&p) != 1 {
|
||||||
|
t.Error("Scalar mul does not match dalek")
|
||||||
|
}
|
||||||
|
checkOnCurve(t, &p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVarTimeDoubleBaseMultVsDalek(t *testing.T) {
|
||||||
|
var p Point
|
||||||
|
var z Scalar
|
||||||
|
p.VarTimeDoubleScalarBaseMult(&dalekScalar, B, &z)
|
||||||
|
if dalekScalarBasepoint.Equal(&p) != 1 {
|
||||||
|
t.Error("VarTimeDoubleScalarBaseMult fails with b=0")
|
||||||
|
}
|
||||||
|
checkOnCurve(t, &p)
|
||||||
|
p.VarTimeDoubleScalarBaseMult(&z, B, &dalekScalar)
|
||||||
|
if dalekScalarBasepoint.Equal(&p) != 1 {
|
||||||
|
t.Error("VarTimeDoubleScalarBaseMult fails with a=0")
|
||||||
|
}
|
||||||
|
checkOnCurve(t, &p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarMultDistributesOverAdd(t *testing.T) {
|
||||||
|
scalarMultDistributesOverAdd := func(x, y Scalar) bool {
|
||||||
|
var z Scalar
|
||||||
|
z.Add(&x, &y)
|
||||||
|
var p, q, r, check Point
|
||||||
|
p.ScalarMult(&x, B)
|
||||||
|
q.ScalarMult(&y, B)
|
||||||
|
r.ScalarMult(&z, B)
|
||||||
|
check.Add(&p, &q)
|
||||||
|
checkOnCurve(t, &p, &q, &r, &check)
|
||||||
|
return check.Equal(&r) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quick.Check(scalarMultDistributesOverAdd, quickCheckConfig32); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarMultNonIdentityPoint(t *testing.T) {
|
||||||
|
// Check whether p.ScalarMult and q.ScalaBaseMult give the same,
|
||||||
|
// when p and q are originally set to the base point.
|
||||||
|
|
||||||
|
scalarMultNonIdentityPoint := func(x Scalar) bool {
|
||||||
|
var p, q Point
|
||||||
|
p.Set(B)
|
||||||
|
q.Set(B)
|
||||||
|
|
||||||
|
p.ScalarMult(&x, B)
|
||||||
|
q.ScalarBaseMult(&x)
|
||||||
|
|
||||||
|
checkOnCurve(t, &p, &q)
|
||||||
|
|
||||||
|
return p.Equal(&q) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quick.Check(scalarMultNonIdentityPoint, quickCheckConfig32); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBasepointTableGeneration(t *testing.T) {
|
||||||
|
// The basepoint table is 32 affineLookupTables,
|
||||||
|
// corresponding to (16^2i)*B for table i.
|
||||||
|
basepointTable := basepointTable()
|
||||||
|
|
||||||
|
tmp1 := &projP1xP1{}
|
||||||
|
tmp2 := &projP2{}
|
||||||
|
tmp3 := &Point{}
|
||||||
|
tmp3.Set(B)
|
||||||
|
table := make([]affineLookupTable, 32)
|
||||||
|
for i := 0; i < 32; i++ {
|
||||||
|
// Build the table
|
||||||
|
table[i].FromP3(tmp3)
|
||||||
|
// Assert equality with the hardcoded one
|
||||||
|
if table[i] != basepointTable[i] {
|
||||||
|
t.Errorf("Basepoint table %d does not match", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set p = (16^2)*p = 256*p = 2^8*p
|
||||||
|
tmp2.FromP3(tmp3)
|
||||||
|
for j := 0; j < 7; j++ {
|
||||||
|
tmp1.Double(tmp2)
|
||||||
|
tmp2.FromP1xP1(tmp1)
|
||||||
|
}
|
||||||
|
tmp1.Double(tmp2)
|
||||||
|
tmp3.fromP1xP1(tmp1)
|
||||||
|
checkOnCurve(t, tmp3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScalarMultMatchesBaseMult(t *testing.T) {
|
||||||
|
scalarMultMatchesBaseMult := func(x Scalar) bool {
|
||||||
|
var p, q Point
|
||||||
|
p.ScalarMult(&x, B)
|
||||||
|
q.ScalarBaseMult(&x)
|
||||||
|
checkOnCurve(t, &p, &q)
|
||||||
|
return p.Equal(&q) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quick.Check(scalarMultMatchesBaseMult, quickCheckConfig32); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBasepointNafTableGeneration(t *testing.T) {
|
||||||
|
var table nafLookupTable8
|
||||||
|
table.FromP3(B)
|
||||||
|
|
||||||
|
if table != *basepointNafTable() {
|
||||||
|
t.Error("BasepointNafTable does not match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVarTimeDoubleBaseMultMatchesBaseMult(t *testing.T) {
|
||||||
|
varTimeDoubleBaseMultMatchesBaseMult := func(x, y Scalar) bool {
|
||||||
|
var p, q1, q2, check Point
|
||||||
|
|
||||||
|
p.VarTimeDoubleScalarBaseMult(&x, B, &y)
|
||||||
|
|
||||||
|
q1.ScalarBaseMult(&x)
|
||||||
|
q2.ScalarBaseMult(&y)
|
||||||
|
check.Add(&q1, &q2)
|
||||||
|
|
||||||
|
checkOnCurve(t, &p, &check, &q1, &q2)
|
||||||
|
return p.Equal(&check) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := quick.Check(varTimeDoubleBaseMultMatchesBaseMult, quickCheckConfig32); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmarks.
|
||||||
|
|
||||||
|
func BenchmarkScalarBaseMult(t *testing.B) {
|
||||||
|
var p Point
|
||||||
|
|
||||||
|
for i := 0; i < t.N; i++ {
|
||||||
|
p.ScalarBaseMult(&dalekScalar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkScalarMult(t *testing.B) {
|
||||||
|
var p Point
|
||||||
|
|
||||||
|
for i := 0; i < t.N; i++ {
|
||||||
|
p.ScalarMult(&dalekScalar, B)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkVarTimeDoubleScalarBaseMult(t *testing.B) {
|
||||||
|
var p Point
|
||||||
|
|
||||||
|
for i := 0; i < t.N; i++ {
|
||||||
|
p.VarTimeDoubleScalarBaseMult(&dalekScalar, B, &dalekScalar)
|
||||||
|
}
|
||||||
|
}
|
||||||
129
src/crypto/ed25519/internal/edwards25519/tables.go
Normal file
129
src/crypto/ed25519/internal/edwards25519/tables.go
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// 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 *Point) {
|
||||||
|
// 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 := Point{}
|
||||||
|
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; fixed-base tables should be precomputed.
|
||||||
|
func (v *affineLookupTable) FromP3(q *Point) {
|
||||||
|
// 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 := Point{}
|
||||||
|
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 *Point) {
|
||||||
|
// 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 := Point{}
|
||||||
|
q2.Add(q, q)
|
||||||
|
tmpP3 := Point{}
|
||||||
|
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; fixed-base tables should be precomputed.
|
||||||
|
func (v *nafLookupTable8) FromP3(q *Point) {
|
||||||
|
v.points[0].FromP3(q)
|
||||||
|
q2 := Point{}
|
||||||
|
q2.Add(q, q)
|
||||||
|
tmpP3 := Point{}
|
||||||
|
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]
|
||||||
|
}
|
||||||
119
src/crypto/ed25519/internal/edwards25519/tables_test.go
Normal file
119
src/crypto/ed25519/internal/edwards25519/tables_test.go
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
// Copyright (c) 2019 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package edwards25519
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
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
|
||||||
|
accP3 := NewIdentityPoint()
|
||||||
|
|
||||||
|
accP1xP1.Add(accP3, &tmp1)
|
||||||
|
accP3.fromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.Add(accP3, &tmp2)
|
||||||
|
accP3.fromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.Add(accP3, &tmp3)
|
||||||
|
accP3.fromP1xP1(&accP1xP1)
|
||||||
|
|
||||||
|
if accP3.Equal(I) != 1 {
|
||||||
|
t.Errorf("Consistency 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
|
||||||
|
accP3 := NewIdentityPoint()
|
||||||
|
|
||||||
|
accP1xP1.AddAffine(accP3, &tmp1)
|
||||||
|
accP3.fromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.AddAffine(accP3, &tmp2)
|
||||||
|
accP3.fromP1xP1(&accP1xP1)
|
||||||
|
accP1xP1.AddAffine(accP3, &tmp3)
|
||||||
|
accP3.fromP1xP1(&accP1xP1)
|
||||||
|
|
||||||
|
if accP3.Equal(I) != 1 {
|
||||||
|
t.Errorf("Consistency 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
|
||||||
|
lhs := NewIdentityPoint()
|
||||||
|
rhs := NewIdentityPoint()
|
||||||
|
|
||||||
|
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("Consistency 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
|
||||||
|
lhs := NewIdentityPoint()
|
||||||
|
rhs := NewIdentityPoint()
|
||||||
|
|
||||||
|
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("Consistency check on nafLookupTable8 failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -392,6 +392,8 @@ var depsRules = `
|
||||||
< crypto
|
< crypto
|
||||||
< crypto/subtle
|
< crypto/subtle
|
||||||
< crypto/internal/subtle
|
< crypto/internal/subtle
|
||||||
|
< crypto/ed25519/internal/edwards25519/field
|
||||||
|
< crypto/ed25519/internal/edwards25519
|
||||||
< crypto/cipher
|
< crypto/cipher
|
||||||
< crypto/aes, crypto/des, crypto/hmac, crypto/md5, crypto/rc4,
|
< crypto/aes, crypto/des, crypto/hmac, crypto/md5, crypto/rc4,
|
||||||
crypto/sha1, crypto/sha256, crypto/sha512
|
crypto/sha1, crypto/sha256, crypto/sha512
|
||||||
|
|
@ -403,7 +405,6 @@ var depsRules = `
|
||||||
CRYPTO, FMT, math/big
|
CRYPTO, FMT, math/big
|
||||||
< crypto/rand
|
< crypto/rand
|
||||||
< crypto/internal/randutil
|
< crypto/internal/randutil
|
||||||
< crypto/ed25519/internal/edwards25519
|
|
||||||
< crypto/ed25519
|
< crypto/ed25519
|
||||||
< encoding/asn1
|
< encoding/asn1
|
||||||
< golang.org/x/crypto/cryptobyte/asn1
|
< golang.org/x/crypto/cryptobyte/asn1
|
||||||
|
|
|
||||||
|
|
@ -306,6 +306,13 @@ func (w *walker) walk(dir string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip submodules, which might not be vendored
|
||||||
|
for _, f := range files {
|
||||||
|
if f.Name() == "go.mod" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// apply pkgh to the files in directory dir
|
// apply pkgh to the files in directory dir
|
||||||
// but ignore files directly under $GOROOT/src (might be temporary test files).
|
// but ignore files directly under $GOROOT/src (might be temporary test files).
|
||||||
if dir != filepath.Join(runtime.GOROOT(), "src") {
|
if dir != filepath.Join(runtime.GOROOT(), "src") {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue