mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-18 17:41:19 +00:00
Add import/export of public keys #79
This commit is contained in:
parent
7fb3f704b3
commit
9c02c8ce93
@ -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)
|
||||
|
@ -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) {
|
||||
|
||||
|
@ -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" {
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user