From 788cc0a79256d4fe0448e17a90217acf3d61346b Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Sun, 14 Jan 2018 00:31:39 -0800 Subject: [PATCH] Use new go-wire; PubKey etc are interfaces; Keybase refactor --- {nano => _nano}/keys.go | 0 {nano => _nano}/keys_test.go | 0 {nano => _nano}/sign.go | 0 {nano => _nano}/sign_test.go | 0 crypto.go | 9 -- embed_test.go | 91 ------------- encode_test.go | 4 + glide.lock | 35 ++--- glide.yaml | 8 +- keys/keybase.go | 227 +++++++++++++------------------- keys/keybase_test.go | 242 ++++++++++++++++++----------------- keys/keys.go | 32 +++++ keys/mintkey.go | 26 ++++ keys/types.go | 63 +++++---- keys/wire.go | 12 ++ priv_key.go | 88 +++++-------- priv_key_test.go | 32 ++--- privkeyinner_wrapper.go | 62 --------- pub_key.go | 86 +++---------- pub_key_test.go | 4 +- pubkeyinner_wrapper.go | 62 --------- signature.go | 58 +++------ signature_test.go | 132 ++++++------------- signatureinner_wrapper.go | 62 --------- wire.go | 36 ++++++ 25 files changed, 494 insertions(+), 877 deletions(-) rename {nano => _nano}/keys.go (100%) rename {nano => _nano}/keys_test.go (100%) rename {nano => _nano}/sign.go (100%) rename {nano => _nano}/sign_test.go (100%) delete mode 100644 crypto.go delete mode 100644 embed_test.go create mode 100644 keys/keys.go create mode 100644 keys/wire.go delete mode 100644 privkeyinner_wrapper.go delete mode 100644 pubkeyinner_wrapper.go delete mode 100644 signatureinner_wrapper.go create mode 100644 wire.go diff --git a/nano/keys.go b/_nano/keys.go similarity index 100% rename from nano/keys.go rename to _nano/keys.go diff --git a/nano/keys_test.go b/_nano/keys_test.go similarity index 100% rename from nano/keys_test.go rename to _nano/keys_test.go diff --git a/nano/sign.go b/_nano/sign.go similarity index 100% rename from nano/sign.go rename to _nano/sign.go diff --git a/nano/sign_test.go b/_nano/sign_test.go similarity index 100% rename from nano/sign_test.go rename to _nano/sign_test.go diff --git a/crypto.go b/crypto.go deleted file mode 100644 index d9caf1ad9..000000000 --- a/crypto.go +++ /dev/null @@ -1,9 +0,0 @@ -package crypto - -// Types of implementations -const ( - TypeEd25519 = byte(0x01) - TypeSecp256k1 = byte(0x02) - NameEd25519 = "ed25519" - NameSecp256k1 = "secp256k1" -) diff --git a/embed_test.go b/embed_test.go deleted file mode 100644 index e5c37c0c1..000000000 --- a/embed_test.go +++ /dev/null @@ -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()) - } - } - } -} diff --git a/encode_test.go b/encode_test.go index 947d91901..bcd9e60c2 100644 --- a/encode_test.go +++ b/encode_test.go @@ -1,5 +1,8 @@ package crypto +/* +XXX Needs to be refactored to not use go-wire/data + import ( "fmt" "strings" @@ -179,3 +182,4 @@ func TestEmbededWireEncodings(t *testing.T) { checkWire(t, msg, &msg2, tc.keyType, tc.size) } } +*/ diff --git a/glide.lock b/glide.lock index 5032685c2..c8e47160f 100644 --- a/glide.lock +++ b/glide.lock @@ -1,18 +1,18 @@ -hash: a21061afc44c148eb6bfeb91478b520661f3d086234383a0208d915b0cb058b8 -updated: 2017-12-27T14:29:49.534901894-08:00 +hash: ed34b08bfc24f64edbc1a16ad2e34198afefc0e2dc133be2c92c6f764f953234 +updated: 2018-01-13T18:02:43.833267692-08:00 imports: - name: github.com/btcsuite/btcd - version: c7588cbf7690cd9f047a28efa2dcd8f2435a4e5e + version: 2e60448ffcc6bf78332d1fe590260095f554dd78 subpackages: - btcec - name: github.com/btcsuite/btcutil - version: 66871daeb12123ece012a9628d2798d01195c4b3 + version: 501929d3d046174c3d39f0ea54ece471aa17238c subpackages: - base58 -- name: github.com/ethanfrey/ledger - version: 5e432577be582bd18a3b4a9cd75dae7a317ade36 -- name: github.com/flynn/hid - version: ed06a31c6245d4552e8dbba7e32e5b010b875d65 +- name: github.com/davecgh/go-spew + version: 04cdfd42973bb9c8589fd6a731800cf222fde1a9 + subpackages: + - spew - name: github.com/go-kit/kit version: e2b298466b32c7cd5579a9b9b07e968fc9d9452c subpackages: @@ -32,7 +32,7 @@ imports: - name: github.com/golang/snappy version: 553a641470496b2327abcac10b36396bd98e45c9 - name: github.com/howeyc/crc16 - version: 96a97a1abb579c7ff1a8ffa77f2e72d1c314b57f + version: 2b2a61e366a66d3efb279e46176e7291001e0354 - name: github.com/jmhodges/levigo version: c42d9e0ca023e2198120196f842701bb4c55d7b9 - name: github.com/kr/logfmt @@ -55,17 +55,14 @@ imports: - leveldb/table - leveldb/util - name: github.com/tendermint/ed25519 - version: 1f52c6f8b8a5c7908aff4497c186af344b428925 + version: d8387025d2b9d158cf4efb07e7ebf814bcce2057 subpackages: - edwards25519 - extra25519 - name: github.com/tendermint/go-wire - version: 27be46e25124ddf775e23317a83647ce62a93f6b - subpackages: - - data - - data/base58 + version: fde4a0bf4dd4c0ec0df2504f79f23ed7e6b3b452 - name: github.com/tendermint/tmlibs - version: 93c05aa8c06ef38f2b15fcdd1d91eafefda2732d + version: 2bb538b150f197a04a0b969a27e9ea24d35edbc1 subpackages: - common - db @@ -83,10 +80,6 @@ imports: - ripemd160 - salsa20/salsa testImports: -- name: github.com/davecgh/go-spew - version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 - subpackages: - - spew - name: github.com/mndrix/btcutil version: d3a63a5752ecf3fbc06bd97365da752111c263df - name: github.com/pmezard/go-difflib @@ -94,11 +87,11 @@ testImports: subpackages: - difflib - name: github.com/stretchr/testify - version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 + version: b91bfb9ebec76498946beb6af7c0230c7cc7ba6c subpackages: - assert - require - name: github.com/tyler-smith/go-bip32 - version: eb790af526c30f23a7c8b00a48e342f9d0bd6386 + version: 2c9cfd17756470a0b7c3e4b7954bae7d11035504 - name: github.com/tyler-smith/go-bip39 version: 8e7a99b3e716f36d3b080a9a70f9eb45abe4edcc diff --git a/glide.yaml b/glide.yaml index f9df5c18d..f92a8c9a4 100644 --- a/glide.yaml +++ b/glide.yaml @@ -19,10 +19,7 @@ import: - package: github.com/tendermint/tmlibs version: sdk2 - package: github.com/tendermint/go-wire - version: develop - subpackages: - - data - - data/base58 + version: sdk2 - package: golang.org/x/crypto subpackages: - blowfish @@ -31,7 +28,8 @@ import: - ripemd160 - package: github.com/pkg/errors - package: github.com/howeyc/crc16 -- package: github.com/ethanfrey/ledger +# Needs refactoring, shouldn't be implementing PrivKey +#- package: github.com/ethanfrey/ledger testImport: - package: github.com/mndrix/btcutil - package: github.com/stretchr/testify diff --git a/keys/keybase.go b/keys/keybase.go index c72518748..88a12000c 100644 --- a/keys/keybase.go +++ b/keys/keybase.go @@ -6,16 +6,10 @@ import ( "github.com/pkg/errors" crypto "github.com/tendermint/go-crypto" - dbm "github.com/tendermint/tmlibs/db" - "github.com/tendermint/go-crypto/keys/words" - "github.com/tendermint/go-crypto/nano" + 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 // a full-featured key manager type dbKeybase struct { @@ -32,58 +26,59 @@ func New(db dbm.DB, codec words.Codec) dbKeybase { var _ Keybase = dbKeybase{} -// Create generates a new key and persists it storage, encrypted using the passphrase. -// It returns the generated seedphrase (mnemonic) and the key Info. -// It returns an error if it fails to generate a key for the given algo type, -// or if another key is already stored under the same name. -func (kb dbKeybase) Create(name, passphrase, algo string) (string, Info, error) { +// Create generates a new key and persists it to storage, encrypted +// using the passphrase. It returns the generated seedphrase +// (mnemonic) and the key Info. It returns an error if it fails to +// generate a key for the given algo type, or if another key is +// already stored under the same name. +func (kb dbKeybase) Create(name, passphrase string, algo CryptoAlgo) (Info, string, error) { // NOTE: secret is SHA256 hashed by secp256k1 and ed25519. // 16 byte secret corresponds to 12 BIP39 words. // XXX: Ledgers use 24 words now - should we ? secret := crypto.CRandBytes(16) - key, err := generate(algo, secret) + priv, err := generate(algo, secret) if err != nil { - return "", Info{}, err + return Info{}, "", err } // 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 words, err := kb.codec.BytesToWords(secret) - seedphrase := strings.Join(words, " ") - return seedphrase, public, err -} - -// Recover converts a seedphrase to a private key and persists it, encrypted with the given passphrase. -// Functions like Create, but seedphrase is input not output. -func (kb dbKeybase) Recover(name, passphrase, algo string, seedphrase string) (Info, error) { - - key, err := kb.SeedToPrivKey(algo, seedphrase) - if err != nil { - return Info{}, err - } - - // Valid seedphrase. Encrypt key and persist to disk. - public := kb.writeKey(key, name, passphrase) - return public, nil + seed := strings.Join(words, " ") + return info, seed, err } -// SeedToPrivKey returns the private key corresponding to a seedphrase -// without persisting the private key. -// TODO: enable the keybase to just hold these in memory so we can sign without persisting (?) -func (kb dbKeybase) SeedToPrivKey(algo, seedphrase string) (crypto.PrivKey, error) { +// Recover converts a seedphrase to a private key and persists it, +// encrypted with the given passphrase. Functions like Create, but +// seedphrase is input not output. +func (kb dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) { words := strings.Split(strings.TrimSpace(seedphrase), " ") secret, err := kb.codec.WordsToBytes(words) if err != nil { - return crypto.PrivKey{}, err + return Info{}, err } - key, err := generate(algo, secret) + // secret is comprised of the actual secret with the type + // appended. + // ie [secret] = [type] + [secret] + typ, secret := secret[0], secret[1:] + algo := byteToCryptoAlgo(typ) + priv, err := generate(algo, secret) if err != nil { - return crypto.PrivKey{}, err + return Info{}, err } - return key, nil + + // encrypt and persist key. + public := kb.writeKey(priv, name, passphrase) + return public, err } // 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) defer iter.Close() for ; iter.Valid(); iter.Next() { - key := iter.Key() - if isPub(key) { - info, err := readInfo(iter.Value()) - if err != nil { - return nil, err - } - res = append(res, info) + // key := iter.Key() + info, err := readInfo(iter.Value()) + if err != nil { + return nil, err } + res = append(res, info) } return res, nil } // Get returns the public information about one key. func (kb dbKeybase) Get(name string) (Info, error) { - bs := kb.db.Get(pubName(name)) + bs := kb.db.Get(infoKey(name)) return readInfo(bs) } // Sign signs the msg with the named key. // It returns an error if the key doesn't exist or the decryption fails. -// TODO: what if leddger fails ? -func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signature, pk crypto.PubKey, err error) { - var key crypto.PrivKey - armorStr := kb.db.Get(privName(name)) - key, err = unarmorDecryptPrivKey(string(armorStr), passphrase) +func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signature, pub crypto.PubKey, err error) { + info, err := kb.Get(name) if err != nil { return } - - sig = key.Sign(msg) - pk = key.PubKey() - return -} - -// Export decodes the private key with the current password, encrypts -// it with a secure one-time password and generates an armored private key -// that can be Imported by another dbKeybase. -// -// This is designed to copy from one device to another, or provide backups -// during version updates. -func (kb dbKeybase) Export(name, oldpass, transferpass string) ([]byte, error) { - armorStr := kb.db.Get(privName(name)) - key, err := unarmorDecryptPrivKey(string(armorStr), oldpass) + priv, err := unarmorDecryptPrivKey(info.PrivKeyArmor, passphrase) if err != nil { - return nil, err + return } + sig = priv.Sign(msg) + pub = priv.PubKey() + return +} - if transferpass == "" { - return key.Bytes(), nil +func (kb dbKeybase) Export(name string) (armor string, err error) { + bz := kb.db.Get(infoKey(name)) + if bz == nil { + return "", errors.New("No key to export with name " + name) } - armorBytes := encryptArmorPrivKey(key, transferpass) - return []byte(armorBytes), nil + return armorInfoBytes(bz), nil } -// Import accepts bytes generated by Export along with the same transferpass. -// If they are valid, it stores the password under the given name with the -// new passphrase. -func (kb dbKeybase) Import(name, newpass, transferpass string, data []byte) (err error) { - var key crypto.PrivKey - if transferpass == "" { - key, err = crypto.PrivKeyFromBytes(data) - } else { - key, err = unarmorDecryptPrivKey(string(data), transferpass) +func (kb dbKeybase) Import(name string, armor string) (err error) { + bz := kb.db.Get(infoKey(name)) + if len(bz) > 0 { + return errors.New("Cannot overwrite data for name " + name) } + infoBytes, err := unarmorInfoBytes(armor) if err != nil { - return err + return } - - kb.writeKey(key, name, newpass) + kb.db.Set(infoKey(name), infoBytes) return nil } @@ -168,80 +144,59 @@ func (kb dbKeybase) Import(name, newpass, transferpass string, data []byte) (err // proper passphrase before deleting it (for security). func (kb dbKeybase) Delete(name, passphrase string) error { // verify we have the proper password before deleting - bs := kb.db.Get(privName(name)) - _, err := unarmorDecryptPrivKey(string(bs), passphrase) + info, err := kb.Get(name) if err != nil { return err } - kb.db.DeleteSync(pubName(name)) - kb.db.DeleteSync(privName(name)) + _, err = unarmorDecryptPrivKey(info.PrivKeyArmor, passphrase) + if err != nil { + return err + } + kb.db.DeleteSync(infoKey(name)) 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 -// the only valid passphrase from this time forward. +// oldpass must be the current passphrase used for encryption, +// newpass will be the only valid passphrase from this time forward. func (kb dbKeybase) Update(name, oldpass, newpass string) error { - bs := kb.db.Get(privName(name)) - key, err := unarmorDecryptPrivKey(string(bs), oldpass) + info, err := kb.Get(name) + if err != nil { + return err + } + key, err := unarmorDecryptPrivKey(info.PrivKeyArmor, oldpass) if err != nil { return err } - // Generate the public bytes and the encrypted privkey - public := info(name, key) - private := encryptArmorPrivKey(key, newpass) - - // We must delete first, as Putting over an existing name returns an error. - // Must be done atomically with the write or we could lose the key. - batch := kb.db.NewBatch() - batch.Delete(pubName(name)) - batch.Delete(privName(name)) - batch.Set(pubName(name), public.bytes()) - batch.Set(privName(name), []byte(private)) - batch.Write() - + kb.writeKey(key, name, newpass) return nil } - -//--------------------------------------------------------------------------------------- - func (kb dbKeybase) writeKey(priv crypto.PrivKey, name, passphrase string) Info { - // Generate the public bytes and the encrypted privkey - public := info(name, priv) - private := encryptArmorPrivKey(priv, passphrase) - - // Write them both - kb.db.SetSync(pubName(name), public.bytes()) - kb.db.SetSync(privName(name), []byte(private)) + // generate the encrypted privkey + privArmor := encryptArmorPrivKey(priv, passphrase) + // make Info + info := newInfo(name, priv.PubKey(), privArmor) - return public + // write them both + kb.db.SetSync(infoKey(name), info.bytes()) + return info } -// TODO: use a `type TypeKeyAlgo string` (?) -func generate(algo string, secret []byte) (crypto.PrivKey, error) { +func generate(algo CryptoAlgo, secret []byte) (crypto.PrivKey, error) { switch algo { - case crypto.NameEd25519: - return crypto.GenPrivKeyEd25519FromSecret(secret).Wrap(), nil - case crypto.NameSecp256k1: - return crypto.GenPrivKeySecp256k1FromSecret(secret).Wrap(), nil - case nano.NameLedgerEd25519: - return nano.NewPrivKeyLedgerEd25519() + case AlgoEd25519: + return crypto.GenPrivKeyEd25519FromSecret(secret), nil + case AlgoSecp256k1: + return crypto.GenPrivKeySecp256k1FromSecret(secret), nil default: err := errors.Errorf("Cannot generate keys for algorithm: %s", algo) - return crypto.PrivKey{}, err + return nil, err } } -func pubName(name string) []byte { - return []byte(fmt.Sprintf("%s.pub", name)) -} - -func privName(name string) []byte { - return []byte(fmt.Sprintf("%s.priv", name)) -} - -func isPub(name []byte) bool { - return strings.HasSuffix(string(name), ".pub") +func infoKey(name string) []byte { + return []byte(fmt.Sprintf("%s.info", name)) } diff --git a/keys/keybase_test.go b/keys/keybase_test.go index 12e5205b0..84c81c81f 100644 --- a/keys/keybase_test.go +++ b/keys/keybase_test.go @@ -2,24 +2,20 @@ package keys_test import ( "fmt" - "os" "testing" - asrt "github.com/stretchr/testify/assert" - rqr "github.com/stretchr/testify/require" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" - cmn "github.com/tendermint/tmlibs/common" 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/words" - "github.com/tendermint/go-crypto/nano" ) // TestKeyManagement makes sure we can manipulate these keys well func TestKeyManagement(t *testing.T) { - assert, require := asrt.New(t), rqr.New(t) // make the storage with reasonable defaults cstore := keys.New( @@ -27,84 +23,83 @@ func TestKeyManagement(t *testing.T) { words.MustLoadCodec("english"), ) - algo := crypto.NameEd25519 + algo := keys.AlgoEd25519 n1, n2, n3 := "personal", "business", "other" p1, p2 := "1234", "really-secure!@#$" // Check empty state l, err := cstore.List() - require.Nil(err) - assert.Empty(l) + require.Nil(t, err) + assert.Empty(t, l) // create some keys _, err = cstore.Get(n1) - assert.NotNil(err) - _, i, err := cstore.Create(n1, p1, algo) - require.Equal(n1, i.Name) - require.Nil(err) + assert.NotNil(t, err) + i, _, err := cstore.Create(n1, p1, algo) + require.Equal(t, n1, i.Name) + require.Nil(t, err) _, _, err = cstore.Create(n2, p2, algo) - require.Nil(err) + require.Nil(t, err) // we can get these keys i2, err := cstore.Get(n2) - assert.Nil(err) + assert.Nil(t, err) _, err = cstore.Get(n3) - assert.NotNil(err) + assert.NotNil(t, err) // list shows them in order keyS, err := cstore.List() - require.Nil(err) - require.Equal(2, len(keyS)) + require.Nil(t, err) + require.Equal(t, 2, len(keyS)) // note these are in alphabetical order - assert.Equal(n2, keyS[0].Name) - assert.Equal(n1, keyS[1].Name) - assert.Equal(i2.PubKey, keyS[0].PubKey) + assert.Equal(t, n2, keyS[0].Name) + assert.Equal(t, n1, keyS[1].Name) + assert.Equal(t, i2.PubKey, keyS[0].PubKey) // deleting a key removes it err = cstore.Delete("bad name", "foo") - require.NotNil(err) + require.NotNil(t, err) err = cstore.Delete(n1, p1) - require.Nil(err) + require.Nil(t, err) keyS, err = cstore.List() - require.Nil(err) - assert.Equal(1, len(keyS)) + require.Nil(t, err) + assert.Equal(t, 1, len(keyS)) _, err = cstore.Get(n1) - assert.NotNil(err) + assert.NotNil(t, err) // make sure that it only signs with the right password // tx := mock.NewSig([]byte("mytransactiondata")) // err = cstore.Sign(n2, p1, tx) - // assert.NotNil(err) + // assert.NotNil(t, err) // err = cstore.Sign(n2, p2, tx) - // assert.Nil(err, "%+v", err) + // assert.Nil(t, err, "%+v", err) // sigs, err := tx.Signers() - // assert.Nil(err, "%+v", err) - // if assert.Equal(1, len(sigs)) { - // assert.Equal(i2.PubKey, sigs[0]) + // assert.Nil(t, err, "%+v", err) + // if assert.Equal(t, 1, len(sigs)) { + // assert.Equal(t, i2.PubKey, sigs[0]) // } } // TestSignVerify does some detailed checks on how we sign and validate // signatures func TestSignVerify(t *testing.T) { - assert, require := asrt.New(t), rqr.New(t) // make the storage with reasonable defaults cstore := keys.New( dbm.NewMemDB(), words.MustLoadCodec("english"), ) - algo := crypto.NameSecp256k1 + algo := keys.AlgoSecp256k1 n1, n2 := "some dude", "a dudette" p1, p2 := "1234", "foobar" // create two users and get their info - _, i1, err := cstore.Create(n1, p1, algo) - require.Nil(err) + i1, _, err := cstore.Create(n1, p1, algo) + require.Nil(t, err) - _, i2, err := cstore.Create(n2, p2, algo) - require.Nil(err) + i2, _, err := cstore.Create(n2, p2, algo) + require.Nil(t, err) // let's try to sign some messages d1 := []byte("my first message") @@ -112,20 +107,20 @@ func TestSignVerify(t *testing.T) { // try signing both data with both keys... s11, pub1, err := cstore.Sign(n1, p1, d1) - require.Nil(err) - require.Equal(i1.PubKey, pub1) + require.Nil(t, err) + require.Equal(t, i1.PubKey, pub1) s12, pub1, err := cstore.Sign(n1, p1, d2) - require.Nil(err) - require.Equal(i1.PubKey, pub1) + require.Nil(t, err) + require.Equal(t, i1.PubKey, pub1) s21, pub2, err := cstore.Sign(n2, p2, d1) - require.Nil(err) - require.Equal(i2.PubKey, pub2) + require.Nil(t, err) + require.Equal(t, i2.PubKey, pub2) s22, pub2, err := cstore.Sign(n2, p2, d2) - require.Nil(err) - require.Equal(i2.PubKey, pub2) + require.Nil(t, err) + require.Equal(t, i2.PubKey, pub2) // let's try to validate and make sure it only works when everything is proper cases := []struct { @@ -148,17 +143,17 @@ func TestSignVerify(t *testing.T) { for i, tc := range cases { 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 // the crypto store. // // This test will only succeed with a ledger attached to the computer // and the cosmos app open func TestSignWithLedger(t *testing.T) { - assert, require := asrt.New(t), rqr.New(t) if os.Getenv("WITH_LEDGER") == "" { t.Skip("Set WITH_LEDGER to run code on real ledger") } @@ -172,19 +167,19 @@ func TestSignWithLedger(t *testing.T) { p := "hard2hack" // create a nano user - _, c, err := cstore.Create(n, p, nano.NameLedgerEd25519) - require.Nil(err, "%+v", err) - assert.Equal(c.Name, n) + c, _, err := cstore.Create(n, p, nano.KeyLedgerEd25519) + require.Nil(t, err, "%+v", err) + assert.Equal(t, c.Key, n) _, ok := c.PubKey.Unwrap().(nano.PubKeyLedgerEd25519) - require.True(ok) + require.True(t, ok) // make sure we can get it back info, err := cstore.Get(n) - require.Nil(err, "%+v", err) - assert.Equal(info.Name, n) + require.Nil(t, err, "%+v", err) + assert.Equal(t, info.Key, n) key := info.PubKey - require.False(key.Empty()) - require.True(key.Equals(c.PubKey)) + require.False(t ,key.Empty()) + require.True(t, key.Equals(c.PubKey)) // let's try to sign some messages d1 := []byte("welcome to cosmos") @@ -192,56 +187,64 @@ func TestSignWithLedger(t *testing.T) { // try signing both data with the ledger... s1, pub, err := cstore.Sign(n, p, d1) - require.Nil(err) - require.Equal(info.PubKey, pub) + require.Nil(t, err) + require.Equal(t, info.PubKey, pub) s2, pub, err := cstore.Sign(n, p, d2) - require.Nil(err) - require.Equal(info.PubKey, pub) + require.Nil(t, err) + require.Equal(t, info.PubKey, pub) // now, let's check those signatures work - assert.True(key.VerifyBytes(d1, s1)) - assert.True(key.VerifyBytes(d2, s2)) + assert.True(t, key.VerifyBytes(d1, s1)) + assert.True(t, key.VerifyBytes(d2, s2)) // 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) - assert.NotNil(err) + assert.NotNil(t, err) 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 -func TestImportUnencrypted(t *testing.T) { - require := rqr.New(t) +// TestExportImport tests exporting and importing keys. +func TestExportImport(t *testing.T) { // make the storage with reasonable defaults + db := dbm.NewMemDB() cstore := keys.New( - dbm.NewMemDB(), + db, 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() + + john, err := cstore.Get("john") + assert.Nil(t, err) + assert.Equal(t, john.Name, "john") + assert.Equal(t, john.PubKey.Address(), addr) - addr := key.PubKey().Address() - name := "john" - pass := "top-secret" + armor, err := cstore.Export("john") + assert.Nil(t, err) - // import raw bytes - err := cstore.Import(name, pass, "", key.Bytes()) - require.Nil(err, "%+v", err) + err = cstore.Import("john2", armor) + assert.Nil(t, err) - // make sure the address matches - info, err := cstore.Get(name) - 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 func TestAdvancedKeyManagement(t *testing.T) { - assert, require := asrt.New(t), rqr.New(t) // make the storage with reasonable defaults cstore := keys.New( @@ -249,42 +252,49 @@ func TestAdvancedKeyManagement(t *testing.T) { words.MustLoadCodec("english"), ) - algo := crypto.NameSecp256k1 + algo := keys.AlgoSecp256k1 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 _, _, err := cstore.Create(n1, p1, algo) - require.Nil(err, "%+v", err) - assertPassword(assert, cstore, n1, p1, p2) + require.Nil(t, err, "%+v", err) + assertPassword(t, cstore, n1, p1, p2) // update password requires the existing password err = cstore.Update(n1, "jkkgkg", p2) - assert.NotNil(err) - assertPassword(assert, cstore, n1, p1, p2) + assert.NotNil(t, err) + assertPassword(t, cstore, n1, p1, p2) // then it changes the password when correct err = cstore.Update(n1, p1, p2) - assert.Nil(err) + assert.Nil(t, err) // 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 - _, err = cstore.Export(n2, p2, pt) - assert.NotNil(err) - _, err = cstore.Export(n1, p1, pt) - assert.NotNil(err) - exported, err := cstore.Export(n1, p2, pt) - require.Nil(err, "%+v", err) - - // import fails on bad transfer pass - err = cstore.Import(n2, p3, p2, exported) - assert.NotNil(err) + _, err = cstore.Export(n1 + ".notreal") + assert.NotNil(t, err) + _, err = cstore.Export(" " + n1) + assert.NotNil(t, err) + _, err = cstore.Export(n1 + " ") + assert.NotNil(t, err) + _, err = cstore.Export("") + assert.NotNil(t, err) + exported, err := cstore.Export(n1) + require.Nil(t, err, "%+v", err) + + // import succeeds + err = cstore.Import(n2, exported) + assert.Nil(t, err) + + // second import fails + err = cstore.Import(n2, exported) + assert.NotNil(t, err) } // TestSeedPhrase verifies restoring from a seed phrase func TestSeedPhrase(t *testing.T) { - assert, require := asrt.New(t), rqr.New(t) // make the storage with reasonable defaults cstore := keys.New( @@ -292,28 +302,28 @@ func TestSeedPhrase(t *testing.T) { words.MustLoadCodec("english"), ) - algo := crypto.NameEd25519 + algo := keys.AlgoEd25519 n1, n2 := "lost-key", "found-again" p1, p2 := "1234", "foobar" // make sure key works with initial password - seed, info, err := cstore.Create(n1, p1, algo) - require.Nil(err, "%+v", err) - assert.Equal(n1, info.Name) - assert.NotEmpty(seed) + info, seed, err := cstore.Create(n1, p1, algo) + require.Nil(t, err, "%+v", err) + assert.Equal(t, n1, info.Name) + assert.NotEmpty(t, seed) // now, let us delete this key err = cstore.Delete(n1, p1) - require.Nil(err, "%+v", err) + require.Nil(t, err, "%+v", err) _, err = cstore.Get(n1) - require.NotNil(err) + require.NotNil(t, err) // let us re-create it from the seed-phrase - newInfo, err := cstore.Recover(n2, p2, algo, seed) - require.Nil(err, "%+v", err) - assert.Equal(n2, newInfo.Name) - assert.Equal(info.Address(), newInfo.Address()) - assert.Equal(info.PubKey, newInfo.PubKey) + newInfo, err := cstore.Recover(n2, p2, seed) + require.Nil(t, err, "%+v", err) + assert.Equal(t, n2, newInfo.Name) + assert.Equal(t, info.Address(), newInfo.Address()) + assert.Equal(t, info.PubKey, newInfo.PubKey) } func ExampleNew() { @@ -322,11 +332,11 @@ func ExampleNew() { dbm.NewMemDB(), words.MustLoadCodec("english"), ) - ed := crypto.NameEd25519 - sec := crypto.NameSecp256k1 + ed := keys.AlgoEd25519 + sec := keys.AlgoSecp256k1 // Add keys and see they return in alphabetical order - _, bob, err := cstore.Create("Bob", "friend", ed) + bob, _, err := cstore.Create("Bob", "friend", ed) if err != nil { // this should never happen fmt.Println(err) diff --git a/keys/keys.go b/keys/keys.go new file mode 100644 index 000000000..0ed89b3fd --- /dev/null +++ b/keys/keys.go @@ -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)) + } +} diff --git a/keys/mintkey.go b/keys/mintkey.go index d2724d658..73263b83a 100644 --- a/keys/mintkey.go +++ b/keys/mintkey.go @@ -12,8 +12,34 @@ import ( const ( 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 { saltBytes, encBytes := encryptPrivKey(privKey, passphrase) header := map[string]string{ diff --git a/keys/types.go b/keys/types.go index fdb729e0a..16b8f6dd0 100644 --- a/keys/types.go +++ b/keys/types.go @@ -1,15 +1,39 @@ package keys import ( - wire "github.com/tendermint/go-wire" - 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 type Info struct { - Name string `json:"name"` - PubKey crypto.PubKey `json:"pubkey"` + Name string `json:"name"` + 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 @@ -18,31 +42,14 @@ func (i Info) Address() []byte { } func (i Info) bytes() []byte { - return wire.BinaryBytes(i) -} - -func readInfo(bs []byte) (info Info, err error) { - err = wire.ReadBinaryBytes(bs, &info) - return -} - -func info(name string, privKey crypto.PrivKey) Info { - return Info{ - Name: name, - PubKey: privKey.PubKey(), + bz, err := cdc.MarshalBinary(i) + if err != nil { + panic(err) } + return bz } -// 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 +func readInfo(bz []byte) (info Info, err error) { + err = cdc.UnmarshalBinary(bz, &info) + return } diff --git a/keys/wire.go b/keys/wire.go new file mode 100644 index 000000000..65d69ba5e --- /dev/null +++ b/keys/wire.go @@ -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) +} diff --git a/priv_key.go b/priv_key.go index 11dcb686f..b86b30f8b 100644 --- a/priv_key.go +++ b/priv_key.go @@ -7,89 +7,60 @@ import ( "github.com/tendermint/ed25519" "github.com/tendermint/ed25519/extra25519" "github.com/tendermint/go-wire" - data "github.com/tendermint/go-wire/data" . "github.com/tendermint/tmlibs/common" ) func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) { - err = wire.ReadBinaryBytes(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() - } - } + err = cdc.UnmarshalBinary(privKeyBytes, &privKey) return } -// validatable is an optional interface for keys that want to -// check integrity -type validatable interface { - ValidateKey() error -} - //---------------------------------------- -// DO NOT USE THIS INTERFACE. -// You probably want to use PrivKey -// +gen wrapper:"PrivKey,Impl[PrivKeyEd25519,PrivKeySecp256k1],ed25519,secp256k1" -type PrivKeyInner interface { - AssertIsPrivKeyInner() +type PrivKey interface { Bytes() []byte Sign(msg []byte) Signature PubKey() PubKey Equals(PrivKey) bool - Wrap() PrivKey } //------------------------------------- -var _ PrivKeyInner = PrivKeyEd25519{} +var _ PrivKey = PrivKeyEd25519{} // Implements PrivKey type PrivKeyEd25519 [64]byte -func (privKey PrivKeyEd25519) AssertIsPrivKeyInner() {} - 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 { privKeyBytes := [64]byte(privKey) signatureBytes := ed25519.Sign(&privKeyBytes, msg) - return SignatureEd25519(*signatureBytes).Wrap() + return SignatureEd25519(*signatureBytes) } func (privKey PrivKeyEd25519) PubKey() PubKey { privKeyBytes := [64]byte(privKey) pubBytes := *ed25519.MakePublicKey(&privKeyBytes) - return PubKeyEd25519(pubBytes).Wrap() + return PubKeyEd25519(pubBytes) } // Equals - you probably don't need to use this. // Runs in constant time based on length of the keys. 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 } else { 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 { keyCurve25519 := new([32]byte) privKeyBytes := [64]byte(privKey) @@ -97,16 +68,22 @@ func (privKey PrivKeyEd25519) ToCurve25519() *[32]byte { return keyCurve25519 } +/* func (privKey PrivKeyEd25519) String() string { return Fmt("PrivKeyEd25519{*****}") } +*/ // Deterministically generates new priv-key bytes from key. func (privKey PrivKeyEd25519) Generate(index int) PrivKeyEd25519 { - newBytes := wire.BinarySha256(struct { + bz, err := wire.MarshalBinary(struct { PrivKey [64]byte Index int }{privKey, index}) + if err != nil { + panic(err) + } + newBytes := Sha256(bz) var newKey [64]byte copy(newKey[:], newBytes) return PrivKeyEd25519(newKey) @@ -131,15 +108,17 @@ func GenPrivKeyEd25519FromSecret(secret []byte) PrivKeyEd25519 { //------------------------------------- -var _ PrivKeyInner = PrivKeySecp256k1{} +var _ PrivKey = PrivKeySecp256k1{} // Implements PrivKey type PrivKeySecp256k1 [32]byte -func (privKey PrivKeySecp256k1) AssertIsPrivKeyInner() {} - 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 { @@ -148,40 +127,31 @@ func (privKey PrivKeySecp256k1) Sign(msg []byte) Signature { if err != nil { PanicSanity(err) } - return SignatureSecp256k1(sig__.Serialize()).Wrap() + return SignatureSecp256k1(sig__.Serialize()) } func (privKey PrivKeySecp256k1) PubKey() PubKey { _, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) var pub PubKeySecp256k1 copy(pub[:], pub__.SerializeCompressed()) - return pub.Wrap() + return pub } // Equals - you probably don't need to use this. // Runs in constant time based on length of the keys. 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 } else { 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 { return Fmt("PrivKeySecp256k1{*****}") } +*/ /* // Deterministically generates new priv-key bytes from key. diff --git a/priv_key_test.go b/priv_key_test.go index 154df5593..3fbfba34d 100644 --- a/priv_key_test.go +++ b/priv_key_test.go @@ -1,42 +1,26 @@ package crypto +/* + import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - wire "github.com/tendermint/go-wire" ) type BadKey struct { 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) { assert, require := assert.New(t), require.New(t) // garbage in, garbage out 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) require.Error(err) @@ -47,13 +31,15 @@ func TestReadPrivKey(t *testing.T) { key PrivKey valid bool }{ - {edKey.Wrap(), true}, - {badKey.Wrap(), false}, + {edKey, true}, + {badKey, false}, } for i, tc := range cases { data := tc.key.Bytes() + fmt.Println(">>>", data) key, err := PrivKeyFromBytes(data) + fmt.Printf("!!! %#v\n", key, err) if tc.valid { assert.NoError(err, "%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) } } - } +*/ diff --git a/privkeyinner_wrapper.go b/privkeyinner_wrapper.go deleted file mode 100644 index 05ce69672..000000000 --- a/privkeyinner_wrapper.go +++ /dev/null @@ -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} -} diff --git a/pub_key.go b/pub_key.go index 32c0b3237..8ed1df356 100644 --- a/pub_key.go +++ b/pub_key.go @@ -8,8 +8,6 @@ import ( secp256k1 "github.com/btcsuite/btcd/btcec" "github.com/tendermint/ed25519" "github.com/tendermint/ed25519/extra25519" - "github.com/tendermint/go-wire" - data "github.com/tendermint/go-wire/data" cmn "github.com/tendermint/tmlibs/common" "golang.org/x/crypto/ripemd160" ) @@ -20,56 +18,44 @@ import ( type Address = cmn.HexBytes func PubKeyFromBytes(pubKeyBytes []byte) (pubKey PubKey, err error) { - if err := wire.ReadBinaryBytes(pubKeyBytes, &pubKey); err != nil { - return PubKey{}, err - } - return pubKey, nil + err = cdc.UnmarshalBinary(pubKeyBytes, &pubKey) + return } //---------------------------------------- -// DO NOT USE THIS INTERFACE. -// You probably want to use PubKey -// +gen wrapper:"PubKey,Impl[PubKeyEd25519,PubKeySecp256k1],ed25519,secp256k1" -type PubKeyInner interface { - AssertIsPubKeyInner() +type PubKey interface { Address() Address Bytes() []byte - KeyString() string VerifyBytes(msg []byte, sig Signature) bool Equals(PubKey) bool - Wrap() PubKey } //------------------------------------- -var _ PubKeyInner = PubKeyEd25519{} +var _ PubKey = PubKeyEd25519{} // Implements PubKeyInner type PubKeyEd25519 [32]byte -func (pubKey PubKeyEd25519) AssertIsPubKeyInner() {} - 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 - encodedPubkey := append([]byte{TypeEd25519}, w.Bytes()...) hasher := ripemd160.New() - hasher.Write(encodedPubkey) // does not error + hasher.Write(pubKey.Bytes()) // does not error return Address(hasher.Sum(nil)) } 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 { // make sure we use the same algorithm to sign - sig, ok := sig_.Unwrap().(SignatureEd25519) + sig, ok := sig_.(SignatureEd25519) if !ok { return false } @@ -78,17 +64,6 @@ func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ Signature) bool { 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 // If error, returns nil. func (pubKey PubKeyEd25519) ToCurve25519() *[32]byte { @@ -104,14 +79,8 @@ func (pubKey PubKeyEd25519) String() string { 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 { - if otherEd, ok := other.Unwrap().(PubKeyEd25519); ok { + if otherEd, ok := other.(PubKeyEd25519); ok { return bytes.Equal(pubKey[:], otherEd[:]) } else { return false @@ -120,15 +89,13 @@ func (pubKey PubKeyEd25519) Equals(other PubKey) bool { //------------------------------------- -var _ PubKeyInner = PubKeySecp256k1{} +var _ PubKey = PubKeySecp256k1{} // Implements PubKey. // Compressed pubkey (just the x-cord), // prefixed with 0x02 or 0x03, depending on the y-cord. type PubKeySecp256k1 [33]byte -func (pubKey PubKeySecp256k1) AssertIsPubKeyInner() {} - // Implements Bitcoin style addresses: RIPEMD160(SHA256(pubkey)) func (pubKey PubKeySecp256k1) Address() Address { hasherSHA256 := sha256.New() @@ -141,12 +108,16 @@ func (pubKey PubKeySecp256k1) Address() Address { } 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 { // and assert same algorithm to sign and verify - sig, ok := sig_.Unwrap().(SignatureSecp256k1) + sig, ok := sig_.(SignatureSecp256k1) if !ok { return false } @@ -162,29 +133,12 @@ func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig_ Signature) bool { 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 { 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 { - if otherSecp, ok := other.Unwrap().(PubKeySecp256k1); ok { + if otherSecp, ok := other.(PubKeySecp256k1); ok { return bytes.Equal(pubKey[:], otherSecp[:]) } else { return false diff --git a/pub_key_test.go b/pub_key_test.go index 95c82534d..29672390b 100644 --- a/pub_key_test.go +++ b/pub_key_test.go @@ -33,7 +33,7 @@ func TestPubKeySecp256k1Address(t *testing.T) { var priv PrivKeySecp256k1 copy(priv[:], privB) - pubT := priv.PubKey().Unwrap().(PubKeySecp256k1) + pubT := priv.PubKey().(PubKeySecp256k1) pub := pubT[:] addr := priv.PubKey().Address() @@ -45,5 +45,5 @@ func TestPubKeySecp256k1Address(t *testing.T) { func TestPubKeyInvalidDataProperReturnsEmpty(t *testing.T) { pk, err := PubKeyFromBytes([]byte("foo")) 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") } diff --git a/pubkeyinner_wrapper.go b/pubkeyinner_wrapper.go deleted file mode 100644 index 7b36c324d..000000000 --- a/pubkeyinner_wrapper.go +++ /dev/null @@ -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} -} diff --git a/signature.go b/signature.go index cd40331cf..3a0f6a7a7 100644 --- a/signature.go +++ b/signature.go @@ -4,40 +4,35 @@ import ( "bytes" "fmt" - "github.com/tendermint/go-wire" - data "github.com/tendermint/go-wire/data" . "github.com/tendermint/tmlibs/common" ) -func SignatureFromBytes(sigBytes []byte) (sig Signature, err error) { - err = wire.ReadBinaryBytes(sigBytes, &sig) +func SignatureFromBytes(pubKeyBytes []byte) (pubKey Signature, err error) { + err = cdc.UnmarshalBinary(pubKeyBytes, &pubKey) return } //---------------------------------------- -// DO NOT USE THIS INTERFACE. -// You probably want to use Signature. -// +gen wrapper:"Signature,Impl[SignatureEd25519,SignatureSecp256k1],ed25519,secp256k1" -type SignatureInner interface { - AssertIsSignatureInner() +type Signature interface { Bytes() []byte IsZero() bool Equals(Signature) bool - Wrap() Signature } //------------------------------------- -var _ SignatureInner = SignatureEd25519{} +var _ Signature = SignatureEd25519{} // Implements Signature type SignatureEd25519 [64]byte -func (sig SignatureEd25519) AssertIsSignatureInner() {} - 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 } @@ -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) Equals(other Signature) bool { - if otherEd, ok := other.Unwrap().(SignatureEd25519); ok { + if otherEd, ok := other.(SignatureEd25519); ok { return bytes.Equal(sig[:], otherEd[:]) } else { 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 { var sig SignatureEd25519 copy(sig[:], data) - return sig.Wrap() + return sig } //------------------------------------- -var _ SignatureInner = SignatureSecp256k1{} +var _ Signature = SignatureSecp256k1{} // Implements Signature type SignatureSecp256k1 []byte -func (sig SignatureSecp256k1) AssertIsSignatureInner() {} - 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 } @@ -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) Equals(other Signature) bool { - if otherSecp, ok := other.Unwrap().(SignatureSecp256k1); ok { + if otherSecp, ok := other.(SignatureSecp256k1); ok { return bytes.Equal(sig[:], otherSecp[:]) } else { 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) -} diff --git a/signature_test.go b/signature_test.go index 08e51940b..7bc94e677 100644 --- a/signature_test.go +++ b/signature_test.go @@ -1,13 +1,12 @@ package crypto import ( - "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/ed25519" - data "github.com/tendermint/go-wire/data" + "github.com/tendermint/go-wire" ) func TestSignAndValidateEd25519(t *testing.T) { @@ -22,9 +21,9 @@ func TestSignAndValidateEd25519(t *testing.T) { assert.True(t, pubKey.VerifyBytes(msg, sig)) // Mutate the signature, just one bit. - sigEd := sig.Unwrap().(SignatureEd25519) + sigEd := sig.(SignatureEd25519) sigEd[7] ^= byte(0x01) - sig = sigEd.Wrap() + sig = sigEd assert.False(t, pubKey.VerifyBytes(msg, sig)) } @@ -39,31 +38,28 @@ func TestSignAndValidateSecp256k1(t *testing.T) { assert.True(t, pubKey.VerifyBytes(msg, sig)) // Mutate the signature, just one bit. - sigEd := sig.Unwrap().(SignatureSecp256k1) + sigEd := sig.(SignatureSecp256k1) sigEd[3] ^= byte(0x01) - sig = sigEd.Wrap() + sig = sigEd assert.False(t, pubKey.VerifyBytes(msg, sig)) } func TestSignatureEncodings(t *testing.T) { cases := []struct { - privKey PrivKey - sigSize int - sigType byte - sigName string + privKey PrivKey + sigSize int + sigPrefix wire.PrefixBytes }{ { - privKey: GenPrivKeyEd25519().Wrap(), - sigSize: ed25519.SignatureSize, - sigType: TypeEd25519, - sigName: NameEd25519, + privKey: GenPrivKeyEd25519(), + sigSize: ed25519.SignatureSize, + sigPrefix: [4]byte{0xe4, 0x51, 0x7b, 0xa3}, }, { - privKey: GenPrivKeySecp256k1().Wrap(), - sigSize: 0, // unknown - sigType: TypeSecp256k1, - sigName: NameSecp256k1, + privKey: GenPrivKeySecp256k1(), + sigSize: 0, // unknown + sigPrefix: [4]byte{0x37, 0xb9, 0x21, 0x3e}, }, } @@ -75,91 +71,37 @@ func TestSignatureEncodings(t *testing.T) { sig := tc.privKey.Sign(msg) // store as wire - bin, err := data.ToWire(sig) + bin, err := cdc.MarshalBinary(sig) require.Nil(t, err, "%+v", err) 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 - sig2 := Signature{} - err = data.FromWire(bin, &sig2) + sig2 := Signature(nil) + err = cdc.UnmarshalBinary(bin, &sig2) require.Nil(t, err, "%+v", err) assert.EqualValues(t, sig, sig2) assert.True(t, pubKey.VerifyBytes(msg, sig2)) - // store as json - js, err := data.ToJSON(sig) - require.Nil(t, err, "%+v", err) - assert.True(t, strings.Contains(string(js), tc.sigName)) - - // and back - sig3 := Signature{} - err = data.FromJSON(js, &sig3) - require.Nil(t, err, "%+v", err) - assert.EqualValues(t, sig, sig3) - assert.True(t, pubKey.VerifyBytes(msg, sig3)) - - // and make sure we can textify it - text, err := data.ToText(sig) - require.Nil(t, err, "%+v", err) - 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)) + /* + // store as json + js, err := data.ToJSON(sig) + require.Nil(t, err, "%+v", err) + assert.True(t, strings.Contains(string(js), tc.sigName)) + + // and back + sig3 := Signature{} + err = data.FromJSON(js, &sig3) + require.Nil(t, err, "%+v", err) + assert.EqualValues(t, sig, sig3) + assert.True(t, pubKey.VerifyBytes(msg, sig3)) + + // and make sure we can textify it + text, err := data.ToText(sig) + require.Nil(t, err, "%+v", err) + assert.True(t, strings.HasPrefix(text, tc.sigName)) + */ } } diff --git a/signatureinner_wrapper.go b/signatureinner_wrapper.go deleted file mode 100644 index 1fdd790d6..000000000 --- a/signatureinner_wrapper.go +++ /dev/null @@ -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} -} diff --git a/wire.go b/wire.go new file mode 100644 index 000000000..18035964f --- /dev/null +++ b/wire.go @@ -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) +}