mirror of
https://github.com/tendermint/tendermint.git
synced 2026-09-17 05:24:42 +00:00
+1
-1
@@ -18,4 +18,4 @@ dependencies:
|
||||
test:
|
||||
override:
|
||||
- "go version"
|
||||
- "cd $PROJECT_PATH && make all"
|
||||
- "cd $PROJECT_PATH && make get_tools && make all"
|
||||
|
||||
@@ -21,19 +21,30 @@ import (
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/btcsuite/btcutil/base58"
|
||||
"github.com/tendermint/go-crypto"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
func ComputeAddress(pubKeyHex string, chainHex string, path string, index int32) string {
|
||||
/*
|
||||
|
||||
This file implements BIP32 HD wallets.
|
||||
Note it only works for SECP256k1 keys.
|
||||
It also includes some Bitcoin specific utility functions.
|
||||
|
||||
*/
|
||||
|
||||
// ComputeBTCAddress returns the BTC address using the pubKeyHex and chainCodeHex
|
||||
// for the given path and index.
|
||||
func ComputeBTCAddress(pubKeyHex string, chainCodeHex string, path string, index int32) string {
|
||||
pubKeyBytes := DerivePublicKeyForPath(
|
||||
HexDecode(pubKeyHex),
|
||||
HexDecode(chainHex),
|
||||
HexDecode(chainCodeHex),
|
||||
fmt.Sprintf("%v/%v", path, index),
|
||||
)
|
||||
return AddrFromPubKeyBytes(pubKeyBytes)
|
||||
return BTCAddrFromPubKeyBytes(pubKeyBytes)
|
||||
}
|
||||
|
||||
// ComputePrivateKey returns the private key using the master mprivHex and chainCodeHex
|
||||
// for the given path and index.
|
||||
func ComputePrivateKey(mprivHex string, chainHex string, path string, index int32) string {
|
||||
privKeyBytes := DerivePrivateKeyForPath(
|
||||
HexDecode(mprivHex),
|
||||
@@ -43,12 +54,14 @@ func ComputePrivateKey(mprivHex string, chainHex string, path string, index int3
|
||||
return HexEncode(privKeyBytes)
|
||||
}
|
||||
|
||||
func ComputeAddressForPrivKey(privKey string) string {
|
||||
// ComputeBTCAddressForPrivKey returns the Bitcoin address for the given privKey.
|
||||
func ComputeBTCAddressForPrivKey(privKey string) string {
|
||||
pubKeyBytes := PubKeyBytesFromPrivKeyBytes(HexDecode(privKey), true)
|
||||
return AddrFromPubKeyBytes(pubKeyBytes)
|
||||
return BTCAddrFromPubKeyBytes(pubKeyBytes)
|
||||
}
|
||||
|
||||
func SignMessage(privKey string, message string, compress bool) string {
|
||||
// SignBTCMessage signs a "Bitcoin Signed Message".
|
||||
func SignBTCMessage(privKey string, message string, compress bool) string {
|
||||
prefixBytes := []byte("Bitcoin Signed Message:\n")
|
||||
messageBytes := []byte(message)
|
||||
bytes := []byte{}
|
||||
@@ -67,25 +80,28 @@ func SignMessage(privKey string, message string, compress bool) string {
|
||||
PublicKey: ecdsaPubKey,
|
||||
D: new(big.Int).SetBytes(privKeyBytes),
|
||||
}
|
||||
sigbytes, err := btcec.SignCompact(btcec.S256(), ecdsaPrivKey, crypto.Sha256(crypto.Sha256(bytes)), compress)
|
||||
sigbytes, err := btcec.SignCompact(btcec.S256(), ecdsaPrivKey, CalcHash256(bytes), compress)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(sigbytes)
|
||||
}
|
||||
|
||||
// returns MPK, Chain, and master secret in hex.
|
||||
func ComputeMastersFromSeed(seed string) (string, string, string, string) {
|
||||
secret, chain := I64([]byte("Bitcoin seed"), []byte(seed))
|
||||
// ComputeMastersFromSeed returns the master public key, master secret, and chain code in hex.
|
||||
func ComputeMastersFromSeed(seed string) (string, string, string) {
|
||||
key, data := []byte("Bitcoin seed"), []byte(seed)
|
||||
secret, chain := I64(key, data)
|
||||
pubKeyBytes := PubKeyBytesFromPrivKeyBytes(secret, true)
|
||||
return HexEncode(pubKeyBytes), HexEncode(secret), HexEncode(chain), HexEncode(secret)
|
||||
return HexEncode(pubKeyBytes), HexEncode(secret), HexEncode(chain)
|
||||
}
|
||||
|
||||
// ComputeWIF returns the privKey in Wallet Import Format.
|
||||
func ComputeWIF(privKey string, compress bool) string {
|
||||
return WIFFromPrivKeyBytes(HexDecode(privKey), compress)
|
||||
}
|
||||
|
||||
func ComputeTxId(rawTxHex string) string {
|
||||
// ComputeBTCTxId returns the bitcoin transaction ID.
|
||||
func ComputeBTCTxId(rawTxHex string) string {
|
||||
return HexEncode(ReverseBytes(CalcHash256(HexDecode(rawTxHex))))
|
||||
}
|
||||
|
||||
@@ -103,7 +119,11 @@ func printKeyInfo(privKeyBytes []byte, pubKeyBytes []byte, chain []byte) {
|
||||
}
|
||||
*/
|
||||
|
||||
func DerivePrivateKeyForPath(privKeyBytes []byte, chain []byte, path string) []byte {
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
// DerivePrivateKeyForPath derives the private key by following the path from privKeyBytes,
|
||||
// using the given chainCode.
|
||||
func DerivePrivateKeyForPath(privKeyBytes []byte, chainCode []byte, path string) []byte {
|
||||
data := privKeyBytes
|
||||
parts := strings.Split(path, "/")
|
||||
for _, part := range parts {
|
||||
@@ -119,13 +139,15 @@ func DerivePrivateKeyForPath(privKeyBytes []byte, chain []byte, path string) []b
|
||||
if i < 0 {
|
||||
panic(errors.New("index too large."))
|
||||
}
|
||||
data, chain = DerivePrivateKey(data, chain, uint32(i), prime)
|
||||
data, chainCode = DerivePrivateKey(data, chainCode, uint32(i), prime)
|
||||
//printKeyInfo(data, nil, chain)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func DerivePublicKeyForPath(pubKeyBytes []byte, chain []byte, path string) []byte {
|
||||
// DerivePublicKeyForPath derives the public key by following the path from pubKeyBytes
|
||||
// using the given chainCode.
|
||||
func DerivePublicKeyForPath(pubKeyBytes []byte, chainCode []byte, path string) []byte {
|
||||
data := pubKeyBytes
|
||||
parts := strings.Split(path, "/")
|
||||
for _, part := range parts {
|
||||
@@ -140,36 +162,42 @@ func DerivePublicKeyForPath(pubKeyBytes []byte, chain []byte, path string) []byt
|
||||
if i < 0 {
|
||||
panic(errors.New("index too large."))
|
||||
}
|
||||
data, chain = DerivePublicKey(data, chain, uint32(i))
|
||||
//printKeyInfo(nil, data, chain)
|
||||
data, chainCode = DerivePublicKey(data, chainCode, uint32(i))
|
||||
//printKeyInfo(nil, data, chainCode)
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func DerivePrivateKey(privKeyBytes []byte, chain []byte, i uint32, prime bool) ([]byte, []byte) {
|
||||
// DerivePrivateKey derives the private key with index and chainCode.
|
||||
// If prime is true, the derivation is 'hardened'.
|
||||
// It returns the new private key and new chain code.
|
||||
func DerivePrivateKey(privKeyBytes []byte, chainCode []byte, index uint32, prime bool) ([]byte, []byte) {
|
||||
var data []byte
|
||||
if prime {
|
||||
i = i | 0x80000000
|
||||
index = index | 0x80000000
|
||||
data = append([]byte{byte(0)}, privKeyBytes...)
|
||||
} else {
|
||||
public := PubKeyBytesFromPrivKeyBytes(privKeyBytes, true)
|
||||
data = public
|
||||
}
|
||||
data = append(data, uint32ToBytes(i)...)
|
||||
data2, chain2 := I64(chain, data)
|
||||
data = append(data, uint32ToBytes(index)...)
|
||||
data2, chainCode2 := I64(chainCode, data)
|
||||
x := addScalars(privKeyBytes, data2)
|
||||
return x, chain2
|
||||
return x, chainCode2
|
||||
}
|
||||
|
||||
func DerivePublicKey(pubKeyBytes []byte, chain []byte, i uint32) ([]byte, []byte) {
|
||||
// DerivePublicKey derives the public key with index and chainCode.
|
||||
// It returns the new public key and new chain code.
|
||||
func DerivePublicKey(pubKeyBytes []byte, chainCode []byte, index uint32) ([]byte, []byte) {
|
||||
data := []byte{}
|
||||
data = append(data, pubKeyBytes...)
|
||||
data = append(data, uint32ToBytes(i)...)
|
||||
data2, chain2 := I64(chain, data)
|
||||
data = append(data, uint32ToBytes(index)...)
|
||||
data2, chainCode2 := I64(chainCode, data)
|
||||
data2p := PubKeyBytesFromPrivKeyBytes(data2, true)
|
||||
return addPoints(pubKeyBytes, data2p), chain2
|
||||
return addPoints(pubKeyBytes, data2p), chainCode2
|
||||
}
|
||||
|
||||
// eliptic curve pubkey addition
|
||||
func addPoints(a []byte, b []byte) []byte {
|
||||
ap, err := btcec.ParsePubKey(a, btcec.S256())
|
||||
if err != nil {
|
||||
@@ -188,6 +216,7 @@ func addPoints(a []byte, b []byte) []byte {
|
||||
return sum.SerializeCompressed()
|
||||
}
|
||||
|
||||
// modular big endian addition
|
||||
func addScalars(a []byte, b []byte) []byte {
|
||||
aInt := new(big.Int).SetBytes(a)
|
||||
bInt := new(big.Int).SetBytes(b)
|
||||
@@ -204,15 +233,21 @@ func uint32ToBytes(i uint32) []byte {
|
||||
return b[:]
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
// HexEncode encodes b in hex.
|
||||
func HexEncode(b []byte) string {
|
||||
return hex.EncodeToString(b)
|
||||
}
|
||||
|
||||
// HexDecode hex decodes the str. If str is not valid hex
|
||||
// it will return an empty byte slice.
|
||||
func HexDecode(str string) []byte {
|
||||
b, _ := hex.DecodeString(str)
|
||||
return b
|
||||
}
|
||||
|
||||
// I64 returns the two halfs of the SHA512 HMAC of key and data.
|
||||
func I64(key []byte, data []byte) ([]byte, []byte) {
|
||||
mac := hmac.New(sha512.New, key)
|
||||
mac.Write(data)
|
||||
@@ -220,27 +255,36 @@ func I64(key []byte, data []byte) ([]byte, []byte) {
|
||||
return I[:32], I[32:]
|
||||
}
|
||||
|
||||
// This returns a Bitcoin-like address.
|
||||
func AddrFromPubKeyBytes(pubKeyBytes []byte) string {
|
||||
prefix := byte(0x00) // TODO Make const or configurable
|
||||
//-------------------------------------------------------------------
|
||||
|
||||
const (
|
||||
btcPrefixPubKeyHash = byte(0x00)
|
||||
btcPrefixPrivKey = byte(0x80)
|
||||
)
|
||||
|
||||
// BTCAddrFromPubKeyBytes returns a B58 encoded Bitcoin mainnet address.
|
||||
func BTCAddrFromPubKeyBytes(pubKeyBytes []byte) string {
|
||||
versionPrefix := btcPrefixPubKeyHash // TODO Make const or configurable
|
||||
h160 := CalcHash160(pubKeyBytes)
|
||||
h160 = append([]byte{prefix}, h160...)
|
||||
h160 = append([]byte{versionPrefix}, h160...)
|
||||
checksum := CalcHash256(h160)
|
||||
b := append(h160, checksum[:4]...)
|
||||
return base58.Encode(b)
|
||||
}
|
||||
|
||||
func AddrBytesFromPubKeyBytes(pubKeyBytes []byte) (addrBytes []byte, checksum []byte) {
|
||||
prefix := byte(0x00) // TODO Make const or configurable
|
||||
// BTCAddrBytesFromPubKeyBytes returns a hex Bitcoin mainnet address and its checksum.
|
||||
func BTCAddrBytesFromPubKeyBytes(pubKeyBytes []byte) (addrBytes []byte, checksum []byte) {
|
||||
versionPrefix := btcPrefixPubKeyHash // TODO Make const or configurable
|
||||
h160 := CalcHash160(pubKeyBytes)
|
||||
_h160 := append([]byte{prefix}, h160...)
|
||||
_h160 := append([]byte{versionPrefix}, h160...)
|
||||
checksum = CalcHash256(_h160)[:4]
|
||||
return h160, checksum
|
||||
}
|
||||
|
||||
// WIFFromPrivKeyBytes returns the privKeyBytes in Wallet Import Format.
|
||||
func WIFFromPrivKeyBytes(privKeyBytes []byte, compress bool) string {
|
||||
prefix := byte(0x80) // TODO Make const or configurable
|
||||
bytes := append([]byte{prefix}, privKeyBytes...)
|
||||
versionPrefix := btcPrefixPrivKey // TODO Make const or configurable
|
||||
bytes := append([]byte{versionPrefix}, privKeyBytes...)
|
||||
if compress {
|
||||
bytes = append(bytes, byte(1))
|
||||
}
|
||||
@@ -249,6 +293,7 @@ func WIFFromPrivKeyBytes(privKeyBytes []byte, compress bool) string {
|
||||
return base58.Encode(bytes)
|
||||
}
|
||||
|
||||
// PubKeyBytesFromPrivKeyBytes returns the optionally compressed public key bytes.
|
||||
func PubKeyBytesFromPrivKeyBytes(privKeyBytes []byte, compress bool) (pubKeyBytes []byte) {
|
||||
x, y := btcec.S256().ScalarBaseMult(privKeyBytes)
|
||||
pub := &btcec.PublicKey{
|
||||
@@ -263,27 +308,30 @@ func PubKeyBytesFromPrivKeyBytes(privKeyBytes []byte, compress bool) (pubKeyByte
|
||||
return pub.SerializeUncompressed()
|
||||
}
|
||||
|
||||
// Calculate the hash of hasher over buf.
|
||||
func CalcHash(buf []byte, hasher hash.Hash) []byte {
|
||||
hasher.Write(buf)
|
||||
//--------------------------------------------------------------
|
||||
|
||||
// CalcHash returns the hash of data using hasher.
|
||||
func CalcHash(data []byte, hasher hash.Hash) []byte {
|
||||
hasher.Write(data)
|
||||
return hasher.Sum(nil)
|
||||
}
|
||||
|
||||
// calculate hash160 which is ripemd160(sha256(data))
|
||||
func CalcHash160(buf []byte) []byte {
|
||||
return CalcHash(CalcHash(buf, sha256.New()), ripemd160.New())
|
||||
// CalcHash160 returns the ripemd160(sha256(data)).
|
||||
func CalcHash160(data []byte) []byte {
|
||||
return CalcHash(CalcHash(data, sha256.New()), ripemd160.New())
|
||||
}
|
||||
|
||||
// calculate hash256 which is sha256(sha256(data))
|
||||
func CalcHash256(buf []byte) []byte {
|
||||
return CalcHash(CalcHash(buf, sha256.New()), sha256.New())
|
||||
// CalcHash256 returns the sha256(sha256(data)).
|
||||
func CalcHash256(data []byte) []byte {
|
||||
return CalcHash(CalcHash(data, sha256.New()), sha256.New())
|
||||
}
|
||||
|
||||
// calculate sha512(data)
|
||||
func CalcSha512(buf []byte) []byte {
|
||||
return CalcHash(buf, sha512.New())
|
||||
// CalcSha512 returns the sha512(data).
|
||||
func CalcSha512(data []byte) []byte {
|
||||
return CalcHash(data, sha512.New())
|
||||
}
|
||||
|
||||
// ReverseBytes returns the buf in the opposite order
|
||||
func ReverseBytes(buf []byte) []byte {
|
||||
var res []byte
|
||||
if len(buf) == 0 {
|
||||
@@ -112,7 +112,7 @@ func ifExit(err error, n int) {
|
||||
|
||||
func gocrypto(seed []byte) ([]byte, []byte, []byte) {
|
||||
|
||||
_, priv, ch, _ := ComputeMastersFromSeed(string(seed))
|
||||
_, priv, ch := ComputeMastersFromSeed(string(seed))
|
||||
|
||||
privBytes := DerivePrivateKeyForPath(
|
||||
HexDecode(priv),
|
||||
+77
-78
@@ -8,6 +8,7 @@ import (
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
dbm "github.com/tendermint/tmlibs/db"
|
||||
|
||||
"github.com/tendermint/go-crypto/keys/words"
|
||||
"github.com/tendermint/go-crypto/nano"
|
||||
)
|
||||
|
||||
@@ -19,10 +20,10 @@ import (
|
||||
// a full-featured key manager
|
||||
type dbKeybase struct {
|
||||
db dbm.DB
|
||||
codec Codec
|
||||
codec words.Codec
|
||||
}
|
||||
|
||||
func New(db dbm.DB, codec Codec) dbKeybase {
|
||||
func New(db dbm.DB, codec words.Codec) dbKeybase {
|
||||
return dbKeybase{
|
||||
db: db,
|
||||
codec: codec,
|
||||
@@ -31,59 +32,61 @@ func New(db dbm.DB, codec Codec) dbKeybase {
|
||||
|
||||
var _ Keybase = dbKeybase{}
|
||||
|
||||
// Create adds a new key to the storage engine, returning error if
|
||||
// another key already stored under this name
|
||||
//
|
||||
// algo must be a supported go-crypto algorithm: ed25519, secp256k1
|
||||
func (kb dbKeybase) Create(name, passphrase, algo string) (Info, string, error) {
|
||||
// 128-bits are the all the randomness we can make use of
|
||||
// Create generates a new key and persists it storage, encrypted using the passphrase.
|
||||
// It returns the generated seedphrase (mnemonic) and the key Info.
|
||||
// It returns an error if it fails to generate a key for the given algo type,
|
||||
// or if another key is already stored under the same name.
|
||||
func (kb dbKeybase) Create(name, passphrase, algo string) (string, Info, error) {
|
||||
// NOTE: secret is SHA256 hashed by secp256k1 and ed25519.
|
||||
// 16 byte secret corresponds to 12 BIP39 words.
|
||||
// XXX: Ledgers use 24 words now - should we ?
|
||||
secret := crypto.CRandBytes(16)
|
||||
key, err := generate(algo, secret)
|
||||
if err != nil {
|
||||
return Info{}, "", err
|
||||
return "", Info{}, err
|
||||
}
|
||||
|
||||
// encrypt and persist the key
|
||||
public := kb.writeKey(key, name, passphrase)
|
||||
|
||||
// we append the type byte to the serialized secret to help with recovery
|
||||
// ie [secret] = [secret] + [type]
|
||||
typ := key.Bytes()[0]
|
||||
secret = append(secret, typ)
|
||||
|
||||
seed, err := kb.codec.BytesToWords(secret)
|
||||
phrase := strings.Join(seed, " ")
|
||||
return public, phrase, err
|
||||
// return the mnemonic phrase
|
||||
words, err := kb.codec.BytesToWords(secret)
|
||||
seedphrase := strings.Join(words, " ")
|
||||
return seedphrase, public, err
|
||||
}
|
||||
|
||||
// Recover takes a seed phrase and tries to recover the private key.
|
||||
//
|
||||
// If the seed phrase is valid, it will create the private key and store
|
||||
// it under name, protected by passphrase.
|
||||
//
|
||||
// Result similar to New(), except it doesn't return the seed again...
|
||||
func (kb dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) {
|
||||
// Recover converts a seedphrase to a private key and persists it, encrypted with the given passphrase.
|
||||
// Functions like Create, but seedphrase is input not output.
|
||||
func (kb dbKeybase) Recover(name, passphrase, algo string, seedphrase string) (Info, error) {
|
||||
|
||||
key, err := kb.SeedToPrivKey(algo, seedphrase)
|
||||
if err != nil {
|
||||
return Info{}, err
|
||||
}
|
||||
|
||||
// Valid seedphrase. Encrypt key and persist to disk.
|
||||
public := kb.writeKey(key, name, passphrase)
|
||||
return public, nil
|
||||
}
|
||||
|
||||
// SeedToPrivKey returns the private key corresponding to a seedphrase
|
||||
// without persisting the private key.
|
||||
// TODO: enable the keybase to just hold these in memory so we can sign without persisting (?)
|
||||
func (kb dbKeybase) SeedToPrivKey(algo, seedphrase string) (crypto.PrivKey, error) {
|
||||
words := strings.Split(strings.TrimSpace(seedphrase), " ")
|
||||
secret, err := kb.codec.WordsToBytes(words)
|
||||
if err != nil {
|
||||
return Info{}, err
|
||||
return crypto.PrivKey{}, err
|
||||
}
|
||||
|
||||
// secret is comprised of the actual secret with the type appended
|
||||
// ie [secret] = [secret] + [type]
|
||||
l := len(secret)
|
||||
secret, typ := secret[:l-1], secret[l-1]
|
||||
|
||||
key, err := generateByType(typ, secret)
|
||||
key, err := generate(algo, secret)
|
||||
if err != nil {
|
||||
return Info{}, err
|
||||
return crypto.PrivKey{}, err
|
||||
}
|
||||
|
||||
// d00d, it worked! create the bugger....
|
||||
public := kb.writeKey(key, name, passphrase)
|
||||
return public, err
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// List loads the keys from the storage and enforces alphabetical order
|
||||
// List returns the keys from storage in alphabetical order.
|
||||
func (kb dbKeybase) List() ([]Info, error) {
|
||||
var res []Info
|
||||
iter := kb.db.Iterator(nil, nil)
|
||||
@@ -101,20 +104,19 @@ func (kb dbKeybase) List() ([]Info, error) {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// Get returns the public information about one key
|
||||
// Get returns the public information about one key.
|
||||
func (kb dbKeybase) Get(name string) (Info, error) {
|
||||
bs := kb.db.Get(pubName(name))
|
||||
return readInfo(bs)
|
||||
}
|
||||
|
||||
// Sign will modify the Signable in order to attach a valid signature with
|
||||
// this public key
|
||||
//
|
||||
// If no key for this name, or the passphrase doesn't match, returns an error
|
||||
// Sign signs the msg with the named key.
|
||||
// It returns an error if the key doesn't exist or the decryption fails.
|
||||
// TODO: what if leddger fails ?
|
||||
func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signature, pk crypto.PubKey, err error) {
|
||||
var key crypto.PrivKey
|
||||
bs := kb.db.Get(privName(name))
|
||||
key, err = unarmorDecryptPrivKey(string(bs), passphrase)
|
||||
armorStr := kb.db.Get(privName(name))
|
||||
key, err = unarmorDecryptPrivKey(string(armorStr), passphrase)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -124,15 +126,15 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signat
|
||||
return
|
||||
}
|
||||
|
||||
// Export decodes the private key with the current password, encodes
|
||||
// it with a secure one-time password and generates a sequence that can be
|
||||
// Imported by another dbKeybase
|
||||
// Export decodes the private key with the current password, encrypts
|
||||
// it with a secure one-time password and generates an armored private key
|
||||
// that can be Imported by another dbKeybase.
|
||||
//
|
||||
// This is designed to copy from one device to another, or provide backups
|
||||
// during version updates.
|
||||
func (kb dbKeybase) Export(name, oldpass, transferpass string) ([]byte, error) {
|
||||
bs := kb.db.Get(privName(name))
|
||||
key, err := unarmorDecryptPrivKey(string(bs), oldpass)
|
||||
armorStr := kb.db.Get(privName(name))
|
||||
key, err := unarmorDecryptPrivKey(string(armorStr), oldpass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -140,11 +142,11 @@ func (kb dbKeybase) Export(name, oldpass, transferpass string) ([]byte, error) {
|
||||
if transferpass == "" {
|
||||
return key.Bytes(), nil
|
||||
}
|
||||
res := encryptArmorPrivKey(key, transferpass)
|
||||
return []byte(res), nil
|
||||
armorBytes := encryptArmorPrivKey(key, transferpass)
|
||||
return []byte(armorBytes), nil
|
||||
}
|
||||
|
||||
// Import accepts bytes generated by Export along with the same transferpass
|
||||
// Import accepts bytes generated by Export along with the same transferpass.
|
||||
// If they are valid, it stores the password under the given name with the
|
||||
// new passphrase.
|
||||
func (kb dbKeybase) Import(name, newpass, transferpass string, data []byte) (err error) {
|
||||
@@ -163,7 +165,7 @@ func (kb dbKeybase) Import(name, newpass, transferpass string, data []byte) (err
|
||||
}
|
||||
|
||||
// Delete removes key forever, but we must present the
|
||||
// proper passphrase before deleting it (for security)
|
||||
// proper passphrase before deleting it (for security).
|
||||
func (kb dbKeybase) Delete(name, passphrase string) error {
|
||||
// verify we have the proper password before deleting
|
||||
bs := kb.db.Get(privName(name))
|
||||
@@ -176,10 +178,10 @@ func (kb dbKeybase) Delete(name, passphrase string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update changes the passphrase with which a already stored key is encoded.
|
||||
// Update changes the passphrase with which an already stored key is encrypted.
|
||||
//
|
||||
// oldpass must be the current passphrase used for encoding, newpass will be
|
||||
// the only valid passphrase from this time forward
|
||||
// oldpass must be the current passphrase used for encryption, newpass will be
|
||||
// the only valid passphrase from this time forward.
|
||||
func (kb dbKeybase) Update(name, oldpass, newpass string) error {
|
||||
bs := kb.db.Get(privName(name))
|
||||
key, err := unarmorDecryptPrivKey(string(bs), oldpass)
|
||||
@@ -187,26 +189,37 @@ func (kb dbKeybase) Update(name, oldpass, newpass string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// we must delete first, as Putting over an existing name returns an error
|
||||
kb.db.DeleteSync(pubName(name))
|
||||
kb.db.DeleteSync(privName(name))
|
||||
kb.writeKey(key, name, newpass)
|
||||
// Generate the public bytes and the encrypted privkey
|
||||
public := info(name, key)
|
||||
private := encryptArmorPrivKey(key, newpass)
|
||||
|
||||
// We must delete first, as Putting over an existing name returns an error.
|
||||
// Must be done atomically with the write or we could lose the key.
|
||||
batch := kb.db.NewBatch()
|
||||
batch.Delete(pubName(name))
|
||||
batch.Delete(privName(name))
|
||||
batch.Set(pubName(name), public.bytes())
|
||||
batch.Set(privName(name), []byte(private))
|
||||
batch.Write()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------------------
|
||||
|
||||
func (kb dbKeybase) writeKey(priv crypto.PrivKey, name, passphrase string) Info {
|
||||
// generate the public bytes
|
||||
// Generate the public bytes and the encrypted privkey
|
||||
public := info(name, priv)
|
||||
// generate the encrypted privkey
|
||||
private := encryptArmorPrivKey(priv, passphrase)
|
||||
|
||||
// write them both
|
||||
// Write them both
|
||||
kb.db.SetSync(pubName(name), public.bytes())
|
||||
kb.db.SetSync(privName(name), []byte(private))
|
||||
|
||||
return public
|
||||
}
|
||||
|
||||
// TODO: use a `type TypeKeyAlgo string` (?)
|
||||
func generate(algo string, secret []byte) (crypto.PrivKey, error) {
|
||||
switch algo {
|
||||
case crypto.NameEd25519:
|
||||
@@ -214,27 +227,13 @@ func generate(algo string, secret []byte) (crypto.PrivKey, error) {
|
||||
case crypto.NameSecp256k1:
|
||||
return crypto.GenPrivKeySecp256k1FromSecret(secret).Wrap(), nil
|
||||
case nano.NameLedgerEd25519:
|
||||
return nano.NewPrivKeyLedgerEd25519Ed25519()
|
||||
return nano.NewPrivKeyLedgerEd25519()
|
||||
default:
|
||||
err := errors.Errorf("Cannot generate keys for algorithm: %s", algo)
|
||||
return crypto.PrivKey{}, err
|
||||
}
|
||||
}
|
||||
|
||||
func generateByType(typ byte, secret []byte) (crypto.PrivKey, error) {
|
||||
switch typ {
|
||||
case crypto.TypeEd25519:
|
||||
return crypto.GenPrivKeyEd25519FromSecret(secret).Wrap(), nil
|
||||
case crypto.TypeSecp256k1:
|
||||
return crypto.GenPrivKeySecp256k1FromSecret(secret).Wrap(), nil
|
||||
case nano.TypeLedgerEd25519:
|
||||
return nano.NewPrivKeyLedgerEd25519Ed25519()
|
||||
default:
|
||||
err := errors.Errorf("Cannot generate keys for algorithm: %X", typ)
|
||||
return crypto.PrivKey{}, err
|
||||
}
|
||||
}
|
||||
|
||||
func pubName(name string) []byte {
|
||||
return []byte(fmt.Sprintf("%s.pub", name))
|
||||
}
|
||||
|
||||
+15
-14
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
crypto "github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/go-crypto/keys"
|
||||
"github.com/tendermint/go-crypto/keys/words"
|
||||
"github.com/tendermint/go-crypto/nano"
|
||||
)
|
||||
|
||||
@@ -23,7 +24,7 @@ func TestKeyManagement(t *testing.T) {
|
||||
// make the storage with reasonable defaults
|
||||
cstore := keys.New(
|
||||
dbm.NewMemDB(),
|
||||
keys.MustLoadCodec("english"),
|
||||
words.MustLoadCodec("english"),
|
||||
)
|
||||
|
||||
algo := crypto.NameEd25519
|
||||
@@ -38,7 +39,7 @@ func TestKeyManagement(t *testing.T) {
|
||||
// create some keys
|
||||
_, err = cstore.Get(n1)
|
||||
assert.NotNil(err)
|
||||
i, _, err := cstore.Create(n1, p1, algo)
|
||||
_, i, err := cstore.Create(n1, p1, algo)
|
||||
require.Equal(n1, i.Name)
|
||||
require.Nil(err)
|
||||
_, _, err = cstore.Create(n2, p2, algo)
|
||||
@@ -91,7 +92,7 @@ func TestSignVerify(t *testing.T) {
|
||||
// make the storage with reasonable defaults
|
||||
cstore := keys.New(
|
||||
dbm.NewMemDB(),
|
||||
keys.MustLoadCodec("english"),
|
||||
words.MustLoadCodec("english"),
|
||||
)
|
||||
algo := crypto.NameSecp256k1
|
||||
|
||||
@@ -99,10 +100,10 @@ func TestSignVerify(t *testing.T) {
|
||||
p1, p2 := "1234", "foobar"
|
||||
|
||||
// create two users and get their info
|
||||
i1, _, err := cstore.Create(n1, p1, algo)
|
||||
_, i1, err := cstore.Create(n1, p1, algo)
|
||||
require.Nil(err)
|
||||
|
||||
i2, _, err := cstore.Create(n2, p2, algo)
|
||||
_, i2, err := cstore.Create(n2, p2, algo)
|
||||
require.Nil(err)
|
||||
|
||||
// let's try to sign some messages
|
||||
@@ -165,13 +166,13 @@ func TestSignWithLedger(t *testing.T) {
|
||||
// make the storage with reasonable defaults
|
||||
cstore := keys.New(
|
||||
dbm.NewMemDB(),
|
||||
keys.MustLoadCodec("english"),
|
||||
words.MustLoadCodec("english"),
|
||||
)
|
||||
n := "nano-s"
|
||||
p := "hard2hack"
|
||||
|
||||
// create a nano user
|
||||
c, _, err := cstore.Create(n, p, nano.NameLedgerEd25519)
|
||||
_, c, err := cstore.Create(n, p, nano.NameLedgerEd25519)
|
||||
require.Nil(err, "%+v", err)
|
||||
assert.Equal(c.Name, n)
|
||||
_, ok := c.PubKey.Unwrap().(nano.PubKeyLedgerEd25519)
|
||||
@@ -219,7 +220,7 @@ func TestImportUnencrypted(t *testing.T) {
|
||||
// make the storage with reasonable defaults
|
||||
cstore := keys.New(
|
||||
dbm.NewMemDB(),
|
||||
keys.MustLoadCodec("english"),
|
||||
words.MustLoadCodec("english"),
|
||||
)
|
||||
|
||||
key := crypto.GenPrivKeyEd25519FromSecret(cmn.RandBytes(16)).Wrap()
|
||||
@@ -245,7 +246,7 @@ func TestAdvancedKeyManagement(t *testing.T) {
|
||||
// make the storage with reasonable defaults
|
||||
cstore := keys.New(
|
||||
dbm.NewMemDB(),
|
||||
keys.MustLoadCodec("english"),
|
||||
words.MustLoadCodec("english"),
|
||||
)
|
||||
|
||||
algo := crypto.NameSecp256k1
|
||||
@@ -288,7 +289,7 @@ func TestSeedPhrase(t *testing.T) {
|
||||
// make the storage with reasonable defaults
|
||||
cstore := keys.New(
|
||||
dbm.NewMemDB(),
|
||||
keys.MustLoadCodec("english"),
|
||||
words.MustLoadCodec("english"),
|
||||
)
|
||||
|
||||
algo := crypto.NameEd25519
|
||||
@@ -296,7 +297,7 @@ func TestSeedPhrase(t *testing.T) {
|
||||
p1, p2 := "1234", "foobar"
|
||||
|
||||
// make sure key works with initial password
|
||||
info, seed, err := cstore.Create(n1, p1, algo)
|
||||
seed, info, err := cstore.Create(n1, p1, algo)
|
||||
require.Nil(err, "%+v", err)
|
||||
assert.Equal(n1, info.Name)
|
||||
assert.NotEmpty(seed)
|
||||
@@ -308,7 +309,7 @@ func TestSeedPhrase(t *testing.T) {
|
||||
require.NotNil(err)
|
||||
|
||||
// let us re-create it from the seed-phrase
|
||||
newInfo, err := cstore.Recover(n2, p2, seed)
|
||||
newInfo, err := cstore.Recover(n2, p2, algo, seed)
|
||||
require.Nil(err, "%+v", err)
|
||||
assert.Equal(n2, newInfo.Name)
|
||||
assert.Equal(info.Address(), newInfo.Address())
|
||||
@@ -319,13 +320,13 @@ func ExampleNew() {
|
||||
// Select the encryption and storage for your cryptostore
|
||||
cstore := keys.New(
|
||||
dbm.NewMemDB(),
|
||||
keys.MustLoadCodec("english"),
|
||||
words.MustLoadCodec("english"),
|
||||
)
|
||||
ed := crypto.NameEd25519
|
||||
sec := crypto.NameSecp256k1
|
||||
|
||||
// Add keys and see they return in alphabetical order
|
||||
bob, _, err := cstore.Create("Bob", "friend", ed)
|
||||
_, bob, err := cstore.Create("Bob", "friend", ed)
|
||||
if err != nil {
|
||||
// this should never happen
|
||||
fmt.Println(err)
|
||||
|
||||
+2
-2
@@ -38,9 +38,9 @@ type Keybase interface {
|
||||
// Sign some bytes
|
||||
Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error)
|
||||
// Create a new keypair
|
||||
Create(name, passphrase, algo string) (_ Info, seedphrase string, _ error)
|
||||
Create(name, passphrase, algo string) (seedphrase string, _ Info, _ error)
|
||||
// Recover takes a seedphrase and loads in the key
|
||||
Recover(name, passphrase, seedphrase string) (Info, error)
|
||||
Recover(name, passphrase, algo, seedphrase string) (Info, error)
|
||||
List() ([]Info, error)
|
||||
Get(name string) (Info, error)
|
||||
Update(name, oldpass, newpass string) error
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package keys
|
||||
package words
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
@@ -1,4 +1,4 @@
|
||||
package keys
|
||||
package words
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,4 +1,4 @@
|
||||
package keys
|
||||
package words
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/tendermint/go-crypto/keys/wordlist"
|
||||
"github.com/tendermint/go-crypto/keys/words/wordlist"
|
||||
)
|
||||
|
||||
const BankSize = 2048
|
||||
@@ -1,4 +1,4 @@
|
||||
package keys
|
||||
package words
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -1,4 +1,4 @@
|
||||
package keys
|
||||
package words
|
||||
|
||||
import (
|
||||
"testing"
|
||||
+7
-7
@@ -33,21 +33,21 @@ func getLedger() (*ledger.Ledger, error) {
|
||||
return device, err
|
||||
}
|
||||
|
||||
func signLedger(device *ledger.Ledger, msg []byte) (pk crypto.PubKey, sig crypto.Signature, err error) {
|
||||
func signLedger(device *ledger.Ledger, msg []byte) (pub crypto.PubKey, sig crypto.Signature, err error) {
|
||||
var resp []byte
|
||||
|
||||
packets := generateSignRequests(msg)
|
||||
for _, pack := range packets {
|
||||
resp, err = device.Exchange(pack, Timeout)
|
||||
if err != nil {
|
||||
return pk, sig, err
|
||||
return pub, sig, err
|
||||
}
|
||||
}
|
||||
|
||||
// the last call is the result we want and needs to be parsed
|
||||
key, bsig, err := parseDigest(resp)
|
||||
if err != nil {
|
||||
return pk, sig, err
|
||||
return pub, sig, err
|
||||
}
|
||||
|
||||
var b [32]byte
|
||||
@@ -64,9 +64,9 @@ type PrivKeyLedgerEd25519 struct {
|
||||
CachedPubKey crypto.PubKey
|
||||
}
|
||||
|
||||
// NewPrivKeyLedgerEd25519Ed25519 will generate a new key and store the
|
||||
// NewPrivKeyLedgerEd25519 will generate a new key and store the
|
||||
// public key for later use.
|
||||
func NewPrivKeyLedgerEd25519Ed25519() (crypto.PrivKey, error) {
|
||||
func NewPrivKeyLedgerEd25519() (crypto.PrivKey, error) {
|
||||
var pk PrivKeyLedgerEd25519
|
||||
// getPubKey will cache the pubkey for later use,
|
||||
// this allows us to return an error early if the ledger
|
||||
@@ -94,13 +94,13 @@ func (pk *PrivKeyLedgerEd25519) ValidateKey() error {
|
||||
// AssertIsPrivKeyInner fulfils PrivKey Interface
|
||||
func (pk *PrivKeyLedgerEd25519) AssertIsPrivKeyInner() {}
|
||||
|
||||
// Bytes fulfils pk Interface - stores the cached pubkey so we can verify
|
||||
// Bytes fulfils PrivKey Interface - but it stores the cached pubkey so we can verify
|
||||
// the same key when we reconnect to a ledger
|
||||
func (pk *PrivKeyLedgerEd25519) Bytes() []byte {
|
||||
return wire.BinaryBytes(pk.Wrap())
|
||||
}
|
||||
|
||||
// Sign calls the ledger and stores the pk for future use
|
||||
// Sign calls the ledger and stores the PubKey for future use
|
||||
//
|
||||
// XXX/TODO: panics if there is an error communicating with the ledger.
|
||||
//
|
||||
|
||||
+2
-2
@@ -83,7 +83,7 @@ func TestRealLedger(t *testing.T) {
|
||||
}
|
||||
msg := []byte("kuhehfeohg")
|
||||
|
||||
priv, err := NewPrivKeyLedgerEd25519Ed25519()
|
||||
priv, err := NewPrivKeyLedgerEd25519()
|
||||
require.Nil(err, "%+v", err)
|
||||
pub := priv.PubKey()
|
||||
sig := priv.Sign(msg)
|
||||
@@ -123,7 +123,7 @@ func TestRealLedgerErrorHandling(t *testing.T) {
|
||||
|
||||
// first, try to generate a key, must return an error
|
||||
// (no panic)
|
||||
_, err := NewPrivKeyLedgerEd25519Ed25519()
|
||||
_, err := NewPrivKeyLedgerEd25519()
|
||||
require.Error(err)
|
||||
|
||||
led := PrivKeyLedgerEd25519{} // empty
|
||||
|
||||
Reference in New Issue
Block a user