VerifyAccountPassword: locate key file by traversing key store folder
This commit is contained in:
parent
68d4d20d66
commit
3e8f9076c1
|
@ -78,8 +78,8 @@ func RecoverAccount(password, mnemonic *C.char) *C.char {
|
||||||
}
|
}
|
||||||
|
|
||||||
//export VerifyAccountPassword
|
//export VerifyAccountPassword
|
||||||
func VerifyAccountPassword(keyPath, address, password *C.char) *C.char {
|
func VerifyAccountPassword(keyStoreDir, address, password *C.char) *C.char {
|
||||||
_, err := geth.VerifyAccountPassword(C.GoString(keyPath), C.GoString(address), C.GoString(password))
|
_, err := geth.VerifyAccountPassword(C.GoString(keyStoreDir), C.GoString(address), C.GoString(password))
|
||||||
return makeJSONErrorResponse(err)
|
return makeJSONErrorResponse(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,11 +120,20 @@ func testVerifyAccountPassword(t *testing.T) bool {
|
||||||
if err = geth.ImportTestAccount(tmpDir, "test-account1.pk"); err != nil {
|
if err = geth.ImportTestAccount(tmpDir, "test-account1.pk"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if err = geth.ImportTestAccount(tmpDir, "test-account2.pk"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rename account file (to see that file's internals reviewed, when locating account key)
|
||||||
|
accountFilePathOriginal := filepath.Join(tmpDir, "test-account1.pk")
|
||||||
|
accountFilePath := filepath.Join(tmpDir, "foo"+testConfig.Account1.Address+"bar.pk")
|
||||||
|
if err := os.Rename(accountFilePathOriginal, accountFilePath); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
accountFilePath := filepath.Join(tmpDir, "test-account1.pk")
|
|
||||||
response := geth.JSONError{}
|
response := geth.JSONError{}
|
||||||
rawResponse := VerifyAccountPassword(
|
rawResponse := VerifyAccountPassword(
|
||||||
C.CString(accountFilePath),
|
C.CString(tmpDir),
|
||||||
C.CString(testConfig.Account1.Address),
|
C.CString(testConfig.Account1.Address),
|
||||||
C.CString(testConfig.Account1.Password))
|
C.CString(testConfig.Account1.Password))
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package geth
|
package geth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||||
|
@ -129,10 +132,39 @@ func RecoverAccount(password, mnemonic string) (address, pubKey string, err erro
|
||||||
|
|
||||||
// VerifyAccountPassword tries to decrypt a given account key file, with a provided password.
|
// VerifyAccountPassword tries to decrypt a given account key file, with a provided password.
|
||||||
// If no error is returned, then account is considered verified.
|
// If no error is returned, then account is considered verified.
|
||||||
func VerifyAccountPassword(keyPath, address, password string) (*keystore.Key, error) {
|
func VerifyAccountPassword(keyStoreDir, address, password string) (*keystore.Key, error) {
|
||||||
keyJSON, err := ioutil.ReadFile(keyPath)
|
var err error
|
||||||
|
var keyJSON []byte
|
||||||
|
|
||||||
|
addressObj := common.BytesToAddress(common.FromHex(address))
|
||||||
|
checkAccountKey := func(path string, fileInfo os.FileInfo) error {
|
||||||
|
if len(keyJSON) > 0 || fileInfo.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
keyJSON, err = ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid account key file: %v", err)
|
||||||
|
}
|
||||||
|
if !bytes.Contains(keyJSON, []byte(fmt.Sprintf(`"address":"%s"`, addressObj.Hex()[2:]))) {
|
||||||
|
keyJSON = []byte{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// locate key within key store directory (address should be within the file)
|
||||||
|
err = filepath.Walk(keyStoreDir, func(path string, fileInfo os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return checkAccountKey(path, fileInfo)
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid account key file: %v", err)
|
return nil, fmt.Errorf("cannot traverse key store folder: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(keyJSON) == 0 {
|
||||||
|
return nil, fmt.Errorf("cannot locate account for address: %x", addressObj)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := keystore.DecryptKey(keyJSON, password)
|
key, err := keystore.DecryptKey(keyJSON, password)
|
||||||
|
@ -141,9 +173,8 @@ func VerifyAccountPassword(keyPath, address, password string) (*keystore.Key, er
|
||||||
}
|
}
|
||||||
|
|
||||||
// avoid swap attack
|
// avoid swap attack
|
||||||
addr := common.BytesToAddress(common.FromHex(address))
|
if key.Address != addressObj {
|
||||||
if key.Address != addr {
|
return nil, fmt.Errorf("account mismatch: have %x, want %x", key.Address, addressObj)
|
||||||
return nil, fmt.Errorf("account mismatch: have %x, want %x", key.Address, addr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return key, nil
|
return key, nil
|
||||||
|
|
|
@ -14,19 +14,27 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestVerifyAccountPassword(t *testing.T) {
|
func TestVerifyAccountPassword(t *testing.T) {
|
||||||
tmpDir, err := ioutil.TempDir(os.TempDir(), "accounts")
|
keyStoreDir, err := ioutil.TempDir(os.TempDir(), "accounts")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(tmpDir) // nolint: errcheck
|
defer os.RemoveAll(keyStoreDir) // nolint: errcheck
|
||||||
|
|
||||||
if err = geth.ImportTestAccount(tmpDir, "test-account1.pk"); err != nil {
|
emptyKeyStoreDir, err := ioutil.TempDir(os.TempDir(), "empty")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(emptyKeyStoreDir) // nolint: errcheck
|
||||||
|
|
||||||
|
// import account keys
|
||||||
|
if err = geth.ImportTestAccount(keyStoreDir, "test-account1.pk"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err = geth.ImportTestAccount(keyStoreDir, "test-account2.pk"); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountFilePath := filepath.Join(tmpDir, "test-account1.pk")
|
|
||||||
account1Address := common.BytesToAddress(common.FromHex(testConfig.Account1.Address))
|
account1Address := common.BytesToAddress(common.FromHex(testConfig.Account1.Address))
|
||||||
account2Address := common.BytesToAddress(common.FromHex(testConfig.Account2.Address))
|
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
name string
|
name string
|
||||||
|
@ -37,28 +45,35 @@ func TestVerifyAccountPassword(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"correct address, correct password (decrypt should succeed)",
|
"correct address, correct password (decrypt should succeed)",
|
||||||
accountFilePath,
|
keyStoreDir,
|
||||||
testConfig.Account1.Address,
|
testConfig.Account1.Address,
|
||||||
testConfig.Account1.Password,
|
testConfig.Account1.Password,
|
||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"correct address, correct password, invalid key file",
|
"correct address, correct password, non-existent key store",
|
||||||
filepath.Join(tmpDir, "non-existent-file.pk"),
|
filepath.Join(keyStoreDir, "non-existent-folder"),
|
||||||
testConfig.Account1.Address,
|
testConfig.Account1.Address,
|
||||||
testConfig.Account1.Password,
|
testConfig.Account1.Password,
|
||||||
fmt.Errorf("invalid account key file: open %s/non-existent-file.pk: no such file or directory", tmpDir),
|
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,
|
||||||
|
testConfig.Account1.Address,
|
||||||
|
testConfig.Account1.Password,
|
||||||
|
fmt.Errorf("cannot locate account for address: %x", account1Address),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"wrong address, correct password",
|
"wrong address, correct password",
|
||||||
accountFilePath,
|
keyStoreDir,
|
||||||
testConfig.Account2.Address, // wrong address (swap attack)
|
"0x79791d3e8f2daa1f7fec29649d152c0ada3cc535",
|
||||||
testConfig.Account1.Password,
|
testConfig.Account1.Password,
|
||||||
fmt.Errorf("account mismatch: have %x, want %x", account1Address, account2Address),
|
fmt.Errorf("cannot locate account for address: %s", "79791d3e8f2daa1f7fec29649d152c0ada3cc535"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"correct address, wrong password",
|
"correct address, wrong password",
|
||||||
accountFilePath,
|
keyStoreDir,
|
||||||
testConfig.Account1.Address,
|
testConfig.Account1.Address,
|
||||||
"wrong password", // wrong password
|
"wrong password", // wrong password
|
||||||
errors.New("could not decrypt key with given passphrase"),
|
errors.New("could not decrypt key with given passphrase"),
|
||||||
|
@ -68,7 +83,7 @@ func TestVerifyAccountPassword(t *testing.T) {
|
||||||
t.Log(testCase.name)
|
t.Log(testCase.name)
|
||||||
accountKey, err := geth.VerifyAccountPassword(testCase.keyPath, testCase.address, testCase.password)
|
accountKey, err := geth.VerifyAccountPassword(testCase.keyPath, testCase.address, testCase.password)
|
||||||
if !reflect.DeepEqual(err, testCase.expectedError) {
|
if !reflect.DeepEqual(err, testCase.expectedError) {
|
||||||
t.Errorf("unexpected error: expected \n'%v', got \n'%v'", testCase.expectedError, err)
|
t.Fatalf("unexpected error: expected \n'%v', got \n'%v'", testCase.expectedError, err)
|
||||||
}
|
}
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if accountKey == nil {
|
if accountKey == nil {
|
||||||
|
@ -76,7 +91,7 @@ func TestVerifyAccountPassword(t *testing.T) {
|
||||||
}
|
}
|
||||||
accountAddress := common.BytesToAddress(common.FromHex(testCase.address))
|
accountAddress := common.BytesToAddress(common.FromHex(testCase.address))
|
||||||
if accountKey.Address != accountAddress {
|
if accountKey.Address != accountAddress {
|
||||||
t.Errorf("account mismatch: have %x, want %x", accountKey.Address, accountAddress)
|
t.Fatalf("account mismatch: have %x, want %x", accountKey.Address, accountAddress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue