diff --git a/crypto/keyring/keyring.go b/crypto/keyring/keyring.go index 8d5c707f1a9c..58daefb15296 100644 --- a/crypto/keyring/keyring.go +++ b/crypto/keyring/keyring.go @@ -586,7 +586,7 @@ func SignWithLedger(k *Record, msg []byte) (sig []byte, pub types.PubKey, err er return } - sig, err = priv.Sign(msg) + sig, err = priv.SignLedgerAminoJSON(msg) if err != nil { return nil, nil, err } diff --git a/crypto/ledger/ledger_mock.go b/crypto/ledger/ledger_mock.go index 21e18cc6c99a..60d6471cef0b 100644 --- a/crypto/ledger/ledger_mock.go +++ b/crypto/ledger/ledger_mock.go @@ -84,7 +84,7 @@ func (mock LedgerSECP256K1Mock) GetAddressPubKeySECP256K1(derivationPath []uint3 return pk, addr, err } -func (mock LedgerSECP256K1Mock) SignSECP256K1(derivationPath []uint32, message []byte) ([]byte, error) { +func (mock LedgerSECP256K1Mock) SignSECP256K1(derivationPath []uint32, message []byte, p2 byte) ([]byte, error) { path := hd.NewParams(derivationPath[0], derivationPath[1], derivationPath[2], derivationPath[3] != 0, derivationPath[4]) seed, err := bip39.NewSeedWithErrorChecking(testdata.TestMnemonic, "") if err != nil { diff --git a/crypto/ledger/ledger_secp256k1.go b/crypto/ledger/ledger_secp256k1.go index 29f50ad4e212..fac4e1716605 100644 --- a/crypto/ledger/ledger_secp256k1.go +++ b/crypto/ledger/ledger_secp256k1.go @@ -32,7 +32,10 @@ type ( // Returns a compressed pubkey and bech32 address (requires user confirmation) GetAddressPubKeySECP256K1([]uint32, string) ([]byte, string, error) // Signs a message (requires user confirmation) - SignSECP256K1([]uint32, []byte) ([]byte, error) + // The last byte denotes the SIGN_MODE to be used by Ledger: 0 for + // LEGACY_AMINO_JSON, 1 for TEXTUAL. It corresponds to the P2 value + // in https://github.com/cosmos/ledger-cosmos/blob/main/docs/APDUSPEC.md + SignSECP256K1([]uint32, []byte, byte) ([]byte, error) } // PrivKeyLedgerSecp256k1 implements PrivKey, calling the ledger nano we @@ -51,7 +54,7 @@ type ( // This function is marked as unsafe as it will retrieve a pubkey without user verification. // It can only be used to verify a pubkey but never to create new accounts/keys. In that case, // please refer to NewPrivKeySecp256k1 -func NewPrivKeySecp256k1Unsafe(path hd.BIP44Params) (types.LedgerPrivKey, error) { +func NewPrivKeySecp256k1Unsafe(path hd.BIP44Params) (types.LedgerPrivKeyAminoJSON, error) { device, err := getDevice() if err != nil { return nil, err @@ -88,7 +91,8 @@ func (pkl PrivKeyLedgerSecp256k1) PubKey() types.PubKey { return pkl.CachedPubKey } -// Sign returns a secp256k1 signature for the corresponding message +// Sign returns a secp256k1 signature for the corresponding message using +// SIGN_MODE_TEXTUAL. func (pkl PrivKeyLedgerSecp256k1) Sign(message []byte) ([]byte, error) { device, err := getDevice() if err != nil { @@ -96,7 +100,19 @@ func (pkl PrivKeyLedgerSecp256k1) Sign(message []byte) ([]byte, error) { } defer warnIfErrors(device.Close) - return sign(device, pkl, message) + return sign(device, pkl, message, 1) +} + +// SignLedgerAminoJSON returns a secp256k1 signature for the corresponding message using +// SIGN_MODE_LEGACY_AMINO_JSON. +func (pkl PrivKeyLedgerSecp256k1) SignLedgerAminoJSON(message []byte) ([]byte, error) { + device, err := getDevice() + if err != nil { + return nil, err + } + defer warnIfErrors(device.Close) + + return sign(device, pkl, message, 0) } // ShowAddress triggers a ledger device to show the corresponding address. @@ -228,13 +244,15 @@ func validateKey(device SECP256K1, pkl PrivKeyLedgerSecp256k1) error { // Communication is checked on NewPrivKeyLedger and PrivKeyFromBytes, returning // an error, so this should only trigger if the private key is held in memory // for a while before use. -func sign(device SECP256K1, pkl PrivKeyLedgerSecp256k1, msg []byte) ([]byte, error) { +// +// Last byte P2 is 0 for LEGACY_AMINO_JSON, and 1 for TEXTUAL. +func sign(device SECP256K1, pkl PrivKeyLedgerSecp256k1, msg []byte, p2 byte) ([]byte, error) { err := validateKey(device, pkl) if err != nil { return nil, err } - sig, err := device.SignSECP256K1(pkl.Path.DerivationPath(), msg) + sig, err := device.SignSECP256K1(pkl.Path.DerivationPath(), msg, p2) if err != nil { return nil, err } diff --git a/crypto/types/types.go b/crypto/types/types.go index eccdba73813d..296900776563 100644 --- a/crypto/types/types.go +++ b/crypto/types/types.go @@ -29,6 +29,17 @@ type LedgerPrivKey interface { Type() string } +// LedgerPrivKeyAminoJSON is a Ledger PrivKey type that supports signing with +// SIGN_MODE_LEGACY_AMINO_JSON. It is added as a non-breaking change, instead of directly +// on the LedgerPrivKey interface (whose Sign method will sign with TEXTUAL), +// and will be deprecated/removed once LEGACY_AMINO_JSON is removed. +type LedgerPrivKeyAminoJSON interface { + LedgerPrivKey + // SignLedgerAminoJSON signs a messages on the Ledger device using + // SIGN_MODE_LEGACY_AMINO_JSON. + SignLedgerAminoJSON(msg []byte) ([]byte, error) +} + // PrivKey defines a private key and extends proto.Message. For now, it extends // LedgerPrivKey (see godoc for LedgerPrivKey). Ultimately, we should remove // LedgerPrivKey and add its methods here directly.