aboutsummaryrefslogtreecommitdiffstats
path: root/accounts
diff options
context:
space:
mode:
authorPaul Berg <hello@paulrberg.com>2019-02-06 15:30:49 +0800
committerMartin Holst Swende <martin@swende.se>2019-02-06 15:30:49 +0800
commit572baae10a28da2d02085df7e2f3a282883f5d6e (patch)
tree3e5424ac64dbeae440604d32aed4293199179c99 /accounts
parent7c60d0a6a2d3925c2862cbbb188988475619fd0d (diff)
downloadgo-tangerine-572baae10a28da2d02085df7e2f3a282883f5d6e.tar
go-tangerine-572baae10a28da2d02085df7e2f3a282883f5d6e.tar.gz
go-tangerine-572baae10a28da2d02085df7e2f3a282883f5d6e.tar.bz2
go-tangerine-572baae10a28da2d02085df7e2f3a282883f5d6e.tar.lz
go-tangerine-572baae10a28da2d02085df7e2f3a282883f5d6e.tar.xz
go-tangerine-572baae10a28da2d02085df7e2f3a282883f5d6e.tar.zst
go-tangerine-572baae10a28da2d02085df7e2f3a282883f5d6e.zip
signer, clef: implement EIP191/712 (#17789)
* Named functions and defined a basic EIP191 content type list * Written basic content type functions * Added ecRecover method in the clef api * Updated the extapi changelog and addded indications in the README * Changed the version of the external API * Added tests for 0x45 * Implementing UnmarshalJSON() for TypedData * Working on TypedData * Solved the auditlog issue * Changed method to signTypedData * Changed mimes and implemented the 'encodeType' function for EIP-712 * Polished docstrings, ran goimports and swapped fmt.Errorf with errors.New where possible * Drafted recursive encodeData * Ran goimports and gofmt * Drafted first version of EIP-712, including tests * Temporarily switched to using common.Address in tests * Drafted text/validator and and rewritten []byte as hexutil.Bytes * Solved stringified address encoding issue * Changed the property type required by signData from bytes to interface{} * Fixed bugs in 'data/typed' signs * Brought legal warning back after temporarily disabling it for development * Added example RPC calls for account_signData and account_signTypedData * Named functions and defined a basic EIP191 content type list * Written basic content type functions * Added ecRecover method in the clef api * Updated the extapi changelog and addded indications in the README * Added tests for 0x45 * Implementing UnmarshalJSON() for TypedData * Working on TypedData * Solved the auditlog issue * Changed method to signTypedData * Changed mimes and implemented the 'encodeType' function for EIP-712 * Polished docstrings, ran goimports and swapped fmt.Errorf with errors.New where possible * Drafted recursive encodeData * Ran goimports and gofmt * Drafted first version of EIP-712, including tests * Temporarily switched to using common.Address in tests * Drafted text/validator and and rewritten []byte as hexutil.Bytes * Solved stringified address encoding issue * Changed the property type required by signData from bytes to interface{} * Fixed bugs in 'data/typed' signs * Brought legal warning back after temporarily disabling it for development * Added example RPC calls for account_signData and account_signTypedData * Polished and fixed PR * Polished and fixed PR * Solved malformed data panics and also wrote tests * Solved malformed data panics and also wrote tests * Added alphabetical sorting to type dependencies * Added alphabetical sorting to type dependencies * Added pretty print to data/typed UI * Added pretty print to data/typed UI * signer: more tests for typed data * signer: more tests for typed data * Fixed TestMalformedData4 errors and renamed IsValid to Validate * Fixed TestMalformedData4 errors and renamed IsValid to Validate * Fixed more new failing tests and deanonymised some functions * Fixed more new failing tests and deanonymised some functions * Added types to EIP712 output in cliui * Added types to EIP712 output in cliui * Fixed regexp issues * Fixed regexp issues * Added pseudo-failing test * Added pseudo-failing test * Fixed false positive test * Fixed false positive test * Added PrettyPrint method * Added PrettyPrint method * signer: refactor formatting and UI * signer: make ui use new message format for signing * Fixed breaking changes * Fixed rules_test failing test * Added extra regexp for reference types * signer: more hard types * Fixed failing test, formatted files * signer: use golang/x keccak * Fixed goimports error * clef, signer: address some review concerns * Implemented latest recommendations * Fixed comments and uintint256 issue * accounts, signer: fix mimetypes, add interface to sign data with passphrase * signer, accounts: remove duplicated code, pass hash preimages to signing * signer: prevent panic in type assertions, make cliui print rawdata as quotable-safe * signer: linter fixes, remove deprecated crypto dependency * accounts: fix goimport
Diffstat (limited to 'accounts')
-rw-r--r--accounts/accounts.go48
-rw-r--r--accounts/external/backend.go9
-rw-r--r--accounts/keystore/wallet.go30
-rw-r--r--accounts/usbwallet/wallet.go7
4 files changed, 66 insertions, 28 deletions
diff --git a/accounts/accounts.go b/accounts/accounts.go
index 11232b19a..b57f282b3 100644
--- a/accounts/accounts.go
+++ b/accounts/accounts.go
@@ -35,6 +35,13 @@ type Account struct {
URL URL `json:"url"` // Optional resource locator within a backend
}
+const (
+ MimetypeTextWithValidator = "text/validator"
+ MimetypeTypedData = "data/typed"
+ MimetypeClique = "application/x-clique-header"
+ MimetypeTextPlain = "text/plain"
+)
+
// Wallet represents a software or hardware wallet that might contain one or more
// accounts (derived from the same seed).
type Wallet interface {
@@ -101,6 +108,12 @@ type Wallet interface {
// the account in a keystore).
SignData(account Account, mimeType string, data []byte) ([]byte, error)
+ // SignDataWithPassphrase is identical to SignData, but also takes a password
+ // NOTE: there's an chance that an erroneous call might mistake the two strings, and
+ // supply password in the mimetype field, or vice versa. Thus, an implementation
+ // should never echo the mimetype or return the mimetype in the error-response
+ SignDataWithPassphrase(account Account, passphrase, mimeType string, data []byte) ([]byte, error)
+
// Signtext requests the wallet to sign the hash of a given piece of data, prefixed
// by the Ethereum prefix scheme
// It looks up the account specified either solely via its address contained within,
@@ -114,6 +127,9 @@ type Wallet interface {
// the account in a keystore).
SignText(account Account, text []byte) ([]byte, error)
+ // SignTextWithPassphrase is identical to Signtext, but also takes a password
+ SignTextWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)
+
// SignTx requests the wallet to sign the given transaction.
//
// It looks up the account specified either solely via its address contained within,
@@ -127,18 +143,7 @@ type Wallet interface {
// the account in a keystore).
SignTx(account Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
- // SignTextWithPassphrase requests the wallet to sign the given text with the
- // given passphrase as extra authentication information.
- //
- // It looks up the account specified either solely via its address contained within,
- // or optionally with the aid of any location metadata from the embedded URL field.
- SignTextWithPassphrase(account Account, passphrase string, hash []byte) ([]byte, error)
-
- // SignTxWithPassphrase requests the wallet to sign the given transaction, with the
- // given passphrase as extra authentication information.
- //
- // It looks up the account specified either solely via its address contained within,
- // or optionally with the aid of any location metadata from the embedded URL field.
+ // SignTxWithPassphrase is identical to SignTx, but also takes a password
SignTxWithPassphrase(account Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error)
}
@@ -170,9 +175,22 @@ type Backend interface {
//
// This gives context to the signed message and prevents signing of transactions.
func TextHash(data []byte) []byte {
- hash := sha3.NewLegacyKeccak256()
- fmt.Fprintf(hash, "\x19Ethereum Signed Message:\n%d%s", len(data), data)
- return hash.Sum(nil)
+ hash, _ := TextAndHash(data)
+ return hash
+}
+
+// TextAndHash is a helper function that calculates a hash for the given message that can be
+// safely used to calculate a signature from.
+//
+// The hash is calulcated as
+// keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
+//
+// This gives context to the signed message and prevents signing of transactions.
+func TextAndHash(data []byte) ([]byte, string) {
+ msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), string(data))
+ hasher := sha3.NewLegacyKeccak256()
+ hasher.Write([]byte(msg))
+ return hasher.Sum(nil), msg
}
// WalletEventType represents the different event types that can be fired by
diff --git a/accounts/external/backend.go b/accounts/external/backend.go
index 35b9c276d..3b8d50f1b 100644
--- a/accounts/external/backend.go
+++ b/accounts/external/backend.go
@@ -184,11 +184,14 @@ func (api *ExternalSigner) SignTx(account accounts.Account, tx *types.Transactio
}
func (api *ExternalSigner) SignTextWithPassphrase(account accounts.Account, passphrase string, text []byte) ([]byte, error) {
- return []byte{}, fmt.Errorf("operation not supported on external signers")
+ return []byte{}, fmt.Errorf("passphrase-operations not supported on external signers")
}
func (api *ExternalSigner) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
- return nil, fmt.Errorf("operation not supported on external signers")
+ return nil, fmt.Errorf("passphrase-operations not supported on external signers")
+}
+func (api *ExternalSigner) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
+ return nil, fmt.Errorf("passphrase-operations not supported on external signers")
}
func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
@@ -201,7 +204,7 @@ func (api *ExternalSigner) listAccounts() ([]common.Address, error) {
func (api *ExternalSigner) signCliqueBlock(a common.Address, rlpBlock hexutil.Bytes) (hexutil.Bytes, error) {
var sig hexutil.Bytes
- if err := api.client.Call(&sig, "account_signData", "application/clique", a, rlpBlock); err != nil {
+ if err := api.client.Call(&sig, "account_signData", core.ApplicationClique.Mime, a, rlpBlock); err != nil {
return nil, err
}
if sig[64] != 27 && sig[64] != 28 {
diff --git a/accounts/keystore/wallet.go b/accounts/keystore/wallet.go
index 0490f39ff..632620ead 100644
--- a/accounts/keystore/wallet.go
+++ b/accounts/keystore/wallet.go
@@ -97,21 +97,18 @@ func (w *keystoreWallet) SignData(account accounts.Account, mimeType string, dat
return w.signHash(account, crypto.Keccak256(data))
}
-func (w *keystoreWallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
- return w.signHash(account, accounts.TextHash(text))
-}
-
-// SignTx implements accounts.Wallet, attempting to sign the given transaction
-// with the given account. If the wallet does not wrap this particular account,
-// an error is returned to avoid account leakage (even though in theory we may
-// be able to sign via our shared keystore backend).
-func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+// SignDataWithPassphrase signs keccak256(data). The mimetype parameter describes the type of data being signed
+func (w *keystoreWallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
// Make sure the requested account is contained within
if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
// Account seems valid, request the keystore to sign
- return w.keystore.SignTx(account, tx, chainID)
+ return w.keystore.SignHashWithPassphrase(account, passphrase, crypto.Keccak256(data))
+}
+
+func (w *keystoreWallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
+ return w.signHash(account, accounts.TextHash(text))
}
// SignHashWithPassphrase implements accounts.Wallet, attempting to sign the
@@ -125,6 +122,19 @@ func (w *keystoreWallet) SignTextWithPassphrase(account accounts.Account, passph
return w.keystore.SignHashWithPassphrase(account, passphrase, accounts.TextHash(text))
}
+// SignTx implements accounts.Wallet, attempting to sign the given transaction
+// with the given account. If the wallet does not wrap this particular account,
+// an error is returned to avoid account leakage (even though in theory we may
+// be able to sign via our shared keystore backend).
+func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
+ // Make sure the requested account is contained within
+ if !w.Contains(account) {
+ return nil, accounts.ErrUnknownAccount
+ }
+ // Account seems valid, request the keystore to sign
+ return w.keystore.SignTx(account, tx, chainID)
+}
+
// SignTxWithPassphrase implements accounts.Wallet, attempting to sign the given
// transaction with the given account using passphrase as extra authentication.
func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
diff --git a/accounts/usbwallet/wallet.go b/accounts/usbwallet/wallet.go
index a99dcd0f5..feab505c9 100644
--- a/accounts/usbwallet/wallet.go
+++ b/accounts/usbwallet/wallet.go
@@ -507,6 +507,13 @@ func (w *wallet) SignData(account accounts.Account, mimeType string, data []byte
return w.signHash(account, crypto.Keccak256(data))
}
+// SignDataWithPassphrase implements accounts.Wallet, attempting to sign the given
+// data with the given account using passphrase as extra authentication.
+// Since USB wallets don't rely on passphrases, these are silently ignored.
+func (w *wallet) SignDataWithPassphrase(account accounts.Account, passphrase, mimeType string, data []byte) ([]byte, error) {
+ return w.SignData(account, mimeType, data)
+}
+
func (w *wallet) SignText(account accounts.Account, text []byte) ([]byte, error) {
return w.signHash(account, accounts.TextHash(text))
}