Private key generation can take advantage of the fact that keyspace for secret key is (0, 2^x - 1), for some possitivite value of 'x' (see SIKE, 1.3.8). It means that all bytes in the secret key, but the last one, can take any value between <0x00,0xFF>. Similarily for the last byte, but generation needs to chop off some bits, to make sure generated value is an element of a key-space. Assuming uniform distribution of bytes generated by RNG, secret key is still chosen uniformly at random, but there is no need to maintain field specific assembly code.trials/PERF
@@ -25,20 +25,18 @@ type Fp2Element struct { | |||
type DomainParams struct { | |||
// P, Q and R=P-Q base points | |||
Affine_P, Affine_Q, Affine_R Fp2Element | |||
// 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 | |||
// Max size of secret key for x-torsion group | |||
SecretBitLen uint | |||
// Max size of secret key for x-torsion group | |||
SecretByteLen uint | |||
} | |||
type SidhParams struct { | |||
Id PrimeFieldId | |||
// Bytelen of P | |||
Bytelen int | |||
// The secret key size, in bytes. | |||
SecretKeySize int | |||
// The public key size, in bytes. | |||
PublicKeySize int | |||
// The shared secret size, in bytes. | |||
@@ -15,12 +15,6 @@ const ( | |||
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 | |||
// P751 bytelen ceil(751/8) | |||
@@ -2,8 +2,8 @@ package sidh | |||
import ( | |||
"errors" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
"io" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
) | |||
// Id's correspond to bitlength of the prime field characteristic | |||
@@ -67,7 +67,11 @@ func (key *key) Variant() KeyVariant { | |||
// 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) | |||
if (v & KeyVariant_SIDH_A) == KeyVariant_SIDH_A { | |||
prv.Scalar = make([]byte, prv.params.A.SecretByteLen) | |||
} else { | |||
prv.Scalar = make([]byte, prv.params.B.SecretByteLen) | |||
} | |||
if v == KeyVariant_SIKE { | |||
prv.S = make([]byte, prv.params.MsgLen) | |||
} | |||
@@ -123,7 +127,7 @@ func (prv *PrivateKey) Export() []byte { | |||
// Size returns size of the private key in bytes | |||
func (prv *PrivateKey) Size() int { | |||
tmp := prv.params.SecretKeySize | |||
tmp := len(prv.Scalar) | |||
if prv.Variant() == KeyVariant_SIKE { | |||
tmp += int(prv.params.MsgLen) | |||
} | |||
@@ -138,29 +142,47 @@ 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.S, input[:len(prv.S)]) | |||
copy(prv.Scalar, input[len(prv.S):]) | |||
return nil | |||
} | |||
// Generates random private key for SIDH or SIKE. Returns error | |||
// in case user provided RNG or memory initialization fails. | |||
// Generates random private key for SIDH or SIKE. Generated value is | |||
// formed as little-endian integer from key-space <2^(e2-1)..2^e2 - 1> | |||
// for KeyVariant_A or <2^(s-1)..2^s - 1>, where s = floor(log_2(3^e3)), | |||
// for KeyVariant_B. | |||
// | |||
// Returns error in case user provided RNG fails. | |||
func (prv *PrivateKey) Generate(rand io.Reader) error { | |||
var err error | |||
var dp *DomainParams | |||
if (prv.keyVariant & KeyVariant_SIDH_A) == KeyVariant_SIDH_A { | |||
err = prv.generatePrivateKeyA(rand) | |||
dp = &prv.params.A | |||
} else { | |||
err = prv.generatePrivateKeyB(rand) | |||
dp = &prv.params.B | |||
} | |||
if prv.keyVariant == KeyVariant_SIKE && err == nil { | |||
_, err = io.ReadFull(rand, prv.S) | |||
} | |||
// Private key generation takes advantage of the fact that keyspace for secret | |||
// key is (0, 2^x - 1), for some possitivite value of 'x' (see SIKE, 1.3.8). | |||
// It means that all bytes in the secret key, but the last one, can take any | |||
// value between <0x00,0xFF>. Similarily for the last byte, but generation | |||
// needs to chop off some bits, to make sure generated value is an element of | |||
// a key-space. | |||
_, err = io.ReadFull(rand, prv.Scalar) | |||
if err != nil { | |||
return err | |||
} | |||
prv.Scalar[len(prv.Scalar)-1] &= (1 << (dp.SecretBitLen % 8)) - 1 | |||
// Make sure scalar is SecretBitLen long. SIKE spec says that key | |||
// space starts from 0, but I'm not confortable with having low | |||
// value scalars used for private keys. It is still secrure as per | |||
// table 5.1 in [SIKE]. | |||
prv.Scalar[len(prv.Scalar)-1] |= 1 << ((dp.SecretBitLen % 8) - 1) | |||
return err | |||
} | |||
@@ -20,24 +20,23 @@ func Params(id PrimeFieldId) *SidhParams { | |||
func init() { | |||
p751 := SidhParams{ | |||
Id: FP_751, | |||
SecretKeySize: p751.P751_SecretKeySize, | |||
PublicKeySize: p751.P751_PublicKeySize, | |||
SharedSecretSize: p751.P751_SharedSecretSize, | |||
A: DomainParams{ | |||
Affine_P: p751.P751_affine_PA, | |||
Affine_Q: p751.P751_affine_QA, | |||
Affine_R: p751.P751_affine_RA, | |||
SecretBitLen: p751.P751_SecretBitLenA, | |||
MaskBytes: []byte{p751.P751_MaskAliceByte1, p751.P751_MaskAliceByte2, p751.P751_MaskAliceByte3}, | |||
IsogenyStrategy: p751.P751_AliceIsogenyStrategy[:], | |||
SecretBitLen: p751.P751_SecretBitLenA, | |||
SecretByteLen: uint((p751.P751_SecretBitLenA+7)/8), | |||
}, | |||
B: DomainParams{ | |||
Affine_P: p751.P751_affine_PB, | |||
Affine_Q: p751.P751_affine_QB, | |||
Affine_R: p751.P751_affine_RB, | |||
SecretBitLen: p751.P751_SecretBitLenB, | |||
MaskBytes: []byte{p751.P751_MaskBobByte}, | |||
IsogenyStrategy: p751.P751_BobIsogenyStrategy[:], | |||
SecretBitLen: p751.P751_SecretBitLenB, | |||
SecretByteLen: uint((p751.P751_SecretBitLenB+7)/8), | |||
}, | |||
OneFp2: p751.P751_OneFp2, | |||
HalfFp2: p751.P751_HalfFp2, | |||
@@ -1,9 +1,6 @@ | |||
package sidh | |||
import ( | |||
"errors" | |||
"io" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
) | |||
@@ -159,69 +156,6 @@ func traverseTreeSharedKeyB(curve *ProjectiveCurveParameters, xR *ProjectivePoin | |||
} | |||
} | |||
// ----------------------------------------------------------------------------- | |||
// 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 uint8 | |||
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 | |||
ok = checkLessThanThree238(prv.Scalar) | |||
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) | |||
// 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 | |||
@@ -1,70 +0,0 @@ | |||
// +build amd64,!noasm | |||
#include "textflag.h" | |||
// Digits of 3^238 - 1 | |||
#define THREE238M1_0 $0xedcd718a828384f8 | |||
#define THREE238M1_1 $0x733b35bfd4427a14 | |||
#define THREE238M1_2 $0xf88229cf94d7cf38 | |||
#define THREE238M1_3 $0x63c56c990c7c2ad6 | |||
#define THREE238M1_4 $0xb858a87e8f4222c7 | |||
#define THREE238M1_5 $0x254c9c6b525eaf5 | |||
// Set result to zero if the input scalar is <= 3^238, otherwise result is 1. | |||
// Scalar must be array of 48 bytes | |||
// func checkLessThanThree238(s_base uintptr, s_len uint, s_cap uint) uint8 | |||
TEXT ·checkLessThanThree238(SB), NOSPLIT, $0-16 | |||
MOVQ scalar+0(FP), SI | |||
XORQ AX, AX | |||
// Set [R10,...,R15] = 3^238 | |||
MOVQ THREE238M1_0, R10 | |||
MOVQ THREE238M1_1, R11 | |||
MOVQ THREE238M1_2, R12 | |||
MOVQ THREE238M1_3, R13 | |||
MOVQ THREE238M1_4, R14 | |||
MOVQ THREE238M1_5, R15 | |||
// Set [R10,...,R15] = 3^238 - scalar | |||
SUBQ (SI), R10 | |||
SBBQ 8(SI), R11 | |||
SBBQ 16(SI), R12 | |||
SBBQ 24(SI), R13 | |||
SBBQ 32(SI), R14 | |||
SBBQ 40(SI), R15 | |||
// Save borrow flag indicating 3^238 - scalar < 0 as a mask in AX (rax) | |||
ADCB $0, AX | |||
MOVB AX, ret+24(FP) | |||
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 | |||
MOVQ 16(SI), R12 | |||
MOVQ 24(SI), R13 | |||
MOVQ 32(SI), R14 | |||
MOVQ 40(SI), R15 | |||
// Add scalar twice to compute 3*scalar | |||
ADDQ R10, (SI) | |||
ADCQ R11, (8)(SI) | |||
ADCQ R12, (16)(SI) | |||
ADCQ R13, (24)(SI) | |||
ADCQ R14, (32)(SI) | |||
ADCQ R15, (40)(SI) | |||
ADDQ R10, (SI) | |||
ADCQ R11, (8)(SI) | |||
ADCQ R12, (16)(SI) | |||
ADCQ R13, (24)(SI) | |||
ADCQ R14, (32)(SI) | |||
ADCQ R15, (40)(SI) | |||
RET | |||
@@ -1,13 +0,0 @@ | |||
// +build amd64,!noasm | |||
package sidh | |||
// Set result to zero if the input scalar is <= 3^238, otherwise result is 1. | |||
// Scalar must be array of 48 bytes. This function is specific to P751. | |||
//go:noescape | |||
func checkLessThanThree238(scalar []byte) uint8 | |||
// 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) |
@@ -1,52 +0,0 @@ | |||
// +build !amd64 noasm | |||
package sidh | |||
var three238m1 = []uint8{ | |||
0xf8, 0x84, 0x83, 0x82, 0x8a, 0x71, 0xcd, 0xed, | |||
0x14, 0x7a, 0x42, 0xd4, 0xbf, 0x35, 0x3b, 0x73, | |||
0x38, 0xcf, 0xd7, 0x94, 0xcf, 0x29, 0x82, 0xf8, | |||
0xd6, 0x2a, 0x7c, 0x0c, 0x99, 0x6c, 0xc5, 0x63, | |||
0xc7, 0x22, 0x42, 0x8f, 0x7e, 0xa8, 0x58, 0xb8, | |||
0xf5, 0xea, 0x25, 0xb5, 0xc6, 0xc9, 0x54, 0x02} | |||
func addc8(cin, a, b uint8) (ret, cout uint8) { | |||
t := a + cin | |||
ret = b + t | |||
cout = ((a & b) | ((a | b) & (^ret))) >> 7 | |||
return | |||
} | |||
func subc8(bIn, a, b uint8) (ret, bOut uint8) { | |||
var tmp1 = a - b | |||
ret = tmp1 - bIn | |||
// Set bOut if bIn!=0 and tmp1==0 in constant time | |||
bOut = bIn & (1 ^ ((tmp1 | uint8(0-tmp1)) >> 7)) | |||
// Constant time check if a<b | |||
bOut |= (a ^ ((a ^ b) | (uint8(a-b) ^ b))) >> 7 | |||
return | |||
} | |||
// Set result to zero if the input scalar is <= 3^238, otherwise result is 1. | |||
// Scalar must be array of 48 bytes. This function is specific to P751. | |||
func checkLessThanThree238(scalar []byte) uint8 { | |||
var borrow uint8 | |||
for i := 0; i < len(three238m1); i++ { | |||
_, borrow = subc8(borrow, three238m1[i], scalar[i]) | |||
} | |||
return borrow | |||
} | |||
// Multiply 48-byte scalar by 3 to get a scalar in 3*[0,3^238). This | |||
// function is specific to P751. | |||
func multiplyByThree(scalar []byte) { | |||
var carry uint8 | |||
var dbl [48]uint8 | |||
for i := 0; i < len(scalar); i++ { | |||
dbl[i], carry = addc8(carry, scalar[i], scalar[i]) | |||
} | |||
for i := 0; i < len(scalar); i++ { | |||
scalar[i], carry = addc8(carry, dbl[i], scalar[i]) | |||
} | |||
} |
@@ -4,6 +4,7 @@ import ( | |||
"bytes" | |||
"crypto/rand" | |||
"encoding/hex" | |||
"math/big" | |||
"testing" | |||
. "github.com/cloudflare/p751sidh/internal/isogeny" | |||
@@ -12,7 +13,7 @@ import ( | |||
const ( | |||
// PrA - Alice's Private Key: 2*randint(0,2^371) | |||
PrA = "C09957CC83045FB4C3726384D784476ACB6FFD92E5B15B3C2D451BA063F1BD4CED8FBCF682A98DD0954D3" + | |||
"7BCAF730E00" | |||
"7BCAF730E" | |||
// PrB - Bob's Private Key: 3*randint(0,3^238) | |||
PrB = "393E8510E78A16D2DC1AACA9C9D17E7E78DB630881D8599C7040D05BB5557ECAE8165C45D5366ECB37B00" + | |||
"969740AF201" | |||
@@ -188,7 +189,7 @@ func TestRoundtripP751(t *testing.T) { | |||
func TestKeyAgreementP751_AliceEvenNumber(t *testing.T) { | |||
// even alice | |||
prE := "C09957CC83045FB4C3726384D784476ACB6FFD92E5B15B3C2D451BA063F1BD4CED8FBCF682A98DD0954D37BCAF730F00" | |||
prE := "C09957CC83045FB4C3726384D784476ACB6FFD92E5B15B3C2D451BA063F1BD4CED8FBCF682A98DD0954D37BCAF730F" | |||
pkE := "8A2DE6FD963C475F7829B689C8B8306FC0917A39EBBC35CA171546269A85698FEC0379E2E1A3C567BE1B8EF5639F81F304889737E6CC444DBED4579DB204DC8C7928F5CBB1ECDD682A1B5C48C0DAF34208C06BF201BE4E6063B1BFDC42413B0537F8E76BEE645C1A24118301BAB17EB8D6E0F283BCB16EFB833E4BB3463953C93165A0DDAC55B385059F27FF7228486D0A733812C81C792BE9EC3A16A5DB0EB099EEA76AC0E59612251A3AD19F7CC567DA2AEBD7733171F48E471D17648692355164E27B515D2A47D7BA34B3B48A047BE7C09C4ABEE2FCC9ACA7396C8A8C9E73E29533FC7369094DFA7988778E55E53F309922C6E233F8F9C7936C3D29CEA640406FCA06450AA1978FF39F227BF06B1E072F1763447C6F513B23CDF3B0EC0379070AEE5A02D9AD8E0EB023461D631F4A9643A4C79921334945F6B33DDFC11D9703BD06B047B4DA404AB12EFD2C3A49E5C42D10DA063352748B21DE41C32A5693FE1C0DCAB111F4990CD58BECADB1892EE7A7E99C9DB4DA4E69C96E57138B99038BC9B877ECE75914EFB98DD08B9E4A2DCCB948A8F7D2F26678A9952BA0EFAB1E9CF6E51B557480DEC2BA30DE0FE4AFE30A6B30765EE75EF64F678316D81C72755AD2CFA0B8C7706B07BFA52FBC3DB84EF9E79796C0089305B1E13C78660779E0FF2A13820CE141104F976B1678990F85B2D3D2B89CD5BC4DD52603A5D24D3EFEDA44BAA0F38CDB75A220AF45EAB70F2799875D435CE50FC6315EDD4BB7AA7260AFD7CD0561B69B4FA3A817904322661C3108DA24" | |||
testKeyAgreement(Params(FP_751), t, pkE, PkB, prE, PrB) | |||
} | |||
@@ -221,64 +222,41 @@ func TestImportExport(t *testing.T) { | |||
} | |||
} | |||
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} | |||
// makes second 64-bit digits bigger than in three238. checks if carries are correctly propagated | |||
var three238plus2power65 = [48]byte{249, 132, 131, 130, 138, 113, 205, 237, 22, 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 uint8 | |||
result = checkLessThanThree238(three238minus1[:]) | |||
if result != 0 { | |||
t.Error("expected 0, got", result) | |||
} | |||
result = checkLessThanThree238(three238[:]) | |||
if result != 1 { | |||
t.Error("expected nonzero, got", result) | |||
} | |||
result = checkLessThanThree238(three238plus1[:]) | |||
if result != 1 { | |||
t.Error("expected nonzero, got", result) | |||
} | |||
result = checkLessThanThree238(three238plus2power65[:]) | |||
if result != 1 { | |||
t.Error("expected nonzero, got", result) | |||
func testPrivateKeyBelowMax(t testing.TB, id PrimeFieldId) { | |||
params := Params(id) | |||
for variant,keySz:=range( | |||
map[KeyVariant]*DomainParams { | |||
KeyVariant_SIDH_A: ¶ms.A, | |||
KeyVariant_SIDH_B: ¶ms.B}){ | |||
func(v KeyVariant, dp *DomainParams) { | |||
var blen = int(dp.SecretByteLen) | |||
var prv = NewPrivateKey(id, v) | |||
// Calculate either (2^e2 - 1) or (2^s - 1); where s=ceil(log_2(3^e3))) | |||
maxSecertVal := big.NewInt(int64(dp.SecretBitLen)) | |||
maxSecertVal.Exp(big.NewInt(int64(2)), maxSecertVal, nil) | |||
maxSecertVal.Sub(maxSecertVal, big.NewInt(1)) | |||
// Do same test 1000 times | |||
for i:=0; i<1000; i++ { | |||
err := prv.Generate(rand.Reader) | |||
checkErr(t, err, "Private key generation") | |||
// Convert to big-endian, as that's what expected by (*Int)SetBytes() | |||
secretBytes := prv.Export() | |||
for i:=0; i<int(blen/2); i++ { | |||
tmp := secretBytes[i] ^ secretBytes[blen-i-1] | |||
secretBytes[i] = tmp ^ secretBytes[i] | |||
secretBytes[blen-i-1] = tmp ^ secretBytes[blen-i-1] | |||
} | |||
prvBig := new(big.Int).SetBytes(secretBytes) | |||
// Check if generated key is bigger then acceptable | |||
if prvBig.Cmp(maxSecertVal) == 1 { | |||
t.Error("Generated private key is wrong") | |||
} | |||
} | |||
}(variant,keySz) | |||
} | |||
} | |||
@@ -126,7 +126,7 @@ func Encapsulate(rng io.Reader, pub *PublicKey) (ctext []byte, secret []byte, er | |||
// Buffer for random, secret message | |||
var ptext = make([]byte, params.MsgLen) | |||
// r = G(ptext||pub) | |||
var r = make([]byte, params.SecretKeySize) | |||
var r = make([]byte, params.A.SecretByteLen) | |||
// Resulting shared secret | |||
secret = make([]byte, params.KemSize) | |||
@@ -141,9 +141,8 @@ func Encapsulate(rng io.Reader, pub *PublicKey) (ctext []byte, secret []byte, er | |||
h.Write(pub.Export()) | |||
h.Read(r) | |||
// cSHAKE256 implementation is byte oriented. Ensure bitlength is less then to E2 | |||
r[len(r)-1] &= params.A.MaskBytes[0] | |||
r[len(r)-2] &= params.A.MaskBytes[1] // clear high bits, so scalar < 2*732 | |||
// cSHAKE256 implementation is byte oriented. Ensure bitlength is not bigger then to 2^e2-1 | |||
r[len(r)-1] &= (1 << (params.A.SecretBitLen % 8)) - 1 | |||
// (c0 || c1) = Enc(pkA, ptext; r) | |||
skA := NewPrivateKey(params.Id, KeyVariant_SIDH_A) | |||
@@ -173,7 +172,7 @@ func Encapsulate(rng io.Reader, pub *PublicKey) (ctext []byte, secret []byte, er | |||
// Constant time for properly initialized input. | |||
func Decapsulate(prv *PrivateKey, pub *PublicKey, ctext []byte) ([]byte, error) { | |||
var params = pub.Params() | |||
var r = make([]byte, params.SecretKeySize) | |||
var r = make([]byte, params.A.SecretByteLen) | |||
// Resulting shared secret | |||
var secret = make([]byte, params.KemSize) | |||
var skA = NewPrivateKey(params.Id, KeyVariant_SIDH_A) | |||
@@ -189,9 +188,8 @@ func Decapsulate(prv *PrivateKey, pub *PublicKey, ctext []byte) ([]byte, error) | |||
h.Write(pub.Export()) | |||
h.Read(r) | |||
// cSHAKE256 implementation is byte oriented: Ensure bitlength is equal to E2 | |||
r[len(r)-1] &= params.A.MaskBytes[0] | |||
r[len(r)-2] &= params.A.MaskBytes[1] // clear high bits, so scalar < 2*732 | |||
// cSHAKE256 implementation is byte oriented: Ensure bitlength is not bigger than 2^e2-1 | |||
r[len(r)-1] &= (1 << (params.A.SecretBitLen % 8)) - 1 | |||
// Never fails | |||
skA.Import(r) | |||
@@ -312,7 +312,7 @@ func TestSIKE_KAT(t *testing.T) { | |||
// sk (secret key in test vector is concatenation of | |||
// MSG + SECRET_BOB_KEY + PUBLIC_BOB_KEY. We use only MSG+SECRET_BOB_KEY | |||
sk := readAndCheckLine(r) | |||
sk = sk[:params.MsgLen+uint(params.SecretKeySize)] | |||
sk = sk[:params.MsgLen+uint(params.B.SecretByteLen)] | |||
// ct | |||
ct := readAndCheckLine(r) | |||
// ss | |||