mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-29 22:21:21 +00:00
Add ed25519, tests will fail until ed25519 verification fix
This commit is contained in:
parent
3186dc4cef
commit
ee411daa17
19
ledger_common.go
Normal file
19
ledger_common.go
Normal 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
|
||||||
|
}
|
156
ledger_ed25519.go
Normal file
156
ledger_ed25519.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
|
// "github.com/tendermint/ed25519"
|
||||||
|
ledger "github.com/zondax/ledger-goclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pubkeyLedgerEd25519(device *ledger.Ledger, path DerivationPath) (pub PubKey, err error) {
|
||||||
|
key, err := device.GetPublicKeyED25519(path)
|
||||||
|
if err != nil {
|
||||||
|
return pub, fmt.Errorf("Error fetching public key: %v", err)
|
||||||
|
}
|
||||||
|
var p PubKeyEd25519
|
||||||
|
copy(p[:], key[0:32])
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func signLedgerEd25519(device *ledger.Ledger, path DerivationPath, msg []byte) (sig Signature, err error) {
|
||||||
|
bsig, err := device.SignED25519(path, msg)
|
||||||
|
if err != nil {
|
||||||
|
return sig, err
|
||||||
|
}
|
||||||
|
sig = SignatureEd25519FromBytes(bsig)
|
||||||
|
return sig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivKeyLedgerEd25519 implements PrivKey, calling the ledger nano
|
||||||
|
// we cache the PubKey from the first call to use it later
|
||||||
|
type PrivKeyLedgerEd25519 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPrivKeyLedgerEd25519 will generate a new key and store the
|
||||||
|
// public key for later use.
|
||||||
|
func NewPrivKeyLedgerEd25519(path DerivationPath) (PrivKey, error) {
|
||||||
|
var pk PrivKeyLedgerEd25519
|
||||||
|
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 PrivKeyLedgerEd25519) 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 errors.New("Cached key does not match retrieved key")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertIsPrivKeyInner fulfils PrivKey Interface
|
||||||
|
func (pk *PrivKeyLedgerEd25519) 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 PrivKeyLedgerEd25519) Bytes() []byte {
|
||||||
|
bin, err := cdc.MarshalBinaryBare(pk)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return bin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign calls the ledger and stores the PubKey for future use
|
||||||
|
//
|
||||||
|
// XXX/TODO: panics if there is an error communicating with the ledger.
|
||||||
|
//
|
||||||
|
// 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 PrivKeyLedgerEd25519) Sign(msg []byte) Signature {
|
||||||
|
// oh, I wish there was better error handling
|
||||||
|
dev, err := getLedger()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sig, err := signLedgerEd25519(dev, pk.Path, msg)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub, err := pubkeyLedgerEd25519(dev, pk.Path)
|
||||||
|
if err != nil {
|
||||||
|
panic(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) {
|
||||||
|
panic("Stored key does not match signing key")
|
||||||
|
}
|
||||||
|
return sig
|
||||||
|
}
|
||||||
|
|
||||||
|
// PubKey returns the stored PubKey
|
||||||
|
// TODO: query the ledger if not there, once it is not volatile
|
||||||
|
func (pk PrivKeyLedgerEd25519) PubKey() PubKey {
|
||||||
|
key, err := pk.getPubKey()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 PrivKeyLedgerEd25519) 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 PrivKeyLedgerEd25519) forceGetPubKey() (key PubKey, err error) {
|
||||||
|
dev, err := getLedger()
|
||||||
|
if err != nil {
|
||||||
|
return key, errors.New(fmt.Sprintf("Cannot connect to Ledger device - error: %v", err))
|
||||||
|
}
|
||||||
|
key, err = pubkeyLedgerEd25519(dev, pk.Path)
|
||||||
|
if err != nil {
|
||||||
|
return key, errors.New(fmt.Sprintf("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 PrivKeyLedgerEd25519) Equals(other PrivKey) bool {
|
||||||
|
if ledger, ok := other.(*PrivKeyLedgerEd25519); ok {
|
||||||
|
return pk.CachedPubKey.Equals(ledger.CachedPubKey)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
@ -8,19 +8,6 @@ import (
|
|||||||
ledger "github.com/zondax/ledger-goclient"
|
ledger "github.com/zondax/ledger-goclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
var device *ledger.Ledger
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func pubkeyLedgerSecp256k1(device *ledger.Ledger, path DerivationPath) (pub PubKey, err error) {
|
func pubkeyLedgerSecp256k1(device *ledger.Ledger, path DerivationPath) (pub PubKey, err error) {
|
||||||
key, err := device.GetPublicKeySECP256K1(path)
|
key, err := device.GetPublicKeySECP256K1(path)
|
||||||
if err != nil {
|
if err != nil {
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRealLedger(t *testing.T) {
|
func TestRealLedgerSecp256k1(t *testing.T) {
|
||||||
|
|
||||||
if os.Getenv("WITH_LEDGER") == "" {
|
if os.Getenv("WITH_LEDGER") == "" {
|
||||||
t.Skip("Set WITH_LEDGER to run code on real ledger")
|
t.Skip("Set WITH_LEDGER to run code on real ledger")
|
||||||
@ -46,6 +46,44 @@ func TestRealLedger(t *testing.T) {
|
|||||||
assert.Equal(t, pub, bpub)
|
assert.Equal(t, pub, bpub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRealLedgerEd25519(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 := NewPrivKeyLedgerEd25519(path)
|
||||||
|
require.Nil(t, err, "%+v", err)
|
||||||
|
pub := priv.PubKey()
|
||||||
|
sig := priv.Sign(msg)
|
||||||
|
|
||||||
|
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 := priv2.PubKey()
|
||||||
|
require.Equal(t, pub, pub2)
|
||||||
|
|
||||||
|
// signing with the loaded key should match the original pubkey
|
||||||
|
sig = priv2.Sign(msg)
|
||||||
|
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
|
// TestRealLedgerErrorHandling calls. These tests assume
|
||||||
// the ledger is not plugged in....
|
// the ledger is not plugged in....
|
||||||
func TestRealLedgerErrorHandling(t *testing.T) {
|
func TestRealLedgerErrorHandling(t *testing.T) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user