github.com/ethersphere/bee/v2@v2.2.0/pkg/crypto/clef/clef_test.go (about)

     1  // Copyright 2020 The Swarm Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package clef_test
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/ecdsa"
    10  	"errors"
    11  	"math/big"
    12  	"testing"
    13  
    14  	"github.com/ethereum/go-ethereum/accounts"
    15  	"github.com/ethereum/go-ethereum/common"
    16  	"github.com/ethereum/go-ethereum/common/hexutil"
    17  	"github.com/ethereum/go-ethereum/core/types"
    18  	"github.com/ethersphere/bee/v2/pkg/crypto"
    19  	"github.com/ethersphere/bee/v2/pkg/crypto/clef"
    20  	"github.com/ethersphere/bee/v2/pkg/crypto/eip712"
    21  )
    22  
    23  type mockClef struct {
    24  	accounts  []accounts.Account
    25  	signature []byte
    26  
    27  	signedMimeType string
    28  	signedData     []byte
    29  	signedAccount  accounts.Account
    30  }
    31  
    32  func (m *mockClef) SignData(account accounts.Account, mimeType string, data []byte) ([]byte, error) {
    33  	m.signedAccount = account
    34  	m.signedMimeType = mimeType
    35  	m.signedData = data
    36  	return m.signature, nil
    37  }
    38  
    39  func (m *mockClef) Accounts() []accounts.Account {
    40  	return m.accounts
    41  }
    42  
    43  func (m *mockClef) SignTx(account accounts.Account, transaction *types.Transaction, chainId *big.Int) (*types.Transaction, error) {
    44  	return nil, nil
    45  }
    46  
    47  func TestNewClefSigner(t *testing.T) {
    48  	t.Parallel()
    49  
    50  	ethAddress := common.HexToAddress("0x31415b599f636129AD03c196cef9f8f8b184D5C7")
    51  	testSignature := make([]byte, 65)
    52  
    53  	key, err := crypto.GenerateSecp256k1Key()
    54  	if err != nil {
    55  		t.Fatal(err)
    56  	}
    57  	publicKey := &key.PublicKey
    58  
    59  	mock := &mockClef{
    60  		accounts: []accounts.Account{
    61  			{
    62  				Address: ethAddress,
    63  			},
    64  			{
    65  				Address: common.Address{},
    66  			},
    67  		},
    68  		signature: testSignature,
    69  	}
    70  
    71  	signer, err := clef.NewSigner(mock, nil, func(signature, data []byte) (*ecdsa.PublicKey, error) {
    72  		if !bytes.Equal(testSignature, signature) {
    73  			t.Fatalf("wrong data used for recover. expected %v got %v", testSignature, signature)
    74  		}
    75  
    76  		if !bytes.Equal(clef.ClefRecoveryMessage, data) {
    77  			t.Fatalf("wrong data used for recover. expected %v got %v", clef.ClefRecoveryMessage, data)
    78  		}
    79  		return publicKey, nil
    80  	}, nil)
    81  	if err != nil {
    82  		t.Fatal(err)
    83  	}
    84  
    85  	if mock.signedAccount.Address != ethAddress {
    86  		t.Fatalf("wrong account used for signing. expected %v got %v", ethAddress, mock.signedAccount.Address)
    87  	}
    88  
    89  	if mock.signedMimeType != accounts.MimetypeTextPlain {
    90  		t.Fatalf("wrong mime type used for signing. expected %v got %v", accounts.MimetypeTextPlain, mock.signedMimeType)
    91  	}
    92  
    93  	if !bytes.Equal(mock.signedData, clef.ClefRecoveryMessage) {
    94  		t.Fatalf("wrong data used for signing. expected %v got %v", clef.ClefRecoveryMessage, mock.signedData)
    95  	}
    96  
    97  	signerPublicKey, err := signer.PublicKey()
    98  	if err != nil {
    99  		t.Fatal(err)
   100  	}
   101  
   102  	if signerPublicKey != publicKey {
   103  		t.Fatalf("wrong public key. expected %v got %v", publicKey, signerPublicKey)
   104  	}
   105  }
   106  
   107  func TestNewClefSignerSpecificAccount(t *testing.T) {
   108  	t.Parallel()
   109  
   110  	ethAddress := common.HexToAddress("0x31415b599f636129AD03c196cef9f8f8b184D5C7")
   111  	wantedAddress := common.HexToAddress("0x41415b599f636129AD03c196cef9f8f8b184D5C7")
   112  	testSignature := make([]byte, 65)
   113  
   114  	key, err := crypto.GenerateSecp256k1Key()
   115  	if err != nil {
   116  		t.Fatal(err)
   117  	}
   118  	publicKey := &key.PublicKey
   119  
   120  	mock := &mockClef{
   121  		accounts: []accounts.Account{
   122  			{
   123  				Address: ethAddress,
   124  			},
   125  			{
   126  				Address: wantedAddress,
   127  			},
   128  		},
   129  		signature: testSignature,
   130  	}
   131  
   132  	signer, err := clef.NewSigner(mock, nil, func(signature, data []byte) (*ecdsa.PublicKey, error) {
   133  		if !bytes.Equal(testSignature, signature) {
   134  			t.Fatalf("wrong data used for recover. expected %v got %v", testSignature, signature)
   135  		}
   136  
   137  		if !bytes.Equal(clef.ClefRecoveryMessage, data) {
   138  			t.Fatalf("wrong data used for recover. expected %v got %v", clef.ClefRecoveryMessage, data)
   139  		}
   140  		return publicKey, nil
   141  	}, &wantedAddress)
   142  	if err != nil {
   143  		t.Fatal(err)
   144  	}
   145  
   146  	if mock.signedAccount.Address != wantedAddress {
   147  		t.Fatalf("wrong account used for signing. expected %v got %v", wantedAddress, mock.signedAccount.Address)
   148  	}
   149  
   150  	if mock.signedMimeType != accounts.MimetypeTextPlain {
   151  		t.Fatalf("wrong mime type used for signing. expected %v got %v", accounts.MimetypeTextPlain, mock.signedMimeType)
   152  	}
   153  
   154  	if !bytes.Equal(mock.signedData, clef.ClefRecoveryMessage) {
   155  		t.Fatalf("wrong data used for signing. expected %v got %v", clef.ClefRecoveryMessage, mock.signedData)
   156  	}
   157  
   158  	signerPublicKey, err := signer.PublicKey()
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  
   163  	if signerPublicKey != publicKey {
   164  		t.Fatalf("wrong public key. expected %v got %v", publicKey, signerPublicKey)
   165  	}
   166  }
   167  
   168  func TestNewClefSignerAccountUnavailable(t *testing.T) {
   169  	t.Parallel()
   170  
   171  	ethAddress := common.HexToAddress("0x31415b599f636129AD03c196cef9f8f8b184D5C7")
   172  	wantedAddress := common.HexToAddress("0x41415b599f636129AD03c196cef9f8f8b184D5C7")
   173  
   174  	mock := &mockClef{
   175  		accounts: []accounts.Account{
   176  			{
   177  				Address: ethAddress,
   178  			},
   179  		},
   180  	}
   181  
   182  	_, err := clef.NewSigner(mock, nil, func(signature, data []byte) (*ecdsa.PublicKey, error) {
   183  		return nil, errors.New("called sign")
   184  	}, &wantedAddress)
   185  	if !errors.Is(err, clef.ErrAccountNotAvailable) {
   186  		t.Fatalf("expected account to be not available. got error %v", err)
   187  	}
   188  }
   189  
   190  func TestClefNoAccounts(t *testing.T) {
   191  	t.Parallel()
   192  
   193  	mock := &mockClef{
   194  		accounts: []accounts.Account{},
   195  	}
   196  
   197  	_, err := clef.NewSigner(mock, nil, nil, nil)
   198  	if err == nil {
   199  		t.Fatal("expected ErrNoAccounts error if no accounts")
   200  	}
   201  	if !errors.Is(err, clef.ErrNoAccounts) {
   202  		t.Fatalf("expected ErrNoAccounts error but got %v", err)
   203  	}
   204  }
   205  
   206  type mockRpc struct {
   207  	call func(result interface{}, method string, args ...interface{}) error
   208  }
   209  
   210  func (m *mockRpc) Call(result interface{}, method string, args ...interface{}) error {
   211  	return m.call(result, method, args...)
   212  }
   213  
   214  func TestClefTypedData(t *testing.T) {
   215  	t.Parallel()
   216  
   217  	key, err := crypto.GenerateSecp256k1Key()
   218  	if err != nil {
   219  		t.Fatal(err)
   220  	}
   221  	publicKey := &key.PublicKey
   222  	signature := common.FromHex("0xabcdef")
   223  
   224  	account := common.HexToAddress("21b26864067deb88e2d5cdca512167815f2910d3")
   225  
   226  	typedData := &eip712.TypedData{
   227  		PrimaryType: "MyType",
   228  	}
   229  
   230  	signer, err := clef.NewSigner(&mockClef{
   231  		accounts: []accounts.Account{
   232  			{
   233  				Address: account,
   234  			},
   235  		},
   236  		signature: make([]byte, 65),
   237  	}, &mockRpc{
   238  		call: func(result interface{}, method string, args ...interface{}) error {
   239  			if method != "account_signTypedData" {
   240  				t.Fatalf("called wrong method. was %s", method)
   241  			}
   242  			if args[0].(common.Address) != account {
   243  				t.Fatalf("called with wrong account. was %x, wanted %x", args[0].(common.Address), account)
   244  			}
   245  			if args[1].(*eip712.TypedData) != typedData {
   246  				t.Fatal("called with wrong data")
   247  			}
   248  			*result.(*hexutil.Bytes) = signature
   249  			return nil
   250  		},
   251  	}, func(signature, data []byte) (*ecdsa.PublicKey, error) {
   252  		return publicKey, nil
   253  	}, nil)
   254  	if err != nil {
   255  		t.Fatal(err)
   256  	}
   257  
   258  	s, err := signer.SignTypedData(typedData)
   259  	if err != nil {
   260  		t.Fatal(err)
   261  	}
   262  
   263  	if !bytes.Equal(s, signature) {
   264  		t.Fatalf("wrong signature. wanted %x, got %x", signature, s)
   265  	}
   266  }