1
0
mirror of https://github.com/henrydcase/nobs.git synced 2024-11-22 15:18:57 +00:00
nobs/dh/csidh/csidh_test.go

392 lines
10 KiB
Go

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