mirror of
https://github.com/fluencelabs/tendermint
synced 2025-04-25 06:42:16 +00:00
Use new go-wire; PubKey etc are interfaces; Keybase refactor
This commit is contained in:
parent
67a47e6a0b
commit
788cc0a792
@ -1,9 +0,0 @@
|
|||||||
package crypto
|
|
||||||
|
|
||||||
// Types of implementations
|
|
||||||
const (
|
|
||||||
TypeEd25519 = byte(0x01)
|
|
||||||
TypeSecp256k1 = byte(0x02)
|
|
||||||
NameEd25519 = "ed25519"
|
|
||||||
NameSecp256k1 = "secp256k1"
|
|
||||||
)
|
|
@ -1,91 +0,0 @@
|
|||||||
package crypto_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
data "github.com/tendermint/go-wire/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PubName struct {
|
|
||||||
PubNameInner
|
|
||||||
}
|
|
||||||
|
|
||||||
type PubNameInner interface {
|
|
||||||
AssertIsPubNameInner()
|
|
||||||
String() string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p PubName) MarshalJSON() ([]byte, error) {
|
|
||||||
return pubNameMapper.ToJSON(p.PubNameInner)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PubName) UnmarshalJSON(data []byte) error {
|
|
||||||
parsed, err := pubNameMapper.FromJSON(data)
|
|
||||||
if err == nil && parsed != nil {
|
|
||||||
p.PubNameInner = parsed.(PubNameInner)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var pubNameMapper = data.NewMapper(PubName{}).
|
|
||||||
RegisterImplementation(PubNameFoo{}, "foo", 1).
|
|
||||||
RegisterImplementation(PubNameBar{}, "bar", 2)
|
|
||||||
|
|
||||||
func (f PubNameFoo) AssertIsPubNameInner() {}
|
|
||||||
func (f PubNameBar) AssertIsPubNameInner() {}
|
|
||||||
|
|
||||||
//----------------------------------------
|
|
||||||
|
|
||||||
type PubNameFoo struct {
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f PubNameFoo) String() string { return "Foo: " + f.Name }
|
|
||||||
|
|
||||||
type PubNameBar struct {
|
|
||||||
Age int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b PubNameBar) String() string { return fmt.Sprintf("Bar #%d", b.Age) }
|
|
||||||
|
|
||||||
//----------------------------------------
|
|
||||||
|
|
||||||
// TestEncodeDemo tries the various strategies to encode the objects
|
|
||||||
func TestEncodeDemo(t *testing.T) {
|
|
||||||
assert, require := assert.New(t), require.New(t)
|
|
||||||
|
|
||||||
cases := []struct {
|
|
||||||
in, out PubNameInner
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{PubName{PubNameFoo{"pub-foo"}}, &PubName{}, "Foo: pub-foo"},
|
|
||||||
{PubName{PubNameBar{7}}, &PubName{}, "Bar #7"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range cases {
|
|
||||||
|
|
||||||
// Make sure it is proper to start
|
|
||||||
require.Equal(tc.expected, tc.in.String())
|
|
||||||
|
|
||||||
// Try to encode as binary
|
|
||||||
b, err := data.ToWire(tc.in)
|
|
||||||
if assert.Nil(err, "%d: %#v", i, tc.in) {
|
|
||||||
err2 := data.FromWire(b, tc.out)
|
|
||||||
if assert.Nil(err2) {
|
|
||||||
assert.Equal(tc.expected, tc.out.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to encode it as json
|
|
||||||
j, err := data.ToJSON(tc.in)
|
|
||||||
if assert.Nil(err, "%d: %#v", i, tc.in) {
|
|
||||||
err2 := data.FromJSON(j, tc.out)
|
|
||||||
if assert.Nil(err2) {
|
|
||||||
assert.Equal(tc.expected, tc.out.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,8 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
|
/*
|
||||||
|
XXX Needs to be refactored to not use go-wire/data
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
@ -179,3 +182,4 @@ func TestEmbededWireEncodings(t *testing.T) {
|
|||||||
checkWire(t, msg, &msg2, tc.keyType, tc.size)
|
checkWire(t, msg, &msg2, tc.keyType, tc.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
35
glide.lock
generated
35
glide.lock
generated
@ -1,18 +1,18 @@
|
|||||||
hash: a21061afc44c148eb6bfeb91478b520661f3d086234383a0208d915b0cb058b8
|
hash: ed34b08bfc24f64edbc1a16ad2e34198afefc0e2dc133be2c92c6f764f953234
|
||||||
updated: 2017-12-27T14:29:49.534901894-08:00
|
updated: 2018-01-13T18:02:43.833267692-08:00
|
||||||
imports:
|
imports:
|
||||||
- name: github.com/btcsuite/btcd
|
- name: github.com/btcsuite/btcd
|
||||||
version: c7588cbf7690cd9f047a28efa2dcd8f2435a4e5e
|
version: 2e60448ffcc6bf78332d1fe590260095f554dd78
|
||||||
subpackages:
|
subpackages:
|
||||||
- btcec
|
- btcec
|
||||||
- name: github.com/btcsuite/btcutil
|
- name: github.com/btcsuite/btcutil
|
||||||
version: 66871daeb12123ece012a9628d2798d01195c4b3
|
version: 501929d3d046174c3d39f0ea54ece471aa17238c
|
||||||
subpackages:
|
subpackages:
|
||||||
- base58
|
- base58
|
||||||
- name: github.com/ethanfrey/ledger
|
- name: github.com/davecgh/go-spew
|
||||||
version: 5e432577be582bd18a3b4a9cd75dae7a317ade36
|
version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9
|
||||||
- name: github.com/flynn/hid
|
subpackages:
|
||||||
version: ed06a31c6245d4552e8dbba7e32e5b010b875d65
|
- spew
|
||||||
- name: github.com/go-kit/kit
|
- name: github.com/go-kit/kit
|
||||||
version: e2b298466b32c7cd5579a9b9b07e968fc9d9452c
|
version: e2b298466b32c7cd5579a9b9b07e968fc9d9452c
|
||||||
subpackages:
|
subpackages:
|
||||||
@ -32,7 +32,7 @@ imports:
|
|||||||
- name: github.com/golang/snappy
|
- name: github.com/golang/snappy
|
||||||
version: 553a641470496b2327abcac10b36396bd98e45c9
|
version: 553a641470496b2327abcac10b36396bd98e45c9
|
||||||
- name: github.com/howeyc/crc16
|
- name: github.com/howeyc/crc16
|
||||||
version: 96a97a1abb579c7ff1a8ffa77f2e72d1c314b57f
|
version: 2b2a61e366a66d3efb279e46176e7291001e0354
|
||||||
- name: github.com/jmhodges/levigo
|
- name: github.com/jmhodges/levigo
|
||||||
version: c42d9e0ca023e2198120196f842701bb4c55d7b9
|
version: c42d9e0ca023e2198120196f842701bb4c55d7b9
|
||||||
- name: github.com/kr/logfmt
|
- name: github.com/kr/logfmt
|
||||||
@ -55,17 +55,14 @@ imports:
|
|||||||
- leveldb/table
|
- leveldb/table
|
||||||
- leveldb/util
|
- leveldb/util
|
||||||
- name: github.com/tendermint/ed25519
|
- name: github.com/tendermint/ed25519
|
||||||
version: 1f52c6f8b8a5c7908aff4497c186af344b428925
|
version: d8387025d2b9d158cf4efb07e7ebf814bcce2057
|
||||||
subpackages:
|
subpackages:
|
||||||
- edwards25519
|
- edwards25519
|
||||||
- extra25519
|
- extra25519
|
||||||
- name: github.com/tendermint/go-wire
|
- name: github.com/tendermint/go-wire
|
||||||
version: 27be46e25124ddf775e23317a83647ce62a93f6b
|
version: fde4a0bf4dd4c0ec0df2504f79f23ed7e6b3b452
|
||||||
subpackages:
|
|
||||||
- data
|
|
||||||
- data/base58
|
|
||||||
- name: github.com/tendermint/tmlibs
|
- name: github.com/tendermint/tmlibs
|
||||||
version: 93c05aa8c06ef38f2b15fcdd1d91eafefda2732d
|
version: 2bb538b150f197a04a0b969a27e9ea24d35edbc1
|
||||||
subpackages:
|
subpackages:
|
||||||
- common
|
- common
|
||||||
- db
|
- db
|
||||||
@ -83,10 +80,6 @@ imports:
|
|||||||
- ripemd160
|
- ripemd160
|
||||||
- salsa20/salsa
|
- salsa20/salsa
|
||||||
testImports:
|
testImports:
|
||||||
- name: github.com/davecgh/go-spew
|
|
||||||
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
|
|
||||||
subpackages:
|
|
||||||
- spew
|
|
||||||
- name: github.com/mndrix/btcutil
|
- name: github.com/mndrix/btcutil
|
||||||
version: d3a63a5752ecf3fbc06bd97365da752111c263df
|
version: d3a63a5752ecf3fbc06bd97365da752111c263df
|
||||||
- name: github.com/pmezard/go-difflib
|
- name: github.com/pmezard/go-difflib
|
||||||
@ -94,11 +87,11 @@ testImports:
|
|||||||
subpackages:
|
subpackages:
|
||||||
- difflib
|
- difflib
|
||||||
- name: github.com/stretchr/testify
|
- name: github.com/stretchr/testify
|
||||||
version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
|
version: b91bfb9ebec76498946beb6af7c0230c7cc7ba6c
|
||||||
subpackages:
|
subpackages:
|
||||||
- assert
|
- assert
|
||||||
- require
|
- require
|
||||||
- name: github.com/tyler-smith/go-bip32
|
- name: github.com/tyler-smith/go-bip32
|
||||||
version: eb790af526c30f23a7c8b00a48e342f9d0bd6386
|
version: 2c9cfd17756470a0b7c3e4b7954bae7d11035504
|
||||||
- name: github.com/tyler-smith/go-bip39
|
- name: github.com/tyler-smith/go-bip39
|
||||||
version: 8e7a99b3e716f36d3b080a9a70f9eb45abe4edcc
|
version: 8e7a99b3e716f36d3b080a9a70f9eb45abe4edcc
|
||||||
|
@ -19,10 +19,7 @@ import:
|
|||||||
- package: github.com/tendermint/tmlibs
|
- package: github.com/tendermint/tmlibs
|
||||||
version: sdk2
|
version: sdk2
|
||||||
- package: github.com/tendermint/go-wire
|
- package: github.com/tendermint/go-wire
|
||||||
version: develop
|
version: sdk2
|
||||||
subpackages:
|
|
||||||
- data
|
|
||||||
- data/base58
|
|
||||||
- package: golang.org/x/crypto
|
- package: golang.org/x/crypto
|
||||||
subpackages:
|
subpackages:
|
||||||
- blowfish
|
- blowfish
|
||||||
@ -31,7 +28,8 @@ import:
|
|||||||
- ripemd160
|
- ripemd160
|
||||||
- package: github.com/pkg/errors
|
- package: github.com/pkg/errors
|
||||||
- package: github.com/howeyc/crc16
|
- package: github.com/howeyc/crc16
|
||||||
- package: github.com/ethanfrey/ledger
|
# Needs refactoring, shouldn't be implementing PrivKey
|
||||||
|
#- package: github.com/ethanfrey/ledger
|
||||||
testImport:
|
testImport:
|
||||||
- package: github.com/mndrix/btcutil
|
- package: github.com/mndrix/btcutil
|
||||||
- package: github.com/stretchr/testify
|
- package: github.com/stretchr/testify
|
||||||
|
227
keys/keybase.go
227
keys/keybase.go
@ -6,16 +6,10 @@ import (
|
|||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
|
||||||
|
|
||||||
"github.com/tendermint/go-crypto/keys/words"
|
"github.com/tendermint/go-crypto/keys/words"
|
||||||
"github.com/tendermint/go-crypto/nano"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 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
|
// dbKeybase combines encyption and storage implementation to provide
|
||||||
// a full-featured key manager
|
// a full-featured key manager
|
||||||
type dbKeybase struct {
|
type dbKeybase struct {
|
||||||
@ -32,58 +26,59 @@ func New(db dbm.DB, codec words.Codec) dbKeybase {
|
|||||||
|
|
||||||
var _ Keybase = dbKeybase{}
|
var _ Keybase = dbKeybase{}
|
||||||
|
|
||||||
// Create generates a new key and persists it storage, encrypted using the passphrase.
|
// Create generates a new key and persists it to storage, encrypted
|
||||||
// It returns the generated seedphrase (mnemonic) and the key Info.
|
// using the passphrase. It returns the generated seedphrase
|
||||||
// It returns an error if it fails to generate a key for the given algo type,
|
// (mnemonic) and the key Info. It returns an error if it fails to
|
||||||
// or if another key is already stored under the same name.
|
// generate a key for the given algo type, or if another key is
|
||||||
func (kb dbKeybase) Create(name, passphrase, algo string) (string, Info, error) {
|
// already stored under the same name.
|
||||||
|
func (kb dbKeybase) Create(name, passphrase string, algo CryptoAlgo) (Info, string, error) {
|
||||||
// NOTE: secret is SHA256 hashed by secp256k1 and ed25519.
|
// NOTE: secret is SHA256 hashed by secp256k1 and ed25519.
|
||||||
// 16 byte secret corresponds to 12 BIP39 words.
|
// 16 byte secret corresponds to 12 BIP39 words.
|
||||||
// XXX: Ledgers use 24 words now - should we ?
|
// XXX: Ledgers use 24 words now - should we ?
|
||||||
secret := crypto.CRandBytes(16)
|
secret := crypto.CRandBytes(16)
|
||||||
key, err := generate(algo, secret)
|
priv, err := generate(algo, secret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", Info{}, err
|
return Info{}, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt and persist the key
|
// encrypt and persist the key
|
||||||
public := kb.writeKey(key, name, passphrase)
|
info := kb.writeKey(priv, name, passphrase)
|
||||||
|
|
||||||
|
// we append the type byte to the serialized secret to help with
|
||||||
|
// recovery
|
||||||
|
// ie [secret] = [type] + [secret]
|
||||||
|
typ := cryptoAlgoToByte(algo)
|
||||||
|
secret = append([]byte{typ}, secret...)
|
||||||
|
|
||||||
// return the mnemonic phrase
|
// return the mnemonic phrase
|
||||||
words, err := kb.codec.BytesToWords(secret)
|
words, err := kb.codec.BytesToWords(secret)
|
||||||
seedphrase := strings.Join(words, " ")
|
seed := strings.Join(words, " ")
|
||||||
return seedphrase, public, err
|
return info, seed, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recover converts a seedphrase to a private key and persists it, encrypted with the given passphrase.
|
// Recover converts a seedphrase to a private key and persists it,
|
||||||
// Functions like Create, but seedphrase is input not output.
|
// encrypted with the given passphrase. Functions like Create, but
|
||||||
func (kb dbKeybase) Recover(name, passphrase, algo string, seedphrase string) (Info, error) {
|
// seedphrase is input not output.
|
||||||
|
func (kb dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) {
|
||||||
key, err := kb.SeedToPrivKey(algo, seedphrase)
|
words := strings.Split(strings.TrimSpace(seedphrase), " ")
|
||||||
|
secret, err := kb.codec.WordsToBytes(words)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Info{}, err
|
return Info{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid seedphrase. Encrypt key and persist to disk.
|
// secret is comprised of the actual secret with the type
|
||||||
public := kb.writeKey(key, name, passphrase)
|
// appended.
|
||||||
return public, nil
|
// ie [secret] = [type] + [secret]
|
||||||
}
|
typ, secret := secret[0], secret[1:]
|
||||||
|
algo := byteToCryptoAlgo(typ)
|
||||||
// SeedToPrivKey returns the private key corresponding to a seedphrase
|
priv, err := generate(algo, secret)
|
||||||
// without persisting the private key.
|
|
||||||
// TODO: enable the keybase to just hold these in memory so we can sign without persisting (?)
|
|
||||||
func (kb dbKeybase) SeedToPrivKey(algo, seedphrase string) (crypto.PrivKey, error) {
|
|
||||||
words := strings.Split(strings.TrimSpace(seedphrase), " ")
|
|
||||||
secret, err := kb.codec.WordsToBytes(words)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return crypto.PrivKey{}, err
|
return Info{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := generate(algo, secret)
|
// encrypt and persist key.
|
||||||
if err != nil {
|
public := kb.writeKey(priv, name, passphrase)
|
||||||
return crypto.PrivKey{}, err
|
return public, err
|
||||||
}
|
|
||||||
return key, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// List returns the keys from storage in alphabetical order.
|
// List returns the keys from storage in alphabetical order.
|
||||||
@ -92,75 +87,56 @@ func (kb dbKeybase) List() ([]Info, error) {
|
|||||||
iter := kb.db.Iterator(nil, nil)
|
iter := kb.db.Iterator(nil, nil)
|
||||||
defer iter.Close()
|
defer iter.Close()
|
||||||
for ; iter.Valid(); iter.Next() {
|
for ; iter.Valid(); iter.Next() {
|
||||||
key := iter.Key()
|
// key := iter.Key()
|
||||||
if isPub(key) {
|
info, err := readInfo(iter.Value())
|
||||||
info, err := readInfo(iter.Value())
|
if err != nil {
|
||||||
if err != nil {
|
return nil, err
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
res = append(res, info)
|
|
||||||
}
|
}
|
||||||
|
res = append(res, info)
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the public information about one key.
|
// Get returns the public information about one key.
|
||||||
func (kb dbKeybase) Get(name string) (Info, error) {
|
func (kb dbKeybase) Get(name string) (Info, error) {
|
||||||
bs := kb.db.Get(pubName(name))
|
bs := kb.db.Get(infoKey(name))
|
||||||
return readInfo(bs)
|
return readInfo(bs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign signs the msg with the named key.
|
// Sign signs the msg with the named key.
|
||||||
// It returns an error if the key doesn't exist or the decryption fails.
|
// It returns an error if the key doesn't exist or the decryption fails.
|
||||||
// TODO: what if leddger fails ?
|
func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signature, pub crypto.PubKey, err error) {
|
||||||
func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signature, pk crypto.PubKey, err error) {
|
info, err := kb.Get(name)
|
||||||
var key crypto.PrivKey
|
|
||||||
armorStr := kb.db.Get(privName(name))
|
|
||||||
key, err = unarmorDecryptPrivKey(string(armorStr), passphrase)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
priv, err := unarmorDecryptPrivKey(info.PrivKeyArmor, passphrase)
|
||||||
sig = key.Sign(msg)
|
if err != nil {
|
||||||
pk = key.PubKey()
|
return
|
||||||
|
}
|
||||||
|
sig = priv.Sign(msg)
|
||||||
|
pub = priv.PubKey()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export decodes the private key with the current password, encrypts
|
func (kb dbKeybase) Export(name string) (armor string, err error) {
|
||||||
// it with a secure one-time password and generates an armored private key
|
bz := kb.db.Get(infoKey(name))
|
||||||
// that can be Imported by another dbKeybase.
|
if bz == nil {
|
||||||
//
|
return "", errors.New("No key to export with name " + name)
|
||||||
// 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) {
|
|
||||||
armorStr := kb.db.Get(privName(name))
|
|
||||||
key, err := unarmorDecryptPrivKey(string(armorStr), oldpass)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
return armorInfoBytes(bz), nil
|
||||||
if transferpass == "" {
|
|
||||||
return key.Bytes(), nil
|
|
||||||
}
|
|
||||||
armorBytes := encryptArmorPrivKey(key, transferpass)
|
|
||||||
return []byte(armorBytes), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import accepts bytes generated by Export along with the same transferpass.
|
func (kb dbKeybase) Import(name string, armor string) (err error) {
|
||||||
// If they are valid, it stores the password under the given name with the
|
bz := kb.db.Get(infoKey(name))
|
||||||
// new passphrase.
|
if len(bz) > 0 {
|
||||||
func (kb dbKeybase) Import(name, newpass, transferpass string, data []byte) (err error) {
|
return errors.New("Cannot overwrite data for name " + name)
|
||||||
var key crypto.PrivKey
|
|
||||||
if transferpass == "" {
|
|
||||||
key, err = crypto.PrivKeyFromBytes(data)
|
|
||||||
} else {
|
|
||||||
key, err = unarmorDecryptPrivKey(string(data), transferpass)
|
|
||||||
}
|
}
|
||||||
|
infoBytes, err := unarmorInfoBytes(armor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
|
kb.db.Set(infoKey(name), infoBytes)
|
||||||
kb.writeKey(key, name, newpass)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,80 +144,59 @@ func (kb dbKeybase) Import(name, newpass, transferpass string, data []byte) (err
|
|||||||
// proper passphrase before deleting it (for security).
|
// proper passphrase before deleting it (for security).
|
||||||
func (kb dbKeybase) Delete(name, passphrase string) error {
|
func (kb dbKeybase) Delete(name, passphrase string) error {
|
||||||
// verify we have the proper password before deleting
|
// verify we have the proper password before deleting
|
||||||
bs := kb.db.Get(privName(name))
|
info, err := kb.Get(name)
|
||||||
_, err := unarmorDecryptPrivKey(string(bs), passphrase)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
kb.db.DeleteSync(pubName(name))
|
_, err = unarmorDecryptPrivKey(info.PrivKeyArmor, passphrase)
|
||||||
kb.db.DeleteSync(privName(name))
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
kb.db.DeleteSync(infoKey(name))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update changes the passphrase with which an already stored key is encrypted.
|
// Update changes the passphrase with which an already stored key is
|
||||||
|
// encrypted.
|
||||||
//
|
//
|
||||||
// oldpass must be the current passphrase used for encryption, newpass will be
|
// oldpass must be the current passphrase used for encryption,
|
||||||
// the only valid passphrase from this time forward.
|
// newpass will be the only valid passphrase from this time forward.
|
||||||
func (kb dbKeybase) Update(name, oldpass, newpass string) error {
|
func (kb dbKeybase) Update(name, oldpass, newpass string) error {
|
||||||
bs := kb.db.Get(privName(name))
|
info, err := kb.Get(name)
|
||||||
key, err := unarmorDecryptPrivKey(string(bs), oldpass)
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
key, err := unarmorDecryptPrivKey(info.PrivKeyArmor, oldpass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate the public bytes and the encrypted privkey
|
kb.writeKey(key, name, newpass)
|
||||||
public := info(name, key)
|
|
||||||
private := encryptArmorPrivKey(key, newpass)
|
|
||||||
|
|
||||||
// We must delete first, as Putting over an existing name returns an error.
|
|
||||||
// Must be done atomically with the write or we could lose the key.
|
|
||||||
batch := kb.db.NewBatch()
|
|
||||||
batch.Delete(pubName(name))
|
|
||||||
batch.Delete(privName(name))
|
|
||||||
batch.Set(pubName(name), public.bytes())
|
|
||||||
batch.Set(privName(name), []byte(private))
|
|
||||||
batch.Write()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
func (kb dbKeybase) writeKey(priv crypto.PrivKey, name, passphrase string) Info {
|
func (kb dbKeybase) writeKey(priv crypto.PrivKey, name, passphrase string) Info {
|
||||||
// Generate the public bytes and the encrypted privkey
|
// generate the encrypted privkey
|
||||||
public := info(name, priv)
|
privArmor := encryptArmorPrivKey(priv, passphrase)
|
||||||
private := encryptArmorPrivKey(priv, passphrase)
|
// make Info
|
||||||
|
info := newInfo(name, priv.PubKey(), privArmor)
|
||||||
|
|
||||||
// Write them both
|
// write them both
|
||||||
kb.db.SetSync(pubName(name), public.bytes())
|
kb.db.SetSync(infoKey(name), info.bytes())
|
||||||
kb.db.SetSync(privName(name), []byte(private))
|
return info
|
||||||
|
|
||||||
return public
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: use a `type TypeKeyAlgo string` (?)
|
func generate(algo CryptoAlgo, secret []byte) (crypto.PrivKey, error) {
|
||||||
func generate(algo string, secret []byte) (crypto.PrivKey, error) {
|
|
||||||
switch algo {
|
switch algo {
|
||||||
case crypto.NameEd25519:
|
case AlgoEd25519:
|
||||||
return crypto.GenPrivKeyEd25519FromSecret(secret).Wrap(), nil
|
return crypto.GenPrivKeyEd25519FromSecret(secret), nil
|
||||||
case crypto.NameSecp256k1:
|
case AlgoSecp256k1:
|
||||||
return crypto.GenPrivKeySecp256k1FromSecret(secret).Wrap(), nil
|
return crypto.GenPrivKeySecp256k1FromSecret(secret), nil
|
||||||
case nano.NameLedgerEd25519:
|
|
||||||
return nano.NewPrivKeyLedgerEd25519()
|
|
||||||
default:
|
default:
|
||||||
err := errors.Errorf("Cannot generate keys for algorithm: %s", algo)
|
err := errors.Errorf("Cannot generate keys for algorithm: %s", algo)
|
||||||
return crypto.PrivKey{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pubName(name string) []byte {
|
func infoKey(name string) []byte {
|
||||||
return []byte(fmt.Sprintf("%s.pub", name))
|
return []byte(fmt.Sprintf("%s.info", name))
|
||||||
}
|
|
||||||
|
|
||||||
func privName(name string) []byte {
|
|
||||||
return []byte(fmt.Sprintf("%s.priv", name))
|
|
||||||
}
|
|
||||||
|
|
||||||
func isPub(name []byte) bool {
|
|
||||||
return strings.HasSuffix(string(name), ".pub")
|
|
||||||
}
|
}
|
||||||
|
@ -2,24 +2,20 @@ package keys_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
asrt "github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
rqr "github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
|
||||||
dbm "github.com/tendermint/tmlibs/db"
|
dbm "github.com/tendermint/tmlibs/db"
|
||||||
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
"github.com/tendermint/go-crypto/keys"
|
"github.com/tendermint/go-crypto/keys"
|
||||||
"github.com/tendermint/go-crypto/keys/words"
|
"github.com/tendermint/go-crypto/keys/words"
|
||||||
"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 := asrt.New(t), rqr.New(t)
|
|
||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := keys.New(
|
cstore := keys.New(
|
||||||
@ -27,84 +23,83 @@ func TestKeyManagement(t *testing.T) {
|
|||||||
words.MustLoadCodec("english"),
|
words.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
|
|
||||||
algo := crypto.NameEd25519
|
algo := keys.AlgoEd25519
|
||||||
n1, n2, n3 := "personal", "business", "other"
|
n1, n2, n3 := "personal", "business", "other"
|
||||||
p1, p2 := "1234", "really-secure!@#$"
|
p1, p2 := "1234", "really-secure!@#$"
|
||||||
|
|
||||||
// Check empty state
|
// Check empty state
|
||||||
l, err := cstore.List()
|
l, err := cstore.List()
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
assert.Empty(l)
|
assert.Empty(t, l)
|
||||||
|
|
||||||
// create some keys
|
// create some keys
|
||||||
_, err = cstore.Get(n1)
|
_, err = cstore.Get(n1)
|
||||||
assert.NotNil(err)
|
assert.NotNil(t, err)
|
||||||
_, i, err := cstore.Create(n1, p1, algo)
|
i, _, err := cstore.Create(n1, p1, algo)
|
||||||
require.Equal(n1, i.Name)
|
require.Equal(t, n1, i.Name)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
_, _, err = cstore.Create(n2, p2, algo)
|
_, _, err = cstore.Create(n2, p2, algo)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
// we can get these keys
|
// we can get these keys
|
||||||
i2, err := cstore.Get(n2)
|
i2, err := cstore.Get(n2)
|
||||||
assert.Nil(err)
|
assert.Nil(t, err)
|
||||||
_, err = cstore.Get(n3)
|
_, err = cstore.Get(n3)
|
||||||
assert.NotNil(err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
// list shows them in order
|
// list shows them in order
|
||||||
keyS, err := cstore.List()
|
keyS, err := cstore.List()
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
require.Equal(2, len(keyS))
|
require.Equal(t, 2, len(keyS))
|
||||||
// note these are in alphabetical order
|
// note these are in alphabetical order
|
||||||
assert.Equal(n2, keyS[0].Name)
|
assert.Equal(t, n2, keyS[0].Name)
|
||||||
assert.Equal(n1, keyS[1].Name)
|
assert.Equal(t, n1, keyS[1].Name)
|
||||||
assert.Equal(i2.PubKey, keyS[0].PubKey)
|
assert.Equal(t, i2.PubKey, keyS[0].PubKey)
|
||||||
|
|
||||||
// deleting a key removes it
|
// deleting a key removes it
|
||||||
err = cstore.Delete("bad name", "foo")
|
err = cstore.Delete("bad name", "foo")
|
||||||
require.NotNil(err)
|
require.NotNil(t, err)
|
||||||
err = cstore.Delete(n1, p1)
|
err = cstore.Delete(n1, p1)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
keyS, err = cstore.List()
|
keyS, err = cstore.List()
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
assert.Equal(1, len(keyS))
|
assert.Equal(t, 1, len(keyS))
|
||||||
_, err = cstore.Get(n1)
|
_, err = cstore.Get(n1)
|
||||||
assert.NotNil(err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
// make sure that it only signs with the right password
|
// make sure that it only signs with the right password
|
||||||
// tx := mock.NewSig([]byte("mytransactiondata"))
|
// tx := mock.NewSig([]byte("mytransactiondata"))
|
||||||
// err = cstore.Sign(n2, p1, tx)
|
// err = cstore.Sign(n2, p1, tx)
|
||||||
// assert.NotNil(err)
|
// assert.NotNil(t, err)
|
||||||
// err = cstore.Sign(n2, p2, tx)
|
// err = cstore.Sign(n2, p2, tx)
|
||||||
// assert.Nil(err, "%+v", err)
|
// assert.Nil(t, err, "%+v", err)
|
||||||
// sigs, err := tx.Signers()
|
// sigs, err := tx.Signers()
|
||||||
// assert.Nil(err, "%+v", err)
|
// assert.Nil(t, err, "%+v", err)
|
||||||
// if assert.Equal(1, len(sigs)) {
|
// if assert.Equal(t, 1, len(sigs)) {
|
||||||
// assert.Equal(i2.PubKey, sigs[0])
|
// assert.Equal(t, i2.PubKey, sigs[0])
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 := asrt.New(t), rqr.New(t)
|
|
||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := keys.New(
|
cstore := keys.New(
|
||||||
dbm.NewMemDB(),
|
dbm.NewMemDB(),
|
||||||
words.MustLoadCodec("english"),
|
words.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
algo := crypto.NameSecp256k1
|
algo := keys.AlgoSecp256k1
|
||||||
|
|
||||||
n1, n2 := "some dude", "a dudette"
|
n1, n2 := "some dude", "a dudette"
|
||||||
p1, p2 := "1234", "foobar"
|
p1, p2 := "1234", "foobar"
|
||||||
|
|
||||||
// create two users and get their info
|
// create two users and get their info
|
||||||
_, i1, err := cstore.Create(n1, p1, algo)
|
i1, _, err := cstore.Create(n1, p1, algo)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
_, i2, err := cstore.Create(n2, p2, algo)
|
i2, _, err := cstore.Create(n2, p2, algo)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
// let's try to sign some messages
|
// let's try to sign some messages
|
||||||
d1 := []byte("my first message")
|
d1 := []byte("my first message")
|
||||||
@ -112,20 +107,20 @@ func TestSignVerify(t *testing.T) {
|
|||||||
|
|
||||||
// try signing both data with both keys...
|
// try signing both data with both keys...
|
||||||
s11, pub1, err := cstore.Sign(n1, p1, d1)
|
s11, pub1, err := cstore.Sign(n1, p1, d1)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
require.Equal(i1.PubKey, pub1)
|
require.Equal(t, i1.PubKey, pub1)
|
||||||
|
|
||||||
s12, pub1, err := cstore.Sign(n1, p1, d2)
|
s12, pub1, err := cstore.Sign(n1, p1, d2)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
require.Equal(i1.PubKey, pub1)
|
require.Equal(t, i1.PubKey, pub1)
|
||||||
|
|
||||||
s21, pub2, err := cstore.Sign(n2, p2, d1)
|
s21, pub2, err := cstore.Sign(n2, p2, d1)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
require.Equal(i2.PubKey, pub2)
|
require.Equal(t, i2.PubKey, pub2)
|
||||||
|
|
||||||
s22, pub2, err := cstore.Sign(n2, p2, d2)
|
s22, pub2, err := cstore.Sign(n2, p2, d2)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
require.Equal(i2.PubKey, pub2)
|
require.Equal(t, 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 {
|
||||||
@ -148,17 +143,17 @@ func TestSignVerify(t *testing.T) {
|
|||||||
|
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
valid := tc.key.VerifyBytes(tc.data, tc.sig)
|
valid := tc.key.VerifyBytes(tc.data, tc.sig)
|
||||||
assert.Equal(tc.valid, valid, "%d", i)
|
assert.Equal(t, tc.valid, valid, "%d", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// TestSignWithLedger makes sure we have ledger compatibility with
|
// TestSignWithLedger makes sure we have ledger compatibility with
|
||||||
// the crypto store.
|
// the crypto store.
|
||||||
//
|
//
|
||||||
// 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 := 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")
|
||||||
}
|
}
|
||||||
@ -172,19 +167,19 @@ func TestSignWithLedger(t *testing.T) {
|
|||||||
p := "hard2hack"
|
p := "hard2hack"
|
||||||
|
|
||||||
// create a nano user
|
// create a nano user
|
||||||
_, c, err := cstore.Create(n, p, nano.NameLedgerEd25519)
|
c, _, err := cstore.Create(n, p, nano.KeyLedgerEd25519)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
assert.Equal(c.Name, n)
|
assert.Equal(t, c.Key, n)
|
||||||
_, ok := c.PubKey.Unwrap().(nano.PubKeyLedgerEd25519)
|
_, ok := c.PubKey.Unwrap().(nano.PubKeyLedgerEd25519)
|
||||||
require.True(ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
// make sure we can get it back
|
// make sure we can get it back
|
||||||
info, err := cstore.Get(n)
|
info, err := cstore.Get(n)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
assert.Equal(info.Name, n)
|
assert.Equal(t, info.Key, n)
|
||||||
key := info.PubKey
|
key := info.PubKey
|
||||||
require.False(key.Empty())
|
require.False(t ,key.Empty())
|
||||||
require.True(key.Equals(c.PubKey))
|
require.True(t, key.Equals(c.PubKey))
|
||||||
|
|
||||||
// let's try to sign some messages
|
// let's try to sign some messages
|
||||||
d1 := []byte("welcome to cosmos")
|
d1 := []byte("welcome to cosmos")
|
||||||
@ -192,56 +187,64 @@ func TestSignWithLedger(t *testing.T) {
|
|||||||
|
|
||||||
// try signing both data with the ledger...
|
// try signing both data with the ledger...
|
||||||
s1, pub, err := cstore.Sign(n, p, d1)
|
s1, pub, err := cstore.Sign(n, p, d1)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
require.Equal(info.PubKey, pub)
|
require.Equal(t, info.PubKey, pub)
|
||||||
|
|
||||||
s2, pub, err := cstore.Sign(n, p, d2)
|
s2, pub, err := cstore.Sign(n, p, d2)
|
||||||
require.Nil(err)
|
require.Nil(t, err)
|
||||||
require.Equal(info.PubKey, pub)
|
require.Equal(t, info.PubKey, pub)
|
||||||
|
|
||||||
// now, let's check those signatures work
|
// now, let's check those signatures work
|
||||||
assert.True(key.VerifyBytes(d1, s1))
|
assert.True(t, key.VerifyBytes(d1, s1))
|
||||||
assert.True(key.VerifyBytes(d2, s2))
|
assert.True(t, key.VerifyBytes(d2, s2))
|
||||||
// and mismatched signatures don't
|
// and mismatched signatures don't
|
||||||
assert.False(key.VerifyBytes(d1, s2))
|
assert.False(t, key.VerifyBytes(d1, s2))
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func assertPassword(assert *asrt.Assertions, cstore keys.Keybase, name, pass, badpass string) {
|
func assertPassword(t *testing.T, cstore keys.Keybase, name, pass, badpass string) {
|
||||||
err := cstore.Update(name, badpass, pass)
|
err := cstore.Update(name, badpass, pass)
|
||||||
assert.NotNil(err)
|
assert.NotNil(t, err)
|
||||||
err = cstore.Update(name, pass, pass)
|
err = cstore.Update(name, pass, pass)
|
||||||
assert.Nil(err, "%+v", err)
|
assert.Nil(t, err, "%+v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestImportUnencrypted tests accepting raw priv keys bytes as input
|
// TestExportImport tests exporting and importing keys.
|
||||||
func TestImportUnencrypted(t *testing.T) {
|
func TestExportImport(t *testing.T) {
|
||||||
require := rqr.New(t)
|
|
||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
|
db := dbm.NewMemDB()
|
||||||
cstore := keys.New(
|
cstore := keys.New(
|
||||||
dbm.NewMemDB(),
|
db,
|
||||||
words.MustLoadCodec("english"),
|
words.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
|
|
||||||
key := crypto.GenPrivKeyEd25519FromSecret(cmn.RandBytes(16)).Wrap()
|
info, _, err := cstore.Create("john", "passphrase", keys.AlgoEd25519)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, info.Name, "john")
|
||||||
|
addr := info.PubKey.Address()
|
||||||
|
|
||||||
addr := key.PubKey().Address()
|
john, err := cstore.Get("john")
|
||||||
name := "john"
|
assert.Nil(t, err)
|
||||||
pass := "top-secret"
|
assert.Equal(t, john.Name, "john")
|
||||||
|
assert.Equal(t, john.PubKey.Address(), addr)
|
||||||
|
|
||||||
// import raw bytes
|
armor, err := cstore.Export("john")
|
||||||
err := cstore.Import(name, pass, "", key.Bytes())
|
assert.Nil(t, err)
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
|
|
||||||
// make sure the address matches
|
err = cstore.Import("john2", armor)
|
||||||
info, err := cstore.Get(name)
|
assert.Nil(t, err)
|
||||||
require.Nil(err, "%+v", err)
|
|
||||||
require.EqualValues(addr, info.Address())
|
john2, err := cstore.Get("john2")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, john.PubKey.Address(), addr)
|
||||||
|
assert.Equal(t, john.Name, "john")
|
||||||
|
assert.Equal(t, john, john2)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAdvancedKeyManagement verifies update, import, export functionality
|
// TestAdvancedKeyManagement verifies update, import, export functionality
|
||||||
func TestAdvancedKeyManagement(t *testing.T) {
|
func TestAdvancedKeyManagement(t *testing.T) {
|
||||||
assert, require := asrt.New(t), rqr.New(t)
|
|
||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := keys.New(
|
cstore := keys.New(
|
||||||
@ -249,42 +252,49 @@ func TestAdvancedKeyManagement(t *testing.T) {
|
|||||||
words.MustLoadCodec("english"),
|
words.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
|
|
||||||
algo := crypto.NameSecp256k1
|
algo := keys.AlgoSecp256k1
|
||||||
n1, n2 := "old-name", "new name"
|
n1, n2 := "old-name", "new name"
|
||||||
p1, p2, p3, pt := "1234", "foobar", "ding booms!", "really-secure!@#$"
|
p1, p2 := "1234", "foobar"
|
||||||
|
|
||||||
// make sure key works with initial password
|
// make sure key works with initial password
|
||||||
_, _, err := cstore.Create(n1, p1, algo)
|
_, _, err := cstore.Create(n1, p1, algo)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
assertPassword(assert, cstore, n1, p1, p2)
|
assertPassword(t, cstore, n1, p1, p2)
|
||||||
|
|
||||||
// update password requires the existing password
|
// update password requires the existing password
|
||||||
err = cstore.Update(n1, "jkkgkg", p2)
|
err = cstore.Update(n1, "jkkgkg", p2)
|
||||||
assert.NotNil(err)
|
assert.NotNil(t, err)
|
||||||
assertPassword(assert, cstore, n1, p1, p2)
|
assertPassword(t, cstore, n1, p1, p2)
|
||||||
|
|
||||||
// then it changes the password when correct
|
// then it changes the password when correct
|
||||||
err = cstore.Update(n1, p1, p2)
|
err = cstore.Update(n1, p1, p2)
|
||||||
assert.Nil(err)
|
assert.Nil(t, err)
|
||||||
// p2 is now the proper one!
|
// p2 is now the proper one!
|
||||||
assertPassword(assert, cstore, n1, p2, p1)
|
assertPassword(t, cstore, n1, p2, p1)
|
||||||
|
|
||||||
// exporting requires the proper name and passphrase
|
// exporting requires the proper name and passphrase
|
||||||
_, err = cstore.Export(n2, p2, pt)
|
_, err = cstore.Export(n1 + ".notreal")
|
||||||
assert.NotNil(err)
|
assert.NotNil(t, err)
|
||||||
_, err = cstore.Export(n1, p1, pt)
|
_, err = cstore.Export(" " + n1)
|
||||||
assert.NotNil(err)
|
assert.NotNil(t, err)
|
||||||
exported, err := cstore.Export(n1, p2, pt)
|
_, err = cstore.Export(n1 + " ")
|
||||||
require.Nil(err, "%+v", err)
|
assert.NotNil(t, err)
|
||||||
|
_, err = cstore.Export("")
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
exported, err := cstore.Export(n1)
|
||||||
|
require.Nil(t, err, "%+v", err)
|
||||||
|
|
||||||
// import fails on bad transfer pass
|
// import succeeds
|
||||||
err = cstore.Import(n2, p3, p2, exported)
|
err = cstore.Import(n2, exported)
|
||||||
assert.NotNil(err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
// second import fails
|
||||||
|
err = cstore.Import(n2, exported)
|
||||||
|
assert.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 := asrt.New(t), rqr.New(t)
|
|
||||||
|
|
||||||
// make the storage with reasonable defaults
|
// make the storage with reasonable defaults
|
||||||
cstore := keys.New(
|
cstore := keys.New(
|
||||||
@ -292,28 +302,28 @@ func TestSeedPhrase(t *testing.T) {
|
|||||||
words.MustLoadCodec("english"),
|
words.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
|
|
||||||
algo := crypto.NameEd25519
|
algo := keys.AlgoEd25519
|
||||||
n1, n2 := "lost-key", "found-again"
|
n1, n2 := "lost-key", "found-again"
|
||||||
p1, p2 := "1234", "foobar"
|
p1, p2 := "1234", "foobar"
|
||||||
|
|
||||||
// make sure key works with initial password
|
// make sure key works with initial password
|
||||||
seed, info, err := cstore.Create(n1, p1, algo)
|
info, seed, err := cstore.Create(n1, p1, algo)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
assert.Equal(n1, info.Name)
|
assert.Equal(t, n1, info.Name)
|
||||||
assert.NotEmpty(seed)
|
assert.NotEmpty(t, seed)
|
||||||
|
|
||||||
// now, let us delete this key
|
// now, let us delete this key
|
||||||
err = cstore.Delete(n1, p1)
|
err = cstore.Delete(n1, p1)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
_, err = cstore.Get(n1)
|
_, err = cstore.Get(n1)
|
||||||
require.NotNil(err)
|
require.NotNil(t, err)
|
||||||
|
|
||||||
// let us re-create it from the seed-phrase
|
// let us re-create it from the seed-phrase
|
||||||
newInfo, err := cstore.Recover(n2, p2, algo, seed)
|
newInfo, err := cstore.Recover(n2, p2, seed)
|
||||||
require.Nil(err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
assert.Equal(n2, newInfo.Name)
|
assert.Equal(t, n2, newInfo.Name)
|
||||||
assert.Equal(info.Address(), newInfo.Address())
|
assert.Equal(t, info.Address(), newInfo.Address())
|
||||||
assert.Equal(info.PubKey, newInfo.PubKey)
|
assert.Equal(t, info.PubKey, newInfo.PubKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleNew() {
|
func ExampleNew() {
|
||||||
@ -322,11 +332,11 @@ func ExampleNew() {
|
|||||||
dbm.NewMemDB(),
|
dbm.NewMemDB(),
|
||||||
words.MustLoadCodec("english"),
|
words.MustLoadCodec("english"),
|
||||||
)
|
)
|
||||||
ed := crypto.NameEd25519
|
ed := keys.AlgoEd25519
|
||||||
sec := crypto.NameSecp256k1
|
sec := keys.AlgoSecp256k1
|
||||||
|
|
||||||
// Add keys and see they return in alphabetical order
|
// Add keys and see they return in alphabetical order
|
||||||
_, bob, err := cstore.Create("Bob", "friend", ed)
|
bob, _, err := cstore.Create("Bob", "friend", ed)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// this should never happen
|
// this should never happen
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
32
keys/keys.go
Normal file
32
keys/keys.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package keys
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type CryptoAlgo string
|
||||||
|
|
||||||
|
const (
|
||||||
|
AlgoEd25519 = CryptoAlgo("ed25519")
|
||||||
|
AlgoSecp256k1 = CryptoAlgo("secp256k1")
|
||||||
|
)
|
||||||
|
|
||||||
|
func cryptoAlgoToByte(key CryptoAlgo) byte {
|
||||||
|
switch key {
|
||||||
|
case AlgoEd25519:
|
||||||
|
return 0x01
|
||||||
|
case AlgoSecp256k1:
|
||||||
|
return 0x02
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unexpected type key %v", key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func byteToCryptoAlgo(b byte) CryptoAlgo {
|
||||||
|
switch b {
|
||||||
|
case 0x01:
|
||||||
|
return AlgoEd25519
|
||||||
|
case 0x02:
|
||||||
|
return AlgoSecp256k1
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("Unexpected type byte %X", b))
|
||||||
|
}
|
||||||
|
}
|
@ -12,8 +12,34 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
blockTypePrivKey = "TENDERMINT PRIVATE KEY"
|
blockTypePrivKey = "TENDERMINT PRIVATE KEY"
|
||||||
|
blockTypeKeyInfo = "TENDERMINT KEY INFO"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func armorInfoBytes(bz []byte) string {
|
||||||
|
header := map[string]string{
|
||||||
|
"type": "Info",
|
||||||
|
"version": "0.0.0",
|
||||||
|
}
|
||||||
|
armorStr := crypto.EncodeArmor(blockTypeKeyInfo, header, bz)
|
||||||
|
return armorStr
|
||||||
|
}
|
||||||
|
|
||||||
|
func unarmorInfoBytes(armorStr string) (bz []byte, err error) {
|
||||||
|
blockType, header, bz, err := crypto.DecodeArmor(armorStr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if blockType != blockTypeKeyInfo {
|
||||||
|
err = fmt.Errorf("Unrecognized armor type: %v", blockType)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if header["version"] != "0.0.0" {
|
||||||
|
err = fmt.Errorf("Unrecognized version: %v", header["version"])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func encryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string {
|
func encryptArmorPrivKey(privKey crypto.PrivKey, passphrase string) string {
|
||||||
saltBytes, encBytes := encryptPrivKey(privKey, passphrase)
|
saltBytes, encBytes := encryptPrivKey(privKey, passphrase)
|
||||||
header := map[string]string{
|
header := map[string]string{
|
||||||
|
@ -1,15 +1,39 @@
|
|||||||
package keys
|
package keys
|
||||||
|
|
||||||
import (
|
import (
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
|
|
||||||
crypto "github.com/tendermint/go-crypto"
|
crypto "github.com/tendermint/go-crypto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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, crypto.PubKey, error)
|
||||||
|
// Create a new keypair
|
||||||
|
Create(name, passphrase string, algo CryptoAlgo) (info Info, seed string, err error)
|
||||||
|
// Recover takes a seedphrase and loads in the key
|
||||||
|
Recover(name, passphrase, seedphrase string) (info Info, erro error)
|
||||||
|
List() ([]Info, error)
|
||||||
|
Get(name string) (Info, error)
|
||||||
|
Update(name, oldpass, newpass string) error
|
||||||
|
Delete(name, passphrase string) error
|
||||||
|
|
||||||
|
Import(name string, armor string) (err error)
|
||||||
|
Export(name string) (armor string, err 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"`
|
||||||
PubKey crypto.PubKey `json:"pubkey"`
|
PubKey crypto.PubKey `json:"pubkey"`
|
||||||
|
PrivKeyArmor string `json:"privkey.armor"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInfo(name string, pub crypto.PubKey, privArmor string) Info {
|
||||||
|
return Info{
|
||||||
|
Name: name,
|
||||||
|
PubKey: pub,
|
||||||
|
PrivKeyArmor: privArmor,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Address is a helper function to calculate the address from the pubkey
|
// Address is a helper function to calculate the address from the pubkey
|
||||||
@ -18,31 +42,14 @@ func (i Info) Address() []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (i Info) bytes() []byte {
|
func (i Info) bytes() []byte {
|
||||||
return wire.BinaryBytes(i)
|
bz, err := cdc.MarshalBinary(i)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return bz
|
||||||
}
|
}
|
||||||
|
|
||||||
func readInfo(bs []byte) (info Info, err error) {
|
func readInfo(bz []byte) (info Info, err error) {
|
||||||
err = wire.ReadBinaryBytes(bs, &info)
|
err = cdc.UnmarshalBinary(bz, &info)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func info(name string, privKey crypto.PrivKey) Info {
|
|
||||||
return Info{
|
|
||||||
Name: name,
|
|
||||||
PubKey: privKey.PubKey(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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, crypto.PubKey, error)
|
|
||||||
// Create a new keypair
|
|
||||||
Create(name, passphrase, algo string) (seedphrase string, _ Info, _ error)
|
|
||||||
// Recover takes a seedphrase and loads in the key
|
|
||||||
Recover(name, passphrase, algo, seedphrase string) (Info, error)
|
|
||||||
List() ([]Info, error)
|
|
||||||
Get(name string) (Info, error)
|
|
||||||
Update(name, oldpass, newpass string) error
|
|
||||||
Delete(name, passphrase string) error
|
|
||||||
}
|
|
||||||
|
12
keys/wire.go
Normal file
12
keys/wire.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package keys
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tendermint/go-crypto"
|
||||||
|
"github.com/tendermint/go-wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cdc = wire.NewCodec()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
crypto.RegisterWire(cdc)
|
||||||
|
}
|
88
priv_key.go
88
priv_key.go
@ -7,89 +7,60 @@ import (
|
|||||||
"github.com/tendermint/ed25519"
|
"github.com/tendermint/ed25519"
|
||||||
"github.com/tendermint/ed25519/extra25519"
|
"github.com/tendermint/ed25519/extra25519"
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
data "github.com/tendermint/go-wire/data"
|
|
||||||
. "github.com/tendermint/tmlibs/common"
|
. "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) {
|
func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) {
|
||||||
err = wire.ReadBinaryBytes(privKeyBytes, &privKey)
|
err = cdc.UnmarshalBinary(privKeyBytes, &privKey)
|
||||||
if err == nil {
|
|
||||||
// add support for a ValidateKey method on PrivKeys
|
|
||||||
// to make sure they load correctly
|
|
||||||
val, ok := privKey.Unwrap().(validatable)
|
|
||||||
if ok {
|
|
||||||
err = val.ValidateKey()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// validatable is an optional interface for keys that want to
|
|
||||||
// check integrity
|
|
||||||
type validatable interface {
|
|
||||||
ValidateKey() error
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
|
||||||
// DO NOT USE THIS INTERFACE.
|
type PrivKey interface {
|
||||||
// You probably want to use PrivKey
|
|
||||||
// +gen wrapper:"PrivKey,Impl[PrivKeyEd25519,PrivKeySecp256k1],ed25519,secp256k1"
|
|
||||||
type PrivKeyInner interface {
|
|
||||||
AssertIsPrivKeyInner()
|
|
||||||
Bytes() []byte
|
Bytes() []byte
|
||||||
Sign(msg []byte) Signature
|
Sign(msg []byte) Signature
|
||||||
PubKey() PubKey
|
PubKey() PubKey
|
||||||
Equals(PrivKey) bool
|
Equals(PrivKey) bool
|
||||||
Wrap() PrivKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
var _ PrivKeyInner = PrivKeyEd25519{}
|
var _ PrivKey = PrivKeyEd25519{}
|
||||||
|
|
||||||
// Implements PrivKey
|
// Implements PrivKey
|
||||||
type PrivKeyEd25519 [64]byte
|
type PrivKeyEd25519 [64]byte
|
||||||
|
|
||||||
func (privKey PrivKeyEd25519) AssertIsPrivKeyInner() {}
|
|
||||||
|
|
||||||
func (privKey PrivKeyEd25519) Bytes() []byte {
|
func (privKey PrivKeyEd25519) Bytes() []byte {
|
||||||
return wire.BinaryBytes(PrivKey{privKey})
|
bz, err := cdc.MarshalBinary(privKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return bz
|
||||||
}
|
}
|
||||||
|
|
||||||
func (privKey PrivKeyEd25519) Sign(msg []byte) Signature {
|
func (privKey PrivKeyEd25519) Sign(msg []byte) Signature {
|
||||||
privKeyBytes := [64]byte(privKey)
|
privKeyBytes := [64]byte(privKey)
|
||||||
signatureBytes := ed25519.Sign(&privKeyBytes, msg)
|
signatureBytes := ed25519.Sign(&privKeyBytes, msg)
|
||||||
return SignatureEd25519(*signatureBytes).Wrap()
|
return SignatureEd25519(*signatureBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (privKey PrivKeyEd25519) PubKey() PubKey {
|
func (privKey PrivKeyEd25519) PubKey() PubKey {
|
||||||
privKeyBytes := [64]byte(privKey)
|
privKeyBytes := [64]byte(privKey)
|
||||||
pubBytes := *ed25519.MakePublicKey(&privKeyBytes)
|
pubBytes := *ed25519.MakePublicKey(&privKeyBytes)
|
||||||
return PubKeyEd25519(pubBytes).Wrap()
|
return PubKeyEd25519(pubBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals - you probably don't need to use this.
|
// Equals - you probably don't need to use this.
|
||||||
// Runs in constant time based on length of the keys.
|
// Runs in constant time based on length of the keys.
|
||||||
func (privKey PrivKeyEd25519) Equals(other PrivKey) bool {
|
func (privKey PrivKeyEd25519) Equals(other PrivKey) bool {
|
||||||
if otherEd, ok := other.Unwrap().(PrivKeyEd25519); ok {
|
if otherEd, ok := other.(PrivKeyEd25519); ok {
|
||||||
return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1
|
return subtle.ConstantTimeCompare(privKey[:], otherEd[:]) == 1
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PrivKeyEd25519) MarshalJSON() ([]byte, error) {
|
|
||||||
return data.Encoder.Marshal(p[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PrivKeyEd25519) UnmarshalJSON(enc []byte) error {
|
|
||||||
var ref []byte
|
|
||||||
err := data.Encoder.Unmarshal(&ref, enc)
|
|
||||||
copy(p[:], ref)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte {
|
func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte {
|
||||||
keyCurve25519 := new([32]byte)
|
keyCurve25519 := new([32]byte)
|
||||||
privKeyBytes := [64]byte(privKey)
|
privKeyBytes := [64]byte(privKey)
|
||||||
@ -97,16 +68,22 @@ func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte {
|
|||||||
return keyCurve25519
|
return keyCurve25519
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
func (privKey PrivKeyEd25519) String() string {
|
func (privKey PrivKeyEd25519) String() string {
|
||||||
return Fmt("PrivKeyEd25519{*****}")
|
return Fmt("PrivKeyEd25519{*****}")
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
// Deterministically generates new priv-key bytes from key.
|
// Deterministically generates new priv-key bytes from key.
|
||||||
func (privKey PrivKeyEd25519) Generate(index int) PrivKeyEd25519 {
|
func (privKey PrivKeyEd25519) Generate(index int) PrivKeyEd25519 {
|
||||||
newBytes := wire.BinarySha256(struct {
|
bz, err := wire.MarshalBinary(struct {
|
||||||
PrivKey [64]byte
|
PrivKey [64]byte
|
||||||
Index int
|
Index int
|
||||||
}{privKey, index})
|
}{privKey, index})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
newBytes := Sha256(bz)
|
||||||
var newKey [64]byte
|
var newKey [64]byte
|
||||||
copy(newKey[:], newBytes)
|
copy(newKey[:], newBytes)
|
||||||
return PrivKeyEd25519(newKey)
|
return PrivKeyEd25519(newKey)
|
||||||
@ -131,15 +108,17 @@ func GenPrivKeyEd25519FromSecret(secret []byte) PrivKeyEd25519 {
|
|||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
var _ PrivKeyInner = PrivKeySecp256k1{}
|
var _ PrivKey = PrivKeySecp256k1{}
|
||||||
|
|
||||||
// Implements PrivKey
|
// Implements PrivKey
|
||||||
type PrivKeySecp256k1 [32]byte
|
type PrivKeySecp256k1 [32]byte
|
||||||
|
|
||||||
func (privKey PrivKeySecp256k1) AssertIsPrivKeyInner() {}
|
|
||||||
|
|
||||||
func (privKey PrivKeySecp256k1) Bytes() []byte {
|
func (privKey PrivKeySecp256k1) Bytes() []byte {
|
||||||
return wire.BinaryBytes(PrivKey{privKey})
|
bz, err := cdc.MarshalBinary(privKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return bz
|
||||||
}
|
}
|
||||||
|
|
||||||
func (privKey PrivKeySecp256k1) Sign(msg []byte) Signature {
|
func (privKey PrivKeySecp256k1) Sign(msg []byte) Signature {
|
||||||
@ -148,40 +127,31 @@ func (privKey PrivKeySecp256k1) Sign(msg []byte) Signature {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
PanicSanity(err)
|
PanicSanity(err)
|
||||||
}
|
}
|
||||||
return SignatureSecp256k1(sig__.Serialize()).Wrap()
|
return SignatureSecp256k1(sig__.Serialize())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (privKey PrivKeySecp256k1) PubKey() PubKey {
|
func (privKey PrivKeySecp256k1) PubKey() PubKey {
|
||||||
_, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:])
|
_, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:])
|
||||||
var pub PubKeySecp256k1
|
var pub PubKeySecp256k1
|
||||||
copy(pub[:], pub__.SerializeCompressed())
|
copy(pub[:], pub__.SerializeCompressed())
|
||||||
return pub.Wrap()
|
return pub
|
||||||
}
|
}
|
||||||
|
|
||||||
// Equals - you probably don't need to use this.
|
// Equals - you probably don't need to use this.
|
||||||
// Runs in constant time based on length of the keys.
|
// Runs in constant time based on length of the keys.
|
||||||
func (privKey PrivKeySecp256k1) Equals(other PrivKey) bool {
|
func (privKey PrivKeySecp256k1) Equals(other PrivKey) bool {
|
||||||
if otherSecp, ok := other.Unwrap().(PrivKeySecp256k1); ok {
|
if otherSecp, ok := other.(PrivKeySecp256k1); ok {
|
||||||
return subtle.ConstantTimeCompare(privKey[:], otherSecp[:]) == 1
|
return subtle.ConstantTimeCompare(privKey[:], otherSecp[:]) == 1
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PrivKeySecp256k1) MarshalJSON() ([]byte, error) {
|
/*
|
||||||
return data.Encoder.Marshal(p[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PrivKeySecp256k1) UnmarshalJSON(enc []byte) error {
|
|
||||||
var ref []byte
|
|
||||||
err := data.Encoder.Unmarshal(&ref, enc)
|
|
||||||
copy(p[:], ref)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (privKey PrivKeySecp256k1) String() string {
|
func (privKey PrivKeySecp256k1) String() string {
|
||||||
return Fmt("PrivKeySecp256k1{*****}")
|
return Fmt("PrivKeySecp256k1{*****}")
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// Deterministically generates new priv-key bytes from key.
|
// Deterministically generates new priv-key bytes from key.
|
||||||
|
@ -1,42 +1,26 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
wire "github.com/tendermint/go-wire"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type BadKey struct {
|
type BadKey struct {
|
||||||
PrivKeyEd25519
|
PrivKeyEd25519
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrap fulfils interface for PrivKey struct
|
|
||||||
func (pk BadKey) Wrap() PrivKey {
|
|
||||||
return PrivKey{pk}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pk BadKey) Bytes() []byte {
|
|
||||||
return wire.BinaryBytes(pk.Wrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pk BadKey) ValidateKey() error {
|
|
||||||
return fmt.Errorf("fuggly key")
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
PrivKeyMapper.
|
|
||||||
RegisterImplementation(BadKey{}, "bad", 0x66)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReadPrivKey(t *testing.T) {
|
func TestReadPrivKey(t *testing.T) {
|
||||||
assert, require := assert.New(t), require.New(t)
|
assert, require := assert.New(t), require.New(t)
|
||||||
|
|
||||||
// garbage in, garbage out
|
// garbage in, garbage out
|
||||||
garbage := []byte("hjgewugfbiewgofwgewr")
|
garbage := []byte("hjgewugfbiewgofwgewr")
|
||||||
|
XXX This test wants to register BadKey globally to go-crypto,
|
||||||
|
but we don't want to support that.
|
||||||
_, err := PrivKeyFromBytes(garbage)
|
_, err := PrivKeyFromBytes(garbage)
|
||||||
require.Error(err)
|
require.Error(err)
|
||||||
|
|
||||||
@ -47,13 +31,15 @@ func TestReadPrivKey(t *testing.T) {
|
|||||||
key PrivKey
|
key PrivKey
|
||||||
valid bool
|
valid bool
|
||||||
}{
|
}{
|
||||||
{edKey.Wrap(), true},
|
{edKey, true},
|
||||||
{badKey.Wrap(), false},
|
{badKey, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, tc := range cases {
|
for i, tc := range cases {
|
||||||
data := tc.key.Bytes()
|
data := tc.key.Bytes()
|
||||||
|
fmt.Println(">>>", data)
|
||||||
key, err := PrivKeyFromBytes(data)
|
key, err := PrivKeyFromBytes(data)
|
||||||
|
fmt.Printf("!!! %#v\n", key, err)
|
||||||
if tc.valid {
|
if tc.valid {
|
||||||
assert.NoError(err, "%d", i)
|
assert.NoError(err, "%d", i)
|
||||||
assert.Equal(tc.key, key, "%d", i)
|
assert.Equal(tc.key, key, "%d", i)
|
||||||
@ -61,5 +47,5 @@ func TestReadPrivKey(t *testing.T) {
|
|||||||
assert.Error(err, "%d: %#v", i, key)
|
assert.Error(err, "%d: %#v", i, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
// Generated by: main
|
|
||||||
// TypeWriter: wrapper
|
|
||||||
// Directive: +gen on PrivKeyInner
|
|
||||||
|
|
||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tendermint/go-wire/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Auto-generated adapters for happily unmarshaling interfaces
|
|
||||||
// Apache License 2.0
|
|
||||||
// Copyright (c) 2017 Ethan Frey (ethan.frey@tendermint.com)
|
|
||||||
|
|
||||||
type PrivKey struct {
|
|
||||||
PrivKeyInner "json:\"unwrap\""
|
|
||||||
}
|
|
||||||
|
|
||||||
var PrivKeyMapper = data.NewMapper(PrivKey{})
|
|
||||||
|
|
||||||
func (h PrivKey) MarshalJSON() ([]byte, error) {
|
|
||||||
return PrivKeyMapper.ToJSON(h.PrivKeyInner)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *PrivKey) UnmarshalJSON(data []byte) (err error) {
|
|
||||||
parsed, err := PrivKeyMapper.FromJSON(data)
|
|
||||||
if err == nil && parsed != nil {
|
|
||||||
h.PrivKeyInner = parsed.(PrivKeyInner)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unwrap recovers the concrete interface safely (regardless of levels of embeds)
|
|
||||||
func (h PrivKey) Unwrap() PrivKeyInner {
|
|
||||||
hi := h.PrivKeyInner
|
|
||||||
for wrap, ok := hi.(PrivKey); ok; wrap, ok = hi.(PrivKey) {
|
|
||||||
hi = wrap.PrivKeyInner
|
|
||||||
}
|
|
||||||
return hi
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h PrivKey) Empty() bool {
|
|
||||||
return h.PrivKeyInner == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** below are bindings for each implementation ***/
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
PrivKeyMapper.RegisterImplementation(PrivKeyEd25519{}, "ed25519", 0x1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hi PrivKeyEd25519) Wrap() PrivKey {
|
|
||||||
return PrivKey{hi}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
PrivKeyMapper.RegisterImplementation(PrivKeySecp256k1{}, "secp256k1", 0x2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hi PrivKeySecp256k1) Wrap() PrivKey {
|
|
||||||
return PrivKey{hi}
|
|
||||||
}
|
|
86
pub_key.go
86
pub_key.go
@ -8,8 +8,6 @@ import (
|
|||||||
secp256k1 "github.com/btcsuite/btcd/btcec"
|
secp256k1 "github.com/btcsuite/btcd/btcec"
|
||||||
"github.com/tendermint/ed25519"
|
"github.com/tendermint/ed25519"
|
||||||
"github.com/tendermint/ed25519/extra25519"
|
"github.com/tendermint/ed25519/extra25519"
|
||||||
"github.com/tendermint/go-wire"
|
|
||||||
data "github.com/tendermint/go-wire/data"
|
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
"golang.org/x/crypto/ripemd160"
|
"golang.org/x/crypto/ripemd160"
|
||||||
)
|
)
|
||||||
@ -20,56 +18,44 @@ import (
|
|||||||
type Address = cmn.HexBytes
|
type Address = cmn.HexBytes
|
||||||
|
|
||||||
func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) {
|
func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) {
|
||||||
if err := wire.ReadBinaryBytes(pubKeyBytes, &pubKey); err != nil {
|
err = cdc.UnmarshalBinary(pubKeyBytes, &pubKey)
|
||||||
return PubKey{}, err
|
return
|
||||||
}
|
|
||||||
return pubKey, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
|
||||||
// DO NOT USE THIS INTERFACE.
|
type PubKey interface {
|
||||||
// You probably want to use PubKey
|
|
||||||
// +gen wrapper:"PubKey,Impl[PubKeyEd25519,PubKeySecp256k1],ed25519,secp256k1"
|
|
||||||
type PubKeyInner interface {
|
|
||||||
AssertIsPubKeyInner()
|
|
||||||
Address() Address
|
Address() Address
|
||||||
Bytes() []byte
|
Bytes() []byte
|
||||||
KeyString() string
|
|
||||||
VerifyBytes(msg []byte, sig Signature) bool
|
VerifyBytes(msg []byte, sig Signature) bool
|
||||||
Equals(PubKey) bool
|
Equals(PubKey) bool
|
||||||
Wrap() PubKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
var _ PubKeyInner = PubKeyEd25519{}
|
var _ PubKey = PubKeyEd25519{}
|
||||||
|
|
||||||
// Implements PubKeyInner
|
// Implements PubKeyInner
|
||||||
type PubKeyEd25519 [32]byte
|
type PubKeyEd25519 [32]byte
|
||||||
|
|
||||||
func (pubKey PubKeyEd25519) AssertIsPubKeyInner() {}
|
|
||||||
|
|
||||||
func (pubKey PubKeyEd25519) Address() Address {
|
func (pubKey PubKeyEd25519) Address() Address {
|
||||||
w, n, err := new(bytes.Buffer), new(int), new(error)
|
|
||||||
wire.WriteBinary(pubKey[:], w, n, err)
|
|
||||||
if *err != nil {
|
|
||||||
panic(*err)
|
|
||||||
}
|
|
||||||
// append type byte
|
// append type byte
|
||||||
encodedPubkey := append([]byte{TypeEd25519}, w.Bytes()...)
|
|
||||||
hasher := ripemd160.New()
|
hasher := ripemd160.New()
|
||||||
hasher.Write(encodedPubkey) // does not error
|
hasher.Write(pubKey.Bytes()) // does not error
|
||||||
return Address(hasher.Sum(nil))
|
return Address(hasher.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pubKey PubKeyEd25519) Bytes() []byte {
|
func (pubKey PubKeyEd25519) Bytes() []byte {
|
||||||
return wire.BinaryBytes(PubKey{pubKey})
|
bz, err := cdc.MarshalBinary(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return bz
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool {
|
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool {
|
||||||
// make sure we use the same algorithm to sign
|
// make sure we use the same algorithm to sign
|
||||||
sig, ok := sig_.Unwrap().(SignatureEd25519)
|
sig, ok := sig_.(SignatureEd25519)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -78,17 +64,6 @@ func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool {
|
|||||||
return ed25519.Verify(&pubKeyBytes, msg, &sigBytes)
|
return ed25519.Verify(&pubKeyBytes, msg, &sigBytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PubKeyEd25519) MarshalJSON() ([]byte, error) {
|
|
||||||
return data.Encoder.Marshal(p[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PubKeyEd25519) UnmarshalJSON(enc []byte) error {
|
|
||||||
var ref []byte
|
|
||||||
err := data.Encoder.Unmarshal(&ref, enc)
|
|
||||||
copy(p[:], ref)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// For use with golang/crypto/nacl/box
|
// For use with golang/crypto/nacl/box
|
||||||
// If error, returns nil.
|
// If error, returns nil.
|
||||||
func (pubKey PubKeyEd25519) ToCurve25519() *[32]byte {
|
func (pubKey PubKeyEd25519) ToCurve25519() *[32]byte {
|
||||||
@ -104,14 +79,8 @@ func (pubKey PubKeyEd25519) String() string {
|
|||||||
return fmt.Sprintf("PubKeyEd25519{%X}", pubKey[:])
|
return fmt.Sprintf("PubKeyEd25519{%X}", pubKey[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must return the full bytes in hex.
|
|
||||||
// Used for map keying, etc.
|
|
||||||
func (pubKey PubKeyEd25519) KeyString() string {
|
|
||||||
return fmt.Sprintf("%X", pubKey[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pubKey PubKeyEd25519) Equals(other PubKey) bool {
|
func (pubKey PubKeyEd25519) Equals(other PubKey) bool {
|
||||||
if otherEd, ok := other.Unwrap().(PubKeyEd25519); ok {
|
if otherEd, ok := other.(PubKeyEd25519); ok {
|
||||||
return bytes.Equal(pubKey[:], otherEd[:])
|
return bytes.Equal(pubKey[:], otherEd[:])
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
@ -120,15 +89,13 @@ func (pubKey PubKeyEd25519) Equals(other PubKey) bool {
|
|||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
var _ PubKeyInner = PubKeySecp256k1{}
|
var _ PubKey = PubKeySecp256k1{}
|
||||||
|
|
||||||
// Implements PubKey.
|
// Implements PubKey.
|
||||||
// Compressed pubkey (just the x-cord),
|
// Compressed pubkey (just the x-cord),
|
||||||
// prefixed with 0x02 or 0x03, depending on the y-cord.
|
// prefixed with 0x02 or 0x03, depending on the y-cord.
|
||||||
type PubKeySecp256k1 [33]byte
|
type PubKeySecp256k1 [33]byte
|
||||||
|
|
||||||
func (pubKey PubKeySecp256k1) AssertIsPubKeyInner() {}
|
|
||||||
|
|
||||||
// Implements Bitcoin style addresses: RIPEMD160(SHA256(pubkey))
|
// Implements Bitcoin style addresses: RIPEMD160(SHA256(pubkey))
|
||||||
func (pubKey PubKeySecp256k1) Address() Address {
|
func (pubKey PubKeySecp256k1) Address() Address {
|
||||||
hasherSHA256 := sha256.New()
|
hasherSHA256 := sha256.New()
|
||||||
@ -141,12 +108,16 @@ func (pubKey PubKeySecp256k1) Address() Address {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pubKey PubKeySecp256k1) Bytes() []byte {
|
func (pubKey PubKeySecp256k1) Bytes() []byte {
|
||||||
return wire.BinaryBytes(PubKey{pubKey})
|
bz, err := cdc.MarshalBinary(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return bz
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig_ Signature) bool {
|
func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig_ Signature) bool {
|
||||||
// and assert same algorithm to sign and verify
|
// and assert same algorithm to sign and verify
|
||||||
sig, ok := sig_.Unwrap().(SignatureSecp256k1)
|
sig, ok := sig_.(SignatureSecp256k1)
|
||||||
if !ok {
|
if !ok {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -162,29 +133,12 @@ func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig_ Signature) bool {
|
|||||||
return sig__.Verify(Sha256(msg), pub__)
|
return sig__.Verify(Sha256(msg), pub__)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p PubKeySecp256k1) MarshalJSON() ([]byte, error) {
|
|
||||||
return data.Encoder.Marshal(p[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *PubKeySecp256k1) UnmarshalJSON(enc []byte) error {
|
|
||||||
var ref []byte
|
|
||||||
err := data.Encoder.Unmarshal(&ref, enc)
|
|
||||||
copy(p[:], ref)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pubKey PubKeySecp256k1) String() string {
|
func (pubKey PubKeySecp256k1) String() string {
|
||||||
return fmt.Sprintf("PubKeySecp256k1{%X}", pubKey[:])
|
return fmt.Sprintf("PubKeySecp256k1{%X}", pubKey[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must return the full bytes in hex.
|
|
||||||
// Used for map keying, etc.
|
|
||||||
func (pubKey PubKeySecp256k1) KeyString() string {
|
|
||||||
return fmt.Sprintf("%X", pubKey[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pubKey PubKeySecp256k1) Equals(other PubKey) bool {
|
func (pubKey PubKeySecp256k1) Equals(other PubKey) bool {
|
||||||
if otherSecp, ok := other.Unwrap().(PubKeySecp256k1); ok {
|
if otherSecp, ok := other.(PubKeySecp256k1); ok {
|
||||||
return bytes.Equal(pubKey[:], otherSecp[:])
|
return bytes.Equal(pubKey[:], otherSecp[:])
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
|
@ -33,7 +33,7 @@ func TestPubKeySecp256k1Address(t *testing.T) {
|
|||||||
var priv PrivKeySecp256k1
|
var priv PrivKeySecp256k1
|
||||||
copy(priv[:], privB)
|
copy(priv[:], privB)
|
||||||
|
|
||||||
pubT := priv.PubKey().Unwrap().(PubKeySecp256k1)
|
pubT := priv.PubKey().(PubKeySecp256k1)
|
||||||
pub := pubT[:]
|
pub := pubT[:]
|
||||||
addr := priv.PubKey().Address()
|
addr := priv.PubKey().Address()
|
||||||
|
|
||||||
@ -45,5 +45,5 @@ func TestPubKeySecp256k1Address(t *testing.T) {
|
|||||||
func TestPubKeyInvalidDataProperReturnsEmpty(t *testing.T) {
|
func TestPubKeyInvalidDataProperReturnsEmpty(t *testing.T) {
|
||||||
pk, err := PubKeyFromBytes([]byte("foo"))
|
pk, err := PubKeyFromBytes([]byte("foo"))
|
||||||
require.NotNil(t, err, "expecting a non-nil error")
|
require.NotNil(t, err, "expecting a non-nil error")
|
||||||
require.True(t, pk.Empty(), "expecting an empty public key on error")
|
require.Nil(t, pk, "expecting an empty public key on error")
|
||||||
}
|
}
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
// Generated by: main
|
|
||||||
// TypeWriter: wrapper
|
|
||||||
// Directive: +gen on PubKeyInner
|
|
||||||
|
|
||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tendermint/go-wire/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Auto-generated adapters for happily unmarshaling interfaces
|
|
||||||
// Apache License 2.0
|
|
||||||
// Copyright (c) 2017 Ethan Frey (ethan.frey@tendermint.com)
|
|
||||||
|
|
||||||
type PubKey struct {
|
|
||||||
PubKeyInner "json:\"unwrap\""
|
|
||||||
}
|
|
||||||
|
|
||||||
var PubKeyMapper = data.NewMapper(PubKey{})
|
|
||||||
|
|
||||||
func (h PubKey) MarshalJSON() ([]byte, error) {
|
|
||||||
return PubKeyMapper.ToJSON(h.PubKeyInner)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *PubKey) UnmarshalJSON(data []byte) (err error) {
|
|
||||||
parsed, err := PubKeyMapper.FromJSON(data)
|
|
||||||
if err == nil && parsed != nil {
|
|
||||||
h.PubKeyInner = parsed.(PubKeyInner)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unwrap recovers the concrete interface safely (regardless of levels of embeds)
|
|
||||||
func (h PubKey) Unwrap() PubKeyInner {
|
|
||||||
hi := h.PubKeyInner
|
|
||||||
for wrap, ok := hi.(PubKey); ok; wrap, ok = hi.(PubKey) {
|
|
||||||
hi = wrap.PubKeyInner
|
|
||||||
}
|
|
||||||
return hi
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h PubKey) Empty() bool {
|
|
||||||
return h.PubKeyInner == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** below are bindings for each implementation ***/
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
PubKeyMapper.RegisterImplementation(PubKeyEd25519{}, "ed25519", 0x1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hi PubKeyEd25519) Wrap() PubKey {
|
|
||||||
return PubKey{hi}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
PubKeyMapper.RegisterImplementation(PubKeySecp256k1{}, "secp256k1", 0x2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hi PubKeySecp256k1) Wrap() PubKey {
|
|
||||||
return PubKey{hi}
|
|
||||||
}
|
|
58
signature.go
58
signature.go
@ -4,40 +4,35 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/tendermint/go-wire"
|
|
||||||
data "github.com/tendermint/go-wire/data"
|
|
||||||
. "github.com/tendermint/tmlibs/common"
|
. "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SignatureFromBytes(sigBytes []byte) (sig Signature, err error) {
|
func SignatureFromBytes(pubKeyBytes []byte) (pubKey Signature, err error) {
|
||||||
err = wire.ReadBinaryBytes(sigBytes, &sig)
|
err = cdc.UnmarshalBinary(pubKeyBytes, &pubKey)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
|
||||||
// DO NOT USE THIS INTERFACE.
|
type Signature interface {
|
||||||
// You probably want to use Signature.
|
|
||||||
// +gen wrapper:"Signature,Impl[SignatureEd25519,SignatureSecp256k1],ed25519,secp256k1"
|
|
||||||
type SignatureInner interface {
|
|
||||||
AssertIsSignatureInner()
|
|
||||||
Bytes() []byte
|
Bytes() []byte
|
||||||
IsZero() bool
|
IsZero() bool
|
||||||
Equals(Signature) bool
|
Equals(Signature) bool
|
||||||
Wrap() Signature
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
var _ SignatureInner = SignatureEd25519{}
|
var _ Signature = SignatureEd25519{}
|
||||||
|
|
||||||
// Implements Signature
|
// Implements Signature
|
||||||
type SignatureEd25519 [64]byte
|
type SignatureEd25519 [64]byte
|
||||||
|
|
||||||
func (sig SignatureEd25519) AssertIsSignatureInner() {}
|
|
||||||
|
|
||||||
func (sig SignatureEd25519) Bytes() []byte {
|
func (sig SignatureEd25519) Bytes() []byte {
|
||||||
return wire.BinaryBytes(Signature{sig})
|
bz, err := cdc.MarshalBinary(sig)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return bz
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 }
|
func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 }
|
||||||
@ -45,41 +40,32 @@ func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 }
|
|||||||
func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) }
|
func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) }
|
||||||
|
|
||||||
func (sig SignatureEd25519) Equals(other Signature) bool {
|
func (sig SignatureEd25519) Equals(other Signature) bool {
|
||||||
if otherEd, ok := other.Unwrap().(SignatureEd25519); ok {
|
if otherEd, ok := other.(SignatureEd25519); ok {
|
||||||
return bytes.Equal(sig[:], otherEd[:])
|
return bytes.Equal(sig[:], otherEd[:])
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sig SignatureEd25519) MarshalJSON() ([]byte, error) {
|
|
||||||
return data.Encoder.Marshal(sig[:])
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sig *SignatureEd25519) UnmarshalJSON(enc []byte) error {
|
|
||||||
var ref []byte
|
|
||||||
err := data.Encoder.Unmarshal(&ref, enc)
|
|
||||||
copy(sig[:], ref)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func SignatureEd25519FromBytes(data []byte) Signature {
|
func SignatureEd25519FromBytes(data []byte) Signature {
|
||||||
var sig SignatureEd25519
|
var sig SignatureEd25519
|
||||||
copy(sig[:], data)
|
copy(sig[:], data)
|
||||||
return sig.Wrap()
|
return sig
|
||||||
}
|
}
|
||||||
|
|
||||||
//-------------------------------------
|
//-------------------------------------
|
||||||
|
|
||||||
var _ SignatureInner = SignatureSecp256k1{}
|
var _ Signature = SignatureSecp256k1{}
|
||||||
|
|
||||||
// Implements Signature
|
// Implements Signature
|
||||||
type SignatureSecp256k1 []byte
|
type SignatureSecp256k1 []byte
|
||||||
|
|
||||||
func (sig SignatureSecp256k1) AssertIsSignatureInner() {}
|
|
||||||
|
|
||||||
func (sig SignatureSecp256k1) Bytes() []byte {
|
func (sig SignatureSecp256k1) Bytes() []byte {
|
||||||
return wire.BinaryBytes(Signature{sig})
|
bz, err := cdc.MarshalBinary(sig)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return bz
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sig SignatureSecp256k1) IsZero() bool { return len(sig) == 0 }
|
func (sig SignatureSecp256k1) IsZero() bool { return len(sig) == 0 }
|
||||||
@ -87,17 +73,9 @@ func (sig SignatureSecp256k1) IsZero() bool { return len(sig) == 0 }
|
|||||||
func (sig SignatureSecp256k1) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) }
|
func (sig SignatureSecp256k1) String() string { return fmt.Sprintf("/%X.../", Fingerprint(sig[:])) }
|
||||||
|
|
||||||
func (sig SignatureSecp256k1) Equals(other Signature) bool {
|
func (sig SignatureSecp256k1) Equals(other Signature) bool {
|
||||||
if otherSecp, ok := other.Unwrap().(SignatureSecp256k1); ok {
|
if otherSecp, ok := other.(SignatureSecp256k1); ok {
|
||||||
return bytes.Equal(sig[:], otherSecp[:])
|
return bytes.Equal(sig[:], otherSecp[:])
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sig SignatureSecp256k1) MarshalJSON() ([]byte, error) {
|
|
||||||
return data.Encoder.Marshal(sig)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sig *SignatureSecp256k1) UnmarshalJSON(enc []byte) error {
|
|
||||||
return data.Encoder.Unmarshal((*[]byte)(sig), enc)
|
|
||||||
}
|
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
package crypto
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/tendermint/ed25519"
|
"github.com/tendermint/ed25519"
|
||||||
data "github.com/tendermint/go-wire/data"
|
"github.com/tendermint/go-wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSignAndValidateEd25519(t *testing.T) {
|
func TestSignAndValidateEd25519(t *testing.T) {
|
||||||
@ -22,9 +21,9 @@ func TestSignAndValidateEd25519(t *testing.T) {
|
|||||||
assert.True(t, pubKey.VerifyBytes(msg, sig))
|
assert.True(t, pubKey.VerifyBytes(msg, sig))
|
||||||
|
|
||||||
// Mutate the signature, just one bit.
|
// Mutate the signature, just one bit.
|
||||||
sigEd := sig.Unwrap().(SignatureEd25519)
|
sigEd := sig.(SignatureEd25519)
|
||||||
sigEd[7] ^= byte(0x01)
|
sigEd[7] ^= byte(0x01)
|
||||||
sig = sigEd.Wrap()
|
sig = sigEd
|
||||||
|
|
||||||
assert.False(t, pubKey.VerifyBytes(msg, sig))
|
assert.False(t, pubKey.VerifyBytes(msg, sig))
|
||||||
}
|
}
|
||||||
@ -39,31 +38,28 @@ func TestSignAndValidateSecp256k1(t *testing.T) {
|
|||||||
assert.True(t, pubKey.VerifyBytes(msg, sig))
|
assert.True(t, pubKey.VerifyBytes(msg, sig))
|
||||||
|
|
||||||
// Mutate the signature, just one bit.
|
// Mutate the signature, just one bit.
|
||||||
sigEd := sig.Unwrap().(SignatureSecp256k1)
|
sigEd := sig.(SignatureSecp256k1)
|
||||||
sigEd[3] ^= byte(0x01)
|
sigEd[3] ^= byte(0x01)
|
||||||
sig = sigEd.Wrap()
|
sig = sigEd
|
||||||
|
|
||||||
assert.False(t, pubKey.VerifyBytes(msg, sig))
|
assert.False(t, pubKey.VerifyBytes(msg, sig))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSignatureEncodings(t *testing.T) {
|
func TestSignatureEncodings(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
privKey PrivKey
|
privKey PrivKey
|
||||||
sigSize int
|
sigSize int
|
||||||
sigType byte
|
sigPrefix wire.PrefixBytes
|
||||||
sigName string
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
privKey: GenPrivKeyEd25519().Wrap(),
|
privKey: GenPrivKeyEd25519(),
|
||||||
sigSize: ed25519.SignatureSize,
|
sigSize: ed25519.SignatureSize,
|
||||||
sigType: TypeEd25519,
|
sigPrefix: [4]byte{0xe4, 0x51, 0x7b, 0xa3},
|
||||||
sigName: NameEd25519,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
privKey: GenPrivKeySecp256k1().Wrap(),
|
privKey: GenPrivKeySecp256k1(),
|
||||||
sigSize: 0, // unknown
|
sigSize: 0, // unknown
|
||||||
sigType: TypeSecp256k1,
|
sigPrefix: [4]byte{0x37, 0xb9, 0x21, 0x3e},
|
||||||
sigName: NameSecp256k1,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,91 +71,37 @@ func TestSignatureEncodings(t *testing.T) {
|
|||||||
sig := tc.privKey.Sign(msg)
|
sig := tc.privKey.Sign(msg)
|
||||||
|
|
||||||
// store as wire
|
// store as wire
|
||||||
bin, err := data.ToWire(sig)
|
bin, err := cdc.MarshalBinary(sig)
|
||||||
require.Nil(t, err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
if tc.sigSize != 0 {
|
if tc.sigSize != 0 {
|
||||||
assert.Equal(t, tc.sigSize+1, len(bin))
|
assert.Equal(t, tc.sigSize+4, len(bin))
|
||||||
}
|
}
|
||||||
assert.Equal(t, tc.sigType, bin[0])
|
assert.Equal(t, tc.sigPrefix[:], bin[0:4])
|
||||||
|
|
||||||
// and back
|
// and back
|
||||||
sig2 := Signature{}
|
sig2 := Signature(nil)
|
||||||
err = data.FromWire(bin, &sig2)
|
err = cdc.UnmarshalBinary(bin, &sig2)
|
||||||
require.Nil(t, err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
assert.EqualValues(t, sig, sig2)
|
assert.EqualValues(t, sig, sig2)
|
||||||
assert.True(t, pubKey.VerifyBytes(msg, sig2))
|
assert.True(t, pubKey.VerifyBytes(msg, sig2))
|
||||||
|
|
||||||
// store as json
|
/*
|
||||||
js, err := data.ToJSON(sig)
|
// store as json
|
||||||
require.Nil(t, err, "%+v", err)
|
js, err := data.ToJSON(sig)
|
||||||
assert.True(t, strings.Contains(string(js), tc.sigName))
|
require.Nil(t, err, "%+v", err)
|
||||||
|
assert.True(t, strings.Contains(string(js), tc.sigName))
|
||||||
|
|
||||||
// and back
|
// and back
|
||||||
sig3 := Signature{}
|
sig3 := Signature{}
|
||||||
err = data.FromJSON(js, &sig3)
|
err = data.FromJSON(js, &sig3)
|
||||||
require.Nil(t, err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
assert.EqualValues(t, sig, sig3)
|
assert.EqualValues(t, sig, sig3)
|
||||||
assert.True(t, pubKey.VerifyBytes(msg, sig3))
|
assert.True(t, pubKey.VerifyBytes(msg, sig3))
|
||||||
|
|
||||||
// and make sure we can textify it
|
// and make sure we can textify it
|
||||||
text, err := data.ToText(sig)
|
text, err := data.ToText(sig)
|
||||||
require.Nil(t, err, "%+v", err)
|
require.Nil(t, err, "%+v", err)
|
||||||
assert.True(t, strings.HasPrefix(text, tc.sigName))
|
assert.True(t, strings.HasPrefix(text, tc.sigName))
|
||||||
}
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
func TestWrapping(t *testing.T) {
|
|
||||||
// construct some basic constructs
|
|
||||||
msg := CRandBytes(128)
|
|
||||||
priv := GenPrivKeyEd25519()
|
|
||||||
pub := priv.PubKey()
|
|
||||||
sig := priv.Sign(msg)
|
|
||||||
|
|
||||||
// do some wrapping
|
|
||||||
pubs := []PubKey{
|
|
||||||
PubKey{nil},
|
|
||||||
pub.Wrap(),
|
|
||||||
pub.Wrap().Wrap().Wrap(),
|
|
||||||
PubKey{PubKey{PubKey{pub}}}.Wrap(),
|
|
||||||
}
|
|
||||||
for _, p := range pubs {
|
|
||||||
_, ok := p.PubKeyInner.(PubKey)
|
|
||||||
assert.False(t, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
sigs := []Signature{
|
|
||||||
Signature{nil},
|
|
||||||
sig.Wrap(),
|
|
||||||
sig.Wrap().Wrap().Wrap(),
|
|
||||||
Signature{Signature{Signature{sig}}}.Wrap(),
|
|
||||||
}
|
|
||||||
for _, s := range sigs {
|
|
||||||
_, ok := s.SignatureInner.(Signature)
|
|
||||||
assert.False(t, ok)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestPrivKeyEquality(t *testing.T) {
|
|
||||||
{
|
|
||||||
privKey := GenPrivKeySecp256k1().Wrap()
|
|
||||||
privKey2 := GenPrivKeySecp256k1().Wrap()
|
|
||||||
assert.False(t, privKey.Equals(privKey2))
|
|
||||||
assert.False(t, privKey2.Equals(privKey))
|
|
||||||
|
|
||||||
privKeyCopy := privKey // copy
|
|
||||||
assert.True(t, privKey.Equals(privKeyCopy))
|
|
||||||
assert.True(t, privKeyCopy.Equals(privKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
privKey := GenPrivKeyEd25519().Wrap()
|
|
||||||
privKey2 := GenPrivKeyEd25519().Wrap()
|
|
||||||
assert.False(t, privKey.Equals(privKey2))
|
|
||||||
assert.False(t, privKey2.Equals(privKey))
|
|
||||||
|
|
||||||
privKeyCopy := privKey // copy
|
|
||||||
assert.True(t, privKey.Equals(privKeyCopy))
|
|
||||||
assert.True(t, privKeyCopy.Equals(privKey))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
// Generated by: main
|
|
||||||
// TypeWriter: wrapper
|
|
||||||
// Directive: +gen on SignatureInner
|
|
||||||
|
|
||||||
package crypto
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tendermint/go-wire/data"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Auto-generated adapters for happily unmarshaling interfaces
|
|
||||||
// Apache License 2.0
|
|
||||||
// Copyright (c) 2017 Ethan Frey (ethan.frey@tendermint.com)
|
|
||||||
|
|
||||||
type Signature struct {
|
|
||||||
SignatureInner "json:\"unwrap\""
|
|
||||||
}
|
|
||||||
|
|
||||||
var SignatureMapper = data.NewMapper(Signature{})
|
|
||||||
|
|
||||||
func (h Signature) MarshalJSON() ([]byte, error) {
|
|
||||||
return SignatureMapper.ToJSON(h.SignatureInner)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Signature) UnmarshalJSON(data []byte) (err error) {
|
|
||||||
parsed, err := SignatureMapper.FromJSON(data)
|
|
||||||
if err == nil && parsed != nil {
|
|
||||||
h.SignatureInner = parsed.(SignatureInner)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unwrap recovers the concrete interface safely (regardless of levels of embeds)
|
|
||||||
func (h Signature) Unwrap() SignatureInner {
|
|
||||||
hi := h.SignatureInner
|
|
||||||
for wrap, ok := hi.(Signature); ok; wrap, ok = hi.(Signature) {
|
|
||||||
hi = wrap.SignatureInner
|
|
||||||
}
|
|
||||||
return hi
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h Signature) Empty() bool {
|
|
||||||
return h.SignatureInner == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/*** below are bindings for each implementation ***/
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SignatureMapper.RegisterImplementation(SignatureEd25519{}, "ed25519", 0x1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hi SignatureEd25519) Wrap() Signature {
|
|
||||||
return Signature{hi}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
SignatureMapper.RegisterImplementation(SignatureSecp256k1{}, "secp256k1", 0x2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hi SignatureSecp256k1) Wrap() Signature {
|
|
||||||
return Signature{hi}
|
|
||||||
}
|
|
36
wire.go
Normal file
36
wire.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/tendermint/go-wire"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cdc = wire.NewCodec()
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// NOTE: It's important that there be no conflicts here,
|
||||||
|
// as that would change the canonical representations,
|
||||||
|
// and therefore change the address.
|
||||||
|
// TODO: Add feature to go-wire to ensure that there
|
||||||
|
// are no conflicts.
|
||||||
|
RegisterWire(cdc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterWire(cdc *wire.Codec) {
|
||||||
|
cdc.RegisterInterface((*PubKey)(nil), nil)
|
||||||
|
cdc.RegisterConcrete(PubKeyEd25519{},
|
||||||
|
"com.tendermint.wire.PubKeyEd25519", nil)
|
||||||
|
cdc.RegisterConcrete(PubKeySecp256k1{},
|
||||||
|
"com.tendermint.wire.PubKeySecp256k1", nil)
|
||||||
|
|
||||||
|
cdc.RegisterInterface((*PrivKey)(nil), nil)
|
||||||
|
cdc.RegisterConcrete(PrivKeyEd25519{},
|
||||||
|
"com.tendermint.wire.PrivKeyEd25519", nil)
|
||||||
|
cdc.RegisterConcrete(PrivKeySecp256k1{},
|
||||||
|
"com.tendermint.wire.PrivKeySecp256k1", nil)
|
||||||
|
|
||||||
|
cdc.RegisterInterface((*Signature)(nil), nil)
|
||||||
|
cdc.RegisterConcrete(SignatureEd25519{},
|
||||||
|
"com.tendermint.wire.SignatureKeyEd25519", nil)
|
||||||
|
cdc.RegisterConcrete(SignatureSecp256k1{},
|
||||||
|
"com.tendermint.wire.SignatureKeySecp256k1", nil)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user