@@ -29,15 +29,9 @@ ifeq ($(DBG),1) | |||||
OPTS_ENV+= GOTRACEBACK=crash # enable core dumps | OPTS_ENV+= GOTRACEBACK=crash # enable core dumps | ||||
endif | endif | ||||
test: | |||||
test: | |||||
$(OPTS_ENV) $(GO) test $(OPTS) $(TEST_PATH) | $(OPTS_ENV) $(GO) test $(OPTS) $(TEST_PATH) | ||||
test_csidh: clean make_dirs $(addprefix prep-,$(TARGETS)) | |||||
cd $(GOPATH_LOCAL); $(OPTS_ENV) GOPATH=$(GOPATH_LOCAL) go test $(OPTS) github.com/henrydcase/nobs/dh/csidh | |||||
test_csidh_bin: clean make_dirs $(addprefix prep-,$(TARGETS)) | |||||
cd $(GOPATH_LOCAL); $(OPTS_ENV) GOPATH=$(GOPATH_LOCAL) go test -c $(OPTS) github.com/henrydcase/nobs/dh/csidh | |||||
cover: | cover: | ||||
$(GO) test \ | $(GO) test \ | ||||
-coverprofile=coverage.txt -covermode=atomic $(OPTS) $(TEST_PATH) | -coverprofile=coverage.txt -covermode=atomic $(OPTS) $(TEST_PATH) | ||||
@@ -45,10 +39,6 @@ cover: | |||||
bench: | bench: | ||||
$(GO) test $(BENCH_OPTS) $(TEST_PATH) | $(GO) test $(BENCH_OPTS) $(TEST_PATH) | ||||
bench_csidh: clean $(addprefix prep-,$(TARGETS)) | |||||
cd $(GOPATH_LOCAL); GOCACHE=$(GOCACHE) GOPATH=$(GOPATH_LOCAL) $(GO) test \ | |||||
$(BENCH_OPTS) github.com/henrydcase/nobs/dh/csidh | |||||
clean: | clean: | ||||
rm -rf $(VENDOR_DIR) | rm -rf $(VENDOR_DIR) | ||||
rm -rf coverage.txt | rm -rf coverage.txt | ||||
@@ -58,6 +48,10 @@ vendor-sidh-for-tls: clean | |||||
rsync -a . $(VENDOR_DIR)/github_com/henrydcase/nobs/ --exclude=$(VENDOR_DIR) --exclude=.git --exclude=.travis.yml --exclude=README.md | rsync -a . $(VENDOR_DIR)/github_com/henrydcase/nobs/ --exclude=$(VENDOR_DIR) --exclude=.git --exclude=.travis.yml --exclude=README.md | ||||
find $(VENDOR_DIR) -type f -print0 -name "*.go" | xargs -0 sed -i 's/github\.com/github_com/g' | find $(VENDOR_DIR) -type f -print0 -name "*.go" | xargs -0 sed -i 's/github\.com/github_com/g' | ||||
gen: clean | |||||
$(GO) generate -v ./... | |||||
$(GO) mod tidy | |||||
pprof-cpu: | pprof-cpu: | ||||
$(GO) tool pprof cpu.out | $(GO) tool pprof cpu.out | ||||
@@ -0,0 +1 @@ | |||||
package sidh |
@@ -10,7 +10,7 @@ import ( | |||||
"testing" | "testing" | ||||
"testing/quick" | "testing/quick" | ||||
"github.com/henrydcase/nobs/sidh/internal/common" | |||||
"github.com/henrydcase/nobs/dh/sidh/internal/common" | |||||
"golang.org/x/sys/cpu" | "golang.org/x/sys/cpu" | ||||
) | ) | ||||
@@ -0,0 +1 @@ | |||||
package p434 |
@@ -9,7 +9,6 @@ import ( | |||||
"testing" | "testing" | ||||
"github.com/henrydcase/nobs/dh/sidh/internal/common" | "github.com/henrydcase/nobs/dh/sidh/internal/common" | ||||
. "github.com/henrydcase/nobs/internal/test" | |||||
) | ) | ||||
/* ------------------------------------------------------------------------- | /* ------------------------------------------------------------------------- | ||||
@@ -24,6 +23,13 @@ type sidhVec struct { | |||||
PrB string | PrB string | ||||
} | } | ||||
func checkErr(t testing.TB, err error, msg string) { | |||||
t.Helper() | |||||
if err != nil { | |||||
t.Error(msg) | |||||
} | |||||
} | |||||
var tdataSidh = map[uint8]sidhVec{ | var tdataSidh = map[uint8]sidhVec{ | ||||
Fp434: { | Fp434: { | ||||
id: Fp434, | id: Fp434, | ||||
@@ -203,9 +209,9 @@ func testRoundtrip(t *testing.T, v sidhVec) { | |||||
// Generate private keys | // Generate private keys | ||||
err = prvA.Generate(rand.Reader) | err = prvA.Generate(rand.Reader) | ||||
CheckNoErr(t, err, "key generation failed") | |||||
checkErr(t, err, "key generation failed") | |||||
err = prvB.Generate(rand.Reader) | err = prvB.Generate(rand.Reader) | ||||
CheckNoErr(t, err, "key generation failed") | |||||
checkErr(t, err, "key generation failed") | |||||
// Generate public keys | // Generate public keys | ||||
prvA.GeneratePublicKey(pubA) | prvA.GeneratePublicKey(pubA) | ||||
@@ -241,11 +247,11 @@ func testKeyAgreement(t *testing.T, v sidhVec) { | |||||
// Negative case | // Negative case | ||||
dec, err := hex.DecodeString(v.PkA) | dec, err := hex.DecodeString(v.PkA) | ||||
CheckNoErr(t, err, "decoding failed") | |||||
checkErr(t, err, "decoding failed") | |||||
dec[0] = ^dec[0] | dec[0] = ^dec[0] | ||||
err = alicePublic.Import(dec) | err = alicePublic.Import(dec) | ||||
CheckNoErr(t, err, "import failed") | |||||
checkErr(t, err, "import failed") | |||||
bobPrivate.DeriveSecret(s1, alicePublic) | bobPrivate.DeriveSecret(s1, alicePublic) | ||||
alicePrivate.DeriveSecret(s2, bobPublic) | alicePrivate.DeriveSecret(s2, bobPublic) | ||||
@@ -261,16 +267,16 @@ func testImportExport(t *testing.T, v sidhVec) { | |||||
// Import keys | // Import keys | ||||
aHex, err := hex.DecodeString(v.PkA) | aHex, err := hex.DecodeString(v.PkA) | ||||
CheckNoErr(t, err, "invalid hex-number provided") | |||||
checkErr(t, err, "invalid hex-number provided") | |||||
err = a.Import(aHex) | err = a.Import(aHex) | ||||
CheckNoErr(t, err, "import failed") | |||||
checkErr(t, err, "import failed") | |||||
bHex, err := hex.DecodeString(v.PkB) | bHex, err := hex.DecodeString(v.PkB) | ||||
CheckNoErr(t, err, "invalid hex-number provided") | |||||
checkErr(t, err, "invalid hex-number provided") | |||||
err = b.Import(bHex) | err = b.Import(bHex) | ||||
CheckNoErr(t, err, "import failed") | |||||
checkErr(t, err, "import failed") | |||||
aBytes := make([]byte, a.Size()) | aBytes := make([]byte, a.Size()) | ||||
bBytes := make([]byte, b.Size()) | bBytes := make([]byte, b.Size()) | ||||
@@ -313,7 +319,7 @@ func testPrivateKeyBelowMax(t *testing.T, vec sidhVec) { | |||||
// Do same test 1000 times | // Do same test 1000 times | ||||
for i := 0; i < 1000; i++ { | for i := 0; i < 1000; i++ { | ||||
err := prv.Generate(rand.Reader) | err := prv.Generate(rand.Reader) | ||||
CheckNoErr(t, err, "Private key generation") | |||||
checkErr(t, err, "Private key generation") | |||||
// Convert to big-endian, as that's what expected by (*Int)SetBytes() | // Convert to big-endian, as that's what expected by (*Int)SetBytes() | ||||
prv.Export(secretBytes) | prv.Export(secretBytes) | ||||
@@ -6,7 +6,7 @@ import ( | |||||
"io" | "io" | ||||
"github.com/henrydcase/nobs/dh/sidh/internal/common" | "github.com/henrydcase/nobs/dh/sidh/internal/common" | ||||
"github.com/henrydcase/nobs/internal/shake" | |||||
"github.com/henrydcase/nobs/hash/sha3" | |||||
) | ) | ||||
// SIKE KEM interface. | // SIKE KEM interface. | ||||
@@ -16,7 +16,7 @@ type KEM struct { | |||||
msg []byte | msg []byte | ||||
secretBytes []byte | secretBytes []byte | ||||
params *common.SidhParams | params *common.SidhParams | ||||
shake shake.Shake | |||||
shake sha3.ShakeHash | |||||
} | } | ||||
// NewSike434 instantiates SIKE/p434 KEM. | // NewSike434 instantiates SIKE/p434 KEM. | ||||
@@ -47,7 +47,7 @@ func (c *KEM) Allocate(id uint8, rng io.Reader) { | |||||
c.params = common.Params(id) | c.params = common.Params(id) | ||||
c.msg = make([]byte, c.params.MsgLen) | c.msg = make([]byte, c.params.MsgLen) | ||||
c.secretBytes = make([]byte, c.params.A.SecretByteLen) | c.secretBytes = make([]byte, c.params.A.SecretByteLen) | ||||
c.shake = shake.NewShake256() | |||||
c.shake = sha3.NewShake256() | |||||
c.allocated = true | c.allocated = true | ||||
} | } | ||||
@@ -5,6 +5,7 @@ import ( | |||||
"bytes" | "bytes" | ||||
"crypto/rand" | "crypto/rand" | ||||
"encoding/hex" | "encoding/hex" | ||||
"errors" | |||||
"fmt" | "fmt" | ||||
"io" | "io" | ||||
"os" | "os" | ||||
@@ -12,7 +13,6 @@ import ( | |||||
"testing" | "testing" | ||||
"github.com/henrydcase/nobs/dh/sidh/internal/common" | "github.com/henrydcase/nobs/dh/sidh/internal/common" | ||||
. "github.com/henrydcase/nobs/internal/test" | |||||
) | ) | ||||
type sikeVec struct { | type sikeVec struct { | ||||
@@ -24,6 +24,25 @@ type sikeVec struct { | |||||
PrB string | PrB string | ||||
} | } | ||||
func Ok(t testing.TB, err error, msg string) { | |||||
t.Helper() | |||||
if err != nil { | |||||
t.Error(msg) | |||||
} | |||||
} | |||||
// testPanic returns true if call to function 'f' caused panic. | |||||
func testPanic(f func()) error { | |||||
var hasPanicked = errors.New("no panic detected") | |||||
defer func() { | |||||
if r := recover(); r != nil { | |||||
hasPanicked = nil | |||||
} | |||||
}() | |||||
f() | |||||
return hasPanicked | |||||
} | |||||
var tdataSike = map[uint8]sikeVec{ | var tdataSike = map[uint8]sikeVec{ | ||||
Fp434: { | Fp434: { | ||||
Fp434, "P-434", NewSike434(rand.Reader), | Fp434, "P-434", NewSike434(rand.Reader), | ||||
@@ -99,17 +118,17 @@ func testPKERoundTrip(t *testing.T, v sikeVec) { | |||||
pkB := NewPublicKey(params.ID, KeyVariantSike) | pkB := NewPublicKey(params.ID, KeyVariantSike) | ||||
skB := NewPrivateKey(params.ID, KeyVariantSike) | skB := NewPrivateKey(params.ID, KeyVariantSike) | ||||
pkHex, err := hex.DecodeString(v.PkB) | pkHex, err := hex.DecodeString(v.PkB) | ||||
CheckNoErr(t, err, "Test vector wrong") | |||||
Ok(t, err, "Test vector wrong") | |||||
skHex, err := hex.DecodeString(v.PrB) | skHex, err := hex.DecodeString(v.PrB) | ||||
CheckNoErr(t, err, "Test vector wrong") | |||||
Ok(t, err, "Test vector wrong") | |||||
err = pkB.Import(pkHex) | err = pkB.Import(pkHex) | ||||
CheckNoErr(t, err, "Public key import failed") | |||||
Ok(t, err, "Public key import failed") | |||||
err = skB.Import(skHex) | err = skB.Import(skHex) | ||||
CheckNoErr(t, err, "Private key import failed") | |||||
Ok(t, err, "Private key import failed") | |||||
err = v.kem.encrypt(ct, rand.Reader, pkB, msg[:]) | err = v.kem.encrypt(ct, rand.Reader, pkB, msg[:]) | ||||
CheckNoErr(t, err, "PKE roundtrip - encryption failed") | |||||
Ok(t, err, "PKE roundtrip - encryption failed") | |||||
ptLen, err := v.kem.decrypt(pt[:], skB, ct) | ptLen, err := v.kem.decrypt(pt[:], skB, ct) | ||||
CheckNoErr(t, err, "PKE roundtrip - decription failed") | |||||
Ok(t, err, "PKE roundtrip - decription failed") | |||||
if !bytes.Equal(pt[:ptLen], msg[:]) { | if !bytes.Equal(pt[:ptLen], msg[:]) { | ||||
t.Errorf("Decryption failed \n got : %X\n exp : %X", pt[:ptLen], msg) | t.Errorf("Decryption failed \n got : %X\n exp : %X", pt[:ptLen], msg) | ||||
@@ -132,13 +151,13 @@ func testPKEKeyGeneration(t *testing.T, v sikeVec) { | |||||
} | } | ||||
err = sk.Generate(rand.Reader) | err = sk.Generate(rand.Reader) | ||||
CheckNoErr(t, err, "PKE key generation") | |||||
Ok(t, err, "PKE key generation") | |||||
sk.GeneratePublicKey(pk) | sk.GeneratePublicKey(pk) | ||||
err = v.kem.encrypt(ct, rand.Reader, pk, msg[:]) | err = v.kem.encrypt(ct, rand.Reader, pk, msg[:]) | ||||
CheckNoErr(t, err, "PKE encryption") | |||||
Ok(t, err, "PKE encryption") | |||||
ptLen, err := v.kem.decrypt(pt[:], sk, ct) | ptLen, err := v.kem.decrypt(pt[:], sk, ct) | ||||
CheckNoErr(t, err, "PKE key decryption") | |||||
Ok(t, err, "PKE key decryption") | |||||
if !bytes.Equal(pt[:ptLen], msg[:]) { | if !bytes.Equal(pt[:ptLen], msg[:]) { | ||||
t.Fatalf("Decryption failed \n got : %X\n exp : %X", pt, msg) | t.Fatalf("Decryption failed \n got : %X\n exp : %X", pt, msg) | ||||
@@ -154,12 +173,15 @@ func testNegativePKE(t *testing.T, v sikeVec) { | |||||
// Generate key | // Generate key | ||||
err = sk.Generate(rand.Reader) | err = sk.Generate(rand.Reader) | ||||
CheckNoErr(t, err, "key generation") | |||||
Ok(t, err, "key generation") | |||||
sk.GeneratePublicKey(pk) | sk.GeneratePublicKey(pk) | ||||
// bytelen(msg) - 1 | // bytelen(msg) - 1 | ||||
err = v.kem.encrypt(ct, rand.Reader, pk, msg[:v.kem.params.KemSize+8-1]) | err = v.kem.encrypt(ct, rand.Reader, pk, msg[:v.kem.params.KemSize+8-1]) | ||||
CheckIsErr(t, err, "PKE encryption doesn't fail") | |||||
if err == nil { | |||||
t.Error(msg) | |||||
} | |||||
for _, v := range ct { | for _, v := range ct { | ||||
if v != 0 { | if v != 0 { | ||||
t.Fatal("Returned ciphertext must be not changed") | t.Fatal("Returned ciphertext must be not changed") | ||||
@@ -178,16 +200,16 @@ func testKEMRoundTrip(t *testing.T, pkB, skB []byte, v sikeVec) { | |||||
var ssBsz = v.kem.SharedSecretSize() | var ssBsz = v.kem.SharedSecretSize() | ||||
err = pk.Import(pkB) | err = pk.Import(pkB) | ||||
CheckNoErr(t, err, "Public key import failed") | |||||
Ok(t, err, "Public key import failed") | |||||
err = sk.Import(skB) | err = sk.Import(skB) | ||||
CheckNoErr(t, err, "Private key import failed") | |||||
Ok(t, err, "Private key import failed") | |||||
v.kem.Reset() | v.kem.Reset() | ||||
err = v.kem.Encapsulate(ct, ssE[:], pk) | err = v.kem.Encapsulate(ct, ssE[:], pk) | ||||
CheckNoErr(t, err, "Encapsulation failed") | |||||
Ok(t, err, "Encapsulation failed") | |||||
v.kem.Reset() | v.kem.Reset() | ||||
err = v.kem.Decapsulate(ssD[:ssBsz], sk, pk, ct) | err = v.kem.Decapsulate(ssD[:ssBsz], sk, pk, ct) | ||||
CheckNoErr(t, err, "Decapsulation failed") | |||||
Ok(t, err, "Decapsulation failed") | |||||
if !bytes.Equal(ssE[:v.kem.SharedSecretSize()], ssD[:v.kem.SharedSecretSize()]) { | if !bytes.Equal(ssE[:v.kem.SharedSecretSize()], ssD[:v.kem.SharedSecretSize()]) { | ||||
t.Errorf("Shared secrets from decapsulation and encapsulation differ [%s]", v.name) | t.Errorf("Shared secrets from decapsulation and encapsulation differ [%s]", v.name) | ||||
@@ -201,16 +223,16 @@ func testKEMKeyGeneration(t *testing.T, v sikeVec) { | |||||
sk := NewPrivateKey(v.id, KeyVariantSike) | sk := NewPrivateKey(v.id, KeyVariantSike) | ||||
pk := NewPublicKey(v.id, KeyVariantSike) | pk := NewPublicKey(v.id, KeyVariantSike) | ||||
CheckNoErr(t, sk.Generate(rand.Reader), "error: key generation") | |||||
Ok(t, sk.Generate(rand.Reader), "error: key generation") | |||||
sk.GeneratePublicKey(pk) | sk.GeneratePublicKey(pk) | ||||
// calculated shared secret | // calculated shared secret | ||||
v.kem.Reset() | v.kem.Reset() | ||||
err := v.kem.Encapsulate(ct, ssE[:], pk) | err := v.kem.Encapsulate(ct, ssE[:], pk) | ||||
CheckNoErr(t, err, "encapsulation failed") | |||||
Ok(t, err, "encapsulation failed") | |||||
v.kem.Reset() | v.kem.Reset() | ||||
err = v.kem.Decapsulate(ssD[:v.kem.SharedSecretSize()], sk, pk, ct) | err = v.kem.Decapsulate(ssD[:v.kem.SharedSecretSize()], sk, pk, ct) | ||||
CheckNoErr(t, err, "decapsulation failed") | |||||
Ok(t, err, "decapsulation failed") | |||||
if !bytes.Equal(ssE[:], ssD[:]) { | if !bytes.Equal(ssE[:], ssD[:]) { | ||||
t.Fatalf("KEM failed \n encapsulated: %X\n decapsulated: %X", ssD[:], ssE[:]) | t.Fatalf("KEM failed \n encapsulated: %X\n decapsulated: %X", ssD[:], ssE[:]) | ||||
@@ -226,33 +248,33 @@ func testNegativeKEM(t *testing.T, v sikeVec) { | |||||
sk := NewPrivateKey(v.id, KeyVariantSike) | sk := NewPrivateKey(v.id, KeyVariantSike) | ||||
pk := NewPublicKey(v.id, KeyVariantSike) | pk := NewPublicKey(v.id, KeyVariantSike) | ||||
CheckNoErr(t, sk.Generate(rand.Reader), "error: key generation") | |||||
Ok(t, sk.Generate(rand.Reader), "error: key generation") | |||||
sk.GeneratePublicKey(pk) | sk.GeneratePublicKey(pk) | ||||
v.kem.Reset() | v.kem.Reset() | ||||
err := v.kem.Encapsulate(ct, ssE[:], pk) | err := v.kem.Encapsulate(ct, ssE[:], pk) | ||||
CheckNoErr(t, err, "pre-requisite for a test failed") | |||||
Ok(t, err, "pre-requisite for a test failed") | |||||
// Try decapsulate too small ciphertext | // Try decapsulate too small ciphertext | ||||
v.kem.Reset() | v.kem.Reset() | ||||
CheckNoErr( | |||||
Ok( | |||||
t, | t, | ||||
CheckPanic(func() { _ = v.kem.Decapsulate(ssTmp[:ssBsz], sk, pk, ct[:len(ct)-2]) }), | |||||
testPanic(func() { _ = v.kem.Decapsulate(ssTmp[:ssBsz], sk, pk, ct[:len(ct)-2]) }), | |||||
"Decapsulation must panic if ciphertext is too small") | "Decapsulation must panic if ciphertext is too small") | ||||
ctTmp := make([]byte, len(ct)+1) | ctTmp := make([]byte, len(ct)+1) | ||||
// Try decapsulate too big ciphertext | // Try decapsulate too big ciphertext | ||||
v.kem.Reset() | v.kem.Reset() | ||||
CheckNoErr( | |||||
Ok( | |||||
t, | t, | ||||
CheckPanic(func() { _ = v.kem.Decapsulate(ssTmp[:ssBsz], sk, pk, ctTmp) }), | |||||
testPanic(func() { _ = v.kem.Decapsulate(ssTmp[:ssBsz], sk, pk, ctTmp) }), | |||||
"Decapsulation must panic if ciphertext is too big") | "Decapsulation must panic if ciphertext is too big") | ||||
// Change ciphertext | // Change ciphertext | ||||
ct[0] = ct[0] - 1 | ct[0] = ct[0] - 1 | ||||
v.kem.Reset() | v.kem.Reset() | ||||
err = v.kem.Decapsulate(ssD[:ssBsz], sk, pk, ct) | err = v.kem.Decapsulate(ssD[:ssBsz], sk, pk, ct) | ||||
CheckNoErr(t, err, "decapsulation returns error when invalid ciphertext provided") | |||||
Ok(t, err, "decapsulation returns error when invalid ciphertext provided") | |||||
if bytes.Equal(ssE[:], ssD[:]) { | if bytes.Equal(ssE[:], ssD[:]) { | ||||
// no idea how this could ever happen, but it would be very bad | // no idea how this could ever happen, but it would be very bad | ||||
@@ -263,16 +285,16 @@ func testNegativeKEM(t *testing.T, v sikeVec) { | |||||
pkSidh := NewPublicKey(v.id, KeyVariantSidhB) | pkSidh := NewPublicKey(v.id, KeyVariantSidhB) | ||||
prSidh := NewPrivateKey(v.id, KeyVariantSidhB) | prSidh := NewPrivateKey(v.id, KeyVariantSidhB) | ||||
v.kem.Reset() | v.kem.Reset() | ||||
CheckNoErr( | |||||
Ok( | |||||
t, | t, | ||||
CheckPanic(func() { _ = v.kem.Encapsulate(ct, ssE[:], pkSidh) }), | |||||
testPanic(func() { _ = v.kem.Encapsulate(ct, ssE[:], pkSidh) }), | |||||
"encapsulation accepts SIDH public key") | "encapsulation accepts SIDH public key") | ||||
// Try decapsulating with SIDH key | // Try decapsulating with SIDH key | ||||
v.kem.Reset() | v.kem.Reset() | ||||
CheckNoErr( | |||||
Ok( | |||||
t, | t, | ||||
CheckPanic(func() { _ = v.kem.Decapsulate(ssD[:ssBsz], prSidh, pk, ct) }), | |||||
testPanic(func() { _ = v.kem.Decapsulate(ssD[:ssBsz], prSidh, pk, ct) }), | |||||
"encapsulation accepts SIDH public key") | "encapsulation accepts SIDH public key") | ||||
} | } | ||||
@@ -287,18 +309,18 @@ func testNegativeKEMSameWrongResult(t *testing.T, v sikeVec) { | |||||
sk := NewPrivateKey(v.id, KeyVariantSike) | sk := NewPrivateKey(v.id, KeyVariantSike) | ||||
pk := NewPublicKey(v.id, KeyVariantSike) | pk := NewPublicKey(v.id, KeyVariantSike) | ||||
CheckNoErr(t, sk.Generate(rand.Reader), "error: key generation") | |||||
Ok(t, sk.Generate(rand.Reader), "error: key generation") | |||||
sk.GeneratePublicKey(pk) | sk.GeneratePublicKey(pk) | ||||
v.kem.Reset() | v.kem.Reset() | ||||
err := v.kem.Encapsulate(ct, ssE[:], pk) | err := v.kem.Encapsulate(ct, ssE[:], pk) | ||||
CheckNoErr(t, err, "pre-requisite for a test failed") | |||||
Ok(t, err, "pre-requisite for a test failed") | |||||
// make ciphertext wrong | // make ciphertext wrong | ||||
ct[0] = ct[0] - 1 | ct[0] = ct[0] - 1 | ||||
v.kem.Reset() | v.kem.Reset() | ||||
err = v.kem.Decapsulate(ssD1[:ssBsz], sk, pk, ct) | err = v.kem.Decapsulate(ssD1[:ssBsz], sk, pk, ct) | ||||
CheckNoErr(t, err, "pre-requisite for a test failed") | |||||
Ok(t, err, "pre-requisite for a test failed") | |||||
// change secret keysecond decapsulation must be done with same, but imported private key | // change secret keysecond decapsulation must be done with same, but imported private key | ||||
var expSk [common.MaxSikePrivateKeyBsz]byte | var expSk [common.MaxSikePrivateKeyBsz]byte | ||||
@@ -306,12 +328,12 @@ func testNegativeKEMSameWrongResult(t *testing.T, v sikeVec) { | |||||
// create new private key | // create new private key | ||||
sk = NewPrivateKey(v.id, KeyVariantSike) | sk = NewPrivateKey(v.id, KeyVariantSike) | ||||
CheckNoErr(t, sk.Import(expSk[:sk.Size()]), "import failed") | |||||
Ok(t, sk.Import(expSk[:sk.Size()]), "import failed") | |||||
// try decapsulating again. | // try decapsulating again. | ||||
v.kem.Reset() | v.kem.Reset() | ||||
err = v.kem.Decapsulate(ssD2[:ssBsz], sk, pk, ct) | err = v.kem.Decapsulate(ssD2[:ssBsz], sk, pk, ct) | ||||
CheckNoErr(t, err, "pre-requisite for a test failed") | |||||
Ok(t, err, "pre-requisite for a test failed") | |||||
// ssD1 must be same as ssD2 | // ssD1 must be same as ssD2 | ||||
if !bytes.Equal(ssD1[:], ssD2[:]) { | if !bytes.Equal(ssD1[:], ssD2[:]) { | ||||
@@ -335,7 +357,7 @@ func testKAT(t *testing.T, v sikeVec) { | |||||
} | } | ||||
err := v.kem.Decapsulate(ssGot, prvKey, pubKey, ct) | err := v.kem.Decapsulate(ssGot, prvKey, pubKey, ct) | ||||
CheckNoErr(t, err, "sike test: can't perform degcapsulation KAT") | |||||
Ok(t, err, "sike test: can't perform degcapsulation KAT") | |||||
if !bytes.Equal(ssGot, ssExpected) { | if !bytes.Equal(ssGot, ssExpected) { | ||||
t.Fatalf("KAT decapsulation failed\n") | t.Fatalf("KAT decapsulation failed\n") | ||||
} | } | ||||
@@ -365,7 +387,7 @@ func testKAT(t *testing.T, v sikeVec) { | |||||
var prvKey = NewPrivateKey(v.id, KeyVariantSike) | var prvKey = NewPrivateKey(v.id, KeyVariantSike) | ||||
var pubKey = NewPublicKey(v.id, KeyVariantSike) | var pubKey = NewPublicKey(v.id, KeyVariantSike) | ||||
var pubKeyBytes = make([]byte, pubKey.Size()) | var pubKeyBytes = make([]byte, pubKey.Size()) | ||||
CheckNoErr(t, prvKey.Import(sk), "Can't load KAT") | |||||
Ok(t, prvKey.Import(sk), "Can't load KAT") | |||||
// Generate public key | // Generate public key | ||||
prvKey.GeneratePublicKey(pubKey) | prvKey.GeneratePublicKey(pubKey) | ||||
@@ -439,9 +461,9 @@ func TestKEMRoundTrip(t *testing.T) { | |||||
for _, val := range tdataSike { | for _, val := range tdataSike { | ||||
// fmt.Printf("\tTesting: %s\n", val.name) | // fmt.Printf("\tTesting: %s\n", val.name) | ||||
pk, err := hex.DecodeString(val.PkB) | pk, err := hex.DecodeString(val.PkB) | ||||
CheckNoErr(t, err, "public key B not a number") | |||||
Ok(t, err, "public key B not a number") | |||||
sk, err := hex.DecodeString(val.PrB) | sk, err := hex.DecodeString(val.PrB) | ||||
CheckNoErr(t, err, "private key B not a number") | |||||
Ok(t, err, "private key B not a number") | |||||
testKEMRoundTrip(t, pk, sk, val) | testKEMRoundTrip(t, pk, sk, val) | ||||
} | } | ||||
} | } | ||||
@@ -1,7 +1,5 @@ | |||||
module github.com/henrydcase/nobs | module github.com/henrydcase/nobs | ||||
go 1.14 | |||||
go 1.12 | |||||
require ( | |||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e | |||||
) | |||||
require golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e |
@@ -1,217 +0,0 @@ | |||||
// [SIKE] http://www.sike.org/files/SIDH-spec.pdf | |||||
// [REF] https://github.com/Microsoft/PQCrypto-SIDH | |||||
package sike | |||||
import ( | |||||
"crypto/subtle" | |||||
"errors" | |||||
"io" | |||||
// TODO: Use implementation from xcrypto, once PR below merged | |||||
// https://go-review.googlesource.com/c/crypto/+/111281/ | |||||
. "github.com/henrydcase/nobs/dh/sidh" | |||||
cshake "github.com/henrydcase/nobs/hash/sha3" | |||||
) | |||||
// Constants used for cSHAKE customization | |||||
// Those values are different than in [SIKE] - they are encoded on 16bits. This is | |||||
// done in order for implementation to be compatible with [REF] and test vectors. | |||||
var G = []byte{0x00, 0x00} | |||||
var H = []byte{0x01, 0x00} | |||||
var F = []byte{0x02, 0x00} | |||||
// Generates cShake-256 sum | |||||
func cshakeSum(out, in, S []byte) { | |||||
h := cshake.NewCShake256(nil, S) | |||||
h.Write(in) | |||||
h.Read(out) | |||||
} | |||||
func encrypt(skA *PrivateKey, pkA, pkB *PublicKey, ptext []byte) ([]byte, error) { | |||||
var n [40]byte // n can is max 320-bit (see 1.4 of [SIKE]) | |||||
var ptextLen = len(ptext) | |||||
if pkB.Variant() != KeyVariant_SIKE { | |||||
return nil, errors.New("wrong key type") | |||||
} | |||||
j, err := DeriveSecret(skA, pkB) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
cshakeSum(n[:ptextLen], j, F) | |||||
for i, _ := range ptext { | |||||
n[i] ^= ptext[i] | |||||
} | |||||
ret := make([]byte, pkA.Size()+ptextLen) | |||||
copy(ret, pkA.Export()) | |||||
copy(ret[pkA.Size():], n[:ptextLen]) | |||||
return ret, nil | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
// PKE interface | |||||
// | |||||
// Uses SIKE public key to encrypt plaintext. Requires cryptographically secure PRNG | |||||
// Returns ciphertext in case encryption succeeds. Returns error in case PRNG fails | |||||
// or wrongly formated input was provided. | |||||
func Encrypt(rng io.Reader, pub *PublicKey, ptext []byte) ([]byte, error) { | |||||
var params = pub.Params() | |||||
var ptextLen = uint(len(ptext)) | |||||
// c1 must be security level + 64 bits (see [SIKE] 1.4 and 4.3.3) | |||||
if ptextLen != (params.KemSize + 8) { | |||||
return nil, errors.New("Unsupported message length") | |||||
} | |||||
skA := NewPrivateKey(params.Id, KeyVariant_SIDH_A) | |||||
err := skA.Generate(rng) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
pkA := skA.GeneratePublicKey() | |||||
return encrypt(skA, pkA, pub, ptext) | |||||
} | |||||
// Uses SIKE private key to decrypt ciphertext. Returns plaintext in case | |||||
// decryption succeeds or error in case unexptected input was provided. | |||||
// Constant time | |||||
func Decrypt(prv *PrivateKey, ctext []byte) ([]byte, error) { | |||||
var params = prv.Params() | |||||
var n [40]byte // n can is max 320-bit (see 1.4 of [SIKE]) | |||||
var c1_len int | |||||
var pk_len = params.PublicKeySize | |||||
if prv.Variant() != KeyVariant_SIKE { | |||||
return nil, errors.New("wrong key type") | |||||
} | |||||
// ctext is a concatenation of (pubkey_A || c1=ciphertext) | |||||
// it must be security level + 64 bits (see [SIKE] 1.4 and 4.3.3) | |||||
c1_len = len(ctext) - pk_len | |||||
if c1_len != (int(params.KemSize) + 8) { | |||||
return nil, errors.New("wrong size of cipher text") | |||||
} | |||||
c0 := NewPublicKey(params.Id, KeyVariant_SIDH_A) | |||||
err := c0.Import(ctext[:pk_len]) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
j, err := DeriveSecret(prv, c0) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
cshakeSum(n[:c1_len], j, F) | |||||
for i, _ := range n[:c1_len] { | |||||
n[i] ^= ctext[pk_len+i] | |||||
} | |||||
return n[:c1_len], nil | |||||
} | |||||
// ----------------------------------------------------------------------------- | |||||
// KEM interface | |||||
// | |||||
// 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 Encapsulate(rng io.Reader, pub *PublicKey) (ctext []byte, secret []byte, err error) { | |||||
var params = pub.Params() | |||||
// Buffer for random, secret message | |||||
var ptext = make([]byte, params.MsgLen) | |||||
// r = G(ptext||pub) | |||||
var r = make([]byte, params.A.SecretByteLen) | |||||
// Resulting shared secret | |||||
secret = make([]byte, params.KemSize) | |||||
// Generate ephemeral value | |||||
_, err = io.ReadFull(rng, ptext) | |||||
if err != nil { | |||||
return nil, nil, err | |||||
} | |||||
h := cshake.NewCShake256(nil, G) | |||||
h.Write(ptext) | |||||
h.Write(pub.Export()) | |||||
h.Read(r) | |||||
// 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) | |||||
err = skA.Import(r) | |||||
if err != nil { | |||||
return nil, nil, err | |||||
} | |||||
pkA := skA.GeneratePublicKey() | |||||
ctext, err = encrypt(skA, pkA, pub, ptext) | |||||
if err != nil { | |||||
return nil, nil, err | |||||
} | |||||
// K = H(ptext||(c0||c1)) | |||||
h = cshake.NewCShake256(nil, H) | |||||
h.Write(ptext) | |||||
h.Write(ctext) | |||||
h.Read(secret) | |||||
return ctext, secret, nil | |||||
} | |||||
// Decapsulate given the keypair and ciphertext as inputs, Decapsulate outputs a shared | |||||
// secret if plaintext verifies correctly, otherwise function outputs random value. | |||||
// Decapsulation may fail in case input is wrongly formated. | |||||
// 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.A.SecretByteLen) | |||||
// Resulting shared secret | |||||
var secret = make([]byte, params.KemSize) | |||||
var skA = NewPrivateKey(params.Id, KeyVariant_SIDH_A) | |||||
m, err := Decrypt(prv, ctext) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
// r' = G(m'||pub) | |||||
h := cshake.NewCShake256(nil, G) | |||||
h.Write(m) | |||||
h.Write(pub.Export()) | |||||
h.Read(r) | |||||
// 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) | |||||
// Never fails | |||||
pkA := skA.GeneratePublicKey() | |||||
c0 := pkA.Export() | |||||
h = cshake.NewCShake256(nil, H) | |||||
if subtle.ConstantTimeCompare(c0, ctext[:len(c0)]) == 1 { | |||||
h.Write(m) | |||||
} else { | |||||
// 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 | |||||
// to other party. Without this check, it is possible to recover a secret, by | |||||
// providing series of invalid ciphertexts. It is also important that in case | |||||
// | |||||
// See more details in "On the security of supersingular isogeny cryptosystems" | |||||
// (S. Galbraith, et al., 2016, ePrint #859). | |||||
h.Write(prv.S) | |||||
} | |||||
h.Write(ctext) | |||||
h.Read(secret) | |||||
return secret, nil | |||||
} |
@@ -1,378 +0,0 @@ | |||||
package sike | |||||
import ( | |||||
"testing" | |||||
"bufio" | |||||
"bytes" | |||||
"encoding/hex" | |||||
"io" | |||||
"os" | |||||
"strings" | |||||
"fmt" | |||||
rand "crypto/rand" | |||||
. "github.com/henrydcase/nobs/dh/sidh" | |||||
) | |||||
const ( | |||||
PkB = "7C55E268665504B9A11A1B30B4363A4957960AD015A7B74DF39FB0141A95CC51A4BEBBB48452EF0C881220D68CB5FF904C0549F05F06BF49A520E684DD610A7E121B420C751B789BDCDB8B6EC136BA0CE74EB6904906057EA7343839EA35FAF2C3D7BE76C81DCA4DF0850CE5F111FF9FF97242EC5520310D7F90A004BACFD75408CBFE8948232A9CCF035136DE3691D9BEF110C3081AADF0D2328CE2CC94998D8AE94D6575083FAFA045F50201FCE841D01C214CC8BBEFCC701484215EA70518204C76A0DA89BEAF0B066F6FD9E78A2C908CF0AFF74E0B55477190F918397F0CF3A537B7911DA846196AD914114A15C2F3C1062D78B19D23348C3D3D4A9C2B2018B382CC44544DA2FA263EB6212D2D13F254216DE002D4AEA55C75C5349A681D7A809BCC29C4CAE1168AC790321FF7429FAAC2FC09465F93E10B9DD970901A1B1D045DDAC9D7B901E00F29AA9F2C87C8EF848E80B7B290ECF85D6BB4C7E975A939A7AFB63069F900A75C9B7B71C2E7472C21A87AB604B6372D4EBEC5974A711281A819636D8FA3E6608F2B81F35599BBB4A1EB5CBD8F743587550F8CE3A809F5C9C399DD52B2D15F217A36F3218C772FD4E67F67D526DEBE1D31FEC4634927A873A1A6CFE55FF1E35AB72EBBD22E3CDD9D2640813345015BB6BD25A6977D0391D4D78998DD178155FEBF247BED3A9F83EAF3346BA90098B908B2359B60491C94330626709D235D1CFB7C87DCA779CFBA23DA280DC06FAEA0FDB3773B0C6391F889D803B7C04AC6AB27375B440336789823176C57" | |||||
PrB = "00010203040506070809000102030405060708090001020304050607080901028626ED79D451140800E03B59B956F8210E556067407D13DC90FA9E8B872BFB8FAB0A7289852106E40538D3575C500201" | |||||
) | |||||
var params = Params(FP_751) | |||||
type MultiIdTestingFunc func(*testing.T, uint8) | |||||
func Do(f MultiIdTestingFunc, t *testing.T) { | |||||
for id, val := range tdata { | |||||
params = Params(id) | |||||
fmt.Printf("\tTesting: %s\n", val.name) | |||||
f(t, id) | |||||
} | |||||
} | |||||
var tdata = map[uint8]struct { | |||||
name string | |||||
KatFile string | |||||
PkB string | |||||
PrB string | |||||
}{ | |||||
FP_503: { | |||||
"P-503", | |||||
"../../etc/PQCkemKAT_434.rsp", | |||||
"F11CF893CE4F794216B11A75B0B2981F8DB3FC8550A75C86DB2279FD4CB445E2F4D21F7380570832963F1445AB898267EC1B84196CAC1A84566D7C4D334505C5AB98D638B2E1A5766F5F716FDF1177AB864D2E2CE10BF8DC3D0A3CAFA05B587D746F5CC78E32F283C035886A96698BDCF0F2CAE0B5D4B9C725A3EB2EA13AA43AEC99488962F8B9A5038DD655C0237023CF21002E3E19B1A993C9118DDC74A07B4F9585C0BCEA6E401A384C4F411A5A6E97DA4E53DA6C8F39F62304F201EC93EDFA76FDA6CE557C4389D5ACE744ED5578A391B6AF01F00F93F4EC7CE41F5C5D1FB11D367C0F2CEB4DD9A92BD8948D777F4285EEBB0870C9C39BD0523804A9FDDFCDE61810D8B958E172702EB97D10A98E9FDDFBE1FC2146230AA26B7FFF48B70ECFDBEF9E7CBBCC12308992FDEF8CA0CD9F0A387F1B68D661A46C37D7FAB9A4ECDE63BEF0A3D7732CA7A8E18C88EBEDF546E842E27CC04FA78A8C03DF22A747E2D627FC9EB3FD8A57337BE759D1957C1D31FCA3FEE6D171192B0C", | |||||
"9BC5315580207C6C16DCF3A30C48DAF278DE12E8C27DF6735A4D0A8A41C4F666854E9B13673071CEB2FD61DEF9A850C211E7C50071B1DD0D"}, | |||||
FP_751: { | |||||
"P-751", | |||||
"../../etc/PQCkemKAT_644.rsp", | |||||
"7C55E268665504B9A11A1B30B4363A4957960AD015A7B74DF39FB0141A95CC51A4BEBBB48452EF0C881220D68CB5FF904C0549F05F06BF49A520E684DD610A7E121B420C751B789BDCDB8B6EC136BA0CE74EB6904906057EA7343839EA35FAF2C3D7BE76C81DCA4DF0850CE5F111FF9FF97242EC5520310D7F90A004BACFD75408CBFE8948232A9CCF035136DE3691D9BEF110C3081AADF0D2328CE2CC94998D8AE94D6575083FAFA045F50201FCE841D01C214CC8BBEFCC701484215EA70518204C76A0DA89BEAF0B066F6FD9E78A2C908CF0AFF74E0B55477190F918397F0CF3A537B7911DA846196AD914114A15C2F3C1062D78B19D23348C3D3D4A9C2B2018B382CC44544DA2FA263EB6212D2D13F254216DE002D4AEA55C75C5349A681D7A809BCC29C4CAE1168AC790321FF7429FAAC2FC09465F93E10B9DD970901A1B1D045DDAC9D7B901E00F29AA9F2C87C8EF848E80B7B290ECF85D6BB4C7E975A939A7AFB63069F900A75C9B7B71C2E7472C21A87AB604B6372D4EBEC5974A711281A819636D8FA3E6608F2B81F35599BBB4A1EB5CBD8F743587550F8CE3A809F5C9C399DD52B2D15F217A36F3218C772FD4E67F67D526DEBE1D31FEC4634927A873A1A6CFE55FF1E35AB72EBBD22E3CDD9D2640813345015BB6BD25A6977D0391D4D78998DD178155FEBF247BED3A9F83EAF3346BA90098B908B2359B60491C94330626709D235D1CFB7C87DCA779CFBA23DA280DC06FAEA0FDB3773B0C6391F889D803B7C04AC6AB27375B440336789823176C57", | |||||
"00010203040506070809000102030405060708090001020304050607080901028626ED79D451140800E03B59B956F8210E556067407D13DC90FA9E8B872BFB8FAB0A7289852106E40538D3575C500201"}, | |||||
} | |||||
// Fail if err !=nil. Display msg as an error message | |||||
func checkErr(t testing.TB, err error, msg string) { | |||||
if err != nil { | |||||
t.Errorf("%s [%s]", msg, err) | |||||
} | |||||
} | |||||
// Encrypt, Decrypt, check if input/output plaintext is the same | |||||
func testPKERoundTrip(t *testing.T, id uint8) { | |||||
// Message to be encrypted | |||||
var params = Params(id) | |||||
var msg = make([]byte, params.MsgLen) | |||||
for i, _ := range msg { | |||||
msg[i] = byte(i) | |||||
} | |||||
// Import keys | |||||
pkB := NewPublicKey(params.Id, KeyVariant_SIKE) | |||||
skB := NewPrivateKey(params.Id, KeyVariant_SIKE) | |||||
pk_hex, err := hex.DecodeString(tdata[id].PkB) | |||||
if err != nil { | |||||
t.Fatal(err) | |||||
} | |||||
sk_hex, err := hex.DecodeString(tdata[id].PrB) | |||||
if err != nil { | |||||
t.Fatal(err) | |||||
} | |||||
if pkB.Import(pk_hex) != nil || skB.Import(sk_hex) != nil { | |||||
t.Error("Import") | |||||
} | |||||
ct, err := Encrypt(rand.Reader, pkB, msg[:]) | |||||
if err != nil { | |||||
t.Fatal(err) | |||||
} | |||||
pt, err := Decrypt(skB, ct) | |||||
if err != nil { | |||||
t.Fatal(err) | |||||
} | |||||
if !bytes.Equal(pt[:], msg[:]) { | |||||
t.Errorf("Decryption failed \n got : %X\n exp : %X", pt, msg) | |||||
} | |||||
} | |||||
// Generate key and check if can encrypt | |||||
func testPKEKeyGeneration(t *testing.T, id uint8) { | |||||
// Message to be encrypted | |||||
var params = Params(id) | |||||
var msg = make([]byte, params.MsgLen) | |||||
var err error | |||||
for i, _ := range msg { | |||||
msg[i] = byte(i) | |||||
} | |||||
sk := NewPrivateKey(id, KeyVariant_SIKE) | |||||
err = sk.Generate(rand.Reader) | |||||
checkErr(t, err, "PEK key generation") | |||||
pk := sk.GeneratePublicKey() | |||||
// Try to encrypt | |||||
ct, err := Encrypt(rand.Reader, pk, msg[:]) | |||||
checkErr(t, err, "PEK encryption") | |||||
pt, err := Decrypt(sk, ct) | |||||
checkErr(t, err, "PEK key decryption") | |||||
if !bytes.Equal(pt[:], msg[:]) { | |||||
t.Fatalf("Decryption failed \n got : %X\n exp : %X", pt, msg) | |||||
} | |||||
} | |||||
func testNegativePKE(t *testing.T, id uint8) { | |||||
var msg [40]byte | |||||
var err error | |||||
var params = Params(id) | |||||
// Generate key | |||||
sk := NewPrivateKey(params.Id, KeyVariant_SIKE) | |||||
err = sk.Generate(rand.Reader) | |||||
checkErr(t, err, "key generation") | |||||
pk := sk.GeneratePublicKey() | |||||
// bytelen(msg) - 1 | |||||
ct, err := Encrypt(rand.Reader, pk, msg[:params.KemSize+8-1]) | |||||
if err == nil { | |||||
t.Fatal("Error hasn't been returned") | |||||
} | |||||
if ct != nil { | |||||
t.Fatal("Ciphertext must be nil") | |||||
} | |||||
// KemSize - 1 | |||||
pt, err := Decrypt(sk, msg[:params.KemSize+8-1]) | |||||
if err == nil { | |||||
t.Fatal("Error hasn't been returned") | |||||
} | |||||
if pt != nil { | |||||
t.Fatal("Ciphertext must be nil") | |||||
} | |||||
} | |||||
func testKEMRoundTrip(t *testing.T, pkB, skB []byte, id uint8) { | |||||
// Import keys | |||||
pk := NewPublicKey(id, KeyVariant_SIKE) | |||||
sk := NewPrivateKey(id, KeyVariant_SIKE) | |||||
if pk.Import(pkB) != nil || sk.Import(skB) != nil { | |||||
t.Error("Import failed") | |||||
} | |||||
ct, ss_e, err := Encapsulate(rand.Reader, pk) | |||||
if err != nil { | |||||
t.Error("Encapsulate failed") | |||||
} | |||||
ss_d, err := Decapsulate(sk, pk, ct) | |||||
if err != nil { | |||||
t.Error("Decapsulate failed") | |||||
} | |||||
if !bytes.Equal(ss_e, ss_d) { | |||||
t.Error("Shared secrets from decapsulation and encapsulation differ") | |||||
} | |||||
} | |||||
func TestKEMRoundTrip(t *testing.T) { | |||||
for id, val := range tdata { | |||||
fmt.Printf("\tTesting: %s\n", val.name) | |||||
pk, err := hex.DecodeString(tdata[id].PkB) | |||||
checkErr(t, err, "public key B not a number") | |||||
sk, err := hex.DecodeString(tdata[id].PrB) | |||||
checkErr(t, err, "private key B not a number") | |||||
testKEMRoundTrip(t, pk, sk, id) | |||||
} | |||||
} | |||||
func testKEMKeyGeneration(t *testing.T, id uint8) { | |||||
// Generate key | |||||
sk := NewPrivateKey(id, KeyVariant_SIKE) | |||||
checkErr(t, sk.Generate(rand.Reader), "error: key generation") | |||||
pk := sk.GeneratePublicKey() | |||||
// calculated shared secret | |||||
ct, ss_e, err := Encapsulate(rand.Reader, pk) | |||||
checkErr(t, err, "encapsulation failed") | |||||
ss_d, err := Decapsulate(sk, pk, ct) | |||||
checkErr(t, err, "decapsulation failed") | |||||
if !bytes.Equal(ss_e, ss_d) { | |||||
t.Fatalf("KEM failed \n encapsulated: %X\n decapsulated: %X", ss_d, ss_e) | |||||
} | |||||
} | |||||
func testNegativeKEM(t *testing.T, id uint8) { | |||||
sk := NewPrivateKey(id, KeyVariant_SIKE) | |||||
checkErr(t, sk.Generate(rand.Reader), "error: key generation") | |||||
pk := sk.GeneratePublicKey() | |||||
ct, ss_e, err := Encapsulate(rand.Reader, pk) | |||||
checkErr(t, err, "pre-requisite for a test failed") | |||||
ct[0] = ct[0] - 1 | |||||
ss_d, err := Decapsulate(sk, pk, ct) | |||||
checkErr(t, err, "decapsulation returns error when invalid ciphertext provided") | |||||
if bytes.Equal(ss_e, ss_d) { | |||||
// no idea how this could ever happen, but it would be very bad | |||||
t.Error("critical error") | |||||
} | |||||
// Try encapsulating with SIDH key | |||||
pkSidh := NewPublicKey(params.Id, KeyVariant_SIDH_B) | |||||
prSidh := NewPrivateKey(params.Id, KeyVariant_SIDH_B) | |||||
_, _, err = Encapsulate(rand.Reader, pkSidh) | |||||
if err == nil { | |||||
t.Error("encapsulation accepts SIDH public key") | |||||
} | |||||
// Try decapsulating with SIDH key | |||||
_, err = Decapsulate(prSidh, pk, ct) | |||||
if err == nil { | |||||
t.Error("decapsulation accepts SIDH private key key") | |||||
} | |||||
} | |||||
// In case invalid ciphertext is provided, SIKE's decapsulation must | |||||
// return same (but unpredictable) result for a given key. | |||||
func testNegativeKEMSameWrongResult(t *testing.T, id uint8) { | |||||
sk := NewPrivateKey(id, KeyVariant_SIKE) | |||||
checkErr(t, sk.Generate(rand.Reader), "error: key generation") | |||||
pk := sk.GeneratePublicKey() | |||||
ct, encSs, err := Encapsulate(rand.Reader, pk) | |||||
checkErr(t, err, "pre-requisite for a test failed") | |||||
// make ciphertext wrong | |||||
ct[0] = ct[0] - 1 | |||||
decSs1, err := Decapsulate(sk, pk, ct) | |||||
checkErr(t, err, "pre-requisite for a test failed") | |||||
// second decapsulation must be done with same, but imported private key | |||||
expSk := sk.Export() | |||||
// creat new private key | |||||
sk = NewPrivateKey(params.Id, KeyVariant_SIKE) | |||||
err = sk.Import(expSk) | |||||
checkErr(t, err, "import failed") | |||||
// try decapsulating again. ss2 must be same as ss1 and different than | |||||
// original plaintext | |||||
decSs2, err := Decapsulate(sk, pk, ct) | |||||
checkErr(t, err, "pre-requisite for a test failed") | |||||
if !bytes.Equal(decSs1, decSs2) { | |||||
t.Error("decapsulation is insecure") | |||||
} | |||||
if bytes.Equal(encSs, decSs1) || bytes.Equal(encSs, decSs2) { | |||||
// this test requires that decapsulation returns wrong result | |||||
t.Errorf("test implementation error") | |||||
} | |||||
} | |||||
func readAndCheckLine(r *bufio.Reader) []byte { | |||||
// Read next line from buffer | |||||
line, isPrefix, err := r.ReadLine() | |||||
if err != nil || isPrefix { | |||||
panic("Wrong format of input file") | |||||
} | |||||
// Function expects that line is in format "KEY = HEX_VALUE". Get | |||||
// value, which should be a hex string | |||||
hexst := strings.Split(string(line), "=")[1] | |||||
hexst = strings.TrimSpace(hexst) | |||||
// Convert value to byte string | |||||
ret, err := hex.DecodeString(hexst) | |||||
if err != nil { | |||||
panic("Wrong format of input file") | |||||
} | |||||
return ret | |||||
} | |||||
func testKeygen(pk, sk []byte) bool { | |||||
// Import provided private key | |||||
var prvKey = NewPrivateKey(params.Id, KeyVariant_SIKE) | |||||
if prvKey.Import(sk) != nil { | |||||
panic("sike test: can't load KAT") | |||||
} | |||||
// Generate public key | |||||
pubKey := prvKey.GeneratePublicKey() | |||||
return bytes.Equal(pubKey.Export(), pk) | |||||
} | |||||
func testDecapsulation(pk, sk, ct, ssExpected []byte) bool { | |||||
var pubKey = NewPublicKey(params.Id, KeyVariant_SIKE) | |||||
var prvKey = NewPrivateKey(params.Id, KeyVariant_SIKE) | |||||
if pubKey.Import(pk) != nil || prvKey.Import(sk) != nil { | |||||
panic("sike test: can't load KAT") | |||||
} | |||||
ssGot, err := Decapsulate(prvKey, pubKey, ct) | |||||
if err != nil { | |||||
panic("sike test: can't perform decapsulation KAT") | |||||
} | |||||
if err != nil { | |||||
return false | |||||
} | |||||
return bytes.Equal(ssGot, ssExpected) | |||||
} | |||||
func testSIKE_KAT(t *testing.T, id uint8) { | |||||
f, err := os.Open(tdata[id].KatFile) | |||||
if err != nil { | |||||
t.Fatal(err) | |||||
} | |||||
r := bufio.NewReader(f) | |||||
for { | |||||
line, isPrefix, err := r.ReadLine() | |||||
if err != nil || isPrefix { | |||||
if err == io.EOF { | |||||
break | |||||
} else { | |||||
t.Fatal(err) | |||||
} | |||||
} | |||||
if len(strings.TrimSpace(string(line))) == 0 || line[0] == '#' { | |||||
continue | |||||
} | |||||
// count | |||||
count := strings.Split(string(line), "=")[1] | |||||
// seed | |||||
_ = readAndCheckLine(r) | |||||
// pk | |||||
pk := readAndCheckLine(r) | |||||
// 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.B.SecretByteLen)] | |||||
// ct | |||||
ct := readAndCheckLine(r) | |||||
// ss | |||||
ss := readAndCheckLine(r) | |||||
if !testKeygen(pk, sk) { | |||||
t.Fatalf("KAT keygen form private failed at %s\n", count) | |||||
} | |||||
if !testDecapsulation(pk, sk, ct, ss) { | |||||
t.Fatalf("KAT decapsulation failed at %s\n", count) | |||||
} | |||||
// aditionally test roundtrip with a keypair | |||||
testKEMRoundTrip(t, pk, sk, id) | |||||
} | |||||
} | |||||
// Interface to "testing" | |||||
func TestPKEKeyGeneration(t *testing.T) { Do(testPKEKeyGeneration, t) } | |||||
func TestPKERoundTrip(t *testing.T) { Do(testPKERoundTrip, t) } | |||||
func TestNegativePKE(t *testing.T) { Do(testNegativePKE, t) } | |||||
func TestKEMKeyGeneration(t *testing.T) { Do(testKEMKeyGeneration, t) } | |||||
func TestNegativeKEM(t *testing.T) { Do(testNegativeKEM, t) } | |||||
func TestSIKE_KAT(t *testing.T) { Do(testSIKE_KAT, t) } | |||||
func TestNegativeKEMSameWrongResult(t *testing.T) { Do(testNegativeKEMSameWrongResult, t) } |