2017-12-19 15:45:30 +00:00
package account
2017-05-16 12:09:52 +00:00
import (
2021-06-23 09:21:21 +00:00
"encoding/json"
2017-05-16 12:09:52 +00:00
"errors"
"fmt"
"os"
"path/filepath"
"testing"
2019-12-11 13:59:37 +00:00
"github.com/status-im/status-go/eth-node/crypto"
2021-06-23 09:21:21 +00:00
"github.com/status-im/status-go/eth-node/keystore"
2019-12-11 13:59:37 +00:00
"github.com/status-im/status-go/eth-node/types"
2024-10-28 20:54:17 +00:00
"github.com/status-im/status-go/protocol/tt"
2019-10-04 15:21:24 +00:00
"github.com/status-im/status-go/t/utils"
2017-10-11 14:20:51 +00:00
"github.com/stretchr/testify/require"
2017-12-19 13:07:31 +00:00
"github.com/stretchr/testify/suite"
2017-05-16 12:09:52 +00:00
)
2021-06-23 09:21:21 +00:00
const testPassword = "test-password"
const newTestPassword = "new-test-password"
2017-10-11 14:20:51 +00:00
func TestVerifyAccountPassword ( t * testing . T ) {
2024-10-28 20:54:17 +00:00
accManager := NewGethManager ( tt . MustCreateTestLogger ( ) )
2023-04-26 20:39:51 +00:00
keyStoreDir := t . TempDir ( )
emptyKeyStoreDir := t . TempDir ( )
2017-05-16 12:09:52 +00:00
// import account keys
2019-10-04 15:21:24 +00:00
utils . Init ( )
require . NoError ( t , utils . ImportTestAccount ( keyStoreDir , utils . GetAccount1PKFile ( ) ) )
require . NoError ( t , utils . ImportTestAccount ( keyStoreDir , utils . GetAccount2PKFile ( ) ) )
2017-05-16 12:09:52 +00:00
2019-12-11 13:59:37 +00:00
account1Address := types . BytesToAddress ( types . FromHex ( utils . TestConfig . Account1 . WalletAddress ) )
2017-05-16 12:09:52 +00:00
testCases := [ ] struct {
name string
keyPath string
address string
password string
expectedError error
} {
{
"correct address, correct password (decrypt should succeed)" ,
keyStoreDir ,
2019-10-04 15:21:24 +00:00
utils . TestConfig . Account1 . WalletAddress ,
utils . TestConfig . Account1 . Password ,
2017-05-16 12:09:52 +00:00
nil ,
} ,
{
"correct address, correct password, non-existent key store" ,
filepath . Join ( keyStoreDir , "non-existent-folder" ) ,
2019-10-04 15:21:24 +00:00
utils . TestConfig . Account1 . WalletAddress ,
utils . TestConfig . Account1 . Password ,
2017-05-16 12:09:52 +00:00
fmt . Errorf ( "cannot traverse key store folder: lstat %s/non-existent-folder: no such file or directory" , keyStoreDir ) ,
} ,
{
"correct address, correct password, empty key store (pk is not there)" ,
emptyKeyStoreDir ,
2019-10-04 15:21:24 +00:00
utils . TestConfig . Account1 . WalletAddress ,
utils . TestConfig . Account1 . Password ,
2022-07-06 16:12:49 +00:00
ErrCannotLocateKeyFile { fmt . Sprintf ( "cannot locate account for address: %s" , account1Address . Hex ( ) ) } ,
2017-05-16 12:09:52 +00:00
} ,
{
"wrong address, correct password" ,
keyStoreDir ,
"0x79791d3e8f2daa1f7fec29649d152c0ada3cc535" ,
2019-10-04 15:21:24 +00:00
utils . TestConfig . Account1 . Password ,
2022-07-06 16:12:49 +00:00
ErrCannotLocateKeyFile { fmt . Sprintf ( "cannot locate account for address: %s" , "0x79791d3E8F2dAa1F7FeC29649d152c0aDA3cc535" ) } ,
2017-05-16 12:09:52 +00:00
} ,
{
"correct address, wrong password" ,
keyStoreDir ,
2019-10-04 15:21:24 +00:00
utils . TestConfig . Account1 . WalletAddress ,
2017-05-16 12:09:52 +00:00
"wrong password" , // wrong password
2019-10-04 15:21:24 +00:00
errors . New ( "could not decrypt key with given password" ) ,
2017-05-16 12:09:52 +00:00
} ,
}
for _ , testCase := range testCases {
2017-12-19 15:45:30 +00:00
accountKey , err := accManager . VerifyAccountPassword ( testCase . keyPath , testCase . address , testCase . password )
2022-07-06 16:12:49 +00:00
if testCase . expectedError != nil && err != nil && testCase . expectedError . Error ( ) != err . Error ( ) ||
( ( testCase . expectedError == nil || err == nil ) && testCase . expectedError != err ) {
2017-10-11 14:20:51 +00:00
require . FailNow ( t , fmt . Sprintf ( "unexpected error: expected \n'%v', got \n'%v'" , testCase . expectedError , err ) )
2017-05-16 12:09:52 +00:00
}
if err == nil {
2022-03-28 10:10:40 +00:00
if accountKey == nil { // nolint: staticcheck
2017-10-11 14:20:51 +00:00
require . Fail ( t , "no error reported, but account key is missing" )
2017-05-16 12:09:52 +00:00
}
2019-12-11 13:59:37 +00:00
accountAddress := types . BytesToAddress ( types . FromHex ( testCase . address ) )
2020-12-28 09:09:45 +00:00
if accountKey . Address != accountAddress { // nolint: staticcheck
2017-10-11 14:20:51 +00:00
require . Fail ( t , "account mismatch: have %s, want %s" , accountKey . Address . Hex ( ) , accountAddress . Hex ( ) )
2017-05-16 12:09:52 +00:00
}
}
}
}
2017-10-10 09:38:49 +00:00
// TestVerifyAccountPasswordWithAccountBeforeEIP55 verifies if VerifyAccountPassword
// can handle accounts before introduction of EIP55.
2017-10-11 14:20:51 +00:00
func TestVerifyAccountPasswordWithAccountBeforeEIP55 ( t * testing . T ) {
2023-04-26 20:39:51 +00:00
keyStoreDir := t . TempDir ( )
2017-10-10 09:38:49 +00:00
// Import keys and make sure one was created before EIP55 introduction.
2019-10-04 15:21:24 +00:00
utils . Init ( )
2023-04-26 20:39:51 +00:00
err := utils . ImportTestAccount ( keyStoreDir , "test-account3-before-eip55.pk" )
2017-10-11 14:20:51 +00:00
require . NoError ( t , err )
2017-10-10 09:38:49 +00:00
2024-10-28 20:54:17 +00:00
accManager := NewGethManager ( tt . MustCreateTestLogger ( ) )
2017-10-10 09:38:49 +00:00
2019-12-11 13:59:37 +00:00
address := types . HexToAddress ( utils . TestConfig . Account3 . WalletAddress )
2019-10-04 15:21:24 +00:00
_ , err = accManager . VerifyAccountPassword ( keyStoreDir , address . Hex ( ) , utils . TestConfig . Account3 . Password )
2017-10-11 14:20:51 +00:00
require . NoError ( t , err )
2017-10-10 09:38:49 +00:00
}
2017-12-14 02:03:55 +00:00
2017-12-19 13:07:31 +00:00
func TestManagerTestSuite ( t * testing . T ) {
2019-08-20 15:38:40 +00:00
suite . Run ( t , new ( ManagerTestSuite ) )
2017-12-19 15:45:30 +00:00
}
2017-12-19 13:07:31 +00:00
type ManagerTestSuite struct {
suite . Suite
2017-12-21 17:07:08 +00:00
testAccount
2019-12-19 18:27:27 +00:00
accManager * GethManager
2019-08-20 15:38:40 +00:00
keydir string
2017-12-19 13:07:31 +00:00
}
2017-12-14 02:03:55 +00:00
2017-12-21 17:07:08 +00:00
type testAccount struct {
2019-01-18 09:01:14 +00:00
password string
walletAddress string
walletPubKey string
chatAddress string
chatPubKey string
mnemonic string
2017-12-21 17:07:08 +00:00
}
2018-01-03 15:51:27 +00:00
// SetupTest is used here for reinitializing the mock before every
// test function to avoid faulty execution.
func ( s * ManagerTestSuite ) SetupTest ( ) {
2024-10-28 20:54:17 +00:00
s . accManager = NewGethManager ( tt . MustCreateTestLogger ( ) )
2019-07-31 07:50:40 +00:00
2023-04-26 20:39:51 +00:00
keyStoreDir := s . T ( ) . TempDir ( )
2019-08-20 15:38:40 +00:00
s . Require ( ) . NoError ( s . accManager . InitKeystore ( keyStoreDir ) )
s . keydir = keyStoreDir
// Initial test - create test account
2020-12-09 14:03:43 +00:00
_ , accountInfo , mnemonic , err := s . accManager . CreateAccount ( testPassword )
2019-08-20 15:38:40 +00:00
s . Require ( ) . NoError ( err )
s . Require ( ) . NotEmpty ( accountInfo . WalletAddress )
s . Require ( ) . NotEmpty ( accountInfo . WalletPubKey )
s . Require ( ) . NotEmpty ( accountInfo . ChatAddress )
s . Require ( ) . NotEmpty ( accountInfo . ChatPubKey )
s . Require ( ) . NotEmpty ( mnemonic )
// Before the complete decoupling of the keys, wallet and chat keys are the same
s . Equal ( accountInfo . WalletAddress , accountInfo . ChatAddress )
s . Equal ( accountInfo . WalletPubKey , accountInfo . ChatPubKey )
s . testAccount = testAccount {
testPassword ,
accountInfo . WalletAddress ,
accountInfo . WalletPubKey ,
accountInfo . ChatAddress ,
accountInfo . ChatPubKey ,
mnemonic ,
}
}
2017-12-28 13:40:56 +00:00
func ( s * ManagerTestSuite ) TestRecoverAccount ( ) {
2019-01-18 09:01:14 +00:00
accountInfo , err := s . accManager . RecoverAccount ( s . password , s . mnemonic )
2017-12-19 13:07:31 +00:00
s . NoError ( err )
2019-01-18 09:01:14 +00:00
s . Equal ( s . walletAddress , accountInfo . WalletAddress )
s . Equal ( s . walletPubKey , accountInfo . WalletPubKey )
s . Equal ( s . chatAddress , accountInfo . ChatAddress )
s . Equal ( s . chatPubKey , accountInfo . ChatPubKey )
2017-12-14 02:03:55 +00:00
}
2017-12-18 14:08:31 +00:00
2019-06-26 22:28:16 +00:00
func ( s * ManagerTestSuite ) TestOnboarding ( ) {
// try to choose an account before starting onboarding
_ , _ , err := s . accManager . ImportOnboardingAccount ( "test-id" , "test-password" )
s . Equal ( ErrOnboardingNotStarted , err )
// generates 5 random accounts
count := 5
accounts , err := s . accManager . StartOnboarding ( count , 24 )
s . Require ( ) . NoError ( err )
s . Equal ( count , len ( accounts ) )
// try to choose an account with an undefined id
_ , _ , err = s . accManager . ImportOnboardingAccount ( "test-id" , "test-password" )
s . Equal ( ErrOnboardingAccountNotFound , err )
// choose one account and encrypt it with password
password := "test-onboarding-account"
account := accounts [ 0 ]
info , mnemonic , err := s . accManager . ImportOnboardingAccount ( account . ID , password )
s . Require ( ) . NoError ( err )
s . Equal ( account . Info , info )
s . Equal ( account . mnemonic , mnemonic )
s . Nil ( s . accManager . onboarding )
// try to decrypt it with password to check if it's been imported correctly
decAccount , _ , err := s . accManager . AddressToDecryptedAccount ( info . WalletAddress , password )
s . Require ( ) . NoError ( err )
s . Equal ( info . WalletAddress , decAccount . Address . Hex ( ) )
// try resetting onboarding
_ , err = s . accManager . StartOnboarding ( count , 24 )
s . Require ( ) . NoError ( err )
s . NotNil ( s . accManager . onboarding )
s . accManager . RemoveOnboarding ( )
s . Nil ( s . accManager . onboarding )
}
2019-08-20 15:38:40 +00:00
func ( s * ManagerTestSuite ) TestSelectAccountSuccess ( ) {
2019-12-11 13:59:37 +00:00
s . testSelectAccount ( types . HexToAddress ( s . testAccount . chatAddress ) , types . HexToAddress ( s . testAccount . walletAddress ) , s . testAccount . password , nil )
2019-08-20 15:38:40 +00:00
}
2019-07-31 07:50:40 +00:00
2019-08-20 15:38:40 +00:00
func ( s * ManagerTestSuite ) TestSelectAccountWrongAddress ( ) {
2019-12-11 13:59:37 +00:00
s . testSelectAccount ( types . HexToAddress ( "0x0000000000000000000000000000000000000001" ) , types . HexToAddress ( s . testAccount . walletAddress ) , s . testAccount . password , errors . New ( "cannot retrieve a valid key for a given account: no key for given address or file" ) )
2019-08-20 15:38:40 +00:00
}
func ( s * ManagerTestSuite ) TestSelectAccountWrongPassword ( ) {
2019-12-11 13:59:37 +00:00
s . testSelectAccount ( types . HexToAddress ( s . testAccount . chatAddress ) , types . HexToAddress ( s . testAccount . walletAddress ) , "wrong" , errors . New ( "cannot retrieve a valid key for a given account: could not decrypt key with given password" ) )
2019-08-20 15:38:40 +00:00
}
2019-01-18 09:01:14 +00:00
2019-12-11 13:59:37 +00:00
func ( s * ManagerTestSuite ) testSelectAccount ( chat , wallet types . Address , password string , expErr error ) {
2019-08-20 15:38:40 +00:00
loginParams := LoginParams {
ChatAddress : chat ,
MainAccount : wallet ,
Password : password ,
2017-12-19 15:45:30 +00:00
}
2019-08-20 15:38:40 +00:00
err := s . accManager . SelectAccount ( loginParams )
s . Require ( ) . Equal ( expErr , err )
selectedMainAccountAddress , walletErr := s . accManager . MainAccountAddress ( )
selectedChatAccount , chatErr := s . accManager . SelectedChatAccount ( )
if expErr == nil {
s . Require ( ) . NoError ( walletErr )
s . Require ( ) . NoError ( chatErr )
s . Equal ( wallet , selectedMainAccountAddress )
s . Equal ( chat , crypto . PubkeyToAddress ( selectedChatAccount . AccountKey . PrivateKey . PublicKey ) )
} else {
2019-12-11 13:59:37 +00:00
s . Equal ( types . Address { } , selectedMainAccountAddress )
2019-08-20 15:38:40 +00:00
s . Nil ( selectedChatAccount )
s . Equal ( walletErr , ErrNoAccountSelected )
s . Equal ( chatErr , ErrNoAccountSelected )
}
s . accManager . Logout ( )
2017-12-19 15:45:30 +00:00
}
2019-01-24 15:44:46 +00:00
func ( s * ManagerTestSuite ) TestSetChatAccount ( ) {
s . accManager . Logout ( )
privKey , err := crypto . GenerateKey ( )
s . Require ( ) . NoError ( err )
address := crypto . PubkeyToAddress ( privKey . PublicKey )
2021-07-12 11:18:46 +00:00
s . Require ( ) . NoError ( s . accManager . SetChatAccount ( privKey ) )
2019-01-24 15:44:46 +00:00
selectedChatAccount , err := s . accManager . SelectedChatAccount ( )
s . Require ( ) . NoError ( err )
s . Require ( ) . NotNil ( selectedChatAccount )
s . Equal ( privKey , selectedChatAccount . AccountKey . PrivateKey )
s . Equal ( address , selectedChatAccount . Address )
2019-07-26 14:45:10 +00:00
selectedMainAccountAddress , err := s . accManager . MainAccountAddress ( )
2019-01-24 15:44:46 +00:00
s . Error ( err )
2019-12-11 13:59:37 +00:00
s . Equal ( types . Address { } , selectedMainAccountAddress )
2017-12-18 14:08:31 +00:00
}
2017-12-19 16:11:46 +00:00
2017-12-19 16:59:43 +00:00
func ( s * ManagerTestSuite ) TestLogout ( ) {
2018-04-20 15:39:53 +00:00
s . accManager . Logout ( )
2019-12-11 13:59:37 +00:00
s . Equal ( types . Address { } , s . accManager . mainAccountAddress )
2019-07-26 14:45:10 +00:00
s . Nil ( s . accManager . selectedChatAccount )
s . Len ( s . accManager . watchAddresses , 0 )
2017-12-19 16:59:43 +00:00
}
2017-12-19 17:20:53 +00:00
2018-01-03 19:57:25 +00:00
// TestAccounts tests cases for (*Manager).Accounts.
2017-12-19 17:20:53 +00:00
func ( s * ManagerTestSuite ) TestAccounts ( ) {
2017-12-21 17:07:08 +00:00
// Select the test account
2019-07-26 14:45:10 +00:00
loginParams := LoginParams {
2019-12-11 13:59:37 +00:00
MainAccount : types . HexToAddress ( s . walletAddress ) ,
ChatAddress : types . HexToAddress ( s . chatAddress ) ,
2019-07-26 14:45:10 +00:00
Password : s . password ,
}
err := s . accManager . SelectAccount ( loginParams )
2017-12-19 17:20:53 +00:00
s . NoError ( err )
// Success
accs , err := s . accManager . Accounts ( )
s . NoError ( err )
s . NotNil ( accs )
2019-07-26 14:45:10 +00:00
// Selected main account address is zero address but doesn't fail
2019-12-11 13:59:37 +00:00
s . accManager . mainAccountAddress = types . Address { }
2017-12-19 17:20:53 +00:00
accs , err = s . accManager . Accounts ( )
s . NoError ( err )
s . NotNil ( accs )
}
2017-12-19 17:32:10 +00:00
2019-08-20 15:38:40 +00:00
func ( s * ManagerTestSuite ) TestAddressToDecryptedAccountSuccess ( ) {
s . testAddressToDecryptedAccount ( s . walletAddress , s . password , nil )
}
2019-07-31 07:50:40 +00:00
2019-08-20 15:38:40 +00:00
func ( s * ManagerTestSuite ) TestAddressToDecryptedAccountWrongAddress ( ) {
s . testAddressToDecryptedAccount ( "0x0001" , s . password , ErrAddressToAccountMappingFailure )
}
func ( s * ManagerTestSuite ) TestAddressToDecryptedAccountWrongPassword ( ) {
2019-10-04 15:21:24 +00:00
s . testAddressToDecryptedAccount ( s . walletAddress , "wrong" , errors . New ( "cannot retrieve a valid key for a given account: could not decrypt key with given password" ) )
2019-08-20 15:38:40 +00:00
}
func ( s * ManagerTestSuite ) testAddressToDecryptedAccount ( wallet , password string , expErr error ) {
acc , key , err := s . accManager . AddressToDecryptedAccount ( wallet , password )
if expErr != nil {
s . Equal ( expErr , err )
} else {
s . Require ( ) . NoError ( err )
s . Require ( ) . NotNil ( acc )
s . Require ( ) . NotNil ( key )
s . Equal ( acc . Address , key . Address )
2017-12-19 17:32:10 +00:00
}
}
2020-06-22 12:03:28 +00:00
func ( s * ManagerTestSuite ) TestMigrateKeyStoreDir ( ) {
oldKeyDir := s . keydir
newKeyDir := filepath . Join ( oldKeyDir , "new_dir" )
err := os . Mkdir ( newKeyDir , 0777 )
s . Require ( ) . NoError ( err )
2024-03-19 13:03:56 +00:00
files , _ := os . ReadDir ( newKeyDir )
2020-06-22 12:03:28 +00:00
s . Equal ( 0 , len ( files ) )
address := types . HexToAddress ( s . walletAddress ) . Hex ( )
addresses := [ ] string { address }
err = s . accManager . MigrateKeyStoreDir ( oldKeyDir , newKeyDir , addresses )
s . Require ( ) . NoError ( err )
2024-03-19 13:03:56 +00:00
files , _ = os . ReadDir ( newKeyDir )
2020-06-22 12:03:28 +00:00
s . Equal ( 1 , len ( files ) )
}
2021-06-23 09:21:21 +00:00
func ( s * ManagerTestSuite ) TestReEncryptKey ( ) {
var firstKeyPath string
2024-03-19 13:03:56 +00:00
files , _ := os . ReadDir ( s . keydir )
2021-06-23 09:21:21 +00:00
2024-03-19 13:03:56 +00:00
// there is only one file in this dir,
2021-06-23 09:21:21 +00:00
// is there a better way to reference it?
for _ , f := range files {
firstKeyPath = filepath . Join ( s . keydir , f . Name ( ) )
}
2024-03-19 13:03:56 +00:00
rawKey , _ := os . ReadFile ( firstKeyPath )
2021-06-23 09:21:21 +00:00
reEncryptedKey , _ := s . accManager . ReEncryptKey ( rawKey , testPassword , newTestPassword )
type Key struct {
Address string ` json:"address" `
}
var unmarshaledRaw , unmarshaledReEncrypted Key
_ = json . Unmarshal ( rawKey , & unmarshaledRaw )
_ = json . Unmarshal ( reEncryptedKey , & unmarshaledReEncrypted )
oldCrypto , _ := keystore . RawKeyToCryptoJSON ( rawKey )
newCrypto , _ := keystore . RawKeyToCryptoJSON ( reEncryptedKey )
// Test address is same post re-encryption
s . Equal ( unmarshaledRaw . Address , unmarshaledReEncrypted . Address )
// Test cipher changes after re-encryption
s . NotEqual ( oldCrypto . CipherText , newCrypto . CipherText )
// Test re-encrypted key cannot be decrypted using old testPasswordword
_ , decryptOldError := keystore . DecryptKey ( reEncryptedKey , testPassword )
s . Require ( ) . Error ( decryptOldError )
// Test re-encrypted key can be decrypted using new testPassword
_ , decryptNewError := keystore . DecryptKey ( reEncryptedKey , newTestPassword )
s . Require ( ) . NoError ( decryptNewError )
}
func ( s * ManagerTestSuite ) TestReEncryptKeyStoreDir ( ) {
err := s . accManager . ReEncryptKeyStoreDir ( s . keydir , testPassword , newTestPassword )
s . Require ( ) . NoError ( err )
err = filepath . Walk ( s . keydir , func ( path string , fileInfo os . FileInfo , err error ) error {
if fileInfo . IsDir ( ) {
return nil
}
// walk should not throw callback errors
s . Require ( ) . NoError ( err )
2024-03-19 13:03:56 +00:00
rawKeyFile , err := os . ReadFile ( path )
2021-06-23 09:21:21 +00:00
s . Require ( ) . NoError ( err )
// should not decrypt with old password
_ , decryptError := keystore . DecryptKey ( rawKeyFile , testPassword )
s . Require ( ) . Error ( decryptError )
// should decrypt with new password
_ , decryptError = keystore . DecryptKey ( rawKeyFile , newTestPassword )
s . Require ( ) . NoError ( decryptError )
return nil
} )
s . Require ( ) . NoError ( err )
}