github.com/cosmos/cosmos-sdk@v0.50.10/crypto/keyring/keyring_ledger_test.go (about)

     1  //go:build ledger || test_ledger_mock
     2  // +build ledger test_ledger_mock
     3  
     4  package keyring
     5  
     6  import (
     7  	"bytes"
     8  	"testing"
     9  
    10  	"github.com/cockroachdb/errors"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"github.com/cosmos/cosmos-sdk/crypto/hd"
    15  	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
    16  	"github.com/cosmos/cosmos-sdk/crypto/ledger"
    17  	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
    18  	"github.com/cosmos/cosmos-sdk/types"
    19  	"github.com/cosmos/cosmos-sdk/types/tx/signing"
    20  )
    21  
    22  func TestInMemoryCreateLedger(t *testing.T) {
    23  	cdc := getCodec()
    24  	kb := NewInMemory(cdc)
    25  
    26  	k, err := kb.SaveLedgerKey("some_account", hd.Secp256k1, "cosmos", 118, 3, 1)
    27  	if err != nil {
    28  		require.Error(t, err)
    29  		require.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error())
    30  		require.Nil(t, k)
    31  		t.Skip("ledger nano S: support for ledger devices is not available in this executable")
    32  		return
    33  	}
    34  
    35  	// The mock is available, check that the address is correct
    36  	pubKey, err := k.GetPubKey()
    37  	require.NoError(t, err)
    38  	expectedPkStr := "PubKeySecp256k1{03602C0CB4D8C0081FEE794BDE96E7B95FA16F2B5283B764AC070584327B2C7202}"
    39  	require.Equal(t, expectedPkStr, pubKey.String())
    40  
    41  	// Check that restoring the key gets the same results
    42  	restoredRecord, err := kb.Key("some_account")
    43  	require.NoError(t, err)
    44  	require.NotNil(t, restoredRecord)
    45  	require.Equal(t, "some_account", restoredRecord.Name)
    46  	pubKey, err = restoredRecord.GetPubKey()
    47  	require.NoError(t, err)
    48  	require.Equal(t, expectedPkStr, pubKey.String())
    49  
    50  	ledgerInfo := restoredRecord.GetLedger()
    51  	require.NotNil(t, ledgerInfo)
    52  	path := ledgerInfo.GetPath()
    53  	require.Equal(t, "m/44'/118'/3'/0/1", path.String())
    54  }
    55  
    56  // TestSignVerify does some detailed checks on how we sign and validate
    57  // signatures
    58  func TestSignVerifyKeyRingWithLedger(t *testing.T) {
    59  	dir := t.TempDir()
    60  	cdc := getCodec()
    61  
    62  	kb, err := New("keybasename", "test", dir, nil, cdc)
    63  	require.NoError(t, err)
    64  
    65  	k, err := kb.SaveLedgerKey("key", hd.Secp256k1, "cosmos", 118, 0, 0)
    66  	if err != nil {
    67  		require.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error())
    68  		t.Skip("ledger nano S: support for ledger devices is not available in this executable")
    69  		return
    70  	}
    71  	require.Equal(t, "key", k.Name)
    72  
    73  	d1 := []byte("my first message")
    74  	s1, pub1, err := kb.Sign("key", d1, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
    75  	require.NoError(t, err)
    76  
    77  	s2, pub2, err := SignWithLedger(k, d1, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
    78  	require.NoError(t, err)
    79  
    80  	require.True(t, pub1.Equals(pub2))
    81  	require.True(t, bytes.Equal(s1, s2))
    82  
    83  	key1, err := k.GetPubKey()
    84  	require.NoError(t, err)
    85  
    86  	require.Equal(t, key1, pub1)
    87  	require.Equal(t, key1, pub2)
    88  	require.True(t, pub1.VerifySignature(d1, s1))
    89  	require.True(t, key1.VerifySignature(d1, s1))
    90  	require.True(t, bytes.Equal(s1, s2))
    91  
    92  	k, _, err = kb.NewMnemonic("test", English, types.FullFundraiserPath, DefaultBIP39Passphrase, hd.Secp256k1)
    93  	require.NoError(t, err)
    94  	_, _, err = SignWithLedger(k, d1, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
    95  	require.Error(t, err)
    96  	require.Equal(t, "not a ledger object", err.Error())
    97  }
    98  
    99  func TestAltKeyring_SaveLedgerKey(t *testing.T) {
   100  	dir := t.TempDir()
   101  	cdc := getCodec()
   102  
   103  	kr, err := New(t.Name(), BackendTest, dir, nil, cdc)
   104  	require.NoError(t, err)
   105  
   106  	// Test unsupported Algo
   107  	_, err = kr.SaveLedgerKey("key", notSupportedAlgo{}, "cosmos", 118, 0, 0)
   108  	require.True(t, errors.Is(err, ErrUnsupportedSigningAlgo))
   109  
   110  	k, err := kr.SaveLedgerKey("some_account", hd.Secp256k1, "cosmos", 118, 3, 1)
   111  	if err != nil {
   112  		require.Equal(t, "ledger nano S: support for ledger devices is not available in this executable", err.Error())
   113  		t.Skip("ledger nano S: support for ledger devices is not available in this executable")
   114  		return
   115  	}
   116  
   117  	// The mock is available, check that the address is correct
   118  	require.Equal(t, "some_account", k.Name)
   119  	pubKey, err := k.GetPubKey()
   120  	require.NoError(t, err)
   121  	expectedPkStr := "PubKeySecp256k1{03602C0CB4D8C0081FEE794BDE96E7B95FA16F2B5283B764AC070584327B2C7202}"
   122  	require.Equal(t, expectedPkStr, pubKey.String())
   123  
   124  	// Check that restoring the key gets the same results
   125  	restoredRecord, err := kr.Key("some_account")
   126  	require.NoError(t, err)
   127  	require.NotNil(t, restoredRecord)
   128  	require.Equal(t, "some_account", restoredRecord.Name)
   129  	//	require.Equal(t, TypeLedger, restoredRecord.GetType())
   130  	pubKey, err = restoredRecord.GetPubKey()
   131  	require.NoError(t, err)
   132  	require.Equal(t, expectedPkStr, pubKey.String())
   133  
   134  	ledgerInfo := k.GetLedger()
   135  	require.NotNil(t, ledgerInfo)
   136  
   137  	path := ledgerInfo.GetPath()
   138  	require.Equal(t, "m/44'/118'/3'/0/1", path.String())
   139  }
   140  
   141  func TestSignWithLedger(t *testing.T) {
   142  	// Create two distinct Ledger records: recordA and recordB.
   143  	// RecordA is added to the Ledger but recordB is not added.
   144  	pathA := hd.NewFundraiserParams(0, types.CoinType, 0)
   145  	privA, _, err := ledger.NewPrivKeySecp256k1(*pathA, "cosmos")
   146  	require.NoError(t, err)
   147  	recordA, err := NewLedgerRecord("ledgerA", privA.PubKey(), pathA)
   148  	require.NoError(t, err)
   149  	pubA, err := recordA.GetPubKey()
   150  	require.NoError(t, err)
   151  
   152  	pathB := hd.NewFundraiserParams(0, types.CoinType, 1)
   153  	// privB won't be added to the Ledger because it doesn't use ledger.NewPrivKeySecp256k1
   154  	privB := secp256k1.GenPrivKey()
   155  	recordB, err := NewLedgerRecord("ledgerB", privB.PubKey(), pathB)
   156  	require.NoError(t, err)
   157  	pubB, err := recordB.GetPubKey()
   158  	require.NoError(t, err)
   159  
   160  	require.NotEqual(t, pubA, pubB)
   161  	type testCase struct {
   162  		name            string
   163  		record          *Record
   164  		msg             []byte
   165  		wantSig         []byte
   166  		wantPub         cryptotypes.PubKey
   167  		wantErr         bool
   168  		wantErrContains string
   169  	}
   170  	testCases := []testCase{
   171  		{
   172  			name:    "ordinary ledger tx",
   173  			record:  recordA,
   174  			msg:     []byte("msg"),
   175  			wantSig: []byte{0xfb, 0x93, 0x1b, 0xb9, 0x75, 0x25, 0xe7, 0x99, 0x64, 0xc2, 0x78, 0xf7, 0x94, 0x9a, 0x63, 0x83, 0xe2, 0x59, 0x76, 0x48, 0x1d, 0x2, 0xbc, 0xc2, 0x83, 0x21, 0x24, 0x4b, 0x95, 0x99, 0x25, 0x8b, 0x30, 0x38, 0x6, 0x61, 0x79, 0x9a, 0x9e, 0x8, 0x98, 0xfd, 0x34, 0xc6, 0x7e, 0x47, 0x4d, 0x5f, 0xe, 0xf3, 0xc3, 0xe7, 0xdd, 0xe3, 0x89, 0x80, 0xda, 0x8b, 0x48, 0x15, 0x34, 0xce, 0xdf, 0x1c},
   176  			wantPub: pubA,
   177  			wantErr: false,
   178  		},
   179  		{
   180  			name:            "want error when the public key the user attempted to sign with doesn't match the public key on the ledger",
   181  			record:          recordB,
   182  			msg:             []byte("msg"),
   183  			wantSig:         []byte(nil),
   184  			wantPub:         nil,
   185  			wantErr:         true,
   186  			wantErrContains: "the public key that the user attempted to sign with does not match the public key on the ledger device",
   187  		},
   188  	}
   189  
   190  	for _, tc := range testCases {
   191  		t.Run(tc.name, func(t *testing.T) {
   192  			sig, pub, err := SignWithLedger(tc.record, tc.msg, signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
   193  			assert.Equal(t, tc.wantSig, sig)
   194  			assert.Equal(t, tc.wantPub, pub)
   195  			if tc.wantErr {
   196  				assert.Error(t, err)
   197  				assert.Contains(t, err.Error(), tc.wantErrContains)
   198  			}
   199  		})
   200  	}
   201  }