dedis / kyber

Advanced crypto library for the Go language
Other
630 stars 170 forks source link

How to use ECIES with existing keys? #444

Closed udhos closed 3 years ago

udhos commented 3 years ago

Hi,

I am doing simplistic testing among some ECIES implementations in Go.

Full test code here: https://github.com/udhos/ecies-go-test/blob/master/main_test.go

Keys are loaded as Go's ECDSA keys. So far I tried the broken code below to interoperate with kyber ECIES.

func encryptKyber(pubKey *ecdsa.PublicKey, data []byte) ([]byte, error) {
    suite := nist.NewBlakeSHA256P256()
    public := suite.Point().Embed(pubKeyBytes(pubKey), random.New())
    return kyber.Encrypt(suite, public, data, suite.Hash)
}

func decryptKyber(privKey *ecdsa.PrivateKey, data []byte) ([]byte, error) {
    suite := nist.NewBlakeSHA256P256()
    private := suite.Scalar().SetBytes(privKey.D.Bytes())
    return kyber.Decrypt(suite, private, data, suite.Hash)
}

However as one would expect it didn't work:

go test | grep kyber
    main_test.go:101: key=key1(secp256r1) text=text1 src= ethereum dst=    kyber FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key1(secp256r1) text=text1 src=    havir dst=    kyber FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key1(secp256r1) text=text1 src= obscuren dst=    kyber FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key1(secp256r1) text=text1 src=    kyber dst= ethereum FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key1(secp256r1) text=text1 src=    kyber dst=    havir FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key1(secp256r1) text=text1 src=    kyber dst= obscuren FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key1(secp256r1) text=text1 src=    kyber dst=    kyber FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key2(secp256k1) text=text1 src=  bitcoin dst=    kyber FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key2(secp256k1) text=text1 src=sghcrypto dst=    kyber FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key2(secp256k1) text=text1 src= ecies_go dst=    kyber FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key2(secp256k1) text=text1 src=    btcec dst=    kyber FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key2(secp256k1) text=text1 src=    kyber dst=  bitcoin FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key2(secp256k1) text=text1 src=    kyber dst=sghcrypto FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key2(secp256k1) text=text1 src=    kyber dst= ecies_go FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key2(secp256k1) text=text1 src=    kyber dst=    btcec FAIL wanted=[abc123] got=[]
    main_test.go:101: key=key2(secp256k1) text=text1 src=    kyber dst=    kyber FAIL wanted=[abc123] got=[]

Anyone, please shed some light on how to use kyber ECIES with existing keys (for curves secp256r1,secp256k1).

Thanks, Everton

jeffallen commented 3 years ago

Hello, sorry for the delay on picking this up. I am working on it now.

The first thing I notice is that the Embed and Data methods are very unlikely to be what you are looking for. They are for embedding and recovering plaintext data into a point, before using that point in more complex crypto protocols (i.e. in evoting, encode votes into a point, then shuffle and reencrypt them later to preserve voter privacy).

In go.dedis.ch/kyber/v3/encrypt/ecies/ecies_test.go line 14, we pick a new private key. In line 15, we calculate the matching public key. In line 16, we do an ECIES encryption of message to the public key. Your encryptKyber must more closely follow that example, or it won't work.

udhos commented 3 years ago

Hi, thanks for the comments!

I did check ecies_test.go and noticed it creates its own key pair. I could not figure out how to inject existing keys into kyber.

Am I understading it correctly that currently kyber can only work with its own generated keys, but not to load existing keys?

Can kyber export its generated keys in a format that I could import from other ECIES implementations?

Thanks a lot, Everton

ineiti commented 3 years ago

The MarshalBinary and UnmarshalBinary should follow the encoding of https://tools.ietf.org/html/rfc8032 - so depending on what you have on your side, you can directly use that.

jeffallen commented 3 years ago

Here is a diff to main_test.go showing how to use UnmarshalBinary.

diff --git a/main_test.go b/main_test.go
index 21218db..93b143e 100644
--- a/main_test.go
+++ b/main_test.go
@@ -20,9 +20,6 @@ import (

    "github.com/udhos/ecies-go-test/secp256k1"

-   "go.dedis.ch/kyber/v3/group/nist"
-   "go.dedis.ch/kyber/v3/util/random"
-
    btcec "github.com/btcsuite/btcd/btcec"
    ecies_go "github.com/ecies/go"
    ethereum "github.com/ethereum/go-ethereum/crypto/ecies"
@@ -31,6 +28,7 @@ import (
    obscuren "github.com/obscuren/ecies"
    havir "github.com/udhos/go-ecies/ecies" // "github.com/danielhavir/go-ecies" with modules support
    kyber "go.dedis.ch/kyber/v3/encrypt/ecies"
+   "go.dedis.ch/kyber/v3/group/nist"
 )

 type testKey struct {
@@ -148,7 +146,7 @@ var testTableCode = []testCode{
    {"sghcrypto", map[string]struct{}{"secp256k1": struct{}{}}, encryptSghcrypto, decryptSghcrypto},
    {"ecies_go", map[string]struct{}{"secp256k1": struct{}{}}, encryptEciesgo, decryptEciesgo},
    {"btcec", map[string]struct{}{"secp256k1": struct{}{}}, encryptBtcec, decryptBtcec},
-   {"kyber", map[string]struct{}{"secp256k1": struct{}{}, "secp256r1": struct{}{}}, encryptKyber, decryptKyber},
+   {"kyber", map[string]struct{}{"secp256r1": struct{}{}}, encryptKyber, decryptKyber},
    {"eciespy", map[string]struct{}{"secp256k1": struct{}{}}, encryptEciespy, decryptEciespy},
    {"eciespy_api", map[string]struct{}{"secp256k1": struct{}{}}, encryptEciespyApi, decryptEciespyApi},
 }
@@ -356,7 +354,11 @@ func decryptBtcec(privKey *ecdsa.PrivateKey, data []byte) ([]byte, error) {

 func encryptKyber(pubKey *ecdsa.PublicKey, data []byte) ([]byte, error) {
    suite := nist.NewBlakeSHA256P256()
-   public := suite.Point().Embed(pubKeyBytes(pubKey), random.New())
+   public := suite.Point()
+   err := public.UnmarshalBinary(pubKeyBytes(pubKey))
+   if err != nil {
+       return nil, err
+   }
    return kyber.Encrypt(suite, public, data, suite.Hash)
 }

With that change, kyber is able to operate with itself, but not with other implementations. And key3 in particular gives a problem, because kyber cannot even unmarshal it because Go's elliptic.Unmarshal function returns nil, nil, as a result of suite.IsOnCurve(x,y) failing.

So... more work to do to understand this. Where did that key3 in your test come from, is it supposed to be a valid key, or not?

jeffallen commented 3 years ago

@ineiti RFC8032 is about ed25519 and friends, so is unrelated to this issue, which is about secp256k1 and secp256r1. But yes, the UnmarshalBinary for Kyber NIST groups is supposed to be compatible with the format that @udhos 's pubKeyBytes() is providing (0x04 followed by X and Y).

jeffallen commented 3 years ago

OK, the problem with key3 is that it is for secp256k1, and Kyber does not support Koblitz, only random curves. So with the appropriate change in testTableCode (see diff above) kyber is able to enc/dec to itself for all test cases.

I'm not sure why it cannot interoperate with other implementations, we are open to further bug reports based on your investigations.

For the moment, I'm going to close this issue because it is possible to use Kyber with your test harness now.

jeffallen commented 3 years ago

One more thing I might not have made clear: The work you are doing to compare the secp256r1 implementations is excellent and valuable, and we encourage you to keep in touch. I only closed this issue because the immediate issue stopping you from comparing Kyber to other implementations was resolved.