@@ -1,122 +1,6 @@ | |||
package sike | |||
// I keep it bool in order to be able to apply logical NOT | |||
type KeyVariant uint | |||
// Representation of an element of the base field F_p. | |||
// | |||
// No particular meaning is assigned to the representation -- it could represent | |||
// an element in Montgomery form, or not. Tracking the meaning of the field | |||
// element is left to higher types. | |||
type Fp [FP_WORDS]uint64 | |||
// Represents an intermediate product of two elements of the base field F_p. | |||
type FpX2 [2 * FP_WORDS]uint64 | |||
// Represents an element of the extended field Fp^2 = Fp(x+i) | |||
type Fp2 struct { | |||
A Fp | |||
B Fp | |||
} | |||
type DomainParams struct { | |||
// P, Q and R=P-Q base points | |||
Affine_P, Affine_Q, Affine_R Fp2 | |||
// 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 uint8 | |||
// Bytelen of P | |||
Bytelen int | |||
// The public key size, in bytes. | |||
PublicKeySize int | |||
// The shared secret size, in bytes. | |||
SharedSecretSize int | |||
// 2- and 3-torsion group parameter definitions | |||
A, B DomainParams | |||
// Precomputed identity element in the Fp2 in Montgomery domain | |||
OneFp2 Fp2 | |||
// Precomputed 1/2 in the Fp2 in Montgomery domain | |||
HalfFp2 Fp2 | |||
// Length of SIKE secret message. Must be one of {24,32,40}, | |||
// depending on size of prime field used (see [SIKE], 1.4 and 5.1) | |||
MsgLen int | |||
// Length of SIKE ephemeral KEM key (see [SIKE], 1.4 and 5.1) | |||
KemSize int | |||
// Size of a ciphertext returned by encapsulation in bytes | |||
CiphertextSize int | |||
} | |||
// 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 Fp2 | |||
C Fp2 | |||
} | |||
// A point on the projective line P^1(F_{p^2}). | |||
// | |||
// This represents a point on the Kummer line of a Montgomery curve. The | |||
// curve is specified by a ProjectiveCurveParameters struct. | |||
type ProjectivePoint struct { | |||
X Fp2 | |||
Z Fp2 | |||
} | |||
// 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-, 3-torsion group or SIKE | |||
keyVariant KeyVariant | |||
} | |||
// Defines operations on private key | |||
type PrivateKey struct { | |||
key | |||
// Secret key | |||
Scalar []byte | |||
// Used only by KEM | |||
S []byte | |||
} | |||
// Defines operations on public key | |||
type PublicKey struct { | |||
key | |||
affine_xP Fp2 | |||
affine_xQ Fp2 | |||
affine_xQmP Fp2 | |||
} | |||
// A point on the projective line P^1(F_{p^2}). | |||
// | |||
// This is used to work projectively with the curve coefficients. | |||
type ProjectiveCurveParameters struct { | |||
A Fp2 | |||
C Fp2 | |||
} | |||
const ( | |||
// First 2 bits identify SIDH variant third bit indicates | |||
// wether key is a SIKE variant (set) or SIDH (not set) | |||
// 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 | |||
// 110 - SIKE | |||
KeyVariant_SIKE = 1<<2 | KeyVariant_SIDH_B | |||
// Number of uint64 limbs used to store field element | |||
FP_WORDS = 8 | |||
) | |||
@@ -7,6 +7,13 @@ import ( | |||
"io" | |||
) | |||
const ( | |||
// n can is max 320-bit (see 1.4 of [SIKE]) | |||
MaxMsgLen = 40 | |||
MaxSharedSecretSize = 188 | |||
MaxSecretByteLenA = 47 | |||
) | |||
var cshakeG, cshakeH, cshakeF *shake.CShake | |||
func init() { | |||
@@ -52,7 +59,7 @@ func zeroize(fp *Fp2) { | |||
// | |||
// The output byte slice must be at least 2*bytelen(p) bytes long. | |||
func convFp2ToBytes(output []byte, fp2 *Fp2) { | |||
if len(output) < 2*Params.Bytelen { | |||
if len(output) < Params.SharedSecretSize { | |||
panic("output byte slice too short") | |||
} | |||
var a Fp2 | |||
@@ -72,7 +79,7 @@ func convFp2ToBytes(output []byte, fp2 *Fp2) { | |||
// | |||
// It is an error to call this function if the input byte slice is less than 2*bytelen(p) bytes long. | |||
func convBytesToFp2(fp2 *Fp2, input []byte) { | |||
if len(input) < 2*Params.Bytelen { | |||
if len(input) < Params.SharedSecretSize { | |||
panic("input byte slice too short") | |||
} | |||
@@ -369,8 +376,8 @@ func deriveSecretB(ss []byte, prv *PrivateKey, pub *PublicKey) { | |||
} | |||
func generateCiphertext(ctext []byte, skA *PrivateKey, pkA, pkB *PublicKey, ptext []byte) error { | |||
var n [40]byte // OZAPTF n can is max 320-bit (see 1.4 of [SIKE]) | |||
var j [126]byte | |||
var n [MaxMsgLen]byte | |||
var j [MaxSharedSecretSize]byte | |||
var ptextLen = len(ptext) | |||
if pkB.keyVariant != KeyVariant_SIKE { | |||
@@ -383,7 +390,7 @@ func generateCiphertext(ctext []byte, skA *PrivateKey, pkA, pkB *PublicKey, ptex | |||
} | |||
cshakeF.Reset() | |||
cshakeF.Write(j[:2*Params.Bytelen]) | |||
cshakeF.Write(j[:Params.SharedSecretSize]) | |||
cshakeF.Read(n[:ptextLen]) | |||
for i, _ := range ptext { | |||
n[i] ^= ptext[i] | |||
@@ -447,12 +454,9 @@ func (pub *PublicKey) Size() int { | |||
// 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 { | |||
// OZAPTF | |||
ret := make([]byte, len(prv.Scalar)+len(prv.S)) | |||
copy(ret, prv.S) | |||
copy(ret[len(prv.S):], prv.Scalar) | |||
return ret | |||
func (prv *PrivateKey) Export(out []byte) { | |||
copy(out, prv.S) | |||
copy(out[len(prv.S):], prv.Scalar) | |||
} | |||
// Size returns size of the private key in bytes | |||
@@ -579,7 +583,7 @@ func encrypt(ctext []byte, rng io.Reader, pub *PublicKey, ptext []byte) error { | |||
// Constant time | |||
func decrypt(n []byte, prv *PrivateKey, ctext []byte) (int, error) { | |||
var c1_len int | |||
var j [2 * 63]byte // OZAPTF: 63 | |||
var j [MaxSharedSecretSize]byte | |||
var pk_len = prv.params.PublicKeySize | |||
if prv.keyVariant != KeyVariant_SIKE { | |||
@@ -604,7 +608,7 @@ func decrypt(n []byte, prv *PrivateKey, ctext []byte) (int, error) { | |||
} | |||
cshakeF.Reset() | |||
cshakeF.Write(j[:2*Params.Bytelen]) | |||
cshakeF.Write(j[:Params.SharedSecretSize]) | |||
cshakeF.Read(n[:c1_len]) | |||
for i, _ := range n[:c1_len] { | |||
n[i] ^= ctext[pk_len+i] | |||
@@ -616,8 +620,8 @@ func decrypt(n []byte, prv *PrivateKey, ctext []byte) (int, error) { | |||
func (kem *KEM) Allocate(rng io.Reader) { | |||
kem.allocated = true | |||
kem.rng = rng | |||
kem.msg = make([]byte, 24) | |||
kem.secretBytes = make([]byte, 32) | |||
kem.msg = make([]byte, Params.MsgLen) | |||
kem.secretBytes = make([]byte, Params.A.SecretByteLen) | |||
} | |||
func (kem *KEM) Reset() { | |||
@@ -638,30 +642,22 @@ func (kem *KEM) SharedSecretSize() int { | |||
return Params.KemSize | |||
} | |||
const ( | |||
// See [SIKE], 1.4 | |||
MaxMsgLen = 40 | |||
MaxPublicKey = 378 | |||
) | |||
// Encapsulation receives the public key and generates SIKE ciphertext and shared secret. | |||
// The generated ciphertext is used for authentication. | |||
// The rng must be cryptographically secure PRNG. | |||
// Error is returned in case PRNG fails or wrongly formated input was provided. | |||
func (kem *KEM) Encapsulate(ciphertext, secret []byte, pub *PublicKey) error { | |||
if !kem.allocated { | |||
panic("KEM unallocated") | |||
} | |||
// OZAPTF: MaxBuf: 3*SharedSecretSize | |||
var buf [3 * 126]byte | |||
var buf [3 * MaxSharedSecretSize]byte | |||
var skA = PrivateKey{ | |||
key: key{ | |||
params: &Params, | |||
keyVariant: KeyVariant_SIDH_A}, | |||
Scalar: kem.secretBytes} | |||
if !kem.allocated { | |||
panic("KEM unallocated") | |||
} | |||
// Generate ephemeral value | |||
_, err := io.ReadFull(kem.rng, kem.msg[:]) | |||
if err != nil { | |||
@@ -671,7 +667,7 @@ func (kem *KEM) Encapsulate(ciphertext, secret []byte, pub *PublicKey) error { | |||
pub.Export(buf[:]) | |||
cshakeG.Reset() | |||
cshakeG.Write(kem.msg) | |||
cshakeG.Write(buf[:]) | |||
cshakeG.Write(buf[:3*Params.SharedSecretSize]) | |||
cshakeG.Read(skA.Scalar) | |||
// Ensure bitlength is not bigger then to 2^e2-1 | |||
@@ -696,16 +692,15 @@ func (kem *KEM) Encapsulate(ciphertext, secret []byte, pub *PublicKey) error { | |||
// Decapsulation may fail in case input is wrongly formated. | |||
// Constant time for properly initialized input. | |||
func (kem *KEM) Decapsulate(secret []byte, prv *PrivateKey, pub *PublicKey, ctext []byte) error { | |||
// Resulting shared secret | |||
var c0 [3 * 126]byte | |||
var r [32]byte // OZAPTF: to change | |||
var keyBuf [3 * 126]byte | |||
var m [40]byte // OZAPTF: to change | |||
var m [MaxMsgLen]byte | |||
var r [MaxSecretByteLenA]byte | |||
var pkBytes [3 * MaxSharedSecretSize]byte | |||
var skA = PrivateKey{ | |||
key: key{ | |||
params: &Params, | |||
keyVariant: KeyVariant_SIDH_A}, | |||
Scalar: kem.secretBytes} | |||
var pkA = NewPublicKey(KeyVariant_SIDH_A) | |||
c1_len, err := decrypt(m[:], prv, ctext) | |||
if err != nil { | |||
@@ -714,21 +709,18 @@ func (kem *KEM) Decapsulate(secret []byte, prv *PrivateKey, pub *PublicKey, ctex | |||
// r' = G(m'||pub) | |||
//var key = make([]byte, pub.Size()+2*Params.MsgLen) | |||
pub.Export(keyBuf[:]) | |||
pub.Export(pkBytes[:]) | |||
cshakeG.Reset() | |||
cshakeG.Write(m[:c1_len]) | |||
cshakeG.Write(keyBuf[:]) | |||
cshakeG.Read(r[:]) | |||
cshakeG.Write(pkBytes[:3*pub.params.SharedSecretSize]) | |||
cshakeG.Read(r[:pub.params.A.SecretByteLen]) | |||
// Ensure bitlength is not bigger than 2^e2-1 | |||
r[len(r)-1] &= (1 << (pub.params.A.SecretBitLen % 8)) - 1 | |||
// Never fails | |||
skA.Import(r[:]) | |||
r[pub.params.A.SecretByteLen-1] &= (1 << (pub.params.A.SecretBitLen % 8)) - 1 | |||
// Never fails | |||
pkA := NewPublicKey(KeyVariant_SIDH_A) | |||
skA.Import(r[:pub.params.A.SecretByteLen]) | |||
skA.GeneratePublicKey(pkA) | |||
pkA.Export(c0[:]) | |||
pkA.Export(pkBytes[:]) | |||
// S is chosen at random when generating a key and unknown to other party. It | |||
// may seem weird, but it's correct. It is important that S is unpredictable | |||
@@ -737,7 +729,7 @@ func (kem *KEM) Decapsulate(secret []byte, prv *PrivateKey, pub *PublicKey, ctex | |||
// | |||
// See more details in "On the security of supersingular isogeny cryptosystems" | |||
// (S. Galbraith, et al., 2016, ePrint #859). | |||
mask := subtle.ConstantTimeCompare(c0[:pub.params.PublicKeySize], ctext[:pub.params.PublicKeySize]) | |||
mask := subtle.ConstantTimeCompare(pkBytes[:pub.params.PublicKeySize], ctext[:pub.params.PublicKeySize]) | |||
cpick(mask, m[:c1_len], m[:c1_len], prv.S) | |||
cshakeH.Reset() | |||
cshakeH.Write(m[:c1_len]) | |||
@@ -172,6 +172,7 @@ func testPrivateKeyBelowMax(t testing.TB) { | |||
func(v KeyVariant, dp *DomainParams) { | |||
var blen = int(dp.SecretByteLen) | |||
var prv = NewPrivateKey(v) | |||
var secretBytes = make([]byte, prv.Size()) | |||
// Calculate either (2^e2 - 1) or (2^s - 1); where s=ceil(log_2(3^e3))) | |||
maxSecertVal := big.NewInt(int64(dp.SecretBitLen)) | |||
@@ -184,7 +185,7 @@ func testPrivateKeyBelowMax(t testing.TB) { | |||
checkErr(t, err, "Private key generation") | |||
// Convert to big-endian, as that's what expected by (*Int)SetBytes() | |||
secretBytes := prv.Export() | |||
prv.Export(secretBytes) | |||
for i := 0; i < int(blen/2); i++ { | |||
tmp := secretBytes[i] ^ secretBytes[blen-i-1] | |||
secretBytes[i] = tmp ^ secretBytes[i] | |||
@@ -484,7 +485,8 @@ func TestNegativeKEMSameWrongResult(t *testing.T) { | |||
"pre-requisite for a test failed") | |||
// second decapsulation must be done with same, but imported private key | |||
expSk := sk.Export() | |||
var expSk = make([]byte, sk.Size()) | |||
sk.Export(expSk) | |||
// creat new private key | |||
sk = NewPrivateKey(KeyVariant_SIKE) | |||
@@ -0,0 +1,120 @@ | |||
package sike | |||
// I keep it bool in order to be able to apply logical NOT | |||
type KeyVariant uint | |||
// Representation of an element of the base field F_p. | |||
// | |||
// No particular meaning is assigned to the representation -- it could represent | |||
// an element in Montgomery form, or not. Tracking the meaning of the field | |||
// element is left to higher types. | |||
type Fp [FP_WORDS]uint64 | |||
// Represents an intermediate product of two elements of the base field F_p. | |||
type FpX2 [2 * FP_WORDS]uint64 | |||
// Represents an element of the extended field Fp^2 = Fp(x+i) | |||
type Fp2 struct { | |||
A Fp | |||
B Fp | |||
} | |||
type DomainParams struct { | |||
// P, Q and R=P-Q base points | |||
Affine_P, Affine_Q, Affine_R Fp2 | |||
// 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 uint8 | |||
// Bytelen of P | |||
Bytelen int | |||
// The public key size, in bytes. | |||
PublicKeySize int | |||
// The shared secret size, in bytes. | |||
SharedSecretSize int | |||
// 2- and 3-torsion group parameter definitions | |||
A, B DomainParams | |||
// Precomputed identity element in the Fp2 in Montgomery domain | |||
OneFp2 Fp2 | |||
// Precomputed 1/2 in the Fp2 in Montgomery domain | |||
HalfFp2 Fp2 | |||
// Length of SIKE secret message. Must be one of {24,32,40}, | |||
// depending on size of prime field used (see [SIKE], 1.4 and 5.1) | |||
MsgLen int | |||
// Length of SIKE ephemeral KEM key (see [SIKE], 1.4 and 5.1) | |||
KemSize int | |||
// Size of a ciphertext returned by encapsulation in bytes | |||
CiphertextSize int | |||
} | |||
// 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 Fp2 | |||
C Fp2 | |||
} | |||
// A point on the projective line P^1(F_{p^2}). | |||
// | |||
// This represents a point on the Kummer line of a Montgomery curve. The | |||
// curve is specified by a ProjectiveCurveParameters struct. | |||
type ProjectivePoint struct { | |||
X Fp2 | |||
Z Fp2 | |||
} | |||
// 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-, 3-torsion group or SIKE | |||
keyVariant KeyVariant | |||
} | |||
// Defines operations on private key | |||
type PrivateKey struct { | |||
key | |||
// Secret key | |||
Scalar []byte | |||
// Used only by KEM | |||
S []byte | |||
} | |||
// Defines operations on public key | |||
type PublicKey struct { | |||
key | |||
affine_xP Fp2 | |||
affine_xQ Fp2 | |||
affine_xQmP Fp2 | |||
} | |||
// A point on the projective line P^1(F_{p^2}). | |||
// | |||
// This is used to work projectively with the curve coefficients. | |||
type ProjectiveCurveParameters struct { | |||
A Fp2 | |||
C Fp2 | |||
} | |||
const ( | |||
// First 2 bits identify SIDH variant third bit indicates | |||
// wether key is a SIKE variant (set) or SIDH (not set) | |||
// 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 | |||
// 110 - SIKE | |||
KeyVariant_SIKE = 1<<2 | KeyVariant_SIDH_B | |||
) |