mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-29 04:31:44 +00:00
3
Makefile
3
Makefile
@ -56,11 +56,12 @@ metalinter_test: ensure_tools
|
|||||||
--enable=structcheck \
|
--enable=structcheck \
|
||||||
--enable=unconvert \
|
--enable=unconvert \
|
||||||
--enable=unused \
|
--enable=unused \
|
||||||
--enable=vetshadow \
|
|
||||||
--enable=vet \
|
--enable=vet \
|
||||||
--enable=varcheck \
|
--enable=varcheck \
|
||||||
|
--enable=vetshadow \
|
||||||
./...
|
./...
|
||||||
|
|
||||||
|
|
||||||
#--enable=dupl \
|
#--enable=dupl \
|
||||||
#--enable=errcheck \
|
#--enable=errcheck \
|
||||||
#--enable=goimports \
|
#--enable=goimports \
|
||||||
|
@ -145,8 +145,6 @@ func (s SigMessage) Bytes() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEmbededWireEncodings(t *testing.T) {
|
func TestEmbededWireEncodings(t *testing.T) {
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
privKey PrivKey
|
privKey PrivKey
|
||||||
keyType byte
|
keyType byte
|
||||||
@ -171,7 +169,7 @@ func TestEmbededWireEncodings(t *testing.T) {
|
|||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
pubKey := tc.privKey.PubKey()
|
pubKey := tc.privKey.PubKey()
|
||||||
sig := tc.privKey.Sign(payload)
|
sig := tc.privKey.Sign(payload)
|
||||||
assert.True(pubKey.VerifyBytes(payload, sig), "%d", i)
|
assert.True(t, pubKey.VerifyBytes(payload, sig), "%d", i)
|
||||||
|
|
||||||
msg := SigMessage{
|
msg := SigMessage{
|
||||||
Key: pubKey,
|
Key: pubKey,
|
||||||
|
30
glide.lock
generated
30
glide.lock
generated
@ -1,5 +1,5 @@
|
|||||||
hash: 6e06a42eafe0aeff112cee86aef6b2cab0e2f62c2e6bfccfb629aa22f6b62773
|
hash: 765599cf3dcac232d3d2ece65c3b016d0abe55d0536adfe93d11e0dd29287de4
|
||||||
updated: 2017-10-27T18:45:18.350198941+02:00
|
updated: 2017-11-13T14:37:54.127163059+01:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/btcsuite/btcd
|
- name: github.com/btcsuite/btcd
|
||||||
version: c7588cbf7690cd9f047a28efa2dcd8f2435a4e5e
|
version: c7588cbf7690cd9f047a28efa2dcd8f2435a4e5e
|
||||||
@ -10,7 +10,7 @@ imports:
|
|||||||
subpackages:
|
subpackages:
|
||||||
- base58
|
- base58
|
||||||
- name: github.com/ethanfrey/hid
|
- name: github.com/ethanfrey/hid
|
||||||
version: 660bb717bd4e7cbcdf0f7cd5cadf1cb2e4be452a
|
version: ea8f4c923d3bc8c9c312b7d38724a294f184258a
|
||||||
- name: github.com/ethanfrey/ledger
|
- name: github.com/ethanfrey/ledger
|
||||||
version: 23a7bb9d74bc83a862fcb4bddde24215b2295ad9
|
version: 23a7bb9d74bc83a862fcb4bddde24215b2295ad9
|
||||||
- name: github.com/go-kit/kit
|
- name: github.com/go-kit/kit
|
||||||
@ -29,26 +29,46 @@ imports:
|
|||||||
version: 71201497bace774495daed26a3874fd339e0b538
|
version: 71201497bace774495daed26a3874fd339e0b538
|
||||||
- name: github.com/go-stack/stack
|
- name: github.com/go-stack/stack
|
||||||
version: 817915b46b97fd7bb80e8ab6b69f01a53ac3eebf
|
version: 817915b46b97fd7bb80e8ab6b69f01a53ac3eebf
|
||||||
|
- name: github.com/golang/snappy
|
||||||
|
version: 553a641470496b2327abcac10b36396bd98e45c9
|
||||||
- name: github.com/howeyc/crc16
|
- name: github.com/howeyc/crc16
|
||||||
version: 96a97a1abb579c7ff1a8ffa77f2e72d1c314b57f
|
version: 96a97a1abb579c7ff1a8ffa77f2e72d1c314b57f
|
||||||
|
- name: github.com/jmhodges/levigo
|
||||||
|
version: c42d9e0ca023e2198120196f842701bb4c55d7b9
|
||||||
- name: github.com/kr/logfmt
|
- name: github.com/kr/logfmt
|
||||||
version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
|
version: b84e30acd515aadc4b783ad4ff83aff3299bdfe0
|
||||||
- name: github.com/pkg/errors
|
- name: github.com/pkg/errors
|
||||||
version: 645ef00459ed84a119197bfb8d8205042c6df63d
|
version: 645ef00459ed84a119197bfb8d8205042c6df63d
|
||||||
|
- name: github.com/syndtr/goleveldb
|
||||||
|
version: b89cc31ef7977104127d34c1bd31ebd1a9db2199
|
||||||
|
subpackages:
|
||||||
|
- leveldb
|
||||||
|
- leveldb/cache
|
||||||
|
- leveldb/comparer
|
||||||
|
- leveldb/errors
|
||||||
|
- leveldb/filter
|
||||||
|
- leveldb/iterator
|
||||||
|
- leveldb/journal
|
||||||
|
- leveldb/memdb
|
||||||
|
- leveldb/opt
|
||||||
|
- leveldb/storage
|
||||||
|
- leveldb/table
|
||||||
|
- leveldb/util
|
||||||
- name: github.com/tendermint/ed25519
|
- name: github.com/tendermint/ed25519
|
||||||
version: d8387025d2b9d158cf4efb07e7ebf814bcce2057
|
version: d8387025d2b9d158cf4efb07e7ebf814bcce2057
|
||||||
subpackages:
|
subpackages:
|
||||||
- edwards25519
|
- edwards25519
|
||||||
- extra25519
|
- extra25519
|
||||||
- name: github.com/tendermint/go-wire
|
- name: github.com/tendermint/go-wire
|
||||||
version: 8ee84b5b2581530168daf66fc89c548d27403c57
|
version: 1c96861c03231361546944d883d99593b2e6b408
|
||||||
subpackages:
|
subpackages:
|
||||||
- data
|
- data
|
||||||
- data/base58
|
- data/base58
|
||||||
- name: github.com/tendermint/tmlibs
|
- name: github.com/tendermint/tmlibs
|
||||||
version: 092eb701c7276907cdbed258750e22ce895b6735
|
version: 56e51bc1133bd54a331e0314d5f80fb547460362
|
||||||
subpackages:
|
subpackages:
|
||||||
- common
|
- common
|
||||||
|
- db
|
||||||
- log
|
- log
|
||||||
- name: golang.org/x/crypto
|
- name: golang.org/x/crypto
|
||||||
version: edd5e9b0879d13ee6970a50153d85b8fec9f7686
|
version: edd5e9b0879d13ee6970a50153d85b8fec9f7686
|
||||||
|
@ -6,11 +6,18 @@ import:
|
|||||||
- package: github.com/btcsuite/btcutil
|
- package: github.com/btcsuite/btcutil
|
||||||
subpackages:
|
subpackages:
|
||||||
- base58
|
- base58
|
||||||
|
- package: github.com/syndtr/goleveldb
|
||||||
|
subpackages:
|
||||||
|
- leveldb
|
||||||
|
- leveldb/errors
|
||||||
|
- leveldb/iterator
|
||||||
|
- leveldb/opt
|
||||||
|
- leveldb/util
|
||||||
- package: github.com/tendermint/ed25519
|
- package: github.com/tendermint/ed25519
|
||||||
subpackages:
|
subpackages:
|
||||||
- extra25519
|
- extra25519
|
||||||
- package: github.com/tendermint/tmlibs
|
- package: github.com/tendermint/tmlibs
|
||||||
version: develop
|
version: sdk-develop
|
||||||
- package: github.com/tendermint/go-wire
|
- package: github.com/tendermint/go-wire
|
||||||
version: develop
|
version: develop
|
||||||
subpackages:
|
subpackages:
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
package cryptostore maintains everything needed for doing public-key signing and
|
|
||||||
key management in software, based on the go-crypto library from tendermint.
|
|
||||||
|
|
||||||
It is flexible, and allows the user to provide a key generation algorithm
|
|
||||||
(currently Ed25519 or Secp256k1), an encoder to passphrase-encrypt our keys
|
|
||||||
when storing them (currently SecretBox from NaCl), and a method to persist
|
|
||||||
the keys (currently FileStorage like ssh, or MemStorage for tests).
|
|
||||||
It should be relatively simple to write your own implementation of these
|
|
||||||
interfaces to match your specific security requirements.
|
|
||||||
|
|
||||||
Note that the private keys are never exposed outside the package, and the
|
|
||||||
interface of Manager could be implemented by an HSM in the future for
|
|
||||||
enhanced security. It would require a completely different implementation
|
|
||||||
however.
|
|
||||||
|
|
||||||
This Manager aims to implement Signer and KeyManager interfaces, along
|
|
||||||
with some extensions to allow importing/exporting keys and updating the
|
|
||||||
passphrase.
|
|
||||||
|
|
||||||
Encoder and Generator implementations are currently in this package,
|
|
||||||
keys.Storage implementations exist as subpackages of
|
|
||||||
keys/storage
|
|
||||||
*/
|
|
||||||
package cryptostore
|
|
@ -1,49 +0,0 @@
|
|||||||
package cryptostore
|
|
||||||
|
|
||||||
import (
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
keys "github.com/tendermint/go-crypto/keys"
|
|
||||||
)
|
|
||||||
|
|
||||||
// encryptedStorage needs passphrase to get private keys
|
|
||||||
type encryptedStorage struct {
|
|
||||||
coder Encoder
|
|
||||||
store keys.Storage
|
|
||||||
}
|
|
||||||
|
|
||||||
func (es encryptedStorage) Put(name, pass string, key crypto.PrivKey) error {
|
|
||||||
secret, err := es.coder.Encrypt(key, pass)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
ki := info(name, key)
|
|
||||||
return es.store.Put(name, secret, ki)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (es encryptedStorage) Get(name, pass string) (crypto.PrivKey, keys.Info, error) {
|
|
||||||
secret, info, err := es.store.Get(name)
|
|
||||||
if err != nil {
|
|
||||||
return crypto.PrivKey{}, info, err
|
|
||||||
}
|
|
||||||
key, err := es.coder.Decrypt(secret, pass)
|
|
||||||
return key, info, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (es encryptedStorage) List() (keys.Infos, error) {
|
|
||||||
return es.store.List()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (es encryptedStorage) Delete(name string) error {
|
|
||||||
return es.store.Delete(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// info hardcodes the encoding of keys
|
|
||||||
func info(name string, key crypto.PrivKey) keys.Info {
|
|
||||||
pub := key.PubKey()
|
|
||||||
return keys.Info{
|
|
||||||
Name: name,
|
|
||||||
Address: pub.Address(),
|
|
||||||
PubKey: pub,
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package cryptostore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// SecretBox uses the algorithm from NaCL to store secrets securely
|
|
||||||
SecretBox Encoder = secretbox{}
|
|
||||||
// Noop doesn't do any encryption, should only be used in test code
|
|
||||||
Noop Encoder = noop{}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Encoder is used to encrypt any key with a passphrase for storage.
|
|
||||||
//
|
|
||||||
// This should use a well-designed symetric encryption algorithm
|
|
||||||
type Encoder interface {
|
|
||||||
Encrypt(key crypto.PrivKey, pass string) ([]byte, error)
|
|
||||||
Decrypt(data []byte, pass string) (crypto.PrivKey, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func secret(passphrase string) []byte {
|
|
||||||
// TODO: Sha256(Bcrypt(passphrase))
|
|
||||||
return crypto.Sha256([]byte(passphrase))
|
|
||||||
}
|
|
||||||
|
|
||||||
type secretbox struct{}
|
|
||||||
|
|
||||||
func (e secretbox) Encrypt(key crypto.PrivKey, pass string) ([]byte, error) {
|
|
||||||
if pass == "" {
|
|
||||||
return key.Bytes(), nil
|
|
||||||
}
|
|
||||||
s := secret(pass)
|
|
||||||
cipher := crypto.EncryptSymmetric(key.Bytes(), s)
|
|
||||||
return cipher, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e secretbox) Decrypt(data []byte, pass string) (key crypto.PrivKey, err error) {
|
|
||||||
private := data
|
|
||||||
if pass != "" {
|
|
||||||
s := secret(pass)
|
|
||||||
private, err = crypto.DecryptSymmetric(data, s)
|
|
||||||
if err != nil {
|
|
||||||
return crypto.PrivKey{}, errors.Wrap(err, "Invalid Passphrase")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
key, err = crypto.PrivKeyFromBytes(private)
|
|
||||||
return key, errors.Wrap(err, "Invalid Passphrase")
|
|
||||||
}
|
|
||||||
|
|
||||||
type noop struct{}
|
|
||||||
|
|
||||||
func (n noop) Encrypt(key crypto.PrivKey, pass string) ([]byte, error) {
|
|
||||||
return key.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n noop) Decrypt(data []byte, pass string) (crypto.PrivKey, error) {
|
|
||||||
return crypto.PrivKeyFromBytes(data)
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
package cryptostore_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
|
||||||
|
|
||||||
"github.com/tendermint/go-crypto/keys/cryptostore"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNoopEncoder(t *testing.T) {
|
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
noop := cryptostore.Noop
|
|
||||||
|
|
||||||
key, err := cryptostore.GenEd25519.Generate(cmn.RandBytes(16))
|
|
||||||
require.NoError(err)
|
|
||||||
key2, err := cryptostore.GenSecp256k1.Generate(cmn.RandBytes(16))
|
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
b, err := noop.Encrypt(key, "encode")
|
|
||||||
require.Nil(err)
|
|
||||||
assert.NotEmpty(b)
|
|
||||||
|
|
||||||
b2, err := noop.Encrypt(key2, "encode")
|
|
||||||
require.Nil(err)
|
|
||||||
assert.NotEmpty(b2)
|
|
||||||
assert.NotEqual(b, b2)
|
|
||||||
|
|
||||||
// note the decode with a different password works - not secure!
|
|
||||||
pk, err := noop.Decrypt(b, "decode")
|
|
||||||
require.Nil(err)
|
|
||||||
require.NotNil(pk)
|
|
||||||
assert.Equal(key, pk)
|
|
||||||
|
|
||||||
pk2, err := noop.Decrypt(b2, "kggugougp")
|
|
||||||
require.Nil(err)
|
|
||||||
require.NotNil(pk2)
|
|
||||||
assert.Equal(key2, pk2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSecretBox(t *testing.T) {
|
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
enc := cryptostore.SecretBox
|
|
||||||
|
|
||||||
key, err := cryptostore.GenEd25519.Generate(cmn.RandBytes(16))
|
|
||||||
require.NoError(err)
|
|
||||||
pass := "some-special-secret"
|
|
||||||
|
|
||||||
b, err := enc.Encrypt(key, pass)
|
|
||||||
require.Nil(err)
|
|
||||||
assert.NotEmpty(b)
|
|
||||||
|
|
||||||
// decoding with a different pass is an error
|
|
||||||
pk, err := enc.Decrypt(b, "decode")
|
|
||||||
require.NotNil(err)
|
|
||||||
require.True(pk.Empty())
|
|
||||||
|
|
||||||
// but decoding with the same passphrase gets us our key
|
|
||||||
pk, err = enc.Decrypt(b, pass)
|
|
||||||
require.Nil(err)
|
|
||||||
assert.Equal(key, pk)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSecretBoxNoPass(t *testing.T) {
|
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
enc := cryptostore.SecretBox
|
|
||||||
|
|
||||||
key, rerr := cryptostore.GenEd25519.Generate(cmn.RandBytes(16))
|
|
||||||
require.NoError(rerr)
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
encode string
|
|
||||||
decode string
|
|
||||||
valid bool
|
|
||||||
}{
|
|
||||||
{"foo", "foo", true},
|
|
||||||
{"foo", "food", false},
|
|
||||||
{"", "", true},
|
|
||||||
{"", "a", false},
|
|
||||||
{"a", "", false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range cases {
|
|
||||||
b, err := enc.Encrypt(key, tc.encode)
|
|
||||||
require.Nil(err, "%d: %+v", i, err)
|
|
||||||
assert.NotEmpty(b, "%d", i)
|
|
||||||
|
|
||||||
pk, err := enc.Decrypt(b, tc.decode)
|
|
||||||
if tc.valid {
|
|
||||||
require.Nil(err, "%d: %+v", i, err)
|
|
||||||
assert.Equal(key, pk, "%d", i)
|
|
||||||
} else {
|
|
||||||
require.NotNil(err, "%d", i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// now let's make sure raw bytes also work...
|
|
||||||
b := key.Bytes()
|
|
||||||
pk, err := enc.Decrypt(b, "")
|
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
assert.Equal(key, pk)
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
package cryptostore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
"github.com/tendermint/go-crypto/nano"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// GenEd25519 produces Ed25519 private keys
|
|
||||||
GenEd25519 Generator = GenFunc(genEd25519)
|
|
||||||
// GenSecp256k1 produces Secp256k1 private keys
|
|
||||||
GenSecp256k1 Generator = GenFunc(genSecp256)
|
|
||||||
// GenLedgerEd25519 used Ed25519 keys stored on nano ledger s with cosmos app
|
|
||||||
GenLedgerEd25519 Generator = GenFunc(genLedgerEd25519)
|
|
||||||
)
|
|
||||||
|
|
||||||
// Generator determines the type of private key the keystore creates
|
|
||||||
type Generator interface {
|
|
||||||
Generate(secret []byte) (crypto.PrivKey, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GenFunc is a helper to transform a function into a Generator
|
|
||||||
type GenFunc func(secret []byte) (crypto.PrivKey, error)
|
|
||||||
|
|
||||||
func (f GenFunc) Generate(secret []byte) (crypto.PrivKey, error) {
|
|
||||||
return f(secret)
|
|
||||||
}
|
|
||||||
|
|
||||||
func genEd25519(secret []byte) (crypto.PrivKey, error) {
|
|
||||||
key := crypto.GenPrivKeyEd25519FromSecret(secret).Wrap()
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func genSecp256(secret []byte) (crypto.PrivKey, error) {
|
|
||||||
key := crypto.GenPrivKeySecp256k1FromSecret(secret).Wrap()
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// secret is completely ignored for the ledger...
|
|
||||||
// just for interface compatibility
|
|
||||||
func genLedgerEd25519(secret []byte) (crypto.PrivKey, error) {
|
|
||||||
return nano.NewPrivKeyLedgerEd25519Ed25519()
|
|
||||||
}
|
|
||||||
|
|
||||||
type genInvalidByte struct {
|
|
||||||
typ byte
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g genInvalidByte) Generate(secret []byte) (crypto.PrivKey, error) {
|
|
||||||
err := errors.Errorf("Cannot generate keys for algorithm: %X", g.typ)
|
|
||||||
return crypto.PrivKey{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type genInvalidAlgo struct {
|
|
||||||
algo string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g genInvalidAlgo) Generate(secret []byte) (crypto.PrivKey, error) {
|
|
||||||
err := errors.Errorf("Cannot generate keys for algorithm: %s", g.algo)
|
|
||||||
return crypto.PrivKey{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func getGenerator(algo string) Generator {
|
|
||||||
switch algo {
|
|
||||||
case crypto.NameEd25519:
|
|
||||||
return GenEd25519
|
|
||||||
case crypto.NameSecp256k1:
|
|
||||||
return GenSecp256k1
|
|
||||||
case nano.NameLedgerEd25519:
|
|
||||||
return GenLedgerEd25519
|
|
||||||
default:
|
|
||||||
return genInvalidAlgo{algo}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getGeneratorByType(typ byte) Generator {
|
|
||||||
switch typ {
|
|
||||||
case crypto.TypeEd25519:
|
|
||||||
return GenEd25519
|
|
||||||
case crypto.TypeSecp256k1:
|
|
||||||
return GenSecp256k1
|
|
||||||
case nano.TypeLedgerEd25519:
|
|
||||||
return GenLedgerEd25519
|
|
||||||
default:
|
|
||||||
return genInvalidByte{typ}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,169 +0,0 @@
|
|||||||
package cryptostore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
keys "github.com/tendermint/go-crypto/keys"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Manager combines encyption and storage implementation to provide
|
|
||||||
// a full-featured key manager
|
|
||||||
type Manager struct {
|
|
||||||
es encryptedStorage
|
|
||||||
codec keys.Codec
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(coder Encoder, store keys.Storage, codec keys.Codec) Manager {
|
|
||||||
return Manager{
|
|
||||||
es: encryptedStorage{
|
|
||||||
coder: coder,
|
|
||||||
store: store,
|
|
||||||
},
|
|
||||||
codec: codec,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert Manager satisfies keys.Signer and keys.Manager interfaces
|
|
||||||
var _ keys.Signer = Manager{}
|
|
||||||
var _ keys.Manager = Manager{}
|
|
||||||
|
|
||||||
// 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 (s Manager) Create(name, passphrase, algo string) (keys.Info, string, error) {
|
|
||||||
// 128-bits are the all the randomness we can make use of
|
|
||||||
secret := crypto.CRandBytes(16)
|
|
||||||
gen := getGenerator(algo)
|
|
||||||
|
|
||||||
key, err := gen.Generate(secret)
|
|
||||||
if err != nil {
|
|
||||||
return keys.Info{}, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s.es.Put(name, passphrase, key)
|
|
||||||
if err != nil {
|
|
||||||
return keys.Info{}, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 := s.codec.BytesToWords(secret)
|
|
||||||
phrase := strings.Join(seed, " ")
|
|
||||||
return info(name, key), phrase, 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 (s Manager) Recover(name, passphrase, seedphrase string) (keys.Info, error) {
|
|
||||||
words := strings.Split(strings.TrimSpace(seedphrase), " ")
|
|
||||||
secret, err := s.codec.WordsToBytes(words)
|
|
||||||
if err != nil {
|
|
||||||
return keys.Info{}, 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]
|
|
||||||
|
|
||||||
gen := getGeneratorByType(typ)
|
|
||||||
key, err := gen.Generate(secret)
|
|
||||||
if err != nil {
|
|
||||||
return keys.Info{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// d00d, it worked! create the bugger....
|
|
||||||
err = s.es.Put(name, passphrase, key)
|
|
||||||
return info(name, key), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// List loads the keys from the storage and enforces alphabetical order
|
|
||||||
func (s Manager) List() (keys.Infos, error) {
|
|
||||||
res, err := s.es.List()
|
|
||||||
res.Sort()
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the public information about one key
|
|
||||||
func (s Manager) Get(name string) (keys.Info, error) {
|
|
||||||
_, info, err := s.es.store.Get(name)
|
|
||||||
return info, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
func (s Manager) Sign(name, passphrase string, tx keys.Signable) error {
|
|
||||||
key, _, err := s.es.Get(name, passphrase)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
sig := key.Sign(tx.SignBytes())
|
|
||||||
pubkey := key.PubKey()
|
|
||||||
return tx.Sign(pubkey, sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 Manager
|
|
||||||
//
|
|
||||||
// This is designed to copy from one device to another, or provide backups
|
|
||||||
// during version updates.
|
|
||||||
func (s Manager) Export(name, oldpass, transferpass string) ([]byte, error) {
|
|
||||||
key, _, err := s.es.Get(name, oldpass)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := s.es.coder.Encrypt(key, transferpass)
|
|
||||||
return res, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 (s Manager) Import(name, newpass, transferpass string, data []byte) error {
|
|
||||||
key, err := s.es.coder.Decrypt(data, transferpass)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.es.Put(name, newpass, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete removes key forever, but we must present the
|
|
||||||
// proper passphrase before deleting it (for security)
|
|
||||||
func (s Manager) Delete(name, passphrase string) error {
|
|
||||||
// verify we have the proper password before deleting
|
|
||||||
_, _, err := s.es.Get(name, passphrase)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return s.es.Delete(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update changes the passphrase with which a already stored key is encoded.
|
|
||||||
//
|
|
||||||
// oldpass must be the current passphrase used for encoding, newpass will be
|
|
||||||
// the only valid passphrase from this time forward
|
|
||||||
func (s Manager) Update(name, oldpass, newpass string) error {
|
|
||||||
key, _, err := s.es.Get(name, oldpass)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// we must delete first, as Putting over an existing name returns an error
|
|
||||||
s.Delete(name, oldpass)
|
|
||||||
|
|
||||||
return s.es.Put(name, newpass, key)
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
package cryptostore
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
|
||||||
|
|
||||||
keys "github.com/tendermint/go-crypto/keys"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSortKeys(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
gen := func() crypto.PrivKey {
|
|
||||||
key, _ := GenEd25519.Generate(cmn.RandBytes(16))
|
|
||||||
return key
|
|
||||||
}
|
|
||||||
assert.NotEqual(gen(), gen())
|
|
||||||
|
|
||||||
// alphabetical order is n3, n1, n2
|
|
||||||
n1, n2, n3 := "john", "mike", "alice"
|
|
||||||
infos := keys.Infos{
|
|
||||||
info(n1, gen()),
|
|
||||||
info(n2, gen()),
|
|
||||||
info(n3, gen()),
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure they are initialized unsorted
|
|
||||||
assert.Equal(n1, infos[0].Name)
|
|
||||||
assert.Equal(n2, infos[1].Name)
|
|
||||||
assert.Equal(n3, infos[2].Name)
|
|
||||||
|
|
||||||
// now they are sorted
|
|
||||||
infos.Sort()
|
|
||||||
assert.Equal(n3, infos[0].Name)
|
|
||||||
assert.Equal(n1, infos[1].Name)
|
|
||||||
assert.Equal(n2, infos[2].Name)
|
|
||||||
|
|
||||||
// make sure info put some real data there...
|
|
||||||
assert.NotEmpty(infos[0].PubKey)
|
|
||||||
assert.NotEmpty(infos[0].PubKey.Address())
|
|
||||||
assert.NotEmpty(infos[1].PubKey)
|
|
||||||
assert.NotEmpty(infos[1].PubKey.Address())
|
|
||||||
assert.NotEqual(infos[0].PubKey, infos[1].PubKey)
|
|
||||||
}
|
|
@ -3,7 +3,7 @@ package keys
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
asrt "github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
@ -21,7 +21,7 @@ var codecs = []ECC{
|
|||||||
|
|
||||||
// TestECCPasses makes sure that the AddECC/CheckECC methods are symetric
|
// TestECCPasses makes sure that the AddECC/CheckECC methods are symetric
|
||||||
func TestECCPasses(t *testing.T) {
|
func TestECCPasses(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := asrt.New(t)
|
||||||
|
|
||||||
checks := append(codecs, NoECC{})
|
checks := append(codecs, NoECC{})
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ func TestECCPasses(t *testing.T) {
|
|||||||
|
|
||||||
// TestECCFails makes sure random data will (usually) fail the checksum
|
// TestECCFails makes sure random data will (usually) fail the checksum
|
||||||
func TestECCFails(t *testing.T) {
|
func TestECCFails(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := asrt.New(t)
|
||||||
|
|
||||||
checks := codecs
|
checks := codecs
|
||||||
attempts := 2000
|
attempts := 2000
|
||||||
|
246
keys/keybase.go
Normal file
246
keys/keybase.go
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
package keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
crypto "github.com/tendermint/go-crypto"
|
||||||
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
|
|
||||||
|
"github.com/tendermint/go-crypto/nano"
|
||||||
|
)
|
||||||
|
|
||||||
|
// XXX Lets use go-crypto/bcrypt and ascii encoding directly in here without
|
||||||
|
// further wrappers around a store or DB.
|
||||||
|
// Copy functions from: https://github.com/tendermint/mintkey/blob/master/cmd/mintkey/common.go
|
||||||
|
//
|
||||||
|
// dbKeybase combines encyption and storage implementation to provide
|
||||||
|
// a full-featured key manager
|
||||||
|
type dbKeybase struct {
|
||||||
|
db dbm.DB
|
||||||
|
codec Codec
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(db dbm.DB, codec Codec) dbKeybase {
|
||||||
|
return dbKeybase{
|
||||||
|
db: db,
|
||||||
|
codec: codec,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
secret := crypto.CRandBytes(16)
|
||||||
|
key, err := generate(algo, secret)
|
||||||
|
if err != nil {
|
||||||
|
return Info{}, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
words := strings.Split(strings.TrimSpace(seedphrase), " ")
|
||||||
|
secret, err := kb.codec.WordsToBytes(words)
|
||||||
|
if err != nil {
|
||||||
|
return Info{}, 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)
|
||||||
|
if err != nil {
|
||||||
|
return Info{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// d00d, it worked! create the bugger....
|
||||||
|
public := kb.writeKey(key, name, passphrase)
|
||||||
|
return public, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List loads the keys from the storage and enforces alphabetical order
|
||||||
|
func (kb dbKeybase) List() ([]Info, error) {
|
||||||
|
var res []Info
|
||||||
|
for iter := kb.db.Iterator(); iter.Valid(); iter.Next() {
|
||||||
|
key := iter.Key()
|
||||||
|
if isPub(key) {
|
||||||
|
info, err := readInfo(iter.Value())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
res = append(res, info)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
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)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sig = key.Sign(msg)
|
||||||
|
pk = key.PubKey()
|
||||||
|
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
|
||||||
|
//
|
||||||
|
// 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)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if transferpass == "" {
|
||||||
|
return key.Bytes(), nil
|
||||||
|
}
|
||||||
|
res := encryptArmorPrivKey(key, transferpass)
|
||||||
|
return []byte(res), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
var key crypto.PrivKey
|
||||||
|
if transferpass == "" {
|
||||||
|
key, err = crypto.PrivKeyFromBytes(data)
|
||||||
|
} else {
|
||||||
|
key, err = unarmorDecryptPrivKey(string(data), transferpass)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
kb.writeKey(key, name, newpass)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes key forever, but we must present the
|
||||||
|
// 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))
|
||||||
|
_, err := unarmorDecryptPrivKey(string(bs), passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
kb.db.DeleteSync(pubName(name))
|
||||||
|
kb.db.DeleteSync(privName(name))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update changes the passphrase with which a already stored key is encoded.
|
||||||
|
//
|
||||||
|
// oldpass must be the current passphrase used for encoding, 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)
|
||||||
|
if err != nil {
|
||||||
|
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)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kb dbKeybase) writeKey(priv crypto.PrivKey, name, passphrase string) Info {
|
||||||
|
// generate the public bytes
|
||||||
|
public := info(name, priv)
|
||||||
|
// generate the encrypted privkey
|
||||||
|
private := encryptArmorPrivKey(priv, passphrase)
|
||||||
|
|
||||||
|
// write them both
|
||||||
|
kb.db.SetSync(pubName(name), public.bytes())
|
||||||
|
kb.db.SetSync(privName(name), []byte(private))
|
||||||
|
|
||||||
|
return public
|
||||||
|
}
|
||||||
|
|
||||||
|
func generate(algo string, secret []byte) (crypto.PrivKey, error) {
|
||||||
|
switch algo {
|
||||||
|
case crypto.NameEd25519:
|
||||||
|
return crypto.GenPrivKeyEd25519FromSecret(secret).Wrap(), nil
|
||||||
|
case crypto.NameSecp256k1:
|
||||||
|
return crypto.GenPrivKeySecp256k1FromSecret(secret).Wrap(), nil
|
||||||
|
case nano.NameLedgerEd25519:
|
||||||
|
return nano.NewPrivKeyLedgerEd25519Ed25519()
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
|
||||||
|
func privName(name string) []byte {
|
||||||
|
return []byte(fmt.Sprintf("%s.priv", name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func isPub(name []byte) bool {
|
||||||
|
return strings.HasSuffix(string(name), ".pub")
|
||||||
|
}
|
@ -1,31 +1,28 @@
|
|||||||
package cryptostore_test
|
package keys_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
asrt "github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
rqr "github.com/stretchr/testify/require"
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
"github.com/tendermint/go-crypto/keys"
|
"github.com/tendermint/go-crypto/keys"
|
||||||
"github.com/tendermint/go-crypto/keys/cryptostore"
|
|
||||||
"github.com/tendermint/go-crypto/keys/storage/memstorage"
|
|
||||||
"github.com/tendermint/go-crypto/nano"
|
"github.com/tendermint/go-crypto/nano"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestKeyManagement makes sure we can manipulate these keys well
|
// TestKeyManagement makes sure we can manipulate these keys well
|
||||||
func TestKeyManagement(t *testing.T) {
|
func TestKeyManagement(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := asrt.New(t), rqr.New(t)
|
||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := cryptostore.New(
|
cstore := keys.New(
|
||||||
cryptostore.SecretBox,
|
dbm.NewMemDB(),
|
||||||
memstorage.New(),
|
|
||||||
keys.MustLoadCodec("english"),
|
keys.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -89,12 +86,11 @@ func TestKeyManagement(t *testing.T) {
|
|||||||
// TestSignVerify does some detailed checks on how we sign and validate
|
// TestSignVerify does some detailed checks on how we sign and validate
|
||||||
// signatures
|
// signatures
|
||||||
func TestSignVerify(t *testing.T) {
|
func TestSignVerify(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := asrt.New(t), rqr.New(t)
|
||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := cryptostore.New(
|
cstore := keys.New(
|
||||||
cryptostore.SecretBox,
|
dbm.NewMemDB(),
|
||||||
memstorage.New(),
|
|
||||||
keys.MustLoadCodec("english"),
|
keys.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
algo := crypto.NameSecp256k1
|
algo := crypto.NameSecp256k1
|
||||||
@ -114,18 +110,21 @@ func TestSignVerify(t *testing.T) {
|
|||||||
d2 := []byte("some other important info!")
|
d2 := []byte("some other important info!")
|
||||||
|
|
||||||
// try signing both data with both keys...
|
// try signing both data with both keys...
|
||||||
s11 := keys.NewMockSignable(d1)
|
s11, pub1, err := cstore.Sign(n1, p1, d1)
|
||||||
err = cstore.Sign(n1, p1, s11)
|
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
s12 := keys.NewMockSignable(d2)
|
require.Equal(i1.PubKey, pub1)
|
||||||
err = cstore.Sign(n1, p1, s12)
|
|
||||||
|
s12, pub1, err := cstore.Sign(n1, p1, d2)
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
s21 := keys.NewMockSignable(d1)
|
require.Equal(i1.PubKey, pub1)
|
||||||
err = cstore.Sign(n2, p2, s21)
|
|
||||||
|
s21, pub2, err := cstore.Sign(n2, p2, d1)
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
s22 := keys.NewMockSignable(d2)
|
require.Equal(i2.PubKey, pub2)
|
||||||
err = cstore.Sign(n2, p2, s22)
|
|
||||||
|
s22, pub2, err := cstore.Sign(n2, p2, d2)
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
|
require.Equal(i2.PubKey, 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 {
|
||||||
@ -135,15 +134,15 @@ func TestSignVerify(t *testing.T) {
|
|||||||
valid bool
|
valid bool
|
||||||
}{
|
}{
|
||||||
// proper matches
|
// proper matches
|
||||||
{i1.PubKey, d1, s11.Signature, true},
|
{i1.PubKey, d1, s11, true},
|
||||||
// change data, pubkey, or signature leads to fail
|
// change data, pubkey, or signature leads to fail
|
||||||
{i1.PubKey, d2, s11.Signature, false},
|
{i1.PubKey, d2, s11, false},
|
||||||
{i2.PubKey, d1, s11.Signature, false},
|
{i2.PubKey, d1, s11, false},
|
||||||
{i1.PubKey, d1, s21.Signature, false},
|
{i1.PubKey, d1, s21, false},
|
||||||
// make sure other successes
|
// make sure other successes
|
||||||
{i1.PubKey, d2, s12.Signature, true},
|
{i1.PubKey, d2, s12, true},
|
||||||
{i2.PubKey, d1, s21.Signature, true},
|
{i2.PubKey, d1, s21, true},
|
||||||
{i2.PubKey, d2, s22.Signature, true},
|
{i2.PubKey, d2, s22, true},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
@ -158,15 +157,14 @@ func TestSignVerify(t *testing.T) {
|
|||||||
// This test will only succeed with a ledger attached to the computer
|
// This test will only succeed with a ledger attached to the computer
|
||||||
// and the cosmos app open
|
// and the cosmos app open
|
||||||
func TestSignWithLedger(t *testing.T) {
|
func TestSignWithLedger(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := asrt.New(t), rqr.New(t)
|
||||||
if os.Getenv("WITH_LEDGER") == "" {
|
if os.Getenv("WITH_LEDGER") == "" {
|
||||||
t.Skip("Set WITH_LEDGER to run code on real ledger")
|
t.Skip("Set WITH_LEDGER to run code on real ledger")
|
||||||
}
|
}
|
||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := cryptostore.New(
|
cstore := keys.New(
|
||||||
cryptostore.SecretBox,
|
dbm.NewMemDB(),
|
||||||
memstorage.New(),
|
|
||||||
keys.MustLoadCodec("english"),
|
keys.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
n := "nano-s"
|
n := "nano-s"
|
||||||
@ -192,21 +190,22 @@ func TestSignWithLedger(t *testing.T) {
|
|||||||
d2 := []byte("please turn on the app")
|
d2 := []byte("please turn on the app")
|
||||||
|
|
||||||
// try signing both data with the ledger...
|
// try signing both data with the ledger...
|
||||||
s1 := keys.NewMockSignable(d1)
|
s1, pub, err := cstore.Sign(n, p, d1)
|
||||||
err = cstore.Sign(n, p, s1)
|
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
s2 := keys.NewMockSignable(d2)
|
require.Equal(info.PubKey, pub)
|
||||||
err = cstore.Sign(n, p, s2)
|
|
||||||
|
s2, pub, err := cstore.Sign(n, p, d2)
|
||||||
require.Nil(err)
|
require.Nil(err)
|
||||||
|
require.Equal(info.PubKey, pub)
|
||||||
|
|
||||||
// now, let's check those signatures work
|
// now, let's check those signatures work
|
||||||
assert.True(key.VerifyBytes(d1, s1.Signature))
|
assert.True(key.VerifyBytes(d1, s1))
|
||||||
assert.True(key.VerifyBytes(d2, s2.Signature))
|
assert.True(key.VerifyBytes(d2, s2))
|
||||||
// and mismatched signatures don't
|
// and mismatched signatures don't
|
||||||
assert.False(key.VerifyBytes(d1, s2.Signature))
|
assert.False(key.VerifyBytes(d1, s2))
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertPassword(assert *assert.Assertions, cstore cryptostore.Manager, name, pass, badpass string) {
|
func assertPassword(assert *asrt.Assertions, cstore keys.Keybase, name, pass, badpass string) {
|
||||||
err := cstore.Update(name, badpass, pass)
|
err := cstore.Update(name, badpass, pass)
|
||||||
assert.NotNil(err)
|
assert.NotNil(err)
|
||||||
err = cstore.Update(name, pass, pass)
|
err = cstore.Update(name, pass, pass)
|
||||||
@ -215,40 +214,37 @@ func assertPassword(assert *assert.Assertions, cstore cryptostore.Manager, name,
|
|||||||
|
|
||||||
// TestImportUnencrypted tests accepting raw priv keys bytes as input
|
// TestImportUnencrypted tests accepting raw priv keys bytes as input
|
||||||
func TestImportUnencrypted(t *testing.T) {
|
func TestImportUnencrypted(t *testing.T) {
|
||||||
require := require.New(t)
|
require := rqr.New(t)
|
||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := cryptostore.New(
|
cstore := keys.New(
|
||||||
cryptostore.SecretBox,
|
dbm.NewMemDB(),
|
||||||
memstorage.New(),
|
|
||||||
keys.MustLoadCodec("english"),
|
keys.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
|
|
||||||
key, err := cryptostore.GenEd25519.Generate(cmn.RandBytes(16))
|
key := crypto.GenPrivKeyEd25519FromSecret(cmn.RandBytes(16)).Wrap()
|
||||||
require.NoError(err)
|
|
||||||
|
|
||||||
addr := key.PubKey().Address()
|
addr := key.PubKey().Address()
|
||||||
name := "john"
|
name := "john"
|
||||||
pass := "top-secret"
|
pass := "top-secret"
|
||||||
|
|
||||||
// import raw bytes
|
// import raw bytes
|
||||||
err = cstore.Import(name, pass, "", key.Bytes())
|
err := cstore.Import(name, pass, "", key.Bytes())
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
|
|
||||||
// make sure the address matches
|
// make sure the address matches
|
||||||
info, err := cstore.Get(name)
|
info, err := cstore.Get(name)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
require.EqualValues(addr, info.Address)
|
require.EqualValues(addr, info.Address())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAdvancedKeyManagement verifies update, import, export functionality
|
// TestAdvancedKeyManagement verifies update, import, export functionality
|
||||||
func TestAdvancedKeyManagement(t *testing.T) {
|
func TestAdvancedKeyManagement(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := asrt.New(t), rqr.New(t)
|
||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := cryptostore.New(
|
cstore := keys.New(
|
||||||
cryptostore.SecretBox,
|
dbm.NewMemDB(),
|
||||||
memstorage.New(),
|
|
||||||
keys.MustLoadCodec("english"),
|
keys.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -287,12 +283,11 @@ func TestAdvancedKeyManagement(t *testing.T) {
|
|||||||
|
|
||||||
// TestSeedPhrase verifies restoring from a seed phrase
|
// TestSeedPhrase verifies restoring from a seed phrase
|
||||||
func TestSeedPhrase(t *testing.T) {
|
func TestSeedPhrase(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := asrt.New(t), rqr.New(t)
|
||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := cryptostore.New(
|
cstore := keys.New(
|
||||||
cryptostore.SecretBox,
|
dbm.NewMemDB(),
|
||||||
memstorage.New(),
|
|
||||||
keys.MustLoadCodec("english"),
|
keys.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -316,16 +311,14 @@ func TestSeedPhrase(t *testing.T) {
|
|||||||
newInfo, err := cstore.Recover(n2, p2, seed)
|
newInfo, err := cstore.Recover(n2, p2, seed)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
assert.Equal(n2, newInfo.Name)
|
assert.Equal(n2, newInfo.Name)
|
||||||
assert.Equal(info.Address, newInfo.Address)
|
assert.Equal(info.Address(), newInfo.Address())
|
||||||
assert.Equal(info.PubKey, newInfo.PubKey)
|
assert.Equal(info.PubKey, newInfo.PubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleNew() {
|
func ExampleNew() {
|
||||||
// Select the encryption and storage for your cryptostore
|
// Select the encryption and storage for your cryptostore
|
||||||
cstore := cryptostore.New(
|
cstore := keys.New(
|
||||||
cryptostore.SecretBox,
|
dbm.NewMemDB(),
|
||||||
// Note: use filestorage.New(dir) for real data
|
|
||||||
memstorage.New(),
|
|
||||||
keys.MustLoadCodec("english"),
|
keys.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
ed := crypto.NameEd25519
|
ed := crypto.NameEd25519
|
||||||
@ -348,8 +341,8 @@ func ExampleNew() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We need to use passphrase to generate a signature
|
// We need to use passphrase to generate a signature
|
||||||
tx := keys.NewMockSignable([]byte("deadbeef"))
|
tx := []byte("deadbeef")
|
||||||
err = cstore.Sign("Bob", "friend", tx)
|
sig, pub, err := cstore.Sign("Bob", "friend", tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("don't accept real passphrase")
|
fmt.Println("don't accept real passphrase")
|
||||||
}
|
}
|
||||||
@ -360,13 +353,11 @@ func ExampleNew() {
|
|||||||
fmt.Println("Get and Create return different keys")
|
fmt.Println("Get and Create return different keys")
|
||||||
}
|
}
|
||||||
|
|
||||||
sigs, err := tx.Signers()
|
if pub.Equals(binfo.PubKey) {
|
||||||
if err != nil {
|
|
||||||
fmt.Println("badly signed")
|
|
||||||
} else if bytes.Equal(sigs[0].Bytes(), binfo.PubKey.Bytes()) {
|
|
||||||
fmt.Println("signed by Bob")
|
fmt.Println("signed by Bob")
|
||||||
} else {
|
}
|
||||||
fmt.Println("signed by someone else")
|
if !pub.VerifyBytes(tx, sig) {
|
||||||
|
fmt.Println("invalid signature")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output:
|
// Output:
|
73
keys/mintkey.go
Normal file
73
keys/mintkey.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
|
|
||||||
|
"github.com/tendermint/go-crypto"
|
||||||
|
"github.com/tendermint/go-crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
blockTypePrivKey = "TENDERMINT PRIVATE KEY"
|
||||||
|
)
|
||||||
|
|
||||||
|
func encryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string {
|
||||||
|
saltBytes, encBytes := encryptPrivKey(privKey, passphrase)
|
||||||
|
header := map[string]string{
|
||||||
|
"kdf": "bcrypt",
|
||||||
|
"salt": fmt.Sprintf("%X", saltBytes),
|
||||||
|
}
|
||||||
|
armorStr := crypto.EncodeArmor(blockTypePrivKey, header, encBytes)
|
||||||
|
return armorStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func unarmorDecryptPrivKey(armorStr string, passphrase string) (crypto.PrivKey, error) {
|
||||||
|
var privKey crypto.PrivKey
|
||||||
|
blockType, header, encBytes, err := crypto.DecodeArmor(armorStr)
|
||||||
|
if err != nil {
|
||||||
|
return privKey, err
|
||||||
|
}
|
||||||
|
if blockType != blockTypePrivKey {
|
||||||
|
return privKey, fmt.Errorf("Unrecognized armor type: %v", blockType)
|
||||||
|
}
|
||||||
|
if header["kdf"] != "bcrypt" {
|
||||||
|
return privKey, fmt.Errorf("Unrecognized KDF type: %v", header["KDF"])
|
||||||
|
}
|
||||||
|
if header["salt"] == "" {
|
||||||
|
return privKey, fmt.Errorf("Missing salt bytes")
|
||||||
|
}
|
||||||
|
saltBytes, err := hex.DecodeString(header["salt"])
|
||||||
|
if err != nil {
|
||||||
|
return privKey, fmt.Errorf("Error decoding salt: %v", err.Error())
|
||||||
|
}
|
||||||
|
privKey, err = decryptPrivKey(saltBytes, encBytes, passphrase)
|
||||||
|
return privKey, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func encryptPrivKey(privKey crypto.PrivKey, passphrase string) (saltBytes []byte, encBytes []byte) {
|
||||||
|
saltBytes = crypto.CRandBytes(16)
|
||||||
|
key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), 12) // TODO parameterize. 12 is good today (2016)
|
||||||
|
if err != nil {
|
||||||
|
cmn.Exit("Error generating bcrypt key from passphrase: " + err.Error())
|
||||||
|
}
|
||||||
|
key = crypto.Sha256(key) // Get 32 bytes
|
||||||
|
privKeyBytes := privKey.Bytes()
|
||||||
|
return saltBytes, crypto.EncryptSymmetric(privKeyBytes, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decryptPrivKey(saltBytes []byte, encBytes []byte, passphrase string) (privKey crypto.PrivKey, err error) {
|
||||||
|
key, err := bcrypt.GenerateFromPassword(saltBytes, []byte(passphrase), 12) // TODO parameterize. 12 is good today (2016)
|
||||||
|
if err != nil {
|
||||||
|
cmn.Exit("Error generating bcrypt key from passphrase: " + err.Error())
|
||||||
|
}
|
||||||
|
key = crypto.Sha256(key) // Get 32 bytes
|
||||||
|
privKeyBytes, err := crypto.DecryptSymmetric(encBytes, key)
|
||||||
|
if err != nil {
|
||||||
|
return privKey, err
|
||||||
|
}
|
||||||
|
privKey, err = crypto.PrivKeyFromBytes(privKeyBytes)
|
||||||
|
return privKey, err
|
||||||
|
}
|
@ -1,177 +0,0 @@
|
|||||||
/*
|
|
||||||
package filestorage provides a secure on-disk storage of private keys and
|
|
||||||
metadata. Security is enforced by file and directory permissions, much
|
|
||||||
like standard ssh key storage.
|
|
||||||
*/
|
|
||||||
package filestorage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
keys "github.com/tendermint/go-crypto/keys"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
BlockType = "Tendermint Light Client"
|
|
||||||
|
|
||||||
// PrivExt is the extension for private keys.
|
|
||||||
PrivExt = "tlc"
|
|
||||||
// PubExt is the extensions for public keys.
|
|
||||||
PubExt = "pub"
|
|
||||||
|
|
||||||
keyPerm = os.FileMode(0600)
|
|
||||||
// pubPerm = os.FileMode(0644)
|
|
||||||
dirPerm = os.FileMode(0700)
|
|
||||||
)
|
|
||||||
|
|
||||||
type FileStore struct {
|
|
||||||
keyDir string
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates an instance of file-based key storage with tight permissions
|
|
||||||
//
|
|
||||||
// dir should be an absolute path of a directory owner by this user. It will
|
|
||||||
// be created if it doesn't exist already.
|
|
||||||
func New(dir string) FileStore {
|
|
||||||
err := os.MkdirAll(dir, dirPerm)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return FileStore{dir}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert FileStore satisfies keys.Storage
|
|
||||||
var _ keys.Storage = FileStore{}
|
|
||||||
|
|
||||||
// Put creates two files, one with the public info as json, the other
|
|
||||||
// with the (encoded) private key as gpg ascii-armor style
|
|
||||||
func (s FileStore) Put(name string, key []byte, info keys.Info) error {
|
|
||||||
pub, priv := s.nameToPaths(name)
|
|
||||||
|
|
||||||
// write public info
|
|
||||||
err := writeInfo(pub, info)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// write private info
|
|
||||||
return write(priv, name, key)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get loads the info and (encoded) private key from the directory
|
|
||||||
// It uses `name` to generate the filename, and returns an error if the
|
|
||||||
// files don't exist or are in the incorrect format
|
|
||||||
func (s FileStore) Get(name string) ([]byte, keys.Info, error) {
|
|
||||||
pub, priv := s.nameToPaths(name)
|
|
||||||
|
|
||||||
info, err := readInfo(pub)
|
|
||||||
if err != nil {
|
|
||||||
return nil, info, err
|
|
||||||
}
|
|
||||||
|
|
||||||
key, _, err := read(priv)
|
|
||||||
return key, info.Format(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// List parses the key directory for public info and returns a list of
|
|
||||||
// Info for all keys located in this directory.
|
|
||||||
func (s FileStore) List() (keys.Infos, error) {
|
|
||||||
dir, err := os.Open(s.keyDir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "List Keys")
|
|
||||||
}
|
|
||||||
defer dir.Close()
|
|
||||||
|
|
||||||
names, err := dir.Readdirnames(0)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrap(err, "List Keys")
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter names for .pub ending and load them one by one
|
|
||||||
// half the files is a good guess for pre-allocating the slice
|
|
||||||
infos := make([]keys.Info, 0, len(names)/2)
|
|
||||||
for _, name := range names {
|
|
||||||
if strings.HasSuffix(name, PubExt) {
|
|
||||||
p := path.Join(s.keyDir, name)
|
|
||||||
info, err := readInfo(p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
infos = append(infos, info.Format())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return infos, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete permanently removes the public and private info for the named key
|
|
||||||
// The calling function should provide some security checks first.
|
|
||||||
func (s FileStore) Delete(name string) error {
|
|
||||||
pub, priv := s.nameToPaths(name)
|
|
||||||
err := os.Remove(priv)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "Deleting Private Key")
|
|
||||||
}
|
|
||||||
err = os.Remove(pub)
|
|
||||||
return errors.Wrap(err, "Deleting Public Key")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s FileStore) nameToPaths(name string) (pub, priv string) {
|
|
||||||
privName := fmt.Sprintf("%s.%s", name, PrivExt)
|
|
||||||
pubName := fmt.Sprintf("%s.%s", name, PubExt)
|
|
||||||
return path.Join(s.keyDir, pubName), path.Join(s.keyDir, privName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeInfo(path string, info keys.Info) error {
|
|
||||||
return write(path, info.Name, info.PubKey.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
func readInfo(path string) (info keys.Info, err error) {
|
|
||||||
var data []byte
|
|
||||||
data, info.Name, err = read(path)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pk, err := crypto.PubKeyFromBytes(data)
|
|
||||||
info.PubKey = pk
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func read(path string) ([]byte, string, error) {
|
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", errors.Wrap(err, "Reading data")
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
d, err := ioutil.ReadAll(f)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", errors.Wrap(err, "Reading data")
|
|
||||||
}
|
|
||||||
block, headers, key, err := crypto.DecodeArmor(string(d))
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", errors.Wrap(err, "Invalid Armor")
|
|
||||||
}
|
|
||||||
if block != BlockType {
|
|
||||||
return nil, "", errors.Errorf("Unknown key type: %s", block)
|
|
||||||
}
|
|
||||||
return key, headers["name"], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func write(path, name string, key []byte) error {
|
|
||||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, keyPerm)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "Writing data")
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
headers := map[string]string{"name": name}
|
|
||||||
text := crypto.EncodeArmor(BlockType, headers, key)
|
|
||||||
_, err = f.WriteString(text)
|
|
||||||
return errors.Wrap(err, "Writing data")
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
package filestorage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
keys "github.com/tendermint/go-crypto/keys"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBasicCRUD(t *testing.T) {
|
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
|
|
||||||
dir, err := ioutil.TempDir("", "filestorage-test")
|
|
||||||
assert.Nil(err)
|
|
||||||
defer os.RemoveAll(dir)
|
|
||||||
store := New(dir)
|
|
||||||
|
|
||||||
name := "bar"
|
|
||||||
key := []byte("secret-key-here")
|
|
||||||
pubkey := crypto.GenPrivKeyEd25519().PubKey()
|
|
||||||
info := keys.Info{
|
|
||||||
Name: name,
|
|
||||||
PubKey: pubkey.Wrap(),
|
|
||||||
}
|
|
||||||
|
|
||||||
// No data: Get and Delete return nothing
|
|
||||||
_, _, err = store.Get(name)
|
|
||||||
assert.NotNil(err)
|
|
||||||
err = store.Delete(name)
|
|
||||||
assert.NotNil(err)
|
|
||||||
// List returns empty list
|
|
||||||
l, err := store.List()
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Empty(l)
|
|
||||||
|
|
||||||
// Putting the key in the store must work
|
|
||||||
err = store.Put(name, key, info)
|
|
||||||
assert.Nil(err)
|
|
||||||
// But a second time is a failure
|
|
||||||
err = store.Put(name, key, info)
|
|
||||||
assert.NotNil(err)
|
|
||||||
|
|
||||||
// Now, we can get and list properly
|
|
||||||
k, i, err := store.Get(name)
|
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
assert.Equal(key, k)
|
|
||||||
assert.Equal(info.Name, i.Name)
|
|
||||||
assert.Equal(info.PubKey, i.PubKey)
|
|
||||||
assert.NotEmpty(i.Address)
|
|
||||||
l, err = store.List()
|
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
assert.Equal(1, len(l))
|
|
||||||
assert.Equal(i, l[0])
|
|
||||||
|
|
||||||
// querying a non-existent key fails
|
|
||||||
_, _, err = store.Get("badname")
|
|
||||||
assert.NotNil(err)
|
|
||||||
|
|
||||||
// We can only delete once
|
|
||||||
err = store.Delete(name)
|
|
||||||
assert.Nil(err)
|
|
||||||
err = store.Delete(name)
|
|
||||||
assert.NotNil(err)
|
|
||||||
|
|
||||||
// and then Get and List don't work
|
|
||||||
_, _, err = store.Get(name)
|
|
||||||
assert.NotNil(err)
|
|
||||||
// List returns empty list
|
|
||||||
l, err = store.List()
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Empty(l)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDirectoryHandling(t *testing.T) {
|
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
|
|
||||||
// prepare a temp dir and make sure it is not there
|
|
||||||
newDir := path.Join(os.TempDir(), "file-test-dir")
|
|
||||||
_, err := os.Open(newDir)
|
|
||||||
assert.True(os.IsNotExist(err))
|
|
||||||
defer os.RemoveAll(newDir)
|
|
||||||
|
|
||||||
// now, check with two levels deep....
|
|
||||||
parentDir := path.Join(os.TempDir(), "missing-dir")
|
|
||||||
nestedDir := path.Join(parentDir, "lots", "of", "levels", "here")
|
|
||||||
_, err = os.Open(parentDir)
|
|
||||||
assert.True(os.IsNotExist(err))
|
|
||||||
defer os.RemoveAll(parentDir)
|
|
||||||
|
|
||||||
// create a new storage, and verify it creates the directory with good permissions
|
|
||||||
for _, dir := range []string{newDir, nestedDir, newDir} {
|
|
||||||
New(dir)
|
|
||||||
d, err := os.Open(dir)
|
|
||||||
require.Nil(err)
|
|
||||||
defer d.Close()
|
|
||||||
|
|
||||||
stat, err := d.Stat()
|
|
||||||
require.Nil(err)
|
|
||||||
assert.Equal(dirPerm, stat.Mode()&os.ModePerm)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
package memstorage provides a simple in-memory key store designed for
|
|
||||||
use in test cases, particularly to isolate them from the filesystem,
|
|
||||||
concurrency, and cleanup issues.
|
|
||||||
*/
|
|
||||||
package memstorage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
keys "github.com/tendermint/go-crypto/keys"
|
|
||||||
)
|
|
||||||
|
|
||||||
type data struct {
|
|
||||||
info keys.Info
|
|
||||||
key []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type MemStore map[string]data
|
|
||||||
|
|
||||||
// New creates an instance of file-based key storage with tight permissions
|
|
||||||
func New() MemStore {
|
|
||||||
return MemStore{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// assert MemStore satisfies keys.Storage
|
|
||||||
var _ keys.Storage = MemStore{}
|
|
||||||
|
|
||||||
// Put adds the given key, returns an error if it another key
|
|
||||||
// is already stored under this name
|
|
||||||
func (s MemStore) Put(name string, key []byte, info keys.Info) error {
|
|
||||||
if _, ok := s[name]; ok {
|
|
||||||
return errors.Errorf("Key named '%s' already exists", name)
|
|
||||||
}
|
|
||||||
s[name] = data{info, key}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get returns the key stored under the name, or returns an error if not present
|
|
||||||
func (s MemStore) Get(name string) ([]byte, keys.Info, error) {
|
|
||||||
var err error
|
|
||||||
d, ok := s[name]
|
|
||||||
if !ok {
|
|
||||||
err = errors.Errorf("Key named '%s' doesn't exist", name)
|
|
||||||
}
|
|
||||||
return d.key, d.info.Format(), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns the public info of all keys in the MemStore in unsorted order
|
|
||||||
func (s MemStore) List() (keys.Infos, error) {
|
|
||||||
res := make([]keys.Info, len(s))
|
|
||||||
i := 0
|
|
||||||
for _, d := range s {
|
|
||||||
res[i] = d.info.Format()
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete removes the named key from the MemStore, raising an error if it
|
|
||||||
// wasn't present yet.
|
|
||||||
func (s MemStore) Delete(name string) error {
|
|
||||||
_, ok := s[name]
|
|
||||||
if !ok {
|
|
||||||
return errors.Errorf("Key named '%s' doesn't exist", name)
|
|
||||||
}
|
|
||||||
delete(s, name)
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package memstorage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
|
||||||
keys "github.com/tendermint/go-crypto/keys"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBasicCRUD(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
store := New()
|
|
||||||
|
|
||||||
name := "foo"
|
|
||||||
key := []byte("secret-key-here")
|
|
||||||
pubkey := crypto.GenPrivKeyEd25519().PubKey()
|
|
||||||
info := keys.Info{
|
|
||||||
Name: name,
|
|
||||||
PubKey: pubkey,
|
|
||||||
}
|
|
||||||
|
|
||||||
// No data: Get and Delete return nothing
|
|
||||||
_, _, err := store.Get(name)
|
|
||||||
assert.NotNil(err)
|
|
||||||
err = store.Delete(name)
|
|
||||||
assert.NotNil(err)
|
|
||||||
// List returns empty list
|
|
||||||
l, err := store.List()
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Empty(l)
|
|
||||||
|
|
||||||
// Putting the key in the store must work
|
|
||||||
err = store.Put(name, key, info)
|
|
||||||
assert.Nil(err)
|
|
||||||
// But a second time is a failure
|
|
||||||
err = store.Put(name, key, info)
|
|
||||||
assert.NotNil(err)
|
|
||||||
|
|
||||||
// Now, we can get and list properly
|
|
||||||
k, i, err := store.Get(name)
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(key, k)
|
|
||||||
assert.Equal(info.Name, i.Name)
|
|
||||||
assert.Equal(info.PubKey, i.PubKey)
|
|
||||||
assert.NotEmpty(i.Address)
|
|
||||||
l, err = store.List()
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Equal(1, len(l))
|
|
||||||
assert.Equal(i, l[0])
|
|
||||||
|
|
||||||
// querying a non-existent key fails
|
|
||||||
_, _, err = store.Get("badname")
|
|
||||||
assert.NotNil(err)
|
|
||||||
|
|
||||||
// We can only delete once
|
|
||||||
err = store.Delete(name)
|
|
||||||
assert.Nil(err)
|
|
||||||
err = store.Delete(name)
|
|
||||||
assert.NotNil(err)
|
|
||||||
|
|
||||||
// and then Get and List don't work
|
|
||||||
_, _, err = store.Get(name)
|
|
||||||
assert.NotNil(err)
|
|
||||||
// List returns empty list
|
|
||||||
l, err = store.List()
|
|
||||||
assert.Nil(err)
|
|
||||||
assert.Empty(l)
|
|
||||||
}
|
|
138
keys/types.go
138
keys/types.go
@ -1,134 +1,48 @@
|
|||||||
package keys
|
package keys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
wire "github.com/tendermint/go-wire"
|
||||||
"sort"
|
|
||||||
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
data "github.com/tendermint/go-wire/data"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Storage has many implementation, based on security and sharing requirements
|
|
||||||
// like disk-backed, mem-backed, vault, db, etc.
|
|
||||||
type Storage interface {
|
|
||||||
Put(name string, key []byte, info Info) error
|
|
||||||
Get(name string) (key []byte, info Info, err error)
|
|
||||||
List() (Infos, error)
|
|
||||||
Delete(name string) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info is the public information about a key
|
// Info is the public information about a key
|
||||||
type Info struct {
|
type Info struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Address data.Bytes `json:"address"`
|
PubKey crypto.PubKey `json:"pubkey"`
|
||||||
PubKey crypto.PubKey `json:"pubkey"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Info) Format() Info {
|
// Address is a helper function to calculate the address from the pubkey
|
||||||
if !i.PubKey.Empty() {
|
func (i Info) Address() []byte {
|
||||||
i.Address = i.PubKey.Address()
|
return i.PubKey.Address()
|
||||||
}
|
|
||||||
return *i
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Infos is a wrapper to allows alphabetical sorting of the keys
|
func (i Info) bytes() []byte {
|
||||||
type Infos []Info
|
return wire.BinaryBytes(i)
|
||||||
|
}
|
||||||
|
|
||||||
func (k Infos) Len() int { return len(k) }
|
func readInfo(bs []byte) (info Info, err error) {
|
||||||
func (k Infos) Less(i, j int) bool { return k[i].Name < k[j].Name }
|
err = wire.ReadBinaryBytes(bs, &info)
|
||||||
func (k Infos) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
|
return
|
||||||
func (k Infos) Sort() {
|
}
|
||||||
if k != nil {
|
|
||||||
sort.Sort(k)
|
func info(name string, privKey crypto.PrivKey) Info {
|
||||||
|
return Info{
|
||||||
|
Name: name,
|
||||||
|
PubKey: privKey.PubKey(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signable represents any transaction we wish to send to tendermint core
|
// Keybase allows simple CRUD on a keystore, as an aid to signing
|
||||||
// These methods allow us to sign arbitrary Tx with the KeyStore
|
type Keybase interface {
|
||||||
type Signable interface {
|
// Sign some bytes
|
||||||
// SignBytes is the immutable data, which needs to be signed
|
Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error)
|
||||||
SignBytes() []byte
|
// Create a new keypair
|
||||||
|
Create(name, passphrase, algo string) (_ Info, seedphrase string, _ error)
|
||||||
// Sign will add a signature and pubkey.
|
// Recover takes a seedphrase and loads in the key
|
||||||
//
|
|
||||||
// Depending on the Signable, one may be able to call this multiple times for multisig
|
|
||||||
// Returns error if called with invalid data or too many times
|
|
||||||
Sign(pubkey crypto.PubKey, sig crypto.Signature) error
|
|
||||||
|
|
||||||
// Signers will return the public key(s) that signed if the signature
|
|
||||||
// is valid, or an error if there is any issue with the signature,
|
|
||||||
// including if there are no signatures
|
|
||||||
Signers() ([]crypto.PubKey, error)
|
|
||||||
|
|
||||||
// TxBytes returns the transaction data as well as all signatures
|
|
||||||
// It should return an error if Sign was never called
|
|
||||||
TxBytes() ([]byte, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signer allows one to use a keystore to sign transactions
|
|
||||||
type Signer interface {
|
|
||||||
Sign(name, passphrase string, tx Signable) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manager allows simple CRUD on a keystore, as an aid to signing
|
|
||||||
type Manager interface {
|
|
||||||
Signer
|
|
||||||
// Create also returns a seed phrase for cold-storage
|
|
||||||
Create(name, passphrase, algo string) (Info, string, error)
|
|
||||||
// Recover takes a seedphrase and loads in the private key
|
|
||||||
Recover(name, passphrase, seedphrase string) (Info, error)
|
Recover(name, passphrase, seedphrase string) (Info, error)
|
||||||
List() (Infos, error)
|
List() ([]Info, error)
|
||||||
Get(name string) (Info, error)
|
Get(name string) (Info, error)
|
||||||
Update(name, oldpass, newpass string) error
|
Update(name, oldpass, newpass string) error
|
||||||
Delete(name, passphrase string) error
|
Delete(name, passphrase string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
/**** MockSignable allows us to view data ***/
|
|
||||||
|
|
||||||
// MockSignable lets us wrap arbitrary data with a go-crypto signature
|
|
||||||
type MockSignable struct {
|
|
||||||
Data []byte
|
|
||||||
PubKey crypto.PubKey
|
|
||||||
Signature crypto.Signature
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Signable = &MockSignable{}
|
|
||||||
|
|
||||||
// NewMockSignable sets the data to sign
|
|
||||||
func NewMockSignable(data []byte) *MockSignable {
|
|
||||||
return &MockSignable{Data: data}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TxBytes returns the full data with signatures
|
|
||||||
func (s *MockSignable) TxBytes() ([]byte, error) {
|
|
||||||
return wire.BinaryBytes(s), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SignBytes returns the original data passed into `NewSig`
|
|
||||||
func (s *MockSignable) SignBytes() []byte {
|
|
||||||
return s.Data
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sign will add a signature and pubkey.
|
|
||||||
//
|
|
||||||
// Depending on the Signable, one may be able to call this multiple times for multisig
|
|
||||||
// Returns error if called with invalid data or too many times
|
|
||||||
func (s *MockSignable) Sign(pubkey crypto.PubKey, sig crypto.Signature) error {
|
|
||||||
s.PubKey = pubkey
|
|
||||||
s.Signature = sig
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signers will return the public key(s) that signed if the signature
|
|
||||||
// is valid, or an error if there is any issue with the signature,
|
|
||||||
// including if there are no signatures
|
|
||||||
func (s *MockSignable) Signers() ([]crypto.PubKey, error) {
|
|
||||||
if s.PubKey.Empty() {
|
|
||||||
return nil, fmt.Errorf("no signers")
|
|
||||||
}
|
|
||||||
if !s.PubKey.VerifyBytes(s.SignBytes(), s.Signature) {
|
|
||||||
return nil, fmt.Errorf("invalid signature")
|
|
||||||
}
|
|
||||||
return []crypto.PubKey{s.PubKey}, nil
|
|
||||||
}
|
|
||||||
|
@ -3,14 +3,14 @@ package keys
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
asrt "github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
rqr "github.com/stretchr/testify/require"
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLengthCalc(t *testing.T) {
|
func TestLengthCalc(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := asrt.New(t)
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
bytes, words int
|
bytes, words int
|
||||||
@ -50,7 +50,7 @@ func TestLengthCalc(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestEncodeDecode(t *testing.T) {
|
func TestEncodeDecode(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := asrt.New(t), rqr.New(t)
|
||||||
|
|
||||||
codec, err := LoadCodec("english")
|
codec, err := LoadCodec("english")
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(err, "%+v", err)
|
||||||
@ -82,7 +82,7 @@ func TestEncodeDecode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckInvalidLists(t *testing.T) {
|
func TestCheckInvalidLists(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := asrt.New(t)
|
||||||
|
|
||||||
trivial := []string{"abc", "def"}
|
trivial := []string{"abc", "def"}
|
||||||
short := make([]string, 1234)
|
short := make([]string, 1234)
|
||||||
@ -144,7 +144,7 @@ func getDiffWord(c *WordCodec, not string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckTypoDetection(t *testing.T) {
|
func TestCheckTypoDetection(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := asrt.New(t), rqr.New(t)
|
||||||
|
|
||||||
banks := []string{"english", "spanish", "japanese", "chinese_simplified"}
|
banks := []string{"english", "spanish", "japanese", "chinese_simplified"}
|
||||||
|
|
||||||
|
@ -5,14 +5,14 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
asrt "github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
rqr "github.com/stretchr/testify/require"
|
||||||
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLedgerKeys(t *testing.T) {
|
func TestLedgerKeys(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := asrt.New(t), rqr.New(t)
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
msg, pubkey, sig string
|
msg, pubkey, sig string
|
||||||
@ -76,7 +76,7 @@ func TestLedgerKeys(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRealLedger(t *testing.T) {
|
func TestRealLedger(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := asrt.New(t), rqr.New(t)
|
||||||
|
|
||||||
if os.Getenv("WITH_LEDGER") == "" {
|
if os.Getenv("WITH_LEDGER") == "" {
|
||||||
t.Skip("Set WITH_LEDGER to run code on real ledger")
|
t.Skip("Set WITH_LEDGER to run code on real ledger")
|
||||||
@ -115,7 +115,7 @@ func TestRealLedger(t *testing.T) {
|
|||||||
// TestRealLedgerErrorHandling calls. These tests assume
|
// TestRealLedgerErrorHandling calls. These tests assume
|
||||||
// the ledger is not plugged in....
|
// the ledger is not plugged in....
|
||||||
func TestRealLedgerErrorHandling(t *testing.T) {
|
func TestRealLedgerErrorHandling(t *testing.T) {
|
||||||
require := require.New(t)
|
require := rqr.New(t)
|
||||||
|
|
||||||
if os.Getenv("WITH_LEDGER") != "" {
|
if os.Getenv("WITH_LEDGER") != "" {
|
||||||
t.Skip("Skipping on WITH_LEDGER as it tests unplugged cases")
|
t.Skip("Skipping on WITH_LEDGER as it tests unplugged cases")
|
||||||
|
@ -5,8 +5,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/stretchr/testify/assert"
|
asrt "github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
rqr "github.com/stretchr/testify/require"
|
||||||
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ func parseSig(data []byte) (key crypto.Signature, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestParseDigest(t *testing.T) {
|
func TestParseDigest(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := asrt.New(t), rqr.New(t)
|
||||||
|
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
output string
|
output string
|
||||||
@ -91,7 +92,7 @@ func toBytes(c cryptoCase) (msg, key, sig []byte, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCryptoConvert(t *testing.T) {
|
func TestCryptoConvert(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := asrt.New(t), rqr.New(t)
|
||||||
|
|
||||||
cases := []cryptoCase{
|
cases := []cryptoCase{
|
||||||
0: {
|
0: {
|
||||||
|
@ -109,8 +109,6 @@ func TestSignatureEncodings(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestWrapping(t *testing.T) {
|
func TestWrapping(t *testing.T) {
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
// construct some basic constructs
|
// construct some basic constructs
|
||||||
msg := CRandBytes(128)
|
msg := CRandBytes(128)
|
||||||
priv := GenPrivKeyEd25519()
|
priv := GenPrivKeyEd25519()
|
||||||
@ -126,7 +124,7 @@ func TestWrapping(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, p := range pubs {
|
for _, p := range pubs {
|
||||||
_, ok := p.PubKeyInner.(PubKey)
|
_, ok := p.PubKeyInner.(PubKey)
|
||||||
assert.False(ok)
|
assert.False(t, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
sigs := []Signature{
|
sigs := []Signature{
|
||||||
@ -137,7 +135,7 @@ func TestWrapping(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for _, s := range sigs {
|
for _, s := range sigs {
|
||||||
_, ok := s.SignatureInner.(Signature)
|
_, ok := s.SignatureInner.(Signature)
|
||||||
assert.False(ok)
|
assert.False(t, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user