signed_data.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. // Copyright 2019 The go-ethereum Authors
  2. // This file is part of the go-ethereum library.
  3. //
  4. // The go-ethereum library is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Lesser General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // The go-ethereum library is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Lesser General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Lesser General Public License
  15. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
  16. package core
  17. import (
  18. "context"
  19. "errors"
  20. "fmt"
  21. "mime"
  22. "github.com/ethereum/go-ethereum/accounts"
  23. "github.com/ethereum/go-ethereum/common"
  24. "github.com/ethereum/go-ethereum/common/hexutil"
  25. "github.com/ethereum/go-ethereum/consensus/clique"
  26. "github.com/ethereum/go-ethereum/core/types"
  27. "github.com/ethereum/go-ethereum/crypto"
  28. "github.com/ethereum/go-ethereum/rlp"
  29. "github.com/ethereum/go-ethereum/signer/core/apitypes"
  30. )
  31. // sign receives a request and produces a signature
  32. //
  33. // Note, the produced signature conforms to the secp256k1 curve R, S and V values,
  34. // where the V value will be 27 or 28 for legacy reasons, if legacyV==true.
  35. func (api *SignerAPI) sign(req *SignDataRequest, legacyV bool) (hexutil.Bytes, error) {
  36. // We make the request prior to looking up if we actually have the account, to prevent
  37. // account-enumeration via the API
  38. res, err := api.UI.ApproveSignData(req)
  39. if err != nil {
  40. return nil, err
  41. }
  42. if !res.Approved {
  43. return nil, ErrRequestDenied
  44. }
  45. // Look up the wallet containing the requested signer
  46. account := accounts.Account{Address: req.Address.Address()}
  47. wallet, err := api.am.Find(account)
  48. if err != nil {
  49. return nil, err
  50. }
  51. pw, err := api.lookupOrQueryPassword(account.Address,
  52. "Password for signing",
  53. fmt.Sprintf("Please enter password for signing data with account %s", account.Address.Hex()))
  54. if err != nil {
  55. return nil, err
  56. }
  57. // Sign the data with the wallet
  58. signature, err := wallet.SignDataWithPassphrase(account, pw, req.ContentType, req.Rawdata)
  59. if err != nil {
  60. return nil, err
  61. }
  62. if legacyV {
  63. signature[64] += 27 // Transform V from 0/1 to 27/28 according to the yellow paper
  64. }
  65. return signature, nil
  66. }
  67. // SignData signs the hash of the provided data, but does so differently
  68. // depending on the content-type specified.
  69. //
  70. // Different types of validation occur.
  71. func (api *SignerAPI) SignData(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (hexutil.Bytes, error) {
  72. var req, transformV, err = api.determineSignatureFormat(ctx, contentType, addr, data)
  73. if err != nil {
  74. return nil, err
  75. }
  76. signature, err := api.sign(req, transformV)
  77. if err != nil {
  78. api.UI.ShowError(err.Error())
  79. return nil, err
  80. }
  81. return signature, nil
  82. }
  83. // determineSignatureFormat determines which signature method should be used based upon the mime type
  84. // In the cases where it matters ensure that the charset is handled. The charset
  85. // resides in the 'params' returned as the second returnvalue from mime.ParseMediaType
  86. // charset, ok := params["charset"]
  87. // As it is now, we accept any charset and just treat it as 'raw'.
  88. // This method returns the mimetype for signing along with the request
  89. func (api *SignerAPI) determineSignatureFormat(ctx context.Context, contentType string, addr common.MixedcaseAddress, data interface{}) (*SignDataRequest, bool, error) {
  90. var (
  91. req *SignDataRequest
  92. useEthereumV = true // Default to use V = 27 or 28, the legacy Ethereum format
  93. )
  94. mediaType, _, err := mime.ParseMediaType(contentType)
  95. if err != nil {
  96. return nil, useEthereumV, err
  97. }
  98. switch mediaType {
  99. case apitypes.IntendedValidator.Mime:
  100. // Data with an intended validator
  101. validatorData, err := UnmarshalValidatorData(data)
  102. if err != nil {
  103. return nil, useEthereumV, err
  104. }
  105. sighash, msg := SignTextValidator(validatorData)
  106. messages := []*apitypes.NameValueType{
  107. {
  108. Name: "This is a request to sign data intended for a particular validator (see EIP 191 version 0)",
  109. Typ: "description",
  110. Value: "",
  111. },
  112. {
  113. Name: "Intended validator address",
  114. Typ: "address",
  115. Value: validatorData.Address.String(),
  116. },
  117. {
  118. Name: "Application-specific data",
  119. Typ: "hexdata",
  120. Value: validatorData.Message,
  121. },
  122. {
  123. Name: "Full message for signing",
  124. Typ: "hexdata",
  125. Value: fmt.Sprintf("%#x", msg),
  126. },
  127. }
  128. req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash}
  129. case apitypes.ApplicationClique.Mime:
  130. // Clique is the Ethereum PoA standard
  131. stringData, ok := data.(string)
  132. if !ok {
  133. return nil, useEthereumV, fmt.Errorf("input for %v must be an hex-encoded string", apitypes.ApplicationClique.Mime)
  134. }
  135. cliqueData, err := hexutil.Decode(stringData)
  136. if err != nil {
  137. return nil, useEthereumV, err
  138. }
  139. header := &types.Header{}
  140. if err := rlp.DecodeBytes(cliqueData, header); err != nil {
  141. return nil, useEthereumV, err
  142. }
  143. // Add space in the extradata to put the signature
  144. newExtra := make([]byte, len(header.Extra)+65)
  145. copy(newExtra, header.Extra)
  146. header.Extra = newExtra
  147. // Get back the rlp data, encoded by us
  148. sighash, cliqueRlp, err := cliqueHeaderHashAndRlp(header)
  149. if err != nil {
  150. return nil, useEthereumV, err
  151. }
  152. messages := []*apitypes.NameValueType{
  153. {
  154. Name: "Clique header",
  155. Typ: "clique",
  156. Value: fmt.Sprintf("clique header %d [%#x]", header.Number, header.Hash()),
  157. },
  158. }
  159. // Clique uses V on the form 0 or 1
  160. useEthereumV = false
  161. req = &SignDataRequest{ContentType: mediaType, Rawdata: cliqueRlp, Messages: messages, Hash: sighash}
  162. default: // also case TextPlain.Mime:
  163. // Calculates an Ethereum ECDSA signature for:
  164. // hash = keccak256("\x19Ethereum Signed Message:\n${message length}${message}")
  165. // We expect it to be a string
  166. if stringData, ok := data.(string); !ok {
  167. return nil, useEthereumV, fmt.Errorf("input for text/plain must be an hex-encoded string")
  168. } else {
  169. if textData, err := hexutil.Decode(stringData); err != nil {
  170. return nil, useEthereumV, err
  171. } else {
  172. sighash, msg := accounts.TextAndHash(textData)
  173. messages := []*apitypes.NameValueType{
  174. {
  175. Name: "message",
  176. Typ: accounts.MimetypeTextPlain,
  177. Value: msg,
  178. },
  179. }
  180. req = &SignDataRequest{ContentType: mediaType, Rawdata: []byte(msg), Messages: messages, Hash: sighash}
  181. }
  182. }
  183. }
  184. req.Address = addr
  185. req.Meta = MetadataFromContext(ctx)
  186. return req, useEthereumV, nil
  187. }
  188. // SignTextValidator signs the given message which can be further recovered
  189. // with the given validator.
  190. // hash = keccak256("\x19\x00"${address}${data}).
  191. func SignTextValidator(validatorData apitypes.ValidatorData) (hexutil.Bytes, string) {
  192. msg := fmt.Sprintf("\x19\x00%s%s", string(validatorData.Address.Bytes()), string(validatorData.Message))
  193. return crypto.Keccak256([]byte(msg)), msg
  194. }
  195. // cliqueHeaderHashAndRlp returns the hash which is used as input for the proof-of-authority
  196. // signing. It is the hash of the entire header apart from the 65 byte signature
  197. // contained at the end of the extra data.
  198. //
  199. // The method requires the extra data to be at least 65 bytes -- the original implementation
  200. // in clique.go panics if this is the case, thus it's been reimplemented here to avoid the panic
  201. // and simply return an error instead
  202. func cliqueHeaderHashAndRlp(header *types.Header) (hash, rlp []byte, err error) {
  203. if len(header.Extra) < 65 {
  204. err = fmt.Errorf("clique header extradata too short, %d < 65", len(header.Extra))
  205. return
  206. }
  207. rlp = clique.CliqueRLP(header)
  208. hash = clique.SealHash(header).Bytes()
  209. return hash, rlp, err
  210. }
  211. // SignTypedData signs EIP-712 conformant typed data
  212. // hash = keccak256("\x19${byteVersion}${domainSeparator}${hashStruct(message)}")
  213. // It returns
  214. // - the signature,
  215. // - and/or any error
  216. func (api *SignerAPI) SignTypedData(ctx context.Context, addr common.MixedcaseAddress, typedData apitypes.TypedData) (hexutil.Bytes, error) {
  217. signature, _, err := api.signTypedData(ctx, addr, typedData, nil)
  218. return signature, err
  219. }
  220. // signTypedData is identical to the capitalized version, except that it also returns the hash (preimage)
  221. // - the signature preimage (hash)
  222. func (api *SignerAPI) signTypedData(ctx context.Context, addr common.MixedcaseAddress,
  223. typedData apitypes.TypedData, validationMessages *apitypes.ValidationMessages) (hexutil.Bytes, hexutil.Bytes, error) {
  224. sighash, rawData, err := apitypes.TypedDataAndHash(typedData)
  225. if err != nil {
  226. return nil, nil, err
  227. }
  228. messages, err := typedData.Format()
  229. if err != nil {
  230. return nil, nil, err
  231. }
  232. req := &SignDataRequest{
  233. ContentType: apitypes.DataTyped.Mime,
  234. Rawdata: []byte(rawData),
  235. Messages: messages,
  236. Hash: sighash,
  237. Address: addr}
  238. if validationMessages != nil {
  239. req.Callinfo = validationMessages.Messages
  240. }
  241. signature, err := api.sign(req, true)
  242. if err != nil {
  243. api.UI.ShowError(err.Error())
  244. return nil, nil, err
  245. }
  246. return signature, sighash, nil
  247. }
  248. // EcRecover recovers the address associated with the given sig.
  249. // Only compatible with `text/plain`
  250. func (api *SignerAPI) EcRecover(ctx context.Context, data hexutil.Bytes, sig hexutil.Bytes) (common.Address, error) {
  251. // Returns the address for the Account that was used to create the signature.
  252. //
  253. // Note, this function is compatible with eth_sign and personal_sign. As such it recovers
  254. // the address of:
  255. // hash = keccak256("\x19Ethereum Signed Message:\n${message length}${message}")
  256. // addr = ecrecover(hash, signature)
  257. //
  258. // Note, the signature must conform to the secp256k1 curve R, S and V values, where
  259. // the V value must be 27 or 28 for legacy reasons.
  260. //
  261. // https://github.com/ethereum/go-ethereum/wiki/Management-APIs#personal_ecRecover
  262. if len(sig) != 65 {
  263. return common.Address{}, fmt.Errorf("signature must be 65 bytes long")
  264. }
  265. if sig[64] != 27 && sig[64] != 28 {
  266. return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
  267. }
  268. sig[64] -= 27 // Transform yellow paper V from 27/28 to 0/1
  269. hash := accounts.TextHash(data)
  270. rpk, err := crypto.SigToPub(hash, sig)
  271. if err != nil {
  272. return common.Address{}, err
  273. }
  274. return crypto.PubkeyToAddress(*rpk), nil
  275. }
  276. // UnmarshalValidatorData converts the bytes input to typed data
  277. func UnmarshalValidatorData(data interface{}) (apitypes.ValidatorData, error) {
  278. raw, ok := data.(map[string]interface{})
  279. if !ok {
  280. return apitypes.ValidatorData{}, errors.New("validator input is not a map[string]interface{}")
  281. }
  282. addr, ok := raw["address"].(string)
  283. if !ok {
  284. return apitypes.ValidatorData{}, errors.New("validator address is not sent as a string")
  285. }
  286. addrBytes, err := hexutil.Decode(addr)
  287. if err != nil {
  288. return apitypes.ValidatorData{}, err
  289. }
  290. if !ok || len(addrBytes) == 0 {
  291. return apitypes.ValidatorData{}, errors.New("validator address is undefined")
  292. }
  293. message, ok := raw["message"].(string)
  294. if !ok {
  295. return apitypes.ValidatorData{}, errors.New("message is not sent as a string")
  296. }
  297. messageBytes, err := hexutil.Decode(message)
  298. if err != nil {
  299. return apitypes.ValidatorData{}, err
  300. }
  301. if !ok || len(messageBytes) == 0 {
  302. return apitypes.ValidatorData{}, errors.New("message is undefined")
  303. }
  304. return apitypes.ValidatorData{
  305. Address: common.BytesToAddress(addrBytes),
  306. Message: messageBytes,
  307. }, nil
  308. }