diff --git a/accounts/accounts.go b/accounts/accounts.go
index 11232b19a0e689157d5029f49ad400c451ab1947..b57f282b32d6859ad290cae5010fdfbaf4d32c35 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 35b9c276d179a7ba2facae8446e0fb62c02b1a1d..3b8d50f1b699021e4f410ffb0920f45e6b2141fe 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 0490f39ff5339acb356c0d3891061a31a360b5c8..632620ead97c0006f773f260dabcc141d6dbbf41 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 a99dcd0f5ad03443f1d99518f59e8943e78b57a6..feab505c9b47ed424d5ce8cb7a5e0341398746d6 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))
 }
diff --git a/cmd/clef/README.md b/cmd/clef/README.md
index c9461be1019436bc0364c1b310754abce1493520..2e1dac2998038b9e10edda0637db161ae2148bb7 100644
--- a/cmd/clef/README.md
+++ b/cmd/clef/README.md
@@ -189,7 +189,9 @@ None
   "method": "account_new",
   "params": []
 }
-
+```
+Response
+```
 {
   "id": 0,
   "jsonrpc": "2.0",
@@ -222,7 +224,9 @@ None
   "jsonrpc": "2.0",
   "method": "account_list"
 }
-
+```
+Response
+```
 {
   "id": 1,
   "jsonrpc": "2.0",
@@ -285,8 +289,8 @@ Response
 
 ```json
 {
+  "id": 2,
   "jsonrpc": "2.0",
-  "id": 67,
   "error": {
     "code": -32000,
     "message": "Request denied"
@@ -298,6 +302,7 @@ Response
 
 ```json
 {
+  "id": 67,
   "jsonrpc": "2.0",
   "method": "account_signTransaction",
   "params": [
@@ -311,8 +316,7 @@ Response
       "data": "0x4401a6e40000000000000000000000000000000000000000000000000000000000000012"
     },
     "safeSend(address)"
-  ],
-  "id": 67
+  ]
 }
 ```
 Response
@@ -346,15 +350,18 @@ Bash example:
 {"jsonrpc":"2.0","id":67,"result":{"raw":"0xf88380018203339407a565b7ed7d7a678680a4c162885bedbb695fe080a44401a6e4000000000000000000000000000000000000000000000000000000000000001226a0223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20ea02aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","tx":{"nonce":"0x0","gasPrice":"0x1","gas":"0x333","to":"0x07a565b7ed7d7a678680a4c162885bedbb695fe0","value":"0x0","input":"0x4401a6e40000000000000000000000000000000000000000000000000000000000000012","v":"0x26","r":"0x223a7c9bcf5531c99be5ea7082183816eb20cfe0bbc322e97cc5c7f71ab8b20e","s":"0x2aadee6b34b45bb15bc42d9c09de4a6754e7000908da72d48cc7704971491663","hash":"0xeba2df809e7a612a0a0d444ccfa5c839624bdc00dd29e3340d46df3870f8a30e"}}}
 ```
 
-
-### account_sign
+### account_signData
 
 #### Sign data
    Signs a chunk of data and returns the calculated signature.
 
 #### Arguments
+  - content type [string]: type of signed data
+     - `text/validator`: hex data with custom validator defined in a contract
+     - `application/clique`: [clique](https://github.com/ethereum/EIPs/issues/225) headers
+     - `text/plain`: simple hex data validated by `account_ecRecover`
   - account [address]: account to sign with
-  - data [data]: data to sign
+  - data [object]: data to sign
 
 #### Result
   - calculated signature [data]
@@ -364,8 +371,9 @@ Bash example:
 {
   "id": 3,
   "jsonrpc": "2.0",
-  "method": "account_sign",
+  "method": "account_signData",
   "params": [
+    "data/plain",
     "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db",
     "0xaabbccdd"
   ]
@@ -381,11 +389,109 @@ Response
 }
 ```
 
+### account_signTypedData
+
+#### Sign data
+   Signs a chunk of structured data conformant to [EIP712]([EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md)) and returns the calculated signature.
+
+#### Arguments
+  - account [address]: account to sign with
+  - data [object]: data to sign
+
+#### Result
+  - calculated signature [data]
+  
+#### Sample call
+```json
+{
+  "id": 68,
+  "jsonrpc": "2.0",
+  "method": "account_signTypedData",
+  "params": [
+    "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826",
+    {
+      "types": {
+        "EIP712Domain": [
+          {
+            "name": "name",
+            "type": "string"
+          },
+          {
+            "name": "version",
+            "type": "string"
+          },
+          {
+            "name": "chainId",
+            "type": "uint256"
+          },
+          {
+            "name": "verifyingContract",
+            "type": "address"
+          }
+        ],
+        "Person": [
+          {
+            "name": "name",
+            "type": "string"
+          },
+          {
+            "name": "wallet",
+            "type": "address"
+          }
+        ],
+        "Mail": [
+          {
+            "name": "from",
+            "type": "Person"
+          },
+          {
+            "name": "to",
+            "type": "Person"
+          },
+          {
+            "name": "contents",
+            "type": "string"
+          }
+        ]
+      },
+      "primaryType": "Mail",
+      "domain": {
+        "name": "Ether Mail",
+        "version": "1",
+        "chainId": 1,
+        "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+      },
+      "message": {
+        "from": {
+          "name": "Cow",
+          "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
+        },
+        "to": {
+          "name": "Bob",
+          "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
+        },
+        "contents": "Hello, Bob!"
+      }
+    }
+  ]
+}
+```
+Response
+
+```json
+{
+    "id": 1,
+    "jsonrpc": "2.0",
+    "result": "0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c"
+}
+```
+
 ### account_ecRecover
 
-#### Recover address
-   Derive the address from the account that was used to sign data from the data and signature.
-   
+#### Sign data
+
+Derive the address from the account that was used to sign data with content type `text/plain` and the signature.
+
 #### Arguments
   - data [data]: data that was signed
   - signature [data]: the signature to verify
@@ -400,6 +506,7 @@ Response
   "jsonrpc": "2.0",
   "method": "account_ecRecover",
   "params": [
+    "data/plain",
     "0xaabbccdd",
     "0x5b6693f153b48ec1c706ba4169960386dbaa6903e249cc79a8e6ddc434451d417e1e57327872c7f538beeb323c300afa9999a3d4a5de6caf3be0d5ef832b67ef1c"
   ]
@@ -413,7 +520,6 @@ Response
   "jsonrpc": "2.0",
   "result": "0x1923f626bb8dc025849e00f99c25fe2b2f7fb0db"
 }
-
 ```
 
 ### account_import
@@ -458,7 +564,7 @@ Response
       },
       "id": "09bccb61-b8d3-4e93-bf4f-205a8194f0b9",
       "version": 3
-    },
+    }
   ]
 }
 ```
diff --git a/cmd/clef/extapi_changelog.md b/cmd/clef/extapi_changelog.md
index 6c2c3e819430ab145f3d98fd05d3e373abd664a0..25f819bddb008b31ced6e4b8dafe5273a6706cbd 100644
--- a/cmd/clef/extapi_changelog.md
+++ b/cmd/clef/extapi_changelog.md
@@ -1,5 +1,15 @@
 ### Changelog for external API
 
+#### 5.0.0
+
+* The external `account_EcRecover`-method was reimplemented.
+* The external method `account_sign(address, data)` was replaced with `account_signData(contentType, address, data)`.
+The addition of `contentType` makes it possible to use the method for different types of objects, such as:
+  * signing data with an intended validator (not yet implemented)
+  * signing clique headers,
+  * signing plain personal messages,
+* The external method `account_signTypedData` implements [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) and makes it possible to sign typed data.
+  
 #### 4.0.0
 
 * The external `account_Ecrecover`-method was removed. 
diff --git a/cmd/clef/intapi_changelog.md b/cmd/clef/intapi_changelog.md
index 92a39a268fd3195dfc60abfef2ce802cb42f5ece..6388af73031ad770e1c02125422f2cebbf63ef02 100644
--- a/cmd/clef/intapi_changelog.md
+++ b/cmd/clef/intapi_changelog.md
@@ -1,5 +1,9 @@
 ### Changelog for internal API (ui-api)
 
+### 3.1.0
+
+* Add `ContentType string` to `SignDataRequest` to accommodate the latest EIP-191 and EIP-712 implementations.
+
 ### 3.0.0
 
 * Make use of `OnInputRequired(info UserInputRequest)` for obtaining master password during startup
diff --git a/cmd/clef/main.go b/cmd/clef/main.go
index e2b85288dd30328a0ec64e17414e9a0aae020045..38821102dc1e0e8e753f1f441aeb922d20ca6edd 100644
--- a/cmd/clef/main.go
+++ b/cmd/clef/main.go
@@ -28,6 +28,7 @@ import (
 	"fmt"
 	"io"
 	"io/ioutil"
+	"math/big"
 	"os"
 	"os/signal"
 	"os/user"
@@ -39,9 +40,11 @@ import (
 	"github.com/ethereum/go-ethereum/cmd/utils"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/console"
+	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/node"
+	"github.com/ethereum/go-ethereum/rlp"
 	"github.com/ethereum/go-ethereum/rpc"
 	"github.com/ethereum/go-ethereum/signer/core"
 	"github.com/ethereum/go-ethereum/signer/rules"
@@ -623,10 +626,40 @@ func testExternalUI(api *core.SignerAPI) {
 	}
 	var err error
 
+	cliqueHeader := types.Header{
+		common.HexToHash("0000H45H"),
+		common.HexToHash("0000H45H"),
+		common.HexToAddress("0000H45H"),
+		common.HexToHash("0000H00H"),
+		common.HexToHash("0000H45H"),
+		common.HexToHash("0000H45H"),
+		types.Bloom{},
+		big.NewInt(1337),
+		big.NewInt(1337),
+		1338,
+		1338,
+		big.NewInt(1338),
+		[]byte("Extra data Extra data Extra data  Extra data  Extra data  Extra data  Extra data Extra data"),
+		common.HexToHash("0x0000H45H"),
+		types.BlockNonce{},
+	}
+	cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader)
+	if err != nil {
+		utils.Fatalf("Should not error: %v", err)
+	}
+	addr, err := common.NewMixedcaseAddressFromString("0x0011223344556677889900112233445566778899")
+	if err != nil {
+		utils.Fatalf("Should not error: %v", err)
+	}
+	_, err = api.SignData(ctx, "application/clique", *addr, cliqueRlp)
+	checkErr("SignData", err)
+
 	_, err = api.SignTransaction(ctx, core.SendTxArgs{From: common.MixedcaseAddress{}}, nil)
 	checkErr("SignTransaction", err)
-	_, err = api.Sign(ctx, common.MixedcaseAddress{}, common.Hex2Bytes("01020304"))
-	checkErr("Sign", err)
+	_, err = api.SignData(ctx, "text/plain", common.MixedcaseAddress{}, common.Hex2Bytes("01020304"))
+	checkErr("SignData", err)
+	//_, err = api.SignTypedData(ctx, common.MixedcaseAddress{}, core.TypedData{})
+	//checkErr("SignTypedData", err)
 	_, err = api.List(ctx)
 	checkErr("List", err)
 	_, err = api.New(ctx)
@@ -646,7 +679,6 @@ func testExternalUI(api *core.SignerAPI) {
 	} else {
 		log.Info("No errors")
 	}
-
 }
 
 // getPassPhrase retrieves the password associated with clef, either fetched
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index c0f78ce65545f098774ca8cc542e27e610119232..967a843dec5963293b8f96df79a1bbf8c9b2b1c3 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -616,7 +616,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results c
 		log.Trace("Out-of-turn signing requested", "wiggle", common.PrettyDuration(wiggle))
 	}
 	// Sign all the things!
-	sighash, err := signFn(accounts.Account{Address: signer}, "application/x-clique-header", CliqueRLP(header))
+	sighash, err := signFn(accounts.Account{Address: signer}, accounts.MimetypeClique, CliqueRLP(header))
 	if err != nil {
 		return err
 	}
diff --git a/signer/core/abihelper.go b/signer/core/abihelper.go
index 0fef2493954919410beab38c77eb92b5a3dcc3fd..de6b815a68f242e84612a1f25289b4cc97fe80a7 100644
--- a/signer/core/abihelper.go
+++ b/signer/core/abihelper.go
@@ -17,17 +17,16 @@
 package core
 
 import (
+	"bytes"
 	"encoding/json"
 	"fmt"
 	"io/ioutil"
+	"os"
+	"regexp"
 	"strings"
 
 	"github.com/ethereum/go-ethereum/accounts/abi"
 	"github.com/ethereum/go-ethereum/common"
-
-	"bytes"
-	"os"
-	"regexp"
 )
 
 type decodedArgument struct {
diff --git a/signer/core/abihelper_test.go b/signer/core/abihelper_test.go
index 878210be1f21c5a4a705f866bf6fdd7d6d1bc1c7..4a3a2f06d29f472e7920d8817fd1332b1544a2df 100644
--- a/signer/core/abihelper_test.go
+++ b/signer/core/abihelper_test.go
@@ -18,12 +18,11 @@ package core
 
 import (
 	"fmt"
-	"strings"
-	"testing"
-
 	"io/ioutil"
 	"math/big"
 	"reflect"
+	"strings"
+	"testing"
 
 	"github.com/ethereum/go-ethereum/accounts/abi"
 	"github.com/ethereum/go-ethereum/common"
diff --git a/signer/core/api.go b/signer/core/api.go
index e112df9c75c1a550fefb4df08f60a8ae80f1ad8c..0521dce47bacc102540f2beaca6cfb061574190d 100644
--- a/signer/core/api.go
+++ b/signer/core/api.go
@@ -30,7 +30,6 @@ import (
 	"github.com/ethereum/go-ethereum/accounts/usbwallet"
 	"github.com/ethereum/go-ethereum/common"
 	"github.com/ethereum/go-ethereum/common/hexutil"
-	"github.com/ethereum/go-ethereum/crypto"
 	"github.com/ethereum/go-ethereum/internal/ethapi"
 	"github.com/ethereum/go-ethereum/log"
 	"github.com/ethereum/go-ethereum/rlp"
@@ -40,9 +39,9 @@ const (
 	// numberOfAccountsToDerive For hardware wallets, the number of accounts to derive
 	numberOfAccountsToDerive = 10
 	// ExternalAPIVersion -- see extapi_changelog.md
-	ExternalAPIVersion = "4.0.0"
+	ExternalAPIVersion = "5.0.0"
 	// InternalAPIVersion -- see intapi_changelog.md
-	InternalAPIVersion = "3.0.0"
+	InternalAPIVersion = "3.1.0"
 )
 
 // ExternalAPI defines the external API through which signing requests are made.
@@ -53,8 +52,12 @@ type ExternalAPI interface {
 	New(ctx context.Context) (accounts.Account, error)
 	// SignTransaction request to sign the specified transaction
 	SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error)
-	// Sign - request to sign the given data (plus prefix)
-	Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error)
+	// SignData - request to sign the given data (plus prefix)
+	SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error)
+	// SignTypedData - request to sign the given structured data (plus prefix)
+	SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data TypedData) (hexutil.Bytes, error)
+	// EcRecover - recover public key from given message and signature
+	EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error)
 	// Export - request to export an account
 	Export(ctx context.Context, addr common.Address) (json.RawMessage, error)
 	// Import - request to import an account
@@ -177,11 +180,12 @@ type (
 		NewPassword string `json:"new_password"`
 	}
 	SignDataRequest struct {
-		Address common.MixedcaseAddress `json:"address"`
-		Rawdata hexutil.Bytes           `json:"raw_data"`
-		Message string                  `json:"message"`
-		Hash    hexutil.Bytes           `json:"hash"`
-		Meta    Metadata                `json:"meta"`
+		ContentType string                  `json:"content_type"`
+		Address     common.MixedcaseAddress `json:"address"`
+		Rawdata     []byte                  `json:"raw_data"`
+		Message     []*NameValueType        `json:"message"`
+		Hash        hexutil.Bytes           `json:"hash"`
+		Meta        Metadata                `json:"meta"`
 	}
 	SignDataResponse struct {
 		Approved bool `json:"approved"`
@@ -517,56 +521,6 @@ func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, meth
 
 }
 
-// Sign calculates an Ethereum ECDSA signature for:
-// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message))
-//
-// Note, the produced signature conforms to the secp256k1 curve R, S and V values,
-// where the V value will be 27 or 28 for legacy reasons.
-//
-// The key used to calculate the signature is decrypted with the given password.
-//
-// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_sign
-func (api *SignerAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) {
-	sighash, msg := SignHash(data)
-	// We make the request prior to looking up if we actually have the account, to prevent
-	// account-enumeration via the API
-	req := &SignDataRequest{Address: addr, Rawdata: data, Message: msg, Hash: sighash, Meta: MetadataFromContext(ctx)}
-	res, err := api.UI.ApproveSignData(req)
-
-	if err != nil {
-		return nil, err
-	}
-	if !res.Approved {
-		return nil, ErrRequestDenied
-	}
-	// Look up the wallet containing the requested signer
-	account := accounts.Account{Address: addr.Address()}
-	wallet, err := api.am.Find(account)
-	if err != nil {
-		return nil, err
-	}
-	// Assemble sign the data with the wallet
-	signature, err := wallet.SignTextWithPassphrase(account, res.Password, data)
-	if err != nil {
-		api.UI.ShowError(err.Error())
-		return nil, err
-	}
-	signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
-	return signature, nil
-}
-
-// SignHash 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 calculated as
-//   keccak256("\x19Ethereum Signed Message:\n"${message length}${message}).
-//
-// This gives context to the signed message and prevents signing of transactions.
-func SignHash(data []byte) ([]byte, string) {
-	msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
-	return crypto.Keccak256([]byte(msg)), msg
-}
-
 // Export returns encrypted private key associated with the given address in web3 keystore format.
 func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) {
 	res, err := api.UI.ApproveExport(&ExportRequest{Address: addr, Meta: MetadataFromContext(ctx)})
diff --git a/signer/core/api_test.go b/signer/core/api_test.go
index 114470cf902b9303fb6edabc348b1b9d1630c4ce..0e0c545178fc340507cf998396ad37215a401a33 100644
--- a/signer/core/api_test.go
+++ b/signer/core/api_test.go
@@ -244,45 +244,6 @@ func TestNewAcc(t *testing.T) {
 	}
 }
 
-func TestSignData(t *testing.T) {
-	api, control := setup(t)
-	//Create two accounts
-	createAccount(control, api, t)
-	createAccount(control, api, t)
-	control <- "1"
-	list, err := api.List(context.Background())
-	if err != nil {
-		t.Fatal(err)
-	}
-	a := common.NewMixedcaseAddress(list[0])
-
-	control <- "Y"
-	control <- "wrongpassword"
-	h, err := api.Sign(context.Background(), a, []byte("EHLO world"))
-	if h != nil {
-		t.Errorf("Expected nil-data, got %x", h)
-	}
-	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 {
-		t.Errorf("Expected nil-data, got %x", h)
-	}
-	if err != ErrRequestDenied {
-		t.Errorf("Expected ErrRequestDenied! %v", err)
-	}
-	control <- "Y"
-	control <- "a_long_password"
-	h, err = api.Sign(context.Background(), a, []byte("EHLO world"))
-	if err != nil {
-		t.Fatal(err)
-	}
-	if h == nil || len(h) != 65 {
-		t.Errorf("Expected 65 byte signature (got %d bytes)", len(h))
-	}
-}
 func mkTestTx(from common.MixedcaseAddress) SendTxArgs {
 	to := common.NewMixedcaseAddress(common.HexToAddress("0x1337"))
 	gas := hexutil.Uint64(21000)
diff --git a/signer/core/auditlog.go b/signer/core/auditlog.go
index 0cb6c9c47327d8eb218f5808de0283035e8c324f..d64ba1ef95e578a6ae39ed43c38faf5a9fb82052 100644
--- a/signer/core/auditlog.go
+++ b/signer/core/auditlog.go
@@ -18,7 +18,6 @@ package core
 
 import (
 	"context"
-
 	"encoding/json"
 
 	"github.com/ethereum/go-ethereum/accounts"
@@ -63,11 +62,27 @@ func (l *AuditLogger) SignTransaction(ctx context.Context, args SendTxArgs, meth
 	return res, e
 }
 
-func (l *AuditLogger) Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) {
-	l.log.Info("Sign", "type", "request", "metadata", MetadataFromContext(ctx).String(),
-		"addr", addr.String(), "data", common.Bytes2Hex(data))
-	b, e := l.api.Sign(ctx, addr, data)
-	l.log.Info("Sign", "type", "response", "data", common.Bytes2Hex(b), "error", e)
+func (l *AuditLogger) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error) {
+	l.log.Info("SignData", "type", "request", "metadata", MetadataFromContext(ctx).String(),
+		"addr", addr.String(), "data", data, "content-type", contentType)
+	b, e := l.api.SignData(ctx, contentType, addr, data)
+	l.log.Info("SignData", "type", "response", "data", common.Bytes2Hex(b), "error", e)
+	return b, e
+}
+
+func (l *AuditLogger) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, data TypedData) (hexutil.Bytes, error) {
+	l.log.Info("SignTypedData", "type", "request", "metadata", MetadataFromContext(ctx).String(),
+		"addr", addr.String(), "data", data)
+	b, e := l.api.SignTypedData(ctx, addr, data)
+	l.log.Info("SignTypedData", "type", "response", "data", common.Bytes2Hex(b), "error", e)
+	return b, e
+}
+
+func (l *AuditLogger) EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) {
+	l.log.Info("EcRecover", "type", "request", "metadata", MetadataFromContext(ctx).String(),
+		"data", common.Bytes2Hex(data), "sig", common.Bytes2Hex(sig))
+	b, e := l.api.EcRecover(ctx, data, sig)
+	l.log.Info("EcRecover", "type", "response", "address", b.String(), "error", e)
 	return b, e
 }
 
diff --git a/signer/core/cliui.go b/signer/core/cliui.go
index 940f1f43aadb3b0858e180e27ab0c83646ffc634..71d489d45967245f5905e4866ad13c2e23d82eea 100644
--- a/signer/core/cliui.go
+++ b/signer/core/cliui.go
@@ -21,7 +21,6 @@ import (
 	"fmt"
 	"os"
 	"strings"
-
 	"sync"
 
 	"github.com/davecgh/go-spew/spew"
@@ -165,8 +164,12 @@ func (ui *CommandlineUI) ApproveSignData(request *SignDataRequest) (SignDataResp
 
 	fmt.Printf("-------- Sign data request--------------\n")
 	fmt.Printf("Account:  %s\n", request.Address.String())
-	fmt.Printf("message:  \n%q\n", request.Message)
-	fmt.Printf("raw data: \n%v\n", request.Rawdata)
+	fmt.Printf("message:\n")
+	for _, nvt := range request.Message {
+		fmt.Printf("%v\n", nvt.Pprint(1))
+	}
+	//fmt.Printf("message:  \n%v\n", request.Message)
+	fmt.Printf("raw data:  \n%q\n", request.Rawdata)
 	fmt.Printf("message hash:  %v\n", request.Hash)
 	fmt.Printf("-------------------------------------------\n")
 	showMetadata(request.Meta)
diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go
new file mode 100644
index 0000000000000000000000000000000000000000..ac0b97bcad8ebf4fbed7717473b1e0aed7dd84eb
--- /dev/null
+++ b/signer/core/signed_data.go
@@ -0,0 +1,899 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+//
+package core
+
+import (
+	"bytes"
+	"context"
+	"errors"
+	"fmt"
+	"math/big"
+	"mime"
+	"regexp"
+	"sort"
+	"strconv"
+	"strings"
+	"unicode"
+
+	"github.com/ethereum/go-ethereum/accounts"
+	"github.com/ethereum/go-ethereum/accounts/abi"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
+	"github.com/ethereum/go-ethereum/common/math"
+	"github.com/ethereum/go-ethereum/consensus/clique"
+	"github.com/ethereum/go-ethereum/core/types"
+	"github.com/ethereum/go-ethereum/crypto"
+	"github.com/ethereum/go-ethereum/rlp"
+)
+
+type SigFormat struct {
+	Mime        string
+	ByteVersion byte
+}
+
+var (
+	TextValidator = SigFormat{
+		accounts.MimetypeTextWithValidator,
+		0x00,
+	}
+	DataTyped = SigFormat{
+		accounts.MimetypeTypedData,
+		0x01,
+	}
+	ApplicationClique = SigFormat{
+		accounts.MimetypeClique,
+		0x02,
+	}
+	TextPlain = SigFormat{
+		accounts.MimetypeTextPlain,
+		0x45,
+	}
+)
+
+type ValidatorData struct {
+	Address common.Address
+	Message hexutil.Bytes
+}
+
+type TypedData struct {
+	Types       Types            `json:"types"`
+	PrimaryType string           `json:"primaryType"`
+	Domain      TypedDataDomain  `json:"domain"`
+	Message     TypedDataMessage `json:"message"`
+}
+
+type Type struct {
+	Name string `json:"name"`
+	Type string `json:"type"`
+}
+
+func (t *Type) isArray() bool {
+	return strings.HasSuffix(t.Type, "[]")
+}
+
+// typeName returns the canonical name of the type. If the type is 'Person[]', then
+// this method returns 'Person'
+func (t *Type) typeName() string {
+	if strings.HasSuffix(t.Type, "[]") {
+		return strings.TrimSuffix(t.Type, "[]")
+	}
+	return t.Type
+}
+
+func (t *Type) isReferenceType() bool {
+	// Reference types must have a leading uppercase characer
+	return unicode.IsUpper([]rune(t.Type)[0])
+}
+
+type Types map[string][]Type
+
+type TypePriority struct {
+	Type  string
+	Value uint
+}
+
+type TypedDataMessage = map[string]interface{}
+
+type TypedDataDomain struct {
+	Name              string   `json:"name"`
+	Version           string   `json:"version"`
+	ChainId           *big.Int `json:"chainId"`
+	VerifyingContract string   `json:"verifyingContract"`
+	Salt              string   `json:"salt"`
+}
+
+var typedDataReferenceTypeRegexp = regexp.MustCompile(`^[A-Z](\w*)(\[\])?$`)
+
+// sign receives a request and produces a signature
+
+// Note, the produced signature conforms to the secp256k1 curve R, S and V values,
+// where the V value will be 27 or 28 for legacy reasons.
+func (api *SignerAPI) sign(addr common.MixedcaseAddress, req *SignDataRequest) (hexutil.Bytes, error) {
+
+	// We make the request prior to looking up if we actually have the account, to prevent
+	// account-enumeration via the API
+	res, err := api.UI.ApproveSignData(req)
+	if err != nil {
+		return nil, err
+	}
+	if !res.Approved {
+		return nil, ErrRequestDenied
+	}
+	// Look up the wallet containing the requested signer
+	account := accounts.Account{Address: addr.Address()}
+	wallet, err := api.am.Find(account)
+	if err != nil {
+		return nil, err
+	}
+	// Sign the data with the wallet
+	signature, err := wallet.SignDataWithPassphrase(account, res.Password, req.ContentType, req.Hash)
+	if err != nil {
+		return nil, err
+	}
+	signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
+	return signature, nil
+}
+
+// SignData signs the hash of the provided data, but does so differently
+// depending on the content-type specified.
+//
+// Different types of validation occur.
+func (api *SignerAPI) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error) {
+	var req, err = api.determineSignatureFormat(ctx, contentType, addr, data)
+	if err != nil {
+		return nil, err
+	}
+
+	signature, err := api.sign(addr, req)
+	if err != nil {
+		api.UI.ShowError(err.Error())
+		return nil, err
+	}
+
+	return signature, nil
+}
+
+// determineSignatureFormat determines which signature method should be used based upon the mime type
+// In the cases where it matters ensure that the charset is handled. The charset
+// resides in the 'params' returned as the second returnvalue from mime.ParseMediaType
+// charset, ok := params["charset"]
+// As it is now, we accept any charset and just treat it as 'raw'.
+// This method returns the mimetype for signing along with the request
+func (api *SignerAPI) determineSignatureFormat(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (*SignDataRequest, error) {
+	var req *SignDataRequest
+
+	mediaType, _, err := mime.ParseMediaType(contentType)
+	if err != nil {
+		return nil, err
+	}
+
+	switch mediaType {
+	case TextValidator.Mime:
+		// Data with an intended validator
+		validatorData, err := UnmarshalValidatorData(data)
+		if err != nil {
+			return nil, err
+		}
+		sighash, msg := SignTextValidator(validatorData)
+		message := []*NameValueType{
+			{
+				Name:  "message",
+				Typ:   "text",
+				Value: msg,
+			},
+		}
+		req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Message: message, Hash: sighash}
+	case ApplicationClique.Mime:
+		// Clique is the Ethereum PoA standard
+		stringData, ok := data.(string)
+		if !ok {
+			return nil, fmt.Errorf("input for %v plain must be an hex-encoded string", ApplicationClique.Mime)
+		}
+		cliqueData, err := hexutil.Decode(stringData)
+		if err != nil {
+			return nil, err
+		}
+		header := &types.Header{}
+		if err := rlp.DecodeBytes(cliqueData, header); err != nil {
+			return nil, err
+		}
+		// Get back the rlp data, encoded by us
+		cliqueData = clique.CliqueRLP(header)
+		sighash, err := SignCliqueHeader(header)
+		if err != nil {
+			return nil, err
+		}
+		message := []*NameValueType{
+			{
+				Name:  "Clique block",
+				Typ:   "clique",
+				Value: fmt.Sprintf("clique block %d [0x%x]", header.Number, header.Hash()),
+			},
+		}
+		req = &SignDataRequest{ContentType: mediaType, Rawdata: cliqueData, Message: message, Hash: sighash}
+	default: // also case TextPlain.Mime:
+		// Calculates an Ethereum ECDSA signature for:
+		// hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}")
+		// We expect it to be a string
+		stringData, ok := data.(string)
+		if !ok {
+			return nil, fmt.Errorf("input for text/plain must be a string")
+		}
+		//plainData, err := hexutil.Decode(stringdata)
+		//if err != nil {
+		//	return nil, err
+		//}
+		sighash, msg := accounts.TextAndHash([]byte(stringData))
+		message := []*NameValueType{
+			{
+				Name:  "message",
+				Typ:   "text/plain",
+				Value: msg,
+			},
+		}
+		req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Message: message, Hash: sighash}
+	}
+	req.Address = addr
+	req.Meta = MetadataFromContext(ctx)
+	return req, nil
+
+}
+
+// SignTextWithValidator signs the given message which can be further recovered
+// with the given validator.
+// hash = keccak256("\x19\x00"${address}${data}).
+func SignTextValidator(validatorData ValidatorData) (hexutil.Bytes, string) {
+	msg := fmt.Sprintf("\x19\x00%s%s", string(validatorData.Address.Bytes()), string(validatorData.Message))
+	fmt.Printf("SignTextValidator:%s\n", msg)
+	return crypto.Keccak256([]byte(msg)), msg
+}
+
+// SignCliqueHeader returns the hash which is used as input for the proof-of-authority
+// signing. It is the hash of the entire header apart from the 65 byte signature
+// contained at the end of the extra data.
+//
+// The method requires the extra data to be at least 65 bytes -- the original implementation
+// in clique.go panics if this is the case, thus it's been reimplemented here to avoid the panic
+// and simply return an error instead
+func SignCliqueHeader(header *types.Header) (hexutil.Bytes, error) {
+	//hash := common.Hash{}
+	if len(header.Extra) < 65 {
+		return nil, fmt.Errorf("clique header extradata too short, %d < 65", len(header.Extra))
+	}
+	hash := clique.SealHash(header)
+	return hash.Bytes(), nil
+}
+
+// SignTypedData signs EIP-712 conformant typed data
+// hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}")
+func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData TypedData) (hexutil.Bytes, error) {
+	domainSeparator, err := typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
+	if err != nil {
+		return nil, err
+	}
+	typedDataHash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
+	if err != nil {
+		return nil, err
+	}
+	rawData := []byte(fmt.Sprintf("\x19\x01%s%s", string(domainSeparator), string(typedDataHash)))
+	sighash := crypto.Keccak256(rawData)
+	message := typedData.Format()
+	req := &SignDataRequest{ContentType: DataTyped.Mime, Rawdata: rawData, Message: message, Hash: sighash}
+	signature, err := api.sign(addr, req)
+	if err != nil {
+		api.UI.ShowError(err.Error())
+		return nil, err
+	}
+	return signature, nil
+}
+
+// HashStruct generates a keccak256 hash of the encoding of the provided data
+func (typedData *TypedData) HashStruct(primaryType string, data TypedDataMessage) (hexutil.Bytes, error) {
+	encodedData, err := typedData.EncodeData(primaryType, data, 1)
+	if err != nil {
+		return nil, err
+	}
+	return crypto.Keccak256(encodedData), nil
+}
+
+// Dependencies returns an array of custom types ordered by their hierarchical reference tree
+func (typedData *TypedData) Dependencies(primaryType string, found []string) []string {
+	includes := func(arr []string, str string) bool {
+		for _, obj := range arr {
+			if obj == str {
+				return true
+			}
+		}
+		return false
+	}
+
+	if includes(found, primaryType) {
+		return found
+	}
+	if typedData.Types[primaryType] == nil {
+		return found
+	}
+	found = append(found, primaryType)
+	for _, field := range typedData.Types[primaryType] {
+		for _, dep := range typedData.Dependencies(field.Type, found) {
+			if !includes(found, dep) {
+				found = append(found, dep)
+			}
+		}
+	}
+	return found
+}
+
+// EncodeType generates the following encoding:
+// `name ‖ "(" ‖ member₁ ‖ "," ‖ member₂ ‖ "," ‖ … ‖ memberₙ ")"`
+//
+// each member is written as `type ‖ " " ‖ name` encodings cascade down and are sorted by name
+func (typedData *TypedData) EncodeType(primaryType string) hexutil.Bytes {
+	// Get dependencies primary first, then alphabetical
+	deps := typedData.Dependencies(primaryType, []string{})
+	slicedDeps := deps[1:]
+	sort.Strings(slicedDeps)
+	deps = append([]string{primaryType}, slicedDeps...)
+
+	// Format as a string with fields
+	var buffer bytes.Buffer
+	for _, dep := range deps {
+		buffer.WriteString(dep)
+		buffer.WriteString("(")
+		for _, obj := range typedData.Types[dep] {
+			buffer.WriteString(obj.Type)
+			buffer.WriteString(" ")
+			buffer.WriteString(obj.Name)
+			buffer.WriteString(",")
+		}
+		buffer.Truncate(buffer.Len() - 1)
+		buffer.WriteString(")")
+	}
+	return buffer.Bytes()
+}
+
+// TypeHash creates the keccak256 hash  of the data
+func (typedData *TypedData) TypeHash(primaryType string) hexutil.Bytes {
+	return crypto.Keccak256(typedData.EncodeType(primaryType))
+}
+
+// EncodeData generates the following encoding:
+// `enc(value₁) ‖ enc(value₂) ‖ … ‖ enc(valueₙ)`
+//
+// each encoded member is 32-byte long
+func (typedData *TypedData) EncodeData(primaryType string, data map[string]interface{}, depth int) (hexutil.Bytes, error) {
+	if err := typedData.validate(); err != nil {
+		return nil, err
+	}
+
+	buffer := bytes.Buffer{}
+
+	// Verify extra data
+	if len(typedData.Types[primaryType]) < len(data) {
+		return nil, errors.New("there is extra data provided in the message")
+	}
+
+	// Add typehash
+	buffer.Write(typedData.TypeHash(primaryType))
+
+	// Add field contents. Structs and arrays have special handlers.
+	for _, field := range typedData.Types[primaryType] {
+		encType := field.Type
+		encValue := data[field.Name]
+		if encType[len(encType)-1:] == "]" {
+			arrayValue, ok := encValue.([]interface{})
+			if !ok {
+				return nil, dataMismatchError(encType, encValue)
+			}
+
+			arrayBuffer := bytes.Buffer{}
+			parsedType := strings.Split(encType, "[")[0]
+			for _, item := range arrayValue {
+				if typedData.Types[parsedType] != nil {
+					mapValue, ok := item.(map[string]interface{})
+					if !ok {
+						return nil, dataMismatchError(parsedType, item)
+					}
+					encodedData, err := typedData.EncodeData(parsedType, mapValue, depth+1)
+					if err != nil {
+						return nil, err
+					}
+					arrayBuffer.Write(encodedData)
+				} else {
+					bytesValue, err := typedData.EncodePrimitiveValue(parsedType, item, depth)
+					if err != nil {
+						return nil, err
+					}
+					arrayBuffer.Write(bytesValue)
+				}
+			}
+
+			buffer.Write(crypto.Keccak256(arrayBuffer.Bytes()))
+		} else if typedData.Types[field.Type] != nil {
+			mapValue, ok := encValue.(map[string]interface{})
+			if !ok {
+				return nil, dataMismatchError(encType, encValue)
+			}
+			encodedData, err := typedData.EncodeData(field.Type, mapValue, depth+1)
+			if err != nil {
+				return nil, err
+			}
+			buffer.Write(crypto.Keccak256(encodedData))
+		} else {
+			byteValue, err := typedData.EncodePrimitiveValue(encType, encValue, depth)
+			if err != nil {
+				return nil, err
+			}
+			buffer.Write(byteValue)
+		}
+	}
+	return buffer.Bytes(), nil
+}
+
+// EncodePrimitiveValue deals with the primitive values found
+// while searching through the typed data
+func (typedData *TypedData) EncodePrimitiveValue(encType string, encValue interface{}, depth int) ([]byte, error) {
+
+	switch encType {
+	case "address":
+		stringValue, ok := encValue.(string)
+		if !ok || !common.IsHexAddress(stringValue) {
+			return nil, dataMismatchError(encType, encValue)
+		}
+		retval := make([]byte, 32)
+		copy(retval[12:], common.HexToAddress(stringValue).Bytes())
+		return retval, nil
+	case "bool":
+		boolValue, ok := encValue.(bool)
+		if !ok {
+			return nil, dataMismatchError(encType, encValue)
+		}
+		if boolValue {
+			return math.PaddedBigBytes(common.Big1, 32), nil
+		}
+		return math.PaddedBigBytes(common.Big0, 32), nil
+	case "string":
+		strVal, ok := encValue.(string)
+		if !ok {
+			return nil, dataMismatchError(encType, encValue)
+		}
+		return crypto.Keccak256([]byte(strVal)), nil
+	case "bytes":
+		bytesValue, ok := encValue.([]byte)
+		if !ok {
+			return nil, dataMismatchError(encType, encValue)
+		}
+		return crypto.Keccak256(bytesValue), nil
+	}
+	if strings.HasPrefix(encType, "bytes") {
+		lengthStr := strings.TrimPrefix(encType, "bytes")
+		length, err := strconv.Atoi(lengthStr)
+		if err != nil {
+			return nil, fmt.Errorf("invalid size on bytes: %v", lengthStr)
+		}
+		if length < 0 || length > 32 {
+			return nil, fmt.Errorf("invalid size on bytes: %d", length)
+		}
+		if byteValue, ok := encValue.(hexutil.Bytes); !ok {
+			return nil, dataMismatchError(encType, encValue)
+		} else {
+			return math.PaddedBigBytes(new(big.Int).SetBytes(byteValue), 32), nil
+		}
+	}
+	if strings.HasPrefix(encType, "int") || strings.HasPrefix(encType, "uint") {
+		length := 0
+		if encType == "int" || encType == "uint" {
+			length = 256
+		} else {
+			lengthStr := ""
+			if strings.HasPrefix(encType, "uint") {
+				lengthStr = strings.TrimPrefix(encType, "uint")
+			} else {
+				lengthStr = strings.TrimPrefix(encType, "int")
+			}
+			atoiSize, err := strconv.Atoi(lengthStr)
+			if err != nil {
+				return nil, fmt.Errorf("invalid size on integer: %v", lengthStr)
+			}
+			length = atoiSize
+		}
+		bigIntValue, ok := encValue.(*big.Int)
+		if bigIntValue.BitLen() > length {
+			return nil, fmt.Errorf("integer larger than '%v'", encType)
+		}
+		if !ok {
+			return nil, dataMismatchError(encType, encValue)
+		}
+		return abi.U256(bigIntValue), nil
+	}
+	return nil, fmt.Errorf("unrecognized type '%s'", encType)
+
+}
+
+// dataMismatchError generates an error for a mismatch between
+// the provided type and data
+func dataMismatchError(encType string, encValue interface{}) error {
+	return fmt.Errorf("provided data '%v' doesn't match type '%s'", encValue, encType)
+}
+
+// EcRecover recovers the address associated with the given sig.
+// Only compatible with `text/plain`
+func (api *SignerAPI) EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) {
+	// Returns the address for the Account that was used to create the signature.
+	//
+	// Note, this function is compatible with eth_sign and personal_sign. As such it recovers
+	// the address of:
+	// hash = keccak256("\x19${byteVersion}Ethereum Signed Message:\n${message length}${message}")
+	// addr = ecrecover(hash, signature)
+	//
+	// Note, the signature must conform to the secp256k1 curve R, S and V values, where
+	// the V value must be be 27 or 28 for legacy reasons.
+	//
+	// https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover
+	if len(sig) != 65 {
+		return common.Address{}, fmt.Errorf("signature must be 65 bytes long")
+	}
+	if sig[64] != 27 && sig[64] != 28 {
+		return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
+	}
+	sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1
+	hash := accounts.TextHash(data)
+	rpk, err := crypto.SigToPub(hash, sig)
+	if err != nil {
+		return common.Address{}, err
+	}
+	return crypto.PubkeyToAddress(*rpk), nil
+}
+
+// UnmarshalValidatorData converts the bytes input to typed data
+func UnmarshalValidatorData(data interface{}) (ValidatorData, error) {
+	raw, ok := data.(map[string]interface{})
+	if !ok {
+		return ValidatorData{}, errors.New("validator input is not a map[string]interface{}")
+	}
+	addr, ok := raw["address"].(string)
+	if !ok {
+		return ValidatorData{}, errors.New("validator address is not sent as a string")
+	}
+	addrBytes, err := hexutil.Decode(addr)
+	if err != nil {
+		return ValidatorData{}, err
+	}
+	if !ok || len(addrBytes) == 0 {
+		return ValidatorData{}, errors.New("validator address is undefined")
+	}
+
+	message, ok := raw["message"].(string)
+	if !ok {
+		return ValidatorData{}, errors.New("message is not sent as a string")
+	}
+	messageBytes, err := hexutil.Decode(message)
+	if err != nil {
+		return ValidatorData{}, err
+	}
+	if !ok || len(messageBytes) == 0 {
+		return ValidatorData{}, errors.New("message is undefined")
+	}
+
+	return ValidatorData{
+		Address: common.BytesToAddress(addrBytes),
+		Message: messageBytes,
+	}, nil
+}
+
+// validate makes sure the types are sound
+func (typedData *TypedData) validate() error {
+	if err := typedData.Types.validate(); err != nil {
+		return err
+	}
+	if err := typedData.Domain.validate(); err != nil {
+		return err
+	}
+	return nil
+}
+
+// Map generates a map version of the typed data
+func (typedData *TypedData) Map() map[string]interface{} {
+	dataMap := map[string]interface{}{
+		"types":       typedData.Types,
+		"domain":      typedData.Domain.Map(),
+		"primaryType": typedData.PrimaryType,
+		"message":     typedData.Message,
+	}
+	return dataMap
+}
+
+// PrettyPrint generates a nice output to help the users
+// of clef present data in their apps
+func (typedData *TypedData) PrettyPrint() string {
+	output := bytes.Buffer{}
+	formatted := typedData.Format()
+	for _, item := range formatted {
+		output.WriteString(fmt.Sprintf("%v\n", item.Pprint(0)))
+	}
+	return output.String()
+}
+
+// Format returns a representation of typedData, which can be easily displayed by a user-interface
+// without in-depth knowledge about 712 rules
+func (typedData *TypedData) Format() []*NameValueType {
+	var nvts []*NameValueType
+	nvts = append(nvts, &NameValueType{
+		Name:  "EIP712Domain",
+		Value: typedData.formatData("EIP712Domain", typedData.Domain.Map()),
+		Typ:   "domain",
+	})
+	nvts = append(nvts, &NameValueType{
+		Name:  typedData.PrimaryType,
+		Value: typedData.formatData(typedData.PrimaryType, typedData.Message),
+		Typ:   "primary type",
+	})
+	return nvts
+}
+
+func (typedData *TypedData) formatData(primaryType string, data map[string]interface{}) []*NameValueType {
+	var output []*NameValueType
+
+	// Add field contents. Structs and arrays have special handlers.
+	for _, field := range typedData.Types[primaryType] {
+		encName := field.Name
+		encValue := data[encName]
+		item := &NameValueType{
+			Name: encName,
+			Typ:  field.Type,
+		}
+		if field.isArray() {
+			arrayValue, _ := encValue.([]interface{})
+			parsedType := field.typeName()
+			for _, v := range arrayValue {
+				if typedData.Types[parsedType] != nil {
+					mapValue, _ := v.(map[string]interface{})
+					mapOutput := typedData.formatData(parsedType, mapValue)
+					item.Value = mapOutput
+				} else {
+					primitiveOutput := formatPrimitiveValue(field.Type, encValue)
+					item.Value = primitiveOutput
+				}
+			}
+		} else if typedData.Types[field.Type] != nil {
+			mapValue, _ := encValue.(map[string]interface{})
+			mapOutput := typedData.formatData(field.Type, mapValue)
+			item.Value = mapOutput
+		} else {
+			primitiveOutput := formatPrimitiveValue(field.Type, encValue)
+			item.Value = primitiveOutput
+		}
+		output = append(output, item)
+	}
+	return output
+}
+
+func formatPrimitiveValue(encType string, encValue interface{}) string {
+	switch encType {
+	case "address":
+		stringValue, _ := encValue.(string)
+		return common.HexToAddress(stringValue).String()
+	case "bool":
+		boolValue, _ := encValue.(bool)
+		return fmt.Sprintf("%t", boolValue)
+	case "bytes", "string":
+		return fmt.Sprintf("%s", encValue)
+	}
+	if strings.HasPrefix(encType, "bytes") {
+		return fmt.Sprintf("%s", encValue)
+	} else if strings.HasPrefix(encType, "uint") || strings.HasPrefix(encType, "int") {
+		bigIntValue, _ := encValue.(*big.Int)
+		return fmt.Sprintf("%d (0x%x)", bigIntValue, bigIntValue)
+	}
+	return "NA"
+}
+
+// NameValueType is a very simple struct with Name, Value and Type. It's meant for simple
+// json structures used to communicate signing-info about typed data with the UI
+type NameValueType struct {
+	Name  string      `json:"name"`
+	Value interface{} `json:"value"`
+	Typ   string      `json:"type"`
+}
+
+// Pprint returns a pretty-printed version of nvt
+func (nvt *NameValueType) Pprint(depth int) string {
+	output := bytes.Buffer{}
+	output.WriteString(strings.Repeat("\u00a0", depth*2))
+	output.WriteString(fmt.Sprintf("%s [%s]: ", nvt.Name, nvt.Typ))
+	if nvts, ok := nvt.Value.([]*NameValueType); ok {
+		output.WriteString("\n")
+		for _, next := range nvts {
+			sublevel := next.Pprint(depth + 1)
+			output.WriteString(sublevel)
+		}
+	} else {
+		output.WriteString(fmt.Sprintf("%s\n", nvt.Value))
+	}
+	return output.String()
+}
+
+// Validate checks if the types object is conformant to the specs
+func (t Types) validate() error {
+	for typeKey, typeArr := range t {
+		for _, typeObj := range typeArr {
+			if typeKey == typeObj.Type {
+				return fmt.Errorf("type '%s' cannot reference itself", typeObj.Type)
+			}
+			if typeObj.isReferenceType() {
+				if _, exist := t[typeObj.Type]; !exist {
+					return fmt.Errorf("reference type '%s' is undefined", typeObj.Type)
+				}
+				if !typedDataReferenceTypeRegexp.MatchString(typeObj.Type) {
+					return fmt.Errorf("unknown reference type '%s", typeObj.Type)
+				}
+			} else if !isPrimitiveTypeValid(typeObj.Type) {
+				return fmt.Errorf("unknown type '%s'", typeObj.Type)
+			}
+		}
+	}
+	return nil
+}
+
+// Checks if the primitive value is valid
+func isPrimitiveTypeValid(primitiveType string) bool {
+	if primitiveType == "address" ||
+		primitiveType == "address[]" ||
+		primitiveType == "bool" ||
+		primitiveType == "bool[]" ||
+		primitiveType == "string" ||
+		primitiveType == "string[]" {
+		return true
+	}
+	if primitiveType == "bytes" ||
+		primitiveType == "bytes[]" ||
+		primitiveType == "bytes1" ||
+		primitiveType == "bytes1[]" ||
+		primitiveType == "bytes2" ||
+		primitiveType == "bytes2[]" ||
+		primitiveType == "bytes3" ||
+		primitiveType == "bytes3[]" ||
+		primitiveType == "bytes4" ||
+		primitiveType == "bytes4[]" ||
+		primitiveType == "bytes5" ||
+		primitiveType == "bytes5[]" ||
+		primitiveType == "bytes6" ||
+		primitiveType == "bytes6[]" ||
+		primitiveType == "bytes7" ||
+		primitiveType == "bytes7[]" ||
+		primitiveType == "bytes8" ||
+		primitiveType == "bytes8[]" ||
+		primitiveType == "bytes9" ||
+		primitiveType == "bytes9[]" ||
+		primitiveType == "bytes10" ||
+		primitiveType == "bytes10[]" ||
+		primitiveType == "bytes11" ||
+		primitiveType == "bytes11[]" ||
+		primitiveType == "bytes12" ||
+		primitiveType == "bytes12[]" ||
+		primitiveType == "bytes13" ||
+		primitiveType == "bytes13[]" ||
+		primitiveType == "bytes14" ||
+		primitiveType == "bytes14[]" ||
+		primitiveType == "bytes15" ||
+		primitiveType == "bytes15[]" ||
+		primitiveType == "bytes16" ||
+		primitiveType == "bytes16[]" ||
+		primitiveType == "bytes17" ||
+		primitiveType == "bytes17[]" ||
+		primitiveType == "bytes18" ||
+		primitiveType == "bytes18[]" ||
+		primitiveType == "bytes19" ||
+		primitiveType == "bytes19[]" ||
+		primitiveType == "bytes20" ||
+		primitiveType == "bytes20[]" ||
+		primitiveType == "bytes21" ||
+		primitiveType == "bytes21[]" ||
+		primitiveType == "bytes22" ||
+		primitiveType == "bytes22[]" ||
+		primitiveType == "bytes23" ||
+		primitiveType == "bytes23[]" ||
+		primitiveType == "bytes24" ||
+		primitiveType == "bytes24[]" ||
+		primitiveType == "bytes25" ||
+		primitiveType == "bytes25[]" ||
+		primitiveType == "bytes26" ||
+		primitiveType == "bytes26[]" ||
+		primitiveType == "bytes27" ||
+		primitiveType == "bytes27[]" ||
+		primitiveType == "bytes28" ||
+		primitiveType == "bytes28[]" ||
+		primitiveType == "bytes29" ||
+		primitiveType == "bytes29[]" ||
+		primitiveType == "bytes30" ||
+		primitiveType == "bytes30[]" ||
+		primitiveType == "bytes31" ||
+		primitiveType == "bytes31[]" {
+		return true
+	}
+	if primitiveType == "int" ||
+		primitiveType == "int[]" ||
+		primitiveType == "int8" ||
+		primitiveType == "int8[]" ||
+		primitiveType == "int16" ||
+		primitiveType == "int16[]" ||
+		primitiveType == "int32" ||
+		primitiveType == "int32[]" ||
+		primitiveType == "int64" ||
+		primitiveType == "int64[]" ||
+		primitiveType == "int128" ||
+		primitiveType == "int128[]" ||
+		primitiveType == "int256" ||
+		primitiveType == "int256[]" {
+		return true
+	}
+	if primitiveType == "uint" ||
+		primitiveType == "uint[]" ||
+		primitiveType == "uint8" ||
+		primitiveType == "uint8[]" ||
+		primitiveType == "uint16" ||
+		primitiveType == "uint16[]" ||
+		primitiveType == "uint32" ||
+		primitiveType == "uint32[]" ||
+		primitiveType == "uint64" ||
+		primitiveType == "uint64[]" ||
+		primitiveType == "uint128" ||
+		primitiveType == "uint128[]" ||
+		primitiveType == "uint256" ||
+		primitiveType == "uint256[]" {
+		return true
+	}
+	return false
+}
+
+// validate checks if the given domain is valid, i.e. contains at least
+// the minimum viable keys and values
+func (domain *TypedDataDomain) validate() error {
+	if domain.ChainId == big.NewInt(0) {
+		return errors.New("chainId must be specified according to EIP-155")
+	}
+
+	if len(domain.Name) == 0 && len(domain.Version) == 0 && len(domain.VerifyingContract) == 0 && len(domain.Salt) == 0 {
+		return errors.New("domain is undefined")
+	}
+
+	return nil
+}
+
+// Map is a helper function to generate a map version of the domain
+func (domain *TypedDataDomain) Map() map[string]interface{} {
+	dataMap := map[string]interface{}{
+		"chainId": domain.ChainId,
+	}
+
+	if len(domain.Name) > 0 {
+		dataMap["name"] = domain.Name
+	}
+
+	if len(domain.Version) > 0 {
+		dataMap["version"] = domain.Version
+	}
+
+	if len(domain.VerifyingContract) > 0 {
+		dataMap["verifyingContract"] = domain.VerifyingContract
+	}
+
+	if len(domain.Salt) > 0 {
+		dataMap["salt"] = domain.Salt
+	}
+	return dataMap
+}
diff --git a/signer/core/signed_data_test.go b/signer/core/signed_data_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..7d44bce2ce728f529128b796d6a0e17f544362b8
--- /dev/null
+++ b/signer/core/signed_data_test.go
@@ -0,0 +1,774 @@
+// Copyright 2018 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+//
+package core
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"math/big"
+	"testing"
+
+	"github.com/ethereum/go-ethereum/accounts/keystore"
+	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var typesStandard = Types{
+	"EIP712Domain": {
+		{
+			Name: "name",
+			Type: "string",
+		},
+		{
+			Name: "version",
+			Type: "string",
+		},
+		{
+			Name: "chainId",
+			Type: "uint256",
+		},
+		{
+			Name: "verifyingContract",
+			Type: "address",
+		},
+	},
+	"Person": {
+		{
+			Name: "name",
+			Type: "string",
+		},
+		{
+			Name: "wallet",
+			Type: "address",
+		},
+	},
+	"Mail": {
+		{
+			Name: "from",
+			Type: "Person",
+		},
+		{
+			Name: "to",
+			Type: "Person",
+		},
+		{
+			Name: "contents",
+			Type: "string",
+		},
+	},
+}
+
+var jsonTypedData = `
+    {
+      "types": {
+        "EIP712Domain": [
+          {
+            "name": "name",
+            "type": "string"
+          },
+          {
+            "name": "version",
+            "type": "string"
+          },
+          {
+            "name": "chainId",
+            "type": "uint256"
+          },
+          {
+            "name": "verifyingContract",
+            "type": "address"
+          }
+        ],
+        "Person": [
+          {
+            "name": "name",
+            "type": "string"
+          },
+          {
+            "name": "test",
+            "type": "uint8"
+          },
+          {
+            "name": "wallet",
+            "type": "address"
+          }
+        ],
+        "Mail": [
+          {
+            "name": "from",
+            "type": "Person"
+          },
+          {
+            "name": "to",
+            "type": "Person"
+          },
+          {
+            "name": "contents",
+            "type": "string"
+          }
+        ]
+      },
+      "primaryType": "Mail",
+      "domain": {
+        "name": "Ether Mail",
+        "version": "1",
+        "chainId": 1,
+        "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+      },
+      "message": {
+        "from": {
+          "name": "Cow",
+		  "test": 3,
+          "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
+        },
+        "to": {
+          "name": "Bob",
+          "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
+        },
+        "contents": "Hello, Bob!"
+      }
+    }
+`
+
+const primaryType = "Mail"
+
+var domainStandard = TypedDataDomain{
+	"Ether Mail",
+	"1",
+	big.NewInt(1),
+	"0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC",
+	"",
+}
+
+var messageStandard = map[string]interface{}{
+	"from": map[string]interface{}{
+		"name":   "Cow",
+		"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826",
+	},
+	"to": map[string]interface{}{
+		"name":   "Bob",
+		"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB",
+	},
+	"contents": "Hello, Bob!",
+}
+
+var typedData = TypedData{
+	Types:       typesStandard,
+	PrimaryType: primaryType,
+	Domain:      domainStandard,
+	Message:     messageStandard,
+}
+
+func TestSignData(t *testing.T) {
+	api, control := setup(t)
+	//Create two accounts
+	createAccount(control, api, t)
+	createAccount(control, api, t)
+	control <- "1"
+	list, err := api.List(context.Background())
+	if err != nil {
+		t.Fatal(err)
+	}
+	a := common.NewMixedcaseAddress(list[0])
+
+	control <- "Y"
+	control <- "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)
+	}
+	if err != keystore.ErrDecrypt {
+		t.Errorf("Expected ErrLocked! '%v'", err)
+	}
+	control <- "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)
+	}
+	if err != ErrRequestDenied {
+		t.Errorf("Expected ErrRequestDenied! '%v'", err)
+	}
+	// text/plain
+	control <- "Y"
+	control <- "a_long_password"
+	signature, err = api.SignData(context.Background(), TextPlain.Mime, a, hexutil.Encode([]byte("EHLO world")))
+	if err != nil {
+		t.Fatal(err)
+	}
+	if signature == nil || len(signature) != 65 {
+		t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
+	}
+	// data/typed
+	control <- "Y"
+	control <- "a_long_password"
+	signature, err = api.SignTypedData(context.Background(), a, typedData)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if signature == nil || len(signature) != 65 {
+		t.Errorf("Expected 65 byte signature (got %d bytes)", len(signature))
+	}
+}
+
+func TestHashStruct(t *testing.T) {
+	hash, err := typedData.HashStruct(typedData.PrimaryType, typedData.Message)
+	if err != nil {
+		t.Fatal(err)
+	}
+	mainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
+	if mainHash != "0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e" {
+		t.Errorf("Expected different hashStruct result (got %s)", mainHash)
+	}
+
+	hash, err = typedData.HashStruct("EIP712Domain", typedData.Domain.Map())
+	if err != nil {
+		t.Error(err)
+	}
+	domainHash := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
+	if domainHash != "0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f" {
+		t.Errorf("Expected different domain hashStruct result (got %s)", domainHash)
+	}
+}
+
+func TestEncodeType(t *testing.T) {
+	domainTypeEncoding := string(typedData.EncodeType("EIP712Domain"))
+	if domainTypeEncoding != "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" {
+		t.Errorf("Expected different encodeType result (got %s)", domainTypeEncoding)
+	}
+
+	mailTypeEncoding := string(typedData.EncodeType(typedData.PrimaryType))
+	if mailTypeEncoding != "Mail(Person from,Person to,string contents)Person(string name,address wallet)" {
+		t.Errorf("Expected different encodeType result (got %s)", mailTypeEncoding)
+	}
+}
+
+func TestTypeHash(t *testing.T) {
+	mailTypeHash := fmt.Sprintf("0x%s", common.Bytes2Hex(typedData.TypeHash(typedData.PrimaryType)))
+	if mailTypeHash != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2" {
+		t.Errorf("Expected different typeHash result (got %s)", mailTypeHash)
+	}
+}
+
+func TestEncodeData(t *testing.T) {
+	hash, err := typedData.EncodeData(typedData.PrimaryType, typedData.Message, 0)
+	if err != nil {
+		t.Fatal(err)
+	}
+	dataEncoding := fmt.Sprintf("0x%s", common.Bytes2Hex(hash))
+	if dataEncoding != "0xa0cedeb2dc280ba39b857546d74f5549c3a1d7bdc2dd96bf881f76108e23dac2fc71e5fa27ff56c350aa531bc129ebdf613b772b6604664f5d8dbe21b85eb0c8cd54f074a4af31b4411ff6a60c9719dbd559c221c8ac3492d9d872b041d703d1b5aadf3154a261abdd9086fc627b61efca26ae5702701d05cd2305f7c52a2fc8" {
+		t.Errorf("Expected different encodeData result (got %s)", dataEncoding)
+	}
+}
+
+func TestMalformedDomainkeys(t *testing.T) {
+	// Verifies that malformed domain keys are properly caught:
+	//{
+	//	"name": "Ether Mail",
+	//	"version": "1",
+	//	"chainId": 1,
+	//	"vxerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+	//}
+	jsonTypedData := `
+    {
+      "types": {
+        "EIP712Domain": [
+          {
+            "name": "name",
+            "type": "string"
+          },
+          {
+            "name": "version",
+            "type": "string"
+          },
+          {
+            "name": "chainId",
+            "type": "uint256"
+          },
+          {
+            "name": "verifyingContract",
+            "type": "address"
+          }
+        ],
+        "Person": [
+          {
+            "name": "name",
+            "type": "string"
+          },
+          {
+            "name": "wallet",
+            "type": "address"
+          }
+        ],
+        "Mail": [
+          {
+            "name": "from",
+            "type": "Person"
+          },
+          {
+            "name": "to",
+            "type": "Person"
+          },
+          {
+            "name": "contents",
+            "type": "string"
+          }
+        ]
+      },
+      "primaryType": "Mail",
+      "domain": {
+        "name": "Ether Mail",
+        "version": "1",
+        "chainId": 1,
+        "vxerifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+      },
+      "message": {
+        "from": {
+          "name": "Cow",
+          "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
+        },
+        "to": {
+          "name": "Bob",
+          "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
+        },
+        "contents": "Hello, Bob!"
+      }
+    }
+`
+	var malformedDomainTypedData TypedData
+	err := json.Unmarshal([]byte(jsonTypedData), &malformedDomainTypedData)
+	if err != nil {
+		t.Fatalf("unmarshalling failed '%v'", err)
+	}
+	_, err = malformedDomainTypedData.HashStruct("EIP712Domain", malformedDomainTypedData.Domain.Map())
+	if err == nil || err.Error() != "provided data '<nil>' doesn't match type 'address'" {
+		t.Errorf("Expected `provided data '<nil>' doesn't match type 'address'`, got '%v'", err)
+	}
+}
+
+func TestMalformedTypesAndExtradata(t *testing.T) {
+	// Verifies several quirks
+	// 1. Using dynamic types and only validating the prefix:
+	//{
+	//	"name": "chainId",
+	//	"type": "uint256 ... and now for something completely different"
+	//}
+	// 2. Extra data in message:
+	//{
+	//  "blahonga": "zonk bonk"
+	//}
+	jsonTypedData := `
+    {
+      "types": {
+        "EIP712Domain": [
+          {
+            "name": "name",
+            "type": "string"
+          },
+          {
+            "name": "version",
+            "type": "string"
+          },
+          {
+            "name": "chainId",
+            "type": "uint256 ... and now for something completely different"
+          },
+          {
+            "name": "verifyingContract",
+            "type": "address"
+          }
+        ],
+        "Person": [
+          {
+            "name": "name",
+            "type": "string"
+          },
+          {
+            "name": "wallet",
+            "type": "address"
+          }
+        ],
+        "Mail": [
+          {
+            "name": "from",
+            "type": "Person"
+          },
+          {
+            "name": "to",
+            "type": "Person"
+          },
+          {
+            "name": "contents",
+            "type": "string"
+          }
+        ]
+      },
+      "primaryType": "Mail",
+      "domain": {
+        "name": "Ether Mail",
+        "version": "1",
+        "chainId": 1,
+        "verifyingContract": "0xCCCcccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+      },
+      "message": {
+        "from": {
+          "name": "Cow",
+          "wallet": "0xcD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
+        },
+        "to": {
+          "name": "Bob",
+          "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
+        },
+        "contents": "Hello, Bob!"
+      }
+    }
+`
+	var malformedTypedData TypedData
+	err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
+	if err != nil {
+		t.Fatalf("unmarshalling failed '%v'", err)
+	}
+
+	malformedTypedData.Types["EIP712Domain"][2].Type = "uint256"
+	malformedTypedData.Message["blahonga"] = "zonk bonk"
+	_, err = malformedTypedData.HashStruct(malformedTypedData.PrimaryType, malformedTypedData.Message)
+	if err == nil || err.Error() != "there is extra data provided in the message" {
+		t.Errorf("Expected `there is extra data provided in the message`, got '%v'", err)
+	}
+}
+
+func TestTypeMismatch(t *testing.T) {
+	// Verifies that:
+	// 1. Mismatches between the given type and data, i.e. `Person` and
+	// 		the data item is a string, are properly caught:
+	//{
+	//	"name": "contents",
+	//	"type": "Person"
+	//},
+	//{
+	//	"contents": "Hello, Bob!" <-- string not "Person"
+	//}
+	// 2. Nonexistent types are properly caught:
+	//{
+	//	"name": "contents",
+	//	"type": "Blahonga"
+	//}
+	jsonTypedData := `
+    {
+      "types": {
+        "EIP712Domain": [
+          {
+            "name": "name",
+            "type": "string"
+          },
+          {
+            "name": "version",
+            "type": "string"
+          },
+          {
+            "name": "chainId",
+            "type": "uint256"
+          },
+          {
+            "name": "verifyingContract",
+            "type": "address"
+          }
+        ],
+        "Person": [
+          {
+            "name": "name",
+            "type": "string"
+          },
+          {
+            "name": "wallet",
+            "type": "address"
+          }
+        ],
+        "Mail": [
+          {
+            "name": "from",
+            "type": "Person"
+          },
+          {
+            "name": "to",
+            "type": "Person"
+          },
+          {
+            "name": "contents",
+            "type": "Person"
+          }
+        ]
+      },
+      "primaryType": "Mail",
+      "domain": {
+        "name": "Ether Mail",
+        "version": "1",
+        "chainId": 1,
+        "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+      },
+      "message": {
+        "from": {
+          "name": "Cow",
+          "wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
+        },
+        "to": {
+          "name": "Bob",
+          "wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
+        },
+        "contents": "Hello, Bob!"
+      }
+    }
+`
+	var mismatchTypedData TypedData
+	err := json.Unmarshal([]byte(jsonTypedData), &mismatchTypedData)
+	if err != nil {
+		t.Fatalf("unmarshalling failed '%v'", err)
+	}
+	_, err = mismatchTypedData.HashStruct(mismatchTypedData.PrimaryType, mismatchTypedData.Message)
+	if err.Error() != "provided data 'Hello, Bob!' doesn't match type 'Person'" {
+		t.Errorf("Expected `provided data 'Hello, Bob!' doesn't match type 'Person'`, got '%v'", err)
+	}
+
+	mismatchTypedData.Types["Mail"][2].Type = "Blahonga"
+	_, err = mismatchTypedData.HashStruct(mismatchTypedData.PrimaryType, mismatchTypedData.Message)
+	if err == nil || err.Error() != "reference type 'Blahonga' is undefined" {
+		t.Fatalf("Expected `reference type 'Blahonga' is undefined`, got '%v'", err)
+	}
+}
+
+func TestTypeOverflow(t *testing.T) {
+	// Verifies data that doesn't fit into it:
+	//{
+	//	"test": 65536 <-- test defined as uint8
+	//}
+	var overflowTypedData TypedData
+	err := json.Unmarshal([]byte(jsonTypedData), &overflowTypedData)
+	if err != nil {
+		t.Fatalf("unmarshalling failed '%v'", err)
+	}
+	// Set test to something outside uint8
+	(overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(65536)
+
+	_, err = overflowTypedData.HashStruct(overflowTypedData.PrimaryType, overflowTypedData.Message)
+	if err == nil || err.Error() != "integer larger than 'uint8'" {
+		t.Fatalf("Expected `integer larger than 'uint8'`, got '%v'", err)
+	}
+
+	(overflowTypedData.Message["from"]).(map[string]interface{})["test"] = big.NewInt(3)
+	(overflowTypedData.Message["to"]).(map[string]interface{})["test"] = big.NewInt(4)
+
+	_, err = overflowTypedData.HashStruct(overflowTypedData.PrimaryType, overflowTypedData.Message)
+	if err != nil {
+		t.Fatalf("Expected no err, got '%v'", err)
+	}
+}
+
+func TestArray(t *testing.T) {
+	// Makes sure that arrays work fine
+	//{
+	//	"type": "address[]"
+	//},
+	//{
+	//	"type": "string[]"
+	//},
+	//{
+	//	"type": "uint16[]",
+	//}
+
+	jsonTypedData := `
+		{
+	      "types": {
+	        "EIP712Domain": [
+	          {
+	            "name": "name",
+	            "type": "string"
+	          },
+	          {
+	            "name": "version",
+	            "type": "string"
+	          },
+	          {
+	            "name": "chainId",
+	            "type": "uint256"
+	          },
+	          {
+	            "name": "verifyingContract",
+	            "type": "address"
+	          }
+	        ],
+	        "Foo": [
+	          {
+	            "name": "bar",
+	            "type": "address[]"
+	          }
+	        ]
+	      },
+	      "primaryType": "Foo",
+	      "domain": {
+	        "name": "Lorem",
+	        "version": "1",
+	        "chainId": 1,
+	        "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+	      },
+	      "message": {
+	        "bar": [
+	        	"0x0000000000000000000000000000000000000001",
+	        	"0x0000000000000000000000000000000000000002",
+	        	"0x0000000000000000000000000000000000000003"
+        	]
+	      }
+	    }
+	`
+	var arrayTypedData TypedData
+	err := json.Unmarshal([]byte(jsonTypedData), &arrayTypedData)
+	if err != nil {
+		t.Fatalf("unmarshalling failed '%v'", err)
+	}
+	_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
+	if err != nil {
+		t.Fatalf("Expected no err, got '%v'", err)
+	}
+
+	// Change array to string
+	arrayTypedData.Types["Foo"][0].Type = "string[]"
+	arrayTypedData.Message["bar"] = []interface{}{
+		"lorem",
+		"ipsum",
+		"dolores",
+	}
+	_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
+	if err != nil {
+		t.Fatalf("Expected no err, got '%v'", err)
+	}
+
+	// Change array to uint
+	arrayTypedData.Types["Foo"][0].Type = "uint[]"
+	arrayTypedData.Message["bar"] = []interface{}{
+		big.NewInt(1955),
+		big.NewInt(108),
+		big.NewInt(44010),
+	}
+	_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
+	if err != nil {
+		t.Fatalf("Expected no err, got '%v'", err)
+	}
+
+	// Should not work with fixed-size arrays
+	arrayTypedData.Types["Foo"][0].Type = "uint[3]"
+	_, err = arrayTypedData.HashStruct(arrayTypedData.PrimaryType, arrayTypedData.Message)
+	if err == nil || err.Error() != "unknown type 'uint[3]'" {
+		t.Fatalf("Expected `unknown type 'uint[3]'`, got '%v'", err)
+	}
+}
+
+func TestCustomTypeAsArray(t *testing.T) {
+	var jsonTypedData = `
+    {
+      "types": {
+        "EIP712Domain": [
+          {
+            "name": "name",
+            "type": "string"
+          },
+          {
+			"name": "version",
+            "type": "string"
+          },
+          {
+            "name": "chainId",
+            "type": "uint256"
+          },
+          {
+			"name": "verifyingContract",
+            "type": "address"
+          }
+        ],
+        "Person": [
+          {
+			"name": "name",
+            "type": "string"
+          },
+          {
+			"name": "wallet",
+            "type": "address"
+          }
+        ],
+        "Person[]": [
+          {
+			"name": "baz",
+            "type": "string"
+          }
+		],
+        "Mail": [
+          {
+			"name": "from",
+            "type": "Person"
+          },
+          {
+			"name": "to",
+            "type": "Person[]"
+          },
+          {
+			"name": "contents",
+            "type": "string"
+          }
+        ]
+      },
+      "primaryType": "Mail",
+      "domain": {
+		"name": "Ether Mail",
+        "version": "1",
+        "chainId": 1,
+        "verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
+      },
+      "message": {
+        "from": {
+			"name": "Cow",
+			"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
+        },
+        "to": {"baz": "foo"},
+        "contents": "Hello, Bob!"
+      }
+    }
+
+`
+	var malformedTypedData TypedData
+	err := json.Unmarshal([]byte(jsonTypedData), &malformedTypedData)
+	if err != nil {
+		t.Fatalf("unmarshalling failed '%v'", err)
+	}
+	_, err = malformedTypedData.HashStruct("EIP712Domain", malformedTypedData.Domain.Map())
+	if err != nil {
+		t.Errorf("Expected no error, got '%v'", err)
+	}
+}
+
+func TestFormatter(t *testing.T) {
+
+	var d TypedData
+	err := json.Unmarshal([]byte(jsonTypedData), &d)
+	if err != nil {
+		t.Fatalf("unmarshalling failed '%v'", err)
+	}
+	formatted := d.Format()
+	for _, item := range formatted {
+		fmt.Printf("'%v'\n", item.Pprint(0))
+	}
+
+	j, _ := json.Marshal(formatted)
+	fmt.Printf("'%v'\n", string(j))
+}
diff --git a/signer/core/types.go b/signer/core/types.go
index 128055774858b9ccc444759fc889bffc9587c97f..8acfa7a6afd47239b2bdb78d4c2b27462e5b19d4 100644
--- a/signer/core/types.go
+++ b/signer/core/types.go
@@ -19,9 +19,8 @@ package core
 import (
 	"encoding/json"
 	"fmt"
-	"strings"
-
 	"math/big"
+	"strings"
 
 	"github.com/ethereum/go-ethereum/accounts"
 	"github.com/ethereum/go-ethereum/common"
diff --git a/signer/rules/rules_test.go b/signer/rules/rules_test.go
index 0b520a15bfcd5679fa83a0c549d914550fe23bd1..d3b2edd5515f6dd430f4fdbd77d1e60a82046767 100644
--- a/signer/rules/rules_test.go
+++ b/signer/rules/rules_test.go
@@ -624,7 +624,7 @@ func TestSignData(t *testing.T) {
 function ApproveSignData(r){
     if( r.address.toLowerCase() == "0x694267f14675d7e1b9494fd8d72fefe1755710fa")
     {
-        if(r.message.indexOf("bazonk") >= 0){
+        if(r.message[0].value.indexOf("bazonk") >= 0){
             return "Approve"
         }
         return "Reject"
@@ -636,18 +636,25 @@ function ApproveSignData(r){
 		t.Errorf("Couldn't create evaluator %v", err)
 		return
 	}
-	message := []byte("baz bazonk foo")
-	hash, msg := core.SignHash(message)
-	raw := hexutil.Bytes(message)
+	message := "baz bazonk foo"
+	hash, rawdata := accounts.TextAndHash([]byte(message))
 	addr, _ := mixAddr("0x694267f14675d7e1b9494fd8d72fefe1755710fa")
 
 	fmt.Printf("address %v %v\n", addr.String(), addr.Original())
+
+	nvt := []*core.NameValueType{
+		{
+			Name:  "message",
+			Typ:   "text/plain",
+			Value: message,
+		},
+	}
 	resp, err := r.ApproveSignData(&core.SignDataRequest{
 		Address: *addr,
-		Message: msg,
+		Message: nvt,
 		Hash:    hash,
 		Meta:    core.Metadata{Remote: "remoteip", Local: "localip", Scheme: "inproc"},
-		Rawdata: raw,
+		Rawdata: []byte(rawdata),
 	})
 	if err != nil {
 		t.Fatalf("Unexpected error %v", err)