From bc32024729f7ac2574abf9912c9f9d1188337ddd Mon Sep 17 00:00:00 2001 From: Henry Case Date: Thu, 14 May 2020 09:51:20 +0100 Subject: [PATCH] sidh: updates (#31) --- Makefile | 16 +- dh/sidh/internal/doc.go | 1 + dh/sidh/internal/p434/arith_amd64_test.go | 2 +- dh/sidh/internal/p434/doc.go | 1 + dh/sidh/sidh_test.go | 26 +- dh/sidh/sike.go | 6 +- dh/sidh/sike_test.go | 100 +++--- go.mod | 6 +- kem/sike/sike.go | 217 ------------- kem/sike/sike_test.go | 378 ---------------------- 10 files changed, 90 insertions(+), 663 deletions(-) create mode 100644 dh/sidh/internal/doc.go create mode 100644 dh/sidh/internal/p434/doc.go delete mode 100644 kem/sike/sike.go delete mode 100644 kem/sike/sike_test.go diff --git a/Makefile b/Makefile index 56c177e..e7534be 100644 --- a/Makefile +++ b/Makefile @@ -29,15 +29,9 @@ ifeq ($(DBG),1) OPTS_ENV+= GOTRACEBACK=crash # enable core dumps endif -test: +test: $(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: $(GO) test \ -coverprofile=coverage.txt -covermode=atomic $(OPTS) $(TEST_PATH) @@ -45,10 +39,6 @@ cover: bench: $(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: rm -rf $(VENDOR_DIR) 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 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: $(GO) tool pprof cpu.out diff --git a/dh/sidh/internal/doc.go b/dh/sidh/internal/doc.go new file mode 100644 index 0000000..729e69b --- /dev/null +++ b/dh/sidh/internal/doc.go @@ -0,0 +1 @@ +package sidh diff --git a/dh/sidh/internal/p434/arith_amd64_test.go b/dh/sidh/internal/p434/arith_amd64_test.go index ec8a001..9ae0549 100644 --- a/dh/sidh/internal/p434/arith_amd64_test.go +++ b/dh/sidh/internal/p434/arith_amd64_test.go @@ -10,7 +10,7 @@ import ( "testing" "testing/quick" - "github.com/henrydcase/nobs/sidh/internal/common" + "github.com/henrydcase/nobs/dh/sidh/internal/common" "golang.org/x/sys/cpu" ) diff --git a/dh/sidh/internal/p434/doc.go b/dh/sidh/internal/p434/doc.go new file mode 100644 index 0000000..dfe8685 --- /dev/null +++ b/dh/sidh/internal/p434/doc.go @@ -0,0 +1 @@ +package p434 diff --git a/dh/sidh/sidh_test.go b/dh/sidh/sidh_test.go index 8160d0f..954236d 100644 --- a/dh/sidh/sidh_test.go +++ b/dh/sidh/sidh_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/henrydcase/nobs/dh/sidh/internal/common" - . "github.com/henrydcase/nobs/internal/test" ) /* ------------------------------------------------------------------------- @@ -24,6 +23,13 @@ type sidhVec struct { PrB string } +func checkErr(t testing.TB, err error, msg string) { + t.Helper() + if err != nil { + t.Error(msg) + } +} + var tdataSidh = map[uint8]sidhVec{ Fp434: { id: Fp434, @@ -203,9 +209,9 @@ func testRoundtrip(t *testing.T, v sidhVec) { // Generate private keys err = prvA.Generate(rand.Reader) - CheckNoErr(t, err, "key generation failed") + checkErr(t, err, "key generation failed") err = prvB.Generate(rand.Reader) - CheckNoErr(t, err, "key generation failed") + checkErr(t, err, "key generation failed") // Generate public keys prvA.GeneratePublicKey(pubA) @@ -241,11 +247,11 @@ func testKeyAgreement(t *testing.T, v sidhVec) { // Negative case dec, err := hex.DecodeString(v.PkA) - CheckNoErr(t, err, "decoding failed") + checkErr(t, err, "decoding failed") dec[0] = ^dec[0] err = alicePublic.Import(dec) - CheckNoErr(t, err, "import failed") + checkErr(t, err, "import failed") bobPrivate.DeriveSecret(s1, alicePublic) alicePrivate.DeriveSecret(s2, bobPublic) @@ -261,16 +267,16 @@ func testImportExport(t *testing.T, v sidhVec) { // Import keys aHex, err := hex.DecodeString(v.PkA) - CheckNoErr(t, err, "invalid hex-number provided") + checkErr(t, err, "invalid hex-number provided") err = a.Import(aHex) - CheckNoErr(t, err, "import failed") + checkErr(t, err, "import failed") bHex, err := hex.DecodeString(v.PkB) - CheckNoErr(t, err, "invalid hex-number provided") + checkErr(t, err, "invalid hex-number provided") err = b.Import(bHex) - CheckNoErr(t, err, "import failed") + checkErr(t, err, "import failed") aBytes := make([]byte, a.Size()) bBytes := make([]byte, b.Size()) @@ -313,7 +319,7 @@ func testPrivateKeyBelowMax(t *testing.T, vec sidhVec) { // Do same test 1000 times for i := 0; i < 1000; i++ { 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() prv.Export(secretBytes) diff --git a/dh/sidh/sike.go b/dh/sidh/sike.go index 0d77ec4..bac8ffe 100644 --- a/dh/sidh/sike.go +++ b/dh/sidh/sike.go @@ -6,7 +6,7 @@ import ( "io" "github.com/henrydcase/nobs/dh/sidh/internal/common" - "github.com/henrydcase/nobs/internal/shake" + "github.com/henrydcase/nobs/hash/sha3" ) // SIKE KEM interface. @@ -16,7 +16,7 @@ type KEM struct { msg []byte secretBytes []byte params *common.SidhParams - shake shake.Shake + shake sha3.ShakeHash } // NewSike434 instantiates SIKE/p434 KEM. @@ -47,7 +47,7 @@ func (c *KEM) Allocate(id uint8, rng io.Reader) { c.params = common.Params(id) c.msg = make([]byte, c.params.MsgLen) c.secretBytes = make([]byte, c.params.A.SecretByteLen) - c.shake = shake.NewShake256() + c.shake = sha3.NewShake256() c.allocated = true } diff --git a/dh/sidh/sike_test.go b/dh/sidh/sike_test.go index cfa63b1..7cec5a0 100644 --- a/dh/sidh/sike_test.go +++ b/dh/sidh/sike_test.go @@ -5,6 +5,7 @@ import ( "bytes" "crypto/rand" "encoding/hex" + "errors" "fmt" "io" "os" @@ -12,7 +13,6 @@ import ( "testing" "github.com/henrydcase/nobs/dh/sidh/internal/common" - . "github.com/henrydcase/nobs/internal/test" ) type sikeVec struct { @@ -24,6 +24,25 @@ type sikeVec struct { 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{ Fp434: { Fp434, "P-434", NewSike434(rand.Reader), @@ -99,17 +118,17 @@ func testPKERoundTrip(t *testing.T, v sikeVec) { pkB := NewPublicKey(params.ID, KeyVariantSike) skB := NewPrivateKey(params.ID, KeyVariantSike) pkHex, err := hex.DecodeString(v.PkB) - CheckNoErr(t, err, "Test vector wrong") + Ok(t, err, "Test vector wrong") skHex, err := hex.DecodeString(v.PrB) - CheckNoErr(t, err, "Test vector wrong") + Ok(t, err, "Test vector wrong") err = pkB.Import(pkHex) - CheckNoErr(t, err, "Public key import failed") + Ok(t, err, "Public key import failed") 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[:]) - CheckNoErr(t, err, "PKE roundtrip - encryption failed") + Ok(t, err, "PKE roundtrip - encryption failed") 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[:]) { 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) - CheckNoErr(t, err, "PKE key generation") + Ok(t, err, "PKE key generation") sk.GeneratePublicKey(pk) 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) - CheckNoErr(t, err, "PKE key decryption") + Ok(t, err, "PKE key decryption") if !bytes.Equal(pt[:ptLen], 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 err = sk.Generate(rand.Reader) - CheckNoErr(t, err, "key generation") + Ok(t, err, "key generation") sk.GeneratePublicKey(pk) // bytelen(msg) - 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 { if v != 0 { 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() err = pk.Import(pkB) - CheckNoErr(t, err, "Public key import failed") + Ok(t, err, "Public key import failed") err = sk.Import(skB) - CheckNoErr(t, err, "Private key import failed") + Ok(t, err, "Private key import failed") v.kem.Reset() err = v.kem.Encapsulate(ct, ssE[:], pk) - CheckNoErr(t, err, "Encapsulation failed") + Ok(t, err, "Encapsulation failed") v.kem.Reset() 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()]) { 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) 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) // calculated shared secret v.kem.Reset() err := v.kem.Encapsulate(ct, ssE[:], pk) - CheckNoErr(t, err, "encapsulation failed") + Ok(t, err, "encapsulation failed") v.kem.Reset() 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[:]) { 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) 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) v.kem.Reset() 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 v.kem.Reset() - CheckNoErr( + Ok( 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") ctTmp := make([]byte, len(ct)+1) // Try decapsulate too big ciphertext v.kem.Reset() - CheckNoErr( + Ok( 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") // Change ciphertext ct[0] = ct[0] - 1 v.kem.Reset() 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[:]) { // 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) prSidh := NewPrivateKey(v.id, KeyVariantSidhB) v.kem.Reset() - CheckNoErr( + Ok( t, - CheckPanic(func() { _ = v.kem.Encapsulate(ct, ssE[:], pkSidh) }), + testPanic(func() { _ = v.kem.Encapsulate(ct, ssE[:], pkSidh) }), "encapsulation accepts SIDH public key") // Try decapsulating with SIDH key v.kem.Reset() - CheckNoErr( + Ok( 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") } @@ -287,18 +309,18 @@ func testNegativeKEMSameWrongResult(t *testing.T, v sikeVec) { sk := NewPrivateKey(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) v.kem.Reset() 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 ct[0] = ct[0] - 1 v.kem.Reset() 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 var expSk [common.MaxSikePrivateKeyBsz]byte @@ -306,12 +328,12 @@ func testNegativeKEMSameWrongResult(t *testing.T, v sikeVec) { // create new private key 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. v.kem.Reset() 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 if !bytes.Equal(ssD1[:], ssD2[:]) { @@ -335,7 +357,7 @@ func testKAT(t *testing.T, v sikeVec) { } 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) { t.Fatalf("KAT decapsulation failed\n") } @@ -365,7 +387,7 @@ func testKAT(t *testing.T, v sikeVec) { var prvKey = NewPrivateKey(v.id, KeyVariantSike) var pubKey = NewPublicKey(v.id, KeyVariantSike) 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 prvKey.GeneratePublicKey(pubKey) @@ -439,9 +461,9 @@ func TestKEMRoundTrip(t *testing.T) { for _, val := range tdataSike { // fmt.Printf("\tTesting: %s\n", val.name) 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) - CheckNoErr(t, err, "private key B not a number") + Ok(t, err, "private key B not a number") testKEMRoundTrip(t, pk, sk, val) } } diff --git a/go.mod b/go.mod index bc8a5eb..4f6215d 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,5 @@ 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 diff --git a/kem/sike/sike.go b/kem/sike/sike.go deleted file mode 100644 index 21d5f40..0000000 --- a/kem/sike/sike.go +++ /dev/null @@ -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 -} diff --git a/kem/sike/sike_test.go b/kem/sike/sike_test.go deleted file mode 100644 index 446568c..0000000 --- a/kem/sike/sike_test.go +++ /dev/null @@ -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) }