Browse Source

Merge remote-tracking branch 'remotes/origin/develop' into bucky/sig-name

# Conflicts:
#	Gopkg.lock
#	keys/keybase_test.go
#	signature_test.go
pull/1782/head
Liamsi 7 years ago
parent
commit
c4484c4a1b
23 changed files with 694 additions and 217 deletions
  1. +34
    -0
      CHANGELOG.md
  2. +25
    -8
      Gopkg.lock
  3. +4
    -1
      Gopkg.toml
  4. +2
    -0
      amino.go
  5. +4
    -2
      encode_test.go
  6. +125
    -43
      keys/keybase.go
  7. +90
    -73
      keys/keybase_test.go
  8. +5
    -5
      keys/keys.go
  9. +103
    -21
      keys/types.go
  10. +4
    -0
      keys/wire.go
  11. +19
    -0
      ledger_common.go
  12. +146
    -0
      ledger_secp256k1.go
  13. +65
    -0
      ledger_test.go
  14. +5
    -8
      merkle/simple_map.go
  15. +6
    -6
      merkle/simple_map_test.go
  16. +11
    -3
      merkle/simple_proof.go
  17. +13
    -34
      priv_key.go
  18. +5
    -1
      priv_key_test.go
  19. +8
    -5
      pub_key.go
  20. +4
    -2
      pub_key_test.go
  21. +6
    -0
      signature.go
  22. +9
    -4
      signature_test.go
  23. +1
    -1
      version.go

+ 34
- 0
CHANGELOG.md View File

@ -1,5 +1,39 @@
# Changelog
## 0.8.0
**TBD**
## 0.7.0
**May 30th, 2018**
BREAKING CHANGES
No breaking changes compared to 0.6.2, but making up for the version bump that
should have happened in 0.6.1.
We also bring in the `tmlibs/merkle` package with breaking changes:
- change the hash function from RIPEMD160 to tmhash (first 20-bytes of SHA256)
- remove unused funcs and unexport SimpleMap
FEATURES
- [xchacha20poly1305] New authenticated encryption module
- [merkle] Moved in from tmlibs
- [merkle/tmhash] New hash function: the first 20-bytes of SHA256
IMPROVEMENTS
- Remove some dead code
- Use constant-time compare for signatures
BUG FIXES
- Fix MixEntropy weakness
- Fix PrivKeyEd25519.Generate()
## 0.6.2 (April 9, 2018)
IMPROVEMENTS


+ 25
- 8
Gopkg.lock View File

@ -1,17 +1,23 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/brejski/hid"
packages = ["."]
revision = "06112dcfcc50a7e0e4fd06e17f9791e788fdaafc"
[[projects]]
branch = "master"
name = "github.com/btcsuite/btcd"
packages = ["btcec"]
revision = "2be2f12b358dc57d70b8f501b00be450192efbc3"
revision = "86fed781132ac890ee03e906e4ecd5d6fa180c64"
[[projects]]
branch = "master"
name = "github.com/btcsuite/btcutil"
packages = ["base58"]
revision = "501929d3d046174c3d39f0ea54ece471aa17238c"
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
[[projects]]
name = "github.com/davecgh/go-spew"
@ -55,7 +61,7 @@
branch = "master"
name = "github.com/golang/snappy"
packages = ["."]
revision = "553a641470496b2327abcac10b36396bd98e45c9"
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
[[projects]]
branch = "master"
@ -113,7 +119,7 @@
"leveldb/table",
"leveldb/util"
]
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
revision = "e2150783cd35f5b607daca48afd8c57ec54cc995"
[[projects]]
branch = "master"
@ -139,8 +145,8 @@
"log",
"test"
]
revision = "2e24b64fc121dcdf1cabceab8dc2f7257675483c"
version = "v0.8.1"
revision = "692f1d86a6e2c0efa698fd1e4541b68c74ffaf38"
version = "v0.8.4"
[[projects]]
branch = "master"
@ -148,6 +154,11 @@
packages = ["."]
revision = "8e7a99b3e716f36d3b080a9a70f9eb45abe4edcc"
[[projects]]
name = "github.com/zondax/ledger-goclient"
packages = ["."]
revision = "065cbf938a16f20335c40cfe180f9cd4955c6a5a"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
@ -164,11 +175,17 @@
"ripemd160",
"salsa20/salsa"
]
revision = "b2aa35443fbc700ab74c586ae79b81c171851023"
revision = "8ac0e0d97ce45cd83d1d7243c060cb8461dda5e9"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["cpu"]
revision = "9527bec2660bd847c050fda93a0f0c6dee0800bb"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "b34cf043cab77178eebff1b7cfce8b31b6c2b6b3318c6d01add271b68f550345"
inputs-digest = "f20e34cd998442d4ffe2f9aa45ab87a55ba6e4cd19f29009adaadac3b5dccf26"
solver-name = "gps-cdcl"
solver-version = 1

+ 4
- 1
Gopkg.toml View File

@ -24,7 +24,6 @@
# go-tests = true
# unused-packages = true
[[constraint]]
name = "github.com/btcsuite/btcutil"
branch = "master"
@ -57,6 +56,10 @@
name = "github.com/tyler-smith/go-bip39"
branch = "master"
[[constraint]]
name = "github.com/zondax/ledger-goclient"
revision = "065cbf938a16f20335c40cfe180f9cd4955c6a5a"
[prune]
go-tests = true
unused-packages = true

+ 2
- 0
amino.go View File

@ -27,6 +27,8 @@ func RegisterAmino(cdc *amino.Codec) {
"tendermint/PrivKeyEd25519", nil)
cdc.RegisterConcrete(PrivKeySecp256k1{},
"tendermint/PrivKeySecp256k1", nil)
cdc.RegisterConcrete(PrivKeyLedgerSecp256k1{},
"tendermint/PrivKeyLedgerSecp256k1", nil)
cdc.RegisterInterface((*Signature)(nil), nil)
cdc.RegisterConcrete(SignatureEd25519{},


+ 4
- 2
encode_test.go View File

@ -82,14 +82,16 @@ func TestKeyEncodings(t *testing.T) {
// Check (de/en)codings of Signatures.
var sig1, sig2, sig3 Signature
sig1 = tc.privKey.Sign([]byte("something"))
sig1, err := tc.privKey.Sign([]byte("something"))
assert.NoError(t, err)
checkAminoBinary(t, sig1, &sig2, -1) // Siganture size changes for Secp anyways.
assert.EqualValues(t, sig1, sig2)
checkAminoJSON(t, sig1, &sig3, false) // TODO also check Prefix bytes.
assert.EqualValues(t, sig1, sig3)
// Check (de/en)codings of PubKeys.
pubKey := tc.privKey.PubKey()
pubKey, err := tc.privKey.PubKey()
assert.NoError(t, err)
var pub2, pub3 PubKey
checkAminoBinary(t, pubKey, &pub2, tc.pubSize)
assert.EqualValues(t, pubKey, pub2)


+ 125
- 43
keys/keybase.go View File

@ -1,7 +1,9 @@
package keys
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/pkg/errors"
@ -26,23 +28,23 @@ func New(db dbm.DB, codec words.Codec) dbKeybase {
var _ Keybase = dbKeybase{}
// Create generates a new key and persists it to storage, encrypted
// CreateMnemonic generates a new key and persists it to storage, encrypted
// using the passphrase. It returns the generated seedphrase
// (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) {
func (kb dbKeybase) CreateMnemonic(name, passphrase string, algo SignAlgo) (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)
priv, err := generate(algo, secret)
if err != nil {
return Info{}, "", err
return nil, "", err
}
// encrypt and persist the key
info := kb.writeKey(priv, name, passphrase)
info := kb.writeLocalKey(priv, name, passphrase)
// we append the type byte to the serialized secret to help with
// recovery
@ -56,6 +58,29 @@ func (kb dbKeybase) Create(name, passphrase string, algo CryptoAlgo) (Info, stri
return info, seed, err
}
// CreateLedger creates a new locally-stored reference to a Ledger keypair
// It returns the created key info and an error if the Ledger could not be queried
func (kb dbKeybase) CreateLedger(name string, path crypto.DerivationPath, algo SignAlgo) (Info, error) {
if algo != AlgoSecp256k1 {
return nil, fmt.Errorf("Only secp256k1 is supported for Ledger devices")
}
priv, err := crypto.NewPrivKeyLedgerSecp256k1(path)
if err != nil {
return nil, err
}
pub, err := priv.PubKey()
if err != nil {
return nil, err
}
return kb.writeLedgerKey(pub, path, name), nil
}
// CreateOffline creates a new reference to an offline keypair
// It returns the created key info
func (kb dbKeybase) CreateOffline(name string, pub crypto.PubKey) (Info, error) {
return kb.writeOfflineKey(pub, name), nil
}
// Recover converts a seedphrase to a private key and persists it,
// encrypted with the given passphrase. Functions like Create, but
// seedphrase is input not output.
@ -63,22 +88,22 @@ func (kb dbKeybase) Recover(name, passphrase, seedphrase string) (Info, error) {
words := strings.Split(strings.TrimSpace(seedphrase), " ")
secret, err := kb.codec.WordsToBytes(words)
if err != nil {
return Info{}, err
return nil, err
}
// secret is comprised of the actual secret with the type
// appended.
// ie [secret] = [type] + [secret]
typ, secret := secret[0], secret[1:]
algo := byteToCryptoAlgo(typ)
algo := byteToSignAlgo(typ)
priv, err := generate(algo, secret)
if err != nil {
return Info{}, err
return nil, err
}
// encrypt and persist key.
public := kb.writeKey(priv, name, passphrase)
return public, err
public := kb.writeLocalKey(priv, name, passphrase)
return public, nil
}
// List returns the keys from storage in alphabetical order.
@ -87,7 +112,6 @@ func (kb dbKeybase) List() ([]Info, error) {
iter := kb.db.Iterator(nil, nil)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
// key := iter.Key()
info, err := readInfo(iter.Value())
if err != nil {
return nil, err
@ -110,17 +134,46 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signat
if err != nil {
return
}
if info.PrivKeyArmor == "" {
err = fmt.Errorf("private key not available")
return
var priv crypto.PrivKey
switch info.(type) {
case localInfo:
linfo := info.(localInfo)
if linfo.PrivKeyArmor == "" {
err = fmt.Errorf("private key not available")
return
}
priv, err = unarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return nil, nil, err
}
case ledgerInfo:
linfo := info.(ledgerInfo)
priv, err = crypto.NewPrivKeyLedgerSecp256k1(linfo.Path)
if err != nil {
return
}
case offlineInfo:
linfo := info.(offlineInfo)
fmt.Printf("Bytes to sign:\n%s", msg)
buf := bufio.NewReader(os.Stdin)
fmt.Printf("\nEnter Amino-encoded signature:\n")
// Will block until user inputs the signature
signed, err := buf.ReadString('\n')
if err != nil {
return nil, nil, err
}
cdc.MustUnmarshalBinary([]byte(signed), sig)
return sig, linfo.GetPubKey(), nil
}
priv, err := unarmorDecryptPrivKey(info.PrivKeyArmor, passphrase)
sig, err = priv.Sign(msg)
if err != nil {
return
return nil, nil, err
}
sig = priv.Sign(msg)
pub = priv.PubKey()
return
pub, err = priv.PubKey()
if err != nil {
return nil, nil, err
}
return sig, pub, nil
}
func (kb dbKeybase) Export(name string) (armor string, err error) {
@ -143,7 +196,7 @@ func (kb dbKeybase) ExportPubKey(name string) (armor string, err error) {
if err != nil {
return
}
return armorPubKeyBytes(info.PubKey.Bytes()), nil
return armorPubKeyBytes(info.GetPubKey().Bytes()), nil
}
func (kb dbKeybase) Import(name string, armor string) (err error) {
@ -175,23 +228,37 @@ func (kb dbKeybase) ImportPubKey(name string, armor string) (err error) {
if err != nil {
return
}
kb.writePubKey(pubKey, name)
kb.writeOfflineKey(pubKey, name)
return
}
// Delete removes key forever, but we must present the
// proper passphrase before deleting it (for security).
// A passphrase of 'yes' is used to delete stored
// references to offline and Ledger / HW wallet keys
func (kb dbKeybase) Delete(name, passphrase string) error {
// verify we have the proper password before deleting
info, err := kb.Get(name)
if err != nil {
return err
}
_, err = unarmorDecryptPrivKey(info.PrivKeyArmor, passphrase)
if err != nil {
return err
switch info.(type) {
case localInfo:
linfo := info.(localInfo)
_, err = unarmorDecryptPrivKey(linfo.PrivKeyArmor, passphrase)
if err != nil {
return err
}
kb.db.DeleteSync(infoKey(name))
return nil
case ledgerInfo:
case offlineInfo:
if passphrase != "yes" {
return fmt.Errorf("enter exactly 'yes' to delete the key")
}
kb.db.DeleteSync(infoKey(name))
return nil
}
kb.db.DeleteSync(infoKey(name))
return nil
}
@ -205,36 +272,51 @@ func (kb dbKeybase) Update(name, oldpass, newpass string) error {
if err != nil {
return err
}
key, err := unarmorDecryptPrivKey(info.PrivKeyArmor, oldpass)
if err != nil {
return err
switch info.(type) {
case localInfo:
linfo := info.(localInfo)
key, err := unarmorDecryptPrivKey(linfo.PrivKeyArmor, oldpass)
if err != nil {
return err
}
kb.writeLocalKey(key, name, newpass)
return nil
default:
return fmt.Errorf("Locally stored key required")
}
kb.writeKey(key, name, newpass)
return nil
}
func (kb dbKeybase) writePubKey(pub crypto.PubKey, name string) Info {
func (kb dbKeybase) writeLocalKey(priv crypto.PrivKey, name, passphrase string) Info {
// encrypt private key using passphrase
privArmor := encryptArmorPrivKey(priv, passphrase)
// make Info
info := newInfo(name, pub, "")
// write them both
kb.db.SetSync(infoKey(name), info.bytes())
pub, err := priv.PubKey()
if err != nil {
panic(err)
}
info := newLocalInfo(name, pub, privArmor)
kb.writeInfo(info, name)
return info
}
func (kb dbKeybase) writeKey(priv crypto.PrivKey, name, passphrase string) Info {
// generate the encrypted privkey
privArmor := encryptArmorPrivKey(priv, passphrase)
// make Info
info := newInfo(name, priv.PubKey(), privArmor)
func (kb dbKeybase) writeLedgerKey(pub crypto.PubKey, path crypto.DerivationPath, name string) Info {
info := newLedgerInfo(name, pub, path)
kb.writeInfo(info, name)
return info
}
// write them both
kb.db.SetSync(infoKey(name), info.bytes())
func (kb dbKeybase) writeOfflineKey(pub crypto.PubKey, name string) Info {
info := newOfflineInfo(name, pub)
kb.writeInfo(info, name)
return info
}
func generate(algo CryptoAlgo, secret []byte) (crypto.PrivKey, error) {
func (kb dbKeybase) writeInfo(info Info, name string) {
// write the info by key
kb.db.SetSync(infoKey(name), writeInfo(info))
}
func generate(algo SignAlgo, secret []byte) (crypto.PrivKey, error) {
switch algo {
case AlgoEd25519:
return crypto.GenPrivKeyEd25519FromSecret(secret), nil


+ 90
- 73
keys/keybase_test.go View File

@ -33,28 +33,28 @@ func TestKeyManagement(t *testing.T) {
assert.Empty(t, l)
// create some keys
i, err := cstore.Get(n1)
assert.Equal(t, i, keys.Info{})
i, _, err = cstore.Create(n1, p1, algo)
require.Equal(t, n1, i.Name)
_, err = cstore.Get(n1)
assert.NotNil(t, err)
i, _, err := cstore.CreateMnemonic(n1, p1, algo)
require.Equal(t, n1, i.GetName())
require.Nil(t, err)
_, _, err = cstore.Create(n2, p2, algo)
_, _, err = cstore.CreateMnemonic(n2, p2, algo)
require.Nil(t, err)
// we can get these keys
i2, err := cstore.Get(n2)
assert.Nil(t, err)
i, err = cstore.Get(n3)
assert.Equal(t, i, keys.Info{})
assert.NoError(t, err)
_, err = cstore.Get(n3)
assert.NotNil(t, err)
// list shows them in order
keyS, err := cstore.List()
require.Nil(t, err)
require.Equal(t, 2, len(keyS))
// note these are in alphabetical order
assert.Equal(t, n2, keyS[0].Name)
assert.Equal(t, n1, keyS[1].Name)
assert.Equal(t, i2.PubKey, keyS[0].PubKey)
assert.Equal(t, n2, keyS[0].GetName())
assert.Equal(t, n1, keyS[1].GetName())
assert.Equal(t, i2.GetPubKey(), keyS[0].GetPubKey())
// deleting a key removes it
err = cstore.Delete("bad name", "foo")
@ -64,8 +64,28 @@ func TestKeyManagement(t *testing.T) {
keyS, err = cstore.List()
require.Nil(t, err)
assert.Equal(t, 1, len(keyS))
i, err = cstore.Get(n1)
assert.Equal(t, i, keys.Info{})
_, err = cstore.Get(n1)
assert.NotNil(t, err)
// create an offline key
o1 := "offline"
priv1 := crypto.GenPrivKeyEd25519()
pub1, err := priv1.PubKey()
require.Nil(t, err)
i, err = cstore.CreateOffline(o1, pub1)
require.Nil(t, err)
require.Equal(t, pub1, i.GetPubKey())
require.Equal(t, o1, i.GetName())
keyS, err = cstore.List()
require.Equal(t, 2, len(keyS))
// delete the offline key
err = cstore.Delete(o1, "no")
require.NotNil(t, err)
err = cstore.Delete(o1, "yes")
require.Nil(t, err)
keyS, err = cstore.List()
require.Equal(t, 1, len(keyS))
// make sure that it only signs with the right password
// tx := mock.NewSig([]byte("mytransactiondata"))
@ -95,19 +115,18 @@ func TestSignVerify(t *testing.T) {
p1, p2, p3 := "1234", "foobar", "foobar"
// create two users and get their info
i1, _, err := cstore.Create(n1, p1, algo)
i1, _, err := cstore.CreateMnemonic(n1, p1, algo)
require.Nil(t, err)
i2, _, err := cstore.Create(n2, p2, algo)
i2, _, err := cstore.CreateMnemonic(n2, p2, algo)
require.Nil(t, err)
// Import a public key
armor, err := cstore.ExportPubKey(n2)
require.Nil(t, err)
cstore.ImportPubKey(n3, armor)
i3, err := cstore.Get(n3)
_, err = cstore.Get(n3)
require.Nil(t, err)
require.Equal(t, i3.PrivKeyArmor, "")
// let's try to sign some messages
d1 := []byte("my first message")
@ -117,19 +136,19 @@ func TestSignVerify(t *testing.T) {
// try signing both data with both keys...
s11, pub1, err := cstore.Sign(n1, p1, d1)
require.Nil(t, err)
require.Equal(t, i1.PubKey, pub1)
require.Equal(t, i1.GetPubKey(), pub1)
s12, pub1, err := cstore.Sign(n1, p1, d2)
require.Nil(t, err)
require.Equal(t, i1.PubKey, pub1)
require.Equal(t, i1.GetPubKey(), pub1)
s21, pub2, err := cstore.Sign(n2, p2, d1)
require.Nil(t, err)
require.Equal(t, i2.PubKey, pub2)
require.Equal(t, i2.GetPubKey(), pub2)
s22, pub2, err := cstore.Sign(n2, p2, d2)
require.Nil(t, err)
require.Equal(t, i2.PubKey, pub2)
require.Equal(t, i2.GetPubKey(), pub2)
// let's try to validate and make sure it only works when everything is proper
cases := []struct {
@ -139,15 +158,15 @@ func TestSignVerify(t *testing.T) {
valid bool
}{
// proper matches
{i1.PubKey, d1, s11, true},
{i1.GetPubKey(), d1, s11, true},
// change data, pubkey, or signature leads to fail
{i1.PubKey, d2, s11, false},
{i2.PubKey, d1, s11, false},
{i1.PubKey, d1, s21, false},
{i1.GetPubKey(), d2, s11, false},
{i2.GetPubKey(), d1, s11, false},
{i1.GetPubKey(), d1, s21, false},
// make sure other successes
{i1.PubKey, d2, s12, true},
{i2.PubKey, d1, s21, true},
{i2.PubKey, d2, s22, true},
{i1.GetPubKey(), d2, s12, true},
{i2.GetPubKey(), d1, s21, true},
{i2.GetPubKey(), d2, s22, true},
}
for i, tc := range cases {
@ -232,27 +251,27 @@ func TestExportImport(t *testing.T) {
words.MustLoadCodec("english"),
)
info, _, err := cstore.Create("john", "passphrase", keys.AlgoEd25519)
assert.Nil(t, err)
assert.Equal(t, info.Name, "john")
addr := info.PubKey.Address()
info, _, err := cstore.CreateMnemonic("john", "passphrase", keys.AlgoEd25519)
assert.NoError(t, err)
assert.Equal(t, info.GetName(), "john")
addr := info.GetPubKey().Address()
john, err := cstore.Get("john")
assert.Nil(t, err)
assert.Equal(t, john.Name, "john")
assert.Equal(t, john.PubKey.Address(), addr)
assert.NoError(t, err)
assert.Equal(t, john.GetName(), "john")
assert.Equal(t, john.GetPubKey().Address(), addr)
armor, err := cstore.Export("john")
assert.Nil(t, err)
assert.NoError(t, err)
err = cstore.Import("john2", armor)
assert.Nil(t, err)
assert.NoError(t, err)
john2, err := cstore.Get("john2")
assert.Nil(t, err)
assert.NoError(t, err)
assert.Equal(t, john.PubKey.Address(), addr)
assert.Equal(t, john.Name, "john")
assert.Equal(t, john.GetPubKey().Address(), addr)
assert.Equal(t, john.GetName(), "john")
assert.Equal(t, john, john2)
}
@ -265,33 +284,31 @@ func TestExportImportPubKey(t *testing.T) {
)
// Create a private-public key pair and ensure consistency
info, _, err := cstore.Create("john", "passphrase", keys.AlgoEd25519)
assert.Nil(t, err)
assert.NotEqual(t, info.PrivKeyArmor, "")
assert.Equal(t, info.Name, "john")
addr := info.PubKey.Address()
info, _, err := cstore.CreateMnemonic("john", "passphrase", keys.AlgoEd25519)
assert.NoError(t, err)
assert.Equal(t, info.GetName(), "john")
addr := info.GetPubKey().Address()
john, err := cstore.Get("john")
assert.Nil(t, err)
assert.Equal(t, john.Name, "john")
assert.Equal(t, john.PubKey.Address(), addr)
assert.NoError(t, err)
assert.Equal(t, john.GetName(), "john")
assert.Equal(t, john.GetPubKey().Address(), addr)
// Export the public key only
armor, err := cstore.ExportPubKey("john")
assert.Nil(t, err)
assert.NoError(t, err)
// Import it under a different name
err = cstore.ImportPubKey("john-pubkey-only", armor)
assert.Nil(t, err)
assert.NoError(t, err)
// Ensure consistency
john2, err := cstore.Get("john-pubkey-only")
assert.Nil(t, err)
assert.Equal(t, john2.PrivKeyArmor, "")
assert.NoError(t, err)
// Compare the public keys
assert.True(t, john.PubKey.Equals(john2.PubKey))
assert.True(t, john.GetPubKey().Equals(john2.GetPubKey()))
// Ensure the original key hasn't changed
john, err = cstore.Get("john")
assert.Nil(t, err)
assert.Equal(t, john.PubKey.Address(), addr)
assert.Equal(t, john.Name, "john")
assert.NoError(t, err)
assert.Equal(t, john.GetPubKey().Address(), addr)
assert.Equal(t, john.GetName(), "john")
// Ensure keys cannot be overwritten
err = cstore.ImportPubKey("john-pubkey-only", armor)
@ -312,7 +329,7 @@ func TestAdvancedKeyManagement(t *testing.T) {
p1, p2 := "1234", "foobar"
// make sure key works with initial password
_, _, err := cstore.Create(n1, p1, algo)
_, _, err := cstore.CreateMnemonic(n1, p1, algo)
require.Nil(t, err, "%+v", err)
assertPassword(t, cstore, n1, p1, p2)
@ -323,7 +340,7 @@ func TestAdvancedKeyManagement(t *testing.T) {
// then it changes the password when correct
err = cstore.Update(n1, p1, p2)
assert.Nil(t, err)
assert.NoError(t, err)
// p2 is now the proper one!
assertPassword(t, cstore, n1, p2, p1)
@ -341,7 +358,7 @@ func TestAdvancedKeyManagement(t *testing.T) {
// import succeeds
err = cstore.Import(n2, exported)
assert.Nil(t, err)
assert.NoError(t, err)
// second import fails
err = cstore.Import(n2, exported)
@ -362,23 +379,23 @@ func TestSeedPhrase(t *testing.T) {
p1, p2 := "1234", "foobar"
// make sure key works with initial password
info, seed, err := cstore.Create(n1, p1, algo)
info, seed, err := cstore.CreateMnemonic(n1, p1, algo)
require.Nil(t, err, "%+v", err)
assert.Equal(t, n1, info.Name)
assert.Equal(t, n1, info.GetName())
assert.NotEmpty(t, seed)
// now, let us delete this key
err = cstore.Delete(n1, p1)
require.Nil(t, err, "%+v", err)
i, err := cstore.Get(n1)
require.Equal(t, i, keys.Info{}, "expected empty info")
_, err = cstore.Get(n1)
require.NotNil(t, err)
// let us re-create it from the seed-phrase
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)
assert.Equal(t, n2, newInfo.GetName())
assert.Equal(t, info.GetPubKey().Address(), newInfo.GetPubKey().Address())
assert.Equal(t, info.GetPubKey(), newInfo.GetPubKey())
}
func ExampleNew() {
@ -391,19 +408,19 @@ func ExampleNew() {
sec := keys.AlgoSecp256k1
// Add keys and see they return in alphabetical order
bob, _, err := cstore.Create("Bob", "friend", ed)
bob, _, err := cstore.CreateMnemonic("Bob", "friend", ed)
if err != nil {
// this should never happen
fmt.Println(err)
} else {
// return info here just like in List
fmt.Println(bob.Name)
fmt.Println(bob.GetName())
}
cstore.Create("Alice", "secret", sec)
cstore.Create("Carl", "mitm", ed)
cstore.CreateMnemonic("Alice", "secret", sec)
cstore.CreateMnemonic("Carl", "mitm", ed)
info, _ := cstore.List()
for _, i := range info {
fmt.Println(i.Name)
fmt.Println(i.GetName())
}
// We need to use passphrase to generate a signature
@ -415,11 +432,11 @@ func ExampleNew() {
// and we can validate the signature with publically available info
binfo, _ := cstore.Get("Bob")
if !binfo.PubKey.Equals(bob.PubKey) {
if !binfo.GetPubKey().Equals(bob.GetPubKey()) {
fmt.Println("Get and Create return different keys")
}
if pub.Equals(binfo.PubKey) {
if pub.Equals(binfo.GetPubKey()) {
fmt.Println("signed by Bob")
}
if !pub.VerifyBytes(tx, sig) {


+ 5
- 5
keys/keys.go View File

@ -2,14 +2,14 @@ package keys
import "fmt"
type CryptoAlgo string
type SignAlgo string
const (
AlgoEd25519 = CryptoAlgo("ed25519")
AlgoSecp256k1 = CryptoAlgo("secp256k1")
AlgoEd25519 = SignAlgo("ed25519")
AlgoSecp256k1 = SignAlgo("secp256k1")
)
func cryptoAlgoToByte(key CryptoAlgo) byte {
func cryptoAlgoToByte(key SignAlgo) byte {
switch key {
case AlgoEd25519:
return 0x01
@ -20,7 +20,7 @@ func cryptoAlgoToByte(key CryptoAlgo) byte {
}
}
func byteToCryptoAlgo(b byte) CryptoAlgo {
func byteToSignAlgo(b byte) SignAlgo {
switch b {
case 0x01:
return AlgoEd25519


+ 103
- 21
keys/types.go View File

@ -4,54 +4,136 @@ import (
crypto "github.com/tendermint/go-crypto"
)
// Keybase allows simple CRUD on a keystore, as an aid to signing
// Keybase exposes operations on a generic keystore
type Keybase interface {
// 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)
// CRUD on the keystore
List() ([]Info, error)
Get(name string) (Info, error)
Update(name, oldpass, newpass string) error
Delete(name, passphrase string) error
// Sign some bytes, looking up the private key to use
Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error)
// Create a new locally-stored keypair, returning the mnemonic
CreateMnemonic(name, passphrase string, algo SignAlgo) (info Info, seed string, err error)
// Recover takes a seedphrase and loads in the key
Recover(name, passphrase, seedphrase string) (info Info, erro error)
// Create, store, and return a new Ledger key reference
CreateLedger(name string, path crypto.DerivationPath, algo SignAlgo) (info Info, err error)
// Create, store, and return a new offline key reference
CreateOffline(name string, pubkey crypto.PubKey) (info Info, err error)
// The following operations will *only* work on locally-stored keys
Update(name, oldpass, newpass string) error
Import(name string, armor string) (err error)
ImportPubKey(name string, armor string) (err error)
Export(name string) (armor string, err error)
ExportPubKey(name string) (armor string, err error)
}
// Info is the public information about a key
type Info struct {
// Publically exposed information about a keypair
type Info interface {
// Human-readable type for key listing
GetType() string
// Name of the key
GetName() string
// Public key
GetPubKey() crypto.PubKey
}
var _ Info = &localInfo{}
var _ Info = &ledgerInfo{}
var _ Info = &offlineInfo{}
// localInfo is the public information about a locally stored key
type localInfo struct {
Name string `json:"name"`
PubKey crypto.PubKey `json:"pubkey"`
PrivKeyArmor string `json:"privkey.armor"`
}
func newInfo(name string, pub crypto.PubKey, privArmor string) Info {
return Info{
func newLocalInfo(name string, pub crypto.PubKey, privArmor string) Info {
return &localInfo{
Name: name,
PubKey: pub,
PrivKeyArmor: privArmor,
}
}
// Address is a helper function to calculate the address from the pubkey
func (i Info) Address() []byte {
return i.PubKey.Address()
func (i localInfo) GetType() string {
return "local"
}
func (i Info) bytes() []byte {
bz, err := cdc.MarshalBinaryBare(i)
if err != nil {
panic(err)
func (i localInfo) GetName() string {
return i.Name
}
func (i localInfo) GetPubKey() crypto.PubKey {
return i.PubKey
}
// ledgerInfo is the public information about a Ledger key
type ledgerInfo struct {
Name string `json:"name"`
PubKey crypto.PubKey `json:"pubkey"`
Path crypto.DerivationPath `json:"path"`
}
func newLedgerInfo(name string, pub crypto.PubKey, path crypto.DerivationPath) Info {
return &ledgerInfo{
Name: name,
PubKey: pub,
Path: path,
}
}
func (i ledgerInfo) GetType() string {
return "ledger"
}
func (i ledgerInfo) GetName() string {
return i.Name
}
func (i ledgerInfo) GetPubKey() crypto.PubKey {
return i.PubKey
}
// offlineInfo is the public information about an offline key
type offlineInfo struct {
Name string `json:"name"`
PubKey crypto.PubKey `json:"pubkey"`
}
func newOfflineInfo(name string, pub crypto.PubKey) Info {
return &offlineInfo{
Name: name,
PubKey: pub,
}
return bz
}
func (i offlineInfo) GetType() string {
return "offline"
}
func (i offlineInfo) GetName() string {
return i.Name
}
func (i offlineInfo) GetPubKey() crypto.PubKey {
return i.PubKey
}
// encoding info
func writeInfo(i Info) []byte {
return cdc.MustMarshalBinary(i)
}
// decoding info
func readInfo(bz []byte) (info Info, err error) {
err = cdc.UnmarshalBinaryBare(bz, &info)
err = cdc.UnmarshalBinary(bz, &info)
return
}

+ 4
- 0
keys/wire.go View File

@ -9,4 +9,8 @@ var cdc = amino.NewCodec()
func init() {
crypto.RegisterAmino(cdc)
cdc.RegisterInterface((*Info)(nil), nil)
cdc.RegisterConcrete(localInfo{}, "crypto/keys/localInfo", nil)
cdc.RegisterConcrete(ledgerInfo{}, "crypto/keys/ledgerInfo", nil)
cdc.RegisterConcrete(offlineInfo{}, "crypto/keys/offlineInfo", nil)
}

+ 19
- 0
ledger_common.go View File

@ -0,0 +1,19 @@
package crypto
import (
ledger "github.com/zondax/ledger-goclient"
)
var device *ledger.Ledger
// Ledger derivation path
type DerivationPath = []uint32
// getLedger gets a copy of the device, and caches it
func getLedger() (*ledger.Ledger, error) {
var err error
if device == nil {
device, err = ledger.FindLedger()
}
return device, err
}

+ 146
- 0
ledger_secp256k1.go View File

@ -0,0 +1,146 @@
package crypto
import (
"fmt"
secp256k1 "github.com/btcsuite/btcd/btcec"
ledger "github.com/zondax/ledger-goclient"
)
func pubkeyLedgerSecp256k1(device *ledger.Ledger, path DerivationPath) (pub PubKey, err error) {
key, err := device.GetPublicKeySECP256K1(path)
if err != nil {
return nil, fmt.Errorf("error fetching public key: %v", err)
}
var p PubKeySecp256k1
// Reserialize in the 33-byte compressed format
cmp, err := secp256k1.ParsePubKey(key[:], secp256k1.S256())
copy(p[:], cmp.SerializeCompressed())
pub = p
return
}
func signLedgerSecp256k1(device *ledger.Ledger, path DerivationPath, msg []byte) (sig Signature, err error) {
bsig, err := device.SignSECP256K1(path, msg)
if err != nil {
return sig, err
}
sig = SignatureSecp256k1FromBytes(bsig)
return
}
// PrivKeyLedgerSecp256k1 implements PrivKey, calling the ledger nano
// we cache the PubKey from the first call to use it later
type PrivKeyLedgerSecp256k1 struct {
// PubKey should be private, but we want to encode it via go-amino
// so we can view the address later, even without having the ledger
// attached
CachedPubKey PubKey
Path DerivationPath
}
// NewPrivKeyLedgerSecp256k1 will generate a new key and store the
// public key for later use.
func NewPrivKeyLedgerSecp256k1(path DerivationPath) (PrivKey, error) {
var pk PrivKeyLedgerSecp256k1
pk.Path = path
// getPubKey will cache the pubkey for later use,
// this allows us to return an error early if the ledger
// is not plugged in
_, err := pk.getPubKey()
return &pk, err
}
// ValidateKey allows us to verify the sanity of a key
// after loading it from disk
func (pk PrivKeyLedgerSecp256k1) ValidateKey() error {
// getPubKey will return an error if the ledger is not
// properly set up...
pub, err := pk.forceGetPubKey()
if err != nil {
return err
}
// verify this matches cached address
if !pub.Equals(pk.CachedPubKey) {
return fmt.Errorf("cached key does not match retrieved key")
}
return nil
}
// AssertIsPrivKeyInner fulfils PrivKey Interface
func (pk *PrivKeyLedgerSecp256k1) AssertIsPrivKeyInner() {}
// Bytes fulfils PrivKey Interface - but it stores the cached pubkey so we can verify
// the same key when we reconnect to a ledger
func (pk PrivKeyLedgerSecp256k1) Bytes() []byte {
return cdc.MustMarshalBinaryBare(pk)
}
// Sign calls the ledger and stores the PubKey for future use
//
// Communication is checked on NewPrivKeyLedger and PrivKeyFromBytes,
// returning an error, so this should only trigger if the privkey is held
// in memory for a while before use.
func (pk PrivKeyLedgerSecp256k1) Sign(msg []byte) (Signature, error) {
dev, err := getLedger()
if err != nil {
return nil, err
}
sig, err := signLedgerSecp256k1(dev, pk.Path, msg)
if err != nil {
return nil, err
}
pub, err := pubkeyLedgerSecp256k1(dev, pk.Path)
if err != nil {
return nil, err
}
// if we have no pubkey yet, store it for future queries
if pk.CachedPubKey == nil {
pk.CachedPubKey = pub
} else if !pk.CachedPubKey.Equals(pub) {
return nil, fmt.Errorf("stored key does not match signing key")
}
return sig, nil
}
// PubKey returns the stored PubKey
func (pk PrivKeyLedgerSecp256k1) PubKey() (PubKey, error) {
return pk.getPubKey()
}
// getPubKey reads the pubkey from cache or from the ledger itself
// since this involves IO, it may return an error, which is not exposed
// in the PubKey interface, so this function allows better error handling
func (pk PrivKeyLedgerSecp256k1) getPubKey() (key PubKey, err error) {
// if we have no pubkey, set it
if pk.CachedPubKey == nil {
pk.CachedPubKey, err = pk.forceGetPubKey()
}
return pk.CachedPubKey, err
}
// forceGetPubKey is like getPubKey but ignores any cached key
// and ensures we get it from the ledger itself.
func (pk PrivKeyLedgerSecp256k1) forceGetPubKey() (key PubKey, err error) {
dev, err := getLedger()
if err != nil {
return key, fmt.Errorf("cannot connect to Ledger device - error: %v", err)
}
key, err = pubkeyLedgerSecp256k1(dev, pk.Path)
if err != nil {
return key, fmt.Errorf("please open Cosmos app on the Ledger device - error: %v", err)
}
return key, err
}
// Equals fulfils PrivKey Interface - makes sure both keys refer to the
// same
func (pk PrivKeyLedgerSecp256k1) Equals(other PrivKey) bool {
if ledger, ok := other.(*PrivKeyLedgerSecp256k1); ok {
return pk.CachedPubKey.Equals(ledger.CachedPubKey)
}
return false
}

+ 65
- 0
ledger_test.go View File

@ -0,0 +1,65 @@
package crypto
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRealLedgerSecp256k1(t *testing.T) {
if os.Getenv("WITH_LEDGER") == "" {
t.Skip("Set WITH_LEDGER to run code on real ledger")
}
msg := []byte("kuhehfeohg")
path := DerivationPath{44, 60, 0, 0, 0}
priv, err := NewPrivKeyLedgerSecp256k1(path)
require.Nil(t, err, "%+v", err)
pub, err := priv.PubKey()
require.Nil(t, err)
sig, err := priv.Sign(msg)
require.Nil(t, err)
valid := pub.VerifyBytes(msg, sig)
assert.True(t, valid)
// now, let's serialize the key and make sure it still works
bs := priv.Bytes()
priv2, err := PrivKeyFromBytes(bs)
require.Nil(t, err, "%+v", err)
// make sure we get the same pubkey when we load from disk
pub2, err := priv2.PubKey()
require.Nil(t, err)
require.Equal(t, pub, pub2)
// signing with the loaded key should match the original pubkey
sig, err = priv2.Sign(msg)
require.Nil(t, err)
valid = pub.VerifyBytes(msg, sig)
assert.True(t, valid)
// make sure pubkeys serialize properly as well
bs = pub.Bytes()
bpub, err := PubKeyFromBytes(bs)
require.NoError(t, err)
assert.Equal(t, pub, bpub)
}
// TestRealLedgerErrorHandling calls. These tests assume
// the ledger is not plugged in....
func TestRealLedgerErrorHandling(t *testing.T) {
if os.Getenv("WITH_LEDGER") != "" {
t.Skip("Skipping on WITH_LEDGER as it tests unplugged cases")
}
// first, try to generate a key, must return an error
// (no panic)
path := DerivationPath{44, 60, 0, 0, 0}
_, err := NewPrivKeyLedgerSecp256k1(path)
require.Error(t, err)
}

+ 5
- 8
merkle/simple_map.go View File

@ -24,16 +24,13 @@ func newSimpleMap() *simpleMap {
func (sm *simpleMap) Set(key string, value Hasher) {
sm.sorted = false
// Hash the key to blind it... why not?
khash := tmhash.Sum([]byte(key))
// And the value is hashed too, so you can
// The value is hashed, so you can
// check for equality with a cached value (say)
// and make a determination to fetch or not.
vhash := value.Hash()
sm.kvs = append(sm.kvs, cmn.KVPair{
Key: khash,
Key: []byte(key),
Value: vhash,
})
}
@ -67,9 +64,9 @@ func (sm *simpleMap) KVPairs() cmn.KVPairs {
// A local extension to KVPair that can be hashed.
// Key and value are length prefixed and concatenated,
// then hashed.
type kvPair cmn.KVPair
type KVPair cmn.KVPair
func (kv kvPair) Hash() []byte {
func (kv KVPair) Hash() []byte {
hasher := tmhash.New()
err := encodeByteSlice(hasher, kv.Key)
if err != nil {
@ -85,7 +82,7 @@ func (kv kvPair) Hash() []byte {
func hashKVPairs(kvs cmn.KVPairs) []byte {
kvsH := make([]Hasher, len(kvs))
for i, kvp := range kvs {
kvsH[i] = kvPair(kvp)
kvsH[i] = KVPair(kvp)
}
return SimpleHashFromHashers(kvsH)
}

+ 6
- 6
merkle/simple_map_test.go View File

@ -18,37 +18,37 @@ func TestSimpleMap(t *testing.T) {
{
db := newSimpleMap()
db.Set("key1", strHasher("value1"))
assert.Equal(t, "3dafc06a52039d029be57c75c9d16356a4256ef4", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
assert.Equal(t, "fa9bc106ffd932d919bee935ceb6cf2b3dd72d8f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := newSimpleMap()
db.Set("key1", strHasher("value2"))
assert.Equal(t, "03eb5cfdff646bc4e80fec844e72fd248a1c6b2c", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
assert.Equal(t, "e00e7dcfe54e9fafef5111e813a587f01ba9c3e8", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := newSimpleMap()
db.Set("key1", strHasher("value1"))
db.Set("key2", strHasher("value2"))
assert.Equal(t, "acc3971eab8513171cc90ce8b74f368c38f9657d", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
assert.Equal(t, "eff12d1c703a1022ab509287c0f196130123d786", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := newSimpleMap()
db.Set("key2", strHasher("value2")) // NOTE: out of order
db.Set("key1", strHasher("value1"))
assert.Equal(t, "acc3971eab8513171cc90ce8b74f368c38f9657d", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
assert.Equal(t, "eff12d1c703a1022ab509287c0f196130123d786", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := newSimpleMap()
db.Set("key1", strHasher("value1"))
db.Set("key2", strHasher("value2"))
db.Set("key3", strHasher("value3"))
assert.Equal(t, "0cd117ad14e6cd22edcd9aa0d84d7063b54b862f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
assert.Equal(t, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
{
db := newSimpleMap()
db.Set("key2", strHasher("value2")) // NOTE: out of order
db.Set("key1", strHasher("value1"))
db.Set("key3", strHasher("value3"))
assert.Equal(t, "0cd117ad14e6cd22edcd9aa0d84d7063b54b862f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
assert.Equal(t, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
}
}

+ 11
- 3
merkle/simple_proof.go View File

@ -27,7 +27,7 @@ func SimpleProofsFromHashers(items []Hasher) (rootHash []byte, proofs []*SimpleP
// SimpleProofsFromMap generates proofs from a map. The keys/values of the map will be used as the keys/values
// in the underlying key-value pairs.
// The keys are sorted before the proofs are computed.
func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs []*SimpleProof) {
func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs map[string]*SimpleProof, keys []string) {
sm := newSimpleMap()
for k, v := range m {
sm.Set(k, v)
@ -36,9 +36,17 @@ func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs []*Simple
kvs := sm.kvs
kvsH := make([]Hasher, 0, len(kvs))
for _, kvp := range kvs {
kvsH = append(kvsH, kvPair(kvp))
kvsH = append(kvsH, KVPair(kvp))
}
return SimpleProofsFromHashers(kvsH)
rootHash, proofList := SimpleProofsFromHashers(kvsH)
proofs = make(map[string]*SimpleProof)
keys = make([]string, len(proofList))
for i, kvp := range kvs {
proofs[string(kvp.Key)] = proofList[i]
keys[i] = string(kvp.Key)
}
return
}
// Verify that leafHash is a leaf hash of the simple-merkle-tree


+ 13
- 34
priv_key.go View File

@ -6,7 +6,6 @@ import (
secp256k1 "github.com/btcsuite/btcd/btcec"
"github.com/tendermint/ed25519"
"github.com/tendermint/ed25519/extra25519"
. "github.com/tendermint/tmlibs/common"
)
func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) {
@ -18,8 +17,8 @@ func PrivKeyFromBytes(privKeyBytes []byte) (privKey PrivKey, err error) {
type PrivKey interface {
Bytes() []byte
Sign(msg []byte) Signature
PubKey() PubKey
Sign(msg []byte) (Signature, error)
PubKey() (PubKey, error)
Equals(PrivKey) bool
}
@ -31,23 +30,19 @@ var _ PrivKey = PrivKeyEd25519{}
type PrivKeyEd25519 [64]byte
func (privKey PrivKeyEd25519) Bytes() []byte {
bz, err := cdc.MarshalBinaryBare(privKey)
if err != nil {
panic(err)
}
return bz
return cdc.MustMarshalBinaryBare(privKey)
}
func (privKey PrivKeyEd25519) Sign(msg []byte) Signature {
func (privKey PrivKeyEd25519) Sign(msg []byte) (Signature, error) {
privKeyBytes := [64]byte(privKey)
signatureBytes := ed25519.Sign(&privKeyBytes, msg)
return SignatureEd25519(*signatureBytes)
return SignatureEd25519(*signatureBytes), nil
}
func (privKey PrivKeyEd25519) PubKey() PubKey {
func (privKey PrivKeyEd25519) PubKey() (PubKey, error) {
privKeyBytes := [64]byte(privKey)
pubBytes := *ed25519.MakePublicKey(&privKeyBytes)
return PubKeyEd25519(pubBytes)
return PubKeyEd25519(pubBytes), nil
}
// Equals - you probably don't need to use this.
@ -67,12 +62,6 @@ 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 {
bz, err := cdc.MarshalBinaryBare(struct {
@ -114,27 +103,23 @@ var _ PrivKey = PrivKeySecp256k1{}
type PrivKeySecp256k1 [32]byte
func (privKey PrivKeySecp256k1) Bytes() []byte {
bz, err := cdc.MarshalBinaryBare(privKey)
if err != nil {
panic(err)
}
return bz
return cdc.MustMarshalBinaryBare(privKey)
}
func (privKey PrivKeySecp256k1) Sign(msg []byte) Signature {
func (privKey PrivKeySecp256k1) Sign(msg []byte) (Signature, error) {
priv__, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:])
sig__, err := priv__.Sign(Sha256(msg))
if err != nil {
PanicSanity(err)
return nil, err
}
return SignatureSecp256k1(sig__.Serialize())
return SignatureSecp256k1(sig__.Serialize()), nil
}
func (privKey PrivKeySecp256k1) PubKey() PubKey {
func (privKey PrivKeySecp256k1) PubKey() (PubKey, error) {
_, pub__ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:])
var pub PubKeySecp256k1
copy(pub[:], pub__.SerializeCompressed())
return pub
return pub, nil
}
// Equals - you probably don't need to use this.
@ -147,12 +132,6 @@ func (privKey PrivKeySecp256k1) Equals(other PrivKey) bool {
}
}
/*
func (privKey PrivKeySecp256k1) String() string {
return Fmt("PrivKeySecp256k1{*****}")
}
*/
/*
// Deterministically generates new priv-key bytes from key.
func (key PrivKeySecp256k1) Generate(index int) PrivKeySecp256k1 {


+ 5
- 1
priv_key_test.go View File

@ -11,7 +11,11 @@ func TestGeneratePrivKey(t *testing.T) {
testPriv := crypto.GenPrivKeyEd25519()
testGenerate := testPriv.Generate(1)
signBytes := []byte("something to sign")
assert.True(t, testGenerate.PubKey().VerifyBytes(signBytes, testGenerate.Sign(signBytes)))
pub, err := testGenerate.PubKey()
assert.NoError(t, err)
sig, err := testGenerate.Sign(signBytes)
assert.NoError(t, err)
assert.True(t, pub.VerifyBytes(signBytes, sig))
}
/*


+ 8
- 5
pub_key.go View File

@ -5,11 +5,16 @@ import (
"crypto/sha256"
"fmt"
"golang.org/x/crypto/ripemd160"
secp256k1 "github.com/btcsuite/btcd/btcec"
"github.com/tendermint/ed25519"
"github.com/tendermint/ed25519/extra25519"
cmn "github.com/tendermint/tmlibs/common"
"golang.org/x/crypto/ripemd160"
"github.com/tendermint/go-crypto/tmhash"
)
// An address is a []byte, but hex-encoded even in JSON.
@ -38,11 +43,9 @@ var _ PubKey = PubKeyEd25519{}
// Implements PubKeyInner
type PubKeyEd25519 [32]byte
// Address is the SHA256-20 of the raw pubkey bytes.
func (pubKey PubKeyEd25519) Address() Address {
// append type byte
hasher := ripemd160.New()
hasher.Write(pubKey.Bytes()) // does not error
return Address(hasher.Sum(nil))
return Address(tmhash.Sum(pubKey[:]))
}
func (pubKey PubKeyEd25519) Bytes() []byte {


+ 4
- 2
pub_key_test.go View File

@ -33,9 +33,11 @@ func TestPubKeySecp256k1Address(t *testing.T) {
var priv PrivKeySecp256k1
copy(priv[:], privB)
pubT := priv.PubKey().(PubKeySecp256k1)
pubKey, err := priv.PubKey()
assert.NoError(t, err)
pubT, _ := pubKey.(PubKeySecp256k1)
pub := pubT[:]
addr := priv.PubKey().Address()
addr := pubKey.Address()
assert.Equal(t, pub, pubB, "Expected pub keys to match")
assert.Equal(t, addr, addrB, "Expected addresses to match")


+ 6
- 0
signature.go View File

@ -80,3 +80,9 @@ func (sig SignatureSecp256k1) Equals(other Signature) bool {
return false
}
}
func SignatureSecp256k1FromBytes(data []byte) Signature {
sig := make(SignatureSecp256k1, len(data))
copy(sig[:], data)
return sig
}

+ 9
- 4
signature_test.go View File

@ -3,16 +3,19 @@ package crypto
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/assert"
)
func TestSignAndValidateEd25519(t *testing.T) {
privKey := GenPrivKeyEd25519()
pubKey := privKey.PubKey()
pubKey, err := privKey.PubKey()
require.Nil(t, err)
msg := CRandBytes(128)
sig := privKey.Sign(msg)
sig, err := privKey.Sign(msg)
require.Nil(t, err)
// Test the signature
assert.True(t, pubKey.VerifyBytes(msg, sig))
@ -27,10 +30,12 @@ func TestSignAndValidateEd25519(t *testing.T) {
func TestSignAndValidateSecp256k1(t *testing.T) {
privKey := GenPrivKeySecp256k1()
pubKey := privKey.PubKey()
pubKey, err := privKey.PubKey()
require.Nil(t, err)
msg := CRandBytes(128)
sig := privKey.Sign(msg)
sig, err := privKey.Sign(msg)
require.Nil(t, err)
assert.True(t, pubKey.VerifyBytes(msg, sig))


+ 1
- 1
version.go View File

@ -1,3 +1,3 @@
package crypto
const Version = "0.6.2"
const Version = "0.8.0-dev"

Loading…
Cancel
Save