mirror of
https://github.com/henrydcase/nobs.git
synced 2024-11-22 15:18:57 +00:00
sidh: updates (#31)
This commit is contained in:
parent
f5a7daf2bb
commit
bc32024729
16
Makefile
16
Makefile
@ -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
|
||||||
|
|
||||||
|
1
dh/sidh/internal/doc.go
Normal file
1
dh/sidh/internal/doc.go
Normal file
@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
1
dh/sidh/internal/p434/doc.go
Normal file
1
dh/sidh/internal/p434/doc.go
Normal file
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
go.mod
6
go.mod
@ -1,7 +1,5 @@
|
|||||||
module github.com/henrydcase/nobs
|
module github.com/henrydcase/nobs
|
||||||
|
|
||||||
go 1.14
|
go 1.12
|
||||||
|
|
||||||
require (
|
require golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e
|
|
||||||
)
|
|
||||||
|
217
kem/sike/sike.go
217
kem/sike/sike.go
@ -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) }
|
|
Loading…
Reference in New Issue
Block a user