Browse Source

Draft of suggested changes

pull/1782/head
Jae Kwon 7 years ago
committed by Ethan Frey
parent
commit
a2583e2783
13 changed files with 52 additions and 951 deletions
  1. +0
    -25
      keys/cryptostore/docs.go
  2. +0
    -49
      keys/cryptostore/enc_storage.go
  3. +0
    -60
      keys/cryptostore/encoder.go
  4. +0
    -105
      keys/cryptostore/encoder_test.go
  5. +0
    -88
      keys/cryptostore/generator.go
  6. +0
    -48
      keys/cryptostore/storage_test.go
  7. +43
    -41
      keys/keybase.go
  8. +0
    -0
      keys/keybase_test.go
  9. +0
    -177
      keys/storage/filestorage/main.go
  10. +0
    -106
      keys/storage/filestorage/main_test.go
  11. +0
    -68
      keys/storage/memstorage/main.go
  12. +0
    -69
      keys/storage/memstorage/main_test.go
  13. +9
    -115
      keys/types.go

+ 0
- 25
keys/cryptostore/docs.go View File

@ -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

+ 0
- 49
keys/cryptostore/enc_storage.go View File

@ -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,
}
}

+ 0
- 60
keys/cryptostore/encoder.go View File

@ -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)
}

+ 0
- 105
keys/cryptostore/encoder_test.go View File

@ -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)
}

+ 0
- 88
keys/cryptostore/generator.go View File

@ -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}
}
}

+ 0
- 48
keys/cryptostore/storage_test.go View File

@ -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)
}

keys/cryptostore/holder.go → keys/keybase.go View File


keys/cryptostore/holder_test.go → keys/keybase_test.go View File


+ 0
- 177
keys/storage/filestorage/main.go View File

@ -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")
}

+ 0
- 106
keys/storage/filestorage/main_test.go View File

@ -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)
}
}

+ 0
- 68
keys/storage/memstorage/main.go View File

@ -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
}

+ 0
- 69
keys/storage/memstorage/main_test.go View File

@ -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)
}

+ 9
- 115
keys/types.go View File

@ -1,134 +1,28 @@
package keys
import (
"fmt"
"sort"
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
type Info struct {
Name string `json:"name"`
Address data.Bytes `json:"address"`
PubKey crypto.PubKey `json:"pubkey"`
}
func (i *Info) Format() Info {
if !i.PubKey.Empty() {
i.Address = i.PubKey.Address()
}
return *i
}
// Infos is a wrapper to allows alphabetical sorting of the keys
type Infos []Info
func (k Infos) Len() int { return len(k) }
func (k Infos) Less(i, j int) bool { return k[i].Name < k[j].Name }
func (k Infos) Swap(i, j int) { k[i], k[j] = k[j], k[i] }
func (k Infos) Sort() {
if k != nil {
sort.Sort(k)
}
}
// Signable represents any transaction we wish to send to tendermint core
// These methods allow us to sign arbitrary Tx with the KeyStore
type Signable interface {
// SignBytes is the immutable data, which needs to be signed
SignBytes() []byte
// 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
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
Name string `json:"name"`
PubKey crypto.PubKey `json:"pubkey"`
}
// 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
// Keybase allows simple CRUD on a keystore, as an aid to signing
type Keybase interface {
// Sign some bytes
Sign(name, passphrase string, msg []byte) (crypto.Signature, error)
// Create a new keypair
Create(name, passphrase, algo string) (_ Info, seedphrase string, _ error)
// Recover takes a seedphrase and loads in the key
Recover(name, passphrase, seedphrase string) (Info, error)
List() (Infos, error)
Get(name string) (Info, error)
Update(name, oldpass, newpass 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
}

Loading…
Cancel
Save