cmd/clef, signer: security fixes (#17554)

* signer: remove local path disclosure from extapi

* signer: show more data in cli ui

* rpc: make http server forward UA and Origin via Context

* signer, clef/core: ui changes + display UA and Origin

* signer: cliui - indicate less trust in remote headers, see https://github.com/ethereum/go-ethereum/issues/17637

* signer: prevent possibility swap KV-entries in aes_gcm storage, fixes #17635

* signer: remove ecrecover from external API

* signer,clef: default reject instead of warn + valideate new passwords. fixes #17632 and #17631

* signer: check calldata length even if no ABI signature is present

* signer: fix failing testcase

* clef: remove account import from external api

* signer: allow space in passwords, improve error messsage

* signer/storage: fix typos
This commit is contained in:
Martin Holst Swende
2018-09-25 15:54:58 +02:00
committed by GitHub
parent a95a601f35
commit d3441ebb56
12 changed files with 307 additions and 133 deletions

View File

@ -45,7 +45,7 @@ func (ui *HeadlessUI) OnSignerStartup(info StartupInfo) {
}
func (ui *HeadlessUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
fmt.Printf("OnApproved called")
fmt.Printf("OnApproved()\n")
}
func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
@ -62,26 +62,27 @@ func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error)
return SignTxResponse{request.Transaction, false, ""}, nil
}
}
func (ui *HeadlessUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
if "Y" == <-ui.controller {
return SignDataResponse{true, <-ui.controller}, nil
}
return SignDataResponse{false, ""}, nil
}
func (ui *HeadlessUI) ApproveExport(request *ExportRequest) (ExportResponse, error) {
func (ui *HeadlessUI) ApproveExport(request *ExportRequest) (ExportResponse, error) {
return ExportResponse{<-ui.controller == "Y"}, nil
}
func (ui *HeadlessUI) ApproveImport(request *ImportRequest) (ImportResponse, error) {
func (ui *HeadlessUI) ApproveImport(request *ImportRequest) (ImportResponse, error) {
if "Y" == <-ui.controller {
return ImportResponse{true, <-ui.controller, <-ui.controller}, nil
}
return ImportResponse{false, "", ""}, nil
}
func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error) {
func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error) {
switch <-ui.controller {
case "A":
return ListResponse{request.Accounts}, nil
@ -93,20 +94,22 @@ func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error)
return ListResponse{nil}, nil
}
}
func (ui *HeadlessUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
func (ui *HeadlessUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
if "Y" == <-ui.controller {
return NewAccountResponse{true, <-ui.controller}, nil
}
return NewAccountResponse{false, ""}, nil
}
func (ui *HeadlessUI) ShowError(message string) {
//stdout is used by communication
fmt.Fprint(os.Stderr, message)
fmt.Fprintln(os.Stderr, message)
}
func (ui *HeadlessUI) ShowInfo(message string) {
//stdout is used by communication
fmt.Fprint(os.Stderr, message)
fmt.Fprintln(os.Stderr, message)
}
func tmpDirName(t *testing.T) string {
@ -123,7 +126,7 @@ func tmpDirName(t *testing.T) string {
func setup(t *testing.T) (*SignerAPI, chan string) {
controller := make(chan string, 10)
controller := make(chan string, 20)
db, err := NewAbiDBFromFile("../../cmd/clef/4byte.json")
if err != nil {
@ -137,14 +140,14 @@ func setup(t *testing.T) (*SignerAPI, chan string) {
true,
ui,
db,
true)
true, true)
)
return api, controller
}
func createAccount(control chan string, api *SignerAPI, t *testing.T) {
control <- "Y"
control <- "apassword"
control <- "a_long_password"
_, err := api.New(context.Background())
if err != nil {
t.Fatal(err)
@ -152,6 +155,25 @@ func createAccount(control chan string, api *SignerAPI, t *testing.T) {
// Some time to allow changes to propagate
time.Sleep(250 * time.Millisecond)
}
func failCreateAccountWithPassword(control chan string, api *SignerAPI, password string, t *testing.T) {
control <- "Y"
control <- password
control <- "Y"
control <- password
control <- "Y"
control <- password
acc, err := api.New(context.Background())
if err == nil {
t.Fatal("Should have returned an error")
}
if acc.Address != (common.Address{}) {
t.Fatal("Empty address should be returned")
}
}
func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) {
control <- "N"
acc, err := api.New(context.Background())
@ -162,7 +184,8 @@ func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) {
t.Fatal("Empty address should be returned")
}
}
func list(control chan string, api *SignerAPI, t *testing.T) []Account {
func list(control chan string, api *SignerAPI, t *testing.T) []common.Address {
control <- "A"
list, err := api.List(context.Background())
if err != nil {
@ -172,7 +195,6 @@ func list(control chan string, api *SignerAPI, t *testing.T) []Account {
}
func TestNewAcc(t *testing.T) {
api, control := setup(t)
verifyNum := func(num int) {
if list := list(control, api, t); len(list) != num {
@ -188,6 +210,13 @@ func TestNewAcc(t *testing.T) {
failCreateAccount(control, api, t)
createAccount(control, api, t)
failCreateAccount(control, api, t)
verifyNum(4)
// Fail to create this, due to bad password
failCreateAccountWithPassword(control, api, "short", t)
failCreateAccountWithPassword(control, api, "longerbutbad\rfoo", t)
verifyNum(4)
// Testing listing:
@ -212,7 +241,6 @@ func TestNewAcc(t *testing.T) {
}
func TestSignData(t *testing.T) {
api, control := setup(t)
//Create two accounts
createAccount(control, api, t)
@ -222,7 +250,7 @@ func TestSignData(t *testing.T) {
if err != nil {
t.Fatal(err)
}
a := common.NewMixedcaseAddress(list[0].Address)
a := common.NewMixedcaseAddress(list[0])
control <- "Y"
control <- "wrongpassword"
@ -233,7 +261,6 @@ func TestSignData(t *testing.T) {
if err != keystore.ErrDecrypt {
t.Errorf("Expected ErrLocked! %v", err)
}
control <- "No way"
h, err = api.Sign(context.Background(), a, []byte("EHLO world"))
if h != nil {
@ -242,11 +269,9 @@ func TestSignData(t *testing.T) {
if err != ErrRequestDenied {
t.Errorf("Expected ErrRequestDenied! %v", err)
}
control <- "Y"
control <- "apassword"
control <- "a_long_password"
h, err = api.Sign(context.Background(), a, []byte("EHLO world"))
if err != nil {
t.Fatal(err)
}
@ -273,9 +298,8 @@ func mkTestTx(from common.MixedcaseAddress) SendTxArgs {
}
func TestSignTx(t *testing.T) {
var (
list Accounts
list []common.Address
res, res2 *ethapi.SignTransactionResult
err error
)
@ -287,7 +311,7 @@ func TestSignTx(t *testing.T) {
if err != nil {
t.Fatal(err)
}
a := common.NewMixedcaseAddress(list[0].Address)
a := common.NewMixedcaseAddress(list[0])
methodSig := "test(uint)"
tx := mkTestTx(a)
@ -301,7 +325,6 @@ func TestSignTx(t *testing.T) {
if err != keystore.ErrDecrypt {
t.Errorf("Expected ErrLocked! %v", err)
}
control <- "No way"
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
if res != nil {
@ -310,9 +333,8 @@ func TestSignTx(t *testing.T) {
if err != ErrRequestDenied {
t.Errorf("Expected ErrRequestDenied! %v", err)
}
control <- "Y"
control <- "apassword"
control <- "a_long_password"
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
if err != nil {
@ -320,12 +342,13 @@ func TestSignTx(t *testing.T) {
}
parsedTx := &types.Transaction{}
rlp.Decode(bytes.NewReader(res.Raw), parsedTx)
//The tx should NOT be modified by the UI
if parsedTx.Value().Cmp(tx.Value.ToInt()) != 0 {
t.Errorf("Expected value to be unchanged, expected %v got %v", tx.Value, parsedTx.Value())
}
control <- "Y"
control <- "apassword"
control <- "a_long_password"
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
if err != nil {
@ -337,20 +360,19 @@ func TestSignTx(t *testing.T) {
//The tx is modified by the UI
control <- "M"
control <- "apassword"
control <- "a_long_password"
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
if err != nil {
t.Fatal(err)
}
parsedTx2 := &types.Transaction{}
rlp.Decode(bytes.NewReader(res.Raw), parsedTx2)
//The tx should be modified by the UI
if parsedTx2.Value().Cmp(tx.Value.ToInt()) != 0 {
t.Errorf("Expected value to be unchanged, got %v", parsedTx.Value())
}
if bytes.Equal(res.Raw, res2.Raw) {
t.Error("Expected tx to be modified by UI")
}
@ -372,9 +394,9 @@ func TestAsyncronousResponses(t *testing.T){
control <- "W" //wait
control <- "Y" //
control <- "apassword"
control <- "a_long_password"
control <- "Y" //
control <- "apassword"
control <- "a_long_password"
var err error