Add import/export of public keys #79

This commit is contained in:
Alessio Treglia 2018-04-04 23:25:14 +01:00
parent 7fb3f704b3
commit 9c02c8ce93
No known key found for this signature in database
GPG Key ID: E8A48AE5311D765A
4 changed files with 127 additions and 7 deletions

View File

@ -110,6 +110,10 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signat
if err != nil {
return
}
if info.PrivKeyArmor == "" {
err = fmt.Errorf("private key not available")
return
}
priv, err := unarmorDecryptPrivKey(info.PrivKeyArmor, passphrase)
if err != nil {
return
@ -127,6 +131,22 @@ func (kb dbKeybase) Export(name string) (armor string, err error) {
return armorInfoBytes(bz), nil
}
// ExportPubKey returns public keys in ASCII armored format.
func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
bz := kb.db.Get(infoKey(name))
if bz == nil {
return "", errors.New("No key to export with name " + name)
}
info, err := readInfo(bz)
if err != nil {
return
}
return armorPubKeyBytes(info.PubKey.Bytes()), nil
}
// ExportPubKey imports ASCII-armored public keys.
// Store a new Info object holding a public key only, i.e. it will
// not be possible to sign with it as it lacks the secret key.
func (kb dbKeybase) Import(name string, armor string) (err error) {
bz := kb.db.Get(infoKey(name))
if len(bz) > 0 {
@ -140,6 +160,23 @@ func (kb dbKeybase) Import(name string, armor string) (err error) {
return nil
}
func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
bz := kb.db.Get(infoKey(name))
if len(bz) > 0 {
return errors.New("Cannot overwrite data for name " + name)
}
pubBytes, err := unarmorPubKeyBytes(armor)
if err != nil {
return
}
pubKey, err := crypto.PubKeyFromBytes(pubBytes)
if err != nil {
return
}
kb.writePubKey(pubKey, name)
return
}
// Delete removes key forever, but we must present the
// proper passphrase before deleting it (for security).
func (kb dbKeybase) Delete(name, passphrase string) error {
@ -174,6 +211,16 @@ func (kb dbKeybase) Update(name, oldpass, newpass string) error {
kb.writeKey(key, name, newpass)
return nil
}
func (kb dbKeybase) writePubKey(pub crypto.PubKey, name string) Info {
// make Info
info := newInfo(name, pub, "")
// write them both
kb.db.SetSync(infoKey(name), info.bytes())
return info
}
func (kb dbKeybase) writeKey(priv crypto.PrivKey, name, passphrase string) Info {
// generate the encrypted privkey
privArmor := encryptArmorPrivKey(priv, passphrase)

View File

@ -91,8 +91,8 @@ func TestSignVerify(t *testing.T) {
)
algo := keys.AlgoSecp256k1
n1, n2 := "some dude", "a dudette"
p1, p2 := "1234", "foobar"
n1, n2, n3 := "some dude", "a dudette", "dude-ish"
p1, p2, p3 := "1234", "foobar", "foobar"
// create two users and get their info
i1, _, err := cstore.Create(n1, p1, algo)
@ -101,9 +101,18 @@ func TestSignVerify(t *testing.T) {
i2, _, err := cstore.Create(n2, p2, algo)
require.Nil(t, err)
// Import a public key
armor, err := cstore.ExportPubKey(n2)
require.Nil(t, err)
cstore.ImportPubKey(n3, armor)
i3, err := cstore.Get(n3)
require.Nil(t, err)
require.Equal(t, i3.PrivKeyArmor, "")
// let's try to sign some messages
d1 := []byte("my first message")
d2 := []byte("some other important info!")
d3 := []byte("feels like I forgot something...")
// try signing both data with both keys...
s11, pub1, err := cstore.Sign(n1, p1, d1)
@ -145,6 +154,10 @@ func TestSignVerify(t *testing.T) {
valid := tc.key.VerifyBytes(tc.data, tc.sig)
assert.Equal(t, tc.valid, valid, "%d", i)
}
// Now try to sign data with a secret-less key
_, _, err = cstore.Sign(n3, p3, d3)
assert.NotNil(t, err)
}
/*
@ -243,6 +256,48 @@ func TestExportImport(t *testing.T) {
assert.Equal(t, john, john2)
}
func TestExportImportPubKey(t *testing.T) {
// make the storage with reasonable defaults
db := dbm.NewMemDB()
cstore := keys.New(
db,
words.MustLoadCodec("english"),
)
// Create a private-public key pair and ensure consistency
info, _, err := cstore.Create("john", "passphrase", keys.AlgoEd25519)
assert.Nil(t, err)
assert.NotEqual(t, info.PrivKeyArmor, "")
assert.Equal(t, info.Name, "john")
addr := info.PubKey.Address()
john, err := cstore.Get("john")
assert.Nil(t, err)
assert.Equal(t, john.Name, "john")
assert.Equal(t, john.PubKey.Address(), addr)
// Export the public key only
armor, err := cstore.ExportPubKey("john")
assert.Nil(t, err)
// Import it under a different name
err = cstore.ImportPubKey("john-pubkey-only", armor)
assert.Nil(t, err)
// Ensure consistency
john2, err := cstore.Get("john-pubkey-only")
assert.Nil(t, err)
assert.Equal(t, john2.PrivKeyArmor, "")
// Compare the public keys
assert.True(t, john.PubKey.Equals(john2.PubKey))
// Ensure the original key hasn't changed
john, err = cstore.Get("john")
assert.Nil(t, err)
assert.Equal(t, john.PubKey.Address(), addr)
assert.Equal(t, john.Name, "john")
// Ensure keys cannot be overwritten
err = cstore.ImportPubKey("john-pubkey-only", armor)
assert.NotNil(t, err)
}
// TestAdvancedKeyManagement verifies update, import, export functionality
func TestAdvancedKeyManagement(t *testing.T) {

View File

@ -13,24 +13,40 @@ import (
const (
blockTypePrivKey = "TENDERMINT PRIVATE KEY"
blockTypeKeyInfo = "TENDERMINT KEY INFO"
blockTypePubKey = "TENDERMINT PUBLIC KEY"
)
func armorInfoBytes(bz []byte) string {
return armorBytes(bz, blockTypeKeyInfo)
}
func armorPubKeyBytes(bz []byte) string {
return armorBytes(bz, blockTypePubKey)
}
func armorBytes(bz []byte, blockType string) string {
header := map[string]string{
"type": "Info",
"version": "0.0.0",
}
armorStr := crypto.EncodeArmor(blockTypeKeyInfo, header, bz)
return armorStr
return crypto.EncodeArmor(blockType, header, bz)
}
func unarmorInfoBytes(armorStr string) (bz []byte, err error) {
blockType, header, bz, err := crypto.DecodeArmor(armorStr)
return unarmorBytes(armorStr, blockTypeKeyInfo)
}
func unarmorPubKeyBytes(armorStr string) (bz []byte, err error) {
return unarmorBytes(armorStr, blockTypePubKey)
}
func unarmorBytes(armorStr, blockType string) (bz []byte, err error) {
bType, header, bz, err := crypto.DecodeArmor(armorStr)
if err != nil {
return
}
if blockType != blockTypeKeyInfo {
err = fmt.Errorf("Unrecognized armor type: %v", blockType)
if bType != blockType {
err = fmt.Errorf("Unrecognized armor type %q, expected: %q", bType, blockType)
return
}
if header["version"] != "0.0.0" {

View File

@ -18,7 +18,9 @@ type Keybase interface {
Delete(name, passphrase string) error
Import(name string, armor string) (err error)
ImportPubKey(name string, armor string) (err error)
Export(name string) (armor string, err error)
ExportPubKey(name string) (armor string, err error)
}
// Info is the public information about a key