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

# Conflicts:
#	Gopkg.lock
#	keys/keybase_test.go
#	signature_test.go
This commit is contained in:
Liamsi
2018-06-11 16:46:08 -07:00
23 changed files with 697 additions and 220 deletions

View File

@ -1,5 +1,39 @@
# Changelog # 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) ## 0.6.2 (April 9, 2018)
IMPROVEMENTS IMPROVEMENTS

33
Gopkg.lock generated
View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,9 @@
package keys package keys
import ( import (
"bufio"
"fmt" "fmt"
"os"
"strings" "strings"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -26,23 +28,23 @@ func New(db dbm.DB, codec words.Codec) dbKeybase {
var _ Keybase = 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 // using the passphrase. It returns the generated seedphrase
// (mnemonic) and the key Info. It returns an error if it fails to // (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 // generate a key for the given algo type, or if another key is
// already stored under the same name. // 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. // NOTE: secret is SHA256 hashed by secp256k1 and ed25519.
// 16 byte secret corresponds to 12 BIP39 words. // 16 byte secret corresponds to 12 BIP39 words.
// XXX: Ledgers use 24 words now - should we ? // XXX: Ledgers use 24 words now - should we ?
secret := crypto.CRandBytes(16) secret := crypto.CRandBytes(16)
priv, err := generate(algo, secret) priv, err := generate(algo, secret)
if err != nil { if err != nil {
return Info{}, "", err return nil, "", err
} }
// encrypt and persist the key // 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 // we append the type byte to the serialized secret to help with
// recovery // recovery
@ -56,6 +58,29 @@ func (kb dbKeybase) Create(name, passphrase string, algo CryptoAlgo) (Info, stri
return info, seed, err 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, // Recover converts a seedphrase to a private key and persists it,
// encrypted with the given passphrase. Functions like Create, but // encrypted with the given passphrase. Functions like Create, but
// seedphrase is input not output. // 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), " ") words := strings.Split(strings.TrimSpace(seedphrase), " ")
secret, err := kb.codec.WordsToBytes(words) secret, err := kb.codec.WordsToBytes(words)
if err != nil { if err != nil {
return Info{}, err return nil, err
} }
// secret is comprised of the actual secret with the type // secret is comprised of the actual secret with the type
// appended. // appended.
// ie [secret] = [type] + [secret] // ie [secret] = [type] + [secret]
typ, secret := secret[0], secret[1:] typ, secret := secret[0], secret[1:]
algo := byteToCryptoAlgo(typ) algo := byteToSignAlgo(typ)
priv, err := generate(algo, secret) priv, err := generate(algo, secret)
if err != nil { if err != nil {
return Info{}, err return nil, err
} }
// encrypt and persist key. // encrypt and persist key.
public := kb.writeKey(priv, name, passphrase) public := kb.writeLocalKey(priv, name, passphrase)
return public, err return public, nil
} }
// List returns the keys from storage in alphabetical order. // 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) iter := kb.db.Iterator(nil, nil)
defer iter.Close() defer iter.Close()
for ; iter.Valid(); iter.Next() { for ; iter.Valid(); iter.Next() {
// key := iter.Key()
info, err := readInfo(iter.Value()) info, err := readInfo(iter.Value())
if err != nil { if err != nil {
return nil, err return nil, err
@ -110,17 +134,46 @@ func (kb dbKeybase) Sign(name, passphrase string, msg []byte) (sig crypto.Signat
if err != nil { if err != nil {
return return
} }
if info.PrivKeyArmor == "" { var priv crypto.PrivKey
err = fmt.Errorf("private key not available") switch info.(type) {
return 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 { if err != nil {
return return nil, nil, err
} }
sig = priv.Sign(msg) pub, err = priv.PubKey()
pub = priv.PubKey() if err != nil {
return return nil, nil, err
}
return sig, pub, nil
} }
func (kb dbKeybase) Export(name string) (armor string, err error) { 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 { if err != nil {
return return
} }
return armorPubKeyBytes(info.PubKey.Bytes()), nil return armorPubKeyBytes(info.GetPubKey().Bytes()), nil
} }
func (kb dbKeybase) Import(name string, armor string) (err error) { 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 { if err != nil {
return return
} }
kb.writePubKey(pubKey, name) kb.writeOfflineKey(pubKey, name)
return return
} }
// Delete removes key forever, but we must present the // Delete removes key forever, but we must present the
// proper passphrase before deleting it (for security). // 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 { func (kb dbKeybase) Delete(name, passphrase string) error {
// verify we have the proper password before deleting // verify we have the proper password before deleting
info, err := kb.Get(name) info, err := kb.Get(name)
if err != nil { if err != nil {
return err return err
} }
_, err = unarmorDecryptPrivKey(info.PrivKeyArmor, passphrase) switch info.(type) {
if err != nil { case localInfo:
return err 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 return nil
} }
@ -205,36 +272,51 @@ func (kb dbKeybase) Update(name, oldpass, newpass string) error {
if err != nil { if err != nil {
return err return err
} }
key, err := unarmorDecryptPrivKey(info.PrivKeyArmor, oldpass) switch info.(type) {
if err != nil { case localInfo:
return err 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 {
// make Info // encrypt private key using passphrase
info := newInfo(name, pub, "")
// write them both
kb.db.SetSync(infoKey(name), info.bytes())
return info
}
func (kb dbKeybase) writeKey(priv crypto.PrivKey, name, passphrase string) Info {
// generate the encrypted privkey
privArmor := encryptArmorPrivKey(priv, passphrase) privArmor := encryptArmorPrivKey(priv, passphrase)
// make Info // make Info
info := newInfo(name, priv.PubKey(), privArmor) pub, err := priv.PubKey()
if err != nil {
// write them both panic(err)
kb.db.SetSync(infoKey(name), info.bytes()) }
info := newLocalInfo(name, pub, privArmor)
kb.writeInfo(info, name)
return info return info
} }
func generate(algo CryptoAlgo, secret []byte) (crypto.PrivKey, error) { func (kb dbKeybase) writeLedgerKey(pub crypto.PubKey, path crypto.DerivationPath, name string) Info {
info := newLedgerInfo(name, pub, path)
kb.writeInfo(info, name)
return info
}
func (kb dbKeybase) writeOfflineKey(pub crypto.PubKey, name string) Info {
info := newOfflineInfo(name, pub)
kb.writeInfo(info, name)
return info
}
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 { switch algo {
case AlgoEd25519: case AlgoEd25519:
return crypto.GenPrivKeyEd25519FromSecret(secret), nil return crypto.GenPrivKeyEd25519FromSecret(secret), nil

View File

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

View File

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

View File

@ -4,54 +4,136 @@ import (
crypto "github.com/tendermint/go-crypto" 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 { type Keybase interface {
// Sign some bytes
Sign(name, passphrase string, msg []byte) (crypto.Signature, crypto.PubKey, error) // CRUD on the keystore
// 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) List() ([]Info, error)
Get(name string) (Info, error) Get(name string) (Info, error)
Update(name, oldpass, newpass string) error
Delete(name, passphrase 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) Import(name string, armor string) (err error)
ImportPubKey(name string, armor string) (err error) ImportPubKey(name string, armor string) (err error)
Export(name string) (armor string, err error) Export(name string) (armor string, err error)
ExportPubKey(name string) (armor string, err error) ExportPubKey(name string) (armor string, err error)
} }
// Info is the public information about a key // Publically exposed information about a keypair
type Info struct { 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"` Name string `json:"name"`
PubKey crypto.PubKey `json:"pubkey"` PubKey crypto.PubKey `json:"pubkey"`
PrivKeyArmor string `json:"privkey.armor"` PrivKeyArmor string `json:"privkey.armor"`
} }
func newInfo(name string, pub crypto.PubKey, privArmor string) Info { func newLocalInfo(name string, pub crypto.PubKey, privArmor string) Info {
return Info{ return &localInfo{
Name: name, Name: name,
PubKey: pub, PubKey: pub,
PrivKeyArmor: privArmor, PrivKeyArmor: privArmor,
} }
} }
// Address is a helper function to calculate the address from the pubkey func (i localInfo) GetType() string {
func (i Info) Address() []byte { return "local"
return i.PubKey.Address()
} }
func (i Info) bytes() []byte { func (i localInfo) GetName() string {
bz, err := cdc.MarshalBinaryBare(i) return i.Name
if err != nil { }
panic(err)
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,
} }
return bz
} }
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,
}
}
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) { func readInfo(bz []byte) (info Info, err error) {
err = cdc.UnmarshalBinaryBare(bz, &info) err = cdc.UnmarshalBinary(bz, &info)
return return
} }

View File

@ -9,4 +9,8 @@ var cdc = amino.NewCodec()
func init() { func init() {
crypto.RegisterAmino(cdc) 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
ledger_common.go Normal file
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
ledger_secp256k1.go Normal file
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
ledger_test.go Normal file
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)
}

View File

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

View File

@ -18,37 +18,37 @@ func TestSimpleMap(t *testing.T) {
{ {
db := newSimpleMap() db := newSimpleMap()
db.Set("key1", strHasher("value1")) 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 := newSimpleMap()
db.Set("key1", strHasher("value2")) 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 := newSimpleMap()
db.Set("key1", strHasher("value1")) db.Set("key1", strHasher("value1"))
db.Set("key2", strHasher("value2")) 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 := newSimpleMap()
db.Set("key2", strHasher("value2")) // NOTE: out of order db.Set("key2", strHasher("value2")) // NOTE: out of order
db.Set("key1", strHasher("value1")) 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 := newSimpleMap()
db.Set("key1", strHasher("value1")) db.Set("key1", strHasher("value1"))
db.Set("key2", strHasher("value2")) db.Set("key2", strHasher("value2"))
db.Set("key3", strHasher("value3")) 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 := newSimpleMap()
db.Set("key2", strHasher("value2")) // NOTE: out of order db.Set("key2", strHasher("value2")) // NOTE: out of order
db.Set("key1", strHasher("value1")) db.Set("key1", strHasher("value1"))
db.Set("key3", strHasher("value3")) 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")
} }
} }

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 // 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. // in the underlying key-value pairs.
// The keys are sorted before the proofs are computed. // 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() sm := newSimpleMap()
for k, v := range m { for k, v := range m {
sm.Set(k, v) sm.Set(k, v)
@ -36,9 +36,17 @@ func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs []*Simple
kvs := sm.kvs kvs := sm.kvs
kvsH := make([]Hasher, 0, len(kvs)) kvsH := make([]Hasher, 0, len(kvs))
for _, kvp := range 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 // Verify that leafHash is a leaf hash of the simple-merkle-tree

View File

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

View File

@ -11,7 +11,11 @@ func TestGeneratePrivKey(t *testing.T) {
testPriv := crypto.GenPrivKeyEd25519() testPriv := crypto.GenPrivKeyEd25519()
testGenerate := testPriv.Generate(1) testGenerate := testPriv.Generate(1)
signBytes := []byte("something to sign") 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))
} }
/* /*

View File

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

View File

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

View File

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

View File

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

View File

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