aboutsummaryrefslogtreecommitdiffstats
path: root/signer
diff options
context:
space:
mode:
Diffstat (limited to 'signer')
-rw-r--r--signer/core/api.go100
-rw-r--r--signer/core/api_test.go186
-rw-r--r--signer/core/cliui.go53
-rw-r--r--signer/core/signed_data.go8
-rw-r--r--signer/core/signed_data_test.go16
-rw-r--r--signer/core/stdioui.go40
-rw-r--r--signer/rules/rules.go50
-rw-r--r--signer/rules/rules_test.go45
-rw-r--r--signer/storage/storage.go16
9 files changed, 189 insertions, 325 deletions
diff --git a/signer/core/api.go b/signer/core/api.go
index 837b7266c..f499b3451 100644
--- a/signer/core/api.go
+++ b/signer/core/api.go
@@ -23,6 +23,7 @@ import (
"fmt"
"math/big"
"reflect"
+ "strings"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
@@ -32,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/signer/storage"
)
const (
@@ -40,7 +42,7 @@ const (
// ExternalAPIVersion -- see extapi_changelog.md
ExternalAPIVersion = "6.0.0"
// InternalAPIVersion -- see intapi_changelog.md
- InternalAPIVersion = "4.0.0"
+ InternalAPIVersion = "6s.0.0"
)
// ExternalAPI defines the external API through which signing requests are made.
@@ -68,10 +70,6 @@ type UIClientAPI interface {
ApproveTx(request *SignTxRequest) (SignTxResponse, error)
// ApproveSignData prompt the user for confirmation to request to sign data
ApproveSignData(request *SignDataRequest) (SignDataResponse, error)
- // ApproveExport prompt the user for confirmation to export encrypted Account json
- ApproveExport(request *ExportRequest) (ExportResponse, error)
- // ApproveImport prompt the user for confirmation to import Account json
- ApproveImport(request *ImportRequest) (ImportResponse, error)
// ApproveListing prompt the user for confirmation to list accounts
// the list of accounts to list can be modified by the UI
ApproveListing(request *ListRequest) (ListResponse, error)
@@ -96,11 +94,12 @@ type UIClientAPI interface {
// SignerAPI defines the actual implementation of ExternalAPI
type SignerAPI struct {
- chainID *big.Int
- am *accounts.Manager
- UI UIClientAPI
- validator *Validator
- rejectMode bool
+ chainID *big.Int
+ am *accounts.Manager
+ UI UIClientAPI
+ validator *Validator
+ rejectMode bool
+ credentials storage.Storage
}
// Metadata about a request
@@ -187,25 +186,6 @@ type (
//The UI may make changes to the TX
Transaction SendTxArgs `json:"transaction"`
Approved bool `json:"approved"`
- Password string `json:"password"`
- }
- // ExportRequest info about query to export accounts
- ExportRequest struct {
- Address common.Address `json:"address"`
- Meta Metadata `json:"meta"`
- }
- // ExportResponse response to export-request
- ExportResponse struct {
- Approved bool `json:"approved"`
- }
- // ImportRequest info about request to import an Account
- ImportRequest struct {
- Meta Metadata `json:"meta"`
- }
- ImportResponse struct {
- Approved bool `json:"approved"`
- OldPassword string `json:"old_password"`
- NewPassword string `json:"new_password"`
}
SignDataRequest struct {
ContentType string `json:"content_type"`
@@ -217,14 +197,12 @@ type (
}
SignDataResponse struct {
Approved bool `json:"approved"`
- Password string
}
NewAccountRequest struct {
Meta Metadata `json:"meta"`
}
NewAccountResponse struct {
- Approved bool `json:"approved"`
- Password string `json:"password"`
+ Approved bool `json:"approved"`
}
ListRequest struct {
Accounts []accounts.Account `json:"accounts"`
@@ -240,8 +218,8 @@ type (
Info map[string]interface{} `json:"info"`
}
UserInputRequest struct {
- Prompt string `json:"prompt"`
Title string `json:"title"`
+ Prompt string `json:"prompt"`
IsPassword bool `json:"isPassword"`
}
UserInputResponse struct {
@@ -256,11 +234,11 @@ var ErrRequestDenied = errors.New("Request denied")
// key that is generated when a new Account is created.
// noUSB disables USB support that is required to support hardware devices such as
// ledger and trezor.
-func NewSignerAPI(am *accounts.Manager, chainID int64, noUSB bool, ui UIClientAPI, abidb *AbiDb, advancedMode bool) *SignerAPI {
+func NewSignerAPI(am *accounts.Manager, chainID int64, noUSB bool, ui UIClientAPI, abidb *AbiDb, advancedMode bool, credentials storage.Storage) *SignerAPI {
if advancedMode {
log.Info("Clef is in advanced mode: will warn instead of reject")
}
- signer := &SignerAPI{big.NewInt(chainID), am, ui, NewValidator(abidb), !advancedMode}
+ signer := &SignerAPI{big.NewInt(chainID), am, ui, NewValidator(abidb), !advancedMode, credentials}
if !noUSB {
signer.startUSBListener()
}
@@ -381,24 +359,27 @@ func (api *SignerAPI) New(ctx context.Context) (common.Address, error) {
if len(be) == 0 {
return common.Address{}, errors.New("password based accounts not supported")
}
- var (
- resp NewAccountResponse
- err error
- )
+ if resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)}); err != nil {
+ return common.Address{}, err
+ } else if !resp.Approved {
+ return common.Address{}, ErrRequestDenied
+ }
+
// Three retries to get a valid password
for i := 0; i < 3; i++ {
- resp, err = api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)})
+ resp, err := api.UI.OnInputRequired(UserInputRequest{
+ "New account password",
+ fmt.Sprintf("Please enter a password for the new account to be created (attempt %d of 3)", i),
+ true})
if err != nil {
- return common.Address{}, err
- }
- if !resp.Approved {
- return common.Address{}, ErrRequestDenied
+ log.Warn("error obtaining password", "attempt", i, "error", err)
+ continue
}
- if pwErr := ValidatePasswordFormat(resp.Password); pwErr != nil {
+ if pwErr := ValidatePasswordFormat(resp.Text); pwErr != nil {
api.UI.ShowError(fmt.Sprintf("Account creation attempt #%d failed due to password requirements: %v", (i + 1), pwErr))
} else {
// No error
- acc, err := be[0].(*keystore.KeyStore).NewAccount(resp.Password)
+ acc, err := be[0].(*keystore.KeyStore).NewAccount(resp.Text)
return acc.Address, err
}
}
@@ -452,6 +433,24 @@ func logDiff(original *SignTxRequest, new *SignTxResponse) bool {
return modified
}
+func (api *SignerAPI) lookupPassword(address common.Address) string {
+ return api.credentials.Get(strings.ToLower(address.String()))
+}
+func (api *SignerAPI) lookupOrQueryPassword(address common.Address, title, prompt string) (string, error) {
+ if pw := api.lookupPassword(address); pw != "" {
+ return pw, nil
+ } else {
+ pwResp, err := api.UI.OnInputRequired(UserInputRequest{title, prompt, true})
+ if err != nil {
+ log.Warn("error obtaining password", "error", err)
+ // We'll not forward the error here, in case the error contains info about the response from the UI,
+ // which could leak the password if it was malformed json or something
+ return "", errors.New("internal error")
+ }
+ return pwResp.Text, nil
+ }
+}
+
// SignTransaction signs the given Transaction and returns it both as json and rlp-encoded form
func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) {
var (
@@ -495,9 +494,14 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
}
// Convert fields into a real transaction
var unsignedTx = result.Transaction.toTransaction()
-
+ // Get the password for the transaction
+ pw, err := api.lookupOrQueryPassword(acc.Address, "Account password",
+ fmt.Sprintf("Please enter the password for account %s", acc.Address.String()))
+ if err != nil {
+ return nil, err
+ }
// The one to sign is the one that was returned from the UI
- signedTx, err := wallet.SignTxWithPassphrase(acc, result.Password, unsignedTx, api.chainID)
+ signedTx, err := wallet.SignTxWithPassphrase(acc, pw, unsignedTx, api.chainID)
if err != nil {
api.UI.ShowError(err.Error())
return nil, err
diff --git a/signer/core/api_test.go b/signer/core/api_test.go
index bf4e91eb9..3225c10ea 100644
--- a/signer/core/api_test.go
+++ b/signer/core/api_test.go
@@ -19,7 +19,6 @@ package core
import (
"bytes"
"context"
- "errors"
"fmt"
"io/ioutil"
"math/big"
@@ -35,62 +34,49 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/signer/storage"
)
//Used for testing
-type HeadlessUI struct {
- controller chan string
+type headlessUi struct {
+ approveCh chan string // to send approve/deny
+ inputCh chan string // to send password
}
-func (ui *HeadlessUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
- return UserInputResponse{}, errors.New("not implemented")
+func (ui *headlessUi) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
+ input := <-ui.inputCh
+ return UserInputResponse{Text: input}, nil
}
-func (ui *HeadlessUI) OnSignerStartup(info StartupInfo) {
-}
-func (ui *HeadlessUI) RegisterUIServer(api *UIServerAPI) {
-}
+func (ui *headlessUi) OnSignerStartup(info StartupInfo) {}
+func (ui *headlessUi) RegisterUIServer(api *UIServerAPI) {}
+func (ui *headlessUi) OnApprovedTx(tx ethapi.SignTransactionResult) {}
-func (ui *HeadlessUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
- fmt.Printf("OnApproved()\n")
-}
+func (ui *headlessUi) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
-func (ui *HeadlessUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
-
- switch <-ui.controller {
+ switch <-ui.approveCh {
case "Y":
- return SignTxResponse{request.Transaction, true, <-ui.controller}, nil
- case "M": //Modify
+ return SignTxResponse{request.Transaction, true}, nil
+ case "M": // modify
+ // The headless UI always modifies the transaction
old := big.Int(request.Transaction.Value)
newVal := big.NewInt(0).Add(&old, big.NewInt(1))
request.Transaction.Value = hexutil.Big(*newVal)
- return SignTxResponse{request.Transaction, true, <-ui.controller}, nil
+ return SignTxResponse{request.Transaction, true}, nil
default:
- return SignTxResponse{request.Transaction, false, ""}, nil
+ 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) {
- return ExportResponse{<-ui.controller == "Y"}, nil
-
-}
-
-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) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
+ approved := "Y" == <-ui.approveCh
+ return SignDataResponse{approved}, nil
}
-func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error) {
- switch <-ui.controller {
+func (ui *headlessUi) ApproveListing(request *ListRequest) (ListResponse, error) {
+ approval := <-ui.approveCh
+ //fmt.Printf("approval %s\n", approval)
+ switch approval {
case "A":
return ListResponse{request.Accounts}, nil
case "1":
@@ -102,19 +88,19 @@ func (ui *HeadlessUI) ApproveListing(request *ListRequest) (ListResponse, error)
}
}
-func (ui *HeadlessUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
- if "Y" == <-ui.controller {
- return NewAccountResponse{true, <-ui.controller}, nil
+func (ui *headlessUi) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
+ if "Y" == <-ui.approveCh {
+ return NewAccountResponse{true}, nil
}
- return NewAccountResponse{false, ""}, nil
+ return NewAccountResponse{false}, nil
}
-func (ui *HeadlessUI) ShowError(message string) {
+func (ui *headlessUi) ShowError(message string) {
//stdout is used by communication
fmt.Fprintln(os.Stderr, message)
}
-func (ui *HeadlessUI) ShowInfo(message string) {
+func (ui *headlessUi) ShowInfo(message string) {
//stdout is used by communication
fmt.Fprintln(os.Stderr, message)
}
@@ -131,25 +117,20 @@ func tmpDirName(t *testing.T) string {
return d
}
-func setup(t *testing.T) (*SignerAPI, chan string) {
-
- controller := make(chan string, 20)
-
+func setup(t *testing.T) (*SignerAPI, *headlessUi) {
db, err := NewAbiDBFromFile("../../cmd/clef/4byte.json")
if err != nil {
t.Fatal(err.Error())
}
- var (
- ui = &HeadlessUI{controller}
- am = StartClefAccountManager(tmpDirName(t), true, true)
- api = NewSignerAPI(am, 1337, true, ui, db, true)
- )
- return api, controller
-}
-func createAccount(control chan string, api *SignerAPI, t *testing.T) {
+ ui := &headlessUi{make(chan string, 20), make(chan string, 20)}
+ am := StartClefAccountManager(tmpDirName(t), true, true)
+ api := NewSignerAPI(am, 1337, true, ui, db, true, &storage.NoStorage{})
+ return api, ui
- control <- "Y"
- control <- "a_long_password"
+}
+func createAccount(ui *headlessUi, api *SignerAPI, t *testing.T) {
+ ui.approveCh <- "Y"
+ ui.inputCh <- "a_long_password"
_, err := api.New(context.Background())
if err != nil {
t.Fatal(err)
@@ -158,14 +139,13 @@ func createAccount(control chan string, api *SignerAPI, t *testing.T) {
time.Sleep(250 * time.Millisecond)
}
-func failCreateAccountWithPassword(control chan string, api *SignerAPI, password string, t *testing.T) {
+func failCreateAccountWithPassword(ui *headlessUi, api *SignerAPI, password string, t *testing.T) {
- control <- "Y"
- control <- password
- control <- "Y"
- control <- password
- control <- "Y"
- control <- password
+ ui.approveCh <- "Y"
+ // We will be asked three times to provide a suitable password
+ ui.inputCh <- password
+ ui.inputCh <- password
+ ui.inputCh <- password
addr, err := api.New(context.Background())
if err == nil {
@@ -176,8 +156,8 @@ func failCreateAccountWithPassword(control chan string, api *SignerAPI, password
}
}
-func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) {
- control <- "N"
+func failCreateAccount(ui *headlessUi, api *SignerAPI, t *testing.T) {
+ ui.approveCh <- "N"
addr, err := api.New(context.Background())
if err != ErrRequestDenied {
t.Fatal(err)
@@ -187,19 +167,20 @@ func failCreateAccount(control chan string, api *SignerAPI, t *testing.T) {
}
}
-func list(control chan string, api *SignerAPI, t *testing.T) []common.Address {
- control <- "A"
- list, err := api.List(context.Background())
- if err != nil {
- t.Fatal(err)
- }
- return list
+func list(ui *headlessUi, api *SignerAPI, t *testing.T) ([]common.Address, error) {
+ ui.approveCh <- "A"
+ return api.List(context.Background())
+
}
func TestNewAcc(t *testing.T) {
api, control := setup(t)
verifyNum := func(num int) {
- if list := list(control, api, t); len(list) != num {
+ list, err := list(control, api, t)
+ if err != nil {
+ t.Errorf("Unexpected error %v", err)
+ }
+ if len(list) != num {
t.Errorf("Expected %d accounts, got %d", num, len(list))
}
}
@@ -212,18 +193,16 @@ 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:
// Listing one Account
- control <- "1"
+ control.approveCh <- "1"
list, err := api.List(context.Background())
if err != nil {
t.Fatal(err)
@@ -232,7 +211,7 @@ func TestNewAcc(t *testing.T) {
t.Fatalf("List should only show one Account")
}
// Listing denied
- control <- "Nope"
+ control.approveCh <- "Nope"
list, err = api.List(context.Background())
if len(list) != 0 {
t.Fatalf("List should be empty")
@@ -269,7 +248,7 @@ func TestSignTx(t *testing.T) {
api, control := setup(t)
createAccount(control, api, t)
- control <- "A"
+ control.approveCh <- "A"
list, err = api.List(context.Background())
if err != nil {
t.Fatal(err)
@@ -279,8 +258,8 @@ func TestSignTx(t *testing.T) {
methodSig := "test(uint)"
tx := mkTestTx(a)
- control <- "Y"
- control <- "wrongpassword"
+ control.approveCh <- "Y"
+ control.inputCh <- "wrongpassword"
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
if res != nil {
t.Errorf("Expected nil-response, got %v", res)
@@ -288,7 +267,7 @@ func TestSignTx(t *testing.T) {
if err != keystore.ErrDecrypt {
t.Errorf("Expected ErrLocked! %v", err)
}
- control <- "No way"
+ control.approveCh <- "No way"
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
if res != nil {
t.Errorf("Expected nil-response, got %v", res)
@@ -296,8 +275,9 @@ func TestSignTx(t *testing.T) {
if err != ErrRequestDenied {
t.Errorf("Expected ErrRequestDenied! %v", err)
}
- control <- "Y"
- control <- "a_long_password"
+ // Sign with correct password
+ control.approveCh <- "Y"
+ control.inputCh <- "a_long_password"
res, err = api.SignTransaction(context.Background(), tx, &methodSig)
if err != nil {
@@ -310,8 +290,8 @@ func TestSignTx(t *testing.T) {
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 <- "a_long_password"
+ control.approveCh <- "Y"
+ control.inputCh <- "a_long_password"
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
if err != nil {
@@ -322,8 +302,8 @@ func TestSignTx(t *testing.T) {
}
//The tx is modified by the UI
- control <- "M"
- control <- "a_long_password"
+ control.approveCh <- "M"
+ control.inputCh <- "a_long_password"
res2, err = api.SignTransaction(context.Background(), tx, &methodSig)
if err != nil {
@@ -341,31 +321,3 @@ func TestSignTx(t *testing.T) {
}
}
-
-/*
-func TestAsyncronousResponses(t *testing.T){
-
- //Set up one account
- api, control := setup(t)
- createAccount(control, api, t)
-
- // Two transactions, the second one with larger value than the first
- tx1 := mkTestTx()
- newVal := big.NewInt(0).Add((*big.Int) (tx1.Value), big.NewInt(1))
- tx2 := mkTestTx()
- tx2.Value = (*hexutil.Big)(newVal)
-
- control <- "W" //wait
- control <- "Y" //
- control <- "a_long_password"
- control <- "Y" //
- control <- "a_long_password"
-
- var err error
-
- h1, err := api.SignTransaction(context.Background(), common.HexToAddress("1111"), tx1, nil)
- h2, err := api.SignTransaction(context.Background(), common.HexToAddress("2222"), tx2, nil)
-
-
- }
-*/
diff --git a/signer/core/cliui.go b/signer/core/cliui.go
index a6c0bdb16..cf7101441 100644
--- a/signer/core/cliui.go
+++ b/signer/core/cliui.go
@@ -87,9 +87,10 @@ func (ui *CommandlineUI) readPasswordText(inputstring string) string {
}
func (ui *CommandlineUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
- fmt.Println(info.Title)
- fmt.Println(info.Prompt)
+
+ fmt.Printf("## %s\n\n%s\n", info.Title, info.Prompt)
if info.IsPassword {
+ fmt.Printf("> ")
text, err := terminal.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
log.Error("Failed to read password", "err", err)
@@ -156,9 +157,9 @@ func (ui *CommandlineUI) ApproveTx(request *SignTxRequest) (SignTxResponse, erro
showMetadata(request.Meta)
fmt.Printf("-------------------------------------------\n")
if !ui.confirm() {
- return SignTxResponse{request.Transaction, false, ""}, nil
+ return SignTxResponse{request.Transaction, false}, nil
}
- return SignTxResponse{request.Transaction, true, ui.readPassword()}, nil
+ return SignTxResponse{request.Transaction, true}, nil
}
// ApproveSignData prompt the user for confirmation to request to sign data
@@ -178,40 +179,9 @@ func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest) (SignDataResp
fmt.Printf("-------------------------------------------\n")
showMetadata(request.Meta)
if !ui.confirm() {
- return SignDataResponse{false, ""}, nil
- }
- return SignDataResponse{true, ui.readPassword()}, nil
-}
-
-// ApproveExport prompt the user for confirmation to export encrypted Account json
-func (ui *CommandlineUI) ApproveExport(request *ExportRequest) (ExportResponse, error) {
- ui.mu.Lock()
- defer ui.mu.Unlock()
-
- fmt.Printf("-------- Export Account request--------------\n")
- fmt.Printf("A request has been made to export the (encrypted) keyfile\n")
- fmt.Printf("Approving this operation means that the caller obtains the (encrypted) contents\n")
- fmt.Printf("\n")
- fmt.Printf("Account: %x\n", request.Address)
- //fmt.Printf("keyfile: \n%v\n", request.file)
- fmt.Printf("-------------------------------------------\n")
- showMetadata(request.Meta)
- return ExportResponse{ui.confirm()}, nil
-}
-
-// ApproveImport prompt the user for confirmation to import Account json
-func (ui *CommandlineUI) ApproveImport(request *ImportRequest) (ImportResponse, error) {
- ui.mu.Lock()
- defer ui.mu.Unlock()
-
- fmt.Printf("-------- Import Account request--------------\n")
- fmt.Printf("A request has been made to import an encrypted keyfile\n")
- fmt.Printf("-------------------------------------------\n")
- showMetadata(request.Meta)
- if !ui.confirm() {
- return ImportResponse{false, "", ""}, nil
+ return SignDataResponse{false}, nil
}
- return ImportResponse{true, ui.readPasswordText("Old password"), ui.readPasswordText("New password")}, nil
+ return SignDataResponse{true}, nil
}
// ApproveListing prompt the user for confirmation to list accounts
@@ -248,21 +218,20 @@ func (ui *CommandlineUI) ApproveNewAccount(request *NewAccountRequest) (NewAccou
fmt.Printf("and the address is returned to the external caller\n\n")
showMetadata(request.Meta)
if !ui.confirm() {
- return NewAccountResponse{false, ""}, nil
+ return NewAccountResponse{false}, nil
}
- return NewAccountResponse{true, ui.readPassword()}, nil
+ return NewAccountResponse{true}, nil
}
// ShowError displays error message to user
func (ui *CommandlineUI) ShowError(message string) {
- fmt.Printf("-------- Error message from Clef-----------\n")
- fmt.Println(message)
+ fmt.Printf("## Error \n%s\n", message)
fmt.Printf("-------------------------------------------\n")
}
// ShowInfo displays info message to user
func (ui *CommandlineUI) ShowInfo(message string) {
- fmt.Printf("Info: %v\n", message)
+ fmt.Printf("## Info \n%s\n", message)
}
func (ui *CommandlineUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go
index 9df39ef59..475a8837e 100644
--- a/signer/core/signed_data.go
+++ b/signer/core/signed_data.go
@@ -139,8 +139,14 @@ func (api *SignerAPI) sign(addr common.MixedcaseAddress, req *SignDataRequest, l
if err != nil {
return nil, err
}
+ pw, err := api.lookupOrQueryPassword(account.Address,
+ "Password for signing",
+ fmt.Sprintf("Please enter password for signing data with account %s", account.Address.Hex()))
+ if err != nil {
+ return nil, err
+ }
// Sign the data with the wallet
- signature, err := wallet.SignDataWithPassphrase(account, res.Password, req.ContentType, req.Rawdata)
+ signature, err := wallet.SignDataWithPassphrase(account, pw, req.ContentType, req.Rawdata)
if err != nil {
return nil, err
}
diff --git a/signer/core/signed_data_test.go b/signer/core/signed_data_test.go
index 7d44bce2c..76e1b72ee 100644
--- a/signer/core/signed_data_test.go
+++ b/signer/core/signed_data_test.go
@@ -179,15 +179,15 @@ func TestSignData(t *testing.T) {
//Create two accounts
createAccount(control, api, t)
createAccount(control, api, t)
- control <- "1"
+ control.approveCh <- "1"
list, err := api.List(context.Background())
if err != nil {
t.Fatal(err)
}
a := common.NewMixedcaseAddress(list[0])
- control <- "Y"
- control <- "wrongpassword"
+ control.approveCh <- "Y"
+ control.inputCh <- "wrongpassword"
signature, err := api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
if signature != nil {
t.Errorf("Expected nil-data, got %x", signature)
@@ -195,7 +195,7 @@ func TestSignData(t *testing.T) {
if err != keystore.ErrDecrypt {
t.Errorf("Expected ErrLocked! '%v'", err)
}
- control <- "No way"
+ control.approveCh <- "No way"
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
if signature != nil {
t.Errorf("Expected nil-data, got %x", signature)
@@ -204,8 +204,8 @@ func TestSignData(t *testing.T) {
t.Errorf("Expected ErrRequestDenied! '%v'", err)
}
// text/plain
- control <- "Y"
- control <- "a_long_password"
+ control.approveCh <- "Y"
+ control.inputCh <- "a_long_password"
signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
if err != nil {
t.Fatal(err)
@@ -214,8 +214,8 @@ func TestSignData(t *testing.T) {
t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
}
// data/typed
- control <- "Y"
- control <- "a_long_password"
+ control.approveCh <- "Y"
+ control.inputCh <- "a_long_password"
signature, err = api.SignTypedData(context.Background(), a, typedData)
if err != nil {
t.Fatal(err)
diff --git a/signer/core/stdioui.go b/signer/core/stdioui.go
index cb25bc2d0..0edb72def 100644
--- a/signer/core/stdioui.go
+++ b/signer/core/stdioui.go
@@ -65,71 +65,59 @@ func (ui *StdIOUI) notify(serviceMethod string, args interface{}) error {
func (ui *StdIOUI) ApproveTx(request *SignTxRequest) (SignTxResponse, error) {
var result SignTxResponse
- err := ui.dispatch("ApproveTx", request, &result)
+ err := ui.dispatch("ui_approveTx", request, &result)
return result, err
}
func (ui *StdIOUI) ApproveSignData(request *SignDataRequest) (SignDataResponse, error) {
var result SignDataResponse
- err := ui.dispatch("ApproveSignData", request, &result)
- return result, err
-}
-
-func (ui *StdIOUI) ApproveExport(request *ExportRequest) (ExportResponse, error) {
- var result ExportResponse
- err := ui.dispatch("ApproveExport", request, &result)
- return result, err
-}
-
-func (ui *StdIOUI) ApproveImport(request *ImportRequest) (ImportResponse, error) {
- var result ImportResponse
- err := ui.dispatch("ApproveImport", request, &result)
+ err := ui.dispatch("ui_approveSignData", request, &result)
return result, err
}
func (ui *StdIOUI) ApproveListing(request *ListRequest) (ListResponse, error) {
var result ListResponse
- err := ui.dispatch("ApproveListing", request, &result)
+ err := ui.dispatch("ui_approveListing", request, &result)
return result, err
}
func (ui *StdIOUI) ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error) {
var result NewAccountResponse
- err := ui.dispatch("ApproveNewAccount", request, &result)
+ err := ui.dispatch("ui_approveNewAccount", request, &result)
return result, err
}
func (ui *StdIOUI) ShowError(message string) {
- err := ui.notify("ShowError", &Message{message})
+ err := ui.notify("ui_showError", &Message{message})
if err != nil {
- log.Info("Error calling 'ShowError'", "exc", err.Error(), "msg", message)
+ log.Info("Error calling 'ui_showError'", "exc", err.Error(), "msg", message)
}
}
func (ui *StdIOUI) ShowInfo(message string) {
- err := ui.notify("ShowInfo", Message{message})
+ err := ui.notify("ui_showInfo", Message{message})
if err != nil {
- log.Info("Error calling 'ShowInfo'", "exc", err.Error(), "msg", message)
+ log.Info("Error calling 'ui_showInfo'", "exc", err.Error(), "msg", message)
}
}
func (ui *StdIOUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
- err := ui.notify("OnApprovedTx", tx)
+ err := ui.notify("ui_onApprovedTx", tx)
if err != nil {
- log.Info("Error calling 'OnApprovedTx'", "exc", err.Error(), "tx", tx)
+ log.Info("Error calling 'ui_onApprovedTx'", "exc", err.Error(), "tx", tx)
}
}
func (ui *StdIOUI) OnSignerStartup(info StartupInfo) {
- err := ui.notify("OnSignerStartup", info)
+ err := ui.notify("ui_onSignerStartup", info)
if err != nil {
- log.Info("Error calling 'OnSignerStartup'", "exc", err.Error(), "info", info)
+ log.Info("Error calling 'ui_onSignerStartup'", "exc", err.Error(), "info", info)
}
}
func (ui *StdIOUI) OnInputRequired(info UserInputRequest) (UserInputResponse, error) {
var result UserInputResponse
- err := ui.dispatch("OnInputRequired", info, &result)
+ err := ui.dispatch("ui_onInputRequired", info, &result)
if err != nil {
- log.Info("Error calling 'OnInputRequired'", "exc", err.Error(), "info", info)
+ log.Info("Error calling 'ui_onInputRequired'", "exc", err.Error(), "info", info)
}
return result, err
}
diff --git a/signer/rules/rules.go b/signer/rules/rules.go
index 1f81e21bd..5ebffa3af 100644
--- a/signer/rules/rules.go
+++ b/signer/rules/rules.go
@@ -22,7 +22,6 @@ import (
"os"
"strings"
- "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/signer/core"
@@ -42,25 +41,23 @@ func consoleOutput(call otto.FunctionCall) otto.Value {
for _, argument := range call.ArgumentList {
output = append(output, fmt.Sprintf("%v", argument))
}
- fmt.Fprintln(os.Stdout, strings.Join(output, " "))
+ fmt.Fprintln(os.Stderr, strings.Join(output, " "))
return otto.Value{}
}
// rulesetUI provides an implementation of UIClientAPI that evaluates a javascript
// file for each defined UI-method
type rulesetUI struct {
- next core.UIClientAPI // The next handler, for manual processing
- storage storage.Storage
- credentials storage.Storage
- jsRules string // The rules to use
+ next core.UIClientAPI // The next handler, for manual processing
+ storage storage.Storage
+ jsRules string // The rules to use
}
-func NewRuleEvaluator(next core.UIClientAPI, jsbackend, credentialsBackend storage.Storage) (*rulesetUI, error) {
+func NewRuleEvaluator(next core.UIClientAPI, jsbackend storage.Storage) (*rulesetUI, error) {
c := &rulesetUI{
- next: next,
- storage: jsbackend,
- credentials: credentialsBackend,
- jsRules: "",
+ next: next,
+ storage: jsbackend,
+ jsRules: "",
}
return c, nil
@@ -153,18 +150,12 @@ func (r *rulesetUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse,
if approved {
return core.SignTxResponse{
Transaction: request.Transaction,
- Approved: true,
- Password: r.lookupPassword(request.Transaction.From.Address()),
- },
+ Approved: true},
nil
}
return core.SignTxResponse{Approved: false}, err
}
-func (r *rulesetUI) lookupPassword(address common.Address) string {
- return r.credentials.Get(strings.ToLower(address.String()))
-}
-
func (r *rulesetUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
jsonreq, err := json.Marshal(request)
approved, err := r.checkApproval("ApproveSignData", jsonreq, err)
@@ -173,28 +164,9 @@ func (r *rulesetUI) ApproveSignData(request *core.SignDataRequest) (core.SignDat
return r.next.ApproveSignData(request)
}
if approved {
- return core.SignDataResponse{Approved: true, Password: r.lookupPassword(request.Address.Address())}, nil
- }
- return core.SignDataResponse{Approved: false, Password: ""}, err
-}
-
-func (r *rulesetUI) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) {
- jsonreq, err := json.Marshal(request)
- approved, err := r.checkApproval("ApproveExport", jsonreq, err)
- if err != nil {
- log.Info("Rule-based approval error, going to manual", "error", err)
- return r.next.ApproveExport(request)
- }
- if approved {
- return core.ExportResponse{Approved: true}, nil
+ return core.SignDataResponse{Approved: true}, nil
}
- return core.ExportResponse{Approved: false}, err
-}
-
-func (r *rulesetUI) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) {
- // This cannot be handled by rules, requires setting a password
- // dispatch to next
- return r.next.ApproveImport(request)
+ return core.SignDataResponse{Approved: false}, err
}
// OnInputRequired not handled by rules
diff --git a/signer/rules/rules_test.go b/signer/rules/rules_test.go
index eca4b29a0..19d9049c7 100644
--- a/signer/rules/rules_test.go
+++ b/signer/rules/rules_test.go
@@ -84,19 +84,11 @@ func (alwaysDenyUI) OnSignerStartup(info core.StartupInfo) {
}
func (alwaysDenyUI) ApproveTx(request *core.SignTxRequest) (core.SignTxResponse, error) {
- return core.SignTxResponse{Transaction: request.Transaction, Approved: false, Password: ""}, nil
+ return core.SignTxResponse{Transaction: request.Transaction, Approved: false}, nil
}
func (alwaysDenyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataResponse, error) {
- return core.SignDataResponse{Approved: false, Password: ""}, nil
-}
-
-func (alwaysDenyUI) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) {
- return core.ExportResponse{Approved: false}, nil
-}
-
-func (alwaysDenyUI) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) {
- return core.ImportResponse{Approved: false, OldPassword: "", NewPassword: ""}, nil
+ return core.SignDataResponse{Approved: false}, nil
}
func (alwaysDenyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
@@ -104,7 +96,7 @@ func (alwaysDenyUI) ApproveListing(request *core.ListRequest) (core.ListResponse
}
func (alwaysDenyUI) ApproveNewAccount(request *core.NewAccountRequest) (core.NewAccountResponse, error) {
- return core.NewAccountResponse{Approved: false, Password: ""}, nil
+ return core.NewAccountResponse{Approved: false}, nil
}
func (alwaysDenyUI) ShowError(message string) {
@@ -120,7 +112,7 @@ func (alwaysDenyUI) OnApprovedTx(tx ethapi.SignTransactionResult) {
}
func initRuleEngine(js string) (*rulesetUI, error) {
- r, err := NewRuleEvaluator(&alwaysDenyUI{}, storage.NewEphemeralStorage(), storage.NewEphemeralStorage())
+ r, err := NewRuleEvaluator(&alwaysDenyUI{}, storage.NewEphemeralStorage())
if err != nil {
return nil, fmt.Errorf("failed to create js engine: %v", err)
}
@@ -225,16 +217,6 @@ func (d *dummyUI) ApproveSignData(request *core.SignDataRequest) (core.SignDataR
return core.SignDataResponse{}, core.ErrRequestDenied
}
-func (d *dummyUI) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) {
- d.calls = append(d.calls, "ApproveExport")
- return core.ExportResponse{}, core.ErrRequestDenied
-}
-
-func (d *dummyUI) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) {
- d.calls = append(d.calls, "ApproveImport")
- return core.ImportResponse{}, core.ErrRequestDenied
-}
-
func (d *dummyUI) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
d.calls = append(d.calls, "ApproveListing")
return core.ListResponse{}, core.ErrRequestDenied
@@ -266,8 +248,7 @@ func TestForwarding(t *testing.T) {
js := ""
ui := &dummyUI{make([]string, 0)}
jsBackend := storage.NewEphemeralStorage()
- credBackend := storage.NewEphemeralStorage()
- r, err := NewRuleEvaluator(ui, jsBackend, credBackend)
+ r, err := NewRuleEvaluator(ui, jsBackend)
if err != nil {
t.Fatalf("Failed to create js engine: %v", err)
}
@@ -276,17 +257,15 @@ func TestForwarding(t *testing.T) {
}
r.ApproveSignData(nil)
r.ApproveTx(nil)
- r.ApproveImport(nil)
r.ApproveNewAccount(nil)
r.ApproveListing(nil)
- r.ApproveExport(nil)
r.ShowError("test")
r.ShowInfo("test")
//This one is not forwarded
r.OnApprovedTx(ethapi.SignTransactionResult{})
- expCalls := 8
+ expCalls := 6
if len(ui.calls) != expCalls {
t.Errorf("Expected %d forwarded calls, got %d: %s", expCalls, len(ui.calls), strings.Join(ui.calls, ","))
@@ -545,16 +524,6 @@ func (d *dontCallMe) ApproveSignData(request *core.SignDataRequest) (core.SignDa
return core.SignDataResponse{}, core.ErrRequestDenied
}
-func (d *dontCallMe) ApproveExport(request *core.ExportRequest) (core.ExportResponse, error) {
- d.t.Fatalf("Did not expect next-handler to be called")
- return core.ExportResponse{}, core.ErrRequestDenied
-}
-
-func (d *dontCallMe) ApproveImport(request *core.ImportRequest) (core.ImportResponse, error) {
- d.t.Fatalf("Did not expect next-handler to be called")
- return core.ImportResponse{}, core.ErrRequestDenied
-}
-
func (d *dontCallMe) ApproveListing(request *core.ListRequest) (core.ListResponse, error) {
d.t.Fatalf("Did not expect next-handler to be called")
return core.ListResponse{}, core.ErrRequestDenied
@@ -597,7 +566,7 @@ func TestContextIsCleared(t *testing.T) {
}
`
ui := &dontCallMe{t}
- r, err := NewRuleEvaluator(ui, storage.NewEphemeralStorage(), storage.NewEphemeralStorage())
+ r, err := NewRuleEvaluator(ui, storage.NewEphemeralStorage())
if err != nil {
t.Fatalf("Failed to create js engine: %v", err)
}
diff --git a/signer/storage/storage.go b/signer/storage/storage.go
index 60f4e3892..50c55e455 100644
--- a/signer/storage/storage.go
+++ b/signer/storage/storage.go
@@ -17,10 +17,6 @@
package storage
-import (
- "fmt"
-)
-
type Storage interface {
// Put stores a value by key. 0-length keys results in no-op
Put(key, value string)
@@ -39,7 +35,7 @@ func (s *EphemeralStorage) Put(key, value string) {
if len(key) == 0 {
return
}
- fmt.Printf("storage: put %v -> %v\n", key, value)
+ //fmt.Printf("storage: put %v -> %v\n", key, value)
s.data[key] = value
}
@@ -47,7 +43,7 @@ func (s *EphemeralStorage) Get(key string) string {
if len(key) == 0 {
return ""
}
- fmt.Printf("storage: get %v\n", key)
+ //fmt.Printf("storage: get %v\n", key)
if v, exist := s.data[key]; exist {
return v
}
@@ -60,3 +56,11 @@ func NewEphemeralStorage() Storage {
}
return s
}
+
+// NoStorage is a dummy construct which doesn't remember anything you tell it
+type NoStorage struct{}
+
+func (s *NoStorage) Put(key, value string) {}
+func (s *NoStorage) Get(key string) string {
+ return ""
+}