Merge PR #5439: Keybase: Multiple Signature Algorithms
This commit is contained in:
committed by
Alexander Bezobchuk
parent
d452e9398b
commit
f367087731
+34
-28
@@ -10,7 +10,6 @@ import (
|
||||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
dbm "github.com/tendermint/tm-db"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
@@ -57,7 +56,7 @@ const (
|
||||
var (
|
||||
// ErrUnsupportedSigningAlgo is raised when the caller tries to use a
|
||||
// different signing scheme than secp256k1.
|
||||
ErrUnsupportedSigningAlgo = errors.New("unsupported signing algo: only secp256k1 is supported")
|
||||
ErrUnsupportedSigningAlgo = errors.New("unsupported signing algo")
|
||||
|
||||
// ErrUnsupportedLanguage is raised when the caller tries to use a
|
||||
// different language than english for creating a mnemonic sentence.
|
||||
@@ -101,18 +100,10 @@ func (kb dbKeybase) CreateMnemonic(
|
||||
// CreateAccount converts a mnemonic to a private key and persists it, encrypted
|
||||
// with the given password.
|
||||
func (kb dbKeybase) CreateAccount(
|
||||
name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32,
|
||||
name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo,
|
||||
) (Info, error) {
|
||||
|
||||
return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, account, index)
|
||||
}
|
||||
|
||||
// Derive computes a BIP39 seed from th mnemonic and bip39Passwd.
|
||||
func (kb dbKeybase) Derive(
|
||||
name, mnemonic, bip39Passphrase, encryptPasswd string, params hd.BIP44Params,
|
||||
) (Info, error) {
|
||||
|
||||
return kb.base.Derive(kb, name, mnemonic, bip39Passphrase, encryptPasswd, params)
|
||||
return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo)
|
||||
}
|
||||
|
||||
// CreateLedger creates a new locally-stored reference to a Ledger keypair.
|
||||
@@ -126,8 +117,8 @@ func (kb dbKeybase) CreateLedger(
|
||||
|
||||
// CreateOffline creates a new reference to an offline keypair. It returns the
|
||||
// created key info.
|
||||
func (kb dbKeybase) CreateOffline(name string, pub tmcrypto.PubKey) (Info, error) {
|
||||
return kb.base.writeOfflineKey(kb, name, pub), nil
|
||||
func (kb dbKeybase) CreateOffline(name string, pub tmcrypto.PubKey, algo SigningAlgo) (Info, error) {
|
||||
return kb.base.writeOfflineKey(kb, name, pub, algo), nil
|
||||
}
|
||||
|
||||
// CreateMulti creates a new reference to a multisig (offline) keypair. It
|
||||
@@ -199,7 +190,7 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig []byte, pub t
|
||||
return
|
||||
}
|
||||
|
||||
priv, err = mintkey.UnarmorDecryptPrivKey(i.PrivKeyArmor, passphrase)
|
||||
priv, _, err = mintkey.UnarmorDecryptPrivKey(i.PrivKeyArmor, passphrase)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -238,7 +229,7 @@ func (kb dbKeybase) ExportPrivateKeyObject(name string, passphrase string) (tmcr
|
||||
return nil, err
|
||||
}
|
||||
|
||||
priv, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
|
||||
priv, _, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -272,7 +263,7 @@ func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
return mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes()), nil
|
||||
return mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes(), string(info.GetAlgo())), nil
|
||||
}
|
||||
|
||||
// ExportPrivKey returns a private key in ASCII armored format.
|
||||
@@ -285,7 +276,12 @@ func (kb dbKeybase) ExportPrivKey(name string, decryptPassphrase string,
|
||||
return "", err
|
||||
}
|
||||
|
||||
return mintkey.EncryptArmorPrivKey(priv, encryptPassphrase), nil
|
||||
info, err := kb.Get(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return mintkey.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
|
||||
}
|
||||
|
||||
// ImportPrivKey imports a private key in ASCII armor format. It returns an
|
||||
@@ -296,12 +292,12 @@ func (kb dbKeybase) ImportPrivKey(name string, armor string, passphrase string)
|
||||
return errors.New("Cannot overwrite key " + name)
|
||||
}
|
||||
|
||||
privKey, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase)
|
||||
privKey, algo, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "couldn't import private key")
|
||||
}
|
||||
|
||||
kb.writeLocalKey(name, privKey, passphrase)
|
||||
kb.writeLocalKey(name, privKey, passphrase, SigningAlgo(algo))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -329,7 +325,7 @@ func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
|
||||
return errors.New("Cannot overwrite data for name " + name)
|
||||
}
|
||||
|
||||
pubBytes, err := mintkey.UnarmorPubKeyBytes(armor)
|
||||
pubBytes, algo, err := mintkey.UnarmorPubKeyBytes(armor)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -339,7 +335,7 @@ func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
kb.base.writeOfflineKey(kb, name, pubKey)
|
||||
kb.base.writeOfflineKey(kb, name, pubKey, SigningAlgo(algo))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -355,7 +351,7 @@ func (kb dbKeybase) Delete(name, passphrase string, skipPass bool) error {
|
||||
}
|
||||
|
||||
if linfo, ok := info.(localInfo); ok && !skipPass {
|
||||
if _, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase); err != nil {
|
||||
if _, _, err = mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -382,7 +378,7 @@ func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, erro
|
||||
case localInfo:
|
||||
linfo := i
|
||||
|
||||
key, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
|
||||
key, _, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -392,7 +388,7 @@ func (kb dbKeybase) Update(name, oldpass string, getNewpass func() (string, erro
|
||||
return err
|
||||
}
|
||||
|
||||
kb.writeLocalKey(name, key, newpass)
|
||||
kb.writeLocalKey(name, key, newpass, i.GetAlgo())
|
||||
return nil
|
||||
|
||||
default:
|
||||
@@ -405,13 +401,23 @@ func (kb dbKeybase) CloseDB() {
|
||||
kb.db.Close()
|
||||
}
|
||||
|
||||
func (kb dbKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string) Info {
|
||||
// SupportedAlgos returns a list of supported signing algorithms.
|
||||
func (kb dbKeybase) SupportedAlgos() []SigningAlgo {
|
||||
return kb.base.SupportedAlgos()
|
||||
}
|
||||
|
||||
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
|
||||
func (kb dbKeybase) SupportedAlgosLedger() []SigningAlgo {
|
||||
return kb.base.SupportedAlgosLedger()
|
||||
}
|
||||
|
||||
func (kb dbKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string, algo SigningAlgo) Info {
|
||||
// encrypt private key using passphrase
|
||||
privArmor := mintkey.EncryptArmorPrivKey(priv, passphrase)
|
||||
privArmor := mintkey.EncryptArmorPrivKey(priv, passphrase, string(algo))
|
||||
|
||||
// make Info
|
||||
pub := priv.PubKey()
|
||||
info := newLocalInfo(name, pub, privArmor)
|
||||
info := newLocalInfo(name, pub, privArmor, algo)
|
||||
|
||||
kb.writeInfo(name, info)
|
||||
return info
|
||||
|
||||
+107
-54
@@ -17,7 +17,10 @@ import (
|
||||
|
||||
type (
|
||||
kbOptions struct {
|
||||
keygenFunc PrivKeyGenFunc
|
||||
keygenFunc PrivKeyGenFunc
|
||||
deriveFunc DeriveKeyFunc
|
||||
supportedAlgos []SigningAlgo
|
||||
supportedAlgosLedger []SigningAlgo
|
||||
}
|
||||
|
||||
// baseKeybase is an auxiliary type that groups Keybase storage agnostic features
|
||||
@@ -32,7 +35,7 @@ type (
|
||||
}
|
||||
|
||||
writeLocalKeyer interface {
|
||||
writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string) Info
|
||||
writeLocalKey(name string, priv tmcrypto.PrivKey, passphrase string, algo SigningAlgo) Info
|
||||
}
|
||||
|
||||
infoWriter interface {
|
||||
@@ -47,10 +50,36 @@ func WithKeygenFunc(f PrivKeyGenFunc) KeybaseOption {
|
||||
}
|
||||
}
|
||||
|
||||
// WithDeriveFunc applies an overridden key derivation function to generate the private key.
|
||||
func WithDeriveFunc(f DeriveKeyFunc) KeybaseOption {
|
||||
return func(o *kbOptions) {
|
||||
o.deriveFunc = f
|
||||
}
|
||||
}
|
||||
|
||||
// WithSupportedAlgos defines the list of accepted SigningAlgos.
|
||||
func WithSupportedAlgos(algos []SigningAlgo) KeybaseOption {
|
||||
return func(o *kbOptions) {
|
||||
o.supportedAlgos = algos
|
||||
}
|
||||
}
|
||||
|
||||
// WithSupportedAlgosLedger defines the list of accepted SigningAlgos compatible with Ledger.
|
||||
func WithSupportedAlgosLedger(algos []SigningAlgo) KeybaseOption {
|
||||
return func(o *kbOptions) {
|
||||
o.supportedAlgosLedger = algos
|
||||
}
|
||||
}
|
||||
|
||||
// newBaseKeybase generates the base keybase with defaulting to tendermint SECP256K1 key type
|
||||
func newBaseKeybase(optionsFns ...KeybaseOption) baseKeybase {
|
||||
// Default options for keybase
|
||||
options := kbOptions{keygenFunc: baseSecpPrivKeyGen}
|
||||
options := kbOptions{
|
||||
keygenFunc: StdPrivKeyGen,
|
||||
deriveFunc: StdDeriveKey,
|
||||
supportedAlgos: []SigningAlgo{Secp256k1},
|
||||
supportedAlgosLedger: []SigningAlgo{Secp256k1},
|
||||
}
|
||||
|
||||
for _, optionFn := range optionsFns {
|
||||
optionFn(&options)
|
||||
@@ -59,9 +88,20 @@ func newBaseKeybase(optionsFns ...KeybaseOption) baseKeybase {
|
||||
return baseKeybase{options: options}
|
||||
}
|
||||
|
||||
// baseSecpPrivKeyGen generates a secp256k1 private key from the given bytes
|
||||
func baseSecpPrivKeyGen(bz [32]byte) tmcrypto.PrivKey {
|
||||
return secp256k1.PrivKeySecp256k1(bz)
|
||||
// StdPrivKeyGen is the default PrivKeyGen function in the keybase.
|
||||
// For now, it only supports Secp256k1
|
||||
func StdPrivKeyGen(bz []byte, algo SigningAlgo) (tmcrypto.PrivKey, error) {
|
||||
if algo == Secp256k1 {
|
||||
return SecpPrivKeyGen(bz), nil
|
||||
}
|
||||
return nil, ErrUnsupportedSigningAlgo
|
||||
}
|
||||
|
||||
// SecpPrivKeyGen generates a secp256k1 private key from the given bytes
|
||||
func SecpPrivKeyGen(bz []byte) tmcrypto.PrivKey {
|
||||
var bzArr [32]byte
|
||||
copy(bzArr[:], bz)
|
||||
return secp256k1.PrivKeySecp256k1(bzArr)
|
||||
}
|
||||
|
||||
// SignWithLedger signs a binary message with the ledger device referenced by an Info object
|
||||
@@ -111,29 +151,26 @@ func (kb baseKeybase) DecodeSignature(info Info, msg []byte) (sig []byte, pub tm
|
||||
|
||||
// CreateAccount creates an account Info object.
|
||||
func (kb baseKeybase) CreateAccount(
|
||||
keyWriter keyWriter, name, mnemonic, bip39Passwd, encryptPasswd string, account, index uint32,
|
||||
) (Info, error) {
|
||||
|
||||
hdPath := CreateHDPath(account, index)
|
||||
return kb.Derive(keyWriter, name, mnemonic, bip39Passwd, encryptPasswd, *hdPath)
|
||||
}
|
||||
|
||||
func (kb baseKeybase) persistDerivedKey(
|
||||
keyWriter keyWriter, seed []byte, passwd, name, fullHdPath string,
|
||||
keyWriter keyWriter, name, mnemonic, bip39Passphrase, encryptPasswd, hdPath string, algo SigningAlgo,
|
||||
) (Info, error) {
|
||||
|
||||
// create master key and derive first key for keyring
|
||||
derivedPriv, err := ComputeDerivedKey(seed, fullHdPath)
|
||||
derivedPriv, err := kb.options.deriveFunc(mnemonic, bip39Passphrase, hdPath, algo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
privKey, err := kb.options.keygenFunc(derivedPriv, algo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var info Info
|
||||
|
||||
if passwd != "" {
|
||||
info = keyWriter.writeLocalKey(name, kb.options.keygenFunc(derivedPriv), passwd)
|
||||
if encryptPasswd != "" {
|
||||
info = keyWriter.writeLocalKey(name, privKey, encryptPasswd, algo)
|
||||
} else {
|
||||
info = kb.writeOfflineKey(keyWriter, name, kb.options.keygenFunc(derivedPriv).PubKey())
|
||||
info = kb.writeOfflineKey(keyWriter, name, privKey.PubKey(), algo)
|
||||
}
|
||||
|
||||
return info, nil
|
||||
@@ -145,7 +182,7 @@ func (kb baseKeybase) CreateLedger(
|
||||
w infoWriter, name string, algo SigningAlgo, hrp string, account, index uint32,
|
||||
) (Info, error) {
|
||||
|
||||
if !IsAlgoSupported(algo) {
|
||||
if !IsSupportedAlgorithm(kb.SupportedAlgosLedger(), algo) {
|
||||
return nil, ErrUnsupportedSigningAlgo
|
||||
}
|
||||
|
||||
@@ -157,7 +194,7 @@ func (kb baseKeybase) CreateLedger(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kb.writeLedgerKey(w, name, priv.PubKey(), *hdPath), nil
|
||||
return kb.writeLedgerKey(w, name, priv.PubKey(), *hdPath, algo), nil
|
||||
}
|
||||
|
||||
// CreateMnemonic generates a new key with the given algorithm and language pair.
|
||||
@@ -169,7 +206,7 @@ func (kb baseKeybase) CreateMnemonic(
|
||||
return nil, "", ErrUnsupportedLanguage
|
||||
}
|
||||
|
||||
if !IsAlgoSupported(algo) {
|
||||
if !IsSupportedAlgorithm(kb.SupportedAlgos(), algo) {
|
||||
return nil, "", ErrUnsupportedSigningAlgo
|
||||
}
|
||||
|
||||
@@ -185,37 +222,22 @@ func (kb baseKeybase) CreateMnemonic(
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
info, err = kb.persistDerivedKey(
|
||||
keyWriter,
|
||||
bip39.NewSeed(mnemonic, DefaultBIP39Passphrase), passwd,
|
||||
name, types.GetConfig().GetFullFundraiserPath(),
|
||||
)
|
||||
info, err = kb.CreateAccount(keyWriter, name, mnemonic, DefaultBIP39Passphrase, passwd, types.GetConfig().GetFullFundraiserPath(), algo)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return info, mnemonic, err
|
||||
}
|
||||
|
||||
// Derive computes a BIP39 seed from the mnemonic and bip39Passphrase. It creates
|
||||
// a private key from the seed using the BIP44 params.
|
||||
func (kb baseKeybase) Derive(
|
||||
keyWriter keyWriter, name, mnemonic, bip39Passphrase, encryptPasswd string, params hd.BIP44Params, // nolint:interfacer
|
||||
) (Info, error) {
|
||||
|
||||
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kb.persistDerivedKey(keyWriter, seed, encryptPasswd, name, params.String())
|
||||
}
|
||||
|
||||
func (kb baseKeybase) writeLedgerKey(w infoWriter, name string, pub tmcrypto.PubKey, path hd.BIP44Params) Info {
|
||||
info := newLedgerInfo(name, pub, path)
|
||||
func (kb baseKeybase) writeLedgerKey(w infoWriter, name string, pub tmcrypto.PubKey, path hd.BIP44Params, algo SigningAlgo) Info {
|
||||
info := newLedgerInfo(name, pub, path, algo)
|
||||
w.writeInfo(name, info)
|
||||
return info
|
||||
}
|
||||
|
||||
func (kb baseKeybase) writeOfflineKey(w infoWriter, name string, pub tmcrypto.PubKey) Info {
|
||||
info := newOfflineInfo(name, pub)
|
||||
func (kb baseKeybase) writeOfflineKey(w infoWriter, name string, pub tmcrypto.PubKey, algo SigningAlgo) Info {
|
||||
info := newOfflineInfo(name, pub, algo)
|
||||
w.writeInfo(name, info)
|
||||
return info
|
||||
}
|
||||
@@ -226,10 +248,28 @@ func (kb baseKeybase) writeMultisigKey(w infoWriter, name string, pub tmcrypto.P
|
||||
return info
|
||||
}
|
||||
|
||||
// ComputeDerivedKey derives and returns the private key for the given seed and HD path.
|
||||
func ComputeDerivedKey(seed []byte, fullHdPath string) ([32]byte, error) {
|
||||
// StdDeriveKey is the default DeriveKey function in the keybase.
|
||||
// For now, it only supports Secp256k1
|
||||
func StdDeriveKey(mnemonic string, bip39Passphrase, hdPath string, algo SigningAlgo) ([]byte, error) {
|
||||
if algo == Secp256k1 {
|
||||
return SecpDeriveKey(mnemonic, bip39Passphrase, hdPath)
|
||||
}
|
||||
return nil, ErrUnsupportedSigningAlgo
|
||||
}
|
||||
|
||||
// SecpDeriveKey derives and returns the secp256k1 private key for the given seed and HD path.
|
||||
func SecpDeriveKey(mnemonic string, bip39Passphrase, hdPath string) ([]byte, error) {
|
||||
seed, err := bip39.NewSeedWithErrorChecking(mnemonic, bip39Passphrase)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
masterPriv, ch := hd.ComputeMastersFromSeed(seed)
|
||||
return hd.DerivePrivateKeyForPath(masterPriv, ch, fullHdPath)
|
||||
if len(hdPath) == 0 {
|
||||
return masterPriv[:], nil
|
||||
}
|
||||
derivedKey, err := hd.DerivePrivateKeyForPath(masterPriv, ch, hdPath)
|
||||
return derivedKey[:], err
|
||||
}
|
||||
|
||||
// CreateHDPath returns BIP 44 object from account and index parameters.
|
||||
@@ -237,9 +277,22 @@ func CreateHDPath(account uint32, index uint32) *hd.BIP44Params {
|
||||
return hd.NewFundraiserParams(account, types.GetConfig().GetCoinType(), index)
|
||||
}
|
||||
|
||||
// IsAlgoSupported returns whether the signing algorithm is supported.
|
||||
//
|
||||
// TODO: Refactor this to be configurable to support interchangeable key signing
|
||||
// and addressing.
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/issues/4941
|
||||
func IsAlgoSupported(algo SigningAlgo) bool { return algo == Secp256k1 }
|
||||
// SupportedAlgos returns a list of supported signing algorithms.
|
||||
func (kb baseKeybase) SupportedAlgos() []SigningAlgo {
|
||||
return kb.options.supportedAlgos
|
||||
}
|
||||
|
||||
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
|
||||
func (kb baseKeybase) SupportedAlgosLedger() []SigningAlgo {
|
||||
return kb.options.supportedAlgosLedger
|
||||
}
|
||||
|
||||
// IsSupportedAlgorithm returns whether the signing algorithm is in the passed-in list of supported algorithms.
|
||||
func IsSupportedAlgorithm(supported []SigningAlgo, algo SigningAlgo) bool {
|
||||
for _, supportedAlgo := range supported {
|
||||
if algo == supportedAlgo {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -38,25 +38,43 @@ func TestCreateAccountInvalidMnemonic(t *testing.T) {
|
||||
_, err := kb.CreateAccount(
|
||||
"some_account",
|
||||
"malarkey pair crucial catch public canyon evil outer stage ten gym tornado",
|
||||
"", "", 0, 1)
|
||||
"", "", CreateHDPath(0, 0).String(), Secp256k1)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "Invalid mnemonic", err.Error())
|
||||
}
|
||||
|
||||
func TestCreateLedgerUnsupportedAlgo(t *testing.T) {
|
||||
kb := NewInMemory()
|
||||
|
||||
supportedLedgerAlgos := kb.SupportedAlgosLedger()
|
||||
for _, supportedAlgo := range supportedLedgerAlgos {
|
||||
if Ed25519 == supportedAlgo {
|
||||
assert.FailNow(t, "Was not an unsupported algorithm")
|
||||
}
|
||||
}
|
||||
|
||||
_, err := kb.CreateLedger("some_account", Ed25519, "cosmos", 0, 1)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, "unsupported signing algo: only secp256k1 is supported", err.Error())
|
||||
assert.Equal(t, "unsupported signing algo", err.Error())
|
||||
}
|
||||
|
||||
func TestCreateLedger(t *testing.T) {
|
||||
kb := NewInMemory()
|
||||
kb := NewInMemory(WithSupportedAlgosLedger([]SigningAlgo{Secp256k1, Ed25519}))
|
||||
|
||||
// test_cover and test_unit will result in different answers
|
||||
// test_cover does not compile some dependencies so ledger is disabled
|
||||
// test_unit may add a ledger mock
|
||||
// both cases are acceptable
|
||||
supportedLedgerAlgos := kb.SupportedAlgosLedger()
|
||||
secpSupported := false
|
||||
edSupported := false
|
||||
for _, supportedAlgo := range supportedLedgerAlgos {
|
||||
secpSupported = secpSupported || (supportedAlgo == Secp256k1)
|
||||
edSupported = edSupported || (supportedAlgo == Ed25519)
|
||||
}
|
||||
assert.True(t, secpSupported)
|
||||
assert.True(t, edSupported)
|
||||
|
||||
ledger, err := kb.CreateLedger("some_account", Secp256k1, "cosmos", 3, 1)
|
||||
|
||||
if err != nil {
|
||||
@@ -92,7 +110,21 @@ func TestCreateLedger(t *testing.T) {
|
||||
// TestKeyManagement makes sure we can manipulate these keys well
|
||||
func TestKeyManagement(t *testing.T) {
|
||||
// make the storage with reasonable defaults
|
||||
cstore := NewInMemory()
|
||||
cstore := NewInMemory(WithSupportedAlgos([]SigningAlgo{Secp256k1, Sr25519}))
|
||||
|
||||
// Test modified supported algos
|
||||
supportedAlgos := cstore.SupportedAlgos()
|
||||
secpSupported := false
|
||||
edSupported := false
|
||||
srSupported := false
|
||||
for _, supportedAlgo := range supportedAlgos {
|
||||
secpSupported = secpSupported || (supportedAlgo == Secp256k1)
|
||||
edSupported = edSupported || (supportedAlgo == Ed25519)
|
||||
srSupported = srSupported || (supportedAlgo == Sr25519)
|
||||
}
|
||||
assert.True(t, secpSupported)
|
||||
assert.False(t, edSupported)
|
||||
assert.True(t, srSupported)
|
||||
|
||||
algo := Secp256k1
|
||||
n1, n2, n3 := "personal", "business", "other"
|
||||
@@ -152,10 +184,12 @@ func TestKeyManagement(t *testing.T) {
|
||||
o1 := "offline"
|
||||
priv1 := ed25519.GenPrivKey()
|
||||
pub1 := priv1.PubKey()
|
||||
i, err = cstore.CreateOffline(o1, pub1)
|
||||
i, err = cstore.CreateOffline(o1, pub1, algo)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, pub1, i.GetPubKey())
|
||||
require.Equal(t, o1, i.GetName())
|
||||
iOffline := i.(*offlineInfo)
|
||||
require.Equal(t, algo, iOffline.GetAlgo())
|
||||
keyS, err = cstore.List()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(keyS))
|
||||
@@ -393,7 +427,8 @@ func TestSeedPhrase(t *testing.T) {
|
||||
|
||||
// let us re-create it from the mnemonic-phrase
|
||||
params := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
|
||||
newInfo, err := cstore.Derive(n2, mnemonic, DefaultBIP39Passphrase, p2, params)
|
||||
hdPath := params.String()
|
||||
newInfo, err := cstore.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, Secp256k1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, n2, newInfo.GetName())
|
||||
require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
|
||||
@@ -402,7 +437,11 @@ func TestSeedPhrase(t *testing.T) {
|
||||
|
||||
func ExampleNew() {
|
||||
// Select the encryption and storage for your cryptostore
|
||||
customKeyGenFunc := func(bz [32]byte) crypto.PrivKey { return secp256k1.PrivKeySecp256k1(bz) }
|
||||
customKeyGenFunc := func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error) {
|
||||
var bzArr [32]byte
|
||||
copy(bzArr[:], bz)
|
||||
return secp256k1.PrivKeySecp256k1(bzArr), nil
|
||||
}
|
||||
cstore := NewInMemory(WithKeygenFunc(customKeyGenFunc))
|
||||
|
||||
sec := Secp256k1
|
||||
|
||||
+29
-24
@@ -20,7 +20,6 @@ import (
|
||||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/input"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/keyerror"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/mintkey"
|
||||
"github.com/cosmos/cosmos-sdk/types"
|
||||
@@ -90,19 +89,10 @@ func (kb keyringKeybase) CreateMnemonic(
|
||||
// CreateAccount converts a mnemonic to a private key and persists it, encrypted
|
||||
// with the given password.
|
||||
func (kb keyringKeybase) CreateAccount(
|
||||
name, mnemonic, bip39Passwd, encryptPasswd string, account, index uint32,
|
||||
name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo,
|
||||
) (Info, error) {
|
||||
|
||||
return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, account, index)
|
||||
}
|
||||
|
||||
// Derive computes a BIP39 seed from th mnemonic and bip39Passphrase. It creates
|
||||
// a private key from the seed using the BIP44 params.
|
||||
func (kb keyringKeybase) Derive(
|
||||
name, mnemonic, bip39Passphrase, encryptPasswd string, params hd.BIP44Params,
|
||||
) (info Info, err error) {
|
||||
|
||||
return kb.base.Derive(kb, name, mnemonic, bip39Passphrase, encryptPasswd, params)
|
||||
return kb.base.CreateAccount(kb, name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo)
|
||||
}
|
||||
|
||||
// CreateLedger creates a new locally-stored reference to a Ledger keypair.
|
||||
@@ -116,8 +106,8 @@ func (kb keyringKeybase) CreateLedger(
|
||||
|
||||
// CreateOffline creates a new reference to an offline keypair. It returns the
|
||||
// created key info.
|
||||
func (kb keyringKeybase) CreateOffline(name string, pub tmcrypto.PubKey) (Info, error) {
|
||||
return kb.base.writeOfflineKey(kb, name, pub), nil
|
||||
func (kb keyringKeybase) CreateOffline(name string, pub tmcrypto.PubKey, algo SigningAlgo) (Info, error) {
|
||||
return kb.base.writeOfflineKey(kb, name, pub, algo), nil
|
||||
}
|
||||
|
||||
// CreateMulti creates a new reference to a multisig (offline) keypair. It
|
||||
@@ -284,7 +274,7 @@ func (kb keyringKeybase) ExportPubKey(name string) (armor string, err error) {
|
||||
return "", fmt.Errorf("no key to export with name: %s", name)
|
||||
}
|
||||
|
||||
return mintkey.ArmorPubKeyBytes(bz.GetPubKey().Bytes()), nil
|
||||
return mintkey.ArmorPubKeyBytes(bz.GetPubKey().Bytes(), string(bz.GetAlgo())), nil
|
||||
}
|
||||
|
||||
// Import imports armored private key.
|
||||
@@ -330,7 +320,12 @@ func (kb keyringKeybase) ExportPrivKey(name, decryptPassphrase, encryptPassphras
|
||||
return "", err
|
||||
}
|
||||
|
||||
return mintkey.EncryptArmorPrivKey(priv, encryptPassphrase), nil
|
||||
info, err := kb.Get(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return mintkey.EncryptArmorPrivKey(priv, encryptPassphrase, string(info.GetAlgo())), nil
|
||||
}
|
||||
|
||||
// ImportPrivKey imports a private key in ASCII armor format. An error is returned
|
||||
@@ -341,13 +336,13 @@ func (kb keyringKeybase) ImportPrivKey(name, armor, passphrase string) error {
|
||||
return fmt.Errorf("cannot overwrite key: %s", name)
|
||||
}
|
||||
|
||||
privKey, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase)
|
||||
privKey, algo, err := mintkey.UnarmorDecryptPrivKey(armor, passphrase)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to decrypt private key")
|
||||
}
|
||||
|
||||
// NOTE: The keyring keystore has no need for a passphrase.
|
||||
kb.writeLocalKey(name, privKey, "")
|
||||
kb.writeLocalKey(name, privKey, "", SigningAlgo(algo))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -370,7 +365,7 @@ func (kb keyringKeybase) ImportPubKey(name string, armor string) error {
|
||||
}
|
||||
}
|
||||
|
||||
pubBytes, err := mintkey.UnarmorPubKeyBytes(armor)
|
||||
pubBytes, algo, err := mintkey.UnarmorPubKeyBytes(armor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -380,7 +375,7 @@ func (kb keyringKeybase) ImportPubKey(name string, armor string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
kb.base.writeOfflineKey(kb, name, pubKey)
|
||||
kb.base.writeOfflineKey(kb, name, pubKey, SigningAlgo(algo))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -419,7 +414,7 @@ func (kb keyringKeybase) Update(name, oldpass string, getNewpass func() (string,
|
||||
|
||||
switch linfo := info.(type) {
|
||||
case localInfo:
|
||||
key, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
|
||||
key, _, err := mintkey.UnarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -429,7 +424,7 @@ func (kb keyringKeybase) Update(name, oldpass string, getNewpass func() (string,
|
||||
return err
|
||||
}
|
||||
|
||||
kb.writeLocalKey(name, key, newpass)
|
||||
kb.writeLocalKey(name, key, newpass, linfo.GetAlgo())
|
||||
return nil
|
||||
|
||||
default:
|
||||
@@ -437,13 +432,23 @@ func (kb keyringKeybase) Update(name, oldpass string, getNewpass func() (string,
|
||||
}
|
||||
}
|
||||
|
||||
// SupportedAlgos returns a list of supported signing algorithms.
|
||||
func (kb keyringKeybase) SupportedAlgos() []SigningAlgo {
|
||||
return kb.base.SupportedAlgos()
|
||||
}
|
||||
|
||||
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
|
||||
func (kb keyringKeybase) SupportedAlgosLedger() []SigningAlgo {
|
||||
return kb.base.SupportedAlgosLedger()
|
||||
}
|
||||
|
||||
// CloseDB releases the lock and closes the storage backend.
|
||||
func (kb keyringKeybase) CloseDB() {}
|
||||
|
||||
func (kb keyringKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, _ string) Info {
|
||||
func (kb keyringKeybase) writeLocalKey(name string, priv tmcrypto.PrivKey, _ string, algo SigningAlgo) Info {
|
||||
// encrypt private key using keyring
|
||||
pub := priv.PubKey()
|
||||
info := newLocalInfo(name, pub, string(priv.Bytes()))
|
||||
info := newLocalInfo(name, pub, string(priv.Bytes()), algo)
|
||||
|
||||
kb.writeInfo(name, info)
|
||||
return info
|
||||
|
||||
@@ -79,7 +79,7 @@ func TestLazyKeyManagementKeyRing(t *testing.T) {
|
||||
o1 := "offline"
|
||||
priv1 := ed25519.GenPrivKey()
|
||||
pub1 := priv1.PubKey()
|
||||
i, err = kb.CreateOffline(o1, pub1)
|
||||
i, err = kb.CreateOffline(o1, pub1, Ed25519)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, pub1, i.GetPubKey())
|
||||
require.Equal(t, o1, i.GetName())
|
||||
@@ -209,10 +209,11 @@ func TestLazyExportImportPubKeyKeyRing(t *testing.T) {
|
||||
defer cleanup()
|
||||
kb, err := NewTestKeyring("keybasename", dir)
|
||||
require.NoError(t, err)
|
||||
algo := Secp256k1
|
||||
|
||||
// CreateMnemonic a private-public key pair and ensure consistency
|
||||
notPasswd := "n9y25ah7"
|
||||
info, _, err := kb.CreateMnemonic("john", English, notPasswd, Secp256k1)
|
||||
info, _, err := kb.CreateMnemonic("john", English, notPasswd, algo)
|
||||
require.Nil(t, err)
|
||||
require.NotEqual(t, info, "")
|
||||
require.Equal(t, info.GetName(), "john")
|
||||
@@ -318,7 +319,8 @@ func TestLazySeedPhraseKeyRing(t *testing.T) {
|
||||
|
||||
// let us re-create it from the mnemonic-phrase
|
||||
params := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
|
||||
newInfo, err := kb.Derive(n2, mnemonic, DefaultBIP39Passphrase, p2, params)
|
||||
hdPath := params.String()
|
||||
newInfo, err := kb.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, Secp256k1)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, n2, newInfo.GetName())
|
||||
require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
|
||||
|
||||
@@ -4,9 +4,13 @@ package keys
|
||||
type SigningAlgo string
|
||||
|
||||
const (
|
||||
// MultiAlgo implies that a pubkey is a multisignature
|
||||
MultiAlgo = SigningAlgo("multi")
|
||||
// Secp256k1 uses the Bitcoin secp256k1 ECDSA parameters.
|
||||
Secp256k1 = SigningAlgo("secp256k1")
|
||||
// Ed25519 represents the Ed25519 signature system.
|
||||
// It is currently not supported for end-user keys (wallets/ledgers).
|
||||
Ed25519 = SigningAlgo("ed25519")
|
||||
// Sr25519 represents the Sr25519 signature system.
|
||||
Sr25519 = SigningAlgo("sr25519")
|
||||
)
|
||||
|
||||
+14
-15
@@ -6,7 +6,6 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/hd"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
@@ -88,7 +87,7 @@ func (lkb lazyKeybase) CreateMnemonic(name string, language Language, passwd str
|
||||
return newDBKeybase(db, lkb.options...).CreateMnemonic(name, language, passwd, algo)
|
||||
}
|
||||
|
||||
func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error) {
|
||||
func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo) (Info, error) {
|
||||
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -96,17 +95,7 @@ func (lkb lazyKeybase) CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd
|
||||
defer db.Close()
|
||||
|
||||
return newDBKeybase(db,
|
||||
lkb.options...).CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, account, index)
|
||||
}
|
||||
|
||||
func (lkb lazyKeybase) Derive(name, mnemonic, bip39Passwd, encryptPasswd string, params hd.BIP44Params) (Info, error) {
|
||||
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
return newDBKeybase(db, lkb.options...).Derive(name, mnemonic, bip39Passwd, encryptPasswd, params)
|
||||
lkb.options...).CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath, algo)
|
||||
}
|
||||
|
||||
func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error) {
|
||||
@@ -119,14 +108,14 @@ func (lkb lazyKeybase) CreateLedger(name string, algo SigningAlgo, hrp string, a
|
||||
return newDBKeybase(db, lkb.options...).CreateLedger(name, algo, hrp, account, index)
|
||||
}
|
||||
|
||||
func (lkb lazyKeybase) CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error) {
|
||||
func (lkb lazyKeybase) CreateOffline(name string, pubkey crypto.PubKey, algo SigningAlgo) (info Info, err error) {
|
||||
db, err := sdk.NewLevelDB(lkb.name, lkb.dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
return newDBKeybase(db, lkb.options...).CreateOffline(name, pubkey)
|
||||
return newDBKeybase(db, lkb.options...).CreateOffline(name, pubkey, algo)
|
||||
}
|
||||
|
||||
func (lkb lazyKeybase) CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error) {
|
||||
@@ -221,4 +210,14 @@ func (lkb lazyKeybase) ExportPrivKey(name string, decryptPassphrase string,
|
||||
return newDBKeybase(db, lkb.options...).ExportPrivKey(name, decryptPassphrase, encryptPassphrase)
|
||||
}
|
||||
|
||||
// SupportedAlgos returns a list of supported signing algorithms.
|
||||
func (lkb lazyKeybase) SupportedAlgos() []SigningAlgo {
|
||||
return newBaseKeybase(lkb.options...).SupportedAlgos()
|
||||
}
|
||||
|
||||
// SupportedAlgosLedger returns a list of supported ledger signing algorithms.
|
||||
func (lkb lazyKeybase) SupportedAlgosLedger() []SigningAlgo {
|
||||
return newBaseKeybase(lkb.options...).SupportedAlgosLedger()
|
||||
}
|
||||
|
||||
func (lkb lazyKeybase) CloseDB() {}
|
||||
|
||||
@@ -89,7 +89,7 @@ func TestLazyKeyManagement(t *testing.T) {
|
||||
o1 := "offline"
|
||||
priv1 := ed25519.GenPrivKey()
|
||||
pub1 := priv1.PubKey()
|
||||
i, err = kb.CreateOffline(o1, pub1)
|
||||
i, err = kb.CreateOffline(o1, pub1, algo)
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, pub1, i.GetPubKey())
|
||||
require.Equal(t, o1, i.GetName())
|
||||
@@ -245,10 +245,11 @@ func TestLazyExportImportPubKey(t *testing.T) {
|
||||
dir, cleanup := tests.NewTestCaseDir(t)
|
||||
defer cleanup()
|
||||
kb := New("keybasename", dir)
|
||||
algo := Secp256k1
|
||||
|
||||
// CreateMnemonic a private-public key pair and ensure consistency
|
||||
notPasswd := "n9y25ah7"
|
||||
info, _, err := kb.CreateMnemonic("john", English, notPasswd, Secp256k1)
|
||||
info, _, err := kb.CreateMnemonic("john", English, notPasswd, algo)
|
||||
require.Nil(t, err)
|
||||
require.NotEqual(t, info, "")
|
||||
require.Equal(t, info.GetName(), "john")
|
||||
@@ -368,7 +369,8 @@ func TestLazySeedPhrase(t *testing.T) {
|
||||
|
||||
// let us re-create it from the mnemonic-phrase
|
||||
params := *hd.NewFundraiserParams(0, sdk.CoinType, 0)
|
||||
newInfo, err := kb.Derive(n2, mnemonic, DefaultBIP39Passphrase, p2, params)
|
||||
hdPath := params.String()
|
||||
newInfo, err := kb.CreateAccount(n2, mnemonic, DefaultBIP39Passphrase, p2, hdPath, algo)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, n2, newInfo.GetName())
|
||||
require.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
|
||||
@@ -421,9 +423,9 @@ func TestKeygenOverride(t *testing.T) {
|
||||
CryptoCdc = testCdc
|
||||
|
||||
overrideCalled := false
|
||||
dummyFunc := func(bz [32]byte) crypto.PrivKey {
|
||||
dummyFunc := func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error) {
|
||||
overrideCalled = true
|
||||
return testPriv(bz[:])
|
||||
return testPriv(bz[:]), nil
|
||||
}
|
||||
|
||||
kb := New("keybasename", dir, WithKeygenFunc(dummyFunc))
|
||||
|
||||
@@ -20,6 +20,11 @@ const (
|
||||
blockTypePrivKey = "TENDERMINT PRIVATE KEY"
|
||||
blockTypeKeyInfo = "TENDERMINT KEY INFO"
|
||||
blockTypePubKey = "TENDERMINT PUBLIC KEY"
|
||||
|
||||
defaultAlgo = "secp256k1"
|
||||
|
||||
headerVersion = "version"
|
||||
headerType = "type"
|
||||
)
|
||||
|
||||
// Make bcrypt security parameter var, so it can be changed within the lcd test
|
||||
@@ -42,36 +47,58 @@ var BcryptSecurityParameter = 12
|
||||
|
||||
// Armor the InfoBytes
|
||||
func ArmorInfoBytes(bz []byte) string {
|
||||
return armorBytes(bz, blockTypeKeyInfo)
|
||||
header := map[string]string{
|
||||
headerType: "Info",
|
||||
headerVersion: "0.0.0",
|
||||
}
|
||||
return armor.EncodeArmor(blockTypeKeyInfo, header, bz)
|
||||
}
|
||||
|
||||
// Armor the PubKeyBytes
|
||||
func ArmorPubKeyBytes(bz []byte) string {
|
||||
return armorBytes(bz, blockTypePubKey)
|
||||
}
|
||||
|
||||
func armorBytes(bz []byte, blockType string) string {
|
||||
func ArmorPubKeyBytes(bz []byte, algo string) string {
|
||||
header := map[string]string{
|
||||
"type": "Info",
|
||||
"version": "0.0.0",
|
||||
headerVersion: "0.0.1",
|
||||
}
|
||||
return armor.EncodeArmor(blockType, header, bz)
|
||||
if algo != "" {
|
||||
header[headerType] = algo
|
||||
}
|
||||
return armor.EncodeArmor(blockTypePubKey, header, bz)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// remove armor
|
||||
|
||||
// Unarmor the InfoBytes
|
||||
func UnarmorInfoBytes(armorStr string) (bz []byte, err error) {
|
||||
return unarmorBytes(armorStr, blockTypeKeyInfo)
|
||||
func UnarmorInfoBytes(armorStr string) ([]byte, error) {
|
||||
bz, header, err := unarmorBytes(armorStr, blockTypeKeyInfo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if header[headerVersion] != "0.0.0" {
|
||||
return nil, fmt.Errorf("unrecognized version: %v", header[headerVersion])
|
||||
}
|
||||
return bz, nil
|
||||
}
|
||||
|
||||
// Unarmor the PubKeyBytes
|
||||
func UnarmorPubKeyBytes(armorStr string) (bz []byte, err error) {
|
||||
return unarmorBytes(armorStr, blockTypePubKey)
|
||||
// UnarmorPubKeyBytes returns the pubkey byte slice, a string of the algo type, and an error
|
||||
func UnarmorPubKeyBytes(armorStr string) (bz []byte, algo string, err error) {
|
||||
bz, header, err := unarmorBytes(armorStr, blockTypePubKey)
|
||||
switch header[headerVersion] {
|
||||
case "0.0.0":
|
||||
return bz, defaultAlgo, err
|
||||
case "0.0.1":
|
||||
if header[headerType] == "" {
|
||||
header[headerType] = defaultAlgo
|
||||
}
|
||||
return bz, header[headerType], err
|
||||
default:
|
||||
err = fmt.Errorf("unrecognized version: %v", header[headerVersion])
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
func unarmorBytes(armorStr, blockType string) (bz []byte, err error) {
|
||||
func unarmorBytes(armorStr, blockType string) (bz []byte, header map[string]string, err error) {
|
||||
bType, header, bz, err := armor.DecodeArmor(armorStr)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -80,10 +107,6 @@ func unarmorBytes(armorStr, blockType string) (bz []byte, err error) {
|
||||
err = fmt.Errorf("unrecognized armor type %q, expected: %q", bType, blockType)
|
||||
return
|
||||
}
|
||||
if header["version"] != "0.0.0" {
|
||||
err = fmt.Errorf("unrecognized version: %v", header["version"])
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -91,12 +114,15 @@ func unarmorBytes(armorStr, blockType string) (bz []byte, err error) {
|
||||
// encrypt/decrypt with armor
|
||||
|
||||
// Encrypt and armor the private key.
|
||||
func EncryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string {
|
||||
func EncryptArmorPrivKey(privKey crypto.PrivKey, passphrase string, algo string) string {
|
||||
saltBytes, encBytes := encryptPrivKey(privKey, passphrase)
|
||||
header := map[string]string{
|
||||
"kdf": "bcrypt",
|
||||
"salt": fmt.Sprintf("%X", saltBytes),
|
||||
}
|
||||
if algo != "" {
|
||||
header[headerType] = algo
|
||||
}
|
||||
armorStr := armor.EncodeArmor(blockTypePrivKey, header, encBytes)
|
||||
return armorStr
|
||||
}
|
||||
@@ -115,28 +141,31 @@ func encryptPrivKey(privKey crypto.PrivKey, passphrase string) (saltBytes []byte
|
||||
return saltBytes, xsalsa20symmetric.EncryptSymmetric(privKeyBytes, key)
|
||||
}
|
||||
|
||||
// Unarmor and decrypt the private key.
|
||||
func UnarmorDecryptPrivKey(armorStr string, passphrase string) (crypto.PrivKey, error) {
|
||||
var privKey crypto.PrivKey
|
||||
// UnarmorDecryptPrivKey returns the privkey byte slice, a string of the algo type, and an error
|
||||
func UnarmorDecryptPrivKey(armorStr string, passphrase string) (privKey crypto.PrivKey, algo string, err error) {
|
||||
blockType, header, encBytes, err := armor.DecodeArmor(armorStr)
|
||||
if err != nil {
|
||||
return privKey, err
|
||||
return privKey, "", err
|
||||
}
|
||||
if blockType != blockTypePrivKey {
|
||||
return privKey, fmt.Errorf("unrecognized armor type: %v", blockType)
|
||||
return privKey, "", fmt.Errorf("unrecognized armor type: %v", blockType)
|
||||
}
|
||||
if header["kdf"] != "bcrypt" {
|
||||
return privKey, fmt.Errorf("unrecognized KDF type: %v", header["KDF"])
|
||||
return privKey, "", fmt.Errorf("unrecognized KDF type: %v", header["KDF"])
|
||||
}
|
||||
if header["salt"] == "" {
|
||||
return privKey, fmt.Errorf("missing salt bytes")
|
||||
return privKey, "", fmt.Errorf("missing salt bytes")
|
||||
}
|
||||
saltBytes, err := hex.DecodeString(header["salt"])
|
||||
if err != nil {
|
||||
return privKey, fmt.Errorf("error decoding salt: %v", err.Error())
|
||||
return privKey, "", fmt.Errorf("error decoding salt: %v", err.Error())
|
||||
}
|
||||
privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase)
|
||||
return privKey, err
|
||||
|
||||
if header[headerType] == "" {
|
||||
header[headerType] = defaultAlgo
|
||||
}
|
||||
return privKey, header[headerType], err
|
||||
}
|
||||
|
||||
func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) {
|
||||
|
||||
@@ -13,11 +13,12 @@ import (
|
||||
|
||||
func TestArmorUnarmorPrivKey(t *testing.T) {
|
||||
priv := secp256k1.GenPrivKey()
|
||||
armor := mintkey.EncryptArmorPrivKey(priv, "passphrase")
|
||||
_, err := mintkey.UnarmorDecryptPrivKey(armor, "wrongpassphrase")
|
||||
armor := mintkey.EncryptArmorPrivKey(priv, "passphrase", "")
|
||||
_, _, err := mintkey.UnarmorDecryptPrivKey(armor, "wrongpassphrase")
|
||||
require.Error(t, err)
|
||||
decrypted, err := mintkey.UnarmorDecryptPrivKey(armor, "passphrase")
|
||||
decrypted, algo, err := mintkey.UnarmorDecryptPrivKey(armor, "passphrase")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(keys.Secp256k1), algo)
|
||||
require.True(t, priv.Equals(decrypted))
|
||||
}
|
||||
|
||||
@@ -28,10 +29,11 @@ func TestArmorUnarmorPubKey(t *testing.T) {
|
||||
// Add keys and see they return in alphabetical order
|
||||
info, _, err := cstore.CreateMnemonic("Bob", keys.English, "passphrase", keys.Secp256k1)
|
||||
require.NoError(t, err)
|
||||
armor := mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes())
|
||||
pubBytes, err := mintkey.UnarmorPubKeyBytes(armor)
|
||||
armor := mintkey.ArmorPubKeyBytes(info.GetPubKey().Bytes(), "")
|
||||
pubBytes, algo, err := mintkey.UnarmorPubKeyBytes(armor)
|
||||
require.NoError(t, err)
|
||||
pub, err := cryptoAmino.PubKeyFromBytes(pubBytes)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, string(keys.Secp256k1), algo)
|
||||
require.True(t, pub.Equals(info.GetPubKey()))
|
||||
}
|
||||
|
||||
+43
-13
@@ -31,21 +31,15 @@ type Keybase interface {
|
||||
// same name.
|
||||
CreateMnemonic(name string, language Language, passwd string, algo SigningAlgo) (info Info, seed string, err error)
|
||||
|
||||
// CreateAccount converts a mnemonic to a private key using a BIP44 path 44'/118'/{account}'/0/{index}
|
||||
// CreateAccount converts a mnemonic to a private key and BIP 32 HD Path
|
||||
// and persists it, encrypted with the given password.
|
||||
CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd string, account uint32, index uint32) (Info, error)
|
||||
|
||||
// Derive computes a BIP39 seed from th mnemonic and bip39Passwd.
|
||||
// Derive private key from the seed using the BIP44 params.
|
||||
// Encrypt the key to disk using encryptPasswd.
|
||||
// See https://github.com/cosmos/cosmos-sdk/issues/2095
|
||||
Derive(name, mnemonic, bip39Passwd, encryptPasswd string, params hd.BIP44Params) (Info, error)
|
||||
CreateAccount(name, mnemonic, bip39Passwd, encryptPasswd, hdPath string, algo SigningAlgo) (Info, error)
|
||||
|
||||
// CreateLedger creates, stores, and returns a new Ledger key reference
|
||||
CreateLedger(name string, algo SigningAlgo, hrp string, account, index uint32) (info Info, err error)
|
||||
|
||||
// CreateOffline creates, stores, and returns a new offline key reference
|
||||
CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error)
|
||||
CreateOffline(name string, pubkey crypto.PubKey, algo SigningAlgo) (info Info, err error)
|
||||
|
||||
// CreateMulti creates, stores, and returns a new multsig (offline) key reference
|
||||
CreateMulti(name string, pubkey crypto.PubKey) (info Info, err error)
|
||||
@@ -81,6 +75,12 @@ type Keybase interface {
|
||||
// ExportPrivateKeyObject *only* works on locally-stored keys. Temporary method until we redo the exporting API
|
||||
ExportPrivateKeyObject(name string, passphrase string) (crypto.PrivKey, error)
|
||||
|
||||
// SupportedAlgos returns a list of signing algorithms supported by the keybase
|
||||
SupportedAlgos() []SigningAlgo
|
||||
|
||||
// SupportedAlgosLedger returns a list of signing algorithms supported by the keybase's ledger integration
|
||||
SupportedAlgosLedger() []SigningAlgo
|
||||
|
||||
// CloseDB closes the database.
|
||||
CloseDB()
|
||||
}
|
||||
@@ -118,6 +118,8 @@ type Info interface {
|
||||
GetPubKey() crypto.PubKey
|
||||
// Address
|
||||
GetAddress() types.AccAddress
|
||||
// Algo
|
||||
GetAlgo() SigningAlgo
|
||||
// Bip44 Path
|
||||
GetPath() (*hd.BIP44Params, error)
|
||||
}
|
||||
@@ -132,15 +134,17 @@ var (
|
||||
// localInfo is the public information about a locally stored key
|
||||
type localInfo struct {
|
||||
Name string `json:"name"`
|
||||
Algo SigningAlgo `json:"algo"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
PrivKeyArmor string `json:"privkey.armor"`
|
||||
}
|
||||
|
||||
func newLocalInfo(name string, pub crypto.PubKey, privArmor string) Info {
|
||||
func newLocalInfo(name string, pub crypto.PubKey, privArmor string, algo SigningAlgo) Info {
|
||||
return &localInfo{
|
||||
Name: name,
|
||||
PubKey: pub,
|
||||
PrivKeyArmor: privArmor,
|
||||
Algo: algo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +168,11 @@ func (i localInfo) GetAddress() types.AccAddress {
|
||||
return i.PubKey.Address().Bytes()
|
||||
}
|
||||
|
||||
// GetType implements Info interface
|
||||
func (i localInfo) GetAlgo() SigningAlgo {
|
||||
return i.Algo
|
||||
}
|
||||
|
||||
// GetType implements Info interface
|
||||
func (i localInfo) GetPath() (*hd.BIP44Params, error) {
|
||||
return nil, fmt.Errorf("BIP44 Paths are not available for this type")
|
||||
@@ -174,13 +183,15 @@ type ledgerInfo struct {
|
||||
Name string `json:"name"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
Path hd.BIP44Params `json:"path"`
|
||||
Algo SigningAlgo `json:"algo"`
|
||||
}
|
||||
|
||||
func newLedgerInfo(name string, pub crypto.PubKey, path hd.BIP44Params) Info {
|
||||
func newLedgerInfo(name string, pub crypto.PubKey, path hd.BIP44Params, algo SigningAlgo) Info {
|
||||
return &ledgerInfo{
|
||||
Name: name,
|
||||
PubKey: pub,
|
||||
Path: path,
|
||||
Algo: algo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,6 +215,11 @@ func (i ledgerInfo) GetAddress() types.AccAddress {
|
||||
return i.PubKey.Address().Bytes()
|
||||
}
|
||||
|
||||
// GetPath implements Info interface
|
||||
func (i ledgerInfo) GetAlgo() SigningAlgo {
|
||||
return i.Algo
|
||||
}
|
||||
|
||||
// GetPath implements Info interface
|
||||
func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) {
|
||||
tmp := i.Path
|
||||
@@ -213,13 +229,15 @@ func (i ledgerInfo) GetPath() (*hd.BIP44Params, error) {
|
||||
// offlineInfo is the public information about an offline key
|
||||
type offlineInfo struct {
|
||||
Name string `json:"name"`
|
||||
Algo SigningAlgo `json:"algo"`
|
||||
PubKey crypto.PubKey `json:"pubkey"`
|
||||
}
|
||||
|
||||
func newOfflineInfo(name string, pub crypto.PubKey) Info {
|
||||
func newOfflineInfo(name string, pub crypto.PubKey, algo SigningAlgo) Info {
|
||||
return &offlineInfo{
|
||||
Name: name,
|
||||
PubKey: pub,
|
||||
Algo: algo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,6 +256,11 @@ func (i offlineInfo) GetPubKey() crypto.PubKey {
|
||||
return i.PubKey
|
||||
}
|
||||
|
||||
// GetAlgo returns the signing algorithm for the key
|
||||
func (i offlineInfo) GetAlgo() SigningAlgo {
|
||||
return i.Algo
|
||||
}
|
||||
|
||||
// GetAddress implements Info interface
|
||||
func (i offlineInfo) GetAddress() types.AccAddress {
|
||||
return i.PubKey.Address().Bytes()
|
||||
@@ -299,6 +322,11 @@ func (i multiInfo) GetAddress() types.AccAddress {
|
||||
return i.PubKey.Address().Bytes()
|
||||
}
|
||||
|
||||
// GetPath implements Info interface
|
||||
func (i multiInfo) GetAlgo() SigningAlgo {
|
||||
return MultiAlgo
|
||||
}
|
||||
|
||||
// GetPath implements Info interface
|
||||
func (i multiInfo) GetPath() (*hd.BIP44Params, error) {
|
||||
return nil, fmt.Errorf("BIP44 Paths are not available for this type")
|
||||
@@ -316,8 +344,10 @@ func unmarshalInfo(bz []byte) (info Info, err error) {
|
||||
}
|
||||
|
||||
type (
|
||||
// DeriveKeyFunc defines the function to derive a new key from a seed and hd path
|
||||
DeriveKeyFunc func(mnemonic string, bip39Passphrase, hdPath string, algo SigningAlgo) ([]byte, error)
|
||||
// PrivKeyGenFunc defines the function to convert derived key bytes to a tendermint private key
|
||||
PrivKeyGenFunc func(bz [32]byte) crypto.PrivKey
|
||||
PrivKeyGenFunc func(bz []byte, algo SigningAlgo) (crypto.PrivKey, error)
|
||||
|
||||
// KeybaseOption overrides options for the db
|
||||
KeybaseOption func(*kbOptions)
|
||||
|
||||
@@ -17,7 +17,7 @@ func Test_writeReadLedgerInfo(t *testing.T) {
|
||||
bz, _ := hex.DecodeString("035AD6810A47F073553FF30D2FCC7E0D3B1C0B74B61A1AAA2582344037151E143A")
|
||||
copy(tmpKey[:], bz)
|
||||
|
||||
lInfo := newLedgerInfo("some_name", tmpKey, *hd.NewFundraiserParams(5, types.CoinType, 1))
|
||||
lInfo := newLedgerInfo("some_name", tmpKey, *hd.NewFundraiserParams(5, types.CoinType, 1), Secp256k1)
|
||||
assert.Equal(t, TypeLedger, lInfo.GetType())
|
||||
|
||||
path, err := lInfo.GetPath()
|
||||
|
||||
Reference in New Issue
Block a user