1
0
mirror of https://github.com/henrydcase/nobs.git synced 2024-11-26 00:51:22 +00:00

cSIDH-511: (#26)

Implementation of Commutative Supersingular Isogeny Diffie Hellman,
based on "A faster way to CSIDH" paper (2018/782).

* For fast isogeny calculation, implementation converts a curve from
  Montgomery to Edwards. All calculations are done on Edwards curve
  and then converted back to Montgomery.
* As multiplication in a field Fp511 is most expensive operation
  the implementation contains multiple multiplications. It has
  most performant, assembly implementation which uses BMI2 and
  ADOX/ADCX instructions for modern CPUs. It also contains
  slower implementation which will run on older CPUs

* Benchmarks (Intel SkyLake):

  BenchmarkGeneratePrivate   	    6459	    172213 ns/op	       0 B/op	       0 allocs/op
  BenchmarkGenerateKeyPair   	      25	  45800356 ns/op	       0 B/op	       0 allocs/op
  BenchmarkValidate          	     297	   3915983 ns/op	       0 B/op	       0 allocs/op
  BenchmarkValidateRandom    	  184683	      6231 ns/op	       0 B/op	       0 allocs/op
  BenchmarkValidateGenerated 	      25	  48481306 ns/op	       0 B/op	       0 allocs/op
  BenchmarkDerive            	      19	  60928763 ns/op	       0 B/op	       0 allocs/op
  BenchmarkDeriveGenerated   	       8	 137342421 ns/op	       0 B/op	       0 allocs/op
  BenchmarkXMul              	    2311	    494267 ns/op	       1 B/op	       0 allocs/op
  BenchmarkXAdd              	 2396754	       501 ns/op	       0 B/op	       0 allocs/op
  BenchmarkXDbl              	 2072690	       571 ns/op	       0 B/op	       0 allocs/op
  BenchmarkIsom              	   78004	     15171 ns/op	       0 B/op	       0 allocs/op
  BenchmarkFp512Sub          	224635152	         5.33 ns/op	       0 B/op	       0 allocs/op
  BenchmarkFp512Mul          	246633255	         4.90 ns/op	       0 B/op	       0 allocs/op
  BenchmarkCSwap             	233228547	         5.10 ns/op	       0 B/op	       0 allocs/op
  BenchmarkAddRdc            	87348240	        12.6 ns/op	       0 B/op	       0 allocs/op
  BenchmarkSubRdc            	95112787	        11.7 ns/op	       0 B/op	       0 allocs/op
  BenchmarkModExpRdc         	   25436	     46878 ns/op	       0 B/op	       0 allocs/op
  BenchmarkMulBmiAsm         	19527573	        60.1 ns/op	       0 B/op	       0 allocs/op
  BenchmarkMulGeneric        	 7117650	       164 ns/op	       0 B/op	       0 allocs/op

* Go code has very similar performance when compared to C
  implementation.
  Results from sidh_torturer (4e2996e12d68364761064341cbe1d1b47efafe23)
  github.com:henrydcase/sidh-torture/csidh

  | TestName         |Go        | C        |
  |------------------|----------|----------|
  |TestSharedSecret  | 57.95774 | 57.91092 |
  |TestKeyGeneration | 62.23614 | 58.12980 |
  |TestSharedSecret  | 55.28988 | 57.23132 |
  |TestKeyGeneration | 61.68745 | 58.66396 |
  |TestSharedSecret  | 63.19408 | 58.64774 |
  |TestKeyGeneration | 62.34022 | 61.62539 |
  |TestSharedSecret  | 62.85453 | 68.74503 |
  |TestKeyGeneration | 52.58518 | 58.40115 |
  |TestSharedSecret  | 50.77081 | 61.91699 |
  |TestKeyGeneration | 59.91843 | 61.09266 |
  |TestSharedSecret  | 59.97962 | 62.98151 |
  |TestKeyGeneration | 64.57525 | 56.22863 |
  |TestSharedSecret  | 56.40521 | 55.77447 |
  |TestKeyGeneration | 67.85850 | 58.52604 |
  |TestSharedSecret  | 60.54290 | 65.14052 |
  |TestKeyGeneration | 65.45766 | 58.42823 |

  On average Go implementation is 2% faster.
This commit is contained in:
Henry Case 2019-11-24 03:39:35 +00:00 committed by Henry Case
parent 15f6ee16b9
commit 7efbbf4745
19 changed files with 7116 additions and 5 deletions

View File

@ -1,8 +1,7 @@
sudo: required
language: go
go:
- 1.11.x
- 1.12.x
- 1.13.x
- master
matrix:

View File

@ -32,6 +32,12 @@ endif
test:
$(OPTS_ENV) $(GO) test $(OPTS) $(TEST_PATH)
test_csidh: clean make_dirs $(addprefix prep-,$(TARGETS))
cd $(GOPATH_LOCAL); $(OPTS_ENV) GOPATH=$(GOPATH_LOCAL) go test $(OPTS) github.com/henrydcase/nobs/dh/csidh
test_csidh_bin: clean make_dirs $(addprefix prep-,$(TARGETS))
cd $(GOPATH_LOCAL); $(OPTS_ENV) GOPATH=$(GOPATH_LOCAL) go test -c $(OPTS) github.com/henrydcase/nobs/dh/csidh
cover:
$(GO) test \
-coverprofile=coverage.txt -covermode=atomic $(OPTS) $(TEST_PATH)
@ -39,6 +45,10 @@ cover:
bench:
$(GO) test $(BENCH_OPTS) $(TEST_PATH)
bench_csidh: clean $(addprefix prep-,$(TARGETS))
cd $(GOPATH_LOCAL); GOCACHE=$(GOCACHE) GOPATH=$(GOPATH_LOCAL) $(GO) test \
$(BENCH_OPTS) github.com/henrydcase/nobs/dh/csidh
clean:
rm -rf $(VENDOR_DIR)
rm -rf coverage.txt

104
dh/csidh/consts.go Normal file
View File

@ -0,0 +1,104 @@
package csidh
const (
// pbits is a bitsize of prime p
pbits = 511
// primeCount number of Elkies primes used for constructing p
primeCount = 74
// (2*5+1)^74 is roughly 2^256
expMax = int8(5)
// size of the limbs, pretty much hardcoded to 64-bit words
limbBitSize = 64
// size of the limbs in bytes
limbByteSize = limbBitSize >> 3
// Number of limbs for a field element
numWords = 8
// PrivateKeySize is a size of cSIDH/512 private key in bytes.
PrivateKeySize = 37
// PublicKeySize is a size of cSIDH/512 public key in bytes.
PublicKeySize = 64
// SharedSecretSize is a size of cSIDH/512 shared secret in bytes.
SharedSecretSize = 64
)
var (
// Elkies primes up to 374 + prime 587
// p = 4 * product(primes) - 1
primes = [primeCount]uint64{
0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013, 0x0017, 0x001D, 0x001F, 0x0025,
0x0029, 0x002B, 0x002F, 0x0035, 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053,
0x0059, 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, 0x0083, 0x0089, 0x008B,
0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD, 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5,
0x00C7, 0x00D3, 0x00DF, 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107,
0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137, 0x0139, 0x013D, 0x014B,
0x0151, 0x015B, 0x015D, 0x0161, 0x0167, 0x016F, 0x0175, 0x024B}
p = fp{
0x1B81B90533C6C87B, 0xC2721BF457ACA835,
0x516730CC1F0B4F25, 0xA7AAC6C567F35507,
0x5AFBFCC69322C9CD, 0xB42D083AEDC88C42,
0xFC8AB0D15E3E4C4A, 0x65B48E8F740F89BF,
}
/* Montgomery R = 2^512 mod p */
one = fp{
0xC8FC8DF598726F0A, 0x7B1BC81750A6AF95,
0x5D319E67C1E961B4, 0xB0AA7275301955F1,
0x4A080672D9BA6C64, 0x97A5EF8A246EE77B,
0x06EA9E5D4383676A, 0x3496E2E117E0EC80,
}
// 2 in Montgomery domain
two = fp{
0x767762E5FD1E1599, 0x33C5743A49A0B6F6,
0x68FC0C0364C77443, 0xB9AA1E24F83F56DB,
0x3914101F20520EFB, 0x7B1ED6D95B1542B4,
0x114A8BE928C8828A, 0x03793732BBB24F40,
}
// -2 in Montgomery domain
twoNeg = fp{
0xA50A561F36A8B2E2, 0x8EACA7BA0E0BF13E,
0xE86B24C8BA43DAE2, 0xEE00A8A06FB3FE2B,
0x21E7ECA772D0BAD1, 0x390E316192B3498E,
0xEB4024E83575C9C0, 0x623B575CB85D3A7F,
}
// 4 in Montgomery domain
four = fp{
0xECEEC5CBFA3C2B32, 0x678AE87493416DEC,
0xD1F81806C98EE886, 0x73543C49F07EADB6,
0x7228203E40A41DF7, 0xF63DADB2B62A8568,
0x229517D251910514, 0x06F26E6577649E80,
}
// 4 * sqrt(p)
fourSqrtP = fp{
0x17895E71E1A20B3F, 0x38D0CD95F8636A56,
0x142B9541E59682CD, 0x856F1399D91D6592,
0x0000000000000002,
}
// -p^-1 mod 2^64
pNegInv = fp{
0x66c1301f632e294d,
}
// (p-1)/2. Used as exponent, hence not in
// montgomery domain
pMin1By2 = fp{
0x8DC0DC8299E3643D, 0xE1390DFA2BD6541A,
0xA8B398660F85A792, 0xD3D56362B3F9AA83,
0x2D7DFE63499164E6, 0x5A16841D76E44621,
0xFE455868AF1F2625, 0x32DA4747BA07C4DF,
}
// p-1 mod 2^64. Used as exponent, hence not
// in montgomery domain
pMin1 = fp{
0x1B81B90533C6C879, 0xC2721BF457ACA835,
0x516730CC1F0B4F25, 0xA7AAC6C567F35507,
0x5AFBFCC69322C9CD, 0xB42D083AEDC88C42,
0xFC8AB0D15E3E4C4A, 0x65B48E8F740F89BF,
}
)

307
dh/csidh/csidh.go Normal file
View File

@ -0,0 +1,307 @@
package csidh
import (
"io"
)
// 511-bit number representing prime field element GF(p)
type fp [numWords]uint64
// Represents projective point on elliptic curve E over fp
type point struct {
x fp
z fp
}
// Curve coefficients
type coeff struct {
a fp
c fp
}
type fpRngGen struct {
// working buffer needed to avoid memory allocation
wbuf [64]byte
}
// Defines operations on public key
type PublicKey struct {
fpRngGen
// Montgomery coefficient: represents y^2 = x^3 + Ax^2 + x
a fp
}
// Defines operations on private key
type PrivateKey struct {
fpRngGen
e [PrivateKeySize]int8
}
// randFp generates random element from Fp
func (s *fpRngGen) randFp(v *fp, rng io.Reader) {
mask := uint64(1<<(pbits%limbBitSize)) - 1
for {
*v = fp{}
_, err := io.ReadFull(rng, s.wbuf[:])
if err != nil {
panic("Can't read random number")
}
for i := 0; i < len(s.wbuf); i++ {
j := i / limbByteSize
k := uint(i % 8)
v[j] |= uint64(s.wbuf[i]) << (8 * k)
}
v[len(v)-1] &= mask
if isLess(v, &p) {
return
}
}
}
func cofactorMultiples(p *point, a *coeff, halfL, halfR int, order *fp) (bool, bool) {
var Q point
var r1, d1, r2, d2 bool
if (halfR - halfL) == 1 {
if !p.z.isZero() {
var tmp = fp{primes[halfL]}
xMul512(p, p, a, &tmp)
if !p.z.isZero() {
// order does not divide p+1
return false, true
}
mul512(order, order, primes[halfL])
if sub512(&tmp, &fourSqrtP, order) == 1 {
// order > 4*sqrt(p) -> supersingular
return true, true
}
}
return false, false
}
// perform another recursive step
mid := halfL + ((halfR - halfL + 1) / 2)
var mulL, mulR = fp{1}, fp{1}
for i := halfL; i < mid; i++ {
mul512(&mulR, &mulR, primes[i])
}
for i := mid; i < halfR; i++ {
mul512(&mulL, &mulL, primes[i])
}
xMul512(&Q, p, a, &mulR)
xMul512(p, p, a, &mulL)
r1, d1 = cofactorMultiples(&Q, a, mid, halfR, order)
r2, d2 = cofactorMultiples(p, a, halfL, mid, order)
return r1 || r2, d1 || d2
}
func groupAction(pub *PublicKey, prv *PrivateKey, rng io.Reader) {
var k [2]fp
var e [2][primeCount]uint8
var done = [2]bool{false, false}
var A = coeff{a: pub.a, c: one}
k[0][0] = 4
k[1][0] = 4
for i, v := range primes {
t := (prv.e[uint(i)>>1] << ((uint(i) % 2) * 4)) >> 4
if t > 0 {
e[0][i] = uint8(t)
e[1][i] = 0
mul512(&k[1], &k[1], v)
} else if t < 0 {
e[1][i] = uint8(-t)
e[0][i] = 0
mul512(&k[0], &k[0], v)
} else {
e[0][i] = 0
e[1][i] = 0
mul512(&k[0], &k[0], v)
mul512(&k[1], &k[1], v)
}
}
for {
var P point
var rhs fp
prv.randFp(&P.x, rng)
P.z = one
montEval(&rhs, &A.a, &P.x)
sign := rhs.isNonQuadRes()
if done[sign] {
continue
}
xMul512(&P, &P, &A, &k[sign])
done[sign] = true
for i, v := range primes {
if e[sign][i] != 0 {
var cof = fp{1}
var K point
for j := i + 1; j < len(primes); j++ {
if e[sign][j] != 0 {
mul512(&cof, &cof, primes[j])
}
}
xMul512(&K, &P, &A, &cof)
if !K.z.isZero() {
isom(&P, &A, &K, v)
e[sign][i] = e[sign][i] - 1
if e[sign][i] == 0 {
mul512(&k[sign], &k[sign], primes[i])
}
}
}
done[sign] = done[sign] && (e[sign][i] == 0)
}
modExpRdc512(&A.c, &A.c, &pMin1)
mulRdc(&A.a, &A.a, &A.c)
A.c = one
if done[0] && done[1] {
break
}
}
pub.a = A.a
}
// PrivateKey operations
func (c *PrivateKey) Import(key []byte) bool {
if len(key) < len(c.e) {
return false
}
for i, v := range key {
c.e[i] = int8(v)
}
return true
}
func (c PrivateKey) Export(out []byte) bool {
if len(out) < len(c.e) {
return false
}
for i, v := range c.e {
out[i] = byte(v)
}
return true
}
func GeneratePrivateKey(key *PrivateKey, rng io.Reader) error {
for i := range key.e {
key.e[i] = 0
}
for i := 0; i < len(primes); {
_, err := io.ReadFull(rng, key.wbuf[:])
if err != nil {
return err
}
for j := range key.wbuf {
if int8(key.wbuf[j]) <= expMax && int8(key.wbuf[j]) >= -expMax {
key.e[i>>1] |= int8((key.wbuf[j] & 0xF) << uint((i%2)*4))
i = i + 1
if i == len(primes) {
break
}
}
}
}
return nil
}
// Public key operations
// Assumes key is in Montgomery domain
func (c *PublicKey) Import(key []byte) bool {
if len(key) != numWords*limbByteSize {
return false
}
for i := 0; i < len(key); i++ {
j := i / limbByteSize
k := uint64(i % 8)
c.a[j] |= uint64(key[i]) << (8 * k)
}
return true
}
// Assumes key is exported as encoded in Montgomery domain
func (c *PublicKey) Export(out []byte) bool {
if len(out) != numWords*limbByteSize {
return false
}
for i := 0; i < len(out); i++ {
j := i / limbByteSize
k := uint64(i % 8)
out[i] = byte(c.a[j] >> (8 * k))
}
return true
}
func (c *PublicKey) reset() {
for i := range c.a {
c.a[i] = 0
}
}
func GeneratePublicKey(pub *PublicKey, prv *PrivateKey, rng io.Reader) {
pub.reset()
groupAction(pub, prv, rng)
}
// Validate does public key validation. It returns true if
// a 'pub' is a valid cSIDH public key, otherwise false.
func Validate(pub *PublicKey, rng io.Reader) bool {
// Check if in range
if !isLess(&pub.a, &p) {
return false
}
// j-invariant for montgomery curves is something like
// j = (256*(A^3-3)^3)/(A^2 - 4), so any |A| = 2 is invalid
if pub.a.equal(&two) || pub.a.equal(&twoNeg) {
return false
}
// P must have big enough order to prove supersingularity. The
// probability that this loop will be repeated is negligible.
for {
var P point
var A = point{pub.a, one}
pub.randFp(&P.x, rng)
P.z = one
xDbl(&P, &P, &A)
xDbl(&P, &P, &A)
res, done := cofactorMultiples(&P, &coeff{A.x, A.z}, 0, len(primes), &fp{1})
if done {
return res
}
}
}
// DeriveSecret computes a cSIDH shared secret. If successful, returns true
// and fills 'out' with shared secret. Function returns false in case 'pub' is invalid.
func DeriveSecret(out *[64]byte, pub *PublicKey, prv *PrivateKey, rng io.Reader) bool {
if !Validate(pub, rng) {
return false
}
groupAction(pub, prv, rng)
pub.Export(out[:])
return true
}

397
dh/csidh/csidh_test.go Normal file
View File

@ -0,0 +1,397 @@
package csidh
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"testing"
crand "crypto/rand"
"github.com/henrydcase/nobs/drbg"
)
// Possible values for "Status"
const (
Valid = iota // Indicates that shared secret must be agreed correctly
ValidPublicKey2 // Public key 2 must succeed validation
InvalidSharedSecret // Calculated shared secret must be different than test vector
InvalidPublicKey1 // Public key 1 generated from private key must be different than test vector
InvalidPublicKey2 // Public key 2 must fail validation
)
var StatusValues = map[int]string{
Valid: "valid",
ValidPublicKey2: "valid_public_key2",
InvalidSharedSecret: "invalid_shared_secret",
InvalidPublicKey1: "invalid_public_key1",
InvalidPublicKey2: "invalid_public_key2",
}
type TestVector struct {
ID int `json:"Id"`
Pk1 string `json:"Pk1"`
Pr1 string `json:"Pr1"`
Pk2 string `json:"Pk2"`
Ss string `json:"Ss"`
Status string `json:"status"`
}
type TestVectors struct {
Vectors []TestVector `json:"Vectors"`
}
var rng *drbg.CtrDrbg
func init() {
var tmp [32]byte
// Init drbg
rng = drbg.NewCtrDrbg()
crand.Read(tmp[:])
if !rng.Init(tmp[:], nil) {
panic("Can't initialize DRBG")
}
}
func TestCompare64(t *testing.T) {
const s uint64 = 0xFFFFFFFFFFFFFFFF
var val1 = fp{0, 2, 3, 4, 5, 6, 7, 8}
var val2 = fp{s, s, s, s, s, s, s, s}
var fp fp
if !fp.isZero() {
t.Errorf("isZero returned true, where it should be false")
}
if val1.isZero() {
t.Errorf("isZero returned false, where it should be true")
}
if val2.isZero() {
t.Errorf("isZero returned false, where it should be true")
}
}
func TestEphemeralKeyExchange(t *testing.T) {
var ss1, ss2 [64]byte
var prv1, prv2 PrivateKey
var pub1, pub2 PublicKey
prvBytes1 := []byte{0xaa, 0x54, 0xe4, 0xd4, 0xd0, 0xbd, 0xee, 0xcb, 0xf4, 0xd0, 0xc2, 0xbc, 0x52, 0x44, 0x11, 0xee, 0xe1, 0x14, 0xd2, 0x24, 0xe5, 0x0, 0xcc, 0xf5, 0xc0, 0xe1, 0x1e, 0xb3, 0x43, 0x52, 0x45, 0xbe, 0xfb, 0x54, 0xc0, 0x55, 0xb2}
prv1.Import(prvBytes1)
GeneratePublicKey(&pub1, &prv1, rng)
GeneratePrivateKey(&prv2, rng)
GeneratePublicKey(&pub2, &prv2, rng)
if !DeriveSecret(&ss1, &pub1, &prv2, rng) {
t.Errorf("Derivation failed\n")
}
if !DeriveSecret(&ss2, &pub2, &prv1, rng) {
t.Errorf("Derivation failed\n")
}
if !bytes.Equal(ss1[:], ss2[:]) {
fmt.Printf("%X\n", ss1)
fmt.Printf("%X\n", ss2)
t.Error("ss1 != ss2")
}
}
func TestPrivateKeyExportImport(t *testing.T) {
var buf [37]byte
for i := 0; i < numIter; i++ {
var prv1, prv2 PrivateKey
GeneratePrivateKey(&prv1, rng)
prv1.Export(buf[:])
prv2.Import(buf[:])
for i := 0; i < len(prv1.e); i++ {
if prv1.e[i] != prv2.e[i] {
t.Error("Error occurred when public key export/import")
}
}
}
}
func TestValidateNegative(t *testing.T) {
pk := PublicKey{a: p}
pk.a[0]++
if Validate(&pk, rng) {
t.Error("Public key > p has been validated")
}
pk = PublicKey{a: p}
if Validate(&pk, rng) {
t.Error("Public key == p has been validated")
}
pk = PublicKey{a: two}
if Validate(&pk, rng) {
t.Error("Public key == 2 has been validated")
}
pk = PublicKey{a: twoNeg}
if Validate(&pk, rng) {
t.Error("Public key == -2 has been validated")
}
}
func TestPublicKeyExportImport(t *testing.T) {
var buf [64]byte
eq64 := func(x, y []uint64) bool {
for i := range x {
if x[i] != y[i] {
return false
}
}
return true
}
for i := 0; i < numIter; i++ {
var prv PrivateKey
var pub1, pub2 PublicKey
GeneratePrivateKey(&prv, rng)
GeneratePublicKey(&pub1, &prv, rng)
pub1.Export(buf[:])
pub2.Import(buf[:])
if !eq64(pub1.a[:], pub2.a[:]) {
t.Error("Error occurred when public key export/import")
}
}
}
// Test vectors generated by reference implementation
func TestKAT(t *testing.T) {
var tests TestVectors
var testVectorFile string
// Helper checks if e==true and reports an error if not.
checkExpr := func(e bool, vec *TestVector, t *testing.T, msg string) {
t.Helper()
if !e {
t.Errorf("[Test ID=%d] "+msg, vec.ID)
}
}
if hasADXandBMI2 {
testVectorFile = "testdata/csidh_testvectors.dat"
} else {
testVectorFile = "testdata/csidh_testvectors_small.dat"
}
// checkSharedSecret implements nominal case - imports asymmetric keys for
// both parties, derives secret key and compares it to value in test vector.
// Comparison must succeed in case status is "Valid" in any other case
// it must fail.
checkSharedSecret := func(vec *TestVector, t *testing.T, status int) {
var prv1 PrivateKey
var pub1, pub2 PublicKey
var ss [SharedSecretSize]byte
prBuf, err := hex.DecodeString(vec.Pr1)
if err != nil {
t.Fatal(err)
}
checkExpr(
prv1.Import(prBuf[:]),
vec, t, "PrivateKey wrong")
pkBuf, err := hex.DecodeString(vec.Pk1)
if err != nil {
t.Fatal(err)
}
checkExpr(
pub1.Import(pkBuf[:]),
vec, t, "PublicKey 1 wrong")
pkBuf, err = hex.DecodeString(vec.Pk2)
if err != nil {
t.Fatal(err)
}
checkExpr(
pub2.Import(pkBuf[:]),
vec, t, "PublicKey 2 wrong")
checkExpr(
DeriveSecret(&ss, &pub2, &prv1, rng),
vec, t, "Error when deriving key")
ssExp, err := hex.DecodeString(vec.Ss)
if err != nil {
t.Fatal(err)
}
checkExpr(
bytes.Equal(ss[:], ssExp) == (status == Valid),
vec, t, "Unexpected value of shared secret")
}
// checkPublicKey1 imports public and private key for one party A
// and tries to generate public key for a private key. After that
// it compares generated key to a key from test vector. Comparison
// must fail.
checkPublicKey1 := func(vec *TestVector, t *testing.T) {
var prv PrivateKey
var pub PublicKey
var pubBytesGot [PublicKeySize]byte
prBuf, err := hex.DecodeString(vec.Pr1)
if err != nil {
t.Fatal(err)
}
pubBytesExp, err := hex.DecodeString(vec.Pk1)
if err != nil {
t.Fatal(err)
}
checkExpr(
prv.Import(prBuf[:]),
vec, t, "PrivateKey wrong")
// Generate public key
GeneratePrivateKey(&prv, rng)
pub.Export(pubBytesGot[:])
// pubBytesGot must be different than pubBytesExp
checkExpr(
!bytes.Equal(pubBytesGot[:], pubBytesExp),
vec, t, "Public key generated is the same as public key from the test vector")
}
// checkPublicKey2 the goal is to test key validation. Test tries to
// import public key for B and ensure that import succeeds in case
// status is "Valid" and fails otherwise.
checkPublicKey2 := func(vec *TestVector, t *testing.T, status int) {
var pub PublicKey
pubBytesExp, err := hex.DecodeString(vec.Pk2)
if err != nil {
t.Fatal(err)
}
// Import validates an input, so it must fail
pub.Import(pubBytesExp[:])
checkExpr(
Validate(&pub, rng) == (status == Valid || status == ValidPublicKey2),
vec, t, "PublicKey has been validated correctly")
}
// Load test data
file, err := os.Open(testVectorFile)
if err != nil {
t.Fatal(err.Error())
}
err = json.NewDecoder(file).Decode(&tests)
if err != nil {
t.Fatal(err.Error())
}
// Loop over all test cases
for _, test := range tests.Vectors {
switch test.Status {
case StatusValues[Valid]:
checkSharedSecret(&test, t, Valid)
checkPublicKey2(&test, t, Valid)
case StatusValues[InvalidSharedSecret]:
checkSharedSecret(&test, t, InvalidSharedSecret)
case StatusValues[InvalidPublicKey1]:
checkPublicKey1(&test, t)
case StatusValues[InvalidPublicKey2]:
checkPublicKey2(&test, t, InvalidPublicKey2)
case StatusValues[InvalidPublicKey2]:
checkPublicKey2(&test, t, InvalidPublicKey2)
case StatusValues[ValidPublicKey2]:
checkPublicKey2(&test, t, ValidPublicKey2)
}
}
}
var prv1, prv2 PrivateKey
var pub1, pub2 PublicKey
// Private key generation
func BenchmarkGeneratePrivate(b *testing.B) {
for n := 0; n < b.N; n++ {
GeneratePrivateKey(&prv1, rng)
}
}
// Public key generation from private (group action on empty key)
func BenchmarkGenerateKeyPair(b *testing.B) {
for n := 0; n < b.N; n++ {
var pub PublicKey
GeneratePrivateKey(&prv1, rng)
GeneratePublicKey(&pub, &prv1, rng)
}
}
// Benchmark validation on same key multiple times
func BenchmarkValidate(b *testing.B) {
prvBytes := []byte{0xaa, 0x54, 0xe4, 0xd4, 0xd0, 0xbd, 0xee, 0xcb, 0xf4, 0xd0, 0xc2, 0xbc, 0x52, 0x44, 0x11, 0xee, 0xe1, 0x14, 0xd2, 0x24, 0xe5, 0x0, 0xcc, 0xf5, 0xc0, 0xe1, 0x1e, 0xb3, 0x43, 0x52, 0x45, 0xbe, 0xfb, 0x54, 0xc0, 0x55, 0xb2}
prv1.Import(prvBytes)
var pub PublicKey
GeneratePublicKey(&pub, &prv1, rng)
for n := 0; n < b.N; n++ {
Validate(&pub, rng)
}
}
// Benchmark validation on random (most probably wrong) key
func BenchmarkValidateRandom(b *testing.B) {
var tmp [64]byte
var pub PublicKey
// Initialize seed
for n := 0; n < b.N; n++ {
if _, err := rng.Read(tmp[:]); err != nil {
b.FailNow()
}
pub.Import(tmp[:])
}
}
// Benchmark validation on different keys
func BenchmarkValidateGenerated(b *testing.B) {
for n := 0; n < b.N; n++ {
GeneratePrivateKey(&prv1, rng)
GeneratePublicKey(&pub1, &prv1, rng)
Validate(&pub1, rng)
}
}
// Generate some keys and benchmark derive
func BenchmarkDerive(b *testing.B) {
var ss [64]byte
GeneratePrivateKey(&prv1, rng)
GeneratePublicKey(&pub1, &prv1, rng)
GeneratePrivateKey(&prv2, rng)
GeneratePublicKey(&pub2, &prv2, rng)
for n := 0; n < b.N; n++ {
DeriveSecret(&ss, &pub2, &prv1, rng)
}
}
// Benchmarks both - key generation and derivation
func BenchmarkDeriveGenerated(b *testing.B) {
var ss [64]byte
for n := 0; n < b.N; n++ {
GeneratePrivateKey(&prv1, rng)
GeneratePublicKey(&pub1, &prv1, rng)
GeneratePrivateKey(&prv2, rng)
GeneratePublicKey(&pub2, &prv2, rng)
DeriveSecret(&ss, &pub2, &prv1, rng)
}
}

203
dh/csidh/curve.go Normal file
View File

@ -0,0 +1,203 @@
package csidh
// Implements differential arithmetic in P^1 for montgomery
// curves a mapping: x(P),x(Q),x(P-Q) -> x(P+Q)
// PaQ = P + Q
// This algorithms is correctly defined only for cases when
// P!=inf, Q!=inf, P!=Q and P!=-Q
func xAdd(PaQ, P, Q, PdQ *point) {
var t0, t1, t2, t3 fp
addRdc(&t0, &P.x, &P.z)
subRdc(&t1, &P.x, &P.z)
addRdc(&t2, &Q.x, &Q.z)
subRdc(&t3, &Q.x, &Q.z)
mulRdc(&t0, &t0, &t3)
mulRdc(&t1, &t1, &t2)
addRdc(&t2, &t0, &t1)
subRdc(&t3, &t0, &t1)
mulRdc(&t2, &t2, &t2) // sqr
mulRdc(&t3, &t3, &t3) // sqr
mulRdc(&PaQ.x, &PdQ.z, &t2)
mulRdc(&PaQ.z, &PdQ.x, &t3)
}
// Q = 2*P on a montgomery curve E(x): x^3 + A*x^2 + x
// It is correctly defined for all P != inf
func xDbl(Q, P, A *point) {
var t0, t1, t2 fp
addRdc(&t0, &P.x, &P.z)
mulRdc(&t0, &t0, &t0) // sqr
subRdc(&t1, &P.x, &P.z)
mulRdc(&t1, &t1, &t1) // sqr
subRdc(&t2, &t0, &t1)
mulRdc(&t1, &four, &t1)
mulRdc(&t1, &t1, &A.z)
mulRdc(&Q.x, &t0, &t1)
addRdc(&t0, &A.z, &A.z)
addRdc(&t0, &t0, &A.x)
mulRdc(&t0, &t0, &t2)
addRdc(&t0, &t0, &t1)
mulRdc(&Q.z, &t0, &t2)
}
// PaP = 2*P; PaQ = P+Q
// PaP can override P and PaQ can override Q
func xDblAdd(PaP, PaQ, P, Q, PdQ *point, A24 *coeff) {
var t0, t1, t2 fp
addRdc(&t0, &P.x, &P.z)
subRdc(&t1, &P.x, &P.z)
mulRdc(&PaP.x, &t0, &t0)
subRdc(&t2, &Q.x, &Q.z)
addRdc(&PaQ.x, &Q.x, &Q.z)
mulRdc(&t0, &t0, &t2)
mulRdc(&PaP.z, &t1, &t1)
mulRdc(&t1, &t1, &PaQ.x)
subRdc(&t2, &PaP.x, &PaP.z)
mulRdc(&PaP.z, &PaP.z, &A24.c)
mulRdc(&PaP.x, &PaP.x, &PaP.z)
mulRdc(&PaQ.x, &A24.a, &t2)
subRdc(&PaQ.z, &t0, &t1)
addRdc(&PaP.z, &PaP.z, &PaQ.x)
addRdc(&PaQ.x, &t0, &t1)
mulRdc(&PaP.z, &PaP.z, &t2)
mulRdc(&PaQ.z, &PaQ.z, &PaQ.z)
mulRdc(&PaQ.x, &PaQ.x, &PaQ.x)
mulRdc(&PaQ.z, &PaQ.z, &PdQ.x)
mulRdc(&PaQ.x, &PaQ.x, &PdQ.z)
}
// Swap P1 with P2 in constant time. The 'choice'
// parameter must have a value of either 1 (results
// in swap) or 0 (results in no-swap).
func cswappoint(P1, P2 *point, choice uint8) {
cswap512(&P1.x, &P2.x, choice)
cswap512(&P1.z, &P2.z, choice)
}
// A uniform Montgomery ladder. co is A coefficient of
// x^3 + A*x^2 + x curve. k MUST be > 0
//
// kP = [k]P. xM=x(0 + k*P)
//
// non-constant time.
func xMul512(kP, P *point, co *coeff, k *fp) {
var A24 coeff
var Q point
var j uint
var A = point{x: co.a, z: co.c}
var R = *P
// Precompyte A24 = (A+2C:4C) => (A24.x = A.x+2A.z; A24.z = 4*A.z)
addRdc(&A24.a, &co.c, &co.c)
addRdc(&A24.a, &A24.a, &co.a)
mulRdc(&A24.c, &co.c, &four)
// Skip initial 0 bits.
for j = 511; j > 0; j-- {
// performance hit from making it constant-time is actually
// quite big, so... unsafe branch for now
if uint8(k[j>>6]>>(j&63)&1) != 0 {
break
}
}
xDbl(&Q, P, &A)
prevBit := uint8(1)
for i := j; i > 0; {
i--
bit := uint8(k[i>>6] >> (i & 63) & 1)
swap := prevBit ^ bit
prevBit = bit
cswappoint(&Q, &R, swap)
xDblAdd(&Q, &R, &Q, &R, P, &A24)
}
cswappoint(&Q, &R, uint8(k[0]&1))
*kP = Q
}
func isom(img *point, co *coeff, kern *point, order uint64) {
var t0, t1, t2, S, D fp
var Q, prod point
var coEd coeff
var M = [3]point{*kern}
// Compute twisted Edwards coefficients
// coEd.a = co.a + 2*co.c
// coEd.c = co.a - 2*co.c
// coEd.a*X^2 + Y^2 = 1 + coEd.c*X^2*Y^2
addRdc(&coEd.c, &co.c, &co.c)
addRdc(&coEd.a, &co.a, &coEd.c)
subRdc(&coEd.c, &co.a, &coEd.c)
// Transfer point to twisted Edwards YZ-coordinates
// (X:Z)->(Y:Z) = (X-Z : X+Z)
addRdc(&S, &img.x, &img.z)
subRdc(&D, &img.x, &img.z)
subRdc(&prod.x, &kern.x, &kern.z)
addRdc(&prod.z, &kern.x, &kern.z)
mulRdc(&t1, &prod.x, &S)
mulRdc(&t0, &prod.z, &D)
addRdc(&Q.x, &t0, &t1)
subRdc(&Q.z, &t0, &t1)
xDbl(&M[1], kern, &point{x: co.a, z: co.c})
// TODO: Not constant time.
for i := uint64(1); i < order>>1; i++ {
if i >= 2 {
xAdd(&M[i%3], &M[(i-1)%3], kern, &M[(i-2)%3])
}
subRdc(&t1, &M[i%3].x, &M[i%3].z)
addRdc(&t0, &M[i%3].x, &M[i%3].z)
mulRdc(&prod.x, &prod.x, &t1)
mulRdc(&prod.z, &prod.z, &t0)
mulRdc(&t1, &t1, &S)
mulRdc(&t0, &t0, &D)
addRdc(&t2, &t0, &t1)
mulRdc(&Q.x, &Q.x, &t2)
subRdc(&t2, &t0, &t1)
mulRdc(&Q.z, &Q.z, &t2)
}
mulRdc(&Q.x, &Q.x, &Q.x)
mulRdc(&Q.z, &Q.z, &Q.z)
mulRdc(&img.x, &img.x, &Q.x)
mulRdc(&img.z, &img.z, &Q.z)
// coEd.a^order and coEd.c^order
modExpRdc64(&coEd.a, &coEd.a, order)
modExpRdc64(&coEd.c, &coEd.c, order)
// prod^8
mulRdc(&prod.x, &prod.x, &prod.x)
mulRdc(&prod.x, &prod.x, &prod.x)
mulRdc(&prod.x, &prod.x, &prod.x)
mulRdc(&prod.z, &prod.z, &prod.z)
mulRdc(&prod.z, &prod.z, &prod.z)
mulRdc(&prod.z, &prod.z, &prod.z)
// Compute image curve params
mulRdc(&coEd.c, &coEd.c, &prod.x)
mulRdc(&coEd.a, &coEd.a, &prod.z)
// Convert curve coefficients back to Montgomery
addRdc(&co.a, &coEd.a, &coEd.c)
subRdc(&co.c, &coEd.a, &coEd.c)
addRdc(&co.a, &co.a, &co.a)
}
// evaluates x^3 + Ax^2 + x
func montEval(res, A, x *fp) {
var t fp
*res = *x
mulRdc(res, res, res)
mulRdc(&t, A, x)
addRdc(res, res, &t)
addRdc(res, res, &one)
mulRdc(res, res, x)
}

371
dh/csidh/curve_test.go Normal file
View File

@ -0,0 +1,371 @@
package csidh
import (
"math/big"
"testing"
)
// Actual test implementation
func TestXAdd(t *testing.T) {
var P, Q, PdQ point
var PaQ point
var expPaQ big.Int
// points from a Elliptic Curve defined in sage as follows:
// A = 0x6055947AAFEBF773CE912680A6A32656073233D2FD6FDF4A143BE82D25B44ECC0431DE564C0F0D6591ACC62D6876E86F5D06B68C9EAF20D0DB0A6B99ED558512
// E = EllipticCurve(GF(p), [0, A, 0, 1, 0])
// where p is CSIDH's 511-bit prime
checkXAdd := func() {
xAdd(&PaQ, &P, &Q, &PdQ)
ret := toNormX(&PaQ)
if ret.Cmp(&expPaQ) != 0 {
t.Errorf("\nExp: %s\nGot: %s", expPaQ.Text(16), ret.Text(16))
}
}
expPaQ.SetString("0x41C98C5D7FF118B1A3987733581FD69C0CC27D7B63BCCA525106B9945869C6DAEDAA3D5D9D2679237EF0D013BE68EF12731DBFB26E12576BAD1E824C67ABD125", 0)
P.x = toFp("0x5840FD8E0165F7F474260F99337461AF195233F791FABE735EC2634B74A95559568B4CEB23959C8A01C5C57E215D22639868ED840D74FE2BAC04830CF75047AD")
P.z = toFp("1")
Q.x = toFp("0x3C1A003C71436698B4A181CEB12BA4B4D1FF7BB14AAAF6FBDA6957C4EBA20AD8E3893DF6F64E67E81163E024C19C7E975F3EC61862F75502C3ED802370E75A3F")
Q.z = toFp("1")
PdQ.x = toFp("0x519B1928F752B0B2143C1C23EB247B370DBB5B9C29B9A3A064D7FBC1B67FAC34B6D3DDA0F3CB87C387B425B36F31B93A8E73252BA701927B767A9DE89D5A92AE")
PdQ.z = toFp("1")
checkXAdd()
expPaQ.SetString("0x5840FD8E0165F7F474260F99337461AF195233F791FABE735EC2634B74A95559568B4CEB23959C8A01C5C57E215D22639868ED840D74FE2BAC04830CF75047AD", 0)
P.x = toFp("0x5840FD8E0165F7F474260F99337461AF195233F791FABE735EC2634B74A95559568B4CEB23959C8A01C5C57E215D22639868ED840D74FE2BAC04830CF75047AD")
P.z = toFp("1")
Q.x = toFp("1")
Q.z = toFp("0x0")
PdQ.x = toFp(expPaQ.Text(10))
PdQ.z = toFp("1")
checkXAdd()
}
func TestXDbl(t *testing.T) {
var P, A point
var PaP point
var expPaP big.Int
// points from a Elliptic Curve defined in sage as follows:
// A = 0x599841D7D1FCD92A85759B7A3D2D5E4C56EFB17F19F86EB70E121EA16305EDE45A55868BE069313F821F7D94069EC220A4AC3B85500376710538246E9B3BC138
// E = EllipticCurve(GF(p), [0, A, 0, 1, 0])
// where p is CSIDH's 511-bit prime
expPaP.SetString("0x6115B5D8BB613D11BDFEA70D436D87C1515553F6A15061727B4001E0AF745AAA9F39EB9464982829D931F77DAB9D71B24FF0D1D34C347F2A51FD45821F2EA06F", 0)
P.x = toFp("0x6C5B4D4AB0765AAB23C10F8455BE522D3A5363324D7AD641CC67C0A52FC1FFE9F3F8EDFE641478CA93D4D0016D83F21487FD4AF4E02F8A2C237CF27C5604BCC")
P.z = toFp("1")
A.x = toFp("0x599841D7D1FCD92A85759B7A3D2D5E4C56EFB17F19F86EB70E121EA16305EDE45A55868BE069313F821F7D94069EC220A4AC3B85500376710538246E9B3BC138")
A.z = toFp("1")
xDbl(&PaP, &P, &A)
ret := toNormX(&PaP)
if ret.Cmp(&expPaP) != 0 {
t.Errorf("\nExp: %s\nGot: %s", expPaP.Text(16), ret.Text(16))
}
}
func TestXDblAddNominal(t *testing.T) {
var P, Q, PdQ point
var PaP, PaQ point
var expPaP, expPaQ big.Int
var A coeff
checkXDblAdd := func() {
var A24 coeff
// A24.a = 2*A.z + A.a
addRdc(&A24.a, &A.c, &A.c)
addRdc(&A24.a, &A24.a, &A.a)
// A24.z = 4*A.z
mulRdc(&A24.c, &A.c, &four)
// Additionally will check if input can be same as output
PaP = P
PaQ = Q
xDblAdd(&PaP, &PaQ, &PaP, &PaQ, &PdQ, &A24)
retPaP := toNormX(&PaP)
retPaQ := toNormX(&PaQ)
if retPaP.Cmp(&expPaP) != 0 {
t.Errorf("\nExp: %s\nGot: %s", expPaP.Text(16), retPaP.Text(16))
}
if retPaQ.Cmp(&expPaQ) != 0 {
t.Errorf("\nExp: %s\nGot: %s", expPaQ.Text(16), retPaQ.Text(16))
}
}
// 2*P
expPaP.SetString("0x38F5B37271A3D8FA50107F88045D6F6B08355DD026C02E0306CE5875F47422736AD841B4122B2BD7DE6166BB6498F6A283378FF8250948E834F15CEA2D59A57B", 0)
// P+Q
expPaQ.SetString("0x53D9B44C5F61651612243CF7987F619FE6ACB5CF29538F96A63E7278E131F41A17D64388E31B028A5183EF9096AE82724BC34D8DDFD67AD68BD552A33C345B8C", 0)
P.x = toFp("0x4FE17B4CC66E85960F57033CD45996C99248DA09DF2E36F8840657B52F74ED8173E0D322FA57D7B4D0EE7F12967BBD59140B42F2626E29167D6419E851E5A4C9")
P.z = toFp("1")
Q.x = toFp("0x465047949CD6574FDBE00EA365CAF7A95DC9DEBE96A188823CA8C9DD9F527CF81290D49864F61DF0C08C1D6052139230735CA6CFDBDC1A8820610CCD71861176")
Q.z = toFp("1")
PdQ.x = toFp("0x49D3B999A0A020B34473568A8F75B5405F2D3BE5A006595015FC6DDC6BED8AB2A51A887B6DC62C64354466865FFD69E50AD37F6F4FBD74119EB65EBC9367B556")
PdQ.z = toFp("1")
A.a = toFp("0x118F955D498D902FD42E5B2926F297CC814CD7649EC5B070295622F97C4A0D9BD34058A7E0E00CB73ED32FCC237F9F6B7D2A15F5CC7C4EC61ECEF80ACBB0EFA4")
A.c = toFp("1")
checkXDblAdd()
// Case P=value, Q=(x=1, z=0). In this case PaQ==P; PaP=2*P
expPaP.SetString("0x38F5B37271A3D8FA50107F88045D6F6B08355DD026C02E0306CE5875F47422736AD841B4122B2BD7DE6166BB6498F6A283378FF8250948E834F15CEA2D59A57B", 0)
expPaQ.SetString("0x4FE17B4CC66E85960F57033CD45996C99248DA09DF2E36F8840657B52F74ED8173E0D322FA57D7B4D0EE7F12967BBD59140B42F2626E29167D6419E851E5A4C9", 0)
P.x = toFp("0x4FE17B4CC66E85960F57033CD45996C99248DA09DF2E36F8840657B52F74ED8173E0D322FA57D7B4D0EE7F12967BBD59140B42F2626E29167D6419E851E5A4C9")
P.z = toFp("1")
Q.x = toFp("1")
Q.z = toFp("0")
PdQ.x = toFp("0x4FE17B4CC66E85960F57033CD45996C99248DA09DF2E36F8840657B52F74ED8173E0D322FA57D7B4D0EE7F12967BBD59140B42F2626E29167D6419E851E5A4C9")
PdQ.z = toFp("1")
A.a = toFp("0x118F955D498D902FD42E5B2926F297CC814CD7649EC5B070295622F97C4A0D9BD34058A7E0E00CB73ED32FCC237F9F6B7D2A15F5CC7C4EC61ECEF80ACBB0EFA4")
A.c = toFp("1")
checkXDblAdd()
}
func TestXDblAddVSxDblxAdd(t *testing.T) {
var P, Q, PdQ point
var PaP1, PaQ1 point
var PaP2, PaQ2 point
var A point
var A24 coeff
P.x = toFp("0x4FE17B4CC66E85960F57033CD45996C99248DA09DF2E36F8840657B52F74ED8173E0D322FA57D7B4D0EE7F12967BBD59140B42F2626E29167D6419E851E5A4C9")
P.z = toFp("1")
Q.x = toFp("0x465047949CD6574FDBE00EA365CAF7A95DC9DEBE96A188823CA8C9DD9F527CF81290D49864F61DF0C08C1D6052139230735CA6CFDBDC1A8820610CCD71861176")
Q.z = toFp("1")
PdQ.x = toFp("0x49D3B999A0A020B34473568A8F75B5405F2D3BE5A006595015FC6DDC6BED8AB2A51A887B6DC62C64354466865FFD69E50AD37F6F4FBD74119EB65EBC9367B556")
PdQ.z = toFp("1")
A.x = toFp("0x118F955D498D902FD42E5B2926F297CC814CD7649EC5B070295622F97C4A0D9BD34058A7E0E00CB73ED32FCC237F9F6B7D2A15F5CC7C4EC61ECEF80ACBB0EFA4")
A.z = toFp("1")
// Precompute A24 for xDblAdd
// (A+2C:4C) => (A24.x = A.x+2A.z; A24.z = 4*A.z)
addRdc(&A24.a, &A.z, &A.z)
addRdc(&A24.a, &A24.a, &A.x)
mulRdc(&A24.c, &A.z, &four)
for i := 0; i < numIter; i++ {
xAdd(&PaQ2, &P, &Q, &PdQ)
xDbl(&PaP2, &P, &A)
xDblAdd(&PaP1, &PaQ1, &P, &Q, &PdQ, &A24)
if !ceqpoint(&PaQ1, &PaQ2) {
exp := toNormX(&PaQ1)
got := toNormX(&PaQ2)
t.Errorf("\nExp: \n\t%s\nGot from xAdd: \n\t%s", exp.Text(16), got.Text(16))
}
if !ceqpoint(&PaP1, &PaP2) {
exp := toNormX(&PaP1)
got := toNormX(&PaP2)
t.Errorf("\nExp: \n\t%s\nGot from xDbl: \n\t%s", exp.Text(16), got.Text(16))
}
// Swap values for next operation
PdQ = Q
Q = P
P = PaP1
}
}
func TestXMul(t *testing.T) {
var P point
var co coeff
var expKP big.Int
var k fp
checkXMul := func() {
var kP point
xMul512(&kP, &P, &co, &k)
retKP := toNormX(&kP)
if expKP.Cmp(&retKP) != 0 {
t.Errorf("\nExp: %s\nGot: %s", expKP.Text(16), retKP.Text(16))
}
// Check if first and second argument can overlap
xMul512(&P, &P, &co, &k)
retKP = toNormX(&P)
if expKP.Cmp(&retKP) != 0 {
t.Errorf("\nExp: %s\nGot: %s", expKP.Text(16), retKP.Text(16))
}
}
// Case C=1
expKP.SetString("0x582B866603E6FBEBD21FE660FB34EF9466FDEC55FFBCE1073134CC557071147821BBAD225E30F7B2B6790B00ED9C39A29AA043F58AF995E440AFB13DA8E6D788", 0)
P.x = toFp("0x1C5CA539C1D5B52DE4750C390C24C05251E8B1D33E48971FA86F5ADDED2D06C8CD31E94887541468BB2925EBD693C9DDFF5BD9508430F25FE28EE30C0760C0FE")
P.z = toFp("1")
co.a = toFp("0x538F785D52996919C8D5C73D842A0249669B5B6BB05338B74EAE8094AE5009A3BA2D73730F527D7403E8184D9B1FA11C0C4C40E7B328A84874A6DBCE99E1DF92")
co.c = toFp("1")
k = fp{0x7A36C930A83EFBD5, 0xD0E80041ED0DDF9F, 0x5AA17134F1B8F877, 0x975711EC94168E51, 0xB3CAD962BED4BAC5, 0x3026DFDD7E4F5687, 0xE67F91AB8EC9C3AF, 0x34671D3FD8C317E7}
checkXMul()
// Check if algorithms works correctly with k=1
expKP.SetString("0x1C5CA539C1D5B52DE4750C390C24C05251E8B1D33E48971FA86F5ADDED2D06C8CD31E94887541468BB2925EBD693C9DDFF5BD9508430F25FE28EE30C0760C0FE", 0)
P.x = toFp("0x1C5CA539C1D5B52DE4750C390C24C05251E8B1D33E48971FA86F5ADDED2D06C8CD31E94887541468BB2925EBD693C9DDFF5BD9508430F25FE28EE30C0760C0FE")
P.z = toFp("1")
co.a = toFp("0x538F785D52996919C8D5C73D842A0249669B5B6BB05338B74EAE8094AE5009A3BA2D73730F527D7403E8184D9B1FA11C0C4C40E7B328A84874A6DBCE99E1DF92")
co.c = toFp("1")
k = fp{1, 0, 0, 0, 0, 0, 0, 0}
checkXMul()
// Check if algorithms works correctly with value of k for which few small and high
// order bits are 0 (test for odd number of cswaps in xMul)
expKP.SetString("0x1925EDA0928C10F427B4E642E7E1481A670D1249956DED6A2292B9BAB841F6AA86A9F41459400845ED4A5E2531A14165F64FE4E43DBD85321B429C6DAE2E8987", 0)
P.x = toFp("0x4CE8603817B9BB06515E921AA201D26B31F3CE181D1E18CD5CD704708CCAD47546CEEAB42B98EE67925A5259E0684A0489F574A999DE127F708B849ACAA12A63")
P.z = toFp("1")
co.a = toFp("0x538F785D52996919C8D5C73D842A0249669B5B6BB05338B74EAE8094AE5009A3BA2D73730F527D7403E8184D9B1FA11C0C4C40E7B328A84874A6DBCE99E1DF92")
co.c = toFp("1")
k = fp{0, 7, 0, 0, 0, 0, 0, 0}
checkXMul()
// Check if algorithms works correctly with value of k for which few small and high
// order bits are 0 (test for even number of cswaps in xMul)
expKP.SetString("0x30C02915C5967C3B6EB2196A934ADF38A183E9C7E814B54121F93048A8FC12D5036992FABF8D807581017A4C1F93D07352413F38F6A902FC76A8894FE8D94805", 0)
P.x = toFp("0x2DDD15ED7C169BE6D9EC02CFE3DC507EC4A7A4D96DE3FAAB9BFCEA1B047807EA301E89830F2FDD0E7E642A85E7ACDE16BAD76DF140F719C4A7AB85153E7D69DC")
P.z = toFp("1")
co.a = toFp("0x538F785D52996919C8D5C73D842A0249669B5B6BB05338B74EAE8094AE5009A3BA2D73730F527D7403E8184D9B1FA11C0C4C40E7B328A84874A6DBCE99E1DF92")
co.c = toFp("1")
k = fp{0, 15, 0, 0, 0, 0, 0, 0}
checkXMul()
// xMul512 does NOT work correctly for k==0. In such case function will return 2*P. But
// thanks to that fact we don't need to handle k==0 case, we get some speedup.
expKP.SetString("0x6115B5D8BB613D11BDFEA70D436D87C1515553F6A15061727B4001E0AF745AAA9F39EB9464982829D931F77DAB9D71B24FF0D1D34C347F2A51FD45821F2EA06F", 0)
P.x = toFp("0x6C5B4D4AB0765AAB23C10F8455BE522D3A5363324D7AD641CC67C0A52FC1FFE9F3F8EDFE641478CA93D4D0016D83F21487FD4AF4E02F8A2C237CF27C5604BCC")
P.z = toFp("1")
co.a = toFp("0x599841D7D1FCD92A85759B7A3D2D5E4C56EFB17F19F86EB70E121EA16305EDE45A55868BE069313F821F7D94069EC220A4AC3B85500376710538246E9B3BC138")
co.c = toFp("1")
k = fp{0, 0, 0, 0, 0, 0, 0, 0}
checkXMul()
}
func TestMappointHardcoded3(t *testing.T) {
var P = point{
x: fp{0xca1a2fdec38c669b, 0xf2fe3678ebeb978b, 0xfda3e9a6f0c719d, 0x6f7bffa41772570b, 0x3d90cdd6283dc150, 0x21b55b738eb1ded9, 0x209515d0a9f41dd6, 0x5275cf397d154a12},
z: fp{0x1fff8309761576e, 0xef239cbeda7c2ba1, 0x6136ae2d76e95873, 0x1f8f6ac909570cec, 0x780fdf0cc7d676d8, 0x548098fe92ed04e1, 0xb39da564701ef35d, 0x5fec19626df41306}}
var A = coeff{
a: fp{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
c: fp{0xc8fc8df598726f0a, 0x7b1bc81750a6af95, 0x5d319e67c1e961b4, 0xb0aa7275301955f1, 0x4a080672d9ba6c64, 0x97a5ef8a246ee77b, 0x6ea9e5d4383676a, 0x3496e2e117e0ec80}}
var K = point{
x: fp{0x597616608e291c6f, 0xd14230b008736798, 0xa63099b1ace67e6e, 0xe37c13afd768bcfa, 0xc6ef718894f08135, 0x53a4fd09091f3522, 0xc9a1f9f670645fe1, 0x628c4a8efd83e5f0},
z: fp{0x8f18a654312ac1ad, 0xbc20a9b2472785c9, 0xdaf97c29bbf9e492, 0xf91a8c799e2f6119, 0xc8dc675cc8e528e6, 0x9a7b2c2f0df95171, 0x85629cd38cdd9fdb, 0x656d5253d3fd1a6e}}
var k uint64 = 3
var expA = coeff{
a: fp{0x6fa92a66e77cfc1, 0x9efbfb7118f1832c, 0x441894cc5d1d24ae, 0x5a2f0fafa26761de, 0x8095c36d3a20a78a, 0xb22be0023612a135, 0x5eb844d06ef0f430, 0x52e53309d1c90cf8},
c: fp{0x98173d5664a23e5c, 0xd8fe1c6306bbc11a, 0xa774fbc502648059, 0x766a0d839aa62c83, 0x4b074f9b93d1633d, 0xf306019dbf87f505, 0x77c720ca059234b0, 0x3d47ab65269c5908}}
var expP = point{
x: fp{0x91aba9b39f280495, 0xfbd8ea69d2990aeb, 0xb03e1b8ed7fe3dba, 0x3d30a41499f08998, 0xb15a42630de9c606, 0xa7dd487fef16f5c8, 0x8673948afed8e968, 0x57ecc8710004cd4d},
z: fp{0xce8819869a942526, 0xb98ca2ff79ef8969, 0xd49c9703743a1812, 0x21dbb090f9152e03, 0xbabdcac831b1adea, 0x8cee90762baa2ddd, 0xa0dd2ddcef809d96, 0x1de2a8887a32f19b}}
isom(&P, &A, &K, k)
if !ceqFp(&P.x, &expP.x) || !ceqFp(&P.z, &expP.z) {
normP := toNormX(&P)
normPExp := toNormX(&expP)
t.Errorf("P != expP [\n %s != %s\n]", normP.Text(16), normPExp.Text(16))
}
if !ceqFp(&A.a, &expA.a) || !ceqFp(&A.c, &expA.c) {
t.Errorf("A != expA %X %X", A.a[0], expA.a[0])
}
}
func TestMappointHardcoded5(t *testing.T) {
var P = point{
x: fp{0xca1a2fdec38c669b, 0xf2fe3678ebeb978b, 0xfda3e9a6f0c719d, 0x6f7bffa41772570b, 0x3d90cdd6283dc150, 0x21b55b738eb1ded9, 0x209515d0a9f41dd6, 0x5275cf397d154a12},
z: fp{0x1fff8309761576e, 0xef239cbeda7c2ba1, 0x6136ae2d76e95873, 0x1f8f6ac909570cec, 0x780fdf0cc7d676d8, 0x548098fe92ed04e1, 0xb39da564701ef35d, 0x5fec19626df41306}}
var A = coeff{
a: fp{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
c: fp{0xc8fc8df598726f0a, 0x7b1bc81750a6af95, 0x5d319e67c1e961b4, 0xb0aa7275301955f1, 0x4a080672d9ba6c64, 0x97a5ef8a246ee77b, 0x6ea9e5d4383676a, 0x3496e2e117e0ec80}}
var K = point{
x: fp{0x597616608e291c6f, 0xd14230b008736798, 0xa63099b1ace67e6e, 0xe37c13afd768bcfa, 0xc6ef718894f08135, 0x53a4fd09091f3522, 0xc9a1f9f670645fe1, 0x628c4a8efd83e5f0},
z: fp{0x8f18a654312ac1ad, 0xbc20a9b2472785c9, 0xdaf97c29bbf9e492, 0xf91a8c799e2f6119, 0xc8dc675cc8e528e6, 0x9a7b2c2f0df95171, 0x85629cd38cdd9fdb, 0x656d5253d3fd1a6e}}
var k uint64 = 5
var expA = coeff{
a: fp{0x32076f58298ed474, 0x5094a1fc8696d307, 0x82e510594157944a, 0xb60ce760f88c83a9, 0xae8a28c325186983, 0xe31d2446a4ad2f18, 0xb266c612b5f141c1, 0x64283e618db5a705},
c: fp{0x4472b49b65272190, 0x2bd5919309778f56, 0x6132753691fe016c, 0x8f654849c09e6d34, 0xfa208dd9aea1ef12, 0xf7df0dd10071411a, 0x75afb7860500922c, 0x52fb7d34b129fb65}}
var expP = point{
x: fp{0x3b75fc94b2a6df2d, 0x96d53dc9b0e867a0, 0x22e87202421d274e, 0x30a361440697ee1a, 0x8b52ee078bdbddcd, 0x64425d500e6b934d, 0xf47d1f568f6df391, 0x5d9d3607431395ab},
z: fp{0x746e02dafa040976, 0xcd408f2cddbf3a8e, 0xf643354e0e13a93f, 0x7c39ed96ce9a5e29, 0xfcdf26f1a1a550ca, 0x2fc8aafc4ca0a559, 0x5d204a2b14cf19ba, 0xbd2c3406762f05d}}
isom(&P, &A, &K, k)
if !ceqFp(&P.x, &expP.x) || !ceqFp(&P.z, &expP.z) {
normP := toNormX(&P)
normPExp := toNormX(&expP)
t.Errorf("P != expP [\n %s != %s\n]", normP.Text(16), normPExp.Text(16))
}
if !ceqFp(&A.a, &expA.a) || !ceqFp(&A.c, &expA.c) {
t.Errorf("A != expA %X %X", A.a[0], expA.a[0])
}
}
func BenchmarkXMul(b *testing.B) {
var kP, P point
var co coeff
var expKP big.Int
var k fp
// Case C=1
expKP.SetString("0x582B866603E6FBEBD21FE660FB34EF9466FDEC55FFBCE1073134CC557071147821BBAD225E30F7B2B6790B00ED9C39A29AA043F58AF995E440AFB13DA8E6D788", 0)
P.x = toFp("0x1C5CA539C1D5B52DE4750C390C24C05251E8B1D33E48971FA86F5ADDED2D06C8CD31E94887541468BB2925EBD693C9DDFF5BD9508430F25FE28EE30C0760C0FE")
P.z = toFp("1")
co.a = toFp("0x538F785D52996919C8D5C73D842A0249669B5B6BB05338B74EAE8094AE5009A3BA2D73730F527D7403E8184D9B1FA11C0C4C40E7B328A84874A6DBCE99E1DF92")
co.c = toFp("1")
k = fp{0x7A36C930A83EFBD5, 0xD0E80041ED0DDF9F, 0x5AA17134F1B8F877, 0x975711EC94168E51, 0xB3CAD962BED4BAC5, 0x3026DFDD7E4F5687, 0xE67F91AB8EC9C3AF, 0x34671D3FD8C317E7}
for n := 0; n < b.N; n++ {
xMul512(&kP, &P, &co, &k)
}
}
func BenchmarkXAdd(b *testing.B) {
var P, Q, PdQ point
var PaQ point
P.x = toFp("0x5840FD8E0165F7F474260F99337461AF195233F791FABE735EC2634B74A95559568B4CEB23959C8A01C5C57E215D22639868ED840D74FE2BAC04830CF75047AD")
P.z = toFp("1")
Q.x = toFp("0x3C1A003C71436698B4A181CEB12BA4B4D1FF7BB14AAAF6FBDA6957C4EBA20AD8E3893DF6F64E67E81163E024C19C7E975F3EC61862F75502C3ED802370E75A3F")
Q.z = toFp("1")
PdQ.x = toFp("0x519B1928F752B0B2143C1C23EB247B370DBB5B9C29B9A3A064D7FBC1B67FAC34B6D3DDA0F3CB87C387B425B36F31B93A8E73252BA701927B767A9DE89D5A92AE")
PdQ.z = toFp("1")
for n := 0; n < b.N; n++ {
xAdd(&PaQ, &P, &Q, &PdQ)
}
}
func BenchmarkXDbl(b *testing.B) {
var P, A point
var PaP point
P.x = toFp("0x6C5B4D4AB0765AAB23C10F8455BE522D3A5363324D7AD641CC67C0A52FC1FFE9F3F8EDFE641478CA93D4D0016D83F21487FD4AF4E02F8A2C237CF27C5604BCC")
P.z = toFp("1")
A.x = toFp("0x599841D7D1FCD92A85759B7A3D2D5E4C56EFB17F19F86EB70E121EA16305EDE45A55868BE069313F821F7D94069EC220A4AC3B85500376710538246E9B3BC138")
A.z = toFp("1")
for n := 0; n < b.N; n++ {
xDbl(&PaP, &P, &A)
}
}
func BenchmarkIsom(b *testing.B) {
var P, kern point
var expPhiP big.Int
var co coeff
var k = uint64(2)
expPhiP.SetString("0x5FEBD68F795F9AEB732ECF0D1507904922F2B0736704E0751EF242B4E191E6F630D83778B5E5681161FD071CDEF7DF4C3A41D0ECEB30E90B119C5BF86C5AB51A", 0)
P.x = toFp("0x5FD8D226C228FD6AA3CCDCAB931C5D3AA000A46B47041F59D9724E517594F696D38F2CB45C987ACF68BB1057D8D518F926D8F55171F337D05354E0022BC66B23")
P.z = toFp("1")
co.a = toFp("0x9E8DBC4914E3C4F080592642DD0B08B9564AB3ADF75EE9B58A685443BA6E39A1ACD1201B7F034077AF344123880AF9D8C77575E6E782E00186881ECE8B87CA3")
co.c = toFp("1")
kern.x = toFp("0x594F77A49EABBF2A12025BC00E1DBC119CDA674B9FE8A00791724B42FEB7D225C4C9940B01B09B8F00B30B0E961212FB63E42614814E38EC9E5E5B0FEBF98C58")
kern.z = toFp("1")
for n := 0; n < b.N; n++ {
isom(&P, &co, &kern, k)
}
}

290
dh/csidh/fp511.go Normal file
View File

@ -0,0 +1,290 @@
package csidh
import (
"math/bits"
"golang.org/x/sys/cpu"
)
// CPU Capabilities. Those flags are referred by assembly code. According to
// https://github.com/golang/go/issues/28230, variables referred from the
// assembly must be in the same package.
// We declare variables not constants, in order to facilitate testing.
var (
// Signals support for BMI2 (MULX)
hasBMI2 = cpu.X86.HasBMI2
// Signals support for ADX and BMI2
hasADXandBMI2 = cpu.X86.HasBMI2 && cpu.X86.HasADX
)
// Constant time select.
// if pick == 0xFF..FF (out = in1)
// if pick == 0 (out = in2)
// else out is undefined
func ctPick64(which uint64, in1, in2 uint64) uint64 {
return (in1 & which) | (in2 & ^which)
}
// ctIsNonZero64 returns 0 in case i == 0, otherwise it returns 1.
// Constant-time.
func ctIsNonZero64(i uint64) int {
// In case i==0 then i-1 will set MSB. Only in such case (i OR ~(i-1))
// will result in MSB being not set (logical implication: (i-1)=>i is
// false iff (i-1)==0 and i==non-zero). In every other case MSB is
// set and hence function returns 1.
return int((i | (^(i - 1))) >> 63)
}
func mulGeneric(r, x, y *fp) {
var s fp // keeps intermediate results
var t1, t2 [9]uint64
var c, q uint64
for i := 0; i < numWords-1; i++ {
q = ((x[i] * y[0]) + s[0]) * pNegInv[0]
mul576(&t1, &p, q)
mul576(&t2, y, x[i])
// x[i]*y + q_i*p
t1[0], c = bits.Add64(t1[0], t2[0], 0)
t1[1], c = bits.Add64(t1[1], t2[1], c)
t1[2], c = bits.Add64(t1[2], t2[2], c)
t1[3], c = bits.Add64(t1[3], t2[3], c)
t1[4], c = bits.Add64(t1[4], t2[4], c)
t1[5], c = bits.Add64(t1[5], t2[5], c)
t1[6], c = bits.Add64(t1[6], t2[6], c)
t1[7], c = bits.Add64(t1[7], t2[7], c)
t1[8], _ = bits.Add64(t1[8], t2[8], c)
// s = (s + x[i]*y + q_i * p) / R
_, c = bits.Add64(t1[0], s[0], 0)
s[0], c = bits.Add64(t1[1], s[1], c)
s[1], c = bits.Add64(t1[2], s[2], c)
s[2], c = bits.Add64(t1[3], s[3], c)
s[3], c = bits.Add64(t1[4], s[4], c)
s[4], c = bits.Add64(t1[5], s[5], c)
s[5], c = bits.Add64(t1[6], s[6], c)
s[6], c = bits.Add64(t1[7], s[7], c)
s[7], _ = bits.Add64(t1[8], 0, c)
}
// last iteration stores result in r
q = ((x[numWords-1] * y[0]) + s[0]) * pNegInv[0]
mul576(&t1, &p, q)
mul576(&t2, y, x[numWords-1])
t1[0], c = bits.Add64(t1[0], t2[0], c)
t1[1], c = bits.Add64(t1[1], t2[1], c)
t1[2], c = bits.Add64(t1[2], t2[2], c)
t1[3], c = bits.Add64(t1[3], t2[3], c)
t1[4], c = bits.Add64(t1[4], t2[4], c)
t1[5], c = bits.Add64(t1[5], t2[5], c)
t1[6], c = bits.Add64(t1[6], t2[6], c)
t1[7], c = bits.Add64(t1[7], t2[7], c)
t1[8], _ = bits.Add64(t1[8], t2[8], c)
_, c = bits.Add64(t1[0], s[0], 0)
r[0], c = bits.Add64(t1[1], s[1], c)
r[1], c = bits.Add64(t1[2], s[2], c)
r[2], c = bits.Add64(t1[3], s[3], c)
r[3], c = bits.Add64(t1[4], s[4], c)
r[4], c = bits.Add64(t1[5], s[5], c)
r[5], c = bits.Add64(t1[6], s[6], c)
r[6], c = bits.Add64(t1[7], s[7], c)
r[7], _ = bits.Add64(t1[8], 0, c)
}
// Returns result of x<y operation.
func isLess(x, y *fp) bool {
for i := numWords - 1; i >= 0; i-- {
v, c := bits.Sub64(y[i], x[i], 0)
if c != 0 {
return false
}
if v != 0 {
return true
}
}
// x == y
return false
}
// r = x + y mod p.
func addRdc(r, x, y *fp) {
var c uint64
var t fp
r[0], c = bits.Add64(x[0], y[0], 0)
r[1], c = bits.Add64(x[1], y[1], c)
r[2], c = bits.Add64(x[2], y[2], c)
r[3], c = bits.Add64(x[3], y[3], c)
r[4], c = bits.Add64(x[4], y[4], c)
r[5], c = bits.Add64(x[5], y[5], c)
r[6], c = bits.Add64(x[6], y[6], c)
r[7], _ = bits.Add64(x[7], y[7], c)
t[0], c = bits.Sub64(r[0], p[0], 0)
t[1], c = bits.Sub64(r[1], p[1], c)
t[2], c = bits.Sub64(r[2], p[2], c)
t[3], c = bits.Sub64(r[3], p[3], c)
t[4], c = bits.Sub64(r[4], p[4], c)
t[5], c = bits.Sub64(r[5], p[5], c)
t[6], c = bits.Sub64(r[6], p[6], c)
t[7], c = bits.Sub64(r[7], p[7], c)
var w = 0 - c
r[0] = ctPick64(w, r[0], t[0])
r[1] = ctPick64(w, r[1], t[1])
r[2] = ctPick64(w, r[2], t[2])
r[3] = ctPick64(w, r[3], t[3])
r[4] = ctPick64(w, r[4], t[4])
r[5] = ctPick64(w, r[5], t[5])
r[6] = ctPick64(w, r[6], t[6])
r[7] = ctPick64(w, r[7], t[7])
}
// r = x - y
func sub512(r, x, y *fp) uint64 {
var c uint64
r[0], c = bits.Sub64(x[0], y[0], 0)
r[1], c = bits.Sub64(x[1], y[1], c)
r[2], c = bits.Sub64(x[2], y[2], c)
r[3], c = bits.Sub64(x[3], y[3], c)
r[4], c = bits.Sub64(x[4], y[4], c)
r[5], c = bits.Sub64(x[5], y[5], c)
r[6], c = bits.Sub64(x[6], y[6], c)
r[7], c = bits.Sub64(x[7], y[7], c)
return c
}
// r = x - y mod p.
func subRdc(r, x, y *fp) {
var c uint64
// Same as sub512(r,x,y). Unfortunately
// compiler is not able to inline it.
r[0], c = bits.Sub64(x[0], y[0], 0)
r[1], c = bits.Sub64(x[1], y[1], c)
r[2], c = bits.Sub64(x[2], y[2], c)
r[3], c = bits.Sub64(x[3], y[3], c)
r[4], c = bits.Sub64(x[4], y[4], c)
r[5], c = bits.Sub64(x[5], y[5], c)
r[6], c = bits.Sub64(x[6], y[6], c)
r[7], c = bits.Sub64(x[7], y[7], c)
// if x<y => r=x-y+p
var w = 0 - c
r[0], c = bits.Add64(r[0], ctPick64(w, p[0], 0), 0)
r[1], c = bits.Add64(r[1], ctPick64(w, p[1], 0), c)
r[2], c = bits.Add64(r[2], ctPick64(w, p[2], 0), c)
r[3], c = bits.Add64(r[3], ctPick64(w, p[3], 0), c)
r[4], c = bits.Add64(r[4], ctPick64(w, p[4], 0), c)
r[5], c = bits.Add64(r[5], ctPick64(w, p[5], 0), c)
r[6], c = bits.Add64(r[6], ctPick64(w, p[6], 0), c)
r[7], _ = bits.Add64(r[7], ctPick64(w, p[7], 0), c)
}
// Fixed-window mod exp for fpBitLen bit value with 4 bit window. Returned
// result is a number in montgomery domain.
// r = b ^ e (mod p).
// Constant time.
func modExpRdcCommon(r, b, e *fp, fpBitLen int) {
var precomp [16]fp
var t fp
var c uint64
// Precompute step, computes an array of small powers of 'b'. As this
// algorithm implements 4-bit window, we need 2^4=16 of such values.
// b^0 = 1, which is equal to R from REDC.
precomp[0] = one // b ^ 0
precomp[1] = *b // b ^ 1
for i := 2; i < 16; i = i + 2 {
// TODO: implement fast squering. Then interleaving fast squaring
// with multiplication should improve performance.
mulRdc(&precomp[i], &precomp[i/2], &precomp[i/2]) // sqr
mulRdc(&precomp[i+1], &precomp[i], b)
}
*r = one
for i := fpBitLen/4 - 1; i >= 0; i-- {
for j := 0; j < 4; j++ {
mulRdc(r, r, r)
}
// note: non resistant to cache SCA
idx := (e[i/16] >> uint((i%16)*4)) & 15
mulRdc(r, r, &precomp[idx])
}
// if p <= r < 2p then r = r-p
t[0], c = bits.Sub64(r[0], p[0], 0)
t[1], c = bits.Sub64(r[1], p[1], c)
t[2], c = bits.Sub64(r[2], p[2], c)
t[3], c = bits.Sub64(r[3], p[3], c)
t[4], c = bits.Sub64(r[4], p[4], c)
t[5], c = bits.Sub64(r[5], p[5], c)
t[6], c = bits.Sub64(r[6], p[6], c)
t[7], c = bits.Sub64(r[7], p[7], c)
var w = 0 - c
r[0] = ctPick64(w, r[0], t[0])
r[1] = ctPick64(w, r[1], t[1])
r[2] = ctPick64(w, r[2], t[2])
r[3] = ctPick64(w, r[3], t[3])
r[4] = ctPick64(w, r[4], t[4])
r[5] = ctPick64(w, r[5], t[5])
r[6] = ctPick64(w, r[6], t[6])
r[7] = ctPick64(w, r[7], t[7])
}
// modExpRdc does modular exponentation of 512-bit number.
// Constant-time.
func modExpRdc512(r, b, e *fp) {
modExpRdcCommon(r, b, e, 512)
}
// modExpRdc does modular exponentation of 64-bit number.
// Constant-time.
func modExpRdc64(r, b *fp, e uint64) {
modExpRdcCommon(r, b, &fp{e}, 64)
}
// isNonQuadRes checks whether value v is quadratic residue.
// Implementation uses Fermat's little theorem (or
// Euler's criterion)
// a^(p-1) == 1, hence
// (a^2) ((p-1)/2) == 1
// Which means v is a quadratic residue iff v^((p-1)/2) == 1.
// Caller provided v must be in montgomery domain.
// Returns 0 in case v is quadratic residue or 1 in case
// v is quadratic non-residue.
func (v *fp) isNonQuadRes() int {
var res fp
var b uint64
modExpRdc512(&res, v, &pMin1By2)
for i := range res {
b |= res[i] ^ one[i]
}
return ctIsNonZero64(b)
}
// isZero returns false in case v is equal to 0, otherwise
// true. Constant time.
func (v *fp) isZero() bool {
var r uint64
for i := 0; i < numWords; i++ {
r |= v[i]
}
return ctIsNonZero64(r) == 0
}
// equal checks if v is equal to in. Constant time
func (v *fp) equal(in *fp) bool {
var r uint64
for i := range v {
r |= v[i] ^ in[i]
}
return ctIsNonZero64(r) == 0
}

50
dh/csidh/fp511_amd64.go Normal file
View File

@ -0,0 +1,50 @@
// +build amd64,!noasm
package csidh
import "math/bits"
//go:noescape
func mul512(a, b *fp, c uint64)
//go:noescape
func mul576(a *[9]uint64, b *fp, c uint64)
//go:noescape
func cswap512(x, y *fp, choice uint8)
//go:noescape
func mulBmiAsm(res, x, y *fp)
// mulRdc performs montgomery multiplication r = x * y mod P.
// Returned result r is already reduced and in Montgomery domain.
func mulRdc(r, x, y *fp) {
var t fp
var c uint64
if hasADXandBMI2 {
mulBmiAsm(r, x, y)
} else {
mulGeneric(r, x, y)
}
// if p <= r < 2p then r = r-p
t[0], c = bits.Sub64(r[0], p[0], 0)
t[1], c = bits.Sub64(r[1], p[1], c)
t[2], c = bits.Sub64(r[2], p[2], c)
t[3], c = bits.Sub64(r[3], p[3], c)
t[4], c = bits.Sub64(r[4], p[4], c)
t[5], c = bits.Sub64(r[5], p[5], c)
t[6], c = bits.Sub64(r[6], p[6], c)
t[7], c = bits.Sub64(r[7], p[7], c)
var w = 0 - c
r[0] = ctPick64(w, r[0], t[0])
r[1] = ctPick64(w, r[1], t[1])
r[2] = ctPick64(w, r[2], t[2])
r[3] = ctPick64(w, r[3], t[3])
r[4] = ctPick64(w, r[4], t[4])
r[5] = ctPick64(w, r[5], t[5])
r[6] = ctPick64(w, r[6], t[6])
r[7] = ctPick64(w, r[7], t[7])
}

192
dh/csidh/fp511_amd64.s Normal file
View File

@ -0,0 +1,192 @@
// +build amd64,!noasm
#include "textflag.h"
// Multipies 512-bit value by 64-bit value. Uses MULQ instruction to
// multiply 2 64-bit values.
//
// Result: x = (y * z) mod 2^512
//
// Registers used: AX, CX, DX, SI, DI, R8
//
// func mul512(a, b *Fp, c uint64)
TEXT ·mul512(SB), NOSPLIT, $0-24
MOVQ a+0(FP), DI // result
MOVQ b+8(FP), SI // multiplicand
// Check wether to use optimized implementation
CMPB ·hasBMI2(SB), $1
JE mul512_mulx
MOVQ c+16(FP), R10 // 64 bit multiplier, used by MULQ
MOVQ R10, AX; MULQ 0(SI); MOVQ DX, R11; MOVQ AX, 0(DI) //x[0]
MOVQ R10, AX; MULQ 8(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 8(DI) //x[1]
MOVQ R10, AX; MULQ 16(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 16(DI) //x[2]
MOVQ R10, AX; MULQ 24(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 24(DI) //x[3]
MOVQ R10, AX; MULQ 32(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 32(DI) //x[4]
MOVQ R10, AX; MULQ 40(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 40(DI) //x[5]
MOVQ R10, AX; MULQ 48(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 48(DI) //x[6]
MOVQ R10, AX; MULQ 56(SI); ADDQ R11, AX; MOVQ AX, 56(DI) //x[7]
RET
// Optimized for CPUs with BMI2
mul512_mulx:
MOVQ c+16(FP), DX // 64 bit multiplier, used by MULX
MULXQ 0(SI), AX, R10; MOVQ AX, 0(DI) // x[0]
MULXQ 8(SI), AX, R11; ADDQ R10, AX; MOVQ AX, 8(DI) // x[1]
MULXQ 16(SI), AX, R10; ADCQ R11, AX; MOVQ AX, 16(DI) // x[2]
MULXQ 24(SI), AX, R11; ADCQ R10, AX; MOVQ AX, 24(DI) // x[3]
MULXQ 32(SI), AX, R10; ADCQ R11, AX; MOVQ AX, 32(DI) // x[4]
MULXQ 40(SI), AX, R11; ADCQ R10, AX; MOVQ AX, 40(DI) // x[5]
MULXQ 48(SI), AX, R10; ADCQ R11, AX; MOVQ AX, 48(DI) // x[6]
MULXQ 56(SI), AX, R11; ADCQ R10, AX; MOVQ AX, 56(DI) // x[7]
RET
// Multipies 512-bit value by 64-bit value and returns 576-bit result. Uses MULQ instruction to
// multiply 2 64-bit values. Returns 576-bit result.
//
// Result: x = (y * z)
//
// Registers used: AX, CX, DX, SI, DI, R8
//
// func mul576(a, b *Fp, c uint64)
TEXT ·mul576(SB), NOSPLIT, $0-24
MOVQ a+0(FP), DI // result
MOVQ b+8(FP), SI // multiplicand
MOVQ c+16(FP), R10 // 64 bit multiplier, used by MULQ
MOVQ R10, AX; MULQ 0(SI); MOVQ DX, R11; MOVQ AX, 0(DI) //x[0]
MOVQ R10, AX; MULQ 8(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 8(DI) //x[1]
MOVQ R10, AX; MULQ 16(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 16(DI) //x[2]
MOVQ R10, AX; MULQ 24(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 24(DI) //x[3]
MOVQ R10, AX; MULQ 32(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 32(DI) //x[4]
MOVQ R10, AX; MULQ 40(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 40(DI) //x[5]
MOVQ R10, AX; MULQ 48(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ DX, R11; MOVQ AX, 48(DI) //x[6]
MOVQ R10, AX; MULQ 56(SI); ADDQ R11, AX; ADCQ $0, DX; MOVQ AX, 56(DI) //x[7]
MOVQ DX, 64(DI) //x[8]
RET
TEXT ·cswap512(SB),NOSPLIT,$0-17
MOVQ x+0(FP), DI
MOVQ y+8(FP), SI
MOVBLZX choice+16(FP), AX // AL = 0 or 1
// Make AX, so that either all bits are set or non
// AX = 0 or 1
NEGQ AX
// Fill xmm15. After this step first half of XMM15 is
// just zeros and second half is whatever in AX
MOVQ AX, X15
// Copy lower double word everywhere else. So that
// XMM15=AL|AL|AL|AL. As AX has either all bits set
// or non result will be that XMM15 has also either
// all bits set or non of them.
PSHUFD $0, X15, X15
#ifndef CSWAP_BLOCK
#define CSWAP_BLOCK(idx) \
MOVOU (idx*16)(DI), X0 \
MOVOU (idx*16)(SI), X1 \
\ // X2 = mask & (X0 ^ X1)
MOVO X1, X2 \
PXOR X0, X2 \
PAND X15, X2 \
\
PXOR X2, X0 \
PXOR X2, X1 \
\
MOVOU X0, (idx*16)(DI) \
MOVOU X1, (idx*16)(SI)
#endif
CSWAP_BLOCK(0)
CSWAP_BLOCK(1)
CSWAP_BLOCK(2)
CSWAP_BLOCK(3)
RET
// mulAsm implements montgomery multiplication interleaved with
// montgomery reduction. It uses MULX and ADCX/ADOX instructions.
// Implementation specific to 511-bit prime 'p'
//
// func mulBmiAsm(res, x, y *fp)
TEXT ·mulBmiAsm(SB),NOSPLIT,$8-24
MOVQ x+8(FP), DI // multiplicand
MOVQ y+16(FP), SI // multiplier
XORQ R8, R8
XORQ R9, R9
XORQ R10, R10
XORQ R11, R11
XORQ R12, R12
XORQ R13, R13
XORQ R14, R14
XORQ R15, R15
MOVQ BP, 0(SP)
XORQ BP, BP
// Uses BMI2 (MULX)
#ifdef MULS_MULX_512
#undef MULS_MULX_512
#endif
#define MULS_MULX_512(idx, r0, r1, r2, r3, r4, r5, r6, r7, r8) \
\ // Reduction step
MOVQ ( 0)(SI), DX \
MULXQ ( 8*idx)(DI), DX, CX \
ADDQ r0, DX \
MULXQ ·pNegInv(SB), DX, CX \
\
XORQ AX, AX \
MULXQ ·p+ 0(SB), AX, BX; ; ADOXQ AX, r0 \
MULXQ ·p+ 8(SB), AX, CX; ADCXQ BX, r1; ADOXQ AX, r1 \
MULXQ ·p+16(SB), AX, BX; ADCXQ CX, r2; ADOXQ AX, r2 \
MULXQ ·p+24(SB), AX, CX; ADCXQ BX, r3; ADOXQ AX, r3 \
MULXQ ·p+32(SB), AX, BX; ADCXQ CX, r4; ADOXQ AX, r4 \
MULXQ ·p+40(SB), AX, CX; ADCXQ BX, r5; ADOXQ AX, r5 \
MULXQ ·p+48(SB), AX, BX; ADCXQ CX, r6; ADOXQ AX, r6 \
MULXQ ·p+56(SB), AX, CX; ADCXQ BX, r7; ADOXQ AX, r7 \
MOVQ $0, AX ; ADCXQ CX, r8; ADOXQ AX, r8 \
\ // Multiplication step
MOVQ (8*idx)(DI), DX \
\
XORQ AX, AX \
MULXQ ( 0)(SI), AX, BX; ADOXQ AX, r0 \
MULXQ ( 8)(SI), AX, CX; ADCXQ BX, r1; ADOXQ AX, r1 \
MULXQ (16)(SI), AX, BX; ADCXQ CX, r2; ADOXQ AX, r2 \
MULXQ (24)(SI), AX, CX; ADCXQ BX, r3; ADOXQ AX, r3 \
MULXQ (32)(SI), AX, BX; ADCXQ CX, r4; ADOXQ AX, r4 \
MULXQ (40)(SI), AX, CX; ADCXQ BX, r5; ADOXQ AX, r5 \
MULXQ (48)(SI), AX, BX; ADCXQ CX, r6; ADOXQ AX, r6 \
MULXQ (56)(SI), AX, CX; ADCXQ BX, r7; ADOXQ AX, r7 \
MOVQ $0, AX ; ADCXQ CX, r8; ADOXQ AX, r8
MULS_MULX_512(0, R8, R9, R10, R11, R12, R13, R14, R15, BP)
MULS_MULX_512(1, R9, R10, R11, R12, R13, R14, R15, BP, R8)
MULS_MULX_512(2, R10, R11, R12, R13, R14, R15, BP, R8, R9)
MULS_MULX_512(3, R11, R12, R13, R14, R15, BP, R8, R9, R10)
MULS_MULX_512(4, R12, R13, R14, R15, BP, R8, R9, R10, R11)
MULS_MULX_512(5, R13, R14, R15, BP, R8, R9, R10, R11, R12)
MULS_MULX_512(6, R14, R15, BP, R8, R9, R10, R11, R12, R13)
MULS_MULX_512(7, R15, BP, R8, R9, R10, R11, R12, R13, R14)
#undef MULS_MULX_512
MOVQ res+0(FP), DI
MOVQ BP, ( 0)(DI)
MOVQ R8, ( 8)(DI)
MOVQ R9, (16)(DI)
MOVQ R10, (24)(DI)
MOVQ R11, (32)(DI)
MOVQ R12, (40)(DI)
MOVQ R13, (48)(DI)
MOVQ R14, (56)(DI)
MOVQ 0(SP), BP
// NOW DI needs to be reduced if > p
RET

117
dh/csidh/fp511_generic.go Normal file
View File

@ -0,0 +1,117 @@
// +build noasm arm64
package csidh
import "math/bits"
func mul512(r, m1 *fp, m2 uint64) {
var c, h, l uint64
c, r[0] = bits.Mul64(m2, m1[0])
h, l = bits.Mul64(m2, m1[1])
r[1], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[2])
r[2], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[3])
r[3], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[4])
r[4], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[5])
r[5], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[6])
r[6], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[7])
r[7], _ = bits.Add64(l, c, 0)
}
func mul576(r *[9]uint64, m1 *fp, m2 uint64) {
var c, h, l uint64
c, r[0] = bits.Mul64(m2, m1[0])
h, l = bits.Mul64(m2, m1[1])
r[1], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[2])
r[2], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[3])
r[3], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[4])
r[4], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[5])
r[5], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[6])
r[6], c = bits.Add64(l, c, 0)
c = h + c
h, l = bits.Mul64(m2, m1[7])
r[7], c = bits.Add64(l, c, 0)
r[8], c = bits.Add64(h, c, 0)
r[8] += c
}
func cswap512(x, y *fp, choice uint8) {
var tmp uint64
mask64 := 0 - uint64(choice)
for i := 0; i < numWords; i++ {
tmp = mask64 & (x[i] ^ y[i])
x[i] = tmp ^ x[i]
y[i] = tmp ^ y[i]
}
}
func mul(res, x, y *fp) {
mulGeneric(res, x, y)
}
// mulRdc performs montgomery multiplication r = x * y mod P.
// Returned result r is already reduced and in Montgomery domain.
func mulRdc(r, x, y *fp) {
var t fp
var c uint64
mulGeneric(r, x, y)
// if p <= r < 2p then r = r-p
t[0], c = bits.Sub64(r[0], p[0], 0)
t[1], c = bits.Sub64(r[1], p[1], c)
t[2], c = bits.Sub64(r[2], p[2], c)
t[3], c = bits.Sub64(r[3], p[3], c)
t[4], c = bits.Sub64(r[4], p[4], c)
t[5], c = bits.Sub64(r[5], p[5], c)
t[6], c = bits.Sub64(r[6], p[6], c)
t[7], c = bits.Sub64(r[7], p[7], c)
var w = uint64(0 - uint64(c))
r[0] = ctPick64(w, r[0], t[0])
r[1] = ctPick64(w, r[1], t[1])
r[2] = ctPick64(w, r[2], t[2])
r[3] = ctPick64(w, r[3], t[3])
r[4] = ctPick64(w, r[4], t[4])
r[5] = ctPick64(w, r[5], t[5])
r[6] = ctPick64(w, r[6], t[6])
r[7] = ctPick64(w, r[7], t[7])
}

443
dh/csidh/fp511_test.go Normal file
View File

@ -0,0 +1,443 @@
package csidh
import (
"math/big"
"math/rand"
"testing"
"golang.org/x/sys/cpu"
)
func resetCPUFeatures() {
hasBMI2 = cpu.X86.HasBMI2
hasADXandBMI2 = cpu.X86.HasBMI2 && cpu.X86.HasADX
}
func testFp512Mul3Nominal(t *testing.T) {
var multiplier64 uint64
var mod big.Int
// modulus: 2^512
mod.SetUint64(1).Lsh(&mod, 512)
for i := 0; i < numIter; i++ {
multiplier64 = rand.Uint64()
fV := randomFp()
exp, _ := new(big.Int).SetString(fp2S(fV), 16)
exp.Mul(exp, new(big.Int).SetUint64(multiplier64))
// Truncate to 512 bits
exp.Mod(exp, &mod)
mul512(&fV, &fV, multiplier64)
res, _ := new(big.Int).SetString(fp2S(fV), 16)
if exp.Cmp(res) != 0 {
t.Errorf("%X != %X", exp, res)
}
}
}
// Check if mul512 produces result
// z = x*y mod 2^512
func TestFp512Mul3_Nominal(t *testing.T) {
hasBMI2 = false
testFp512Mul3Nominal(t)
resetCPUFeatures()
testFp512Mul3Nominal(t)
}
func TestAddRdcRandom(t *testing.T) {
for i := 0; i < numIter; i++ {
a := randomFp()
bigA, _ := new(big.Int).SetString(fp2S(a), 16)
bigA.Mod(bigA, modulus)
copy(a[:], intGetU64(bigA))
b := randomFp()
bigB, _ := new(big.Int).SetString(fp2S(b), 16)
bigB.Mod(bigB, modulus)
copy(b[:], intGetU64(bigB))
addRdc(&a, &a, &b)
bigRet, _ := new(big.Int).SetString(fp2S(a), 16)
bigA.Add(bigA, bigB)
bigA.Mod(bigA, modulus)
if bigRet.Cmp(bigA) != 0 {
t.Errorf("%X != %X", bigRet, bigA)
}
}
}
func TestAddRdcNominal(t *testing.T) {
var res fp
tmp := oneFp512
addRdc(&res, &tmp, &p)
if !ceq512(&res, &tmp) {
t.Errorf("Wrong value\n%X", res)
}
tmp = zeroFp512
addRdc(&res, &p, &p)
if !ceq512(&res, &p) {
t.Errorf("Wrong value\n%X", res)
}
tmp = fp{1, 1, 1, 1, 1, 1, 1, 1}
addRdc(&res, &p, &tmp)
if !ceq512(&res, &tmp) {
t.Errorf("Wrong value\n%X", res)
}
tmp = fp{1, 1, 1, 1, 1, 1, 1, 1}
exp := fp{2, 2, 2, 2, 2, 2, 2, 2}
addRdc(&res, &tmp, &tmp)
if !ceq512(&res, &exp) {
t.Errorf("Wrong value\n%X", res)
}
}
func TestFp512Sub3_Nominal(t *testing.T) {
var ret fp
var mod big.Int
// modulus: 2^512
mod.SetUint64(1).Lsh(&mod, 512)
for i := 0; i < numIter; i++ {
a := randomFp()
bigA, _ := new(big.Int).SetString(fp2S(a), 16)
b := randomFp()
bigB, _ := new(big.Int).SetString(fp2S(b), 16)
sub512(&ret, &a, &b)
bigRet, _ := new(big.Int).SetString(fp2S(ret), 16)
bigA.Sub(bigA, bigB)
// Truncate to 512 bits
bigA.Mod(bigA, &mod)
if bigRet.Cmp(bigA) != 0 {
t.Errorf("%X != %X", bigRet, bigA)
}
}
}
func TestFp512Sub3_DoesntReturnCarry(t *testing.T) {
a := fp{}
b := fp{
0xFFFFFFFFFFFFFFFF, 1,
0, 0,
0, 0,
0, 0}
c := fp{
0xFFFFFFFFFFFFFFFF, 2,
0, 0,
0, 0,
0, 0}
if sub512(&a, &b, &c) != 1 {
t.Error("Carry not returned")
}
}
func TestFp512Sub3_ReturnsCarry(t *testing.T) {
a := fp{}
b := fp{
0xFFFFFFFFFFFFFFFF, 2,
0, 0,
0, 0,
0, 0}
c := fp{
0xFFFFFFFFFFFFFFFF, 1,
0, 0,
0, 0,
0, 0}
if sub512(&a, &b, &c) != 0 {
t.Error("Carry not returned")
}
}
func TestCswap(t *testing.T) {
arg1 := randomFp()
arg2 := randomFp()
arg1cpy := arg1
cswap512(&arg1, &arg2, 0)
if !ceq512(&arg1, &arg1cpy) {
t.Error("cswap swapped")
}
arg1cpy = arg1
cswap512(&arg1, &arg2, 1)
if ceq512(&arg1, &arg1cpy) {
t.Error("cswap didn't swapped")
}
arg1cpy = arg1
cswap512(&arg1, &arg2, 0xF2)
if ceq512(&arg1, &arg1cpy) {
t.Error("cswap didn't swapped")
}
}
func TestSubRdc(t *testing.T) {
var res fp
// 1 - 1 mod P
tmp := oneFp512
subRdc(&res, &tmp, &tmp)
if !ceq512(&res, &zeroFp512) {
t.Errorf("Wrong value\n%X", res)
}
zero(&res)
// 0 - 1 mod P
exp := p
exp[0]--
subRdc(&res, &zeroFp512, &oneFp512)
if !ceq512(&res, &exp) {
t.Errorf("Wrong value\n%X\n%X", res, exp)
}
zero(&res)
// P - (P-1)
pMinusOne := p
pMinusOne[0]--
subRdc(&res, &p, &pMinusOne)
if !ceq512(&res, &oneFp512) {
t.Errorf("Wrong value\n[%X != %X]", res, oneFp512)
}
zero(&res)
subRdc(&res, &p, &oneFp512)
if !ceq512(&res, &pMinusOne) {
t.Errorf("Wrong value\n[%X != %X]", res, pMinusOne)
}
}
func testMulRdc(t *testing.T) {
var res fp
var m1 = fp{
0x85E2579C786882D0, 0x4E3433657E18DA95,
0x850AE5507965A0B3, 0xA15BC4E676475964}
var m2 = fp{
0x85E2579C786882CF, 0x4E3433657E18DA95,
0x850AE5507965A0B3, 0xA15BC4E676475964}
// Expected
var m1m1 = fp{
0xAEBF46E92C88A4B4, 0xCFE857977B946347,
0xD3B264FF08493901, 0x6EEB3D23746B6C7C,
0xC0CA874A349D64B4, 0x7AD4A38B406F8504,
0x38B6B6CEB82472FB, 0x1587015FD7DDFC7D}
var m1m2 = fp{
0x51534771258C4624, 0x2BFEDE86504E2160,
0xE8127D5E9329670B, 0x0C84DBD584491D75,
0x656C73C68B16E38C, 0x01C0DA470B30B8DE,
0x2532E3903EAA950B, 0x3F2C28EA97FE6FEC}
// 0*0
tmp := zeroFp512
mulRdc(&res, &tmp, &tmp)
if !ceq512(&res, &tmp) {
t.Errorf("Wrong value\n%X", res)
}
// 1*m1 == m1
zero(&res)
mulRdc(&res, &m1, &one)
if !ceq512(&res, &m1) {
t.Errorf("Wrong value\n%X", res)
}
// m1*m2 < p
zero(&res)
mulRdc(&res, &m1, &m2)
if !ceq512(&res, &m1m2) {
t.Errorf("Wrong value\n%X", res)
}
// m1*m1 > p
zero(&res)
mulRdc(&res, &m1, &m1)
if !ceq512(&res, &m1m1) {
t.Errorf("Wrong value\n%X", res)
}
}
func TestMulRdc(t *testing.T) {
hasADXandBMI2 = false
testMulRdc(t)
resetCPUFeatures()
testMulRdc(t)
}
func TestModExp(t *testing.T) {
var resExp, base, exp big.Int
var baseFp, expFp, resFp, resFpExp fp
for i := 0; i < numIter; i++ {
// Perform modexp with reference implementation
// in Montgomery domain
base.SetString(fp2S(randomFp()), 16)
exp.SetString(fp2S(randomFp()), 16)
resExp.Exp(&base, &exp, modulus)
toMont(&base, true)
toMont(&resExp, true)
// Convert to fp
copy(baseFp[:], intGetU64(&base))
copy(expFp[:], intGetU64(&exp))
copy(resFpExp[:], intGetU64(&resExp))
// Perform modexp with our implementation
modExpRdc512(&resFp, &baseFp, &expFp)
if !ceq512(&resFp, &resFpExp) {
t.Errorf("Wrong value\n%X!=%X", resFp, intGetU64(&resExp))
}
}
}
// Test uses Euler's Criterion
func TestIsNonQuadRes(t *testing.T) {
var n, nMont big.Int
var pm1o2, rawP big.Int
var nMontFp fp
// (p-1)/2
pm1o2.SetString("0x32da4747ba07c4dffe455868af1f26255a16841d76e446212d7dfe63499164e6d3d56362b3f9aa83a8b398660f85a792e1390dfa2bd6541a8dc0dc8299e3643d", 0)
// modulus value (not in montgomery)
rawP.SetString("0x65b48e8f740f89bffc8ab0d15e3e4c4ab42d083aedc88c425afbfcc69322c9cda7aac6c567f35507516730cc1f0b4f25c2721bf457aca8351b81b90533c6c87b", 0)
// There is 641 quadratic residues in this range
for i := uint64(1); i < 1000; i++ {
n.SetUint64(i)
n.Exp(&n, &pm1o2, &rawP)
// exp == 1 iff n is quadratic non-residue
exp := n.Cmp(big.NewInt(1))
if exp < 0 {
panic("Should never happen")
}
nMont.SetUint64(i)
toMont(&nMont, true)
copy(nMontFp[:], intGetU64(&nMont))
ret := nMontFp.isNonQuadRes()
if ret != exp {
toMont(&nMont, false)
t.Errorf("Test failed for value %s", nMont.Text(10))
}
}
}
func TestCheckSmaller(t *testing.T) {
// p-1
var pMin1 = p
pMin1[0]--
// p-1 < p => 1
if !isLess(&pMin1, &p) {
t.Error("pMin1>p")
}
// p < p-1 => 0
if isLess(&p, &pMin1) {
t.Error("p>pMin1")
}
// p == p => 0
if isLess(&p, &p) {
t.Error("p==p")
}
}
func BenchmarkFp512Sub(b *testing.B) {
var arg1 fp
arg2, arg3 := randomFp(), randomFp()
for n := 0; n < b.N; n++ {
sub512(&arg1, &arg2, &arg3)
}
}
func BenchmarkFp512Mul(b *testing.B) {
var arg1 = rand.Uint64()
arg2, arg3 := randomFp(), randomFp()
for n := 0; n < b.N; n++ {
mul512(&arg2, &arg3, arg1)
}
}
func BenchmarkCSwap(b *testing.B) {
arg1 := randomFp()
arg2 := randomFp()
for n := 0; n < b.N; n++ {
cswap512(&arg1, &arg2, uint8(n%2))
}
}
func BenchmarkAddRdc(b *testing.B) {
var res fp
arg1 := randomFp()
arg2 := randomFp()
for n := 0; n < b.N; n++ {
addRdc(&res, &arg1, &arg2)
}
}
func BenchmarkSubRdc(b *testing.B) {
arg1 := randomFp()
arg2 := randomFp()
var res fp
for n := 0; n < b.N; n++ {
subRdc(&res, &arg1, &arg2)
}
}
func BenchmarkModExpRdc(b *testing.B) {
arg1 := randomFp()
arg2 := randomFp()
var res fp
for n := 0; n < b.N; n++ {
modExpRdc512(&res, &arg1, &arg2)
}
}
func BenchmarkMulGeneric(b *testing.B) {
arg1 := randomFp()
arg2 := randomFp()
var res fp
for n := 0; n < b.N; n++ {
mulGeneric(&res, &arg1, &arg2)
}
}
func BenchmarkMulBmiAsm(b *testing.B) {
arg1 := randomFp()
arg2 := randomFp()
var res fp
for n := 0; n < b.N; n++ {
mulRdc(&res, &arg1, &arg2)
}
}
func BenchmarkMulGenAsm(b *testing.B) {
arg1 := randomFp()
arg2 := randomFp()
var res fp
hasADXandBMI2 = false
for n := 0; n < b.N; n++ {
mulRdc(&res, &arg1, &arg2)
}
resetCPUFeatures()
}

2956
dh/csidh/testdata/csidh_testvectors.dat vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,310 @@
{
"Vectors": [
{
"Id": 0,
"Pk1": "6dab00640f2188d02bba6c44ae620716a27d318ff7d7165d0eae9d07fbfa92c9104164591abc45fa4aab00446c6c67a985ff1407abdb55957a309b00c5cc4e17",
"Pr1": "31224d4b0d4fcbb44cddbd03e5f2bfc4b20132103bcde143130b351edd253ee12bddb4bf5c",
"Pk2": "2e7019f96d032f7fd7de226c4aef299f85af61bfeb77212f1b91833cd7925ddaabbbddc06e5455305bd269e375bd1742201f513a36e33bfda248861b324a5f00",
"Ss": "cd7cccb0015057914d3805bd820100e82373b20acd39c925eb154d8668f5422163fdb1fec9e88f65b060782d5d0d92235c3e0e894fe8cfb9ccba72769d784b23",
"status": "valid",
"comment": ""
},
{
"Id": 1,
"Pk1": "5f6170b8794b019cf55d8b484acbe175bdca3296a19dce1f3426a45ef858a69dc95fadf96c9350708df681193048b07a9235c69dacbd2c02f1574abd7c599a2c",
"Pr1": "3d421d2dc031dd5e0443541f424fb3031b5d015e521155ec5fd14052c0cb24c4fc4040df31",
"Pk2": "7d77ea0fdd457dd95e4846dd47df04f0704a7e204cb291d0922dd0e12370e281ec1dcc41fe58501dc0377ebcb1385bf3be96ccd67348bd4e7335f5188f588b3a",
"Ss": "d222fe01db50e2aab122e5493297a6d5e4f572d540ed0ae32683b2acdf3f36e7462a0acc8f0f6fc365ef306560699cf184432237df534e0219bbf284c7de3f48",
"status": "valid",
"comment": ""
},
{
"Id": 2,
"Pk1": "494682a2e1a9923ea03a011e4c0766150435bd6dd6286c23c42bb39b894883fd12806db2b536975e80aa92bff3046cfae835f29e2d62f1c505e32cd6909d5612",
"Pr1": "503eb0b02f4c2f30cc20522c0335551b0cebddefe023013c03d4e21cdee0bdf3d52ccb24d3",
"Pk2": "1f08928cc0f380f8ac3c8b7727fcb0e8f7621688dbdae5b9cc46ba74cafc6a750b542d6918542587b10c4cc67850b8c202133c1ce09de7107b6401f0b975834a",
"Ss": "c9dee98952c240c515f222bfadb9273c82f29084ae1a0a22003d0dc3e6639f31c107c86035b990163276a129b930e74a1411f0f68e130141dda9ca607898dc25",
"status": "valid",
"comment": ""
},
{
"Id": 3,
"Pk1": "b46a93d93911445c78731cf155b8c8c9f289a04d680d05bca57607da77ced4ed9c9cded6115eaf4b3d7bd83d8f021a603247bfdd378cf4f39d5a3e8aa1f3c21f",
"Pr1": "d00fb24cc43e34e3d034ccc011dbd25330d51dd0bce2b34b11c51ee5b4b142dc1d0b2e3223",
"Pk2": "37d852a3468e940a21f46c2e0ffe5b532d94d75b17d67549d91d42b3ab70271322d58d880074de934b533a32ed4a946d8f34c23f50ca438c7c725077c23f5048",
"Ss": "c31cbdb68fd2c41a2c69a272b45c925426f058a62c6c8f41fe704650c8669e8455c2e43837017d1a77521990c3becd7b400e3329f8860b1b6a1443f0cb0b222d",
"status": "valid",
"comment": ""
},
{
"Id": 4,
"Pk1": "40b65f6d3b2697077b5ecbead5349281aa8ec13a994e43c0031738c09f6f0a8e7eac36f65aaa0e16e84b6a23056cc15edb6b505da48d034153df6e471b253047",
"Pr1": "f5e00bd245e0c203ccf25b2cbbbb2314c41db22dcbbdb010b1423b3cf5bffddfbe312f11e5",
"Pk2": "e3ef78ffa88dccdd5ff193b5b1e2e735dfdd2960d7f149595d9a0eb5504b17662a20968fce42800dc85d86501a19f3f63fd516a8a86f30ab59b4bfdf3592eb18",
"Ss": "c1478f0595ea340737c0dd60a3006910e01e44edd6e814604851271a88f195ae1bec8c0dc4cee9fd00d5342b4b48051878776c688a52302a995f48d23fdbd560",
"status": "valid",
"comment": ""
},
{
"Id": 5,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "544cdd933d23db69c9561008195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 6,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "1891dd933d23db69c9561008195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 7,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "184c4e933d23db69c9561008195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 8,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "184cddae3d23db69c9561008195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 9,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "184cdd931e23db69c9561008195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 10,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "184cdd933df8db69c9561008195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 11,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "184cdd933d23b269c9561008195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 12,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "184cdd933d23dba0c9561008195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 13,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "184cdd933d23db699f561008195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 14,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "184cdd933d23db69c9461008195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 15,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "184cdd933d23db69c9561808195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 16,
"Pk1": "a25f8365986dcf5148cb6c3e9cf70830c299d9109a60d41ea0546a3c0ad2b1543a26928b45641c1e895669f5531941ce29e1263c8f7fc9168659064b459b4344",
"Pr1": "bde4301e1ef2d1c3cee30303f1c40420bed4bb21d1d4f1111012b003b3551deedd4eb301d5",
"Pk2": "4ead6c7e11a20a8d024f88132e308af3c39905e7951d30f2da0b317a7955da60fb099a6020c4c5f7cfda5f562893988ac5b685b99507c5484bb4878d6116bf3d",
"Ss": "184cdd933d23db69c9561011195a6ce59d9167d48be05704d16a14a7418bda56b83b72ade75b8eeeb7a274bcdc5b42267604c58e2ca1292b2957465758d79a55",
"status": "invalid_shared_secret",
"comment": ""
},
{
"Id": 17,
"Pk1": "6deb7ef111841952a6d5795a67295140c2c7ad707a6d681a43beeeaea94655dab9a17e451ed664252cface4ab52d4d3129f091a24d1440598282709b0802910d",
"Pr1": "cfe5e5cb244b2cee05c01bf0522244ed50d3cfd4ee142de2ccbe5c1c4dc024e3cd250d41bf",
"Pk2": "",
"Ss": "",
"status": "invalid_public_key1",
"comment": ""
},
{
"Id": 18,
"Pk1": "f6d276b93ee1b8df1ecafd10e2e6740a2aaee2ce83c829ee848e0d4a0e02ce41873f49f8ee5e97e3256b97df74201e2116454bfa039f4035bc2443901949115d",
"Pr1": "f0e02ce05cd1cfcf2f5dc2bb54d4c2d2534520f5d30e0fc51545b5ded555241211df3454d4",
"Pk2": "",
"Ss": "",
"status": "invalid_public_key1",
"comment": ""
},
{
"Id": 19,
"Pk1": "67d02ac51d0bd87105df92931a0259cb0050b23018b99dd626c27e07a6db67595e449f239ff36c90af7207bf9c6c807deaeafefbeb3149b1494f0d273bdca009",
"Pr1": "5ebf1155d2cee131d3452f04bbdc4fed12df2012bbe5ec4e3be30cfdb32eef15e320502120",
"Pk2": "",
"Ss": "",
"status": "invalid_public_key1",
"comment": ""
},
{
"Id": 20,
"Pk1": "4a6012d9b25b31ccf884020c19b7839adf5af27ae30140257ef23920dc8461788fffc316ecbd92360183f0e087c6b6ec1faf4afd7c4231088348ad150731a22d",
"Pr1": "5b02d5332c55ebed5ebce124bfd02db24c2252d13d133bd5d343f4b11d1f30e4f114f020db",
"Pk2": "",
"Ss": "",
"status": "invalid_public_key1",
"comment": ""
},
{
"Id": 21,
"Pk1": "130c94af0b0c4552dfa7a0e8a8e31151fd12052e5a7840c21606f05c253ed971c4d324b36d4e6cd98f93635b16489cc62ed293b1716c4869658b85cc02011441",
"Pr1": "3552e0e421250022012fb4e4e1d14cb2c53bbc1bcd010d30bff0c4d42fb1b333bb143b4fe3",
"Pk2": "",
"Ss": "",
"status": "invalid_public_key1",
"comment": ""
},
{
"Id": 22,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "95523424cd589421b224d742b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
},
{
"Id": 23,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "c7663424cd589421b224d742b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
},
{
"Id": 24,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "c7521024cd589421b224d742b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
},
{
"Id": 25,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "c75234e9cd589421b224d742b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
},
{
"Id": 26,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "c752342495589421b224d742b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
},
{
"Id": 27,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "c7523424cdcc9421b224d742b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
},
{
"Id": 28,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "c7523424cd58b521b224d742b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
},
{
"Id": 29,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "c7523424cd589493b224d742b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
},
{
"Id": 30,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "c7523424cd5894219624d742b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
},
{
"Id": 31,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "c7523424cd589421b2f3d742b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
},
{
"Id": 32,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "c7523424cd589421b2249542b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
},
{
"Id": 33,
"Pk1": "bfbd184a2e193b5c4535bceecfd8443cc562ff95bf7e351e9c3f0ae35d0c163239310156bc5584072cf19024d1dea7c7a40bb4af366b46465c3bce02f999654c",
"Pr1": "ec34443ef230d3b1cbdfdb5c24cded13e2f1c13fddee442ffbb22bb1334e545ee100220e54",
"Pk2": "c7523424cd589421b224d7f5b7d39ecb6229edd92f12e88f97aed80b20dd9caeca3e31bddcf96674396753a56226a5cd53e4481af8d8ee3ac17d558088c6d534",
"Ss": "69196c279c5a7cbcbbf443509e422d363f0c2e0811b4a741869ee143f5d48117a54deea7c152e9371d1d190fcde44a6c32df27c859e2ed27981aa37f24feb73d",
"status": "invalid_public_key2",
"comment": ""
}
]
}

156
dh/csidh/utils_test.go Normal file
View File

@ -0,0 +1,156 @@
package csidh
import (
"fmt"
"math/big"
mrand "math/rand"
)
// Commonly used variables
var (
// Number of interations
numIter = 10
// Modulus
modulus, _ = new(big.Int).SetString(fp2S(p), 16)
// Zero in fp
zeroFp512 = fp{}
// One in fp
oneFp512 = fp{1, 0, 0, 0, 0, 0, 0, 0}
)
// Converts dst to Montgomery if "toMont==true" or from Montgomery domain otherwise.
func toMont(dst *big.Int, toMont bool) {
var bigP, bigR big.Int
intSetU64(&bigP, p[:])
bigR.SetUint64(1)
bigR.Lsh(&bigR, 512)
if !toMont {
bigR.ModInverse(&bigR, &bigP)
}
dst.Mul(dst, &bigR)
dst.Mod(dst, &bigP)
}
func fp2S(v fp) string {
var str string
for i := 0; i < 8; i++ {
str = fmt.Sprintf("%016x", v[i]) + str
}
return str
}
// zeroize fp
func zero(v *fp) {
for i := range *v {
v[i] = 0
}
}
// returns random value in a range (0,p)
func randomFp() fp {
var u fp
for i := 0; i < 8; i++ {
u[i] = mrand.Uint64()
}
return u
}
// x<y: <0
// x>y: >0
// x==y: 0
func cmp512(x, y *fp) int {
if len(*x) == len(*y) {
for i := len(*x) - 1; i >= 0; i-- {
if x[i] < y[i] {
return -1
} else if x[i] > y[i] {
return 1
}
}
return 0
}
return len(*x) - len(*y)
}
// return x==y for fp
func ceqFp(l, r *fp) bool {
for idx := range l {
if l[idx] != r[idx] {
return false
}
}
return true
}
// return x==y for point
func ceqpoint(l, r *point) bool {
return ceqFp(&l.x, &r.x) && ceqFp(&l.z, &r.z)
}
// return x==y
func ceq512(x, y *fp) bool {
return cmp512(x, y) == 0
}
// Converts src to big.Int. Function assumes that src is a slice of uint64
// values encoded in little-endian byte order.
func intSetU64(dst *big.Int, src []uint64) *big.Int {
var tmp big.Int
dst.SetUint64(0)
for i := range src {
tmp.SetUint64(src[i])
tmp.Lsh(&tmp, uint(i*64))
dst.Add(dst, &tmp)
}
return dst
}
// Converts src to an array of uint64 values encoded in little-endian
// byte order.
func intGetU64(src *big.Int) []uint64 {
var tmp, mod big.Int
dst := make([]uint64, (src.BitLen()/64)+1)
u64 := uint64(0)
u64--
mod.SetUint64(u64)
for i := 0; i < (src.BitLen()/64)+1; i++ {
tmp.Set(src)
tmp.Rsh(&tmp, uint(i)*64)
tmp.And(&tmp, &mod)
dst[i] = tmp.Uint64()
}
return dst
}
// Returns projective coordinate X of normalized EC 'point' (point.x / point.z).
func toNormX(point *point) big.Int {
var bigP, bigDnt, bigDor big.Int
intSetU64(&bigP, p[:])
intSetU64(&bigDnt, point.x[:])
intSetU64(&bigDor, point.z[:])
bigDor.ModInverse(&bigDor, &bigP)
bigDnt.Mul(&bigDnt, &bigDor)
bigDnt.Mod(&bigDnt, &bigP)
return bigDnt
}
// Converts string to fp element in Montgomery domain of cSIDH-512
func toFp(num string) fp {
var tmp big.Int
var ok bool
var ret fp
_, ok = tmp.SetString(num, 0)
if !ok {
panic("Can't parse a number")
}
toMont(&tmp, true)
copy(ret[:], intGetU64(&tmp))
return ret
}

1197
etc/csidh_testvectors.dat Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,11 @@
FROM multiarch/debian-debootstrap:arm64-buster-slim
FROM multiarch/debian-debootstrap:arm64-buster
RUN apt-get upgrade -y
RUN apt-get update -qq
USER root
RUN apt-get install -y make golang
RUN rm -rf /var/lib/apt/lists/*
RUN apt-get install -y make wget ca-certificates
RUN wget https://dl.google.com/go/go1.12.5.linux-arm64.tar.gz
RUN tar -xzf go1.12.5.linux-arm64.tar.gz
RUN mv go /usr/local/
RUN ln -s /usr/local/go/bin/go /usr/bin/
RUN rm -rf /var/lib/apt/lists/* go1.12.5.linux-arm64.tar.gz

View File

@ -5,6 +5,8 @@
# Configuration
# kWindowSize and kP34 must be specified
#
# P434
#kP34 = [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
# P503
#kP34 = [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
# P751

2
go.mod
View File

@ -1,3 +1,5 @@
module github.com/henrydcase/nobs
go 1.12
require golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e