Преглед изворни кода

sidh: updates algorithm to SIDHv3 and refactoring (#6)

* tests: adds continues integration

* Makefile has targets for running tests, benchmarks and code coverage. It also
  contains target for env preparation. In order to run sidh tests
  GOPATH must contain p751toolbox package. I've chosen to manualy
  copy this package to the temporary GOPATH directory. It could also be done
  by calling "go get", but then any commit to both p751toolbox and sidh would need
  to be done in 2 steps.
* .travis.yml calls make and uploads code coverage to Codecov

* move sidh to seperated folder

* sidh: updates algorithm to SIDHv3 and refactoring

* makes an algorithm compatible with Microsoft's SIDHv3
  implementation. This is required to implement SIKE key
  encapsulation mechanism, as specified in PQC NIST submission
  from Nov, 30 2017
* removes SIDHBobPublicKey/SIDHAlicePublicKey/SIDHBobPrivateKey/
  SIDHAlicePrivateKey. Instead PrivateKey and PublicKey structures
  where introduced. Each of this structure stores variant of a key
  A or B. Implementation uses a key variant in order to differentiate
  between 2- and 3-torision groups.
  Main goal of removing "Alice" and "Bob" specific structures is to
  remove code duplication
* Introduces SidhParams: structure to store prime field and SIDH
  domain parameters.
* Refactors public API. Introduces:
    - Functions to generate, import, export keypair
    - DeriveSecret function to create shared secret
    - Supporting functions and types
* Removes code which is not used by implementation anymore, like
  DistortAndDifference(), SecretPoint(), DblAdd(),
  OkeyaSakuraiCoordinateRecovery() and many more. Also tests for those
  functions are removed.
* Adds fixes for key import/export
trials/prep_p503_trial3
Henry Case пре 6 година
committed by Nick Sullivan
родитељ
комит
b332b47af1
15 измењених фајлова са 1819 додато и 1918 уклоњено
  1. +15
    -0
      .travis.yml
  2. +29
    -0
      Makefile
  3. +167
    -10
      p751toolbox/consts.go
  4. +224
    -628
      p751toolbox/curve.go
  5. +231
    -209
      p751toolbox/curve_test.go
  6. +119
    -165
      p751toolbox/isogeny.go
  7. +34
    -78
      p751toolbox/isogeny_test.go
  8. +29
    -0
      p751toolbox/print_test.go
  9. +0
    -421
      sidh.go
  10. +184
    -0
      sidh/api.go
  11. +68
    -0
      sidh/params.go
  12. +384
    -0
      sidh/sidh.go
  13. +4
    -1
      sidh/sidh_amd64.s
  14. +331
    -0
      sidh/sidh_test.go
  15. +0
    -406
      sidh_test.go

+ 15
- 0
.travis.yml Прегледај датотеку

@@ -0,0 +1,15 @@
sudo: required
language: go

go:
- 1.10.x

matrix:
fast_finish: true

script:
- make bench
- make cover

after_script:
- bash <(curl -s https://codecov.io/bash) -t 3cee0edf-64e9-4440-82f1-f1f41967f336

+ 29
- 0
Makefile Прегледај датотеку

@@ -0,0 +1,29 @@
# Constants
MK_FILE_PATH = $(lastword $(MAKEFILE_LIST))
PRJ_DIR = $(abspath $(dir $(MK_FILE_PATH)))
GOPATH_LOCAL = $(PRJ_DIR)/build
GOPATH_PKG = src/github.com/cloudflare/p751sidh
TARGETS = p751toolbox sidh

clean:
rm -rf $(GOPATH_LOCAL)
rm -rf coverage*.txt

prep:
mkdir -p $(GOPATH_LOCAL)/$(GOPATH_PKG)
cp -rf p751toolbox $(GOPATH_LOCAL)/$(GOPATH_PKG)
cp -rf sidh $(GOPATH_LOCAL)/$(GOPATH_PKG)

test-%: clean prep
GOPATH=$(GOPATH_LOCAL) go test -race -v ./$*

bench-%: clean prep
cd $*; GOPATH=$(GOPATH_LOCAL) go test -v -bench=.

cover-%: clean prep
GOPATH=$(GOPATH_LOCAL) go test -race -coverprofile=coverage_$*.txt -covermode=atomic ./$*
cat coverage_$*.txt >> coverage.txt

test: $(addprefix test-, $(TARGETS))
bench: $(addprefix bench-, $(TARGETS))
cover: $(addprefix cover-, $(TARGETS))

+ 167
- 10
p751toolbox/consts.go Прегледај датотеку

@@ -1,16 +1,173 @@
package p751toolbox

// The x-coordinate of P_A = [3^239](11, oddsqrt(11^3 + 11)) on E_0(F_p)
var Affine_xPA = PrimeFieldElement{A: Fp751Element{0xd56fe52627914862, 0x1fad60dc96b5baea, 0x1e137d0bf07ab91, 0x404d3e9252161964, 0x3c5385e4cd09a337, 0x4476426769e4af73, 0x9790c6db989dfe33, 0xe06e1c04d2aa8b5e, 0x38c08185edea73b9, 0xaa41f678a4396ca6, 0x92b9259b2229e9a0, 0x2f9326818be0}}
const (
// The secret key size, in bytes. Secret key is actually different for
// torsion group 2 and 3. For 2-torsion group, last byte of secret key
// is always set to 0.
P751_SecretKeySize = 48
// SIDH public key byte size
P751_PublicKeySize = 564
// SIDH shared secret byte size.
P751_SharedSecretSize = 188
// Max size of secret key for 2-torsion group, corresponds to 2^e2
P751_SecretBitLenA = 372
// Size of secret key for 3-torsion group, corresponds to log_2(3^e3)
P751_SecretBitLenB = 379
// Corresponds to (8 - e2 % 8). Used for ensuring bitlength equal to e2
P751_MaskAliceByte1 = 0x00
P751_MaskAliceByte2 = 0x0f
P751_MaskAliceByte3 = 0xfe
// Corresponds to (8 - e3 % 8). Used for ensuring bitlength equal to e3
P751_MaskBobByte = 0x03
// Sample rate to obtain a value in [0,3^238]
P751_SampleRate = 102
// Size of a compuatation strategy for 2-torsion group
strategySizeA = 185
// Size of a compuatation strategy for 3-torsion group
strategySizeB = 238
)

// The y-coordinate of P_A = [3^239](11, oddsqrt(11^3 + 11)) on E_0(F_p)
var Affine_yPA = PrimeFieldElement{A: Fp751Element{0x332bd16fbe3d7739, 0x7e5e20ff2319e3db, 0xea856234aefbd81b, 0xe016df7d6d071283, 0x8ae42796f73cd34f, 0x6364b408a4774575, 0xa71c97f17ce99497, 0xda03cdd9aa0cbe71, 0xe52b4fda195bd56f, 0xdac41f811fce0a46, 0x9333720f0ee84a61, 0x1399f006e578}}
// The x-coordinate of PA
var P751_affine_PA = ExtensionFieldElement{
A: Fp751Element{
0xC2FC08CEAB50AD8B, 0x1D7D710F55E457B1, 0xE8738D92953DCD6E,
0xBAA7EBEE8A3418AA, 0xC9A288345F03F46F, 0xC8D18D167CFE2616,
0x02043761F6B1C045, 0xAA1975E13180E7E9, 0x9E13D3FDC6690DE6,
0x3A024640A3A3BB4F, 0x4E5AD44E6ACBBDAE, 0x0000544BEB561DAD,
},
B: Fp751Element{
0xE6CC41D21582E411, 0x07C2ECB7C5DF400A, 0xE8E34B521432AEC4,
0x50761E2AB085167D, 0x032CFBCAA6094B3C, 0x6C522F5FDF9DDD71,
0x1319217DC3A1887D, 0xDC4FB25803353A86, 0x362C8D7B63A6AB09,
0x39DCDFBCE47EA488, 0x4C27C99A2C28D409, 0x00003CB0075527C4,
},
}

// The x-coordinate of P_B = [2^372](6, oddsqrt(6^3 + 6)) on E_0(F_p)
var Affine_xPB = PrimeFieldElement{A: Fp751Element{0xf1a8c9ed7b96c4ab, 0x299429da5178486e, 0xef4926f20cd5c2f4, 0x683b2e2858b4716a, 0xdda2fbcc3cac3eeb, 0xec055f9f3a600460, 0xd5a5a17a58c3848b, 0x4652d836f42eaed5, 0x2f2e71ed78b3a3b3, 0xa771c057180add1d, 0xc780a5d2d835f512, 0x114ea3b55ac1}}
// The x-coordinate of QA
var P751_affine_QA = ExtensionFieldElement{
A: Fp751Element{
0xD56FE52627914862, 0x1FAD60DC96B5BAEA, 0x01E137D0BF07AB91,
0x404D3E9252161964, 0x3C5385E4CD09A337, 0x4476426769E4AF73,
0x9790C6DB989DFE33, 0xE06E1C04D2AA8B5E, 0x38C08185EDEA73B9,
0xAA41F678A4396CA6, 0x92B9259B2229E9A0, 0x00002F9326818BE0,
},
B: Fp751Element{
0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
},
}

// The y-coordinate of P_B = [2^372](6, oddsqrt(6^3 + 6)) on E_0(F_p)
var Affine_yPB = PrimeFieldElement{A: Fp751Element{0xd1e1471273e3736b, 0xf9301ba94da241fe, 0xe14ab3c17fef0a85, 0xb4ddd26a037e9e62, 0x66142dfb2afeb69, 0xe297cb70649d6c9e, 0x214dfc6e8b1a0912, 0x9f5ba818b01cf859, 0x87d15b4907c12828, 0xa4da70c53a880dbf, 0xac5df62a72c8f253, 0x2e26a42ec617}}
// The x-coordinate of RA = PA-QA
var P751_affine_RA = ExtensionFieldElement{
A: Fp751Element{
0x0BB84441DFFD19B3, 0x84B4DEA99B48C18E, 0x692DE648AD313805,
0xE6D72761B6DFAEE0, 0x223975C672C3058D, 0xA0FDE0C3CBA26FDC,
0xA5326132A922A3CA, 0xCA5E7F5D5EA96FA4, 0x127C7EFE33FFA8C6,
0x4749B1567E2A23C4, 0x2B7DF5B4AF413BFA, 0x0000656595B9623C,
},
B: Fp751Element{
0xED78C17F1EC71BE8, 0xF824D6DF753859B1, 0x33A10839B2A8529F,
0xFC03E9E25FDEA796, 0xC4708A8054DF1762, 0x4034F2EC034C6467,
0xABFB70FBF06ECC79, 0xDABE96636EC108B7, 0x49CBCFB090605FD3,
0x20B89711819A45A7, 0xFB8E1590B2B0F63E, 0x0000556A5F964AB2,
},
}

// The value of (a+2)/4 for the starting curve E_0 with a=0: this is 1/2
var E0_aPlus2Over4 = PrimeFieldElement{A: Fp751Element{0x124d6, 0x0, 0x0, 0x0, 0x0, 0xb8e0000000000000, 0x9c8a2434c0aa7287, 0xa206996ca9a378a3, 0x6876280d41a41b52, 0xe903b49f175ce04f, 0xf8511860666d227, 0x4ea07cff6e7f}}
// The x-coordinate of PB
var P751_affine_PB = ExtensionFieldElement{
A: Fp751Element{
0xCFB6D71EF867AB0B, 0x4A5FDD76E9A45C76, 0x38B1EE69194B1F03,
0xF6E7B18A7761F3F0, 0xFCF01A486A52C84C, 0xCBE2F63F5AA75466,
0x6487BCE837B5E4D6, 0x7747F5A8C622E9B8, 0x4CBFE1E4EE6AEBBA,
0x8A8616A13FA91512, 0x53DB980E1579E0A5, 0x000058FEBFF3BE69,
},
B: Fp751Element{
0xA492034E7C075CC3, 0x677BAF00B04AA430, 0x3AAE0C9A755C94C8,
0x1DC4B064E9EBB08B, 0x3684EDD04E826C66, 0x9BAA6CB661F01B22,
0x20285A00AD2EFE35, 0xDCE95ABD0497065F, 0x16C7FBB3778E3794,
0x26B3AC29CEF25AAF, 0xFB3C28A31A30AC1D, 0x000046ED190624EE,
},
}

// The x-coordinate of QB
var P751_affine_QB = ExtensionFieldElement{
A: Fp751Element{
0xF1A8C9ED7B96C4AB, 0x299429DA5178486E, 0xEF4926F20CD5C2F4,
0x683B2E2858B4716A, 0xDDA2FBCC3CAC3EEB, 0xEC055F9F3A600460,
0xD5A5A17A58C3848B, 0x4652D836F42EAED5, 0x2F2E71ED78B3A3B3,
0xA771C057180ADD1D, 0xC780A5D2D835F512, 0x0000114EA3B55AC1,
},
B: Fp751Element{
0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0x0000000000000000,
},
}

// The x-coordinate of RB = PB - QB
var P751_affine_RB = ExtensionFieldElement{
A: Fp751Element{
0x1C0D6733769D0F31, 0xF084C3086E2659D1, 0xE23D5DA27BCBD133,
0xF38EC9A8D5864025, 0x6426DC781B3B645B, 0x4B24E8E3C9FB03EE,
0x6432792F9D2CEA30, 0x7CC8E8B1AE76E857, 0x7F32BFB626BB8963,
0xB9F05995B48D7B74, 0x4D71200A7D67E042, 0x0000228457AF0637,
},
B: Fp751Element{
0x4AE37E7D8F72BD95, 0xDD2D504B3E993488, 0x5D14E7FA1ECB3C3E,
0x127610CEB75D6350, 0x255B4B4CAC446B11, 0x9EA12336C1F70CAF,
0x79FA68A2147BC2F8, 0x11E895CFDADBBC49, 0xE4B9D3C4D6356C18,
0x44B25856A67F951C, 0x5851541F61308D0B, 0x00002FFD994F7E4C,
},
}

// 2-torsion group computation strategy
var P751_AliceIsogenyStrategy = [strategySizeA]uint32{
0x50, 0x30, 0x1B, 0x0F, 0x08, 0x04, 0x02, 0x01, 0x01, 0x02,
0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x07,
0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x01,
0x01, 0x01, 0x01, 0x0C, 0x07, 0x04, 0x02, 0x01, 0x01, 0x02,
0x01, 0x01, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x05, 0x03,
0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x15,
0x0C, 0x07, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03,
0x02, 0x01, 0x01, 0x01, 0x01, 0x05, 0x03, 0x02, 0x01, 0x01,
0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x09, 0x05, 0x03, 0x02,
0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x04, 0x02,
0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x21, 0x14, 0x0C, 0x07,
0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x01,
0x01, 0x01, 0x01, 0x05, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x01, 0x08, 0x05, 0x03, 0x02, 0x01, 0x01,
0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01,
0x02, 0x01, 0x01, 0x10, 0x08, 0x04, 0x02, 0x01, 0x01, 0x01,
0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01,
0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04, 0x02,
0x01, 0x01, 0x02, 0x01, 0x01}

// 3-torsion group computation strategy
var P751_BobIsogenyStrategy = [strategySizeB]uint32{
0x70, 0x3F, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x01, 0x02,
0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x08,
0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04, 0x02, 0x01,
0x01, 0x02, 0x01, 0x01, 0x10, 0x08, 0x04, 0x02, 0x01, 0x01,
0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01,
0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04, 0x02,
0x01, 0x01, 0x02, 0x01, 0x01, 0x1F, 0x10, 0x08, 0x04, 0x02,
0x01, 0x01, 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02,
0x01, 0x01, 0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01,
0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x0F, 0x08, 0x04,
0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01,
0x02, 0x01, 0x01, 0x07, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01,
0x01, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x31, 0x1F, 0x10,
0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04, 0x02,
0x01, 0x01, 0x02, 0x01, 0x01, 0x08, 0x04, 0x02, 0x01, 0x01,
0x02, 0x01, 0x01, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01,
0x0F, 0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x04,
0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x07, 0x04, 0x02, 0x01,
0x01, 0x02, 0x01, 0x01, 0x03, 0x02, 0x01, 0x01, 0x01, 0x01,
0x15, 0x0C, 0x08, 0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01,
0x04, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01, 0x05, 0x03, 0x02,
0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x09, 0x05,
0x03, 0x02, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01,
0x04, 0x02, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01}

+ 224
- 628
p751toolbox/curve.go Прегледај датотеку

@@ -8,92 +8,20 @@ type ProjectiveCurveParameters struct {
C ExtensionFieldElement
}

func (params *ProjectiveCurveParameters) FromAffine(a *ExtensionFieldElement) {
params.A = *a
params.C = oneExtensionField
}

type CachedCurveParameters struct {
Aplus2C ExtensionFieldElement
C4 ExtensionFieldElement
}

type CachedTripleCurveParameters struct {
Aminus2C ExtensionFieldElement
C2 ExtensionFieldElement
}

// = 256
var const256 = ExtensionFieldElement{
A: Fp751Element{0x249ad67, 0x0, 0x0, 0x0, 0x0, 0x730000000000000, 0x738154969973da8b, 0x856657c146718c7f, 0x461860e4e363a697, 0xf9fd6510bba838cd, 0x4e1a3c3f06993c0c, 0x55abef5b75c7},
B: Fp751Element{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
}

// Recover the curve parameters from three points on the curve.
func RecoverCurveParameters(affine_xP, affine_xQ, affine_xQmP *ExtensionFieldElement) ProjectiveCurveParameters {
var curveParams ProjectiveCurveParameters
var t0, t1 ExtensionFieldElement
t0.One() // = 1
t1.Mul(affine_xP, affine_xQ) // = x_P * x_Q
t0.Sub(&t0, &t1) // = 1 - x_P * x_Q
t1.Mul(affine_xP, affine_xQmP) // = x_P * x_{Q-P}
t0.Sub(&t0, &t1) // = 1 - x_P * x_Q - x_P * x_{Q-P}
t1.Mul(affine_xQ, affine_xQmP) // = x_Q * x_{Q-P}
t0.Sub(&t0, &t1) // = 1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P}
curveParams.A.Square(&t0) // = (1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P})^2
t1.Mul(&t1, affine_xP) // = x_P * x_Q * x_{Q-P}
t1.Add(&t1, &t1) // = 2 * x_P * x_Q * x_{Q-P}
curveParams.C.Add(&t1, &t1) // = 4 * x_P * x_Q * x_{Q-P}
t0.Add(affine_xP, affine_xQ) // = x_P + x_Q
t0.Add(&t0, affine_xQmP) // = x_P + x_Q + x_{Q-P}
t1.Mul(&curveParams.C, &t0) // = 4 * x_P * x_Q * x_{Q-P} * (x_P + x_Q + x_{Q-P})
curveParams.A.Sub(&curveParams.A, &t1) // = (1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P})^2 - 4 * x_P * x_Q * x_{Q-P} * (x_P + x_Q + x_{Q-P})

return curveParams
}

// Compute the j-invariant (not the J-invariant) of the given curve.
func (curveParams *ProjectiveCurveParameters) JInvariant() ExtensionFieldElement {
var v0, v1, v2, v3 ExtensionFieldElement
A := &curveParams.A
C := &curveParams.C
v0.Square(C) // C^2
v1.Square(A) // A^2
v2.Add(&v0, &v0) // 2C^2
v3.Add(&v2, &v0) // 3C^2
v2.Add(&v2, &v2) // 4C^2
v2.Sub(&v1, &v2) // A^2 - 4C^2
v1.Sub(&v1, &v3) // A^2 - 3C^2
v3.Square(&v1) // (A^2 - 3C^2)^2
v3.Mul(&v3, &v1) // (A^2 - 3C^2)^3
v0.Square(&v0) // C^4
v3.Mul(&v3, &const256) // 256(A^2 - 3C^2)^3
v2.Mul(&v2, &v0) // C^4(A^2 - 4C^2)
v2.Inv(&v2) // 1/C^4(A^2 - 4C^2)
v0.Mul(&v3, &v2) // 256(A^2 - 3C^2)^3 / C^4(A^2 - 4C^2)
return v0
}

// Compute cached parameters A + 2C, 4C.
func (curve *ProjectiveCurveParameters) cachedParams() CachedCurveParameters {
var cached CachedCurveParameters
cached.Aplus2C.Add(&curve.C, &curve.C) // = 2*C
cached.C4.Add(&cached.Aplus2C, &cached.Aplus2C) // = 4*C
cached.Aplus2C.Add(&cached.Aplus2C, &curve.A) // = 2*C + A
return cached
}

// Compute cached parameters A - 2C, 2C.
func (curve *ProjectiveCurveParameters) cachedTripleParams() CachedTripleCurveParameters {
var cached CachedTripleCurveParameters
cached.C2.Add(&curve.C, &curve.C) // = 2*C
cached.Aminus2C.Sub(&curve.A, &cached.C2) // = A- 2*C
return cached
// Stores curve projective parameters equivalent to A/C. Meaning of the
// values depends on the context. When working with isogenies over
// subgroup that are powers of:
// * three then (A:C) ~ (A+2C:A-2C)
// * four then (A:C) ~ (A+2C: 4C)
// See Appendix A of SIKE for more details
type CurveCoefficientsEquiv struct {
A ExtensionFieldElement
C ExtensionFieldElement
}

// A point on the projective line P^1(F_{p^2}).
//
// This represents a point on the (Kummer line) of a Montgomery curve. The
// This represents a point on the Kummer line of a Montgomery curve. The
// curve is specified by a ProjectiveCurveParameters struct.
type ProjectivePoint struct {
X ExtensionFieldElement
@@ -109,6 +37,127 @@ type ProjectivePrimeFieldPoint struct {
Z PrimeFieldElement
}

func (params *ProjectiveCurveParameters) FromAffine(a *ExtensionFieldElement) {
params.A = *a
params.C.One()
}

// Computes j-invariant for a curve y2=x3+A/Cx+x with A,C in F_(p^2). Result
// is returned in jBytes buffer, encoded in little-endian format. Caller
// provided jBytes buffer has to be big enough to j-invariant value. In case
// of SIDH, buffer size must be at least size of shared secret.
// Implementation corresponds to Algorithm 9 from SIKE.
func (cparams *ProjectiveCurveParameters) Jinvariant(jBytes []byte) {
var j, t0, t1 ExtensionFieldElement

j.Square(&cparams.A) // j = A^2
t1.Square(&cparams.C) // t1 = C^2
t0.Add(&t1, &t1) // t0 = t1 + t1
t0.Sub(&j, &t0) // t0 = j - t0
t0.Sub(&t0, &t1) // t0 = t0 - t1
j.Sub(&t0, &t1) // t0 = t0 - t1
t1.Square(&t1) // t1 = t1^2
j.Mul(&j, &t1) // t0 = t0 * t1
t0.Add(&t0, &t0) // t0 = t0 + t0
t0.Add(&t0, &t0) // t0 = t0 + t0
t1.Square(&t0) // t1 = t0^2
t0.Mul(&t0, &t1) // t0 = t0 * t1
t0.Add(&t0, &t0) // t0 = t0 + t0
t0.Add(&t0, &t0) // t0 = t0 + t0
j.Inv(&j) // j = 1/j
j.Mul(&t0, &j) // j = t0 * j

j.ToBytes(jBytes)
}

// Given affine points x(P), x(Q) and x(Q-P) in a extension field F_{p^2}, function
// recorvers projective coordinate A of a curve. This is Algorithm 10 from SIKE.
func (curve *ProjectiveCurveParameters) RecoverCoordinateA(xp, xq, xr *ExtensionFieldElement) {
var t0, t1 ExtensionFieldElement

t1.Add(xp, xq) // t1 = Xp + Xq
t0.Mul(xp, xq) // t0 = Xp * Xq
curve.A.Mul(xr, &t1) // A = X(q-p) * t1
curve.A.Add(&curve.A, &t0) // A = A + t0
t0.Mul(&t0, xr) // t0 = t0 * X(q-p)
curve.A.Sub(&curve.A, &oneExtensionField) // A = A - 1
t0.Add(&t0, &t0) // t0 = t0 + t0
t1.Add(&t1, xr) // t1 = t1 + X(q-p)
t0.Add(&t0, &t0) // t0 = t0 + t0
curve.A.Square(&curve.A) // A = A^2
t0.Inv(&t0) // t0 = 1/t0
curve.A.Mul(&curve.A, &t0) // A = A * t0
curve.A.Sub(&curve.A, &t1) // A = A - t1
}

// Computes equivalence (A:C) ~ (A+2C : A-2C)
func (curve *ProjectiveCurveParameters) CalcCurveParamsEquiv3() CurveCoefficientsEquiv {
var coef CurveCoefficientsEquiv
var c2 ExtensionFieldElement

c2.Add(&curve.C, &curve.C)
// A24p = A+2*C
coef.A.Add(&curve.A, &c2)
// A24m = A-2*C
coef.C.Sub(&curve.A, &c2)
return coef
}

// Computes equivalence (A:C) ~ (A+2C : 4C)
func (cparams *ProjectiveCurveParameters) CalcCurveParamsEquiv4() CurveCoefficientsEquiv {
var coefEq CurveCoefficientsEquiv

coefEq.C.Add(&cparams.C, &cparams.C)
// A24p = A+2C
coefEq.A.Add(&cparams.A, &coefEq.C)
// C24 = 4*C
coefEq.C.Add(&coefEq.C, &coefEq.C)
return coefEq
}

// Helper function for RightToLeftLadder(). Returns A+2C / 4.
func (cparams *ProjectiveCurveParameters) calcAplus2Over4() (ret ExtensionFieldElement) {
var tmp ExtensionFieldElement
// 2C
tmp.Add(&cparams.C, &cparams.C)
// A+2C
ret.Add(&cparams.A, &tmp)
// 1/4C
tmp.Add(&tmp, &tmp).Inv(&tmp)
// A+2C/4C
ret.Mul(&ret, &tmp)
return
}

// Recovers (A:C) curve parameters from projectively equivalent (A+2C:A-2C).
func (cparams *ProjectiveCurveParameters) RecoverCurveCoefficients3(coefEq *CurveCoefficientsEquiv) {
cparams.A.Add(&coefEq.A, &coefEq.C)
// cparams.A = 2*(A+2C+A-2C) = 4A
cparams.A.Add(&cparams.A, &cparams.A)
// cparams.C = (A+2C-A+2C) = 4C
cparams.C.Sub(&coefEq.A, &coefEq.C)
return
}

// Recovers (A:C) curve parameters from projectively equivalent (A+2C:4C).
func (cparams *ProjectiveCurveParameters) RecoverCurveCoefficients4(coefEq *CurveCoefficientsEquiv) {
var half = ExtensionFieldElement{
A: Fp751Element{
0x00000000000124D6, 0x0000000000000000, 0x0000000000000000,
0x0000000000000000, 0x0000000000000000, 0xB8E0000000000000,
0x9C8A2434C0AA7287, 0xA206996CA9A378A3, 0x6876280D41A41B52,
0xE903B49F175CE04F, 0x0F8511860666D227, 0x00004EA07CFF6E7F},
B: Fp751Element{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
}
// cparams.C = (4C)*1/2=2C
cparams.C.Mul(&coefEq.C, &half)
// cparams.A = A+2C - 2C = A
cparams.A.Sub(&coefEq.A, &cparams.C)
// cparams.C = 2C * 1/2 = C
cparams.C.Mul(&cparams.C, &half)
return
}

func (point *ProjectivePoint) FromAffinePrimeField(x *PrimeFieldElement) {
point.X.A = x.A
point.X.B = zeroExtensionField.B
@@ -161,575 +210,122 @@ func ProjectivePrimeFieldPointConditionalSwap(xP, xQ *ProjectivePrimeFieldPoint,
PrimeFieldConditionalSwap(&xP.Z, &xQ.Z, choice)
}

// Given xP = x(P), xQ = x(Q), and xPmQ = x(P-Q), compute xR = x(P+Q).
//
// Returns xR to allow chaining. Safe to overlap xP, xQ, xR.
func (xR *ProjectivePoint) Add(xP, xQ, xPmQ *ProjectivePoint) *ProjectivePoint {
// Algorithm 1 of Costello-Smith.
var v0, v1, v2, v3, v4 ExtensionFieldElement
v0.Add(&xP.X, &xP.Z) // X_P + Z_P
v1.Sub(&xQ.X, &xQ.Z).Mul(&v1, &v0) // (X_Q - Z_Q)(X_P + Z_P)
v0.Sub(&xP.X, &xP.Z) // X_P - Z_P
v2.Add(&xQ.X, &xQ.Z).Mul(&v2, &v0) // (X_Q + Z_Q)(X_P - Z_P)
v3.Add(&v1, &v2).Square(&v3) // 4(X_Q X_P - Z_Q Z_P)^2
v4.Sub(&v1, &v2).Square(&v4) // 4(X_Q Z_P - Z_Q X_P)^2
v0.Mul(&xPmQ.Z, &v3) // 4X_{P-Q}(X_Q X_P - Z_Q Z_P)^2
xR.Z.Mul(&xPmQ.X, &v4) // 4Z_{P-Q}(X_Q Z_P - Z_Q X_P)^2
xR.X = v0
return xR
}

// Given xP = x(P), xQ = x(Q), and xPmQ = x(P-Q), compute xR = x(P+Q).
//
// Returns xR to allow chaining. Safe to overlap xP, xQ, xR.
func (xR *ProjectivePrimeFieldPoint) Add(xP, xQ, xPmQ *ProjectivePrimeFieldPoint) *ProjectivePrimeFieldPoint {
// Algorithm 1 of Costello-Smith.
var v0, v1, v2, v3, v4 PrimeFieldElement
v0.Add(&xP.X, &xP.Z) // X_P + Z_P
v1.Sub(&xQ.X, &xQ.Z).Mul(&v1, &v0) // (X_Q - Z_Q)(X_P + Z_P)
v0.Sub(&xP.X, &xP.Z) // X_P - Z_P
v2.Add(&xQ.X, &xQ.Z).Mul(&v2, &v0) // (X_Q + Z_Q)(X_P - Z_P)
v3.Add(&v1, &v2).Square(&v3) // 4(X_Q X_P - Z_Q Z_P)^2
v4.Sub(&v1, &v2).Square(&v4) // 4(X_Q Z_P - Z_Q X_P)^2
v0.Mul(&xPmQ.Z, &v3) // 4X_{P-Q}(X_Q X_P - Z_Q Z_P)^2
xR.Z.Mul(&xPmQ.X, &v4) // 4Z_{P-Q}(X_Q Z_P - Z_Q X_P)^2
xR.X = v0
return xR
}

// Given xP = x(P) and cached curve parameters Aplus2C = A + 2*C, C4 = 4*C, compute xQ = x([2]P).
//
// Returns xQ to allow chaining. Safe to overlap xP, xQ.
func (xQ *ProjectivePoint) Double(xP *ProjectivePoint, curve *CachedCurveParameters) *ProjectivePoint {
// Algorithm 2 of Costello-Smith, amended to work with projective curve coefficients.
var v1, v2, v3, xz4 ExtensionFieldElement
v1.Add(&xP.X, &xP.Z).Square(&v1) // (X+Z)^2
v2.Sub(&xP.X, &xP.Z).Square(&v2) // (X-Z)^2
xz4.Sub(&v1, &v2) // 4XZ = (X+Z)^2 - (X-Z)^2
v2.Mul(&v2, &curve.C4) // 4C(X-Z)^2
xQ.X.Mul(&v1, &v2) // 4C(X+Z)^2(X-Z)^2
v3.Mul(&xz4, &curve.Aplus2C) // 4XZ(A + 2C)
v3.Add(&v3, &v2) // 4XZ(A + 2C) + 4C(X-Z)^2
xQ.Z.Mul(&v3, &xz4) // (4XZ(A + 2C) + 4C(X-Z)^2)4XZ
// Now (xQ.x : xQ.z)
// = (4C(X+Z)^2(X-Z)^2 : (4XZ(A + 2C) + 4C(X-Z)^2)4XZ )
// = ((X+Z)^2(X-Z)^2 : (4XZ((A + 2C)/4C) + (X-Z)^2)4XZ )
// = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ )
return xQ
}

// Given xP = x(P) and cached curve parameter aPlus2Over4 = (a+2)/4, compute xQ = x([2]P).
//
// Note that we don't use projective curve coefficients here because we only
// ever use a fixed curve (in our case, the base curve E_0).
//
// Returns xQ to allow chaining. Safe to overlap xP, xQ.
func (xQ *ProjectivePrimeFieldPoint) Double(xP *ProjectivePrimeFieldPoint, aPlus2Over4 *PrimeFieldElement) *ProjectivePrimeFieldPoint {
// Algorithm 2 of Costello-Smith
var v1, v2, v3, xz4 PrimeFieldElement
v1.Add(&xP.X, &xP.Z).Square(&v1) // (X+Z)^2
v2.Sub(&xP.X, &xP.Z).Square(&v2) // (X-Z)^2
xz4.Sub(&v1, &v2) // 4XZ = (X+Z)^2 - (X-Z)^2
xQ.X.Mul(&v1, &v2) // (X+Z)^2(X-Z)^2
v3.Mul(&xz4, aPlus2Over4) // 4XZ((a+2)/4)
v3.Add(&v3, &v2) // 4XZ((a+2)/4) + (X-Z)^2
xQ.Z.Mul(&v3, &xz4) // (4XZ((a+2)/4) + (X-Z)^2)4XZ
// Now (xQ.x : xQ.z)
// = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ )
return xQ
}

// DblAdd method calculates the x-coordinate of 2P and P+Q from the x-coordinate of P, Q and P-Q.
// Params: C4 = 4*C and Aplus2C = (A+2C)
// Cost: 8M+4S+8A in Fp2
func (xP *ProjectivePoint) DblAdd(xQ, xPmQ *ProjectivePoint, params *CachedCurveParameters) (x2P, xPaddQ ProjectivePoint) {
var t0, t1, t2, t3 ExtensionFieldElement
x1, z1 := &xPmQ.X, &xPmQ.Z
x2, z2 := &xP.X, &xP.Z
x3, z3 := &xQ.X, &xQ.Z

t0.Add(x2, z2) // A = x2+z2
t1.Sub(x2, z2) // B = x2-z2
t3.Add(x3, z3) // C = x3+z3
t2.Sub(x3, z3) // D = x3-z3
t2.Mul(&t2, &t0) // DA = D*A
t3.Mul(&t3, &t1) // CB = C*B

xPaddQ.X.Add(&t2, &t3) // x5 = DA+CB
xPaddQ.Z.Sub(&t2, &t3) // z5 = DA-CB
xPaddQ.X.Square(&xPaddQ.X) // x5 = (DA+CB)^2
xPaddQ.Z.Square(&xPaddQ.Z) // z5 = (DA-CB)^2
xPaddQ.X.Mul(&xPaddQ.X, z1) // x5 = z1*(DA+CB)^2
xPaddQ.Z.Mul(&xPaddQ.Z, x1) // z5 = x1*(DA-CB)^2

t0.Square(&t0) // t0 = AA = A^2
t1.Square(&t1) // t1 = BB = B^2
t2.Sub(&t0, &t1) // t2 = E = AA-BB
t3.Mul(&t1, &params.C4) // t3 = (4C)*BB
x2P.Z.Mul(&t2, &params.Aplus2C) // z4 = (A+2C)*E
x2P.Z.Add(&x2P.Z, &t3) // z4 = (4C)*BB+(A+2C)*E
x2P.X.Mul(&t0, &t3) // x4 = AA*(4C)*BB
x2P.Z.Mul(&x2P.Z, &t2) // z4 = E*((4C)*BB+(A+2C)*E)

return
}

// DblAdd method calculates the x-coordinate of 2P and P+Q from the x-coordinate of P, Q and P-Q.
// Assumptions:
// aPlus2Over2 = (A+2)/4.
// z(P-Q) = 1, the Z-coordinate of P-Q is equal to 1.
// Cost: 6M+4S+8A in Fp
func (xP *ProjectivePrimeFieldPoint) DblAdd(xQ, xPmQ *ProjectivePrimeFieldPoint, aPlus2Over4 *PrimeFieldElement) (x2P, xPaddQ ProjectivePrimeFieldPoint) {
var t0, t1, t2, t3 PrimeFieldElement
x1 := &xPmQ.X
x2, z2 := &xP.X, &xP.Z
x3, z3 := &xQ.X, &xQ.Z

t0.Add(x2, z2) // A = x2+z2
t1.Sub(x2, z2) // B = x2-z2
t3.Add(x3, z3) // C = x3+z3
t2.Sub(x3, z3) // D = x3-z3
t2.Mul(&t2, &t0) // DA = D*A
t3.Mul(&t3, &t1) // CB = C*B

xPaddQ.X.Add(&t2, &t3) // x5 = DA+CB
xPaddQ.Z.Sub(&t2, &t3) // z5 = DA-CB
xPaddQ.X.Square(&xPaddQ.X) // x5 = (DA+CB)^2
xPaddQ.Z.Square(&xPaddQ.Z) // z5 = (DA-CB)^2
xPaddQ.Z.Mul(&xPaddQ.Z, x1) // z5 = x1*(DA-CB)^2

t0.Square(&t0) // t0 = AA = A^2
t1.Square(&t1) // t1 = BB = B^2
x2P.X.Mul(&t0, &t1) // x4 = AA*BB
t0.Sub(&t0, &t1) // t2 = E = AA-BB
x2P.Z.Mul(&t0, aPlus2Over4) // z4 = ((A+2C)/4)*E
x2P.Z.Add(&x2P.Z, &t1) // z4 = BB+((A+2C)/4)*E
x2P.Z.Mul(&x2P.Z, &t0) // z4 = E*(BB+((A+2C)/4)*E)

// Combined coordinate doubling and differential addition. Takes projective points
// P,Q,Q-P and (A+2C)/4C curve E coefficient. Returns 2*P and P+Q calculated on E.
// Function is used only by RightToLeftLadder. Corresponds to Algorithm 5 of SIKE
func xDblAdd(P, Q, QmP *ProjectivePoint, a24 *ExtensionFieldElement) (dblP, PaQ ProjectivePoint) {
var t0, t1, t2 ExtensionFieldElement
xQmP, zQmP := &QmP.X, &QmP.Z
xPaQ, zPaQ := &PaQ.X, &PaQ.Z
x2P, z2P := &dblP.X, &dblP.Z
xP, zP := &P.X, &P.Z
xQ, zQ := &Q.X, &Q.Z

t0.Add(xP, zP) // t0 = Xp+Zp
t1.Sub(xP, zP) // t1 = Xp-Zp
x2P.Square(&t0) // 2P.X = t0^2
t2.Sub(xQ, zQ) // t2 = Xq-Zq
xPaQ.Add(xQ, zQ) // Xp+q = Xq+Zq
t0.Mul(&t0, &t2) // t0 = t0 * t2
z2P.Mul(&t1, &t1) // 2P.Z = t1 * t1
t1.Mul(&t1, xPaQ) // t1 = t1 * Xp+q
t2.Sub(x2P, z2P) // t2 = 2P.X - 2P.Z
x2P.Mul(x2P, z2P) // 2P.X = 2P.X * 2P.Z
xPaQ.Mul(a24, &t2) // Xp+q = A24 * t2
zPaQ.Sub(&t0, &t1) // Zp+q = t0 - t1
z2P.Add(xPaQ, z2P) // 2P.Z = Xp+q + 2P.Z
xPaQ.Add(&t0, &t1) // Xp+q = t0 + t1
z2P.Mul(z2P, &t2) // 2P.Z = 2P.Z * t2
zPaQ.Square(zPaQ) // Zp+q = Zp+q ^ 2
xPaQ.Square(xPaQ) // Xp+q = Xp+q ^ 2
zPaQ.Mul(xQmP, zPaQ) // Zp+q = Xq-p * Zp+q
xPaQ.Mul(zQmP, xPaQ) // Xp+q = Zq-p * Xp+q
return
}

// Given the curve parameters, xP = x(P), and k >= 0, compute xQ = x([2^k]P).
// Given the curve parameters, xP = x(P), and k >= 0, compute x2P = x([2^k]P).
//
// Returns xQ to allow chaining. Safe to overlap xP, xQ.
func (xQ *ProjectivePoint) Pow2k(curve *ProjectiveCurveParameters, xP *ProjectivePoint, k uint32) *ProjectivePoint {
cachedParams := curve.cachedParams()
*xQ = *xP
for i := uint32(0); i < k; i++ {
xQ.Double(xQ, &cachedParams)
}

return xQ
}
// Returns x2P to allow chaining. Safe to overlap xP, x2P.
func (x2P *ProjectivePoint) Pow2k(params *CurveCoefficientsEquiv, xP *ProjectivePoint, k uint32) *ProjectivePoint {
var t0, t1 ExtensionFieldElement

// Uses the efficient Montgomery tripling formulas from FLOR-SIDH-x64
// Given xP = x(P) and cached tripling curve parameters Aminus2C = A - 2*C, C2 = 2*C, compute xQ = x([3]P).
// Returns xQ to allow chaining. Safe to overlap xP, xQ.
// Reference: A faster SW implementation of SIDH (github.com/armfazh/flor-sidh-x64).
func (xQ *ProjectivePoint) Triple(xP *ProjectivePoint, curve *CachedTripleCurveParameters) *ProjectivePoint {
var t0, t1, t2, t3, t4, t5 ExtensionFieldElement
x1, z1 := &xP.X, &xP.Z
t0.Square(x1) // t0 = x1^2
t1.Square(z1) // t1 = z1^2
t2.Add(x1, z1) // t2 = x1+z1
t2.Square(&t2) // t2 = t2^2
t3.Add(&t0, &t1) // t3 = t0+t1
t4.Sub(&t2, &t3) // t4 = t2-t3
t5.Mul(&curve.Aminus2C, &t4) // t5 = (A-2C)*t4
t2.Mul(&curve.C2, &t2) // t2 = (2C)*t2
t5.Add(&t5, &t2) // t5 = t2+t5
t5.Add(&t5, &t5) // t5 = t5+t5
t5.Add(&t5, &t5) // t5 = t5+t5
t0.Mul(&t0, &t5) // t0 = t0*t5
t1.Mul(&t1, &t5) // t1 = t1*t5
t4.Sub(&t3, &t4) // t4 = t3-t4
t2.Mul(&t2, &t4) // t2 = t2*t4
t0.Sub(&t2, &t0) // t0 = t2-t0
t1.Sub(&t2, &t1) // t1 = t2-t1
t0.Square(&t0) // t0 = t0^2
t1.Square(&t1) // t1 = t1^2
xQ.X.Mul(x1, &t1) // x3 = x1*t1
xQ.Z.Mul(z1, &t0) // z3 = z1*t0
return xQ
}
*x2P = *xP
x, z := &x2P.X, &x2P.Z

// Given the curve parameters, xP = x(P), and k >= 0, compute xQ = x([3^k]P).
//
// Returns xQ to allow chaining. Safe to overlap xP, xQ.
func (xQ *ProjectivePoint) Pow3k(curve *ProjectiveCurveParameters, xP *ProjectivePoint, k uint32) *ProjectivePoint {
cachedParams := curve.cachedTripleParams()
*xQ = *xP
for i := uint32(0); i < k; i++ {
xQ.Triple(xQ, &cachedParams)
t0.Sub(x, z) // t0 = Xp - Zp
t1.Add(x, z) // t1 = Xp + Zp
t0.Square(&t0) // t0 = t0 ^ 2
t1.Square(&t1) // t1 = t1 ^ 2
z.Mul(&params.C, &t0) // Z2p = C24 * t0
x.Mul(z, &t1) // X2p = Z2p * t1
t1.Sub(&t1, &t0) // t1 = t1 - t0
t0.Mul(&params.A, &t1) // t0 = A24+ * t1
z.Add(z, &t0) // Z2p = Z2p + t0
z.Mul(z, &t1) // Zp = Z2p * t1
}

return xQ
}

// Given x(P) and a scalar m in little-endian bytes, compute x([m]P) using the
// Montgomery ladder. This is described in Algorithm 8 of Costello-Smith.
//
// This function's execution time is dependent only on the byte-length of the
// input scalar. All scalars of the same input length execute in uniform time.
// The scalar can be padded with zero bytes to ensure a uniform length.
//
// Safe to overlap the source with the destination.
func (xQ *ProjectivePoint) ScalarMult(curve *ProjectiveCurveParameters, xP *ProjectivePoint, scalar []uint8) *ProjectivePoint {
cachedParams := curve.cachedParams()
var x0, x1, tmp ProjectivePoint

x0.X.One()
x0.Z.Zero()
x1 = *xP

// Iterate over the bits of the scalar, top to bottom
prevBit := uint8(0)
for i := len(scalar) - 1; i >= 0; i-- {
scalarByte := scalar[i]
for j := 7; j >= 0; j-- {
bit := (scalarByte >> uint(j)) & 0x1
ProjectivePointConditionalSwap(&x0, &x1, (bit ^ prevBit))
tmp.Double(&x0, &cachedParams)
x1.Add(&x0, &x1, xP)
x0 = tmp
prevBit = bit
}
}
// now prevBit is the lowest bit of the scalar
ProjectivePointConditionalSwap(&x0, &x1, prevBit)
*xQ = x0
return xQ
return x2P
}

// Given x(P) and a scalar m in little-endian bytes, compute x([m]P), x([m+1]P) using the
// Montgomery ladder. This is described in Algorithm 8 of Costello-Smith.
//
// The extra value x([m+1]P) is returned to allow y-coordinate recovery;
// otherwise, it can be ignored.
// Given the curve parameters, xP = x(P), and k >= 0, compute x3P = x([3^k]P).
//
// This function's execution time is dependent only on the byte-length of the
// input scalar. All scalars of the same input length execute in uniform time.
// The scalar can be padded with zero bytes to ensure a uniform length.
func ScalarMultPrimeField(aPlus2Over4 *PrimeFieldElement, xP *ProjectivePrimeFieldPoint, scalar []uint8) (ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint) {
var x0, x1 ProjectivePrimeFieldPoint
// Returns x3P to allow chaining. Safe to overlap xP, xR.
func (x3P *ProjectivePoint) Pow3k(params *CurveCoefficientsEquiv, xP *ProjectivePoint, k uint32) *ProjectivePoint {
var t0, t1, t2, t3, t4, t5, t6 ExtensionFieldElement

x0.X.One()
x0.Z.Zero()
x1 = *xP
*x3P = *xP
x, z := &x3P.X, &x3P.Z

// Iterate over the bits of the scalar, top to bottom
prevBit := uint8(0)
for i := len(scalar) - 1; i >= 0; i-- {
scalarByte := scalar[i]
for j := 7; j >= 0; j-- {
bit := (scalarByte >> uint(j)) & 0x1
ProjectivePrimeFieldPointConditionalSwap(&x0, &x1, (bit ^ prevBit))
x0, x1 = x0.DblAdd(&x1, xP, aPlus2Over4)
prevBit = bit
}
}
// now prevBit is the lowest bit of the scalar
ProjectivePrimeFieldPointConditionalSwap(&x0, &x1, prevBit)
return x0, x1
}

// Given P = (x_P, y_P) in affine coordinates, as well as projective points
// x(Q), x(R) = x(P+Q), all in the prime-field subgroup of the starting curve
// E_0(F_p), use the Okeya-Sakurai coordinate recovery strategy to recover Q =
// (X_Q : Y_Q : Z_Q).
//
// This is Algorithm 5 of Costello-Smith, with the constants a = 0, b = 1 hardcoded.
func OkeyaSakuraiCoordinateRecovery(affine_xP, affine_yP *PrimeFieldElement, xQ, xR *ProjectivePrimeFieldPoint) (X_Q, Y_Q, Z_Q PrimeFieldElement) {
var v1, v2, v3, v4 PrimeFieldElement
v1.Mul(affine_xP, &xQ.Z) // = x_P*Z_Q
v2.Add(&xQ.X, &v1) // = X_Q + x_P*Z_Q
v3.Sub(&xQ.X, &v1).Square(&v3) // = (X_Q - x_P*Z_Q)^2
v3.Mul(&v3, &xR.X) // = X_R*(X_Q - x_P*Z_Q)^2
// Skip setting v1 = 2a*Z_Q (step 6) since we hardcode a = 0
// Skip adding v1 to v2 (step 7) since v1 is zero
v4.Mul(affine_xP, &xQ.X) // = x_P*X_Q
v4.Add(&v4, &xQ.Z) // = x_P*X_Q + Z_Q
v2.Mul(&v2, &v4) // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q)
// Skip multiplication by v1 (step 11) since v1 is zero
// Skip subtracting v1 from v2 (step 12) since v1 is zero
v2.Mul(&v2, &xR.Z) // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q)*Z_R
Y_Q.Sub(&v2, &v3) // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q)*Z_R - X_R*(X_Q - x_P*Z_Q)^2
v1.Add(affine_yP, affine_yP) // = 2b*y_P
v1.Mul(&v1, &xQ.Z).Mul(&v1, &xR.Z) // = 2b*y_P*Z_Q*Z_R
X_Q.Mul(&v1, &xQ.X) // = 2b*y_P*Z_Q*Z_R*X_Q
Z_Q.Mul(&v1, &xQ.Z) // = 2b*y_P*Z_Q^2*Z_R

return
}

// Given x(P), x(Q), x(P-Q), as well as a scalar m in little-endian bytes,
// compute x(P + [m]Q) using the "three-point ladder" of de Feo, Jao, and Plut.
//
// Safe to overlap the source with the destination.
//
// This function's execution time is dependent only on the byte-length of the
// input scalar. All scalars of the same input length execute in uniform time.
// The scalar can be padded with zero bytes to ensure a uniform length.
//
// The algorithm, as described in de Feo-Jao-Plut, is as follows:
//
// (x0, x1, x2) <--- (x(O), x(Q), x(P))
//
// for i = |m| down to 0, indexing the bits of m:
// Invariant: (x0, x1, x2) == (x( [t]Q ), x( [t+1]Q ), x( P + [t]Q ))
// where t = m//2^i is the high bits of m, starting at i
// if m_i == 0:
// (x0, x1, x2) <--- (xDBL(x0), xADD(x1, x0, x(Q)), xADD(x2, x0, x(P)))
// Invariant: (x0, x1, x2) == (x( [2t]Q ), x( [2t+1]Q ), x( P + [2t]Q ))
// == (x( [t']Q ), x( [t'+1]Q ), x( P + [t']Q ))
// where t' = m//2^{i-1} is the high bits of m, starting at i-1
// if m_i == 1:
// (x0, x1, x2) <--- (xADD(x1, x0, x(Q)), xDBL(x1), xADD(x2, x1, x(P-Q)))
// Invariant: (x0, x1, x2) == (x( [2t+1]Q ), x( [2t+2]Q ), x( P + [2t+1]Q ))
// == (x( [t']Q ), x( [t'+1]Q ), x( P + [t']Q ))
// where t' = m//2^{i-1} is the high bits of m, starting at i-1
// return x2
//
// Notice that the roles of (x0,x1) and (x(P), x(P-Q)) swap depending on the
// current bit of the scalar. Instead of swapping which operations we do, we
// can swap variable names, producing the following uniform algorithm:
//
// (x0, x1, x2) <--- (x(O), x(Q), x(P))
// (y0, y1) <--- (x(P), x(P-Q))
//
// for i = |m| down to 0, indexing the bits of m:
// (x0, x1) <--- SWAP( m_{i+1} xor m_i, (x0,x1) )
// (y0, y1) <--- SWAP( m_{i+1} xor m_i, (y0,y1) )
// (x0, x1, x2) <--- ( xDBL(x0), xADD(x1,x0,x(Q)), xADD(x2, x0, y0) )
//
// return x2
//
func (xR *ProjectivePoint) ThreePointLadder(curve *ProjectiveCurveParameters, xP, xQ, xPmQ *ProjectivePoint, scalar []uint8) *ProjectivePoint {
cachedParams := curve.cachedParams()
var x0, x1, x2, y0, y1, tmp ProjectivePoint

// (x0, x1, x2) <--- (x(O), x(Q), x(P))
x0.X.One()
x0.Z.Zero()
x1 = *xQ
x2 = *xP
// (y0, y1) <--- (x(P), x(P-Q))
y0 = *xP
y1 = *xPmQ

// Iterate over the bits of the scalar, top to bottom
prevBit := uint8(0)
for i := len(scalar) - 1; i >= 0; i-- {
scalarByte := scalar[i]
for j := 7; j >= 0; j-- {
bit := (scalarByte >> uint(j)) & 0x1
ProjectivePointConditionalSwap(&x0, &x1, (bit ^ prevBit))
ProjectivePointConditionalSwap(&y0, &y1, (bit ^ prevBit))
tmp, x2 = x0.DblAdd(&x2, &y0, &cachedParams)
x1.Add(&x1, &x0, xQ) // = xADD(x1, x0, x(Q))
x0 = tmp
prevBit = bit
}
for i := uint32(0); i < k; i++ {
t0.Sub(x, z) // t0 = Xp - Zp
t2.Square(&t0) // t2 = t0^2
t1.Add(x, z) // t1 = Xp + Zp
t3.Square(&t1) // t3 = t1^2
t4.Add(&t1, &t0) // t4 = t1 + t0
t0.Sub(&t1, &t0) // t0 = t1 - t0
t1.Square(&t4) // t1 = t4^2
t1.Sub(&t1, &t3) // t1 = t1 - t3
t1.Sub(&t1, &t2) // t1 = t1 - t2
t5.Mul(&t3, &params.A) // t5 = t3 * A24+
t3.Mul(&t3, &t5) // t3 = t5 * t3
t6.Mul(&t2, &params.C) // t6 = t2 * A24-
t2.Mul(&t2, &t6) // t2 = t2 * t6
t3.Sub(&t2, &t3) // t3 = t2 - t3
t2.Sub(&t5, &t6) // t2 = t5 - t6
t1.Mul(&t2, &t1) // t1 = t2 * t1
t2.Add(&t3, &t1) // t2 = t3 + t1
t2.Square(&t2) // t2 = t2^2
x.Mul(&t2, &t4) // X3p = t2 * t4
t1.Sub(&t3, &t1) // t1 = t3 - t1
t1.Square(&t1) // t1 = t1^2
z.Mul(&t1, &t0) // Z3p = t1 * t0
}

*xR = x2
return xR
return x3P
}

// RightToLeftLadder is a right-to-left point multiplication that given the
// x-coordinate of P, Q and P-Q calculates the x-coordinate of R=P+[k]Q.
func (xR *ProjectivePoint) RightToLeftLadder(curve *ProjectiveCurveParameters, xP, xQ, xPmQ *ProjectivePoint, scalar []uint8) *ProjectivePoint {
cachedParams := curve.cachedParams()
// x-coordinate of P, Q and P-Q calculates the x-coordinate of R=Q+[scalar]P.
// nbits must be smaller or equal to len(scalar).
func RightToLeftLadder(c *ProjectiveCurveParameters, P, Q, PmQ *ProjectivePoint,
nbits uint, scalar []uint8) ProjectivePoint {
var R0, R2, R1 ProjectivePoint

R1 = *xP
R2 = *xPmQ
R0 = *xQ
aPlus2Over4 := c.calcAplus2Over4()
R1 = *P
R2 = *PmQ
R0 = *Q

// Iterate over the bits of the scalar, bottom to top
prevBit := uint8(0)
for i := 0; i < len(scalar); i++ {
scalarByte := scalar[i]
for j := 0; j < 8; j++ {
bit := (scalarByte >> uint(j)) & 0x1
ProjectivePointConditionalSwap(&R1, &R2, (bit ^ prevBit))
R0, R2 = R0.DblAdd(&R2, &R1, &cachedParams)
prevBit = bit
}
for i := uint(0); i < nbits; i++ {
bit := (scalar[i>>3] >> (i & 7) & 1)
swap := prevBit ^ bit
prevBit = bit
ProjectivePointConditionalSwap(&R1, &R2, swap)
R0, R2 = xDblAdd(&R0, &R2, &R1, &aPlus2Over4)
}
ProjectivePointConditionalSwap(&R1, &R2, prevBit)
*xR = R1
return xR
}

// Given the affine x-coordinate affine_xP of P, compute the x-coordinate
// x(\tau(P)-P) of \tau(P)-P.
func DistortAndDifference(affine_xP *PrimeFieldElement) ProjectivePoint {
var xR ProjectivePoint
var t0, t1 PrimeFieldElement
t0.Square(affine_xP) // = x_P^2
t1.One().Add(&t1, &t0) // = x_P^2 + 1
xR.X.B = t1.A // = 0 + (x_P^2 + 1)*i
t0.Add(affine_xP, affine_xP) // = 2*x_P
xR.Z.A = t0.A // = 2*x_P + 0*i

return xR
}

// Given an affine point P = (x_P, y_P) in the prime-field subgroup of the
// starting curve E_0(F_p), together with a secret scalar m, compute x(P+[m]Q),
// where Q = \tau(P) is the image of P under the distortion map described
// below.
//
// The computation uses basically the same strategy as the
// Costello-Longa-Naehrig implementation:
//
// 1. Use the standard Montgomery ladder to compute x([m]Q), x([m+1]Q)
//
// 2. Use Okeya-Sakurai coordinate recovery to recover [m]Q from Q, x([m]Q),
// x([m+1]Q)
//
// 3. Use P and [m]Q to compute x(P + [m]Q)
//
// The distortion map \tau is defined as
//
// \tau : E_0(F_{p^2}) ---> E_0(F_{p^2})
//
// \tau : (x,y) |---> (-x, iy).
//
// The image of the distortion map is the _trace-zero_ subgroup of E_0(F_{p^2})
// defined by Tr(P) = P + \pi_p(P) = id, where \pi_p((x,y)) = (x^p, y^p) is the
// p-power Frobenius map. To see this, take P = (x,y) \in E_0(F_{p^2}). Then
// Tr(P) = id if and only if \pi_p(P) = -P, so that
//
// -P = (x, -y) = (x^p, y^p) = \pi_p(P);
//
// we have x^p = x if and only if x \in F_p, while y^p = -y if and only if y =
// i*y' for y' \in F_p.
//
// Thus (excepting the identity) every point in the trace-zero subgroup is of
// the form \tau((x,y)) = (-x,i*y) for (x,y) \in E_0(F_p).
//
// Since the Montgomery ladder only uses the x-coordinate, and the x-coordinate
// is always in the prime subfield, we can compute x([m]Q), x([m+1]Q) entirely
// in the prime subfield.
//
// The affine form of the relation for Okeya-Sakurai coordinate recovery is
// given on p. 13 of Costello-Smith:
//
// y_Q = ((x_P*x_Q + 1)*(x_P + x_Q + 2*a) - 2*a - x_R*(x_P - x_Q)^2)/(2*b*y_P),
//
// where R = Q + P and a,b are the Montgomery parameters. In our setting
// (a,b)=(0,1) and our points are P=Q, Q=[m]Q, P+Q=[m+1]Q, so this becomes
//
// y_{mQ} = ((x_Q*x_{mQ} + 1)*(x_Q + x_{mQ}) - x_{m1Q}*(x_Q - x_{mQ})^2)/(2*y_Q)
//
// y_{mQ} = ((1 - x_P*x_{mQ})*(x_{mQ} - x_P) - x_{m1Q}*(x_P + x_{mQ})^2)/(2*y_P*i)
//
// y_{mQ} = i*((1 - x_P*x_{mQ})*(x_{mQ} - x_P) - x_{m1Q}*(x_P + x_{mQ})^2)/(-2*y_P)
//
// since (x_Q, y_Q) = (-x_P, y_P*i). In projective coordinates this is
//
// Y_{mQ}' = ((Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q}
// - X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2)
//
// with denominator
//
// Z_{mQ}' = (-2*y_P*Z_{mQ}*Z_{m1Q})*Z_{mQ}.
//
// Setting
//
// X_{mQ}' = (-2*y_P*Z_{mQ}*Z_{m1Q})*X_{mQ}
//
// gives [m]Q = (X_{mQ}' : i*Y_{mQ}' : Z_{mQ}') with X,Y,Z all in F_p. (Here
// the ' just denotes that we've added extra terms to the denominators during
// the computation of Y)
//
// To compute the x-coordinate x(P+[m]Q) from P and [m]Q, we use the affine
// addition formulas of section 2.2 of Costello-Smith. We're only interested
// in the x-coordinate, giving
//
// X_R = Z_{mQ}*(i*Y_{mQ} - y_P*Z_{mQ})^2 - (x_P*Z_{mQ} + X_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2
//
// Z_R = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2.
//
// Notice that although X_R \in F_{p^2}, we can split the computation into
// coordinates X_R = X_{R,a} + X_{R,b}*i as
//
// (i*Y_{mQ} - y_P*Z_{mQ})^2 = (y_P*Z_{mQ})^2 - Y_{mQ}^2 - 2*y_P*Z_{mQ}*Y_{mQ}*i,
//
// giving
//
// X_{R,a} = Z_{mQ}*((y_P*Z_{mQ})^2 - Y_{mQ}^2)
// - (x_P*Z_{mQ} + X_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2
//
// X_{R,b} = -2*y_P*Y_{mQ}*Z_{mQ}^2
//
// Z_R = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2.
//
// These formulas could probably be combined with the formulas for y-recover
// and computed more efficiently, but efficiency isn't the biggest concern
// here, since the bulk of the cost is already in the ladder.
func SecretPoint(affine_xP, affine_yP *PrimeFieldElement, scalar []uint8) ProjectivePoint {
var xQ ProjectivePrimeFieldPoint
xQ.FromAffine(affine_xP)
xQ.X.Neg(&xQ.X)

// Compute x([m]Q) = (X_{mQ} : Z_{mQ}), x([m+1]Q) = (X_{m1Q} : Z_{m1Q})
var xmQ, xm1Q = ScalarMultPrimeField(&E0_aPlus2Over4, &xQ, scalar)

// Now perform coordinate recovery:
// [m]Q = (X_{mQ} : Y_{mQ}*i : Z_{mQ})
var XmQ, YmQ, ZmQ PrimeFieldElement
var t0, t1 PrimeFieldElement

// Y_{mQ} = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q}
// - X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2
t0.Mul(affine_xP, &xmQ.X) // = x_P*X_{mQ}
YmQ.Sub(&xmQ.Z, &t0) // = Z_{mQ} - x_P*X_{mQ}
t1.Mul(affine_xP, &xmQ.Z) // = x_P*Z_{mQ}
t0.Sub(&xmQ.X, &t1) // = X_{mQ} - x_P*Z_{mQ}
YmQ.Mul(&YmQ, &t0) // = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})
YmQ.Mul(&YmQ, &xm1Q.Z) // = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q}
t1.Add(&t1, &xmQ.X).Square(&t1) // = (X_{mQ} + x_P*Z_{mQ})^2
t1.Mul(&t1, &xm1Q.X) // = X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2
YmQ.Sub(&YmQ, &t1) // = Y_{mQ}

// Z_{mQ} = -2*(Z_{mQ}^2 * Z_{m1Q} * y_P)
t0.Mul(&xmQ.Z, &xm1Q.Z).Mul(&t0, affine_yP) // = Z_{mQ} * Z_{m1Q} * y_P
t0.Neg(&t0) // = -1*(Z_{mQ} * Z_{m1Q} * y_P)
t0.Add(&t0, &t0) // = -2*(Z_{mQ} * Z_{m1Q} * y_P)
ZmQ.Mul(&xmQ.Z, &t0) // = -2*(Z_{mQ}^2 * Z_{m1Q} * y_P)

// We added terms to the denominator Z_{mQ}, so multiply them to X_{mQ}
// X_{mQ} = -2*X_{mQ}*Z_{mQ}*Z_{m1Q}*y_P
XmQ.Mul(&xmQ.X, &t0)

// Now compute x(P + [m]Q) = (X_Ra + i*X_Rb : Z_R)
var XRa, XRb, ZR PrimeFieldElement

XRb.Square(&ZmQ).Mul(&XRb, &YmQ) // = Y_{mQ} * Z_{mQ}^2
XRb.Mul(&XRb, affine_yP) // = Y_{mQ} * y_P * Z_{mQ}^2
XRb.Add(&XRb, &XRb) // = 2 * Y_{mQ} * y_P * Z_{mQ}^2
XRb.Neg(&XRb) // = -2 * Y_{mQ} * y_P * Z_{mQ}^2

t0.Mul(affine_yP, &ZmQ).Square(&t0) // = (y_P * Z_{mQ})^2
t1.Square(&YmQ) // = Y_{mQ}^2
XRa.Sub(&t0, &t1) // = (y_P * Z_{mQ})^2 - Y_{mQ}^2
XRa.Mul(&XRa, &ZmQ) // = Z_{mQ}*((y_P * Z_{mQ})^2 - Y_{mQ}^2)
t0.Mul(affine_xP, &ZmQ) // = x_P * Z_{mQ}
t1.Add(&XmQ, &t0) // = X_{mQ} + x_P*Z_{mQ}
t0.Sub(&XmQ, &t0) // = X_{mQ} - x_P*Z_{mQ}
t0.Square(&t0) // = (X_{mQ} - x_P*Z_{mQ})^2
t1.Mul(&t1, &t0) // = (X_{mQ} + x_P*Z_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2
XRa.Sub(&XRa, &t1) // = Z_{mQ}*((y_P*Z_{mQ})^2 - Y_{mQ}^2) - (X_{mQ} + x_P*Z_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2

ZR.Mul(&ZmQ, &t0) // = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2

var xR ProjectivePoint
xR.X.A = XRa.A
xR.X.B = XRb.A
xR.Z.A = ZR.A

return xR
ProjectivePointConditionalSwap(&R1, &R2, prevBit)
return R1
}

+ 231
- 209
p751toolbox/curve_test.go Прегледај датотеку

@@ -1,6 +1,8 @@
package p751toolbox

import (
"bytes"
. "github.com/cloudflare/p751sidh/sidh"
"math/rand"
"reflect"
"testing"
@@ -15,43 +17,81 @@ import (
// sage: A = 4385300808024233870220415655826946795549183378139271271040522089756750951667981765872679172832050962894122367066234419550072004266298327417513857609747116903999863022476533671840646615759860564818837299058134292387429068536219*i + 1408083354499944307008104531475821995920666351413327060806684084512082259107262519686546161682384352696826343970108773343853651664489352092568012759783386151707999371397181344707721407830640876552312524779901115054295865393760
// sage: C = 933177602672972392833143808100058748100491911694554386487433154761658932801917030685312352302083870852688835968069519091048283111836766101703759957146191882367397129269726925521881467635358356591977198680477382414690421049768*i + 9088894745865170214288643088620446862479558967886622582768682946704447519087179261631044546285104919696820250567182021319063155067584445633834024992188567423889559216759336548208016316396859149888322907914724065641454773776307
// sage: E = EllipticCurve(Fp2, [0,A/C,0,1,0])
// sage: X, Y, Z = (8172151271761071554796221948801462094972242987811852753144865524899433583596839357223411088919388342364651632180452081960511516040935428737829624206426287774255114241789158000915683252363913079335550843837650671094705509470594*i + 9326574858039944121604015439381720195556183422719505497448541073272720545047742235526963773359004021838961919129020087515274115525812121436661025030481584576474033630899768377131534320053412545346268645085054880212827284581557, 2381174772709336084066332457520782192315178511983342038392622832616744048226360647551642232950959910067260611740876401494529727990031260499974773548012283808741733925525689114517493995359390158666069816204787133942283380884077*i + 5378956232034228335189697969144556552783858755832284194802470922976054645696324118966333158267442767138528227968841257817537239745277092206433048875637709652271370008564179304718555812947398374153513738054572355903547642836171, 1)
// sage: P = E((X,Y,Z))
// sage: XP, YP, ZP = (8172151271761071554796221948801462094972242987811852753144865524899433583596839357223411088919388342364651632180452081960511516040935428737829624206426287774255114241789158000915683252363913079335550843837650671094705509470594*i + 9326574858039944121604015439381720195556183422719505497448541073272720545047742235526963773359004021838961919129020087515274115525812121436661025030481584576474033630899768377131534320053412545346268645085054880212827284581557, 2381174772709336084066332457520782192315178511983342038392622832616744048226360647551642232950959910067260611740876401494529727990031260499974773548012283808741733925525689114517493995359390158666069816204787133942283380884077*i + 5378956232034228335189697969144556552783858755832284194802470922976054645696324118966333158267442767138528227968841257817537239745277092206433048875637709652271370008564179304718555812947398374153513738054572355903547642836171, 1)
// sage: XQ, YQ, ZQ = (58415083458086949460774631288254059520198050925769753726965257584725433404854146588020043440874302516770401547098747946831855304325931998303167820332782040123488940125615738529117852871565495005635582483848203360379709783913*i + 5253691516070829381103946367549411712646323371150903710970418364086793242952116417060222617507143384179331507713214357463046698181663711723374230655213762988214127964685312538735038552802853885461137159568726585741937776693865 : 5158855522131677856751020091923296498106687563952623634063620100943451774747711264382913073742659403103540173656392945587315798626256921403472228775673617431559609635074280718249654789362069112718012186738875122436282913402060*i + 6207982879267706771450280080677554401823496760317291100742934673980817977221212113336809046153999230028113719924529054308271353271269929392876016936836430864080079684360816968551808465413552611322477628209203066093559492022329 : 1)
// sage: P = E((XP,YP,ZP))
// sage: X2, Y2, Z2 = 2*P
// sage: X3, Y3, Z3 = 3*P
// sage: m = 96550223052359874398280314003345143371473380422728857598463622014420884224892

// A = 4385300808024233870220415655826946795549183378139271271040522089756750951667981765872679172832050962894122367066234419550072004266298327417513857609747116903999863022476533671840646615759860564818837299058134292387429068536219*i + 1408083354499944307008104531475821995920666351413327060806684084512082259107262519686546161682384352696826343970108773343853651664489352092568012759783386151707999371397181344707721407830640876552312524779901115054295865393760
var curve_A = ExtensionFieldElement{A: Fp751Element{0x8319eb18ca2c435e, 0x3a93beae72cd0267, 0x5e465e1f72fd5a84, 0x8617fa4150aa7272, 0x887da24799d62a13, 0xb079b31b3c7667fe, 0xc4661b150fa14f2e, 0xd4d2b2967bc6efd6, 0x854215a8b7239003, 0x61c5302ccba656c2, 0xf93194a27d6f97a2, 0x1ed9532bca75}, B: Fp751Element{0xb6f541040e8c7db6, 0x99403e7365342e15, 0x457e9cee7c29cced, 0x8ece72dc073b1d67, 0x6e73cef17ad28d28, 0x7aed836ca317472, 0x89e1de9454263b54, 0x745329277aa0071b, 0xf623dfc73bc86b9b, 0xb8e3c1d8a9245882, 0x6ad0b3d317770bec, 0x5b406e8d502b}}
var curve_A = ExtensionFieldElement{
A: Fp751Element{0x8319eb18ca2c435e, 0x3a93beae72cd0267, 0x5e465e1f72fd5a84, 0x8617fa4150aa7272, 0x887da24799d62a13, 0xb079b31b3c7667fe, 0xc4661b150fa14f2e, 0xd4d2b2967bc6efd6, 0x854215a8b7239003, 0x61c5302ccba656c2, 0xf93194a27d6f97a2, 0x1ed9532bca75},
B: Fp751Element{0xb6f541040e8c7db6, 0x99403e7365342e15, 0x457e9cee7c29cced, 0x8ece72dc073b1d67, 0x6e73cef17ad28d28, 0x7aed836ca317472, 0x89e1de9454263b54, 0x745329277aa0071b, 0xf623dfc73bc86b9b, 0xb8e3c1d8a9245882, 0x6ad0b3d317770bec, 0x5b406e8d502b}}

// C = 933177602672972392833143808100058748100491911694554386487433154761658932801917030685312352302083870852688835968069519091048283111836766101703759957146191882367397129269726925521881467635358356591977198680477382414690421049768*i + 9088894745865170214288643088620446862479558967886622582768682946704447519087179261631044546285104919696820250567182021319063155067584445633834024992188567423889559216759336548208016316396859149888322907914724065641454773776307
var curve_C = ExtensionFieldElement{A: Fp751Element{0x4fb2358bbf723107, 0x3a791521ac79e240, 0x283e24ef7c4c922f, 0xc89baa1205e33cc, 0x3031be81cff6fee1, 0xaf7a494a2f6a95c4, 0x248d251eaac83a1d, 0xc122fca1e2550c88, 0xbc0451b11b6cfd3d, 0x9c0a114ab046222c, 0x43b957b32f21f6ea, 0x5b9c87fa61de}, B: Fp751Element{0xacf142afaac15ec6, 0xfd1322a504a071d5, 0x56bb205e10f6c5c6, 0xe204d2849a97b9bd, 0x40b0122202fe7f2e, 0xecf72c6fafacf2cb, 0x45dfc681f869f60a, 0x11814c9aff4af66c, 0x9278b0c4eea54fe7, 0x9a633d5baf7f2e2e, 0x69a329e6f1a05112, 0x1d874ace23e4}}
var curve_C = ExtensionFieldElement{
A: Fp751Element{0x4fb2358bbf723107, 0x3a791521ac79e240, 0x283e24ef7c4c922f, 0xc89baa1205e33cc, 0x3031be81cff6fee1, 0xaf7a494a2f6a95c4, 0x248d251eaac83a1d, 0xc122fca1e2550c88, 0xbc0451b11b6cfd3d, 0x9c0a114ab046222c, 0x43b957b32f21f6ea, 0x5b9c87fa61de},
B: Fp751Element{0xacf142afaac15ec6, 0xfd1322a504a071d5, 0x56bb205e10f6c5c6, 0xe204d2849a97b9bd, 0x40b0122202fe7f2e, 0xecf72c6fafacf2cb, 0x45dfc681f869f60a, 0x11814c9aff4af66c, 0x9278b0c4eea54fe7, 0x9a633d5baf7f2e2e, 0x69a329e6f1a05112, 0x1d874ace23e4}}

var curve = ProjectiveCurveParameters{A: curve_A, C: curve_C}

// x(P) = 8172151271761071554796221948801462094972242987811852753144865524899433583596839357223411088919388342364651632180452081960511516040935428737829624206426287774255114241789158000915683252363913079335550843837650671094705509470594*i + 9326574858039944121604015439381720195556183422719505497448541073272720545047742235526963773359004021838961919129020087515274115525812121436661025030481584576474033630899768377131534320053412545346268645085054880212827284581557
var affine_xP = ExtensionFieldElement{A: Fp751Element{0xe8d05f30aac47247, 0x576ec00c55441de7, 0xbf1a8ec5fe558518, 0xd77cb17f77515881, 0x8e9852837ee73ec4, 0x8159634ad4f44a6b, 0x2e4eb5533a798c5, 0x9be8c4354d5bc849, 0xf47dc61806496b84, 0x25d0e130295120e0, 0xdbef54095f8139e3, 0x5a724f20862c}, B: Fp751Element{0x3ca30d7623602e30, 0xfb281eddf45f07b7, 0xd2bf62d5901a45bc, 0xc67c9baf86306dd2, 0x4e2bd93093f538ca, 0xcfd92075c25b9cbe, 0xceafe9a3095bcbab, 0x7d928ad380c85414, 0x37c5f38b2afdc095, 0x75325899a7b779f4, 0xf130568249f20fdd, 0x178f264767d1}}
var affine_xP = ExtensionFieldElement{
A: Fp751Element{0xe8d05f30aac47247, 0x576ec00c55441de7, 0xbf1a8ec5fe558518, 0xd77cb17f77515881, 0x8e9852837ee73ec4, 0x8159634ad4f44a6b, 0x2e4eb5533a798c5, 0x9be8c4354d5bc849, 0xf47dc61806496b84, 0x25d0e130295120e0, 0xdbef54095f8139e3, 0x5a724f20862c},
B: Fp751Element{0x3ca30d7623602e30, 0xfb281eddf45f07b7, 0xd2bf62d5901a45bc, 0xc67c9baf86306dd2, 0x4e2bd93093f538ca, 0xcfd92075c25b9cbe, 0xceafe9a3095bcbab, 0x7d928ad380c85414, 0x37c5f38b2afdc095, 0x75325899a7b779f4, 0xf130568249f20fdd, 0x178f264767d1}}

// x([2]P) = 1476586462090705633631615225226507185986710728845281579274759750260315746890216330325246185232948298241128541272709769576682305216876843626191069809810990267291824247158062860010264352034514805065784938198193493333201179504845*i + 3623708673253635214546781153561465284135688791018117615357700171724097420944592557655719832228709144190233454198555848137097153934561706150196041331832421059972652530564323645509890008896574678228045006354394485640545367112224
var affine_xP2 = ExtensionFieldElement{A: Fp751Element{0x2a77afa8576ce979, 0xab1360e69b0aeba0, 0xd79e3e3cbffad660, 0x5fd0175aa10f106b, 0x1800ebafce9fbdbc, 0x228fc9142bdd6166, 0x867cf907314e34c3, 0xa58d18c94c13c31c, 0x699a5bc78b11499f, 0xa29fc29a01f7ccf1, 0x6c69c0c5347eebce, 0x38ecee0cc57}, B: Fp751Element{0x43607fd5f4837da0, 0x560bad4ce27f8f4a, 0x2164927f8495b4dd, 0x621103fdb831a997, 0xad740c4eea7db2db, 0x2cde0442205096cd, 0x2af51a70ede8324e, 0x41a4e680b9f3466, 0x5481f74660b8f476, 0xfcb2f3e656ff4d18, 0x42e3ce0837171acc, 0x44238c30530c}}
var affine_xP2 = ExtensionFieldElement{
A: Fp751Element{0x2a77afa8576ce979, 0xab1360e69b0aeba0, 0xd79e3e3cbffad660, 0x5fd0175aa10f106b, 0x1800ebafce9fbdbc, 0x228fc9142bdd6166, 0x867cf907314e34c3, 0xa58d18c94c13c31c, 0x699a5bc78b11499f, 0xa29fc29a01f7ccf1, 0x6c69c0c5347eebce, 0x38ecee0cc57},
B: Fp751Element{0x43607fd5f4837da0, 0x560bad4ce27f8f4a, 0x2164927f8495b4dd, 0x621103fdb831a997, 0xad740c4eea7db2db, 0x2cde0442205096cd, 0x2af51a70ede8324e, 0x41a4e680b9f3466, 0x5481f74660b8f476, 0xfcb2f3e656ff4d18, 0x42e3ce0837171acc, 0x44238c30530c}}

// x([3]P) = 9351941061182433396254169746041546943662317734130813745868897924918150043217746763025923323891372857734564353401396667570940585840576256269386471444236630417779544535291208627646172485976486155620044292287052393847140181703665*i + 9010417309438761934687053906541862978676948345305618417255296028956221117900864204687119686555681136336037659036201780543527957809743092793196559099050594959988453765829339642265399496041485088089691808244290286521100323250273
var affine_xP3 = ExtensionFieldElement{A: Fp751Element{0x2096e3f23feca947, 0xf36f635aa4ad8634, 0xdae3b1c6983c5e9a, 0xe08df6c262cb74b4, 0xd2ca4edc37452d3d, 0xfb5f3fe42f500c79, 0x73740aa3abc2b21f, 0xd535fd869f914cca, 0x4a558466823fb67f, 0x3e50a7a0e3bfc715, 0xf43c6da9183a132f, 0x61aca1e1b8b9}, B: Fp751Element{0x1e54ec26ea5077bd, 0x61380572d8769f9a, 0xc615170684f59818, 0x6309c3b93e84ef6e, 0x33c74b1318c3fcd0, 0xfe8d7956835afb14, 0x2d5a7b55423c1ecc, 0x869db67edfafea68, 0x1292632394f0a628, 0x10bba48225bfd141, 0x6466c28b408daba, 0x63cacfdb7c43}}
var affine_xP3 = ExtensionFieldElement{
A: Fp751Element{0x2096e3f23feca947, 0xf36f635aa4ad8634, 0xdae3b1c6983c5e9a, 0xe08df6c262cb74b4, 0xd2ca4edc37452d3d, 0xfb5f3fe42f500c79, 0x73740aa3abc2b21f, 0xd535fd869f914cca, 0x4a558466823fb67f, 0x3e50a7a0e3bfc715, 0xf43c6da9183a132f, 0x61aca1e1b8b9},
B: Fp751Element{0x1e54ec26ea5077bd, 0x61380572d8769f9a, 0xc615170684f59818, 0x6309c3b93e84ef6e, 0x33c74b1318c3fcd0, 0xfe8d7956835afb14, 0x2d5a7b55423c1ecc, 0x869db67edfafea68, 0x1292632394f0a628, 0x10bba48225bfd141, 0x6466c28b408daba, 0x63cacfdb7c43}}

// m = 96550223052359874398280314003345143371473380422728857598463622014420884224892
var mScalarBytes = [...]uint8{124, 123, 149, 250, 180, 117, 108, 72, 140, 23, 85, 180, 73, 245, 30, 163, 11, 49, 240, 164, 166, 129, 173, 148, 81, 17, 231, 245, 91, 125, 117, 213}
// x([2^2]P) = 441719501189485559222919502512761433931671682884872259563221427434901842337947564993718830905758163254463901652874331063768876314142359813382575876106725244985607032091781306919778265250690045578695338669105227100119314831452*i + 6961734028200975729170216310486458180126343885294922940439352055937945948015840788921225114530454649744697857047401608073256634790353321931728699534700109268264491160589480994022419317695690866764726967221310990488404411684053
var affine_xP4 = ExtensionFieldElement{
A: Fp751Element{0x6f9dbe4c39175153, 0xf2fec757eb99e88, 0x43d7361a93733d91, 0x3abd10ed19c85a3d, 0xc4de9ab9c5ef7181, 0x53e375901684c900, 0x68ffc3e7d71c41ff, 0x47adab62c8d942fe, 0x226a33fd6fbb381d, 0x87ef4c8fdd83309a, 0xaca1cf44c5fa8799, 0x6cbae86c755f},
B: Fp751Element{0x4c80c37fe68282a7, 0xbd8b9d7248bf553a, 0x1fb0e8e74d5e1762, 0xb63fa0e4e5f91482, 0xc675ab8a45a1439, 0xdfa6772deace7820, 0xf0d813d71d9a9255, 0x53a1a58c634534bd, 0x4ebfc6485fdfd888, 0x6991fe4358bcf169, 0xc0547bdaca85b6fd, 0xf461548d632}}

// x([a]P) = 7893578558852400052689739833699289348717964559651707250677393044951777272628231794999463214496545377542328262828965953246725804301238040891993859185944339366910592967840967752138115122568615081881937109746463885908097382992642*i + 8293895847098220389503562888233557012043261770526854885191188476280014204211818299871679993460086974249554528517413590157845430186202704783785316202196966198176323445986064452630594623103149383929503089342736311904030571524837
var affine_xaP = ExtensionFieldElement{A: Fp751Element{0x2112f3c7d7f938bb, 0x704a677f0a4df08f, 0x825370e31fb4ef00, 0xddbf79b7469f902, 0x27640c899ea739fd, 0xfb7b8b19f244108e, 0x546a6679dd3baebc, 0xe9f0ecf398d5265f, 0x223d2b350e75e461, 0x84b322a0b6aff016, 0xfabe426f539f8b39, 0x4507a0604f50}, B: Fp751Element{0xac77737e5618a5fe, 0xf91c0e08c436ca52, 0xd124037bc323533c, 0xc9a772bf52c58b63, 0x3b30c8f38ef6af4d, 0xb9eed160e134f36e, 0x24e3836393b25017, 0xc828be1b11baf1d9, 0x7b7dab585df50e93, 0x1ca3852c618bd8e0, 0x4efa73bcb359fa00, 0x50b6a923c2d4}}
// x([3^2]P) = 3957171963425208493644602380039721164492341594850197356580248639045894821895524981729970650520936632013218950972842867220898274664982599375786979902471523505057611521217523103474682939638645404445093536997296151472632038973463*i + 1357869545269286021642168835877253886774707209614159162748874474269328421720121175566245719916322684751967981171882659798149072149161259103020057556362998810229937432814792024248155991141511691087135859252304684633946087474060
var affine_xP9 = ExtensionFieldElement{
A: Fp751Element{0x7c0daa0f04ded4e0, 0x52dc4f883d85e065, 0x91afbdc2c1714d0b, 0xb7b3db8e658cfeba, 0x43d4e72a692882f3, 0x535c56d83753da30, 0xc8a58724433cbf5d, 0x351153c0a5e74219, 0x2c81827d19f93dd5, 0x26ef8aca3370ea1a, 0x1cf939a6dd225dec, 0x3403cb28ad41},
B: Fp751Element{0x93e7bc373a9ff7b, 0x57b8cc47635ebc0f, 0x92eab55689106cf3, 0x93643111d421f24c, 0x1c58b519506f6b7a, 0xebd409fb998faa13, 0x5c86ed799d09d80e, 0xd9a1d764d6363562, 0xf95e87f92fb0c4cc, 0x6b2bbaf5632a5609, 0x2d9b6a809dfaff7f, 0x29c0460348b}}

var one = ExtensionFieldElement{A: Fp751Element{0x249ad, 0x0, 0x0, 0x0, 0x0, 0x8310000000000000, 0x5527b1e4375c6c66, 0x697797bf3f4f24d0, 0xc89db7b2ac5c4e2e, 0x4ca4b439d2076956, 0x10f7926c7512c7e9, 0x2d5b24bce5e2}, B: Fp751Element{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}
// m = 96550223052359874398280314003345143371473380422728857598463622014420884224892
var mScalarBytes = [...]uint8{0x7c, 0x7b, 0x95, 0xfa, 0xb4, 0x75, 0x6c, 0x48, 0x8c, 0x17, 0x55, 0xb4, 0x49, 0xf5, 0x1e, 0xa3, 0xb, 0x31, 0xf0, 0xa4, 0xa6, 0x81, 0xad, 0x94, 0x51, 0x11, 0xe7, 0xf5, 0x5b, 0x7d, 0x75, 0xd5}

func TestOne(t *testing.T) {
var tmp ExtensionFieldElement
tmp.Mul(&one, &affine_xP)
if !tmp.VartimeEq(&affine_xP) {
t.Error("Not equal 1")
}
// x([m]P) = 7893578558852400052689739833699289348717964559651707250677393044951777272628231794999463214496545377542328262828965953246725804301238040891993859185944339366910592967840967752138115122568615081881937109746463885908097382992642*i + 8293895847098220389503562888233557012043261770526854885191188476280014204211818299871679993460086974249554528517413590157845430186202704783785316202196966198176323445986064452630594623103149383929503089342736311904030571524837
var affine_xaP = ExtensionFieldElement{
A: Fp751Element{0x2112f3c7d7f938bb, 0x704a677f0a4df08f, 0x825370e31fb4ef00, 0xddbf79b7469f902, 0x27640c899ea739fd, 0xfb7b8b19f244108e, 0x546a6679dd3baebc, 0xe9f0ecf398d5265f, 0x223d2b350e75e461, 0x84b322a0b6aff016, 0xfabe426f539f8b39, 0x4507a0604f50},
B: Fp751Element{0xac77737e5618a5fe, 0xf91c0e08c436ca52, 0xd124037bc323533c, 0xc9a772bf52c58b63, 0x3b30c8f38ef6af4d, 0xb9eed160e134f36e, 0x24e3836393b25017, 0xc828be1b11baf1d9, 0x7b7dab585df50e93, 0x1ca3852c618bd8e0, 0x4efa73bcb359fa00, 0x50b6a923c2d4}}

// Inputs for testing 3-point-ladder
var threePointLadderInputs = []ProjectivePoint{
// x(P)
ProjectivePoint{
X: ExtensionFieldElement{
A: Fp751Element{0xe8d05f30aac47247, 0x576ec00c55441de7, 0xbf1a8ec5fe558518, 0xd77cb17f77515881, 0x8e9852837ee73ec4, 0x8159634ad4f44a6b, 0x2e4eb5533a798c5, 0x9be8c4354d5bc849, 0xf47dc61806496b84, 0x25d0e130295120e0, 0xdbef54095f8139e3, 0x5a724f20862c},
B: Fp751Element{0x3ca30d7623602e30, 0xfb281eddf45f07b7, 0xd2bf62d5901a45bc, 0xc67c9baf86306dd2, 0x4e2bd93093f538ca, 0xcfd92075c25b9cbe, 0xceafe9a3095bcbab, 0x7d928ad380c85414, 0x37c5f38b2afdc095, 0x75325899a7b779f4, 0xf130568249f20fdd, 0x178f264767d1}},
Z: oneExtensionField,
},
// x(Q)
ProjectivePoint{
X: ExtensionFieldElement{
A: Fp751Element{0x2b71a2a93ad1e10e, 0xf0b9842a92cfb333, 0xae17373615a27f5c, 0x3039239f428330c4, 0xa0c4b735ed7dcf98, 0x6e359771ddf6af6a, 0xe986e4cac4584651, 0x8233a2b622d5518, 0xbfd67bf5f06b818b, 0xdffe38d0f5b966a6, 0xa86b36a3272ee00a, 0x193e2ea4f68f},
B: Fp751Element{0x5a0f396459d9d998, 0x479f42250b1b7dda, 0x4016b57e2a15bf75, 0xc59f915203fa3749, 0xd5f90257399cf8da, 0x1fb2dadfd86dcef4, 0x600f20e6429021dc, 0x17e347d380c57581, 0xc1b0d5fa8fe3e440, 0xbcf035330ac20e8, 0x50c2eb5f6a4f03e6, 0x86b7c4571}},
Z: oneExtensionField,
},
// x(P-Q)
ProjectivePoint{
X: ExtensionFieldElement{
A: Fp751Element{0x4aafa9f378f7b5ff, 0x1172a683aa8eee0, 0xea518d8cbec2c1de, 0xe191bcbb63674557, 0x97bc19637b259011, 0xdbeae5c9f4a2e454, 0x78f64d1b72a42f95, 0xe71cb4ea7e181e54, 0xe4169d4c48543994, 0x6198c2286a98730f, 0xd21d675bbab1afa5, 0x2e7269fce391},
B: Fp751Element{0x23355783ce1d0450, 0x683164cf4ce3d93f, 0xae6d1c4d25970fd8, 0x7807007fb80b48cf, 0xa005a62ec2bbb8a2, 0x6b5649bd016004cb, 0xbb1a13fa1330176b, 0xbf38e51087660461, 0xe577fddc5dd7b930, 0x5f38116f56947cd3, 0x3124f30b98c36fde, 0x4ca9b6e6db37}},
Z: oneExtensionField,
},
}

func (P ProjectivePoint) Generate(rand *rand.Rand, size int) reflect.Value {
@@ -74,23 +114,131 @@ func (curve ProjectiveCurveParameters) Generate(rand *rand.Rand, size int) refle
})
}

// Helpers

// Given xP = x(P), xQ = x(Q), and xPmQ = x(P-Q), compute xR = x(P+Q).
//
// Returns xR to allow chaining. Safe to overlap xP, xQ, xR.
func (xR *ProjectivePoint) Add(xP, xQ, xPmQ *ProjectivePoint) *ProjectivePoint {
// Algorithm 1 of Costello-Smith.
var v0, v1, v2, v3, v4 ExtensionFieldElement
v0.Add(&xP.X, &xP.Z) // X_P + Z_P
v1.Sub(&xQ.X, &xQ.Z).Mul(&v1, &v0) // (X_Q - Z_Q)(X_P + Z_P)
v0.Sub(&xP.X, &xP.Z) // X_P - Z_P
v2.Add(&xQ.X, &xQ.Z).Mul(&v2, &v0) // (X_Q + Z_Q)(X_P - Z_P)
v3.Add(&v1, &v2).Square(&v3) // 4(X_Q X_P - Z_Q Z_P)^2
v4.Sub(&v1, &v2).Square(&v4) // 4(X_Q Z_P - Z_Q X_P)^2
v0.Mul(&xPmQ.Z, &v3) // 4X_{P-Q}(X_Q X_P - Z_Q Z_P)^2
xR.Z.Mul(&xPmQ.X, &v4) // 4Z_{P-Q}(X_Q Z_P - Z_Q X_P)^2
xR.X = v0
return xR
}

// Given xP = x(P) and cached curve parameters Aplus2C = A + 2*C, C4 = 4*C,
// compute xQ = x([2]P).
//
// Returns xQ to allow chaining. Safe to overlap xP, xQ.
func (xQ *ProjectivePoint) Double(xP *ProjectivePoint, Aplus2C, C4 *ExtensionFieldElement) *ProjectivePoint {
// Algorithm 2 of Costello-Smith, amended to work with projective curve coefficients.
var v1, v2, v3, xz4 ExtensionFieldElement
v1.Add(&xP.X, &xP.Z).Square(&v1) // (X+Z)^2
v2.Sub(&xP.X, &xP.Z).Square(&v2) // (X-Z)^2
xz4.Sub(&v1, &v2) // 4XZ = (X+Z)^2 - (X-Z)^2
v2.Mul(&v2, C4) // 4C(X-Z)^2
xQ.X.Mul(&v1, &v2) // 4C(X+Z)^2(X-Z)^2
v3.Mul(&xz4, Aplus2C) // 4XZ(A + 2C)
v3.Add(&v3, &v2) // 4XZ(A + 2C) + 4C(X-Z)^2
xQ.Z.Mul(&v3, &xz4) // (4XZ(A + 2C) + 4C(X-Z)^2)4XZ
// Now (xQ.x : xQ.z)
// = (4C(X+Z)^2(X-Z)^2 : (4XZ(A + 2C) + 4C(X-Z)^2)4XZ )
// = ((X+Z)^2(X-Z)^2 : (4XZ((A + 2C)/4C) + (X-Z)^2)4XZ )
// = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ )
return xQ
}

// Given x(P) and a scalar m in little-endian bytes, compute x([m]P) using the
// Montgomery ladder. This is described in Algorithm 8 of Costello-Smith.
//
// This function's execution time is dependent only on the byte-length of the
// input scalar. All scalars of the same input length execute in uniform time.
// The scalar can be padded with zero bytes to ensure a uniform length.
//
// Safe to overlap the source with the destination.
func (xQ *ProjectivePoint) ScalarMult(curve *ProjectiveCurveParameters, xP *ProjectivePoint, scalar []uint8) *ProjectivePoint {
var x0, x1, tmp ProjectivePoint
var Aplus2C, C4 ExtensionFieldElement

Aplus2C.Add(&curve.C, &curve.C) // = 2*C
C4.Add(&Aplus2C, &Aplus2C) // = 4*C
Aplus2C.Add(&Aplus2C, &curve.A) // = 2*C + A

x0.X.One()
x0.Z.Zero()
x1 = *xP

// Iterate over the bits of the scalar, top to bottom
prevBit := uint8(0)
for i := len(scalar) - 1; i >= 0; i-- {
scalarByte := scalar[i]
for j := 7; j >= 0; j-- {
bit := (scalarByte >> uint(j)) & 0x1
ProjectivePointConditionalSwap(&x0, &x1, (bit ^ prevBit))
tmp.Double(&x0, &Aplus2C, &C4)
x1.Add(&x0, &x1, xP)
x0 = tmp
prevBit = bit
}
}
// now prevBit is the lowest bit of the scalar
ProjectivePointConditionalSwap(&x0, &x1, prevBit)
*xQ = x0
return xQ
}

// Tests

func TestOne(t *testing.T) {
var tmp ExtensionFieldElement

tmp.Mul(&oneExtensionField, &affine_xP)
if !tmp.VartimeEq(&affine_xP) {
t.Error("Not equal 1")
}
}

func TestScalarMultVersusSage(t *testing.T) {
var xP ProjectivePoint

xP.FromAffine(&affine_xP)
affine_xQ := xP.ScalarMult(&curve, &xP, mScalarBytes[:]).ToAffine() // = x([m]P)
if !affine_xaP.VartimeEq(affine_xQ) {
t.Error("\nExpected\n", affine_xaP, "\nfound\n", affine_xQ)
}
}

func Test_jInvariant(t *testing.T) {
var curve = ProjectiveCurveParameters{A: curve_A, C: curve_C}
j := curve.JInvariant()
var jbufRes = make([]byte, Params(FP_751).SharedSecretSize)
var jbufExp = make([]byte, Params(FP_751).SharedSecretSize)
// Computed using Sage
// j = 3674553797500778604587777859668542828244523188705960771798425843588160903687122861541242595678107095655647237100722594066610650373491179241544334443939077738732728884873568393760629500307797547379838602108296735640313894560419*i + 3127495302417548295242630557836520229396092255080675419212556702820583041296798857582303163183558315662015469648040494128968509467224910895884358424271180055990446576645240058960358037224785786494172548090318531038910933793845
known_j := ExtensionFieldElement{
var known_j = ExtensionFieldElement{
A: Fp751Element{0xc7a8921c1fb23993, 0xa20aea321327620b, 0xf1caa17ed9676fa8, 0x61b780e6b1a04037, 0x47784af4c24acc7a, 0x83926e2e300b9adf, 0xcd891d56fae5b66, 0x49b66985beb733bc, 0xd4bcd2a473d518f, 0xe242239991abe224, 0xa8af5b20f98672f8, 0x139e4d4e4d98},
B: Fp751Element{0xb5b52a21f81f359, 0x715e3a865db6d920, 0x9bac2f9d8911978b, 0xef14acd8ac4c1e3d, 0xe81aacd90cfb09c8, 0xaf898288de4a09d9, 0xb85a7fb88c5c4601, 0x2c37c3f1dd303387, 0x7ad3277fe332367c, 0xd4cbee7f25a8e6f8, 0x36eacbe979eaeffa, 0x59eb5a13ac33},
}

if !j.VartimeEq(&known_j) {
t.Error("Computed incorrect j-invariant: found\n", j, "\nexpected\n", known_j)
curve.Jinvariant(jbufRes)
known_j.ToBytes(jbufExp)

if !bytes.Equal(jbufRes, jbufExp) {
t.Error("Computed incorrect j-invariant: found\n", jbufRes, "\nexpected\n", jbufExp)
}
}

func TestProjectivePointVartimeEq(t *testing.T) {
xP := ProjectivePoint{X: affine_xP, Z: one}
var xP ProjectivePoint

xP.FromAffine(&affine_xP)
xQ := xP
// Scale xQ, which results in the same projective point
xQ.X.Mul(&xQ.X, &curve_A)
@@ -102,30 +250,44 @@ func TestProjectivePointVartimeEq(t *testing.T) {

func TestPointDoubleVersusSage(t *testing.T) {
var curve = ProjectiveCurveParameters{A: curve_A, C: curve_C}
var params = curve.CalcCurveParamsEquiv4()
var xP, xQ ProjectivePoint
xP = ProjectivePoint{X: affine_xP, Z: one}
affine_xQ := xQ.Pow2k(&curve, &xP, 1).ToAffine()

xP.FromAffine(&affine_xP)
affine_xQ := xQ.Pow2k(&params, &xP, 1).ToAffine()
if !affine_xQ.VartimeEq(&affine_xP2) {
t.Error("\nExpected\n", affine_xP2, "\nfound\n", affine_xQ)
}
}

func TestPointTripleVersusSage(t *testing.T) {
var curve = ProjectiveCurveParameters{A: curve_A, C: curve_C}
func TestPointMul4VersusSage(t *testing.T) {
var params = curve.CalcCurveParamsEquiv4()
var xP, xQ ProjectivePoint

xP.FromAffine(&affine_xP)
affine_xQ := xQ.Pow2k(&params, &xP, 2).ToAffine()
if !affine_xQ.VartimeEq(&affine_xP4) {
t.Error("\nExpected\n", affine_xP4, "\nfound\n", affine_xQ)
}
}

func TestPointMul9VersusSage(t *testing.T) {
var params = curve.CalcCurveParamsEquiv3()
var xP, xQ ProjectivePoint
xP = ProjectivePoint{X: affine_xP, Z: one}
affine_xQ := xQ.Pow3k(&curve, &xP, 1).ToAffine()

if !affine_xQ.VartimeEq(&affine_xP3) {
t.Error("\nExpected\n", affine_xP3, "\nfound\n", affine_xQ)
xP.FromAffine(&affine_xP)
affine_xQ := xQ.Pow3k(&params, &xP, 2).ToAffine()
if !affine_xQ.VartimeEq(&affine_xP9) {
t.Error("\nExpected\n", affine_xP9, "\nfound\n", affine_xQ)
}
}

func TestPointPow2kVersusScalarMult(t *testing.T) {
var xP, xQ, xR ProjectivePoint
xP = ProjectivePoint{X: affine_xP, Z: one}
affine_xQ := xQ.Pow2k(&curve, &xP, 5).ToAffine() // = x([32]P)
var params = curve.CalcCurveParamsEquiv4()

xP.FromAffine(&affine_xP)
affine_xQ := xQ.Pow2k(&params, &xP, 5).ToAffine() // = x([32]P)
affine_xR := xR.ScalarMult(&curve, &xP, []byte{32}).ToAffine() // = x([32]P)

if !affine_xQ.VartimeEq(affine_xR) {
@@ -133,71 +295,45 @@ func TestPointPow2kVersusScalarMult(t *testing.T) {
}
}

func TestScalarMultVersusSage(t *testing.T) {
xP := ProjectivePoint{X: affine_xP, Z: one}
affine_xQ := xP.ScalarMult(&curve, &xP, mScalarBytes[:]).ToAffine() // = x([m]P)

if !affine_xaP.VartimeEq(affine_xQ) {
t.Error("\nExpected\n", affine_xaP, "\nfound\n", affine_xQ)
func TestRecoverCoordinateA(t *testing.T) {
var cparam ProjectiveCurveParameters
// Vectors generated with SIKE reference implementation
var a = ExtensionFieldElement{
A: Fp751Element{0x9331D9C5AAF59EA4, 0xB32B702BE4046931, 0xCEBB333912ED4D34, 0x5628CE37CD29C7A2, 0x0BEAC5ED48B7F58E, 0x1FB9D3E281D65B07, 0x9C0CFACC1E195662, 0xAE4BCE0F6B70F7D9, 0x59E4E63D43FE71A0, 0xEF7CE57560CC8615, 0xE44A8FB7901E74E8, 0x000069D13C8366D1},
B: Fp751Element{0xF6DA1070279AB966, 0xA78FB0CE7268C762, 0x19B40F044A57ABFA, 0x7AC8EE6160C0C233, 0x93D4993442947072, 0x757D2B3FA4E44860, 0x073A920F8C4D5257, 0x2031F1B054734037, 0xDEFAA1D2406555CD, 0x26F9C70E1496BE3D, 0x5B3F335A0A4D0976, 0x000013628B2E9C59}}
var affine_xP = ExtensionFieldElement{
A: Fp751Element{0xea6b2d1e2aebb250, 0x35d0b205dc4f6386, 0xb198e93cb1830b8d, 0x3b5b456b496ddcc6, 0x5be3f0d41132c260, 0xce5f188807516a00, 0x54f3e7469ea8866d, 0x33809ef47f36286, 0x6fa45f83eabe1edb, 0x1b3391ae5d19fd86, 0x1e66daf48584af3f, 0xb430c14aaa87},
B: Fp751Element{0x97b41ebc61dcb2ad, 0x80ead31cb932f641, 0x40a940099948b642, 0x2a22fd16cdc7fe84, 0xaabf35b17579667f, 0x76c1d0139feb4032, 0x71467e1e7b1949be, 0x678ca8dadd0d6d81, 0x14445daea9064c66, 0x92d161eab4fa4691, 0x8dfbb01b6b238d36, 0x2e3718434e4e}}
var affine_xQ = ExtensionFieldElement{
A: Fp751Element{0xb055cf0ca1943439, 0xa9ff5de2fa6c69ed, 0x4f2761f934e5730a, 0x61a1dcaa1f94aa4b, 0xce3c8fadfd058543, 0xeac432aaa6701b8e, 0x8491d523093aea8b, 0xba273f9bd92b9b7f, 0xd8f59fd34439bb5a, 0xdc0350261c1fe600, 0x99375ab1eb151311, 0x14d175bbdbc5},
B: Fp751Element{0xffb0ef8c2111a107, 0x55ceca3825991829, 0xdbf8a1ccc075d34b, 0xb8e9187bd85d8494, 0x670aa2d5c34a03b0, 0xef9fe2ed2b064953, 0xc911f5311d645aee, 0xf4411f409e410507, 0x934a0a852d03e1a8, 0xe6274e67ae1ad544, 0x9f4bc563c69a87bc, 0x6f316019681e}}
var affine_xQmP = ExtensionFieldElement{
A: Fp751Element{0x6ffb44306a153779, 0xc0ffef21f2f918f3, 0x196c46d35d77f778, 0x4a73f80452edcfe6, 0x9b00836bce61c67f, 0x387879418d84219e, 0x20700cf9fc1ec5d1, 0x1dfe2356ec64155e, 0xf8b9e33038256b1c, 0xd2aaf2e14bada0f0, 0xb33b226e79a4e313, 0x6be576fad4e5},
B: Fp751Element{0x7db5dbc88e00de34, 0x75cc8cb9f8b6e11e, 0x8c8001c04ebc52ac, 0x67ef6c981a0b5a94, 0xc3654fbe73230738, 0xc6a46ee82983ceca, 0xed1aa61a27ef49f0, 0x17fe5a13b0858fe0, 0x9ae0ca945a4c6b3c, 0x234104a218ad8878, 0xa619627166104394, 0x556a01ff2e7e}}

cparam.RecoverCoordinateA(&affine_xP, &affine_xQ, &affine_xQmP)
cparam.C.One()

// Check A is correct
if !cparam.A.VartimeEq(&a) {
t.Error("\nExpected\n", a, "\nfound\n", cparam.A)
}
}

func TestRecoverCurveParameters(t *testing.T) {
// Created using old public key generation code that output the a value:
var a = ExtensionFieldElement{A: Fp751Element{0x9331d9c5aaf59ea4, 0xb32b702be4046931, 0xcebb333912ed4d34, 0x5628ce37cd29c7a2, 0xbeac5ed48b7f58e, 0x1fb9d3e281d65b07, 0x9c0cfacc1e195662, 0xae4bce0f6b70f7d9, 0x59e4e63d43fe71a0, 0xef7ce57560cc8615, 0xe44a8fb7901e74e8, 0x69d13c8366d1}, B: Fp751Element{0xf6da1070279ab966, 0xa78fb0ce7268c762, 0x19b40f044a57abfa, 0x7ac8ee6160c0c233, 0x93d4993442947072, 0x757d2b3fa4e44860, 0x73a920f8c4d5257, 0x2031f1b054734037, 0xdefaa1d2406555cd, 0x26f9c70e1496be3d, 0x5b3f335a0a4d0976, 0x13628b2e9c59}}
var affine_xP = ExtensionFieldElement{A: Fp751Element{0xea6b2d1e2aebb250, 0x35d0b205dc4f6386, 0xb198e93cb1830b8d, 0x3b5b456b496ddcc6, 0x5be3f0d41132c260, 0xce5f188807516a00, 0x54f3e7469ea8866d, 0x33809ef47f36286, 0x6fa45f83eabe1edb, 0x1b3391ae5d19fd86, 0x1e66daf48584af3f, 0xb430c14aaa87}, B: Fp751Element{0x97b41ebc61dcb2ad, 0x80ead31cb932f641, 0x40a940099948b642, 0x2a22fd16cdc7fe84, 0xaabf35b17579667f, 0x76c1d0139feb4032, 0x71467e1e7b1949be, 0x678ca8dadd0d6d81, 0x14445daea9064c66, 0x92d161eab4fa4691, 0x8dfbb01b6b238d36, 0x2e3718434e4e}}
var affine_xQ = ExtensionFieldElement{A: Fp751Element{0xb055cf0ca1943439, 0xa9ff5de2fa6c69ed, 0x4f2761f934e5730a, 0x61a1dcaa1f94aa4b, 0xce3c8fadfd058543, 0xeac432aaa6701b8e, 0x8491d523093aea8b, 0xba273f9bd92b9b7f, 0xd8f59fd34439bb5a, 0xdc0350261c1fe600, 0x99375ab1eb151311, 0x14d175bbdbc5}, B: Fp751Element{0xffb0ef8c2111a107, 0x55ceca3825991829, 0xdbf8a1ccc075d34b, 0xb8e9187bd85d8494, 0x670aa2d5c34a03b0, 0xef9fe2ed2b064953, 0xc911f5311d645aee, 0xf4411f409e410507, 0x934a0a852d03e1a8, 0xe6274e67ae1ad544, 0x9f4bc563c69a87bc, 0x6f316019681e}}
var affine_xQmP = ExtensionFieldElement{A: Fp751Element{0x6ffb44306a153779, 0xc0ffef21f2f918f3, 0x196c46d35d77f778, 0x4a73f80452edcfe6, 0x9b00836bce61c67f, 0x387879418d84219e, 0x20700cf9fc1ec5d1, 0x1dfe2356ec64155e, 0xf8b9e33038256b1c, 0xd2aaf2e14bada0f0, 0xb33b226e79a4e313, 0x6be576fad4e5}, B: Fp751Element{0x7db5dbc88e00de34, 0x75cc8cb9f8b6e11e, 0x8c8001c04ebc52ac, 0x67ef6c981a0b5a94, 0xc3654fbe73230738, 0xc6a46ee82983ceca, 0xed1aa61a27ef49f0, 0x17fe5a13b0858fe0, 0x9ae0ca945a4c6b3c, 0x234104a218ad8878, 0xa619627166104394, 0x556a01ff2e7e}}

var curveParams = RecoverCurveParameters(&affine_xP, &affine_xQ, &affine_xQmP)

var tmp ExtensionFieldElement
tmp.Inv(&curveParams.C).Mul(&tmp, &curveParams.A)

if !tmp.VartimeEq(&a) {
t.Error("\nExpected\n", a, "\nfound\n", tmp)
}
}

var threePointLadderInputs = [3]ProjectivePoint{
// x(P)
ProjectivePoint{
X: ExtensionFieldElement{A: Fp751Element{0xe8d05f30aac47247, 0x576ec00c55441de7, 0xbf1a8ec5fe558518, 0xd77cb17f77515881, 0x8e9852837ee73ec4, 0x8159634ad4f44a6b, 0x2e4eb5533a798c5, 0x9be8c4354d5bc849, 0xf47dc61806496b84, 0x25d0e130295120e0, 0xdbef54095f8139e3, 0x5a724f20862c}, B: Fp751Element{0x3ca30d7623602e30, 0xfb281eddf45f07b7, 0xd2bf62d5901a45bc, 0xc67c9baf86306dd2, 0x4e2bd93093f538ca, 0xcfd92075c25b9cbe, 0xceafe9a3095bcbab, 0x7d928ad380c85414, 0x37c5f38b2afdc095, 0x75325899a7b779f4, 0xf130568249f20fdd, 0x178f264767d1}},
Z: oneExtensionField,
},
// x(Q)
ProjectivePoint{
X: ExtensionFieldElement{A: Fp751Element{0x2b71a2a93ad1e10e, 0xf0b9842a92cfb333, 0xae17373615a27f5c, 0x3039239f428330c4, 0xa0c4b735ed7dcf98, 0x6e359771ddf6af6a, 0xe986e4cac4584651, 0x8233a2b622d5518, 0xbfd67bf5f06b818b, 0xdffe38d0f5b966a6, 0xa86b36a3272ee00a, 0x193e2ea4f68f}, B: Fp751Element{0x5a0f396459d9d998, 0x479f42250b1b7dda, 0x4016b57e2a15bf75, 0xc59f915203fa3749, 0xd5f90257399cf8da, 0x1fb2dadfd86dcef4, 0x600f20e6429021dc, 0x17e347d380c57581, 0xc1b0d5fa8fe3e440, 0xbcf035330ac20e8, 0x50c2eb5f6a4f03e6, 0x86b7c4571}},
Z: oneExtensionField,
},
// x(P-Q)
ProjectivePoint{
X: ExtensionFieldElement{A: Fp751Element{0x4aafa9f378f7b5ff, 0x1172a683aa8eee0, 0xea518d8cbec2c1de, 0xe191bcbb63674557, 0x97bc19637b259011, 0xdbeae5c9f4a2e454, 0x78f64d1b72a42f95, 0xe71cb4ea7e181e54, 0xe4169d4c48543994, 0x6198c2286a98730f, 0xd21d675bbab1afa5, 0x2e7269fce391}, B: Fp751Element{0x23355783ce1d0450, 0x683164cf4ce3d93f, 0xae6d1c4d25970fd8, 0x7807007fb80b48cf, 0xa005a62ec2bbb8a2, 0x6b5649bd016004cb, 0xbb1a13fa1330176b, 0xbf38e51087660461, 0xe577fddc5dd7b930, 0x5f38116f56947cd3, 0x3124f30b98c36fde, 0x4ca9b6e6db37}},
Z: oneExtensionField,
},
}

func TestThreePointLadderVersusSage(t *testing.T) {
var xR ProjectivePoint
xR.ThreePointLadder(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], mScalarBytes[:])

affine_xR := xR.ToAffine()

sageAffine_xR := ExtensionFieldElement{A: Fp751Element{0x729465ba800d4fd5, 0x9398015b59e514a1, 0x1a59dd6be76c748e, 0x1a7db94eb28dd55c, 0x444686e680b1b8ec, 0xcc3d4ace2a2454ff, 0x51d3dab4ec95a419, 0xc3b0f33594acac6a, 0x9598a74e7fd44f8a, 0x4fbf8c638f1c2e37, 0x844e347033052f51, 0x6cd6de3eafcf}, B: Fp751Element{0x85da145412d73430, 0xd83c0e3b66eb3232, 0xd08ff2d453ec1369, 0xa64aaacfdb395b13, 0xe9cba211a20e806e, 0xa4f80b175d937cfc, 0x556ce5c64b1f7937, 0xb59b39ea2b3fdf7a, 0xc2526b869a4196b3, 0x8dad90bca9371750, 0xdfb4a30c9d9147a2, 0x346d2130629b}}

if !affine_xR.VartimeEq(&sageAffine_xR) {
t.Error("\nExpected\n", sageAffine_xR, "\nfound\n", affine_xR)
// Check C is not changed
if !cparam.C.VartimeEq(&oneExtensionField) {
t.Error("\nExpected\n", cparam.C, "\nfound\n", oneExtensionField)
}
}

func TestR2LVersusSage(t *testing.T) {
var xR ProjectivePoint
xR.RightToLeftLadder(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], mScalarBytes[:])

sageAffine_xR := ExtensionFieldElement{
A: Fp751Element{0x729465ba800d4fd5, 0x9398015b59e514a1, 0x1a59dd6be76c748e, 0x1a7db94eb28dd55c, 0x444686e680b1b8ec, 0xcc3d4ace2a2454ff, 0x51d3dab4ec95a419, 0xc3b0f33594acac6a, 0x9598a74e7fd44f8a, 0x4fbf8c638f1c2e37, 0x844e347033052f51, 0x6cd6de3eafcf},
B: Fp751Element{0x85da145412d73430, 0xd83c0e3b66eb3232, 0xd08ff2d453ec1369, 0xa64aaacfdb395b13, 0xe9cba211a20e806e, 0xa4f80b175d937cfc, 0x556ce5c64b1f7937, 0xb59b39ea2b3fdf7a, 0xc2526b869a4196b3, 0x8dad90bca9371750, 0xdfb4a30c9d9147a2, 0x346d2130629b}}
xR = RightToLeftLadder(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(mScalarBytes)*8), mScalarBytes[:])
affine_xR := xR.ToAffine()

sageAffine_xR := ExtensionFieldElement{A: Fp751Element{0x729465ba800d4fd5, 0x9398015b59e514a1, 0x1a59dd6be76c748e, 0x1a7db94eb28dd55c, 0x444686e680b1b8ec, 0xcc3d4ace2a2454ff, 0x51d3dab4ec95a419, 0xc3b0f33594acac6a, 0x9598a74e7fd44f8a, 0x4fbf8c638f1c2e37, 0x844e347033052f51, 0x6cd6de3eafcf}, B: Fp751Element{0x85da145412d73430, 0xd83c0e3b66eb3232, 0xd08ff2d453ec1369, 0xa64aaacfdb395b13, 0xe9cba211a20e806e, 0xa4f80b175d937cfc, 0x556ce5c64b1f7937, 0xb59b39ea2b3fdf7a, 0xc2526b869a4196b3, 0x8dad90bca9371750, 0xdfb4a30c9d9147a2, 0x346d2130629b}}

if !affine_xR.VartimeEq(&sageAffine_xR) {
t.Error("\nExpected\n", sageAffine_xR, "\nfound\n", affine_xR)
}
@@ -205,14 +341,13 @@ func TestR2LVersusSage(t *testing.T) {

func TestPointTripleVersusAddDouble(t *testing.T) {
tripleEqualsAddDouble := func(curve ProjectiveCurveParameters, P ProjectivePoint) bool {

cachedParams := curve.cachedParams()
cachedTripleParams := curve.cachedTripleParams()
var P2, P3, P2plusP ProjectivePoint
P2.Double(&P, &cachedParams) // = x([2]P)
P3.Triple(&P, &cachedTripleParams) // = x([3]P)
P2plusP.Add(&P2, &P, &P) // = x([2]P + P)

eqivParams4 := curve.CalcCurveParamsEquiv4()
eqivParams3 := curve.CalcCurveParamsEquiv3()
P2.Pow2k(&eqivParams4, &P, 1) // = x([2]P)
P3.Pow3k(&eqivParams3, &P, 1) // = x([3]P)
P2plusP.Add(&P2, &P, &P) // = x([2]P + P)
return P3.VartimeEq(&P2plusP)
}

@@ -221,131 +356,18 @@ func TestPointTripleVersusAddDouble(t *testing.T) {
}
}

func TestScalarMultPrimeFieldAndCoordinateRecoveryVersusSageGeneratedTorsionPoints(t *testing.T) {
// x((11,...)) = 11
var x11 = ProjectivePrimeFieldPoint{X: PrimeFieldElement{A: Fp751Element{0x192a73, 0x0, 0x0, 0x0, 0x0, 0xe6f0000000000000, 0x19024ab93916c5c3, 0x1dcd18cf68876318, 0x7d8c830e0c47ba23, 0x3588ea6a9388299a, 0x8259082aa8e3256c, 0x33533f160446}}, Z: onePrimeField}
// y((11,...)) = oddsqrt(11^3 + 11)
var y11 = PrimeFieldElement{A: Fp751Element{0xd38a264df57f3c8a, 0x9c0450d25042dcdf, 0xaf1ab7be7bbed0b6, 0xa307981c42b29630, 0x845a7e79e0fa2ecb, 0x7ef77ef732108f55, 0x97b5836751081f0d, 0x59e3d115f5275ff4, 0x9a02736282284916, 0xec39f71196540e99, 0xf8b521b28dcc965a, 0x6af0b9d7f54c}}

// x((6,...)) = 6
var x6 = ProjectivePrimeFieldPoint{X: PrimeFieldElement{A: Fp751Element{0xdba10, 0x0, 0x0, 0x0, 0x0, 0x3500000000000000, 0x3714fe4eb8399915, 0xc3a2584753eb43f4, 0xa3151d605c520428, 0xc116cf5232c7c978, 0x49a84d4b8efaf6aa, 0x305731e97514}}, Z: onePrimeField}
// y((6,...)) = oddsqrt(6^3 + 6)
var y6 = PrimeFieldElement{A: Fp751Element{0xe4786c67ba55ff3c, 0x6ffa02bcc2a148e0, 0xe1c5d019df326e2a, 0x232148910f712e87, 0x6ade324bee99c196, 0x4372f82c6bb821f3, 0x91a374a15d391ec4, 0x6e98998b110b7c75, 0x2e093f44d4eeb574, 0x33cdd14668840958, 0xb017cea89e353067, 0x6f907085d4b7}}

// Little-endian bytes of 3^239
var three239Bytes = [...]byte{235, 142, 138, 135, 159, 84, 104, 201, 62, 110, 199, 124, 63, 161, 177, 89, 169, 109, 135, 190, 110, 125, 134, 233, 132, 128, 116, 37, 203, 69, 80, 43, 86, 104, 198, 173, 123, 249, 9, 41, 225, 192, 113, 31, 84, 93, 254, 6}
// Little-endian bytes of 2^372
var two372Bytes = [...]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16}

// E_0 : y^2 = x^3 + x has a = 0, so (a+2)/4 = 1/2
var aPlus2Over4 = PrimeFieldElement{A: Fp751Element{0x124d6, 0x0, 0x0, 0x0, 0x0, 0xb8e0000000000000, 0x9c8a2434c0aa7287, 0xa206996ca9a378a3, 0x6876280d41a41b52, 0xe903b49f175ce04f, 0xf8511860666d227, 0x4ea07cff6e7f}}

// Compute x(P_A) = x([3^239](11,...)) and x([3^239 + 1](11,...))
var xPA, xPAplus11 = ScalarMultPrimeField(&aPlus2Over4, &x11, three239Bytes[:])

// Compute x(P_B) = x([2^372](6,...)) and x([2^372 + 1](6,...))
var xPB, xPBplus6 = ScalarMultPrimeField(&aPlus2Over4, &x6, two372Bytes[:])

// Check that the computed x-coordinates are correct:

var testAffine_xPA = xPA.ToAffine()
if !testAffine_xPA.VartimeEq(&Affine_xPA) {
t.Error("Recomputed x(P_A) incorrectly: found\n", Affine_xPA, "\nexpected\n", testAffine_xPA)
}

var testAffine_xPB = xPB.ToAffine()
if !testAffine_xPB.VartimeEq(&Affine_xPB) {
t.Error("Recomputed x(P_A) incorrectly: found\n", Affine_xPB, "\nexpected\n", testAffine_xPB)
}

// Recover y-coordinates and check that those are correct:
var invZ_A, invZ_B PrimeFieldElement

var X_A, Y_A, Z_A = OkeyaSakuraiCoordinateRecovery(&x11.X, &y11, &xPA, &xPAplus11)
invZ_A.Inv(&Z_A)
Y_A.Mul(&Y_A, &invZ_A) // = Y_A / Z_A
X_A.Mul(&X_A, &invZ_A) // = X_A / Z_A
if !Affine_yPA.VartimeEq(&Y_A) {
t.Error("Recovered y(P_A) incorrectly: found\n", Y_A, "\nexpected\n", Affine_yPA)
}
if !Affine_xPA.VartimeEq(&X_A) {
t.Error("Recovered x(P_A) incorrectly: found\n", X_A, "\nexpected\n", Affine_xPA)
}

var X_B, Y_B, Z_B = OkeyaSakuraiCoordinateRecovery(&x6.X, &y6, &xPB, &xPBplus6)
invZ_B.Inv(&Z_B)
Y_B.Mul(&Y_B, &invZ_B) // = Y_B / Z_B
X_B.Mul(&X_B, &invZ_B) // = X_B / Z_B
if !Affine_yPB.VartimeEq(&Y_B) {
t.Error("Recovered y(P_B) incorrectly: found\n", Y_B, "\nexpected\n", Affine_yPB)
}
if !Affine_xPB.VartimeEq(&X_B) {
t.Error("Recovered x(P_B) incorrectly: found\n", X_B, "\nexpected\n", Affine_xPB)
}
}

func BenchmarkPointAddition(b *testing.B) {
var xP = ProjectivePoint{X: curve_A, Z: curve_C}
var xP2, xP3 ProjectivePoint
cachedParams := curve.cachedParams()
xP2.Double(&xP, &cachedParams)

for n := 0; n < b.N; n++ {
xP3.Add(&xP2, &xP, &xP)
}
}

func BenchmarkPointDouble(b *testing.B) {
var xP = ProjectivePoint{X: curve_A, Z: curve_C}
cachedParams := curve.cachedParams()

for n := 0; n < b.N; n++ {
xP.Double(&xP, &cachedParams)
}
}

func BenchmarkPointTriple(b *testing.B) {
var xP = ProjectivePoint{X: curve_A, Z: curve_C}
cachedParams := curve.cachedTripleParams()

for n := 0; n < b.N; n++ {
xP.Triple(&xP, &cachedParams)
}
}

func BenchmarkScalarMult379BitScalar(b *testing.B) {
var xR ProjectivePoint
var mScalarBytes = [...]uint8{84, 222, 146, 63, 85, 18, 173, 162, 167, 38, 10, 8, 143, 176, 93, 228, 247, 128, 50, 128, 205, 42, 15, 137, 119, 67, 43, 3, 61, 91, 237, 24, 235, 12, 53, 96, 186, 164, 232, 223, 197, 224, 64, 109, 137, 63, 246, 4}

for n := 0; n < b.N; n++ {
xR.ScalarMult(&curve, &threePointLadderInputs[0], mScalarBytes[:])
}
}

func BenchmarkScalarPrimeFieldMult379BitScalar(b *testing.B) {
var xR ProjectivePrimeFieldPoint
var a24 PrimeFieldElement
var mScalarBytes = [...]uint8{84, 222, 146, 63, 85, 18, 173, 162, 167, 38, 10, 8, 143, 176, 93, 228, 247, 128, 50, 128, 205, 42, 15, 137, 119, 67, 43, 3, 61, 91, 237, 24, 235, 12, 53, 96, 186, 164, 232, 223, 197, 224, 64, 109, 137, 63, 246, 4}

for n := 0; n < b.N; n++ {
ScalarMultPrimeField(&a24, &xR, mScalarBytes[:])
}
}

func BenchmarkThreePointLadder379BitScalar(b *testing.B) {
var xR ProjectivePoint
var mScalarBytes = [...]uint8{84, 222, 146, 63, 85, 18, 173, 162, 167, 38, 10, 8, 143, 176, 93, 228, 247, 128, 50, 128, 205, 42, 15, 137, 119, 67, 43, 3, 61, 91, 237, 24, 235, 12, 53, 96, 186, 164, 232, 223, 197, 224, 64, 109, 137, 63, 246, 4}

for n := 0; n < b.N; n++ {
xR.ThreePointLadder(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], mScalarBytes[:])
RightToLeftLadder(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(mScalarBytes)*8), mScalarBytes[:])
}
}

func BenchmarkR2L379BitScalar(b *testing.B) {
var xR ProjectivePoint
var mScalarBytes = [...]uint8{84, 222, 146, 63, 85, 18, 173, 162, 167, 38, 10, 8, 143, 176, 93, 228, 247, 128, 50, 128, 205, 42, 15, 137, 119, 67, 43, 3, 61, 91, 237, 24, 235, 12, 53, 96, 186, 164, 232, 223, 197, 224, 64, 109, 137, 63, 246, 4}

for n := 0; n < b.N; n++ {
xR.RightToLeftLadder(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], mScalarBytes[:])
RightToLeftLadder(&curve, &threePointLadderInputs[0], &threePointLadderInputs[1], &threePointLadderInputs[2], uint(len(mScalarBytes)*8), mScalarBytes[:])
}
}

+ 119
- 165
p751toolbox/isogeny.go Прегледај датотеку

@@ -1,190 +1,144 @@
package p751toolbox

// Represents a 3-isogeny phi, holding the data necessary to evaluate phi.
type ThreeIsogeny struct {
x ExtensionFieldElement
z ExtensionFieldElement
// Interface for working with isogenies.
type Isogeny interface {
// Given a torsion point on a curve computes isogenous curve.
// Returns curve coefficients (A:C), so that E_(A/C) = E_(A/C)/<P>,
// where P is a provided projective point. Sets also isogeny constants
// that are needed for isogeny evaluation.
GenerateCurve(*ProjectivePoint) CurveCoefficientsEquiv
// Evaluates isogeny at caller provided point. Requires isogeny curve constants
// to be earlier computed by GenerateCurve.
EvaluatePoint(*ProjectivePoint) ProjectivePoint
}

// Given a three-torsion point x3 = x(P_3) on the curve E_(A:C), construct the
// three-isogeny phi : E_(A:C) -> E_(A:C)/<P_3> = E_(A':C').
//
// Returns a tuple (codomain, isogeny) = (E_(A':C'), phi).
func ComputeThreeIsogeny(x3 *ProjectivePoint) (ProjectiveCurveParameters, ThreeIsogeny) {
var isogeny ThreeIsogeny
isogeny.x = x3.X
isogeny.z = x3.Z
// We want to compute
// (A':C') = (Z^4 + 18X^2Z^2 - 27X^4 : 4XZ^3)
// To do this, use the identity 18X^2Z^2 - 27X^4 = 9X^2(2Z^2 - 3X^2)
var codomain ProjectiveCurveParameters
var v0, v1, v2, v3 ExtensionFieldElement
v1.Square(&x3.X) // = X^2
v0.Add(&v1, &v1).Add(&v1, &v0) // = 3X^2
v1.Add(&v0, &v0).Add(&v1, &v0) // = 9X^2
v2.Square(&x3.Z) // = Z^2
v3.Square(&v2) // = Z^4
v2.Add(&v2, &v2) // = 2Z^2
v0.Sub(&v2, &v0) // = 2Z^2 - 3X^2
v1.Mul(&v1, &v0) // = 9X^2(2Z^2 - 3X^2)
v0.Mul(&x3.X, &x3.Z) // = XZ
v0.Add(&v0, &v0) // = 2XZ
codomain.A.Add(&v3, &v1) // = Z^4 + 9X^2(2Z^2 - 3X^2)
codomain.C.Mul(&v0, &v2) // = 4XZ^3

return codomain, isogeny
// Stores Isogeny 4 curve constants
type isogeny4 struct {
isogeny3
K3 ExtensionFieldElement
}

// Given a 3-isogeny phi and a point xP = x(P), compute x(Q), the x-coordinate
// of the image Q = phi(P) of P under phi : E_(A:C) -> E_(A':C').
//
// The output xQ = x(Q) is then a point on the curve E_(A':C'); the curve
// parameters are returned by the Compute3Isogeny function used to construct
// phi.
func (phi *ThreeIsogeny) Eval(xP *ProjectivePoint) ProjectivePoint {
var xQ ProjectivePoint
var t0, t1, t2 ExtensionFieldElement
t0.Mul(&phi.x, &xP.X) // = X3*XP
t1.Mul(&phi.z, &xP.Z) // = Z3*XP
t2.Sub(&t0, &t1) // = X3*XP - Z3*ZP
t0.Mul(&phi.z, &xP.X) // = Z3*XP
t1.Mul(&phi.x, &xP.Z) // = X3*ZP
t0.Sub(&t0, &t1) // = Z3*XP - X3*ZP
t2.Square(&t2) // = (X3*XP - Z3*ZP)^2
t0.Square(&t0) // = (Z3*XP - X3*ZP)^2
xQ.X.Mul(&t2, &xP.X) // = XP*(X3*XP - Z3*ZP)^2
xQ.Z.Mul(&t0, &xP.Z) // = ZP*(Z3*XP - X3*ZP)^2
// Stores Isogeny 3 curve constants
type isogeny3 struct {
K1 ExtensionFieldElement
K2 ExtensionFieldElement
}

return xQ
// Constructs isogeny4 objects
func NewIsogeny4() Isogeny {
return new(isogeny4)
}

// Represents a 4-isogeny phi, holding the data necessary to evaluate phi.
//
// See ComputeFourIsogeny for more details.
type FourIsogeny struct {
Xsq_plus_Zsq ExtensionFieldElement
Xsq_minus_Zsq ExtensionFieldElement
XZ2 ExtensionFieldElement
Xpow4 ExtensionFieldElement
Zpow4 ExtensionFieldElement
// Constructs isogeny3 objects
func NewIsogeny3() Isogeny {
return new(isogeny3)
}

// Given a four-torsion point x4 = x(P_4) on the curve E_(A:C), compute the
// coefficients of the codomain E_(A':C') of the four-isogeny phi : E_(A:C) ->
// E_(A:C)/<P_4>.
//
// Returns a tuple (codomain, isogeny) = (E_(A':C') : phi).
// Given a three-torsion point p = x(PB) on the curve E_(A:C), construct the
// three-isogeny phi : E_(A:C) -> E_(A:C)/<P_3> = E_(A':C').
//
// There are two sets of formulas in Costello-Longa-Naehrig for computing
// four-isogenies. One set is for the case where (1,...) lies in the kernel of
// the isogeny (this is the FirstFourIsogeny), and the other (this set) is for
// the case that (1,...) is *not* in the kernel.
func ComputeFourIsogeny(x4 *ProjectivePoint) (ProjectiveCurveParameters, FourIsogeny) {
var codomain ProjectiveCurveParameters
var isogeny FourIsogeny
var v0, v1 ExtensionFieldElement
v0.Square(&x4.X) // = X4^2
v1.Square(&x4.Z) // = Z4^2
isogeny.Xsq_plus_Zsq.Add(&v0, &v1) // = X4^2 + Z4^2
isogeny.Xsq_minus_Zsq.Sub(&v0, &v1) // = X4^2 - Z4^2
isogeny.XZ2.Add(&x4.X, &x4.Z) // = X4 + Z4
isogeny.XZ2.Square(&isogeny.XZ2) // = X4^2 + Z4^2 + 2X4Z4
isogeny.XZ2.Sub(&isogeny.XZ2, &isogeny.Xsq_plus_Zsq) // = 2X4Z4
isogeny.Xpow4.Square(&v0) // = X4^4
isogeny.Zpow4.Square(&v1) // = Z4^4
v0.Add(&isogeny.Xpow4, &isogeny.Xpow4) // = 2X4^4
v0.Sub(&v0, &isogeny.Zpow4) // = 2X4^4 - Z4^4
codomain.A.Add(&v0, &v0) // = 2(2X4^4 - Z4^4)
codomain.C = isogeny.Zpow4 // = Z4^4

return codomain, isogeny
// Input: (XP_3: ZP_3), where P_3 has exact order 3 on E_A/C
// Output: * Curve coordinates (A' + 2C', A' - 2C') corresponding to E_A'/C' = A_E/C/<P3>
// * Isogeny phi with constants in F_p^2
func (phi *isogeny3) GenerateCurve(p *ProjectivePoint) CurveCoefficientsEquiv {
var t0, t1, t2, t3, t4 ExtensionFieldElement
var coefEq CurveCoefficientsEquiv
var K1, K2 = &phi.K1, &phi.K2

K1.Sub(&p.X, &p.Z) // K1 = XP3 - ZP3
t0.Square(K1) // t0 = K1^2
K2.Add(&p.X, &p.Z) // K2 = XP3 + ZP3
t1.Square(K2) // t1 = K2^2
t2.Add(&t0, &t1) // t2 = t0 + t1
t3.Add(K1, K2) // t3 = K1 + K2
t3.Square(&t3) // t3 = t3^2
t3.Sub(&t3, &t2) // t3 = t3 - t2
t2.Add(&t1, &t3) // t2 = t1 + t3
t3.Add(&t3, &t0) // t3 = t3 + t0
t4.Add(&t3, &t0) // t4 = t3 + t0
t4.Add(&t4, &t4) // t4 = t4 + t4
t4.Add(&t1, &t4) // t4 = t1 + t4
coefEq.C.Mul(&t2, &t4) // A24m = t2 * t4
t4.Add(&t1, &t2) // t4 = t1 + t2
t4.Add(&t4, &t4) // t4 = t4 + t4
t4.Add(&t0, &t4) // t4 = t0 + t4
t4.Mul(&t3, &t4) // t4 = t3 * t4
t0.Sub(&t4, &coefEq.C) // t0 = t4 - A24m
coefEq.A.Add(&coefEq.C, &t0) // A24p = A24m + t0
return coefEq
}

// Given a 4-isogeny phi and a point xP = x(P), compute x(Q), the x-coordinate
// of the image Q = phi(P) of P under phi : E_(A:C) -> E_(A':C').
// Given a 3-isogeny phi and a point pB = x(PB), compute x(QB), the x-coordinate
// of the image QB = phi(PB) of PB under phi : E_(A:C) -> E_(A':C').
//
// The output xQ = x(Q) is then a point on the curve E_(A':C'); the curve
// parameters are returned by the ComputeFourIsogeny function used to construct
// phi.
func (phi *FourIsogeny) Eval(xP *ProjectivePoint) ProjectivePoint {
var xQ ProjectivePoint
// parameters are returned by the GenerateCurve function used to construct phi.
func (phi *isogeny3) EvaluatePoint(p *ProjectivePoint) ProjectivePoint {
var t0, t1, t2 ExtensionFieldElement
// We want to compute formula (7) of Costello-Longa-Naehrig, namely
//
// Xprime = (2*X_4*Z*Z_4 - (X_4^2 + Z_4^2)*X)*(X*X_4 - Z*Z_4)^2*X
// Zprime = (2*X*X_4*Z_4 - (X_4^2 + Z_4^2)*Z)*(X_4*Z - X*Z_4)^2*Z
//
// To do this we adapt the method in the MSR implementation, which computes
//
// X_Q = Xprime*( 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4 )
// Z_Q = Zprime*( 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4 )
//
t0.Mul(&xP.X, &phi.XZ2) // = 2*X*X_4*Z_4
t1.Mul(&xP.Z, &phi.Xsq_plus_Zsq) // = (X_4^2 + Z_4^2)*Z
t0.Sub(&t0, &t1) // = -X_4^2*Z + 2*X*X_4*Z_4 - Z*Z_4^2
t1.Mul(&xP.Z, &phi.Xsq_minus_Zsq) // = (X_4^2 - Z_4^2)*Z
t2.Sub(&t0, &t1).Square(&t2) // = 4*(X_4*Z - X*Z_4)^2*X_4^2
t0.Mul(&t0, &t1).Add(&t0, &t0).Add(&t0, &t0) // = 4*(2*X*X_4*Z_4 - (X_4^2 + Z_4^2)*Z)*(X_4^2 - Z_4^2)*Z
t1.Add(&t0, &t2) // = 4*(X*X_4 - Z*Z_4)^2*Z_4^2
t0.Mul(&t0, &t2) // = Zprime * 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2
xQ.Z.Mul(&t0, &phi.Zpow4) // = Zprime * 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4
t2.Mul(&t2, &phi.Zpow4) // = 4*(X_4*Z - X*Z_4)^2*X_4^2*Z_4^4
t0.Mul(&t1, &phi.Xpow4) // = 4*(X*X_4 - Z*Z_4)^2*X_4^4*Z_4^2
t0.Sub(&t2, &t0) // = -4*(X*X_4^2 - 2*X_4*Z*Z_4 + X*Z_4^2)*X*(X_4^2 - Z_4^2)*X_4^2*Z_4^2
xQ.X.Mul(&t1, &t0) // = Xprime * 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4

return xQ
}

// Represents a 4-isogeny phi. See ComputeFourIsogeny for details.
type FirstFourIsogeny struct {
A ExtensionFieldElement
C ExtensionFieldElement
var q ProjectivePoint
var K1, K2 = &phi.K1, &phi.K2
var px, pz = &p.X, &p.Z

t0.Add(px, pz) // t0 = XQ + ZQ
t1.Sub(px, pz) // t1 = XQ - ZQ
t0.Mul(K1, &t0) // t2 = K1 * t0
t1.Mul(K2, &t1) // t1 = K2 * t1
t2.Add(&t0, &t1) // t2 = t0 + t1
t0.Sub(&t1, &t0) // t0 = t1 - t0
t2.Square(&t2) // t2 = t2 ^ 2
t0.Square(&t0) // t0 = t0 ^ 2
q.X.Mul(px, &t2) // XQ'= XQ * t2
q.Z.Mul(pz, &t0) // ZQ'= ZQ * t0
return q
}

// Compute the "first" four-isogeny from the given curve. See also
// ComputeFourIsogeny and Costello-Longa-Naehrig for more details.
func ComputeFirstFourIsogeny(domain *ProjectiveCurveParameters) (ProjectiveCurveParameters, FirstFourIsogeny) {
var codomain ProjectiveCurveParameters
var isogeny FirstFourIsogeny
var t0, t1 ExtensionFieldElement

t0.Add(&domain.C, &domain.C) // = 2*C
codomain.C.Sub(&domain.A, &t0) // = A - 2*C
t1.Add(&t0, &t0) // = 4*C
t1.Add(&t1, &t0) // = 6*C
t0.Add(&t1, &domain.A) // = A + 6*C
codomain.A.Add(&t0, &t0) // = 2*(A + 6*C)

isogeny.A = domain.A
isogeny.C = domain.C

return codomain, isogeny
// Given a four-torsion point p = x(PB) on the curve E_(A:C), construct the
// four-isogeny phi : E_(A:C) -> E_(A:C)/<P_4> = E_(A':C').
//
// Input: (XP_4: ZP_4), where P_4 has exact order 4 on E_A/C
// Output: * Curve coordinates (A' + 2C', 4C') corresponding to E_A'/C' = A_E/C/<P4>
// * Isogeny phi with constants in F_p^2
func (phi *isogeny4) GenerateCurve(p *ProjectivePoint) CurveCoefficientsEquiv {
var coefEq CurveCoefficientsEquiv
var xp4, zp4 = &p.X, &p.Z
var K1, K2, K3 = &phi.K1, &phi.K2, &phi.K3

K2.Sub(xp4, zp4)
K3.Add(xp4, zp4)
K1.Square(zp4)
K1.Add(K1, K1)
coefEq.C.Square(K1)
K1.Add(K1, K1)
coefEq.A.Square(xp4)
coefEq.A.Add(&coefEq.A, &coefEq.A)
coefEq.A.Square(&coefEq.A)
return coefEq
}

// Given a 4-isogeny phi and a point xP = x(P), compute x(Q), the x-coordinate
// of the image Q = phi(P) of P under phi : E_(A:C) -> E_(A':C').
//
// The output xQ = x(Q) is then a point on the curve E_(A':C'); the curve
// parameters are returned by the ComputeFirstFourIsogeny function used to construct
// phi.
func (phi *FirstFourIsogeny) Eval(xP *ProjectivePoint) ProjectivePoint {
var xQ ProjectivePoint
var t0, t1, t2, t3 ExtensionFieldElement

t0.Add(&xP.X, &xP.Z).Square(&t0) // = (X+Z)^2
t2.Mul(&xP.X, &xP.Z) // = X*Z
t1.Add(&t2, &t2) // = 2*X*Z
t1.Sub(&t0, &t1) // = X^2 + Z^2
xQ.X.Mul(&phi.A, &t2) // = A*X*Z
t3.Mul(&phi.C, &t1) // = C*(X^2 + Z^2)
xQ.X.Add(&xQ.X, &t3) // = A*X*Z + C*(X^2 + Z^2)
xQ.X.Mul(&xQ.X, &t0) // = (X+Z)^2 * (A*X*Z + C*(X^2 + Z^2))
t0.Sub(&xP.X, &xP.Z).Square(&t0) // = (X-Z)^2
t0.Mul(&t0, &t2) // = X*Z*(X-Z)^2
t1.Add(&phi.C, &phi.C) // = 2*C
t1.Sub(&t1, &phi.A) // = 2*C - A
xQ.Z.Mul(&t1, &t0) // = (2*C - A)*X*Z*(X-Z)^2

return xQ
// Input: Isogeny returned by GenerateCurve and point q=(Qx,Qz) from E0_A/C
// Output: Corresponding point q from E1_A'/C', where E1 is 4-isogenous to E0
func (phi *isogeny4) EvaluatePoint(p *ProjectivePoint) ProjectivePoint {
var t0, t1 ExtensionFieldElement
var q = *p
var xq, zq = &q.X, &q.Z
var K1, K2, K3 = &phi.K1, &phi.K2, &phi.K3

t0.Add(xq, zq)
t1.Sub(xq, zq)
xq.Mul(&t0, K2)
zq.Mul(&t1, K3)
t0.Mul(&t0, &t1)
t0.Mul(&t0, K1)
t1.Add(xq, zq)
zq.Sub(xq, zq)
t1.Square(&t1)
zq.Square(zq)
xq.Add(&t0, &t1)
t0.Sub(zq, &t0)
xq.Mul(xq, &t1)
zq.Mul(zq, &t0)
return q
}

+ 34
- 78
p751toolbox/isogeny_test.go Прегледај датотеку

@@ -4,59 +4,11 @@ import (
"testing"
)

// Test the first four-isogeny from the base curve E_0(F_{p^2})
func TestFirstFourIsogenyVersusSage(t *testing.T) {
var xR, isogenized_xR, sageIsogenized_xR ProjectivePoint

// sage: p = 2^372 * 3^239 - 1; Fp = GF(p)
// sage: R.<x> = Fp[]
// sage: Fp2 = Fp.extension(x^2 + 1, 'i')
// sage: i = Fp2.gen()
// sage: E0Fp = EllipticCurve(Fp, [0,0,0,1,0])
// sage: E0Fp2 = EllipticCurve(Fp2, [0,0,0,1,0])
// sage: x_PA = 11
// sage: y_PA = -Fp(11^3 + 11).sqrt()
// sage: x_PB = 6
// sage: y_PB = -Fp(6^3 + 6).sqrt()
// sage: P_A = 3^239 * E0Fp((x_PA,y_PA))
// sage: P_B = 2^372 * E0Fp((x_PB,y_PB))
// sage: def tau(P):
// ....: return E0Fp2( (-P.xy()[0], i*P.xy()[1]))
// ....:
// sage: m_B = 3*randint(0,3^238)
// sage: m_A = 2*randint(0,2^371)
// sage: R_A = E0Fp2(P_A) + m_A*tau(P_A)
// sage: def y_recover(x, a):
// ....: return (x**3 + a*x**2 + x).sqrt()
// ....:
// sage: first_4_torsion_point = E0Fp2(1, y_recover(Fp2(1),0))
// sage: sage_first_4_isogeny = E0Fp2.isogeny(first_4_torsion_point)
// sage: a = Fp2(0)
// sage: sage_isomorphism = sage_first_4_isogeny.codomain().isomorphism_to(EllipticCurve(Fp2, [0,(2*(a+6))/(a-2),0,1,0]))
// sage: isogenized_R_A = sage_isomorphism(sage_first_4_isogeny(R_A))

xR.FromAffine(&ExtensionFieldElement{A: Fp751Element{0xa179cb7e2a95fce9, 0xbfd6a0f3a0a892c0, 0x8b2f0aa4250ab3f3, 0x2e7aa4dd4118732d, 0x627969e493acbc2a, 0x21a5b852c7b8cc83, 0x26084278586324f2, 0x383be1aa5aa947c0, 0xc6558ecbb5c0183e, 0xf1f192086a52b035, 0x4c58b755b865c1b, 0x67b4ceea2d2c}, B: Fp751Element{0xfceb02a2797fecbf, 0x3fee9e1d21f95e99, 0xa1c4ce896024e166, 0xc09c024254517358, 0xf0255994b17b94e7, 0xa4834359b41ee894, 0x9487f7db7ebefbe, 0x3bbeeb34a0bf1f24, 0xfa7e5533514c6a05, 0x92b0328146450a9a, 0xfde71ca3fada4c06, 0x3610f995c2bd}})

sageIsogenized_xR.FromAffine(&ExtensionFieldElement{A: Fp751Element{0xff99e76f78da1e05, 0xdaa36bd2bb8d97c4, 0xb4328cee0a409daf, 0xc28b099980c5da3f, 0xf2d7cd15cfebb852, 0x1935103dded6cdef, 0xade81528de1429c3, 0x6775b0fa90a64319, 0x25f89817ee52485d, 0x706e2d00848e697, 0xc4958ec4216d65c0, 0xc519681417f}, B: Fp751Element{0x742fe7dde60e1fb9, 0x801a3c78466a456b, 0xa9f945b786f48c35, 0x20ce89e1b144348f, 0xf633970b7776217e, 0x4c6077a9b38976e5, 0x34a513fc766c7825, 0xacccba359b9cd65, 0xd0ca8383f0fd0125, 0x77350437196287a, 0x9fe1ad7706d4ea21, 0x4d26129ee42d}})

var params ProjectiveCurveParameters
params.A.Zero()
params.C.One()

_, phi := ComputeFirstFourIsogeny(&params)

isogenized_xR = phi.Eval(&xR)

if !sageIsogenized_xR.VartimeEq(&isogenized_xR) {
t.Error("\nExpected\n", sageIsogenized_xR.ToAffine(), "\nfound\n", isogenized_xR.ToAffine())
}
}

func TestFourIsogenyVersusSage(t *testing.T) {
var xP4, xR, isogenized_xR, sageIsogenized_xR ProjectivePoint
var xR, xP4, resPhiXr, expPhiXr ProjectivePoint
var phi = NewIsogeny4()

// sage: p = 2^372 * 3^239 - 1; Fp = GF(p)
// *** Warning: increasing stack size to 2000000.
// *** Warning: increasing stack size to 4000000.
// sage: R.<x> = Fp[]
// sage: Fp2 = Fp.extension(x^2 + 1, 'i')
// sage: i = Fp2.gen()
@@ -93,28 +45,29 @@ func TestFourIsogenyVersusSage(t *testing.T) {
// sage: sage_iso = E2A_sage.isomorphism_to(E2A)
// sage: isogenized2_R_A = sage_iso(phi4(isogenized_R_A))

xP4.FromAffine(&ExtensionFieldElement{A: Fp751Element{0x2afd75a913f3d5e7, 0x2918fba06f88c9ab, 0xa4ac4dc7cb526f05, 0x2d19e9391a607300, 0x7a79e2b34091b54, 0x3ad809dcb42f1792, 0xd46179328bd6402a, 0x1afa73541e2c4f3f, 0xf602d73ace9bdbd8, 0xd77ac58f6bab7004, 0x4689d97f6793b3b3, 0x4f26b00e42b7}, B: Fp751Element{0x6cdf918dafdcb890, 0x666f273cc29cfae2, 0xad00fcd31ba618e2, 0x5fbcf62bef2f6a33, 0xf408bb88318e5098, 0x84ab97849453d175, 0x501bbfcdcfb8e1ac, 0xf2370098e6b5542c, 0xc7dc73f5f0f6bd32, 0xdd76dcd86729d1cf, 0xca22c905029996e4, 0x5cf4a9373de3}})

xR.FromAffine(&ExtensionFieldElement{A: Fp751Element{0xff99e76f78da1e05, 0xdaa36bd2bb8d97c4, 0xb4328cee0a409daf, 0xc28b099980c5da3f, 0xf2d7cd15cfebb852, 0x1935103dded6cdef, 0xade81528de1429c3, 0x6775b0fa90a64319, 0x25f89817ee52485d, 0x706e2d00848e697, 0xc4958ec4216d65c0, 0xc519681417f}, B: Fp751Element{0x742fe7dde60e1fb9, 0x801a3c78466a456b, 0xa9f945b786f48c35, 0x20ce89e1b144348f, 0xf633970b7776217e, 0x4c6077a9b38976e5, 0x34a513fc766c7825, 0xacccba359b9cd65, 0xd0ca8383f0fd0125, 0x77350437196287a, 0x9fe1ad7706d4ea21, 0x4d26129ee42d}})

sageIsogenized_xR.FromAffine(&ExtensionFieldElement{A: Fp751Element{0x111efd8bd0b7a01e, 0x6ab75a4f3789ca9b, 0x939dbe518564cac4, 0xf9eeaba1601d0434, 0x8d41f8ba6edac998, 0xfcd2557efe9aa170, 0xb3c3549c098b7844, 0x52874fef6f81127c, 0xb2b9ac82aa518bb3, 0xee70820230520a86, 0xd4012b7f5efb184a, 0x573e4536329b}, B: Fp751Element{0xa99952281e932902, 0x569a89a571f2c7b1, 0x6150143846ba3f6b, 0x11fd204441e91430, 0x7f469bd55c9b07b, 0xb72db8b9de35b161, 0x455a9a37a940512a, 0xb0cff7670abaf906, 0x18c785b7583375fe, 0x603ab9ca403c9148, 0xab54ba3a6e6c62c1, 0x2726d7d57c4f}})

_, phi := ComputeFourIsogeny(&xP4)

isogenized_xR = phi.Eval(&xR)

if !sageIsogenized_xR.VartimeEq(&isogenized_xR) {
t.Error("\nExpected\n", sageIsogenized_xR.ToAffine(), "\nfound\n", isogenized_xR.ToAffine())
xP4.FromAffine(&ExtensionFieldElement{
A: Fp751Element{0x2afd75a913f3d5e7, 0x2918fba06f88c9ab, 0xa4ac4dc7cb526f05, 0x2d19e9391a607300, 0x7a79e2b34091b54, 0x3ad809dcb42f1792, 0xd46179328bd6402a, 0x1afa73541e2c4f3f, 0xf602d73ace9bdbd8, 0xd77ac58f6bab7004, 0x4689d97f6793b3b3, 0x4f26b00e42b7},
B: Fp751Element{0x6cdf918dafdcb890, 0x666f273cc29cfae2, 0xad00fcd31ba618e2, 0x5fbcf62bef2f6a33, 0xf408bb88318e5098, 0x84ab97849453d175, 0x501bbfcdcfb8e1ac, 0xf2370098e6b5542c, 0xc7dc73f5f0f6bd32, 0xdd76dcd86729d1cf, 0xca22c905029996e4, 0x5cf4a9373de3}})
xR.FromAffine(&ExtensionFieldElement{
A: Fp751Element{0xff99e76f78da1e05, 0xdaa36bd2bb8d97c4, 0xb4328cee0a409daf, 0xc28b099980c5da3f, 0xf2d7cd15cfebb852, 0x1935103dded6cdef, 0xade81528de1429c3, 0x6775b0fa90a64319, 0x25f89817ee52485d, 0x706e2d00848e697, 0xc4958ec4216d65c0, 0xc519681417f},
B: Fp751Element{0x742fe7dde60e1fb9, 0x801a3c78466a456b, 0xa9f945b786f48c35, 0x20ce89e1b144348f, 0xf633970b7776217e, 0x4c6077a9b38976e5, 0x34a513fc766c7825, 0xacccba359b9cd65, 0xd0ca8383f0fd0125, 0x77350437196287a, 0x9fe1ad7706d4ea21, 0x4d26129ee42d}})
expPhiXr.FromAffine(&ExtensionFieldElement{
A: Fp751Element{0x111efd8bd0b7a01e, 0x6ab75a4f3789ca9b, 0x939dbe518564cac4, 0xf9eeaba1601d0434, 0x8d41f8ba6edac998, 0xfcd2557efe9aa170, 0xb3c3549c098b7844, 0x52874fef6f81127c, 0xb2b9ac82aa518bb3, 0xee70820230520a86, 0xd4012b7f5efb184a, 0x573e4536329b},
B: Fp751Element{0xa99952281e932902, 0x569a89a571f2c7b1, 0x6150143846ba3f6b, 0x11fd204441e91430, 0x7f469bd55c9b07b, 0xb72db8b9de35b161, 0x455a9a37a940512a, 0xb0cff7670abaf906, 0x18c785b7583375fe, 0x603ab9ca403c9148, 0xab54ba3a6e6c62c1, 0x2726d7d57c4f}})

phi.GenerateCurve(&xP4)
resPhiXr = phi.EvaluatePoint(&xR)
if !expPhiXr.VartimeEq(&resPhiXr) {
t.Error("\nExpected\n", expPhiXr.ToAffine(), "\nfound\n", resPhiXr.ToAffine())
}
}

func TestThreeIsogenyVersusSage(t *testing.T) {
var xR, xP3, isogenized_xR, sageIsogenized_xR ProjectivePoint
var xR, xP3, resPhiXr, expPhiXr ProjectivePoint
var phi = NewIsogeny3()

// sage: %colors Linux
// sage: p = 2^372 * 3^239 - 1; Fp = GF(p)
// *** Warning: increasing stack size to 2000000.
// *** Warning: increasing stack size to 4000000.
// sage: R.<x> = Fp[]
// sage: Fp2 = Fp.extension(x^2 + 1, 'i')
// sage: i = Fp2.gen()
@@ -142,17 +95,20 @@ func TestThreeIsogenyVersusSage(t *testing.T) {
// ....:
// sage: isogenized_R_B = three_isog(P_3, R_B)

xR.FromAffine(&ExtensionFieldElement{A: Fp751Element{0xbd0737ed5cc9a3d7, 0x45ae6d476517c101, 0x6f228e9e7364fdb2, 0xbba4871225b3dbd, 0x6299ccd2e5da1a07, 0x38488fe4af5f2d0e, 0xec23cae5a86e980c, 0x26c804ba3f1edffa, 0xfbbed81932df60e5, 0x7e00e9d182ae9187, 0xc7654abb66d05f4b, 0x262d0567237b}, B: Fp751Element{0x3a3b5b6ad0b2ac33, 0x246602b5179127d3, 0x502ae0e9ad65077d, 0x10a3a37237e1bf70, 0x4a1ab9294dd05610, 0xb0f3adac30fe1fa6, 0x341995267faf70cb, 0xa14dd94d39cf4ec1, 0xce4b7527d1bf5568, 0xe0410423ed45c7e4, 0x38011809b6425686, 0x28f52472ebed}})

xP3.FromAffine(&ExtensionFieldElement{A: Fp751Element{0x7bb7a4a07b0788dc, 0xdc36a3f6607b21b0, 0x4750e18ee74cf2f0, 0x464e319d0b7ab806, 0xc25aa44c04f758ff, 0x392e8521a46e0a68, 0xfc4e76b63eff37df, 0x1f3566d892e67dd8, 0xf8d2eb0f73295e65, 0x457b13ebc470bccb, 0xfda1cc9efef5be33, 0x5dbf3d92cc02}, B: Fp751Element{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}})

sageIsogenized_xR.FromAffine(&ExtensionFieldElement{A: Fp751Element{0x286db7d75913c5b1, 0xcb2049ad50189220, 0xccee90ef765fa9f4, 0x65e52ce2730e7d88, 0xa6b6b553bd0d06e7, 0xb561ecec14591590, 0x17b7a66d8c64d959, 0x77778cecbe1461e, 0x9405c9c0c41a57ce, 0x8f6b4847e8ca7d3d, 0xf625eb987b366937, 0x421b3590e345}, B: Fp751Element{0x566b893803e7d8d6, 0xe8c71a04d527e696, 0x5a1d8f87bf5eb51, 0x42ae08ae098724f, 0x4ee3d7c7af40ca2e, 0xd9f9ab9067bb10a7, 0xecd53d69edd6328c, 0xa581e9202dea107d, 0x8bcdfb6c8ecf9257, 0xe7cbbc2e5cbcf2af, 0x5f031a8701f0e53e, 0x18312d93e3cb}})

_, phi := ComputeThreeIsogeny(&xP3)

isogenized_xR = phi.Eval(&xR)

if !sageIsogenized_xR.VartimeEq(&isogenized_xR) {
t.Error("\nExpected\n", sageIsogenized_xR.ToAffine(), "\nfound\n", isogenized_xR.ToAffine())
xR.FromAffine(&ExtensionFieldElement{
A: Fp751Element{0xbd0737ed5cc9a3d7, 0x45ae6d476517c101, 0x6f228e9e7364fdb2, 0xbba4871225b3dbd, 0x6299ccd2e5da1a07, 0x38488fe4af5f2d0e, 0xec23cae5a86e980c, 0x26c804ba3f1edffa, 0xfbbed81932df60e5, 0x7e00e9d182ae9187, 0xc7654abb66d05f4b, 0x262d0567237b},
B: Fp751Element{0x3a3b5b6ad0b2ac33, 0x246602b5179127d3, 0x502ae0e9ad65077d, 0x10a3a37237e1bf70, 0x4a1ab9294dd05610, 0xb0f3adac30fe1fa6, 0x341995267faf70cb, 0xa14dd94d39cf4ec1, 0xce4b7527d1bf5568, 0xe0410423ed45c7e4, 0x38011809b6425686, 0x28f52472ebed}})
xP3.FromAffine(&ExtensionFieldElement{
A: Fp751Element{0x7bb7a4a07b0788dc, 0xdc36a3f6607b21b0, 0x4750e18ee74cf2f0, 0x464e319d0b7ab806, 0xc25aa44c04f758ff, 0x392e8521a46e0a68, 0xfc4e76b63eff37df, 0x1f3566d892e67dd8, 0xf8d2eb0f73295e65, 0x457b13ebc470bccb, 0xfda1cc9efef5be33, 0x5dbf3d92cc02},
B: Fp751Element{0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}})
expPhiXr.FromAffine(&ExtensionFieldElement{
A: Fp751Element{0x286db7d75913c5b1, 0xcb2049ad50189220, 0xccee90ef765fa9f4, 0x65e52ce2730e7d88, 0xa6b6b553bd0d06e7, 0xb561ecec14591590, 0x17b7a66d8c64d959, 0x77778cecbe1461e, 0x9405c9c0c41a57ce, 0x8f6b4847e8ca7d3d, 0xf625eb987b366937, 0x421b3590e345},
B: Fp751Element{0x566b893803e7d8d6, 0xe8c71a04d527e696, 0x5a1d8f87bf5eb51, 0x42ae08ae098724f, 0x4ee3d7c7af40ca2e, 0xd9f9ab9067bb10a7, 0xecd53d69edd6328c, 0xa581e9202dea107d, 0x8bcdfb6c8ecf9257, 0xe7cbbc2e5cbcf2af, 0x5f031a8701f0e53e, 0x18312d93e3cb}})

phi.GenerateCurve(&xP3)
resPhiXr = phi.EvaluatePoint(&xR)

if !expPhiXr.VartimeEq(&resPhiXr) {
t.Error("\nExpected\n", expPhiXr.ToAffine(), "\nfound\n", resPhiXr.ToAffine())
}
}

p751toolbox/print.go → p751toolbox/print_test.go Прегледај датотеку

@@ -1,5 +1,7 @@
package p751toolbox

// Tools used for testing and debugging

import (
"bytes"
"fmt"
@@ -39,3 +41,30 @@ func (point ProjectivePoint) String() string {
func (point ProjectivePrimeFieldPoint) String() string {
return fmt.Sprintf("X:\n%sZ:\n%s", point.X.String(), point.Z.String())
}

func (point Fp751Element) PrintHex() {
fmt.Printf("\t")
for i := 0; i < fp751NumWords/2; i++ {
fmt.Printf("0x%0.16X, ", point[i])
}
fmt.Printf("\n\t\t")
for i := fp751NumWords / 2; i < fp751NumWords; i++ {
fmt.Printf("0x%0.16X, ", point[i])
}
fmt.Printf("\n")
}

func (extElement ExtensionFieldElement) PrintHex() {
fmt.Printf("\t[r]: ")
extElement.A.PrintHex()
fmt.Printf("\t[u]: ")
extElement.B.PrintHex()
}

func (point ProjectivePoint) PrintHex() {
fmt.Printf("X:")
point.X.PrintHex()
fmt.Printf("Z:")
point.X.PrintHex()
fmt.Printf("\n")
}

+ 0
- 421
sidh.go Прегледај датотеку

@@ -1,421 +0,0 @@
// Package p751sidh implements (ephemeral) supersingular isogeny
// Diffie-Hellman, as described in Costello-Longa-Naehrig 2016. Portions of
// the field arithmetic implementation were based on their implementation.
// Internal functions useful for the implementation are published in the
// p751toolbox package.
//
// This package follows their naming convention, writing "Alice" for the party
// using 2^e-isogenies and "Bob" for the party using 3^e-isogenies.
//
// This package does NOT implement SIDH key validation, so it should only be
// used for ephemeral DH. Each keypair should be used at most once.
//
// If you feel that SIDH may be appropriate for you, consult your
// cryptographer.
package p751sidh

import (
"errors"
"io"
)

import . "github.com/cloudflare/p751sidh/p751toolbox"

const (
// The secret key size, in bytes.
SecretKeySize = 48
// The public key size, in bytes.
PublicKeySize = 564
// The shared secret size, in bytes.
SharedSecretSize = 188
)

const maxAlice = 185

var aliceIsogenyStrategy = [maxAlice]int{0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 5,
6, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 12, 11, 12, 12, 13, 14, 15, 16, 16, 16, 16,
16, 16, 17, 17, 18, 18, 17, 21, 17, 18, 21, 20, 21, 21, 21, 21, 21, 22, 25, 25,
25, 26, 27, 28, 28, 29, 30, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 35, 36,
36, 33, 36, 35, 36, 36, 35, 36, 36, 37, 38, 38, 39, 40, 41, 42, 38, 39, 40, 41,
42, 40, 46, 42, 43, 46, 46, 46, 46, 48, 48, 48, 48, 49, 49, 48, 53, 54, 51, 52,
53, 54, 55, 56, 57, 58, 59, 59, 60, 62, 62, 63, 64, 64, 64, 64, 64, 64, 64, 64,
65, 65, 65, 65, 65, 66, 67, 65, 66, 67, 66, 69, 70, 66, 67, 66, 69, 70, 69, 70,
70, 71, 72, 71, 72, 72, 74, 74, 75, 72, 72, 74, 74, 75, 72, 72, 74, 75, 75, 72,
72, 74, 75, 75, 77, 77, 79, 80, 80, 82}

const maxBob = 239

var bobIsogenyStrategy = [maxBob]int{0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6,
7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16,
16, 16, 16, 16, 17, 16, 16, 17, 19, 19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 24, 24, 25, 27, 27, 28, 28, 29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29,
30, 33, 33, 33, 33, 34, 35, 37, 37, 37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39,
43, 38, 38, 38, 38, 43, 40, 41, 42, 43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50,
51, 50, 49, 49, 49, 49, 51, 49, 53, 50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56,
56, 56, 56, 56, 58, 58, 61, 61, 61, 63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65,
65, 66, 66, 66, 66, 66, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66,
73, 68, 68, 71, 71, 73, 73, 73, 75, 75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83,
84, 85, 86, 86, 86, 86, 86, 87, 86, 88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86,
88, 88, 86, 86, 86, 93, 90, 90, 92, 92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97,
97, 97}

// Bob's public key.
type SIDHPublicKeyBob struct {
affine_xP ExtensionFieldElement
affine_xQ ExtensionFieldElement
affine_xQmP ExtensionFieldElement
}

// Read a public key from a byte slice. The input must be at least 564 bytes long.
func (pubKey *SIDHPublicKeyBob) FromBytes(input []byte) {
if len(input) < 564 {
panic("Too short input to SIDH pubkey FromBytes, expected 564 bytes")
}
pubKey.affine_xP.FromBytes(input[0:188])
pubKey.affine_xQ.FromBytes(input[188:376])
pubKey.affine_xQmP.FromBytes(input[376:564])
}

// Write a public key to a byte slice. The output must be at least 564 bytes long.
func (pubKey *SIDHPublicKeyBob) ToBytes(output []byte) {
if len(output) < 564 {
panic("Too short output for SIDH pubkey FromBytes, expected 564 bytes")
}
pubKey.affine_xP.ToBytes(output[0:188])
pubKey.affine_xQ.ToBytes(output[188:376])
pubKey.affine_xQmP.ToBytes(output[376:564])
}

// Alice's public key.
type SIDHPublicKeyAlice struct {
affine_xP ExtensionFieldElement
affine_xQ ExtensionFieldElement
affine_xQmP ExtensionFieldElement
}

// Read a public key from a byte slice. The input must be at least 564 bytes long.
func (pubKey *SIDHPublicKeyAlice) FromBytes(input []byte) {
if len(input) < 564 {
panic("Too short input to SIDH pubkey FromBytes, expected 564 bytes")
}
pubKey.affine_xP.FromBytes(input[0:188])
pubKey.affine_xQ.FromBytes(input[188:376])
pubKey.affine_xQmP.FromBytes(input[376:564])
}

// Write a public key to a byte slice. The output must be at least 564 bytes long.
func (pubKey *SIDHPublicKeyAlice) ToBytes(output []byte) {
if len(output) < 564 {
panic("Too short output for SIDH pubkey FromBytes, expected 564 bytes")
}
pubKey.affine_xP.ToBytes(output[0:188])
pubKey.affine_xQ.ToBytes(output[188:376])
pubKey.affine_xQmP.ToBytes(output[376:564])
}

// Bob's secret key.
type SIDHSecretKeyBob struct {
Scalar [SecretKeySize]byte
}

// Alice's secret key.
type SIDHSecretKeyAlice struct {
Scalar [SecretKeySize]byte
}

// Generate a keypair for "Alice". Note that because this library does not
// implement SIDH validation, each keypair should be used for at most one
// shared secret computation.
func GenerateAliceKeypair(rand io.Reader) (publicKey *SIDHPublicKeyAlice, secretKey *SIDHSecretKeyAlice, err error) {
publicKey = new(SIDHPublicKeyAlice)
secretKey = new(SIDHSecretKeyAlice)

_, err = io.ReadFull(rand, secretKey.Scalar[:])
if err != nil {
return nil, nil, err
}

// Bit-twiddle to ensure scalar is in 2*[0,2^371):
secretKey.Scalar[47] = 0
secretKey.Scalar[46] &= 15 // clear high bits, so scalar < 2^372
secretKey.Scalar[0] &= 254 // clear low bit, so scalar is even

// We actually want scalar in 2*(0,2^371), but the above procedure
// generates 0 with probability 2^(-371), which isn't worth checking
// for.

*publicKey = secretKey.PublicKey()

return
}

// Set result to zero if the input scalar is <= 3^238.
//go:noescape
func checkLessThanThree238(scalar *[48]byte, result *uint32)

// Set scalar = 3*scalar
//go:noescape
func multiplyByThree(scalar *[48]byte)

// Generate a keypair for "Bob". Note that because this library does not
// implement SIDH validation, each keypair should be used for at most one
// shared secret computation.
func GenerateBobKeypair(rand io.Reader) (publicKey *SIDHPublicKeyBob, secretKey *SIDHSecretKeyBob, err error) {
publicKey = new(SIDHPublicKeyBob)
secretKey = new(SIDHSecretKeyBob)

// Perform rejection sampling to obtain a random value in [0,3^238]:
var ok uint32
for i := 0; i < 102; i++ {
_, err = io.ReadFull(rand, secretKey.Scalar[:])
if err != nil {
return nil, nil, err
}
// Mask the high bits to obtain a uniform value in [0,2^378):
secretKey.Scalar[47] &= 3
// Accept if scalar < 3^238 (this happens w/ prob ~0.5828)
checkLessThanThree238(&secretKey.Scalar, &ok)
if ok == 0 {
break
}
}
// ok is nonzero if all 102 trials failed.
// This happens with probability 0.41719...^102 < 2^(-128), i.e., never
if ok != 0 {
return nil, nil, errors.New("WOW! An event with probability < 2^(-128) occurred!!")
}

// Multiply by 3 to get a scalar in 3*[0,3^238):
multiplyByThree(&secretKey.Scalar)

// We actually want scalar in 2*(0,2^371), but the above procedure
// generates 0 with probability 3^(-238), which isn't worth checking
// for.

*publicKey = secretKey.PublicKey()

return
}

// Compute the corresponding public key for the given secret key.
func (secretKey *SIDHSecretKeyAlice) PublicKey() SIDHPublicKeyAlice {
var xP, xQ, xQmP, xR ProjectivePoint

xP.FromAffinePrimeField(&Affine_xPB) // = ( x_P : 1) = x(P_B)
xQ.FromAffinePrimeField(&Affine_xPB) //
xQ.X.Neg(&xQ.X) // = (-x_P : 1) = x(Q_B)
xQmP = DistortAndDifference(&Affine_xPB) // = x(Q_B - P_B)

xR = SecretPoint(&Affine_xPA, &Affine_yPA, secretKey.Scalar[:])

var currentCurve ProjectiveCurveParameters
// Starting curve has a = 0, so (A:C) = (0,1)
currentCurve.A.Zero()
currentCurve.C.One()

var firstPhi FirstFourIsogeny
currentCurve, firstPhi = ComputeFirstFourIsogeny(&currentCurve)

xP = firstPhi.Eval(&xP)
xQ = firstPhi.Eval(&xQ)
xQmP = firstPhi.Eval(&xQmP)
xR = firstPhi.Eval(&xR)

var points = make([]ProjectivePoint, 0, 8)
var indices = make([]int, 0, 8)
var phi FourIsogeny

var i = 0

for j := 1; j < 185; j++ {
for i < 185-j {
points = append(points, xR)
indices = append(indices, i)
k := int(aliceIsogenyStrategy[185-i-j])
xR.Pow2k(&currentCurve, &xR, uint32(2*k))
i = i + k
}
currentCurve, phi = ComputeFourIsogeny(&xR)

for k := 0; k < len(points); k++ {
points[k] = phi.Eval(&points[k])
}

xP = phi.Eval(&xP)
xQ = phi.Eval(&xQ)
xQmP = phi.Eval(&xQmP)

// pop xR from points
xR, points = points[len(points)-1], points[:len(points)-1]
i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
}

currentCurve, phi = ComputeFourIsogeny(&xR)
xP = phi.Eval(&xP)
xQ = phi.Eval(&xQ)
xQmP = phi.Eval(&xQmP)

var invZP, invZQ, invZQmP ExtensionFieldElement
ExtensionFieldBatch3Inv(&xP.Z, &xQ.Z, &xQmP.Z, &invZP, &invZQ, &invZQmP)

var publicKey SIDHPublicKeyAlice
publicKey.affine_xP.Mul(&xP.X, &invZP)
publicKey.affine_xQ.Mul(&xQ.X, &invZQ)
publicKey.affine_xQmP.Mul(&xQmP.X, &invZQmP)

return publicKey
}

// Compute the public key corresponding to the secret key.
func (secretKey *SIDHSecretKeyBob) PublicKey() SIDHPublicKeyBob {
var xP, xQ, xQmP, xR ProjectivePoint

xP.FromAffinePrimeField(&Affine_xPA) // = ( x_P : 1) = x(P_A)
xQ.FromAffinePrimeField(&Affine_xPA) //
xQ.X.Neg(&xQ.X) // = (-x_P : 1) = x(Q_A)
xQmP = DistortAndDifference(&Affine_xPA) // = x(Q_B - P_B)

xR = SecretPoint(&Affine_xPB, &Affine_yPB, secretKey.Scalar[:])

var currentCurve ProjectiveCurveParameters
// Starting curve has a = 0, so (A:C) = (0,1)
currentCurve.A.Zero()
currentCurve.C.One()

var points = make([]ProjectivePoint, 0, 8)
var indices = make([]int, 0, 8)
var phi ThreeIsogeny

var i = 0

for j := 1; j < 239; j++ {
for i < 239-j {
points = append(points, xR)
indices = append(indices, i)
k := int(bobIsogenyStrategy[239-i-j])
xR.Pow3k(&currentCurve, &xR, uint32(k))
i = i + k
}
currentCurve, phi = ComputeThreeIsogeny(&xR)

for k := 0; k < len(points); k++ {
points[k] = phi.Eval(&points[k])
}

xP = phi.Eval(&xP)
xQ = phi.Eval(&xQ)
xQmP = phi.Eval(&xQmP)

// pop xR from points
xR, points = points[len(points)-1], points[:len(points)-1]
i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
}

currentCurve, phi = ComputeThreeIsogeny(&xR)
xP = phi.Eval(&xP)
xQ = phi.Eval(&xQ)
xQmP = phi.Eval(&xQmP)

var invZP, invZQ, invZQmP ExtensionFieldElement
ExtensionFieldBatch3Inv(&xP.Z, &xQ.Z, &xQmP.Z, &invZP, &invZQ, &invZQmP)

var publicKey SIDHPublicKeyBob
publicKey.affine_xP.Mul(&xP.X, &invZP)
publicKey.affine_xQ.Mul(&xQ.X, &invZQ)
publicKey.affine_xQmP.Mul(&xQmP.X, &invZQmP)

return publicKey
}

// Compute (Alice's view of) a shared secret using Alice's secret key and Bob's public key.
func (aliceSecret *SIDHSecretKeyAlice) SharedSecret(bobPublic *SIDHPublicKeyBob) [SharedSecretSize]byte {
var currentCurve = RecoverCurveParameters(&bobPublic.affine_xP, &bobPublic.affine_xQ, &bobPublic.affine_xQmP)

var xR, xP, xQ, xQmP ProjectivePoint

xP.FromAffine(&bobPublic.affine_xP)
xQ.FromAffine(&bobPublic.affine_xQ)
xQmP.FromAffine(&bobPublic.affine_xQmP)

xR.RightToLeftLadder(&currentCurve, &xP, &xQ, &xQmP, aliceSecret.Scalar[:])

var firstPhi FirstFourIsogeny
currentCurve, firstPhi = ComputeFirstFourIsogeny(&currentCurve)
xR = firstPhi.Eval(&xR)

var points = make([]ProjectivePoint, 0, 8)
var indices = make([]int, 0, 8)
var phi FourIsogeny

var i = 0

for j := 1; j < 185; j++ {
for i < 185-j {
points = append(points, xR)
indices = append(indices, i)
k := int(aliceIsogenyStrategy[185-i-j])
xR.Pow2k(&currentCurve, &xR, uint32(2*k))
i = i + k
}
currentCurve, phi = ComputeFourIsogeny(&xR)

for k := 0; k < len(points); k++ {
points[k] = phi.Eval(&points[k])
}

// pop xR from points
xR, points = points[len(points)-1], points[:len(points)-1]
i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
}

currentCurve, _ = ComputeFourIsogeny(&xR)

var sharedSecret [SharedSecretSize]byte
var jInv = currentCurve.JInvariant()
jInv.ToBytes(sharedSecret[:])
return sharedSecret
}

// Compute (Bob's view of) a shared secret using Bob's secret key and Alice's public key.
func (bobSecret *SIDHSecretKeyBob) SharedSecret(alicePublic *SIDHPublicKeyAlice) [SharedSecretSize]byte {
var currentCurve = RecoverCurveParameters(&alicePublic.affine_xP, &alicePublic.affine_xQ, &alicePublic.affine_xQmP)

var xR, xP, xQ, xQmP ProjectivePoint

xP.FromAffine(&alicePublic.affine_xP)
xQ.FromAffine(&alicePublic.affine_xQ)
xQmP.FromAffine(&alicePublic.affine_xQmP)

xR.RightToLeftLadder(&currentCurve, &xP, &xQ, &xQmP, bobSecret.Scalar[:])

var points = make([]ProjectivePoint, 0, 8)
var indices = make([]int, 0, 8)
var phi ThreeIsogeny

var i = 0

for j := 1; j < 239; j++ {
for i < 239-j {
points = append(points, xR)
indices = append(indices, i)
k := int(bobIsogenyStrategy[239-i-j])
xR.Pow3k(&currentCurve, &xR, uint32(k))
i = i + k
}
currentCurve, phi = ComputeThreeIsogeny(&xR)

for k := 0; k < len(points); k++ {
points[k] = phi.Eval(&points[k])
}

// pop xR from points
xR, points = points[len(points)-1], points[:len(points)-1]
i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
}
currentCurve, _ = ComputeThreeIsogeny(&xR)

var sharedSecret [SharedSecretSize]byte
var jInv = currentCurve.JInvariant()
jInv.ToBytes(sharedSecret[:])
return sharedSecret
}

+ 184
- 0
sidh/api.go Прегледај датотеку

@@ -0,0 +1,184 @@
package sidh

import (
"errors"
. "github.com/cloudflare/p751sidh/p751toolbox"
"io"
)

// I keep it bool in order to be able to apply logical NOT
type KeyVariant uint
type PrimeFieldId uint

// Id's correspond to bitlength of the prime field characteristic
// Currently FP_751 is the only one supported by this implementation
const (
FP_503 PrimeFieldId = iota
FP_751
FP_964
maxPrimeFieldId
)

const (
// SIDH variant - either A for 2-torsion or B for 3-torsion group

// 001 - SIDH: corresponds to 2-torsion group
KeyVariant_SIDH_A KeyVariant = 1 << 0
// 010 - SIDH: corresponds to 3-torsion group
KeyVariant_SIDH_B = 1 << 1
)

// Base type for public and private key. Used mainly to carry domain
// parameters.
type key struct {
// Domain parameters of the algorithm to be used with a key
params *SidhParams
// Flag indicates wether corresponds to 2- or 3-torsion group
keyVariant KeyVariant
}

// Defines operations on public key
type PublicKey struct {
key
affine_xP ExtensionFieldElement
affine_xQ ExtensionFieldElement
affine_xQmP ExtensionFieldElement
}

// Defines operations on private key
type PrivateKey struct {
key
// Secret key
Scalar []byte
}

// Accessor to the domain parameters
func (key *key) Params() *SidhParams {
return key.params
}

// Accessor to key variant
func (key *key) Variant() KeyVariant {
return key.keyVariant
}

// NewPrivateKey initializes private key.
// Usage of this function guarantees that the object is correctly initialized.
func NewPrivateKey(id PrimeFieldId, v KeyVariant) *PrivateKey {
prv := &PrivateKey{key: key{params: Params(id), keyVariant: v}}
prv.Scalar = make([]byte, prv.params.SecretKeySize)
return prv
}

// NewPublicKey initializes public key.
// Usage of this function guarantees that the object is correctly initialized.
func NewPublicKey(id PrimeFieldId, v KeyVariant) *PublicKey {
return &PublicKey{key: key{params: Params(id), keyVariant: v}}
}

// Import clears content of the public key currently stored in the structure
// and imports key stored in the byte string. Returns error in case byte string
// size is wrong. Doesn't perform any validation.
func (pub *PublicKey) Import(input []byte) error {
if len(input) != pub.Size() {
return errors.New("sidh: input to short")
}
pub.affine_xP.FromBytes(input[0:pub.params.SharedSecretSize])
pub.affine_xQ.FromBytes(input[pub.params.SharedSecretSize : 2*pub.params.SharedSecretSize])
pub.affine_xQmP.FromBytes(input[2*pub.params.SharedSecretSize : 3*pub.params.SharedSecretSize])
return nil
}

// Exports currently stored key. In case structure hasn't been filled with key data
// returned byte string is filled with zeros.
func (pub *PublicKey) Export() []byte {
output := make([]byte, pub.params.PublicKeySize)
pub.affine_xP.ToBytes(output[0:pub.params.SharedSecretSize])
pub.affine_xQ.ToBytes(output[pub.params.SharedSecretSize : 2*pub.params.SharedSecretSize])
pub.affine_xQmP.ToBytes(output[2*pub.params.SharedSecretSize : 3*pub.params.SharedSecretSize])
return output
}

// Size returns size of the public key in bytes
func (pub *PublicKey) Size() int {
return pub.params.PublicKeySize
}

// Exports currently stored key. In case structure hasn't been filled with key data
// returned byte string is filled with zeros.
func (prv *PrivateKey) Export() []byte {
ret := make([]byte, len(prv.Scalar))
copy(ret, prv.Scalar)
return ret
}

// Size returns size of the private key in bytes
func (prv *PrivateKey) Size() int {
return prv.params.SecretKeySize
}

// Import clears content of the private key currently stored in the structure
// and imports key from octet string.
func (prv *PrivateKey) Import(input []byte) error {
if len(input) != prv.Size() {
return errors.New("sidh: input to short")
}
if len(prv.Scalar) != prv.params.SecretKeySize {
return errors.New("sidh: object wrongly initialized")
}
copy(prv.Scalar, input)
return nil
}

// Generates random private key for SIDH. Returns error
// in case user provided RNG or memory initialization fails.
func (prv *PrivateKey) Generate(rand io.Reader) error {
var err error

if (prv.keyVariant & KeyVariant_SIDH_A) == KeyVariant_SIDH_A {
err = prv.generatePrivateKeyA(rand)
} else {
err = prv.generatePrivateKeyB(rand)
}

return err
}

// Generates public key corresponding to prv. KeyVariant of generated public key
// is same as PrivateKey. Fails only if prv was wrongly initialized.
func GeneratePublicKey(prv *PrivateKey) (*PublicKey, error) {
if prv == nil {
return nil, errors.New("sidh: invalid arguments")
}

if (prv.keyVariant & KeyVariant_SIDH_A) == KeyVariant_SIDH_A {
return publicKeyGenA(prv), nil
} else {
return publicKeyGenB(prv), nil
}
}

// Computes a shared secret which is a j-invariant. Function requires that pub has
// different KeyVariant than prv. Length of returned output is 2*ceil(log_2 P)/8),
// where P is a prime defining finite field.
//
// It's important to notice that each keypair must not be used more than once
// to calculate shared secret.
//
// Function may return error. This happens only in case provided input is invalid.
func DeriveSecret(prv *PrivateKey, pub *PublicKey) ([]byte, error) {

if (pub == nil) || (prv == nil) {
return nil, errors.New("sidh: invalid arguments")
}

if (pub.keyVariant == prv.keyVariant) || (pub.params.Id != prv.params.Id) {
return nil, errors.New("sidh: public and private are incompatbile")
}

if (prv.keyVariant & KeyVariant_SIDH_A) == KeyVariant_SIDH_A {
return deriveSecretA(prv, pub), nil
} else {
return deriveSecretB(prv, pub), nil
}
}

+ 68
- 0
sidh/params.go Прегледај датотеку

@@ -0,0 +1,68 @@
package sidh

import . "github.com/cloudflare/p751sidh/p751toolbox"

type DomainParams struct {
// P, Q and R=P-Q base points
Affine_P, Affine_Q, Affine_R ExtensionFieldElement
// Max size of secret key for x-torsion group
SecretBitLen uint
// MaskBytes
MaskBytes []byte
// Size of a compuatation strategy for x-torsion group
IsogenyStrategy []uint32
}

type SidhParams struct {
Id PrimeFieldId
// The secret key size, in bytes.
SecretKeySize int
// The public key size, in bytes.
PublicKeySize int
// The shared secret size, in bytes.
SharedSecretSize uint
// 2- and 3-torsion group parameter definitions
A, B DomainParams
// Sample rate to obtain a value in [0,3^238]
SampleRate uint
}

// Keeps mapping: SIDH prime field ID to domain parameters
var sidhParams = make(map[PrimeFieldId]SidhParams)

// Params returns domain parameters corresponding to finite field and identified by
// `id` provieded by the caller. Function panics in case `id` wasn't registered earlier.
func Params(id PrimeFieldId) *SidhParams {
if val, ok := sidhParams[id]; ok {
return &val
}
panic("sidh: SIDH Params ID unregistered")
}

func init() {
p751 := SidhParams{
Id: FP_751,
SecretKeySize: P751_SecretKeySize,
PublicKeySize: P751_PublicKeySize,
SharedSecretSize: P751_SharedSecretSize,
A: DomainParams{
Affine_P: P751_affine_PA,
Affine_Q: P751_affine_QA,
Affine_R: P751_affine_RA,
SecretBitLen: P751_SecretBitLenA,
MaskBytes: []byte{P751_MaskAliceByte1, P751_MaskAliceByte2, P751_MaskAliceByte3},
IsogenyStrategy: P751_AliceIsogenyStrategy[:],
},
B: DomainParams{
Affine_P: P751_affine_PB,
Affine_Q: P751_affine_QB,
Affine_R: P751_affine_RB,
SecretBitLen: P751_SecretBitLenB,
MaskBytes: []byte{P751_MaskBobByte},
IsogenyStrategy: P751_BobIsogenyStrategy[:],
},
SampleRate: P751_SampleRate,
}

sidhParams[FP_751] = p751
}

+ 384
- 0
sidh/sidh.go Прегледај датотеку

@@ -0,0 +1,384 @@
package sidh

import (
"errors"
"io"

// TODO: This is needed by ExtensionFieldElement struct, which itself
// depends on implementation of p751.
. "github.com/cloudflare/p751sidh/p751toolbox"
)

// Set result to zero if the input scalar is <= 3^238. scalar must be 48-byte array
// of bytes. This function is specific to P751.
//go:noescape
func checkLessThanThree238(scalar *byte, result *uint32)

// Multiply 48-byte scalar by 3 to get a scalar in 3*[0,3^238). This
// function is specific to P751.
//go:noescape
func multiplyByThree(scalar *byte)

// -----------------------------------------------------------------------------
// Functions for traversing isogeny trees acoording to strategy. Key type 'A' is
//

// Traverses isogeny tree in order to compute xR, xP, xQ and xQmP needed
// for public key generation.
func traverseTreePublicKeyA(curve *ProjectiveCurveParameters, xR, phiP, phiQ, phiR *ProjectivePoint, pub *PublicKey) {
var points = make([]ProjectivePoint, 0, 8)
var indices = make([]int, 0, 8)
var i, sidx int

cparam := curve.CalcCurveParamsEquiv4()
phi := NewIsogeny4()
strat := pub.params.A.IsogenyStrategy
stratSz := len(strat)

for j := 1; j <= stratSz; j++ {
for i <= stratSz-j {
points = append(points, *xR)
indices = append(indices, i)

k := strat[sidx]
sidx++
xR.Pow2k(&cparam, xR, 2*k)
i += int(k)
}

cparam = phi.GenerateCurve(xR)
for k := 0; k < len(points); k++ {
points[k] = phi.EvaluatePoint(&points[k])
}

*phiP = phi.EvaluatePoint(phiP)
*phiQ = phi.EvaluatePoint(phiQ)
*phiR = phi.EvaluatePoint(phiR)

// pop xR from points
*xR, points = points[len(points)-1], points[:len(points)-1]
i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
}
}

// Traverses isogeny tree in order to compute xR needed
// for public key generation.
func traverseTreeSharedKeyA(curve *ProjectiveCurveParameters, xR *ProjectivePoint, pub *PublicKey) {
var points = make([]ProjectivePoint, 0, 8)
var indices = make([]int, 0, 8)
var i, sidx int

cparam := curve.CalcCurveParamsEquiv4()
phi := NewIsogeny4()
strat := pub.params.A.IsogenyStrategy
stratSz := len(strat)

for j := 1; j <= stratSz; j++ {
for i <= stratSz-j {
points = append(points, *xR)
indices = append(indices, i)

k := strat[sidx]
sidx++
xR.Pow2k(&cparam, xR, 2*k)
i += int(k)
}

cparam = phi.GenerateCurve(xR)
for k := 0; k < len(points); k++ {
points[k] = phi.EvaluatePoint(&points[k])
}

// pop xR from points
*xR, points = points[len(points)-1], points[:len(points)-1]
i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
}
}

// Traverses isogeny tree in order to compute xR, xP, xQ and xQmP needed
// for public key generation.
func traverseTreePublicKeyB(curve *ProjectiveCurveParameters, xR, phiP, phiQ, phiR *ProjectivePoint, pub *PublicKey) {
var points = make([]ProjectivePoint, 0, 8)
var indices = make([]int, 0, 8)
var i, sidx int

cparam := curve.CalcCurveParamsEquiv3()
phi := NewIsogeny3()
strat := pub.params.B.IsogenyStrategy
stratSz := len(strat)

for j := 1; j <= stratSz; j++ {
for i <= stratSz-j {
points = append(points, *xR)
indices = append(indices, i)

k := strat[sidx]
sidx++
xR.Pow3k(&cparam, xR, k)
i += int(k)
}

cparam = phi.GenerateCurve(xR)
for k := 0; k < len(points); k++ {
points[k] = phi.EvaluatePoint(&points[k])
}

*phiP = phi.EvaluatePoint(phiP)
*phiQ = phi.EvaluatePoint(phiQ)
*phiR = phi.EvaluatePoint(phiR)

// pop xR from points
*xR, points = points[len(points)-1], points[:len(points)-1]
i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
}
}

// Traverses isogeny tree in order to compute xR, xP, xQ and xQmP needed
// for public key generation.
func traverseTreeSharedKeyB(curve *ProjectiveCurveParameters, xR *ProjectivePoint, pub *PublicKey) {
var points = make([]ProjectivePoint, 0, 8)
var indices = make([]int, 0, 8)
var i, sidx int

cparam := curve.CalcCurveParamsEquiv3()
phi := NewIsogeny3()
strat := pub.params.B.IsogenyStrategy
stratSz := len(strat)

for j := 1; j <= stratSz; j++ {
for i <= stratSz-j {
points = append(points, *xR)
indices = append(indices, i)

k := strat[sidx]
sidx++
xR.Pow3k(&cparam, xR, k)
i += int(k)
}

cparam = phi.GenerateCurve(xR)
for k := 0; k < len(points); k++ {
points[k] = phi.EvaluatePoint(&points[k])
}

// pop xR from points
*xR, points = points[len(points)-1], points[:len(points)-1]
i, indices = int(indices[len(indices)-1]), indices[:len(indices)-1]
}
}

// -----------------------------------------------------------------------------
// Key generation functions
//

// Generate a private key for "Alice". Note that because this library does not
// implement SIDH validation, each keypair must be used for at most one
// shared secret computation.
func (prv *PrivateKey) generatePrivateKeyA(rand io.Reader) error {
_, err := io.ReadFull(rand, prv.Scalar)
if err != nil {
return err
}

// Bit-twiddle to ensure scalar is in 2*[0,2^371):
prv.Scalar[prv.params.SecretKeySize-1] = prv.params.A.MaskBytes[0]
prv.Scalar[prv.params.SecretKeySize-2] &= prv.params.A.MaskBytes[1] // clear high bits, so scalar < 2^372
prv.Scalar[0] &= prv.params.A.MaskBytes[2] // clear low bit, so scalar is even

// We actually want scalar in 2*(0,2^371), but the above procedure
// generates 0 with probability 2^(-371), which isn't worth checking
// for.
return nil
}

// Generate a private key for "Bob". Note that because this library does not
// implement SIDH validation, each keypair must be used for at most one
// shared secret computation.
func (prv *PrivateKey) generatePrivateKeyB(rand io.Reader) error {
// Perform rejection sampling to obtain a random value in [0,3^238]:
var ok uint32
for i := uint(0); i < prv.params.SampleRate; i++ {
_, err := io.ReadFull(rand, prv.Scalar)
if err != nil {
return err
}
// Mask the high bits to obtain a uniform value in [0,2^378):
// TODO: simply run it in loop, if rand distribution is uniform you surelly get non 0
// if not - better die, keep looping, hang, whatever, but don't generate secure key
prv.Scalar[prv.params.SecretKeySize-1] &= prv.params.B.MaskBytes[0]

// Accept if scalar < 3^238 (this happens w/ prob ~0.5828)
// TODO this is specific to P751
checkLessThanThree238(&prv.Scalar[0], &ok)
if ok == 0 {
break
}
}
// ok is nonzero if all sampleRate trials failed.
// This happens with probability 0.41719...^102 < 2^(-128), i.e., never
if ok != 0 {
// In case this happens user should retry. In practice it is highly
// improbable (< 2^-128).
return errors.New("sidh: private key generation failed")
}

// Multiply by 3 to get a scalar in 3*[0,3^238):
multiplyByThree(&prv.Scalar[0])
// We actually want scalar in 2*(0,2^371), but the above procedure
// generates 0 with probability 2^(-371), which isn't worth checking
// for.
return nil
}

// Generate a public key in the 2-torsion group
func publicKeyGenA(prv *PrivateKey) (pub *PublicKey) {
var xPA, xQA, xRA ProjectivePoint
var xPB, xQB, xRB, xR ProjectivePoint
var invZP, invZQ, invZR ExtensionFieldElement
var tmp ProjectiveCurveParameters
var phi = NewIsogeny4()
pub = NewPublicKey(prv.params.Id, KeyVariant_SIDH_A)

// Load points for A
xPA.FromAffine(&prv.params.A.Affine_P)
xPA.Z.One()
xQA.FromAffine(&prv.params.A.Affine_Q)
xQA.Z.One()
xRA.FromAffine(&prv.params.A.Affine_R)
xRA.Z.One()

// Load points for B
xRB.FromAffine(&prv.params.B.Affine_R)
xRB.Z.One()
xQB.FromAffine(&prv.params.B.Affine_Q)
xQB.Z.One()
xPB.FromAffine(&prv.params.B.Affine_P)
xPB.Z.One()

// Find isogeny kernel
tmp.A.Zero()
tmp.C.One()
xR = RightToLeftLadder(&tmp, &xPA, &xQA, &xRA, prv.params.A.SecretBitLen, prv.Scalar)

// Reset params object and travers isogeny tree
tmp.A.Zero()
tmp.C.One()
traverseTreePublicKeyA(&tmp, &xR, &xPB, &xQB, &xRB, pub)

// Secret isogeny
phi.GenerateCurve(&xR)
xPA = phi.EvaluatePoint(&xPB)
xQA = phi.EvaluatePoint(&xQB)
xRA = phi.EvaluatePoint(&xRB)
ExtensionFieldBatch3Inv(&xPA.Z, &xQA.Z, &xRA.Z, &invZP, &invZQ, &invZR)

pub.affine_xP.Mul(&xPA.X, &invZP)
pub.affine_xQ.Mul(&xQA.X, &invZQ)
pub.affine_xQmP.Mul(&xRA.X, &invZR)
return
}

// Generate a public key in the 3-torsion group
func publicKeyGenB(prv *PrivateKey) (pub *PublicKey) {
var xPB, xQB, xRB, xR ProjectivePoint
var xPA, xQA, xRA ProjectivePoint
var invZP, invZQ, invZR ExtensionFieldElement
var tmp ProjectiveCurveParameters
var phi = NewIsogeny3()
pub = NewPublicKey(prv.params.Id, prv.keyVariant)

// Load points for B
xRB.FromAffine(&prv.params.B.Affine_R)
xRB.Z.One()
xQB.FromAffine(&prv.params.B.Affine_Q)
xQB.Z.One()
xPB.FromAffine(&prv.params.B.Affine_P)
xPB.Z.One()

// Load points for A
xPA.FromAffine(&prv.params.A.Affine_P)
xPA.Z.One()
xQA.FromAffine(&prv.params.A.Affine_Q)
xQA.Z.One()
xRA.FromAffine(&prv.params.A.Affine_R)
xRA.Z.One()

tmp.A.Zero()
tmp.C.One()
xR = RightToLeftLadder(&tmp, &xPB, &xQB, &xRB, prv.params.B.SecretBitLen, prv.Scalar)

tmp.A.Zero()
tmp.C.One()
traverseTreePublicKeyB(&tmp, &xR, &xPA, &xQA, &xRA, pub)

phi.GenerateCurve(&xR)
xPB = phi.EvaluatePoint(&xPA)
xQB = phi.EvaluatePoint(&xQA)
xRB = phi.EvaluatePoint(&xRA)
ExtensionFieldBatch3Inv(&xPB.Z, &xQB.Z, &xRB.Z, &invZP, &invZQ, &invZR)

pub.affine_xP.Mul(&xPB.X, &invZP)
pub.affine_xQ.Mul(&xQB.X, &invZQ)
pub.affine_xQmP.Mul(&xRB.X, &invZR)
return
}

// -----------------------------------------------------------------------------
// Key agreement functions
//

// Establishing shared keys in in 2-torsion group
func deriveSecretA(prv *PrivateKey, pub *PublicKey) []byte {
var sharedSecret = make([]byte, pub.params.SharedSecretSize)
var cparam ProjectiveCurveParameters
var xP, xQ, xQmP ProjectivePoint
var xR ProjectivePoint
var phi = NewIsogeny4()

// Recover curve coefficients
cparam.RecoverCoordinateA(&pub.affine_xP, &pub.affine_xQ, &pub.affine_xQmP)
cparam.C.One()

// Find kernel of the morphism
xP.FromAffine(&pub.affine_xP)
xQ.FromAffine(&pub.affine_xQ)
xQmP.FromAffine(&pub.affine_xQmP)
xR = RightToLeftLadder(&cparam, &xP, &xQ, &xQmP, pub.params.A.SecretBitLen, prv.Scalar)

// Traverse isogeny tree
traverseTreeSharedKeyA(&cparam, &xR, pub)

// Calculate j-invariant on isogeneus curve
c := phi.GenerateCurve(&xR)
cparam.RecoverCurveCoefficients4(&c)
cparam.Jinvariant(sharedSecret)
return sharedSecret
}

// Establishing shared keys in in 3-torsion group
func deriveSecretB(prv *PrivateKey, pub *PublicKey) []byte {
var sharedSecret = make([]byte, pub.params.SharedSecretSize)
var xP, xQ, xQmP ProjectivePoint
var xR ProjectivePoint
var cparam ProjectiveCurveParameters
var phi = NewIsogeny3()

// Recover curve coefficients
cparam.RecoverCoordinateA(&pub.affine_xP, &pub.affine_xQ, &pub.affine_xQmP)
cparam.C.One()

// Find kernel of the morphism
xP.FromAffine(&pub.affine_xP)
xQ.FromAffine(&pub.affine_xQ)
xQmP.FromAffine(&pub.affine_xQmP)
xR = RightToLeftLadder(&cparam, &xP, &xQ, &xQmP, pub.params.B.SecretBitLen, prv.Scalar)

// Traverse isogeny tree
traverseTreeSharedKeyB(&cparam, &xR, pub)

// Calculate j-invariant on isogeneus curve
c := phi.GenerateCurve(&xR)
cparam.RecoverCurveCoefficients3(&c)
cparam.Jinvariant(sharedSecret)
return sharedSecret
}

sidh_amd64.s → sidh/sidh_amd64.s Прегледај датотеку

@@ -8,6 +8,8 @@
#define THREE238M1_4 $0xb858a87e8f4222c7
#define THREE238M1_5 $0x254c9c6b525eaf5

// Set result to zero if the input scalar is <= 3^238. scalar must be 48-byte array
// of bytes.
TEXT ·checkLessThanThree238(SB), NOSPLIT, $0-16
MOVQ scalar+0(FP), SI
MOVQ result+8(FP), DI
@@ -36,9 +38,10 @@ TEXT ·checkLessThanThree238(SB), NOSPLIT, $0-16

RET

// Multiply 48-byte scalar by 3 to get a scalar in 3*[0,3^238)
TEXT ·multiplyByThree(SB), NOSPLIT, $0-8
MOVQ scalar+0(FP), SI
// Set [R10,...,R15] = scalar
MOVQ (SI), R10
MOVQ (8)(SI), R11

+ 331
- 0
sidh/sidh_test.go Прегледај датотеку

@@ -0,0 +1,331 @@
package sidh

import (
"bytes"
"crypto/rand"
"encoding/hex"
"testing"
)

const (
// PrA - Alice's Private Key: 2*randint(0,2^371)
PrA = "C09957CC83045FB4C3726384D784476ACB6FFD92E5B15B3C2D451BA063F1BD4CED8FBCF682A98DD0954D3" +
"7BCAF730E00"
// PrB - Bob's Private Key: 3*randint(0,3^238)
PrB = "393E8510E78A16D2DC1AACA9C9D17E7E78DB630881D8599C7040D05BB5557ECAE8165C45D5366ECB37B00" +
"969740AF201"
PkA = "74D8EF08CB74EC99BF08B6FBE4FB3D048873B67F018E44988B9D70C564D058401D20E093C7DF0C66F022C" +
"823E5139D2EA0EE137804B4820E950B046A90B0597759A0B6A197C56270128EA089FA1A2007DDE3430B37" +
"A3E6350BD47B7F513863741C125FA63DEDAFC475C13DB59E533055B7CBE4B2F32672DF2DF97E03E29617B" +
"0E9B6A35B58ABB26527A721142701EB147C7050E1D9125DA577B08CD51C8BB50627B8B47FACFC9C7C07DD" +
"00DD75115DD83719FD5F96115DED23ECAA50B1044C6BF3F27442DA284BA4A272D850F414FB185801BF2EF" +
"7E628EDB5643E35694B992CF30A2C5120CAF9434F09ACFCA3645B3FFC3A308901FAC7B8955FD5C98576AE" +
"FD03F5806CB7430F75B3431B75BEC080596ABCA26E637E6E8D4C25175A8C052C9CBE77900A863F83FAB00" +
"95B32D9C3858EF8A35B9F163D429E71DBA47539EB4791D117FE39DDE94EA7801A42DB12D84DE4740ACF51" +
"CD7C32BB854569D7D94E11E69D9663CC7ED02E78CF48F4069DF3D3E86198B307095C6B11D46C0DC849F9D" +
"94C7693209E5B3848AFAA6DA6A8D73362D779CBC43515902ED2BCE3A748C537DE2FCF092FD3E91B790AF5" +
"4E1092C5E5B89BE5BE23B955A52F769D97277EF69F820109042F28C316AC90AE69EB374C9280300B816E6" +
"2494B2E01072D1CA96E4B284D2BE1368D6969744B614FACBC8C165864E26E33481D4FDC47B6E523954A25" +
"C1A096A37CD23FB81AE64FB11BD0A439609F1CE40673B06DD96F698A910E935219D840F3D411EDFB00D98" +
"065AB9868C32D3DA05FF415"
PkB = "F6C260C4141E418457CB442E11F0F5558375437576E55D211D19EF83E2839E51D07A82765D8E7B6366FA7" +
"0B56CDE3AD3B629ACF542A433369496EDA51EDFBE16EFA1B8DEE1CE46B37820ECBD0CD674AACD4F21FABC" +
"2436651E3AF604356FF3EB2CA87976890E34A56FAEC9A2ACD9559B1BB67B69AC1A521342E1E787DA5D709" +
"32B0F5842ECA1C99B269DB6C2ED8397F0FC49F114CF8B5AF327A698C0251575CDD1D67732668109A91A3B" +
"FA5B47D413C7FAB8817FCBEBFE9BDD9C0B1F3B1934A7028A65233E8B58A92E7E9F66B68B2057ECBF7E44A" +
"0EF6EFCC3C8AA5414E100FA0C24F7545324AD17062FC11377A2A4749DEE27E192460E099DBDA8E840EA11" +
"AD9D5C83DF065AF77030E7FE18CE24CFC71D356B9B9601811B93676C12CB6B41747133D5259E7A20CC065" +
"FAB99DF944FDB34ABB9A374F9E9CC8F9C186BD2181DC2771F69C02629C3E4801A7E7C21F6F3CFF7D257E2" +
"257C88C015F0CC8DC0E7FB3373CF4ED6A786AB329E7F16895CA147AD91F6EAE1DFE38116580DF52381599" +
"E4246278CB1848FE4A56ABF98652E9E7C2E681551A3D78FA033D932087D8B6567D779A56B726B153033D7" +
"2231A1B5C16ED7DC4458308D6B64AF6723CC0F52C94E04C58FCA9739E890AA40CC05E22321F10129D2B59" +
"1F317102034C109A56D711591E5B44C717CFC9C9B9461894767CAFA42D2B394194B03999C2A9EF48868F3" +
"FB03D1A40F596613AF97F4ED7643A1C2D12692E959C6DEB8E72403ADC0E42204DBCE5056EEF0CC60B0C6E" +
"83B8B55AC01F6C85644EE49"
)

var params *SidhParams

// Use init() function to initialize params in order to avoid
// static initialization order fiasco.
func init() {
params = Params(FP_751)
}

// Fail if err !=nil. Display msg as an error message
func checkErr(t testing.TB, err error, msg string) {
if err != nil {
t.Error(msg)
}
}

// Converts string to private key
func convToPrv(s string, v KeyVariant) *PrivateKey {
key := NewPrivateKey(params.Id, v)
hex, e := hex.DecodeString(s)
if e != nil {
panic("non-hex number provided")
}
e = key.Import(hex)
if e != nil {
panic("Can't import private key")
}
return key
}

// Converts string to public key
func convToPub(s string, v KeyVariant) *PublicKey {
key := NewPublicKey(params.Id, v)
hex, e := hex.DecodeString(s)
if e != nil {
panic("non-hex number provided")
}
e = key.Import(hex)
if e != nil {
panic("Can't import public key")
}
return key
}

func testKeygen(s *SidhParams, t *testing.T) {
alicePrivate := convToPrv(PrA, KeyVariant_SIDH_A)
bobPrivate := convToPrv(PrB, KeyVariant_SIDH_B)
expPubA := convToPub(PkA, KeyVariant_SIDH_A)
expPubB := convToPub(PkB, KeyVariant_SIDH_B)

pubA, err := GeneratePublicKey(alicePrivate)
checkErr(t, err, "public key A generation failed")
pubB, err := GeneratePublicKey(bobPrivate)
checkErr(t, err, "public key B generation failed")

if !bytes.Equal(pubA.Export(), expPubA.Export()) {
t.Fatalf("unexpected value of public key A")
}
if !bytes.Equal(pubB.Export(), expPubB.Export()) {
t.Fatalf("unexpected value of public key B")
}
}

func testRoundtrip(s *SidhParams, t *testing.T) {
var err error

prvA := NewPrivateKey(params.Id, KeyVariant_SIDH_A)
prvB := NewPrivateKey(params.Id, KeyVariant_SIDH_B)

// Generate private keys
err = prvA.Generate(rand.Reader)
checkErr(t, err, "key generation failed")
err = prvB.Generate(rand.Reader)
checkErr(t, err, "key generation failed")

// Generate public keys
pubA, err := GeneratePublicKey(prvA)
checkErr(t, err, "")

pubB, err := GeneratePublicKey(prvB)
checkErr(t, err, "")

// Derive shared secret
s1, err := DeriveSecret(prvB, pubA)
checkErr(t, err, "")

s2, err := DeriveSecret(prvA, pubB)
checkErr(t, err, "")

if !bytes.Equal(s1[:], s2[:]) {
t.Fatalf("Tthe two shared keys: \n%X, \n%X do not match", s1, s2)
}
}

func testKeyAgreement(s *SidhParams, t testing.TB, pkA, pkB, prA, prB string) {
var e error

// KeyPairs
alicePublic := convToPub(pkA, KeyVariant_SIDH_A)
bobPublic := convToPub(pkB, KeyVariant_SIDH_B)
alicePrivate := convToPrv(prA, KeyVariant_SIDH_A)
bobPrivate := convToPrv(prB, KeyVariant_SIDH_B)

// Do actual test
s1, e := DeriveSecret(bobPrivate, alicePublic)
checkErr(t, e, "derivation s1")
s2, e := DeriveSecret(alicePrivate, bobPublic)
checkErr(t, e, "derivation s1")

if !bytes.Equal(s1[:], s2[:]) {
t.Fatalf("two shared keys: %d, %d do not match", s1, s2)
}

// Negative case
dec, e := hex.DecodeString(PkA)
if e != nil {
t.FailNow()
}
dec[0] = ^dec[0]
e = alicePublic.Import(dec)
if e != nil {
t.FailNow()
}

s1, e = DeriveSecret(bobPrivate, alicePublic)
checkErr(t, e, "derivation of s1 failed")
s2, e = DeriveSecret(alicePrivate, bobPublic)
checkErr(t, e, "derivation of s2 failed")

if bytes.Equal(s1[:], s2[:]) {
t.Fatalf("The two shared keys: %d, %d match", s1, s2)
}
}

func TestKeygenP751(t *testing.T) {
testKeygen(Params(FP_751), t)
}

func TestKeyAgreementP751(t *testing.T) {
testKeyAgreement(Params(FP_751), t, PkA, PkB, PrA, PrB)
}

func TestRoundtripP751(t *testing.T) {
testRoundtrip(Params(FP_751), t)
}

func TestKeyAgreementP751_AliceEvenNumber(t *testing.T) {
// even alice
prE := "C09957CC83045FB4C3726384D784476ACB6FFD92E5B15B3C2D451BA063F1BD4CED8FBCF682A98DD0954D37BCAF730F00"
pkE := "8A2DE6FD963C475F7829B689C8B8306FC0917A39EBBC35CA171546269A85698FEC0379E2E1A3C567BE1B8EF5639F81F304889737E6CC444DBED4579DB204DC8C7928F5CBB1ECDD682A1B5C48C0DAF34208C06BF201BE4E6063B1BFDC42413B0537F8E76BEE645C1A24118301BAB17EB8D6E0F283BCB16EFB833E4BB3463953C93165A0DDAC55B385059F27FF7228486D0A733812C81C792BE9EC3A16A5DB0EB099EEA76AC0E59612251A3AD19F7CC567DA2AEBD7733171F48E471D17648692355164E27B515D2A47D7BA34B3B48A047BE7C09C4ABEE2FCC9ACA7396C8A8C9E73E29533FC7369094DFA7988778E55E53F309922C6E233F8F9C7936C3D29CEA640406FCA06450AA1978FF39F227BF06B1E072F1763447C6F513B23CDF3B0EC0379070AEE5A02D9AD8E0EB023461D631F4A9643A4C79921334945F6B33DDFC11D9703BD06B047B4DA404AB12EFD2C3A49E5C42D10DA063352748B21DE41C32A5693FE1C0DCAB111F4990CD58BECADB1892EE7A7E99C9DB4DA4E69C96E57138B99038BC9B877ECE75914EFB98DD08B9E4A2DCCB948A8F7D2F26678A9952BA0EFAB1E9CF6E51B557480DEC2BA30DE0FE4AFE30A6B30765EE75EF64F678316D81C72755AD2CFA0B8C7706B07BFA52FBC3DB84EF9E79796C0089305B1E13C78660779E0FF2A13820CE141104F976B1678990F85B2D3D2B89CD5BC4DD52603A5D24D3EFEDA44BAA0F38CDB75A220AF45EAB70F2799875D435CE50FC6315EDD4BB7AA7260AFD7CD0561B69B4FA3A817904322661C3108DA24"
testKeyAgreement(Params(FP_751), t, pkE, PkB, prE, PrB)
}

func TestImportExport(t *testing.T) {
var err error
a := NewPublicKey(params.Id, KeyVariant_SIDH_A)
b := NewPublicKey(params.Id, KeyVariant_SIDH_B)

// Import keys
a_hex, err := hex.DecodeString(PkA)
checkErr(t, err, "invalid hex-number provided")

err = a.Import(a_hex)
checkErr(t, err, "import failed")

b_hex, err := hex.DecodeString(PkB)
checkErr(t, err, "invalid hex-number provided")

err = b.Import(b_hex)
checkErr(t, err, "import failed")

// Export and check if same
if !bytes.Equal(b.Export(), b_hex) || !bytes.Equal(a.Export(), a_hex) {
t.Fatalf("export/import failed")
}

if (len(b.Export()) != b.Size()) || (len(a.Export()) != a.Size()) {
t.Fatalf("wrong size of exported keys")
}
}

func TestMultiplyByThree(t *testing.T) {
// sage: repr((3^238 -1).digits(256))
var three238minus1 = [48]byte{
248, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207,
215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143,
126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2}
// sage: repr((3*(3^238 -1)).digits(256))
var threeTimesThree238minus1 = [48]byte{
232, 142, 138, 135, 159, 84, 104, 201, 62, 110, 199, 124, 63, 161, 177, 89, 169, 109,
135, 190, 110, 125, 134, 233, 132, 128, 116, 37, 203, 69, 80, 43, 86, 104, 198, 173,
123, 249, 9, 41, 225, 192, 113, 31, 84, 93, 254, 6}

multiplyByThree(&three238minus1[0])

for i := 0; i < 48; i++ {
if three238minus1[i] != threeTimesThree238minus1[i] {
t.Error("Digit", i, "error: found", three238minus1[i],
"expected", threeTimesThree238minus1[i])
}
}
}

func TestCheckLessThanThree238(t *testing.T) {
var three238minus1 = [48]byte{
248, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115,
56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99,
199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2}
var three238 = [48]byte{
249, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115,
56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199,
34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2}
var three238plus1 = [48]byte{250, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66,
212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12,
153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198,
201, 84, 2}

var result = uint32(57)

checkLessThanThree238(&three238minus1[0], &result)
if result != 0 {
t.Error("expected 0, got", result)
}
checkLessThanThree238(&three238[0], &result)
if result == 0 {
t.Error("expected nonzero, got", result)
}
checkLessThanThree238(&three238plus1[0], &result)
if result == 0 {
t.Error("expected nonzero, got", result)
}
}

func BenchmarkSidhKeyAgreement(b *testing.B) {
// KeyPairs
alicePublic := convToPub(PkA, KeyVariant_SIDH_A)
bobPublic := convToPub(PkB, KeyVariant_SIDH_B)
alicePrivate := convToPrv(PrA, KeyVariant_SIDH_A)
bobPrivate := convToPrv(PrB, KeyVariant_SIDH_B)

for i := 0; i < b.N; i++ {
// Derive shared secret
DeriveSecret(bobPrivate, alicePublic)
DeriveSecret(alicePrivate, bobPublic)
}
}

func BenchmarkAliceKeyGenPrv(b *testing.B) {
prv := NewPrivateKey(params.Id, KeyVariant_SIDH_A)
for n := 0; n < b.N; n++ {
prv.Generate(rand.Reader)
}
}

func BenchmarkAliceKeyGenPub(b *testing.B) {
prv := NewPrivateKey(params.Id, KeyVariant_SIDH_A)
prv.Generate(rand.Reader)
for n := 0; n < b.N; n++ {
GeneratePublicKey(prv)
}
}

func BenchmarkBobKeyGenPub(b *testing.B) {
prv := NewPrivateKey(params.Id, KeyVariant_SIDH_B)
prv.Generate(rand.Reader)
for n := 0; n < b.N; n++ {
GeneratePublicKey(prv)
}
}

func BenchmarkSharedSecretAlice(b *testing.B) {
aPr := convToPrv(PrA, KeyVariant_SIDH_A)
bPk := convToPub(PkB, KeyVariant_SIDH_B)
for n := 0; n < b.N; n++ {
DeriveSecret(aPr, bPk)
}
}

func BenchmarkSharedSecretBob(b *testing.B) {
// m_B = 3*randint(0,3^238)
aPk := convToPub(PkA, KeyVariant_SIDH_A)
bPr := convToPrv(PrB, KeyVariant_SIDH_B)
for n := 0; n < b.N; n++ {
DeriveSecret(bPr, aPk)
}
}

+ 0
- 406
sidh_test.go Прегледај датотеку

@@ -1,406 +0,0 @@
package p751sidh

import (
"bytes"
"crypto/rand"
mathRand "math/rand"
"reflect"
"testing"
"testing/quick"
)

import . "github.com/cloudflare/p751sidh/p751toolbox"

func TestMultiplyByThree(t *testing.T) {
// sage: repr((3^238 -1).digits(256))
var three238minus1 = [48]byte{248, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2}
// sage: repr((3*(3^238 -1)).digits(256))
var threeTimesThree238minus1 = [48]byte{232, 142, 138, 135, 159, 84, 104, 201, 62, 110, 199, 124, 63, 161, 177, 89, 169, 109, 135, 190, 110, 125, 134, 233, 132, 128, 116, 37, 203, 69, 80, 43, 86, 104, 198, 173, 123, 249, 9, 41, 225, 192, 113, 31, 84, 93, 254, 6}

multiplyByThree(&three238minus1)

for i := 0; i < 48; i++ {
if three238minus1[i] != threeTimesThree238minus1[i] {
t.Error("Digit", i, "error: found", three238minus1[i], "expected", threeTimesThree238minus1[i])
}
}
}

func TestCheckLessThanThree238(t *testing.T) {
var three238minus1 = [48]byte{248, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2}
var three238 = [48]byte{249, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2}
var three238plus1 = [48]byte{250, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2}

var result = uint32(57)

checkLessThanThree238(&three238minus1, &result)
if result != 0 {
t.Error("Expected 0, got", result)
}
checkLessThanThree238(&three238, &result)
if result == 0 {
t.Error("Expected nonzero, got", result)
}
checkLessThanThree238(&three238plus1, &result)
if result == 0 {
t.Error("Expected nonzero, got", result)
}
}

// This throws away the generated public key, forcing us to recompute it in the test,
// but generating the value *in* the quickcheck predicate breaks the testing.
func (x SIDHSecretKeyAlice) Generate(quickCheckRand *mathRand.Rand, size int) reflect.Value {
// use crypto/rand instead of the quickCheck-provided RNG
_, aliceSecret, err := GenerateAliceKeypair(rand.Reader)
if err != nil {
panic("error generating secret key")
}
return reflect.ValueOf(*aliceSecret)
}

func (x SIDHSecretKeyBob) Generate(quickCheckRand *mathRand.Rand, size int) reflect.Value {
// use crypto/rand instead of the quickCheck-provided RNG
_, bobSecret, err := GenerateBobKeypair(rand.Reader)
if err != nil {
panic("error generating secret key")
}
return reflect.ValueOf(*bobSecret)
}

func TestEphemeralSharedSecret(t *testing.T) {
sharedSecretsMatch := func(aliceSecret SIDHSecretKeyAlice, bobSecret SIDHSecretKeyBob) bool {
alicePublic := aliceSecret.PublicKey()
bobPublic := bobSecret.PublicKey()

aliceSharedSecret := aliceSecret.SharedSecret(&bobPublic)
bobSharedSecret := bobSecret.SharedSecret(&alicePublic)

return bytes.Equal(aliceSharedSecret[:], bobSharedSecret[:])
}

if err := quick.Check(sharedSecretsMatch, nil); err != nil {
t.Error(err)
}
}

// Perform Alice's (2-isogeny) key generation, using the slow but simple multiplication-based strategy.
//
// This function just exists to ensure that the fast isogeny-tree strategy works correctly.
func aliceKeyGenSlow(secretKey *SIDHSecretKeyAlice) SIDHPublicKeyAlice {
var xP, xQ, xQmP, xR, xS ProjectivePoint

xP.FromAffinePrimeField(&Affine_xPB) // = ( x_P : 1) = x(P_B)
xQ.FromAffinePrimeField(&Affine_xPB) //
xQ.X.Neg(&xQ.X) // = (-x_P : 1) = x(Q_B)
xQmP = DistortAndDifference(&Affine_xPB) // = x(Q_B - P_B)

xR = SecretPoint(&Affine_xPA, &Affine_yPA, secretKey.Scalar[:])

var currentCurve ProjectiveCurveParameters
// Starting curve has a = 0, so (A:C) = (0,1)
currentCurve.A.Zero()
currentCurve.C.One()

var firstPhi FirstFourIsogeny
currentCurve, firstPhi = ComputeFirstFourIsogeny(&currentCurve)

xP = firstPhi.Eval(&xP)
xQ = firstPhi.Eval(&xQ)
xQmP = firstPhi.Eval(&xQmP)
xR = firstPhi.Eval(&xR)

var phi FourIsogeny
for e := (372 - 4); e >= 0; e -= 2 {
xS.Pow2k(&currentCurve, &xR, uint32(e))
currentCurve, phi = ComputeFourIsogeny(&xS)
xR = phi.Eval(&xR)
xP = phi.Eval(&xP)
xQ = phi.Eval(&xQ)
xQmP = phi.Eval(&xQmP)
}

var invZP, invZQ, invZQmP ExtensionFieldElement
ExtensionFieldBatch3Inv(&xP.Z, &xQ.Z, &xQmP.Z, &invZP, &invZQ, &invZQmP)

var publicKey SIDHPublicKeyAlice
publicKey.affine_xP.Mul(&xP.X, &invZP)
publicKey.affine_xQ.Mul(&xQ.X, &invZQ)
publicKey.affine_xQmP.Mul(&xQmP.X, &invZQmP)

return publicKey
}

// Perform Bob's (3-isogeny) key generation, using the slow but simple multiplication-based strategy.
//
// This function just exists to ensure that the fast isogeny-tree strategy works correctly.
func bobKeyGenSlow(secretKey *SIDHSecretKeyBob) SIDHPublicKeyBob {
var xP, xQ, xQmP, xR, xS ProjectivePoint

xP.FromAffinePrimeField(&Affine_xPA) // = ( x_P : 1) = x(P_A)
xQ.FromAffinePrimeField(&Affine_xPA) //
xQ.X.Neg(&xQ.X) // = (-x_P : 1) = x(Q_A)
xQmP = DistortAndDifference(&Affine_xPA) // = x(Q_B - P_B)

xR = SecretPoint(&Affine_xPB, &Affine_yPB, secretKey.Scalar[:])

var currentCurve ProjectiveCurveParameters
// Starting curve has a = 0, so (A:C) = (0,1)
currentCurve.A.Zero()
currentCurve.C.One()

var phi ThreeIsogeny
for e := 238; e >= 0; e-- {
xS.Pow3k(&currentCurve, &xR, uint32(e))
currentCurve, phi = ComputeThreeIsogeny(&xS)
xR = phi.Eval(&xR)
xP = phi.Eval(&xP)
xQ = phi.Eval(&xQ)
xQmP = phi.Eval(&xQmP)
}

var invZP, invZQ, invZQmP ExtensionFieldElement
ExtensionFieldBatch3Inv(&xP.Z, &xQ.Z, &xQmP.Z, &invZP, &invZQ, &invZQmP)

var publicKey SIDHPublicKeyBob
publicKey.affine_xP.Mul(&xP.X, &invZP)
publicKey.affine_xQ.Mul(&xQ.X, &invZQ)
publicKey.affine_xQmP.Mul(&xQmP.X, &invZQmP)

return publicKey
}

// Perform Alice's key agreement, using the slow but simple multiplication-based strategy.
//
// This function just exists to ensure that the fast isogeny-tree strategy works correctly.
func aliceSharedSecretSlow(bobPublic *SIDHPublicKeyBob, aliceSecret *SIDHSecretKeyAlice) [188]byte {
var currentCurve = RecoverCurveParameters(&bobPublic.affine_xP, &bobPublic.affine_xQ, &bobPublic.affine_xQmP)

var xR, xS, xP, xQ, xQmP ProjectivePoint

xP.FromAffine(&bobPublic.affine_xP)
xQ.FromAffine(&bobPublic.affine_xQ)
xQmP.FromAffine(&bobPublic.affine_xQmP)

xR.ThreePointLadder(&currentCurve, &xP, &xQ, &xQmP, aliceSecret.Scalar[:])

var firstPhi FirstFourIsogeny
currentCurve, firstPhi = ComputeFirstFourIsogeny(&currentCurve)
xR = firstPhi.Eval(&xR)

var phi FourIsogeny
for e := (372 - 4); e >= 2; e -= 2 {
xS.Pow2k(&currentCurve, &xR, uint32(e))
currentCurve, phi = ComputeFourIsogeny(&xS)
xR = phi.Eval(&xR)
}

currentCurve, _ = ComputeFourIsogeny(&xR)

var sharedSecret [SharedSecretSize]byte
var jInv = currentCurve.JInvariant()
jInv.ToBytes(sharedSecret[:])
return sharedSecret
}

// Perform Bob's key agreement, using the slow but simple multiplication-based strategy.
//
// This function just exists to ensure that the fast isogeny-tree strategy works correctly.
func bobSharedSecretSlow(alicePublic *SIDHPublicKeyAlice, bobSecret *SIDHSecretKeyBob) [188]byte {
var currentCurve = RecoverCurveParameters(&alicePublic.affine_xP, &alicePublic.affine_xQ, &alicePublic.affine_xQmP)

var xR, xS, xP, xQ, xQmP ProjectivePoint

xP.FromAffine(&alicePublic.affine_xP)
xQ.FromAffine(&alicePublic.affine_xQ)
xQmP.FromAffine(&alicePublic.affine_xQmP)

xR.ThreePointLadder(&currentCurve, &xP, &xQ, &xQmP, bobSecret.Scalar[:])

var phi ThreeIsogeny
for e := 238; e >= 1; e-- {
xS.Pow3k(&currentCurve, &xR, uint32(e))
currentCurve, phi = ComputeThreeIsogeny(&xS)
xR = phi.Eval(&xR)
}

currentCurve, _ = ComputeThreeIsogeny(&xR)

var sharedSecret [SharedSecretSize]byte
var jInv = currentCurve.JInvariant()
jInv.ToBytes(sharedSecret[:])
return sharedSecret
}

func TestBobKeyGenFastVsSlow(t *testing.T) {
// m_B = 3*randint(0,3^238)
var m_B = [48]uint8{246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0}

var bobSecretKey = SIDHSecretKeyBob{Scalar: m_B}
var fastPubKey = bobSecretKey.PublicKey()
var slowPubKey = bobKeyGenSlow(&bobSecretKey)

if !fastPubKey.affine_xP.VartimeEq(&slowPubKey.affine_xP) {
t.Error("Expected affine_xP = ", fastPubKey.affine_xP, "found", slowPubKey.affine_xP)
}
if !fastPubKey.affine_xQ.VartimeEq(&slowPubKey.affine_xQ) {
t.Error("Expected affine_xQ = ", fastPubKey.affine_xQ, "found", slowPubKey.affine_xQ)
}
if !fastPubKey.affine_xQmP.VartimeEq(&slowPubKey.affine_xQmP) {
t.Error("Expected affine_xQmP = ", fastPubKey.affine_xQmP, "found", slowPubKey.affine_xQmP)
}
}

func TestAliceKeyGenFastVsSlow(t *testing.T) {
// m_A = 2*randint(0,2^371)
var m_A = [48]uint8{248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0}

var aliceSecretKey = SIDHSecretKeyAlice{Scalar: m_A}
var fastPubKey = aliceSecretKey.PublicKey()
var slowPubKey = aliceKeyGenSlow(&aliceSecretKey)

if !fastPubKey.affine_xP.VartimeEq(&slowPubKey.affine_xP) {
t.Error("Expected affine_xP = ", fastPubKey.affine_xP, "found", slowPubKey.affine_xP)
}
if !fastPubKey.affine_xQ.VartimeEq(&slowPubKey.affine_xQ) {
t.Error("Expected affine_xQ = ", fastPubKey.affine_xQ, "found", slowPubKey.affine_xQ)
}
if !fastPubKey.affine_xQmP.VartimeEq(&slowPubKey.affine_xQmP) {
t.Error("Expected affine_xQmP = ", fastPubKey.affine_xQmP, "found", slowPubKey.affine_xQmP)
}
}

func TestSharedSecret(t *testing.T) {
// m_A = 2*randint(0,2^371)
var m_A = [48]uint8{248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0}
// m_B = 3*randint(0,3^238)
var m_B = [48]uint8{246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0}

var aliceSecret = SIDHSecretKeyAlice{Scalar: m_A}
var bobSecret = SIDHSecretKeyBob{Scalar: m_B}

var alicePublic = aliceSecret.PublicKey()
var bobPublic = bobSecret.PublicKey()

var aliceSharedSecretSlow = aliceSharedSecretSlow(&bobPublic, &aliceSecret)
var aliceSharedSecretFast = aliceSecret.SharedSecret(&bobPublic)
var bobSharedSecretSlow = bobSharedSecretSlow(&alicePublic, &bobSecret)
var bobSharedSecretFast = bobSecret.SharedSecret(&alicePublic)

if !bytes.Equal(aliceSharedSecretFast[:], aliceSharedSecretSlow[:]) {
t.Error("Shared secret (fast) mismatch: Alice has ", aliceSharedSecretFast, " Bob has ", bobSharedSecretFast)
}
if !bytes.Equal(aliceSharedSecretSlow[:], bobSharedSecretSlow[:]) {
t.Error("Shared secret (slow) mismatch: Alice has ", aliceSharedSecretSlow, " Bob has ", bobSharedSecretSlow)
}
if !bytes.Equal(aliceSharedSecretSlow[:], bobSharedSecretFast[:]) {
t.Error("Shared secret mismatch: Alice (slow) has ", aliceSharedSecretSlow, " Bob (fast) has ", bobSharedSecretFast)
}
}

func TestSecretPoint(t *testing.T) {
// m_A = 2*randint(0,2^371)
var m_A = [48]uint8{248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0}
// m_B = 3*randint(0,3^238)
var m_B = [48]uint8{246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0}

var xR_A = SecretPoint(&Affine_xPA, &Affine_yPA, m_A[:])
var xR_B = SecretPoint(&Affine_xPB, &Affine_yPB, m_B[:])

var sageAffine_xR_A = ExtensionFieldElement{A: Fp751Element{0x29f1dff12103d089, 0x7409b9bf955e0d87, 0xe812441c1cca7288, 0xc32b8b13efba55f9, 0xc3b76a80696d83da, 0x185dd4f93a3dc373, 0xfc07c1a9115b6717, 0x39bfcdd63b5c4254, 0xc4d097d51d41efd8, 0x4f893494389b21c7, 0x373433211d3d0446, 0x53c35ccc3d22}, B: Fp751Element{0x722e718f33e40815, 0x8c5fc0fdf715667, 0x850fd292bbe8c74c, 0x212938a60fcbf5d3, 0xfdb2a099d58dc6e7, 0x232f83ab63c9c205, 0x23eda62fa5543f5e, 0x49b5758855d9d04f, 0x6b455e6642ef25d1, 0x9651162537470202, 0xfeced582f2e96ff0, 0x33a9e0c0dea8}}
var sageAffine_xR_B = ExtensionFieldElement{A: Fp751Element{0xdd4e66076e8499f5, 0xe7efddc6907519da, 0xe31f9955b337108c, 0x8e558c5479ffc5e1, 0xfee963ead776bfc2, 0x33aa04c35846bf15, 0xab77d91b23617a0d, 0xbdd70948746070e2, 0x66f71291c277e942, 0x187c39db2f901fce, 0x69262987d5d32aa2, 0xe1db40057dc}, B: Fp751Element{0xd1b766abcfd5c167, 0x4591059dc8a382fa, 0x1ddf9490736c223d, 0xc96db091bdf2b3dd, 0x7b8b9c3dc292f502, 0xe5b18ad85e4d3e33, 0xc3f3479b6664b931, 0xa4f17865299e21e6, 0x3f7ef5b332fa1c6e, 0x875bedb5dab06119, 0x9b5a06ea2e23b93, 0x43d48296fb26}}

var affine_xR_A = xR_A.ToAffine()
if !sageAffine_xR_A.VartimeEq(affine_xR_A) {
t.Error("Expected \n", sageAffine_xR_A, "\nfound\n", affine_xR_A)
}

var affine_xR_B = xR_B.ToAffine()
if !sageAffine_xR_B.VartimeEq(affine_xR_B) {
t.Error("Expected \n", sageAffine_xR_B, "\nfound\n", affine_xR_B)
}
}

var keygenBenchPubKeyAlice SIDHPublicKeyAlice
var keygenBenchPubKeyBob SIDHPublicKeyBob

func BenchmarkAliceKeyGen(b *testing.B) {
for n := 0; n < b.N; n++ {
GenerateAliceKeypair(rand.Reader)
}
}

func BenchmarkAliceKeyGenSlow(b *testing.B) {
// m_A = 2*randint(0,2^371)
var m_A = [48]uint8{248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0}

var aliceSecretKey = SIDHSecretKeyAlice{Scalar: m_A}

for n := 0; n < b.N; n++ {
keygenBenchPubKeyAlice = aliceKeyGenSlow(&aliceSecretKey)
}
}

func BenchmarkBobKeyGen(b *testing.B) {
for n := 0; n < b.N; n++ {
GenerateBobKeypair(rand.Reader)
}
}

func BenchmarkBobKeyGenSlow(b *testing.B) {
// m_B = 3*randint(0,3^238)
var m_B = [48]uint8{246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0}

var bobSecretKey = SIDHSecretKeyBob{Scalar: m_B}

for n := 0; n < b.N; n++ {
keygenBenchPubKeyBob = bobKeyGenSlow(&bobSecretKey)
}
}

var benchSharedSecretAlicePublic = SIDHPublicKeyAlice{affine_xP: ExtensionFieldElement{A: Fp751Element{0xea6b2d1e2aebb250, 0x35d0b205dc4f6386, 0xb198e93cb1830b8d, 0x3b5b456b496ddcc6, 0x5be3f0d41132c260, 0xce5f188807516a00, 0x54f3e7469ea8866d, 0x33809ef47f36286, 0x6fa45f83eabe1edb, 0x1b3391ae5d19fd86, 0x1e66daf48584af3f, 0xb430c14aaa87}, B: Fp751Element{0x97b41ebc61dcb2ad, 0x80ead31cb932f641, 0x40a940099948b642, 0x2a22fd16cdc7fe84, 0xaabf35b17579667f, 0x76c1d0139feb4032, 0x71467e1e7b1949be, 0x678ca8dadd0d6d81, 0x14445daea9064c66, 0x92d161eab4fa4691, 0x8dfbb01b6b238d36, 0x2e3718434e4e}}, affine_xQ: ExtensionFieldElement{A: Fp751Element{0xb055cf0ca1943439, 0xa9ff5de2fa6c69ed, 0x4f2761f934e5730a, 0x61a1dcaa1f94aa4b, 0xce3c8fadfd058543, 0xeac432aaa6701b8e, 0x8491d523093aea8b, 0xba273f9bd92b9b7f, 0xd8f59fd34439bb5a, 0xdc0350261c1fe600, 0x99375ab1eb151311, 0x14d175bbdbc5}, B: Fp751Element{0xffb0ef8c2111a107, 0x55ceca3825991829, 0xdbf8a1ccc075d34b, 0xb8e9187bd85d8494, 0x670aa2d5c34a03b0, 0xef9fe2ed2b064953, 0xc911f5311d645aee, 0xf4411f409e410507, 0x934a0a852d03e1a8, 0xe6274e67ae1ad544, 0x9f4bc563c69a87bc, 0x6f316019681e}}, affine_xQmP: ExtensionFieldElement{A: Fp751Element{0x6ffb44306a153779, 0xc0ffef21f2f918f3, 0x196c46d35d77f778, 0x4a73f80452edcfe6, 0x9b00836bce61c67f, 0x387879418d84219e, 0x20700cf9fc1ec5d1, 0x1dfe2356ec64155e, 0xf8b9e33038256b1c, 0xd2aaf2e14bada0f0, 0xb33b226e79a4e313, 0x6be576fad4e5}, B: Fp751Element{0x7db5dbc88e00de34, 0x75cc8cb9f8b6e11e, 0x8c8001c04ebc52ac, 0x67ef6c981a0b5a94, 0xc3654fbe73230738, 0xc6a46ee82983ceca, 0xed1aa61a27ef49f0, 0x17fe5a13b0858fe0, 0x9ae0ca945a4c6b3c, 0x234104a218ad8878, 0xa619627166104394, 0x556a01ff2e7e}}}

var benchSharedSecretBobPublic = SIDHPublicKeyBob{affine_xP: ExtensionFieldElement{A: Fp751Element{0x6e1b8b250595b5fb, 0x800787f5197d963b, 0x6f4a4e314162a8a4, 0xe75cba4d37c02128, 0x2212e7579817a216, 0xd8a5fdb0ab2f843c, 0x44230c9f998cfd6c, 0x311ff789b26aa292, 0x73d05c379ff53e40, 0xddd8f5a223bad56c, 0x94b611e6e931c8b5, 0x4d6b9bfe3555}, B: Fp751Element{0x1a3686cfc8381294, 0x57f089b14f639cc4, 0xdb6a1565f2f5cabe, 0x83d67e8f6a02f215, 0x1946272593815e87, 0x2d839631785ca74c, 0xf149dcb2dee2bee, 0x705acd79efe405bf, 0xae3769b67687fbed, 0xacd5e29f2c203cb0, 0xdd91f08fa3153e08, 0x5a9ad8cb7400}}, affine_xQ: ExtensionFieldElement{A: Fp751Element{0xd30ed48b8c0d0c4a, 0x949cad95959ec462, 0x188675581e9d1f2a, 0xf57ed3233d33031c, 0x564c6532f7283ce7, 0x80cbef8ee3b66ecb, 0x5c687359315f22ce, 0x1da950f8671fac50, 0x6fa6c045f513ef6, 0x25ffc65a8da12d4a, 0x8b0f4ac0f5244f23, 0xadcb0e07fd92}, B: Fp751Element{0x37a43cd933ebfec4, 0x2a2806ef28dacf84, 0xd671fe718611b71e, 0xef7d73f01a676326, 0x99db1524e5799cf2, 0x860271dfbf67ff62, 0xedc2a0a14114bcf, 0x6c7b9b14b1264e5a, 0xf52de61707dc38b4, 0xccddb13fcc691f5a, 0x80f37a1220163920, 0x6a9175b9d5a1}}, affine_xQmP: ExtensionFieldElement{A: Fp751Element{0xf08af9e695c626da, 0x7a4b4d52b54e1b38, 0x980272cd4c8b8c10, 0x1afcb6151d113176, 0xaef7dbd877c00f0c, 0xe8a5ea89078700c3, 0x520c1901aa8323fa, 0xfba049c947f3383a, 0x1c38abcab48be9af, 0x9f1212b923481ea, 0x1522da3457a7c293, 0xb746f78e3a61}, B: Fp751Element{0x48010d0b48491128, 0x6d1c5c509f99f450, 0xaa3522330e3a8a62, 0x872aaf46193b2bb2, 0xc89260a2d8508973, 0x98bbbebf5524be83, 0x35711d01d895c217, 0x5e44e09ec506ed7, 0xac653a760ef6fd58, 0x5837954e30ad688d, 0xcbd3e9a1b5661da8, 0x15547f5d091a}}}

func BenchmarkSharedSecretAlice(b *testing.B) {
// m_A = 2*randint(0,2^371)
var m_A = [48]uint8{248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0}

var aliceSecret = SIDHSecretKeyAlice{Scalar: m_A}

for n := 0; n < b.N; n++ {
aliceSecret.SharedSecret(&benchSharedSecretBobPublic)
}
}

func BenchmarkSharedSecretAliceSlow(b *testing.B) {
// m_A = 2*randint(0,2^371)
var m_A = [48]uint8{248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0}

var aliceSecret = SIDHSecretKeyAlice{Scalar: m_A}

for n := 0; n < b.N; n++ {
aliceSharedSecretSlow(&benchSharedSecretBobPublic, &aliceSecret)
}
}

func BenchmarkSharedSecretBob(b *testing.B) {
// m_B = 3*randint(0,3^238)
var m_B = [48]uint8{246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0}

var bobSecret = SIDHSecretKeyBob{Scalar: m_B}

for n := 0; n < b.N; n++ {
bobSecret.SharedSecret(&benchSharedSecretAlicePublic)
}
}

func BenchmarkSharedSecretBobSlow(b *testing.B) {
// m_B = 3*randint(0,3^238)
var m_B = [48]uint8{246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0}

var bobSecret = SIDHSecretKeyBob{Scalar: m_B}

for n := 0; n < b.N; n++ {
bobSharedSecretSlow(&benchSharedSecretAlicePublic, &bobSecret)
}
}

Loading…
Откажи
Сачувај