* 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/exporttrials/prep_p503_trial3
@@ -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 |
@@ -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)) |
@@ -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} |
@@ -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, ¶ms.C4) // t3 = (4C)*BB | |||
x2P.Z.Mul(&t2, ¶ms.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(¶ms.C, &t0) // Z2p = C24 * t0 | |||
x.Mul(z, &t1) // X2p = Z2p * t1 | |||
t1.Sub(&t1, &t0) // t1 = t1 - t0 | |||
t0.Mul(¶ms.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, ¶ms.A) // t5 = t3 * A24+ | |||
t3.Mul(&t3, &t5) // t3 = t5 * t3 | |||
t6.Mul(&t2, ¶ms.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 | |||
} |
@@ -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(¶ms, &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(¶ms, &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(¶ms, &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(¶ms, &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[:]) | |||
} | |||
} |
@@ -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 | |||
} |
@@ -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(¶ms) | |||
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()) | |||
} | |||
} |
@@ -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") | |||
} |
@@ -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(¤tCurve) | |||
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(¤tCurve, &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(¤tCurve, &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(¤tCurve, &xP, &xQ, &xQmP, aliceSecret.Scalar[:]) | |||
var firstPhi FirstFourIsogeny | |||
currentCurve, firstPhi = ComputeFirstFourIsogeny(¤tCurve) | |||
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(¤tCurve, &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(¤tCurve, &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(¤tCurve, &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 | |||
} |
@@ -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 | |||
} | |||
} |
@@ -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 | |||
} |
@@ -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 | |||
} |
@@ -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 |
@@ -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) | |||
} | |||
} |
@@ -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(¤tCurve) | |||
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(¤tCurve, &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(¤tCurve, &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(¤tCurve, &xP, &xQ, &xQmP, aliceSecret.Scalar[:]) | |||
var firstPhi FirstFourIsogeny | |||
currentCurve, firstPhi = ComputeFirstFourIsogeny(¤tCurve) | |||
xR = firstPhi.Eval(&xR) | |||
var phi FourIsogeny | |||
for e := (372 - 4); e >= 2; e -= 2 { | |||
xS.Pow2k(¤tCurve, &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(¤tCurve, &xP, &xQ, &xQmP, bobSecret.Scalar[:]) | |||
var phi ThreeIsogeny | |||
for e := 238; e >= 1; e-- { | |||
xS.Pow3k(¤tCurve, &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) | |||
} | |||
} |