mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-21 17:01:35 +00:00
Merge remote-tracking branch 'remotes/origin/develop' into bucky/sig-name
# Conflicts: # Gopkg.lock # keys/keybase_test.go # signature_test.go
This commit is contained in:
34
CHANGELOG.md
34
CHANGELOG.md
@ -1,5 +1,39 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 0.8.0
|
||||||
|
|
||||||
|
**TBD**
|
||||||
|
|
||||||
|
## 0.7.0
|
||||||
|
|
||||||
|
**May 30th, 2018**
|
||||||
|
|
||||||
|
BREAKING CHANGES
|
||||||
|
|
||||||
|
No breaking changes compared to 0.6.2, but making up for the version bump that
|
||||||
|
should have happened in 0.6.1.
|
||||||
|
|
||||||
|
We also bring in the `tmlibs/merkle` package with breaking changes:
|
||||||
|
|
||||||
|
- change the hash function from RIPEMD160 to tmhash (first 20-bytes of SHA256)
|
||||||
|
- remove unused funcs and unexport SimpleMap
|
||||||
|
|
||||||
|
FEATURES
|
||||||
|
|
||||||
|
- [xchacha20poly1305] New authenticated encryption module
|
||||||
|
- [merkle] Moved in from tmlibs
|
||||||
|
- [merkle/tmhash] New hash function: the first 20-bytes of SHA256
|
||||||
|
|
||||||
|
IMPROVEMENTS
|
||||||
|
|
||||||
|
- Remove some dead code
|
||||||
|
- Use constant-time compare for signatures
|
||||||
|
|
||||||
|
BUG FIXES
|
||||||
|
|
||||||
|
- Fix MixEntropy weakness
|
||||||
|
- Fix PrivKeyEd25519.Generate()
|
||||||
|
|
||||||
## 0.6.2 (April 9, 2018)
|
## 0.6.2 (April 9, 2018)
|
||||||
|
|
||||||
IMPROVEMENTS
|
IMPROVEMENTS
|
||||||
|
33
Gopkg.lock
generated
33
Gopkg.lock
generated
@ -1,17 +1,23 @@
|
|||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||||
|
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/brejski/hid"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "06112dcfcc50a7e0e4fd06e17f9791e788fdaafc"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/btcsuite/btcd"
|
name = "github.com/btcsuite/btcd"
|
||||||
packages = ["btcec"]
|
packages = ["btcec"]
|
||||||
revision = "2be2f12b358dc57d70b8f501b00be450192efbc3"
|
revision = "86fed781132ac890ee03e906e4ecd5d6fa180c64"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/btcsuite/btcutil"
|
name = "github.com/btcsuite/btcutil"
|
||||||
packages = ["base58"]
|
packages = ["base58"]
|
||||||
revision = "501929d3d046174c3d39f0ea54ece471aa17238c"
|
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/davecgh/go-spew"
|
name = "github.com/davecgh/go-spew"
|
||||||
@ -55,7 +61,7 @@
|
|||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/golang/snappy"
|
name = "github.com/golang/snappy"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "553a641470496b2327abcac10b36396bd98e45c9"
|
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@ -113,7 +119,7 @@
|
|||||||
"leveldb/table",
|
"leveldb/table",
|
||||||
"leveldb/util"
|
"leveldb/util"
|
||||||
]
|
]
|
||||||
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
|
revision = "e2150783cd35f5b607daca48afd8c57ec54cc995"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@ -139,8 +145,8 @@
|
|||||||
"log",
|
"log",
|
||||||
"test"
|
"test"
|
||||||
]
|
]
|
||||||
revision = "2e24b64fc121dcdf1cabceab8dc2f7257675483c"
|
revision = "692f1d86a6e2c0efa698fd1e4541b68c74ffaf38"
|
||||||
version = "v0.8.1"
|
version = "v0.8.4"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@ -148,6 +154,11 @@
|
|||||||
packages = ["."]
|
packages = ["."]
|
||||||
revision = "8e7a99b3e716f36d3b080a9a70f9eb45abe4edcc"
|
revision = "8e7a99b3e716f36d3b080a9a70f9eb45abe4edcc"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
name = "github.com/zondax/ledger-goclient"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "065cbf938a16f20335c40cfe180f9cd4955c6a5a"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/crypto"
|
name = "golang.org/x/crypto"
|
||||||
@ -164,11 +175,17 @@
|
|||||||
"ripemd160",
|
"ripemd160",
|
||||||
"salsa20/salsa"
|
"salsa20/salsa"
|
||||||
]
|
]
|
||||||
revision = "b2aa35443fbc700ab74c586ae79b81c171851023"
|
revision = "8ac0e0d97ce45cd83d1d7243c060cb8461dda5e9"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/sys"
|
||||||
|
packages = ["cpu"]
|
||||||
|
revision = "9527bec2660bd847c050fda93a0f0c6dee0800bb"
|
||||||
|
|
||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "b34cf043cab77178eebff1b7cfce8b31b6c2b6b3318c6d01add271b68f550345"
|
inputs-digest = "f20e34cd998442d4ffe2f9aa45ab87a55ba6e4cd19f29009adaadac3b5dccf26"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
# go-tests = true
|
# go-tests = true
|
||||||
# unused-packages = true
|
# unused-packages = true
|
||||||
|
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/btcsuite/btcutil"
|
name = "github.com/btcsuite/btcutil"
|
||||||
branch = "master"
|
branch = "master"
|
||||||
@ -57,6 +56,10 @@
|
|||||||
name = "github.com/tyler-smith/go-bip39"
|
name = "github.com/tyler-smith/go-bip39"
|
||||||
branch = "master"
|
branch = "master"
|
||||||
|
|
||||||
|
[[constraint]]
|
||||||
|
name = "github.com/zondax/ledger-goclient"
|
||||||
|
revision = "065cbf938a16f20335c40cfe180f9cd4955c6a5a"
|
||||||
|
|
||||||
[prune]
|
[prune]
|
||||||
go-tests = true
|
go-tests = true
|
||||||
unused-packages = true
|
unused-packages = true
|
||||||
|
2
amino.go
2
amino.go
@ -27,6 +27,8 @@ func RegisterAmino(cdc *amino.Codec) {
|
|||||||
"tendermint/PrivKeyEd25519", nil)
|
"tendermint/PrivKeyEd25519", nil)
|
||||||
cdc.RegisterConcrete(PrivKeySecp256k1{},
|
cdc.RegisterConcrete(PrivKeySecp256k1{},
|
||||||
"tendermint/PrivKeySecp256k1", nil)
|
"tendermint/PrivKeySecp256k1", nil)
|
||||||
|
cdc.RegisterConcrete(PrivKeyLedgerSecp256k1{},
|
||||||
|
"tendermint/PrivKeyLedgerSecp256k1", nil)
|
||||||
|
|
||||||
cdc.RegisterInterface((*Signature)(nil), nil)
|
cdc.RegisterInterface((*Signature)(nil), nil)
|
||||||
cdc.RegisterConcrete(SignatureEd25519{},
|
cdc.RegisterConcrete(SignatureEd25519{},
|
||||||
|
@ -82,14 +82,16 @@ func TestKeyEncodings(t *testing.T) {
|
|||||||
|
|
||||||
// Check (de/en)codings of Signatures.
|
// Check (de/en)codings of Signatures.
|
||||||
var sig1, sig2, sig3 Signature
|
var sig1, sig2, sig3 Signature
|
||||||
sig1 = tc.privKey.Sign([]byte("something"))
|
sig1, err := tc.privKey.Sign([]byte("something"))
|
||||||
|
assert.NoError(t, err)
|
||||||
checkAminoBinary(t, sig1, &sig2, -1) // Siganture size changes for Secp anyways.
|
checkAminoBinary(t, sig1, &sig2, -1) // Siganture size changes for Secp anyways.
|
||||||
assert.EqualValues(t, sig1, sig2)
|
assert.EqualValues(t, sig1, sig2)
|
||||||
checkAminoJSON(t, sig1, &sig3, false) // TODO also check Prefix bytes.
|
checkAminoJSON(t, sig1, &sig3, false) // TODO also check Prefix bytes.
|
||||||
assert.EqualValues(t, sig1, sig3)
|
assert.EqualValues(t, sig1, sig3)
|
||||||
|
|
||||||
// Check (de/en)codings of PubKeys.
|
// Check (de/en)codings of PubKeys.
|
||||||
pubKey := tc.privKey.PubKey()
|
pubKey, err := tc.privKey.PubKey()
|
||||||
|
assert.NoError(t, err)
|
||||||
var pub2, pub3 PubKey
|
var pub2, pub3 PubKey
|
||||||
checkAminoBinary(t, pubKey, &pub2, tc.pubSize)
|
checkAminoBinary(t, pubKey, &pub2, tc.pubSize)
|
||||||
assert.EqualValues(t, pubKey, pub2)
|
assert.EqualValues(t, pubKey, pub2)
|
||||||
|
174
keys/keybase.go
174
keys/keybase.go
@ -1,7 +1,9 @@
|
|||||||
package keys
|
package keys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -26,23 +28,23 @@ func New(db dbm.DB, codec words.Codec) dbKeybase {
|
|||||||
|
|
||||||
var _ Keybase = dbKeybase{}
|
var _ Keybase = dbKeybase{}
|
||||||
|
|
||||||
// Create generates a new key and persists it to storage, encrypted
|
// CreateMnemonic generates a new key and persists it to storage, encrypted
|
||||||
// using the passphrase. It returns the generated seedphrase
|
// using the passphrase. It returns the generated seedphrase
|
||||||
// (mnemonic) and the key Info. It returns an error if it fails to
|
// (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
|
// generate a key for the given algo type, or if another key is
|
||||||
// already stored under the same name.
|
// already stored under the same name.
|
||||||
func (kb dbKeybase) Create(name, passphrase string, algo CryptoAlgo) (Info, string, error) {
|
func (kb dbKeybase) CreateMnemonic(name, passphrase string, algo SignAlgo) (Info, string, error) {
|
||||||
// NOTE: secret is SHA256 hashed by secp256k1 and ed25519.
|
// NOTE: secret is SHA256 hashed by secp256k1 and ed25519.
|
||||||
// 16 byte secret corresponds to 12 BIP39 words.
|
// 16 byte secret corresponds to 12 BIP39 words.
|
||||||
// XXX: Ledgers use 24 words now - should we ?
|
// XXX: Ledgers use 24 words now - should we ?
|
||||||
secret := crypto.CRandBytes(16)
|
secret := crypto.CRandBytes(16)
|
||||||
priv, err := generate(algo, secret)
|
priv, err := generate(algo, secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Info{}, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt and persist the key
|
// encrypt and persist the key
|
||||||
info := kb.writeKey(priv, name, passphrase)
|
info := kb.writeLocalKey(priv, name, passphrase)
|
||||||
|
|
||||||
// we append the type byte to the serialized secret to help with
|
// we append the type byte to the serialized secret to help with
|
||||||
// recovery
|
// recovery
|
||||||
@ -56,6 +58,29 @@ func (kb dbKeybase) Create(name, passphrase string, algo CryptoAlgo) (Info, stri
|
|||||||
return info, seed, err
|
return info, seed, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateLedger creates a new locally-stored reference to a Ledger keypair
|
||||||
|
// It returns the created key info and an error if the Ledger could not be queried
|
||||||
|
func (kb dbKeybase) CreateLedger(name string, path crypto.DerivationPath, algo SignAlgo) (Info, error) {
|
||||||
|
if algo != AlgoSecp256k1 {
|
||||||
|
return nil, fmt.Errorf("Only secp256k1 is supported for Ledger devices")
|
||||||
|
}
|
||||||
|
priv, err := crypto.NewPrivKeyLedgerSecp256k1(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pub, err := priv.PubKey()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return kb.writeLedgerKey(pub, path, name), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOffline creates a new reference to an offline keypair
|
||||||
|
// It returns the created key info
|
||||||
|
func (kb dbKeybase) CreateOffline(name string, pub crypto.PubKey) (Info, error) {
|
||||||
|
return kb.writeOfflineKey(pub, name), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Recover converts a seedphrase to a private key and persists it,
|
// Recover converts a seedphrase to a private key and persists it,
|
||||||
// encrypted with the given passphrase. Functions like Create, but
|
// encrypted with the given passphrase. Functions like Create, but
|
||||||
// seedphrase is input not output.
|
// seedphrase is input not output.
|
||||||
@ -63,22 +88,22 @@ func (kb dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) {
|
|||||||
words := strings.Split(strings.TrimSpace(seedphrase), " ")
|
words := strings.Split(strings.TrimSpace(seedphrase), " ")
|
||||||
secret, err := kb.codec.WordsToBytes(words)
|
secret, err := kb.codec.WordsToBytes(words)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Info{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// secret is comprised of the actual secret with the type
|
// secret is comprised of the actual secret with the type
|
||||||
// appended.
|
// appended.
|
||||||
// ie [secret] = [type] + [secret]
|
// ie [secret] = [type] + [secret]
|
||||||
typ, secret := secret[0], secret[1:]
|
typ, secret := secret[0], secret[1:]
|
||||||
algo := byteToCryptoAlgo(typ)
|
algo := byteToSignAlgo(typ)
|
||||||
priv, err := generate(algo, secret)
|
priv, err := generate(algo, secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Info{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt and persist key.
|
// encrypt and persist key.
|
||||||
public := kb.writeKey(priv, name, passphrase)
|
public := kb.writeLocalKey(priv, name, passphrase)
|
||||||
return public, err
|
return public, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// List returns the keys from storage in alphabetical order.
|
// List returns the keys from storage in alphabetical order.
|
||||||
@ -87,7 +112,6 @@ func (kb dbKeybase) List() ([]Info, error) {
|
|||||||
iter := kb.db.Iterator(nil, nil)
|
iter := kb.db.Iterator(nil, nil)
|
||||||
defer iter.Close()
|
defer iter.Close()
|
||||||
for ; iter.Valid(); iter.Next() {
|
for ; iter.Valid(); iter.Next() {
|
||||||
// key := iter.Key()
|
|
||||||
info, err := readInfo(iter.Value())
|
info, err := readInfo(iter.Value())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -110,17 +134,46 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signat
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if info.PrivKeyArmor == "" {
|
var priv crypto.PrivKey
|
||||||
err = fmt.Errorf("private key not available")
|
switch info.(type) {
|
||||||
return
|
case localInfo:
|
||||||
|
linfo := info.(localInfo)
|
||||||
|
if linfo.PrivKeyArmor == "" {
|
||||||
|
err = fmt.Errorf("private key not available")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
priv, err = unarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
case ledgerInfo:
|
||||||
|
linfo := info.(ledgerInfo)
|
||||||
|
priv, err = crypto.NewPrivKeyLedgerSecp256k1(linfo.Path)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case offlineInfo:
|
||||||
|
linfo := info.(offlineInfo)
|
||||||
|
fmt.Printf("Bytes to sign:\n%s", msg)
|
||||||
|
buf := bufio.NewReader(os.Stdin)
|
||||||
|
fmt.Printf("\nEnter Amino-encoded signature:\n")
|
||||||
|
// Will block until user inputs the signature
|
||||||
|
signed, err := buf.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
cdc.MustUnmarshalBinary([]byte(signed), sig)
|
||||||
|
return sig, linfo.GetPubKey(), nil
|
||||||
}
|
}
|
||||||
priv, err := unarmorDecryptPrivKey(info.PrivKeyArmor, passphrase)
|
sig, err = priv.Sign(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
sig = priv.Sign(msg)
|
pub, err = priv.PubKey()
|
||||||
pub = priv.PubKey()
|
if err != nil {
|
||||||
return
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return sig, pub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kb dbKeybase) Export(name string) (armor string, err error) {
|
func (kb dbKeybase) Export(name string) (armor string, err error) {
|
||||||
@ -143,7 +196,7 @@ func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return armorPubKeyBytes(info.PubKey.Bytes()), nil
|
return armorPubKeyBytes(info.GetPubKey().Bytes()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kb dbKeybase) Import(name string, armor string) (err error) {
|
func (kb dbKeybase) Import(name string, armor string) (err error) {
|
||||||
@ -175,23 +228,37 @@ func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
kb.writePubKey(pubKey, name)
|
kb.writeOfflineKey(pubKey, name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete removes key forever, but we must present the
|
// Delete removes key forever, but we must present the
|
||||||
// proper passphrase before deleting it (for security).
|
// proper passphrase before deleting it (for security).
|
||||||
|
// A passphrase of 'yes' is used to delete stored
|
||||||
|
// references to offline and Ledger / HW wallet keys
|
||||||
func (kb dbKeybase) Delete(name, passphrase string) error {
|
func (kb dbKeybase) Delete(name, passphrase string) error {
|
||||||
// verify we have the proper password before deleting
|
// verify we have the proper password before deleting
|
||||||
info, err := kb.Get(name)
|
info, err := kb.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = unarmorDecryptPrivKey(info.PrivKeyArmor, passphrase)
|
switch info.(type) {
|
||||||
if err != nil {
|
case localInfo:
|
||||||
return err
|
linfo := info.(localInfo)
|
||||||
|
_, err = unarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
kb.db.DeleteSync(infoKey(name))
|
||||||
|
return nil
|
||||||
|
case ledgerInfo:
|
||||||
|
case offlineInfo:
|
||||||
|
if passphrase != "yes" {
|
||||||
|
return fmt.Errorf("enter exactly 'yes' to delete the key")
|
||||||
|
}
|
||||||
|
kb.db.DeleteSync(infoKey(name))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
kb.db.DeleteSync(infoKey(name))
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,36 +272,51 @@ func (kb dbKeybase) Update(name, oldpass, newpass string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
key, err := unarmorDecryptPrivKey(info.PrivKeyArmor, oldpass)
|
switch info.(type) {
|
||||||
if err != nil {
|
case localInfo:
|
||||||
return err
|
linfo := info.(localInfo)
|
||||||
|
key, err := unarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
kb.writeLocalKey(key, name, newpass)
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Locally stored key required")
|
||||||
}
|
}
|
||||||
|
|
||||||
kb.writeKey(key, name, newpass)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kb dbKeybase) writePubKey(pub crypto.PubKey, name string) Info {
|
func (kb dbKeybase) writeLocalKey(priv crypto.PrivKey, name, passphrase string) Info {
|
||||||
// make Info
|
// encrypt private key using passphrase
|
||||||
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)
|
privArmor := encryptArmorPrivKey(priv, passphrase)
|
||||||
// make Info
|
// make Info
|
||||||
info := newInfo(name, priv.PubKey(), privArmor)
|
pub, err := priv.PubKey()
|
||||||
|
if err != nil {
|
||||||
// write them both
|
panic(err)
|
||||||
kb.db.SetSync(infoKey(name), info.bytes())
|
}
|
||||||
|
info := newLocalInfo(name, pub, privArmor)
|
||||||
|
kb.writeInfo(info, name)
|
||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
func generate(algo CryptoAlgo, secret []byte) (crypto.PrivKey, error) {
|
func (kb dbKeybase) writeLedgerKey(pub crypto.PubKey, path crypto.DerivationPath, name string) Info {
|
||||||
|
info := newLedgerInfo(name, pub, path)
|
||||||
|
kb.writeInfo(info, name)
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kb dbKeybase) writeOfflineKey(pub crypto.PubKey, name string) Info {
|
||||||
|
info := newOfflineInfo(name, pub)
|
||||||
|
kb.writeInfo(info, name)
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kb dbKeybase) writeInfo(info Info, name string) {
|
||||||
|
// write the info by key
|
||||||
|
kb.db.SetSync(infoKey(name), writeInfo(info))
|
||||||
|
}
|
||||||
|
|
||||||
|
func generate(algo SignAlgo, secret []byte) (crypto.PrivKey, error) {
|
||||||
switch algo {
|
switch algo {
|
||||||
case AlgoEd25519:
|
case AlgoEd25519:
|
||||||
return crypto.GenPrivKeyEd25519FromSecret(secret), nil
|
return crypto.GenPrivKeyEd25519FromSecret(secret), nil
|
||||||
|
@ -33,28 +33,28 @@ func TestKeyManagement(t *testing.T) {
|
|||||||
assert.Empty(t, l)
|
assert.Empty(t, l)
|
||||||
|
|
||||||
// create some keys
|
// create some keys
|
||||||
i, err := cstore.Get(n1)
|
_, err = cstore.Get(n1)
|
||||||
assert.Equal(t, i, keys.Info{})
|
assert.NotNil(t, err)
|
||||||
i, _, err = cstore.Create(n1, p1, algo)
|
i, _, err := cstore.CreateMnemonic(n1, p1, algo)
|
||||||
require.Equal(t, n1, i.Name)
|
require.Equal(t, n1, i.GetName())
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
_, _, err = cstore.Create(n2, p2, algo)
|
_, _, err = cstore.CreateMnemonic(n2, p2, algo)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
// we can get these keys
|
// we can get these keys
|
||||||
i2, err := cstore.Get(n2)
|
i2, err := cstore.Get(n2)
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
i, err = cstore.Get(n3)
|
_, err = cstore.Get(n3)
|
||||||
assert.Equal(t, i, keys.Info{})
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
// list shows them in order
|
// list shows them in order
|
||||||
keyS, err := cstore.List()
|
keyS, err := cstore.List()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, 2, len(keyS))
|
require.Equal(t, 2, len(keyS))
|
||||||
// note these are in alphabetical order
|
// note these are in alphabetical order
|
||||||
assert.Equal(t, n2, keyS[0].Name)
|
assert.Equal(t, n2, keyS[0].GetName())
|
||||||
assert.Equal(t, n1, keyS[1].Name)
|
assert.Equal(t, n1, keyS[1].GetName())
|
||||||
assert.Equal(t, i2.PubKey, keyS[0].PubKey)
|
assert.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey())
|
||||||
|
|
||||||
// deleting a key removes it
|
// deleting a key removes it
|
||||||
err = cstore.Delete("bad name", "foo")
|
err = cstore.Delete("bad name", "foo")
|
||||||
@ -64,8 +64,28 @@ func TestKeyManagement(t *testing.T) {
|
|||||||
keyS, err = cstore.List()
|
keyS, err = cstore.List()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
assert.Equal(t, 1, len(keyS))
|
assert.Equal(t, 1, len(keyS))
|
||||||
i, err = cstore.Get(n1)
|
_, err = cstore.Get(n1)
|
||||||
assert.Equal(t, i, keys.Info{})
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
// create an offline key
|
||||||
|
o1 := "offline"
|
||||||
|
priv1 := crypto.GenPrivKeyEd25519()
|
||||||
|
pub1, err := priv1.PubKey()
|
||||||
|
require.Nil(t, err)
|
||||||
|
i, err = cstore.CreateOffline(o1, pub1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, pub1, i.GetPubKey())
|
||||||
|
require.Equal(t, o1, i.GetName())
|
||||||
|
keyS, err = cstore.List()
|
||||||
|
require.Equal(t, 2, len(keyS))
|
||||||
|
|
||||||
|
// delete the offline key
|
||||||
|
err = cstore.Delete(o1, "no")
|
||||||
|
require.NotNil(t, err)
|
||||||
|
err = cstore.Delete(o1, "yes")
|
||||||
|
require.Nil(t, err)
|
||||||
|
keyS, err = cstore.List()
|
||||||
|
require.Equal(t, 1, len(keyS))
|
||||||
|
|
||||||
// make sure that it only signs with the right password
|
// make sure that it only signs with the right password
|
||||||
// tx := mock.NewSig([]byte("mytransactiondata"))
|
// tx := mock.NewSig([]byte("mytransactiondata"))
|
||||||
@ -95,19 +115,18 @@ func TestSignVerify(t *testing.T) {
|
|||||||
p1, p2, p3 := "1234", "foobar", "foobar"
|
p1, p2, p3 := "1234", "foobar", "foobar"
|
||||||
|
|
||||||
// create two users and get their info
|
// create two users and get their info
|
||||||
i1, _, err := cstore.Create(n1, p1, algo)
|
i1, _, err := cstore.CreateMnemonic(n1, p1, algo)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
i2, _, err := cstore.Create(n2, p2, algo)
|
i2, _, err := cstore.CreateMnemonic(n2, p2, algo)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
// Import a public key
|
// Import a public key
|
||||||
armor, err := cstore.ExportPubKey(n2)
|
armor, err := cstore.ExportPubKey(n2)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
cstore.ImportPubKey(n3, armor)
|
cstore.ImportPubKey(n3, armor)
|
||||||
i3, err := cstore.Get(n3)
|
_, err = cstore.Get(n3)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, i3.PrivKeyArmor, "")
|
|
||||||
|
|
||||||
// let's try to sign some messages
|
// let's try to sign some messages
|
||||||
d1 := []byte("my first message")
|
d1 := []byte("my first message")
|
||||||
@ -117,19 +136,19 @@ func TestSignVerify(t *testing.T) {
|
|||||||
// try signing both data with both keys...
|
// try signing both data with both keys...
|
||||||
s11, pub1, err := cstore.Sign(n1, p1, d1)
|
s11, pub1, err := cstore.Sign(n1, p1, d1)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, i1.PubKey, pub1)
|
require.Equal(t, i1.GetPubKey(), pub1)
|
||||||
|
|
||||||
s12, pub1, err := cstore.Sign(n1, p1, d2)
|
s12, pub1, err := cstore.Sign(n1, p1, d2)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, i1.PubKey, pub1)
|
require.Equal(t, i1.GetPubKey(), pub1)
|
||||||
|
|
||||||
s21, pub2, err := cstore.Sign(n2, p2, d1)
|
s21, pub2, err := cstore.Sign(n2, p2, d1)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, i2.PubKey, pub2)
|
require.Equal(t, i2.GetPubKey(), pub2)
|
||||||
|
|
||||||
s22, pub2, err := cstore.Sign(n2, p2, d2)
|
s22, pub2, err := cstore.Sign(n2, p2, d2)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, i2.PubKey, pub2)
|
require.Equal(t, i2.GetPubKey(), pub2)
|
||||||
|
|
||||||
// let's try to validate and make sure it only works when everything is proper
|
// let's try to validate and make sure it only works when everything is proper
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@ -139,15 +158,15 @@ func TestSignVerify(t *testing.T) {
|
|||||||
valid bool
|
valid bool
|
||||||
}{
|
}{
|
||||||
// proper matches
|
// proper matches
|
||||||
{i1.PubKey, d1, s11, true},
|
{i1.GetPubKey(), d1, s11, true},
|
||||||
// change data, pubkey, or signature leads to fail
|
// change data, pubkey, or signature leads to fail
|
||||||
{i1.PubKey, d2, s11, false},
|
{i1.GetPubKey(), d2, s11, false},
|
||||||
{i2.PubKey, d1, s11, false},
|
{i2.GetPubKey(), d1, s11, false},
|
||||||
{i1.PubKey, d1, s21, false},
|
{i1.GetPubKey(), d1, s21, false},
|
||||||
// make sure other successes
|
// make sure other successes
|
||||||
{i1.PubKey, d2, s12, true},
|
{i1.GetPubKey(), d2, s12, true},
|
||||||
{i2.PubKey, d1, s21, true},
|
{i2.GetPubKey(), d1, s21, true},
|
||||||
{i2.PubKey, d2, s22, true},
|
{i2.GetPubKey(), d2, s22, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
@ -232,27 +251,27 @@ func TestExportImport(t *testing.T) {
|
|||||||
words.MustLoadCodec("english"),
|
words.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
|
|
||||||
info, _, err := cstore.Create("john", "passphrase", keys.AlgoEd25519)
|
info, _, err := cstore.CreateMnemonic("john", "passphrase", keys.AlgoEd25519)
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, info.Name, "john")
|
assert.Equal(t, info.GetName(), "john")
|
||||||
addr := info.PubKey.Address()
|
addr := info.GetPubKey().Address()
|
||||||
|
|
||||||
john, err := cstore.Get("john")
|
john, err := cstore.Get("john")
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, john.Name, "john")
|
assert.Equal(t, john.GetName(), "john")
|
||||||
assert.Equal(t, john.PubKey.Address(), addr)
|
assert.Equal(t, john.GetPubKey().Address(), addr)
|
||||||
|
|
||||||
armor, err := cstore.Export("john")
|
armor, err := cstore.Export("john")
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
err = cstore.Import("john2", armor)
|
err = cstore.Import("john2", armor)
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
john2, err := cstore.Get("john2")
|
john2, err := cstore.Get("john2")
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, john.PubKey.Address(), addr)
|
assert.Equal(t, john.GetPubKey().Address(), addr)
|
||||||
assert.Equal(t, john.Name, "john")
|
assert.Equal(t, john.GetName(), "john")
|
||||||
assert.Equal(t, john, john2)
|
assert.Equal(t, john, john2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,33 +284,31 @@ func TestExportImportPubKey(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Create a private-public key pair and ensure consistency
|
// Create a private-public key pair and ensure consistency
|
||||||
info, _, err := cstore.Create("john", "passphrase", keys.AlgoEd25519)
|
info, _, err := cstore.CreateMnemonic("john", "passphrase", keys.AlgoEd25519)
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEqual(t, info.PrivKeyArmor, "")
|
assert.Equal(t, info.GetName(), "john")
|
||||||
assert.Equal(t, info.Name, "john")
|
addr := info.GetPubKey().Address()
|
||||||
addr := info.PubKey.Address()
|
|
||||||
john, err := cstore.Get("john")
|
john, err := cstore.Get("john")
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, john.Name, "john")
|
assert.Equal(t, john.GetName(), "john")
|
||||||
assert.Equal(t, john.PubKey.Address(), addr)
|
assert.Equal(t, john.GetPubKey().Address(), addr)
|
||||||
|
|
||||||
// Export the public key only
|
// Export the public key only
|
||||||
armor, err := cstore.ExportPubKey("john")
|
armor, err := cstore.ExportPubKey("john")
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
// Import it under a different name
|
// Import it under a different name
|
||||||
err = cstore.ImportPubKey("john-pubkey-only", armor)
|
err = cstore.ImportPubKey("john-pubkey-only", armor)
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
// Ensure consistency
|
// Ensure consistency
|
||||||
john2, err := cstore.Get("john-pubkey-only")
|
john2, err := cstore.Get("john-pubkey-only")
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, john2.PrivKeyArmor, "")
|
|
||||||
// Compare the public keys
|
// Compare the public keys
|
||||||
assert.True(t, john.PubKey.Equals(john2.PubKey))
|
assert.True(t, john.GetPubKey().Equals(john2.GetPubKey()))
|
||||||
// Ensure the original key hasn't changed
|
// Ensure the original key hasn't changed
|
||||||
john, err = cstore.Get("john")
|
john, err = cstore.Get("john")
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, john.PubKey.Address(), addr)
|
assert.Equal(t, john.GetPubKey().Address(), addr)
|
||||||
assert.Equal(t, john.Name, "john")
|
assert.Equal(t, john.GetName(), "john")
|
||||||
|
|
||||||
// Ensure keys cannot be overwritten
|
// Ensure keys cannot be overwritten
|
||||||
err = cstore.ImportPubKey("john-pubkey-only", armor)
|
err = cstore.ImportPubKey("john-pubkey-only", armor)
|
||||||
@ -312,7 +329,7 @@ func TestAdvancedKeyManagement(t *testing.T) {
|
|||||||
p1, p2 := "1234", "foobar"
|
p1, p2 := "1234", "foobar"
|
||||||
|
|
||||||
// make sure key works with initial password
|
// make sure key works with initial password
|
||||||
_, _, err := cstore.Create(n1, p1, algo)
|
_, _, err := cstore.CreateMnemonic(n1, p1, algo)
|
||||||
require.Nil(t, err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
assertPassword(t, cstore, n1, p1, p2)
|
assertPassword(t, cstore, n1, p1, p2)
|
||||||
|
|
||||||
@ -323,7 +340,7 @@ func TestAdvancedKeyManagement(t *testing.T) {
|
|||||||
|
|
||||||
// then it changes the password when correct
|
// then it changes the password when correct
|
||||||
err = cstore.Update(n1, p1, p2)
|
err = cstore.Update(n1, p1, p2)
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
// p2 is now the proper one!
|
// p2 is now the proper one!
|
||||||
assertPassword(t, cstore, n1, p2, p1)
|
assertPassword(t, cstore, n1, p2, p1)
|
||||||
|
|
||||||
@ -341,7 +358,7 @@ func TestAdvancedKeyManagement(t *testing.T) {
|
|||||||
|
|
||||||
// import succeeds
|
// import succeeds
|
||||||
err = cstore.Import(n2, exported)
|
err = cstore.Import(n2, exported)
|
||||||
assert.Nil(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// second import fails
|
// second import fails
|
||||||
err = cstore.Import(n2, exported)
|
err = cstore.Import(n2, exported)
|
||||||
@ -362,23 +379,23 @@ func TestSeedPhrase(t *testing.T) {
|
|||||||
p1, p2 := "1234", "foobar"
|
p1, p2 := "1234", "foobar"
|
||||||
|
|
||||||
// make sure key works with initial password
|
// make sure key works with initial password
|
||||||
info, seed, err := cstore.Create(n1, p1, algo)
|
info, seed, err := cstore.CreateMnemonic(n1, p1, algo)
|
||||||
require.Nil(t, err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
assert.Equal(t, n1, info.Name)
|
assert.Equal(t, n1, info.GetName())
|
||||||
assert.NotEmpty(t, seed)
|
assert.NotEmpty(t, seed)
|
||||||
|
|
||||||
// now, let us delete this key
|
// now, let us delete this key
|
||||||
err = cstore.Delete(n1, p1)
|
err = cstore.Delete(n1, p1)
|
||||||
require.Nil(t, err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
i, err := cstore.Get(n1)
|
_, err = cstore.Get(n1)
|
||||||
require.Equal(t, i, keys.Info{}, "expected empty info")
|
require.NotNil(t, err)
|
||||||
|
|
||||||
// let us re-create it from the seed-phrase
|
// let us re-create it from the seed-phrase
|
||||||
newInfo, err := cstore.Recover(n2, p2, seed)
|
newInfo, err := cstore.Recover(n2, p2, seed)
|
||||||
require.Nil(t, err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
assert.Equal(t, n2, newInfo.Name)
|
assert.Equal(t, n2, newInfo.GetName())
|
||||||
assert.Equal(t, info.Address(), newInfo.Address())
|
assert.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
|
||||||
assert.Equal(t, info.PubKey, newInfo.PubKey)
|
assert.Equal(t, info.GetPubKey(), newInfo.GetPubKey())
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleNew() {
|
func ExampleNew() {
|
||||||
@ -391,19 +408,19 @@ func ExampleNew() {
|
|||||||
sec := keys.AlgoSecp256k1
|
sec := keys.AlgoSecp256k1
|
||||||
|
|
||||||
// Add keys and see they return in alphabetical order
|
// Add keys and see they return in alphabetical order
|
||||||
bob, _, err := cstore.Create("Bob", "friend", ed)
|
bob, _, err := cstore.CreateMnemonic("Bob", "friend", ed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// this should never happen
|
// this should never happen
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
} else {
|
} else {
|
||||||
// return info here just like in List
|
// return info here just like in List
|
||||||
fmt.Println(bob.Name)
|
fmt.Println(bob.GetName())
|
||||||
}
|
}
|
||||||
cstore.Create("Alice", "secret", sec)
|
cstore.CreateMnemonic("Alice", "secret", sec)
|
||||||
cstore.Create("Carl", "mitm", ed)
|
cstore.CreateMnemonic("Carl", "mitm", ed)
|
||||||
info, _ := cstore.List()
|
info, _ := cstore.List()
|
||||||
for _, i := range info {
|
for _, i := range info {
|
||||||
fmt.Println(i.Name)
|
fmt.Println(i.GetName())
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to use passphrase to generate a signature
|
// We need to use passphrase to generate a signature
|
||||||
@ -415,11 +432,11 @@ func ExampleNew() {
|
|||||||
|
|
||||||
// and we can validate the signature with publically available info
|
// and we can validate the signature with publically available info
|
||||||
binfo, _ := cstore.Get("Bob")
|
binfo, _ := cstore.Get("Bob")
|
||||||
if !binfo.PubKey.Equals(bob.PubKey) {
|
if !binfo.GetPubKey().Equals(bob.GetPubKey()) {
|
||||||
fmt.Println("Get and Create return different keys")
|
fmt.Println("Get and Create return different keys")
|
||||||
}
|
}
|
||||||
|
|
||||||
if pub.Equals(binfo.PubKey) {
|
if pub.Equals(binfo.GetPubKey()) {
|
||||||
fmt.Println("signed by Bob")
|
fmt.Println("signed by Bob")
|
||||||
}
|
}
|
||||||
if !pub.VerifyBytes(tx, sig) {
|
if !pub.VerifyBytes(tx, sig) {
|
||||||
|
10
keys/keys.go
10
keys/keys.go
@ -2,14 +2,14 @@ package keys
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
type CryptoAlgo string
|
type SignAlgo string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
AlgoEd25519 = CryptoAlgo("ed25519")
|
AlgoEd25519 = SignAlgo("ed25519")
|
||||||
AlgoSecp256k1 = CryptoAlgo("secp256k1")
|
AlgoSecp256k1 = SignAlgo("secp256k1")
|
||||||
)
|
)
|
||||||
|
|
||||||
func cryptoAlgoToByte(key CryptoAlgo) byte {
|
func cryptoAlgoToByte(key SignAlgo) byte {
|
||||||
switch key {
|
switch key {
|
||||||
case AlgoEd25519:
|
case AlgoEd25519:
|
||||||
return 0x01
|
return 0x01
|
||||||
@ -20,7 +20,7 @@ func cryptoAlgoToByte(key CryptoAlgo) byte {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func byteToCryptoAlgo(b byte) CryptoAlgo {
|
func byteToSignAlgo(b byte) SignAlgo {
|
||||||
switch b {
|
switch b {
|
||||||
case 0x01:
|
case 0x01:
|
||||||
return AlgoEd25519
|
return AlgoEd25519
|
||||||
|
124
keys/types.go
124
keys/types.go
@ -4,54 +4,136 @@ import (
|
|||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Keybase allows simple CRUD on a keystore, as an aid to signing
|
// Keybase exposes operations on a generic keystore
|
||||||
type Keybase interface {
|
type Keybase interface {
|
||||||
// Sign some bytes
|
|
||||||
Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error)
|
// CRUD on the keystore
|
||||||
// Create a new keypair
|
|
||||||
Create(name, passphrase string, algo CryptoAlgo) (info Info, seed string, err error)
|
|
||||||
// Recover takes a seedphrase and loads in the key
|
|
||||||
Recover(name, passphrase, seedphrase string) (info Info, erro error)
|
|
||||||
List() ([]Info, error)
|
List() ([]Info, error)
|
||||||
Get(name string) (Info, error)
|
Get(name string) (Info, error)
|
||||||
Update(name, oldpass, newpass string) error
|
|
||||||
Delete(name, passphrase string) error
|
Delete(name, passphrase string) error
|
||||||
|
|
||||||
|
// Sign some bytes, looking up the private key to use
|
||||||
|
Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error)
|
||||||
|
|
||||||
|
// Create a new locally-stored keypair, returning the mnemonic
|
||||||
|
CreateMnemonic(name, passphrase string, algo SignAlgo) (info Info, seed string, err error)
|
||||||
|
// Recover takes a seedphrase and loads in the key
|
||||||
|
Recover(name, passphrase, seedphrase string) (info Info, erro error)
|
||||||
|
|
||||||
|
// Create, store, and return a new Ledger key reference
|
||||||
|
CreateLedger(name string, path crypto.DerivationPath, algo SignAlgo) (info Info, err error)
|
||||||
|
|
||||||
|
// Create, store, and return a new offline key reference
|
||||||
|
CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error)
|
||||||
|
|
||||||
|
// The following operations will *only* work on locally-stored keys
|
||||||
|
Update(name, oldpass, newpass string) error
|
||||||
Import(name string, armor string) (err error)
|
Import(name string, armor string) (err error)
|
||||||
ImportPubKey(name string, armor string) (err error)
|
ImportPubKey(name string, armor string) (err error)
|
||||||
Export(name string) (armor string, err error)
|
Export(name string) (armor string, err error)
|
||||||
ExportPubKey(name string) (armor string, err error)
|
ExportPubKey(name string) (armor string, err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info is the public information about a key
|
// Publically exposed information about a keypair
|
||||||
type Info struct {
|
type Info interface {
|
||||||
|
// Human-readable type for key listing
|
||||||
|
GetType() string
|
||||||
|
// Name of the key
|
||||||
|
GetName() string
|
||||||
|
// Public key
|
||||||
|
GetPubKey() crypto.PubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ Info = &localInfo{}
|
||||||
|
var _ Info = &ledgerInfo{}
|
||||||
|
var _ Info = &offlineInfo{}
|
||||||
|
|
||||||
|
// localInfo is the public information about a locally stored key
|
||||||
|
type localInfo struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
PubKey crypto.PubKey `json:"pubkey"`
|
PubKey crypto.PubKey `json:"pubkey"`
|
||||||
PrivKeyArmor string `json:"privkey.armor"`
|
PrivKeyArmor string `json:"privkey.armor"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func newInfo(name string, pub crypto.PubKey, privArmor string) Info {
|
func newLocalInfo(name string, pub crypto.PubKey, privArmor string) Info {
|
||||||
return Info{
|
return &localInfo{
|
||||||
Name: name,
|
Name: name,
|
||||||
PubKey: pub,
|
PubKey: pub,
|
||||||
PrivKeyArmor: privArmor,
|
PrivKeyArmor: privArmor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address is a helper function to calculate the address from the pubkey
|
func (i localInfo) GetType() string {
|
||||||
func (i Info) Address() []byte {
|
return "local"
|
||||||
return i.PubKey.Address()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i Info) bytes() []byte {
|
func (i localInfo) GetName() string {
|
||||||
bz, err := cdc.MarshalBinaryBare(i)
|
return i.Name
|
||||||
if err != nil {
|
}
|
||||||
panic(err)
|
|
||||||
|
func (i localInfo) GetPubKey() crypto.PubKey {
|
||||||
|
return i.PubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// ledgerInfo is the public information about a Ledger key
|
||||||
|
type ledgerInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
PubKey crypto.PubKey `json:"pubkey"`
|
||||||
|
Path crypto.DerivationPath `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLedgerInfo(name string, pub crypto.PubKey, path crypto.DerivationPath) Info {
|
||||||
|
return &ledgerInfo{
|
||||||
|
Name: name,
|
||||||
|
PubKey: pub,
|
||||||
|
Path: path,
|
||||||
}
|
}
|
||||||
return bz
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i ledgerInfo) GetType() string {
|
||||||
|
return "ledger"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i ledgerInfo) GetName() string {
|
||||||
|
return i.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i ledgerInfo) GetPubKey() crypto.PubKey {
|
||||||
|
return i.PubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// offlineInfo is the public information about an offline key
|
||||||
|
type offlineInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
PubKey crypto.PubKey `json:"pubkey"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOfflineInfo(name string, pub crypto.PubKey) Info {
|
||||||
|
return &offlineInfo{
|
||||||
|
Name: name,
|
||||||
|
PubKey: pub,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i offlineInfo) GetType() string {
|
||||||
|
return "offline"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i offlineInfo) GetName() string {
|
||||||
|
return i.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i offlineInfo) GetPubKey() crypto.PubKey {
|
||||||
|
return i.PubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// encoding info
|
||||||
|
func writeInfo(i Info) []byte {
|
||||||
|
return cdc.MustMarshalBinary(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// decoding info
|
||||||
func readInfo(bz []byte) (info Info, err error) {
|
func readInfo(bz []byte) (info Info, err error) {
|
||||||
err = cdc.UnmarshalBinaryBare(bz, &info)
|
err = cdc.UnmarshalBinary(bz, &info)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -9,4 +9,8 @@ var cdc = amino.NewCodec()
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
crypto.RegisterAmino(cdc)
|
crypto.RegisterAmino(cdc)
|
||||||
|
cdc.RegisterInterface((*Info)(nil), nil)
|
||||||
|
cdc.RegisterConcrete(localInfo{}, "crypto/keys/localInfo", nil)
|
||||||
|
cdc.RegisterConcrete(ledgerInfo{}, "crypto/keys/ledgerInfo", nil)
|
||||||
|
cdc.RegisterConcrete(offlineInfo{}, "crypto/keys/offlineInfo", nil)
|
||||||
}
|
}
|
||||||
|
19
ledger_common.go
Normal file
19
ledger_common.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
ledger "github.com/zondax/ledger-goclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
var device *ledger.Ledger
|
||||||
|
|
||||||
|
// Ledger derivation path
|
||||||
|
type DerivationPath = []uint32
|
||||||
|
|
||||||
|
// getLedger gets a copy of the device, and caches it
|
||||||
|
func getLedger() (*ledger.Ledger, error) {
|
||||||
|
var err error
|
||||||
|
if device == nil {
|
||||||
|
device, err = ledger.FindLedger()
|
||||||
|
}
|
||||||
|
return device, err
|
||||||
|
}
|
146
ledger_secp256k1.go
Normal file
146
ledger_secp256k1.go
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
secp256k1 "github.com/btcsuite/btcd/btcec"
|
||||||
|
ledger "github.com/zondax/ledger-goclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pubkeyLedgerSecp256k1(device *ledger.Ledger, path DerivationPath) (pub PubKey, err error) {
|
||||||
|
key, err := device.GetPublicKeySECP256K1(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error fetching public key: %v", err)
|
||||||
|
}
|
||||||
|
var p PubKeySecp256k1
|
||||||
|
// Reserialize in the 33-byte compressed format
|
||||||
|
cmp, err := secp256k1.ParsePubKey(key[:], secp256k1.S256())
|
||||||
|
copy(p[:], cmp.SerializeCompressed())
|
||||||
|
pub = p
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func signLedgerSecp256k1(device *ledger.Ledger, path DerivationPath, msg []byte) (sig Signature, err error) {
|
||||||
|
bsig, err := device.SignSECP256K1(path, msg)
|
||||||
|
if err != nil {
|
||||||
|
return sig, err
|
||||||
|
}
|
||||||
|
sig = SignatureSecp256k1FromBytes(bsig)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivKeyLedgerSecp256k1 implements PrivKey, calling the ledger nano
|
||||||
|
// we cache the PubKey from the first call to use it later
|
||||||
|
type PrivKeyLedgerSecp256k1 struct {
|
||||||
|
// PubKey should be private, but we want to encode it via go-amino
|
||||||
|
// so we can view the address later, even without having the ledger
|
||||||
|
// attached
|
||||||
|
CachedPubKey PubKey
|
||||||
|
Path DerivationPath
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPrivKeyLedgerSecp256k1 will generate a new key and store the
|
||||||
|
// public key for later use.
|
||||||
|
func NewPrivKeyLedgerSecp256k1(path DerivationPath) (PrivKey, error) {
|
||||||
|
var pk PrivKeyLedgerSecp256k1
|
||||||
|
pk.Path = path
|
||||||
|
// getPubKey will cache the pubkey for later use,
|
||||||
|
// this allows us to return an error early if the ledger
|
||||||
|
// is not plugged in
|
||||||
|
_, err := pk.getPubKey()
|
||||||
|
return &pk, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateKey allows us to verify the sanity of a key
|
||||||
|
// after loading it from disk
|
||||||
|
func (pk PrivKeyLedgerSecp256k1) ValidateKey() error {
|
||||||
|
// getPubKey will return an error if the ledger is not
|
||||||
|
// properly set up...
|
||||||
|
pub, err := pk.forceGetPubKey()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// verify this matches cached address
|
||||||
|
if !pub.Equals(pk.CachedPubKey) {
|
||||||
|
return fmt.Errorf("cached key does not match retrieved key")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertIsPrivKeyInner fulfils PrivKey Interface
|
||||||
|
func (pk *PrivKeyLedgerSecp256k1) AssertIsPrivKeyInner() {}
|
||||||
|
|
||||||
|
// 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 PrivKeyLedgerSecp256k1) Bytes() []byte {
|
||||||
|
return cdc.MustMarshalBinaryBare(pk)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign calls the ledger and stores the PubKey for future use
|
||||||
|
//
|
||||||
|
// Communication is checked on NewPrivKeyLedger and PrivKeyFromBytes,
|
||||||
|
// returning an error, so this should only trigger if the privkey is held
|
||||||
|
// in memory for a while before use.
|
||||||
|
func (pk PrivKeyLedgerSecp256k1) Sign(msg []byte) (Signature, error) {
|
||||||
|
dev, err := getLedger()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := signLedgerSecp256k1(dev, pk.Path, msg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err := pubkeyLedgerSecp256k1(dev, pk.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we have no pubkey yet, store it for future queries
|
||||||
|
if pk.CachedPubKey == nil {
|
||||||
|
pk.CachedPubKey = pub
|
||||||
|
} else if !pk.CachedPubKey.Equals(pub) {
|
||||||
|
return nil, fmt.Errorf("stored key does not match signing key")
|
||||||
|
}
|
||||||
|
return sig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PubKey returns the stored PubKey
|
||||||
|
func (pk PrivKeyLedgerSecp256k1) PubKey() (PubKey, error) {
|
||||||
|
return pk.getPubKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPubKey reads the pubkey from cache or from the ledger itself
|
||||||
|
// since this involves IO, it may return an error, which is not exposed
|
||||||
|
// in the PubKey interface, so this function allows better error handling
|
||||||
|
func (pk PrivKeyLedgerSecp256k1) getPubKey() (key PubKey, err error) {
|
||||||
|
// if we have no pubkey, set it
|
||||||
|
if pk.CachedPubKey == nil {
|
||||||
|
pk.CachedPubKey, err = pk.forceGetPubKey()
|
||||||
|
}
|
||||||
|
return pk.CachedPubKey, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// forceGetPubKey is like getPubKey but ignores any cached key
|
||||||
|
// and ensures we get it from the ledger itself.
|
||||||
|
func (pk PrivKeyLedgerSecp256k1) forceGetPubKey() (key PubKey, err error) {
|
||||||
|
dev, err := getLedger()
|
||||||
|
if err != nil {
|
||||||
|
return key, fmt.Errorf("cannot connect to Ledger device - error: %v", err)
|
||||||
|
}
|
||||||
|
key, err = pubkeyLedgerSecp256k1(dev, pk.Path)
|
||||||
|
if err != nil {
|
||||||
|
return key, fmt.Errorf("please open Cosmos app on the Ledger device - error: %v", err)
|
||||||
|
}
|
||||||
|
return key, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals fulfils PrivKey Interface - makes sure both keys refer to the
|
||||||
|
// same
|
||||||
|
func (pk PrivKeyLedgerSecp256k1) Equals(other PrivKey) bool {
|
||||||
|
if ledger, ok := other.(*PrivKeyLedgerSecp256k1); ok {
|
||||||
|
return pk.CachedPubKey.Equals(ledger.CachedPubKey)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
65
ledger_test.go
Normal file
65
ledger_test.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRealLedgerSecp256k1(t *testing.T) {
|
||||||
|
|
||||||
|
if os.Getenv("WITH_LEDGER") == "" {
|
||||||
|
t.Skip("Set WITH_LEDGER to run code on real ledger")
|
||||||
|
}
|
||||||
|
msg := []byte("kuhehfeohg")
|
||||||
|
|
||||||
|
path := DerivationPath{44, 60, 0, 0, 0}
|
||||||
|
|
||||||
|
priv, err := NewPrivKeyLedgerSecp256k1(path)
|
||||||
|
require.Nil(t, err, "%+v", err)
|
||||||
|
pub, err := priv.PubKey()
|
||||||
|
require.Nil(t, err)
|
||||||
|
sig, err := priv.Sign(msg)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
valid := pub.VerifyBytes(msg, sig)
|
||||||
|
assert.True(t, valid)
|
||||||
|
|
||||||
|
// now, let's serialize the key and make sure it still works
|
||||||
|
bs := priv.Bytes()
|
||||||
|
priv2, err := PrivKeyFromBytes(bs)
|
||||||
|
require.Nil(t, err, "%+v", err)
|
||||||
|
|
||||||
|
// make sure we get the same pubkey when we load from disk
|
||||||
|
pub2, err := priv2.PubKey()
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, pub, pub2)
|
||||||
|
|
||||||
|
// signing with the loaded key should match the original pubkey
|
||||||
|
sig, err = priv2.Sign(msg)
|
||||||
|
require.Nil(t, err)
|
||||||
|
valid = pub.VerifyBytes(msg, sig)
|
||||||
|
assert.True(t, valid)
|
||||||
|
|
||||||
|
// make sure pubkeys serialize properly as well
|
||||||
|
bs = pub.Bytes()
|
||||||
|
bpub, err := PubKeyFromBytes(bs)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, pub, bpub)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRealLedgerErrorHandling calls. These tests assume
|
||||||
|
// the ledger is not plugged in....
|
||||||
|
func TestRealLedgerErrorHandling(t *testing.T) {
|
||||||
|
if os.Getenv("WITH_LEDGER") != "" {
|
||||||
|
t.Skip("Skipping on WITH_LEDGER as it tests unplugged cases")
|
||||||
|
}
|
||||||
|
|
||||||
|
// first, try to generate a key, must return an error
|
||||||
|
// (no panic)
|
||||||
|
path := DerivationPath{44, 60, 0, 0, 0}
|
||||||
|
_, err := NewPrivKeyLedgerSecp256k1(path)
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
@ -24,16 +24,13 @@ func newSimpleMap() *simpleMap {
|
|||||||
func (sm *simpleMap) Set(key string, value Hasher) {
|
func (sm *simpleMap) Set(key string, value Hasher) {
|
||||||
sm.sorted = false
|
sm.sorted = false
|
||||||
|
|
||||||
// Hash the key to blind it... why not?
|
// The value is hashed, so you can
|
||||||
khash := tmhash.Sum([]byte(key))
|
|
||||||
|
|
||||||
// And the value is hashed too, so you can
|
|
||||||
// check for equality with a cached value (say)
|
// check for equality with a cached value (say)
|
||||||
// and make a determination to fetch or not.
|
// and make a determination to fetch or not.
|
||||||
vhash := value.Hash()
|
vhash := value.Hash()
|
||||||
|
|
||||||
sm.kvs = append(sm.kvs, cmn.KVPair{
|
sm.kvs = append(sm.kvs, cmn.KVPair{
|
||||||
Key: khash,
|
Key: []byte(key),
|
||||||
Value: vhash,
|
Value: vhash,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -67,9 +64,9 @@ func (sm *simpleMap) KVPairs() cmn.KVPairs {
|
|||||||
// A local extension to KVPair that can be hashed.
|
// A local extension to KVPair that can be hashed.
|
||||||
// Key and value are length prefixed and concatenated,
|
// Key and value are length prefixed and concatenated,
|
||||||
// then hashed.
|
// then hashed.
|
||||||
type kvPair cmn.KVPair
|
type KVPair cmn.KVPair
|
||||||
|
|
||||||
func (kv kvPair) Hash() []byte {
|
func (kv KVPair) Hash() []byte {
|
||||||
hasher := tmhash.New()
|
hasher := tmhash.New()
|
||||||
err := encodeByteSlice(hasher, kv.Key)
|
err := encodeByteSlice(hasher, kv.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -85,7 +82,7 @@ func (kv kvPair) Hash() []byte {
|
|||||||
func hashKVPairs(kvs cmn.KVPairs) []byte {
|
func hashKVPairs(kvs cmn.KVPairs) []byte {
|
||||||
kvsH := make([]Hasher, len(kvs))
|
kvsH := make([]Hasher, len(kvs))
|
||||||
for i, kvp := range kvs {
|
for i, kvp := range kvs {
|
||||||
kvsH[i] = kvPair(kvp)
|
kvsH[i] = KVPair(kvp)
|
||||||
}
|
}
|
||||||
return SimpleHashFromHashers(kvsH)
|
return SimpleHashFromHashers(kvsH)
|
||||||
}
|
}
|
||||||
|
@ -18,37 +18,37 @@ func TestSimpleMap(t *testing.T) {
|
|||||||
{
|
{
|
||||||
db := newSimpleMap()
|
db := newSimpleMap()
|
||||||
db.Set("key1", strHasher("value1"))
|
db.Set("key1", strHasher("value1"))
|
||||||
assert.Equal(t, "3dafc06a52039d029be57c75c9d16356a4256ef4", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
assert.Equal(t, "fa9bc106ffd932d919bee935ceb6cf2b3dd72d8f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
db := newSimpleMap()
|
db := newSimpleMap()
|
||||||
db.Set("key1", strHasher("value2"))
|
db.Set("key1", strHasher("value2"))
|
||||||
assert.Equal(t, "03eb5cfdff646bc4e80fec844e72fd248a1c6b2c", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
assert.Equal(t, "e00e7dcfe54e9fafef5111e813a587f01ba9c3e8", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
db := newSimpleMap()
|
db := newSimpleMap()
|
||||||
db.Set("key1", strHasher("value1"))
|
db.Set("key1", strHasher("value1"))
|
||||||
db.Set("key2", strHasher("value2"))
|
db.Set("key2", strHasher("value2"))
|
||||||
assert.Equal(t, "acc3971eab8513171cc90ce8b74f368c38f9657d", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
assert.Equal(t, "eff12d1c703a1022ab509287c0f196130123d786", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
db := newSimpleMap()
|
db := newSimpleMap()
|
||||||
db.Set("key2", strHasher("value2")) // NOTE: out of order
|
db.Set("key2", strHasher("value2")) // NOTE: out of order
|
||||||
db.Set("key1", strHasher("value1"))
|
db.Set("key1", strHasher("value1"))
|
||||||
assert.Equal(t, "acc3971eab8513171cc90ce8b74f368c38f9657d", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
assert.Equal(t, "eff12d1c703a1022ab509287c0f196130123d786", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
db := newSimpleMap()
|
db := newSimpleMap()
|
||||||
db.Set("key1", strHasher("value1"))
|
db.Set("key1", strHasher("value1"))
|
||||||
db.Set("key2", strHasher("value2"))
|
db.Set("key2", strHasher("value2"))
|
||||||
db.Set("key3", strHasher("value3"))
|
db.Set("key3", strHasher("value3"))
|
||||||
assert.Equal(t, "0cd117ad14e6cd22edcd9aa0d84d7063b54b862f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
assert.Equal(t, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
db := newSimpleMap()
|
db := newSimpleMap()
|
||||||
db.Set("key2", strHasher("value2")) // NOTE: out of order
|
db.Set("key2", strHasher("value2")) // NOTE: out of order
|
||||||
db.Set("key1", strHasher("value1"))
|
db.Set("key1", strHasher("value1"))
|
||||||
db.Set("key3", strHasher("value3"))
|
db.Set("key3", strHasher("value3"))
|
||||||
assert.Equal(t, "0cd117ad14e6cd22edcd9aa0d84d7063b54b862f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
assert.Equal(t, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ func SimpleProofsFromHashers(items []Hasher) (rootHash []byte, proofs []*SimpleP
|
|||||||
// SimpleProofsFromMap generates proofs from a map. The keys/values of the map will be used as the keys/values
|
// SimpleProofsFromMap generates proofs from a map. The keys/values of the map will be used as the keys/values
|
||||||
// in the underlying key-value pairs.
|
// in the underlying key-value pairs.
|
||||||
// The keys are sorted before the proofs are computed.
|
// The keys are sorted before the proofs are computed.
|
||||||
func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs []*SimpleProof) {
|
func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs map[string]*SimpleProof, keys []string) {
|
||||||
sm := newSimpleMap()
|
sm := newSimpleMap()
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
sm.Set(k, v)
|
sm.Set(k, v)
|
||||||
@ -36,9 +36,17 @@ func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs []*Simple
|
|||||||
kvs := sm.kvs
|
kvs := sm.kvs
|
||||||
kvsH := make([]Hasher, 0, len(kvs))
|
kvsH := make([]Hasher, 0, len(kvs))
|
||||||
for _, kvp := range kvs {
|
for _, kvp := range kvs {
|
||||||
kvsH = append(kvsH, kvPair(kvp))
|
kvsH = append(kvsH, KVPair(kvp))
|
||||||
}
|
}
|
||||||
return SimpleProofsFromHashers(kvsH)
|
|
||||||
|
rootHash, proofList := SimpleProofsFromHashers(kvsH)
|
||||||
|
proofs = make(map[string]*SimpleProof)
|
||||||
|
keys = make([]string, len(proofList))
|
||||||
|
for i, kvp := range kvs {
|
||||||
|
proofs[string(kvp.Key)] = proofList[i]
|
||||||
|
keys[i] = string(kvp.Key)
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that leafHash is a leaf hash of the simple-merkle-tree
|
// Verify that leafHash is a leaf hash of the simple-merkle-tree
|
||||||
|
47
priv_key.go
47
priv_key.go
@ -6,7 +6,6 @@ import (
|
|||||||
secp256k1 "github.com/btcsuite/btcd/btcec"
|
secp256k1 "github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/tendermint/ed25519"
|
"github.com/tendermint/ed25519"
|
||||||
"github.com/tendermint/ed25519/extra25519"
|
"github.com/tendermint/ed25519/extra25519"
|
||||||
. "github.com/tendermint/tmlibs/common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) {
|
func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) {
|
||||||
@ -18,8 +17,8 @@ func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) {
|
|||||||
|
|
||||||
type PrivKey interface {
|
type PrivKey interface {
|
||||||
Bytes() []byte
|
Bytes() []byte
|
||||||
Sign(msg []byte) Signature
|
Sign(msg []byte) (Signature, error)
|
||||||
PubKey() PubKey
|
PubKey() (PubKey, error)
|
||||||
Equals(PrivKey) bool
|
Equals(PrivKey) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,23 +30,19 @@ var _ PrivKey = PrivKeyEd25519{}
|
|||||||
type PrivKeyEd25519 [64]byte
|
type PrivKeyEd25519 [64]byte
|
||||||
|
|
||||||
func (privKey PrivKeyEd25519) Bytes() []byte {
|
func (privKey PrivKeyEd25519) Bytes() []byte {
|
||||||
bz, err := cdc.MarshalBinaryBare(privKey)
|
return cdc.MustMarshalBinaryBare(privKey)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return bz
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (privKey PrivKeyEd25519) Sign(msg []byte) Signature {
|
func (privKey PrivKeyEd25519) Sign(msg []byte) (Signature, error) {
|
||||||
privKeyBytes := [64]byte(privKey)
|
privKeyBytes := [64]byte(privKey)
|
||||||
signatureBytes := ed25519.Sign(&privKeyBytes, msg)
|
signatureBytes := ed25519.Sign(&privKeyBytes, msg)
|
||||||
return SignatureEd25519(*signatureBytes)
|
return SignatureEd25519(*signatureBytes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (privKey PrivKeyEd25519) PubKey() PubKey {
|
func (privKey PrivKeyEd25519) PubKey() (PubKey, error) {
|
||||||
privKeyBytes := [64]byte(privKey)
|
privKeyBytes := [64]byte(privKey)
|
||||||
pubBytes := *ed25519.MakePublicKey(&privKeyBytes)
|
pubBytes := *ed25519.MakePublicKey(&privKeyBytes)
|
||||||
return PubKeyEd25519(pubBytes)
|
return PubKeyEd25519(pubBytes), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals - you probably don't need to use this.
|
// Equals - you probably don't need to use this.
|
||||||
@ -67,12 +62,6 @@ func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte {
|
|||||||
return keyCurve25519
|
return keyCurve25519
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func (privKey PrivKeyEd25519) String() string {
|
|
||||||
return Fmt("PrivKeyEd25519{*****}")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Deterministically generates new priv-key bytes from key.
|
// Deterministically generates new priv-key bytes from key.
|
||||||
func (privKey PrivKeyEd25519) Generate(index int) PrivKeyEd25519 {
|
func (privKey PrivKeyEd25519) Generate(index int) PrivKeyEd25519 {
|
||||||
bz, err := cdc.MarshalBinaryBare(struct {
|
bz, err := cdc.MarshalBinaryBare(struct {
|
||||||
@ -114,27 +103,23 @@ var _ PrivKey = PrivKeySecp256k1{}
|
|||||||
type PrivKeySecp256k1 [32]byte
|
type PrivKeySecp256k1 [32]byte
|
||||||
|
|
||||||
func (privKey PrivKeySecp256k1) Bytes() []byte {
|
func (privKey PrivKeySecp256k1) Bytes() []byte {
|
||||||
bz, err := cdc.MarshalBinaryBare(privKey)
|
return cdc.MustMarshalBinaryBare(privKey)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return bz
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (privKey PrivKeySecp256k1) Sign(msg []byte) Signature {
|
func (privKey PrivKeySecp256k1) Sign(msg []byte) (Signature, error) {
|
||||||
priv__, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:])
|
priv__, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:])
|
||||||
sig__, err := priv__.Sign(Sha256(msg))
|
sig__, err := priv__.Sign(Sha256(msg))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
PanicSanity(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
return SignatureSecp256k1(sig__.Serialize())
|
return SignatureSecp256k1(sig__.Serialize()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (privKey PrivKeySecp256k1) PubKey() PubKey {
|
func (privKey PrivKeySecp256k1) PubKey() (PubKey, error) {
|
||||||
_, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:])
|
_, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:])
|
||||||
var pub PubKeySecp256k1
|
var pub PubKeySecp256k1
|
||||||
copy(pub[:], pub__.SerializeCompressed())
|
copy(pub[:], pub__.SerializeCompressed())
|
||||||
return pub
|
return pub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals - you probably don't need to use this.
|
// Equals - you probably don't need to use this.
|
||||||
@ -147,12 +132,6 @@ func (privKey PrivKeySecp256k1) Equals(other PrivKey) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
func (privKey PrivKeySecp256k1) String() string {
|
|
||||||
return Fmt("PrivKeySecp256k1{*****}")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Deterministically generates new priv-key bytes from key.
|
// Deterministically generates new priv-key bytes from key.
|
||||||
func (key PrivKeySecp256k1) Generate(index int) PrivKeySecp256k1 {
|
func (key PrivKeySecp256k1) Generate(index int) PrivKeySecp256k1 {
|
||||||
|
@ -11,7 +11,11 @@ func TestGeneratePrivKey(t *testing.T) {
|
|||||||
testPriv := crypto.GenPrivKeyEd25519()
|
testPriv := crypto.GenPrivKeyEd25519()
|
||||||
testGenerate := testPriv.Generate(1)
|
testGenerate := testPriv.Generate(1)
|
||||||
signBytes := []byte("something to sign")
|
signBytes := []byte("something to sign")
|
||||||
assert.True(t, testGenerate.PubKey().VerifyBytes(signBytes, testGenerate.Sign(signBytes)))
|
pub, err := testGenerate.PubKey()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
sig, err := testGenerate.Sign(signBytes)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.True(t, pub.VerifyBytes(signBytes, sig))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
13
pub_key.go
13
pub_key.go
@ -5,11 +5,16 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ripemd160"
|
||||||
|
|
||||||
secp256k1 "github.com/btcsuite/btcd/btcec"
|
secp256k1 "github.com/btcsuite/btcd/btcec"
|
||||||
|
|
||||||
"github.com/tendermint/ed25519"
|
"github.com/tendermint/ed25519"
|
||||||
"github.com/tendermint/ed25519/extra25519"
|
"github.com/tendermint/ed25519/extra25519"
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
"golang.org/x/crypto/ripemd160"
|
|
||||||
|
"github.com/tendermint/go-crypto/tmhash"
|
||||||
)
|
)
|
||||||
|
|
||||||
// An address is a []byte, but hex-encoded even in JSON.
|
// An address is a []byte, but hex-encoded even in JSON.
|
||||||
@ -38,11 +43,9 @@ var _ PubKey = PubKeyEd25519{}
|
|||||||
// Implements PubKeyInner
|
// Implements PubKeyInner
|
||||||
type PubKeyEd25519 [32]byte
|
type PubKeyEd25519 [32]byte
|
||||||
|
|
||||||
|
// Address is the SHA256-20 of the raw pubkey bytes.
|
||||||
func (pubKey PubKeyEd25519) Address() Address {
|
func (pubKey PubKeyEd25519) Address() Address {
|
||||||
// append type byte
|
return Address(tmhash.Sum(pubKey[:]))
|
||||||
hasher := ripemd160.New()
|
|
||||||
hasher.Write(pubKey.Bytes()) // does not error
|
|
||||||
return Address(hasher.Sum(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pubKey PubKeyEd25519) Bytes() []byte {
|
func (pubKey PubKeyEd25519) Bytes() []byte {
|
||||||
|
@ -33,9 +33,11 @@ func TestPubKeySecp256k1Address(t *testing.T) {
|
|||||||
var priv PrivKeySecp256k1
|
var priv PrivKeySecp256k1
|
||||||
copy(priv[:], privB)
|
copy(priv[:], privB)
|
||||||
|
|
||||||
pubT := priv.PubKey().(PubKeySecp256k1)
|
pubKey, err := priv.PubKey()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
pubT, _ := pubKey.(PubKeySecp256k1)
|
||||||
pub := pubT[:]
|
pub := pubT[:]
|
||||||
addr := priv.PubKey().Address()
|
addr := pubKey.Address()
|
||||||
|
|
||||||
assert.Equal(t, pub, pubB, "Expected pub keys to match")
|
assert.Equal(t, pub, pubB, "Expected pub keys to match")
|
||||||
assert.Equal(t, addr, addrB, "Expected addresses to match")
|
assert.Equal(t, addr, addrB, "Expected addresses to match")
|
||||||
|
@ -80,3 +80,9 @@ func (sig SignatureSecp256k1) Equals(other Signature) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SignatureSecp256k1FromBytes(data []byte) Signature {
|
||||||
|
sig := make(SignatureSecp256k1, len(data))
|
||||||
|
copy(sig[:], data)
|
||||||
|
return sig
|
||||||
|
}
|
||||||
|
@ -3,16 +3,19 @@ package crypto
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSignAndValidateEd25519(t *testing.T) {
|
func TestSignAndValidateEd25519(t *testing.T) {
|
||||||
|
|
||||||
privKey := GenPrivKeyEd25519()
|
privKey := GenPrivKeyEd25519()
|
||||||
pubKey := privKey.PubKey()
|
pubKey, err := privKey.PubKey()
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
msg := CRandBytes(128)
|
msg := CRandBytes(128)
|
||||||
sig := privKey.Sign(msg)
|
sig, err := privKey.Sign(msg)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
// Test the signature
|
// Test the signature
|
||||||
assert.True(t, pubKey.VerifyBytes(msg, sig))
|
assert.True(t, pubKey.VerifyBytes(msg, sig))
|
||||||
@ -27,10 +30,12 @@ func TestSignAndValidateEd25519(t *testing.T) {
|
|||||||
|
|
||||||
func TestSignAndValidateSecp256k1(t *testing.T) {
|
func TestSignAndValidateSecp256k1(t *testing.T) {
|
||||||
privKey := GenPrivKeySecp256k1()
|
privKey := GenPrivKeySecp256k1()
|
||||||
pubKey := privKey.PubKey()
|
pubKey, err := privKey.PubKey()
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
msg := CRandBytes(128)
|
msg := CRandBytes(128)
|
||||||
sig := privKey.Sign(msg)
|
sig, err := privKey.Sign(msg)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
assert.True(t, pubKey.VerifyBytes(msg, sig))
|
assert.True(t, pubKey.VerifyBytes(msg, sig))
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
const Version = "0.6.2"
|
const Version = "0.8.0-dev"
|
||||||
|
Reference in New Issue
Block a user