Skip to content

Commit

Permalink
Public and Private Key Bi-directional Serialization (TBD54566975#149)
Browse files Browse the repository at this point in the history
* pub key conversion

* priv key conversion
  • Loading branch information
decentralgabe authored and andorsk committed Jul 27, 2022
1 parent c79bd66 commit 62accc8
Show file tree
Hide file tree
Showing 2 changed files with 316 additions and 0 deletions.
107 changes: 107 additions & 0 deletions crypto/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func GenerateKeyByKeyType(kt KeyType) (crypto.PublicKey, crypto.PrivateKey, erro
return nil, nil, fmt.Errorf("unsupported key type: %s", kt)
}

// PubKeyToBytes constructs a byte representation of a public key, for a set number of supported key types
func PubKeyToBytes(key crypto.PublicKey) ([]byte, error) {
ed25519Key, ok := key.(ed25519.PublicKey)
if ok {
Expand Down Expand Up @@ -69,6 +70,112 @@ func PubKeyToBytes(key crypto.PublicKey) ([]byte, error) {
return nil, errors.New("unknown public key type; could not convert to bytes")
}

// BytesToPubKey reconstructs a public key given some bytes and a target key type
// It is assumed the key was turned into byte form using the sibling method `PubKeyToBytes`
func BytesToPubKey(keyBytes []byte, kt KeyType) (crypto.PublicKey, error) {
switch kt {
case Ed25519, X25519:
return keyBytes, nil
case Secp256k1:
pubKey, err := secp.ParsePubKey(keyBytes)
if err != nil {
return nil, err
}
return *pubKey, nil
case P224:
x, y := elliptic.Unmarshal(elliptic.P224(), keyBytes)
return ecdsa.PublicKey{
Curve: elliptic.P224(),
X: x,
Y: y,
}, nil
case P256:
x, y := elliptic.Unmarshal(elliptic.P256(), keyBytes)
return ecdsa.PublicKey{
Curve: elliptic.P256(),
X: x,
Y: y,
}, nil
case P384:
x, y := elliptic.Unmarshal(elliptic.P384(), keyBytes)
return ecdsa.PublicKey{
Curve: elliptic.P384(),
X: x,
Y: y,
}, nil
case P521:
x, y := elliptic.Unmarshal(elliptic.P521(), keyBytes)
return ecdsa.PublicKey{
Curve: elliptic.P521(),
X: x,
Y: y,
}, nil
case RSA:
pubKey, err := x509.ParsePKCS1PublicKey(keyBytes)
if err != nil {
return nil, err
}
return *pubKey, nil
default:
return nil, fmt.Errorf("unsupported key type: %s", kt)
}
}

// PrivKeyToBytes constructs a byte representation of a private key, for a set number of supported key types
func PrivKeyToBytes(key crypto.PrivateKey) ([]byte, error) {
ed25519Key, ok := key.(ed25519.PrivateKey)
if ok {
return ed25519Key, nil
}

x25519Key, ok := key.(x25519.PrivateKey)
if ok {
return x25519Key, nil
}

secp256k1Key, ok := key.(secp.PrivateKey)
if ok {
return secp256k1Key.Serialize(), nil
}

ecdsaKey, ok := key.(ecdsa.PrivateKey)
if ok {
return x509.MarshalECPrivateKey(&ecdsaKey)
}

rsaKey, ok := key.(rsa.PrivateKey)
if ok {
return x509.MarshalPKCS1PrivateKey(&rsaKey), nil
}

return nil, errors.New("unknown private key type; could not convert to bytes")
}

// BytesToPrivKey reconstructs a private key given some bytes and a target key type
// It is assumed the key was turned into byte form using the sibling method `PrivKeyToBytes`
func BytesToPrivKey(keyBytes []byte, kt KeyType) (crypto.PrivateKey, error) {
switch kt {
case Ed25519, X25519:
return keyBytes, nil
case Secp256k1:
return *secp.PrivKeyFromBytes(keyBytes), nil
case P224, P256, P384, P521:
privKey, err := x509.ParseECPrivateKey(keyBytes)
if err != nil {
return nil, err
}
return *privKey, nil
case RSA:
privKey, err := x509.ParsePKCS1PrivateKey(keyBytes)
if err != nil {
return nil, err
}
return *privKey, nil
default:
return nil, fmt.Errorf("unsupported key type: %s", kt)
}
}

func GenerateEd25519Key() (ed25519.PublicKey, ed25519.PrivateKey, error) {
return ed25519.GenerateKey(rand.Reader)
}
Expand Down
209 changes: 209 additions & 0 deletions crypto/keys_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
package crypto

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestKeyToBytes(t *testing.T) {
t.Run("ed25519", func(tt *testing.T) {
pub, priv, err := GenerateKeyByKeyType(Ed25519)
assert.NoError(tt, err)
assert.NotEmpty(tt, pub)
assert.NotEmpty(tt, priv)

pubKeyBytes, err := PubKeyToBytes(pub)
assert.NoError(tt, err)
assert.NotEmpty(tt, pubKeyBytes)

reconstructedPub, err := BytesToPubKey(pubKeyBytes, Ed25519)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPub)
assert.EqualValues(tt, pub, reconstructedPub)

privKeyBytes, err := PrivKeyToBytes(priv)
assert.NoError(tt, err)
assert.NotEmpty(tt, privKeyBytes)

reconstructedPriv, err := BytesToPrivKey(privKeyBytes, Ed25519)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPriv)
assert.EqualValues(tt, priv, reconstructedPriv)
})

t.Run("X25519", func(tt *testing.T) {
pub, priv, err := GenerateKeyByKeyType(X25519)
assert.NoError(tt, err)
assert.NotEmpty(tt, pub)
assert.NotEmpty(tt, priv)

pubKeyBytes, err := PubKeyToBytes(pub)
assert.NoError(tt, err)
assert.NotEmpty(tt, pubKeyBytes)

reconstructedPub, err := BytesToPubKey(pubKeyBytes, X25519)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPub)
assert.EqualValues(tt, pub, reconstructedPub)

privKeyBytes, err := PrivKeyToBytes(priv)
assert.NoError(tt, err)
assert.NotEmpty(tt, privKeyBytes)

reconstructedPriv, err := BytesToPrivKey(privKeyBytes, X25519)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPriv)
assert.EqualValues(tt, priv, reconstructedPriv)
})

t.Run("Secp256k1", func(tt *testing.T) {
pub, priv, err := GenerateKeyByKeyType(Secp256k1)
assert.NoError(tt, err)
assert.NotEmpty(tt, pub)
assert.NotEmpty(tt, priv)

pubKeyBytes, err := PubKeyToBytes(pub)
assert.NoError(tt, err)
assert.NotEmpty(tt, pubKeyBytes)

reconstructedPub, err := BytesToPubKey(pubKeyBytes, Secp256k1)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPub)
assert.EqualValues(tt, pub, reconstructedPub)

privKeyBytes, err := PrivKeyToBytes(priv)
assert.NoError(tt, err)
assert.NotEmpty(tt, privKeyBytes)

reconstructedPriv, err := BytesToPrivKey(privKeyBytes, Secp256k1)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPriv)
assert.EqualValues(tt, priv, reconstructedPriv)
})

t.Run("P224", func(tt *testing.T) {
pub, priv, err := GenerateKeyByKeyType(P224)
assert.NoError(tt, err)
assert.NotEmpty(tt, pub)
assert.NotEmpty(tt, priv)

pubKeyBytes, err := PubKeyToBytes(pub)
assert.NoError(tt, err)
assert.NotEmpty(tt, pubKeyBytes)

reconstructedPub, err := BytesToPubKey(pubKeyBytes, P224)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPub)
assert.EqualValues(tt, pub, reconstructedPub)

privKeyBytes, err := PrivKeyToBytes(priv)
assert.NoError(tt, err)
assert.NotEmpty(tt, privKeyBytes)

reconstructedPriv, err := BytesToPrivKey(privKeyBytes, P224)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPriv)
assert.EqualValues(tt, priv, reconstructedPriv)
})

t.Run("P256", func(tt *testing.T) {
pub, priv, err := GenerateKeyByKeyType(P256)
assert.NoError(tt, err)
assert.NotEmpty(tt, pub)
assert.NotEmpty(tt, priv)

pubKeyBytes, err := PubKeyToBytes(pub)
assert.NoError(tt, err)
assert.NotEmpty(tt, pubKeyBytes)

reconstructedPub, err := BytesToPubKey(pubKeyBytes, P256)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPub)
assert.EqualValues(tt, pub, reconstructedPub)

privKeyBytes, err := PrivKeyToBytes(priv)
assert.NoError(tt, err)
assert.NotEmpty(tt, privKeyBytes)

reconstructedPriv, err := BytesToPrivKey(privKeyBytes, P256)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPriv)
assert.EqualValues(tt, priv, reconstructedPriv)
})

t.Run("P384", func(tt *testing.T) {
pub, priv, err := GenerateKeyByKeyType(P384)
assert.NoError(tt, err)
assert.NotEmpty(tt, pub)
assert.NotEmpty(tt, priv)

pubKeyBytes, err := PubKeyToBytes(pub)
assert.NoError(tt, err)
assert.NotEmpty(tt, pubKeyBytes)

reconstructedPub, err := BytesToPubKey(pubKeyBytes, P384)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPub)
assert.EqualValues(tt, pub, reconstructedPub)

privKeyBytes, err := PrivKeyToBytes(priv)
assert.NoError(tt, err)
assert.NotEmpty(tt, privKeyBytes)

reconstructedPriv, err := BytesToPrivKey(privKeyBytes, P384)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPriv)
assert.EqualValues(tt, priv, reconstructedPriv)
})

t.Run("P521", func(tt *testing.T) {
pub, priv, err := GenerateKeyByKeyType(P521)
assert.NoError(tt, err)
assert.NotEmpty(tt, pub)
assert.NotEmpty(tt, priv)

pubKeyBytes, err := PubKeyToBytes(pub)
assert.NoError(tt, err)
assert.NotEmpty(tt, pubKeyBytes)

reconstructedPub, err := BytesToPubKey(pubKeyBytes, P521)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPub)
assert.EqualValues(tt, pub, reconstructedPub)

privKeyBytes, err := PrivKeyToBytes(priv)
assert.NoError(tt, err)
assert.NotEmpty(tt, privKeyBytes)

reconstructedPriv, err := BytesToPrivKey(privKeyBytes, P521)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPriv)
assert.EqualValues(tt, priv, reconstructedPriv)
})

t.Run("RSA", func(tt *testing.T) {
pub, priv, err := GenerateKeyByKeyType(RSA)
assert.NoError(tt, err)
assert.NotEmpty(tt, pub)
assert.NotEmpty(tt, priv)

pubKeyBytes, err := PubKeyToBytes(pub)
assert.NoError(tt, err)
assert.NotEmpty(tt, pubKeyBytes)

reconstructedPub, err := BytesToPubKey(pubKeyBytes, RSA)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPub)
assert.EqualValues(tt, pub, reconstructedPub)

privKeyBytes, err := PrivKeyToBytes(priv)
assert.NoError(tt, err)
assert.NotEmpty(tt, privKeyBytes)

reconstructedPriv, err := BytesToPrivKey(privKeyBytes, RSA)
assert.NoError(tt, err)
assert.NotEmpty(tt, reconstructedPriv)
assert.EqualValues(tt, priv, reconstructedPriv)
})
}

0 comments on commit 62accc8

Please sign in to comment.