mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
Fix signing method bugs (#8833)
* update signTypedData validation * update tests for new eth-json-rpc-middleware * remove lowercasing of tx 'from' addresses
This commit is contained in:
parent
41c8e486af
commit
04de9a92c5
@ -75,6 +75,7 @@ export const SAFE_METHODS = [
|
|||||||
'eth_sendTransaction',
|
'eth_sendTransaction',
|
||||||
'eth_sign',
|
'eth_sign',
|
||||||
'personal_sign',
|
'personal_sign',
|
||||||
|
'personal_ecRecover',
|
||||||
'eth_signTypedData',
|
'eth_signTypedData',
|
||||||
'eth_signTypedData_v1',
|
'eth_signTypedData_v1',
|
||||||
'eth_signTypedData_v3',
|
'eth_signTypedData_v3',
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { addHexPrefix, isValidAddress } from 'ethereumjs-util'
|
import { addHexPrefix, isValidAddress } from 'ethereumjs-util'
|
||||||
|
|
||||||
const normalizers = {
|
const normalizers = {
|
||||||
from: (from, lowerCase = true) => (lowerCase ? addHexPrefix(from).toLowerCase() : addHexPrefix(from)),
|
from: (from) => addHexPrefix(from),
|
||||||
to: (to, lowerCase = true) => (lowerCase ? addHexPrefix(to).toLowerCase() : addHexPrefix(to)),
|
to: (to, lowerCase) => (lowerCase ? addHexPrefix(to).toLowerCase() : addHexPrefix(to)),
|
||||||
nonce: (nonce) => addHexPrefix(nonce),
|
nonce: (nonce) => addHexPrefix(nonce),
|
||||||
value: (value) => addHexPrefix(value),
|
value: (value) => addHexPrefix(value),
|
||||||
data: (data) => addHexPrefix(data),
|
data: (data) => addHexPrefix(data),
|
||||||
@ -12,11 +12,12 @@ const normalizers = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalizes the given txParams
|
* Normalizes the given txParams
|
||||||
* @param {Object} txParams - the tx params
|
* @param {Object} txParams - The transaction params
|
||||||
* @param {boolean} lowerCase - whether to return the addresses lower cased
|
* @param {boolean} [lowerCase] - Whether to lowercase the 'to' address.
|
||||||
|
* Default: true
|
||||||
* @returns {Object} the normalized tx params
|
* @returns {Object} the normalized tx params
|
||||||
*/
|
*/
|
||||||
export function normalizeTxParams (txParams, lowerCase) {
|
export function normalizeTxParams (txParams, lowerCase = true) {
|
||||||
// apply only keys in the normalizers
|
// apply only keys in the normalizers
|
||||||
const normalizedTxParams = {}
|
const normalizedTxParams = {}
|
||||||
for (const key in normalizers) {
|
for (const key in normalizers) {
|
||||||
|
@ -283,5 +283,4 @@ export default class EncryptionPublicKeyManager extends EventEmitter {
|
|||||||
this.memStore.updateState({ unapprovedEncryptionPublicKeyMsgs, unapprovedEncryptionPublicKeyMsgCount })
|
this.memStore.updateState({ unapprovedEncryptionPublicKeyMsgs, unapprovedEncryptionPublicKeyMsgCount })
|
||||||
this.emit('updateBadge')
|
this.emit('updateBadge')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,11 @@ import createId from './random-id'
|
|||||||
import assert from 'assert'
|
import assert from 'assert'
|
||||||
import { ethErrors } from 'eth-json-rpc-errors'
|
import { ethErrors } from 'eth-json-rpc-errors'
|
||||||
import sigUtil from 'eth-sig-util'
|
import sigUtil from 'eth-sig-util'
|
||||||
|
import { isValidAddress } from 'ethereumjs-util'
|
||||||
import log from 'loglevel'
|
import log from 'loglevel'
|
||||||
import jsonschema from 'jsonschema'
|
import jsonschema from 'jsonschema'
|
||||||
import { MESSAGE_TYPE } from './enums'
|
import { MESSAGE_TYPE } from './enums'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a
|
* Represents, and contains data about, an 'eth_signTypedData' type signature request. These are created when a
|
||||||
* signature for an eth_signTypedData call is requested.
|
* signature for an eth_signTypedData call is requested.
|
||||||
@ -102,14 +104,15 @@ export default class TypedMessageManager extends EventEmitter {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
addUnapprovedMessage (msgParams, req, version) {
|
addUnapprovedMessage (msgParams, req, version) {
|
||||||
|
|
||||||
msgParams.version = version
|
msgParams.version = version
|
||||||
this.validateParams(msgParams)
|
|
||||||
// add origin from request
|
|
||||||
if (req) {
|
if (req) {
|
||||||
msgParams.origin = req.origin
|
msgParams.origin = req.origin
|
||||||
}
|
}
|
||||||
|
this.validateParams(msgParams)
|
||||||
|
|
||||||
log.debug(`TypedMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`)
|
log.debug(`TypedMessageManager addUnapprovedMessage: ${JSON.stringify(msgParams)}`)
|
||||||
|
|
||||||
// create txData obj with parameters and meta data
|
// create txData obj with parameters and meta data
|
||||||
const time = (new Date()).getTime()
|
const time = (new Date()).getTime()
|
||||||
const msgId = createId()
|
const msgId = createId()
|
||||||
@ -134,37 +137,38 @@ export default class TypedMessageManager extends EventEmitter {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
validateParams (params) {
|
validateParams (params) {
|
||||||
|
|
||||||
|
assert.ok(params && typeof params === 'object', 'Params must be an object.')
|
||||||
|
assert.ok('data' in params, 'Params must include a "data" field.')
|
||||||
|
assert.ok('from' in params, 'Params must include a "from" field.')
|
||||||
|
assert.ok(
|
||||||
|
typeof params.from === 'string' && isValidAddress(params.from),
|
||||||
|
'"from" field must be a valid, lowercase, hexadecimal Ethereum address string.'
|
||||||
|
)
|
||||||
|
|
||||||
switch (params.version) {
|
switch (params.version) {
|
||||||
case 'V1':
|
case 'V1':
|
||||||
assert.equal(typeof params, 'object', 'Params should ben an object.')
|
assert.ok(Array.isArray(params.data), '"params.data" must be an array.')
|
||||||
assert.ok('data' in params, 'Params must include a data field.')
|
|
||||||
assert.ok('from' in params, 'Params must include a from field.')
|
|
||||||
assert.ok(Array.isArray(params.data), 'Data should be an array.')
|
|
||||||
assert.equal(typeof params.from, 'string', 'From field must be a string.')
|
|
||||||
assert.doesNotThrow(() => {
|
assert.doesNotThrow(() => {
|
||||||
sigUtil.typedSignatureHash(params.data)
|
sigUtil.typedSignatureHash(params.data)
|
||||||
}, 'Expected EIP712 typed data')
|
}, 'Signing data must be valid EIP-712 typed data.')
|
||||||
break
|
break
|
||||||
case 'V3':
|
case 'V3':
|
||||||
case 'V4':
|
case 'V4':
|
||||||
|
assert.equal(typeof params.data, 'string', '"params.data" must be a string.')
|
||||||
let data
|
let data
|
||||||
assert.equal(typeof params, 'object', 'Params should be an object.')
|
|
||||||
assert.ok('data' in params, 'Params must include a data field.')
|
|
||||||
assert.ok('from' in params, 'Params must include a from field.')
|
|
||||||
assert.equal(typeof params.from, 'string', 'From field must be a string.')
|
|
||||||
assert.equal(typeof params.data, 'string', 'Data must be passed as a valid JSON string.')
|
|
||||||
assert.doesNotThrow(() => {
|
assert.doesNotThrow(() => {
|
||||||
data = JSON.parse(params.data)
|
data = JSON.parse(params.data)
|
||||||
}, 'Data must be passed as a valid JSON string.')
|
}, '"data" must be a valid JSON string.')
|
||||||
const validation = jsonschema.validate(data, sigUtil.TYPED_MESSAGE_SCHEMA)
|
const validation = jsonschema.validate(data, sigUtil.TYPED_MESSAGE_SCHEMA)
|
||||||
assert.ok(data.primaryType in data.types, `Primary type of "${data.primaryType}" has no type definition.`)
|
assert.ok(data.primaryType in data.types, `Primary type of "${data.primaryType}" has no type definition.`)
|
||||||
assert.equal(validation.errors.length, 0, 'Data must conform to EIP-712 schema. See https://git.io/fNtcx.')
|
assert.equal(validation.errors.length, 0, 'Signing data must conform to EIP-712 schema. See https://git.io/fNtcx.')
|
||||||
const chainId = data.domain.chainId
|
const chainId = data.domain.chainId
|
||||||
const activeChainId = parseInt(this.networkController.getNetworkState())
|
const activeChainId = parseInt(this.networkController.getNetworkState())
|
||||||
chainId && assert.equal(chainId, activeChainId, `Provided chainId (${chainId}) must match the active chainId (${activeChainId})`)
|
chainId && assert.equal(chainId, activeChainId, `Provided chainId "${chainId}" must match the active chainId "${activeChainId}"`)
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
assert.fail(`Unknown params.version ${params.version}`)
|
assert.fail(`Unknown typed data version "${params.version}"`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@
|
|||||||
"eth-json-rpc-errors": "^2.0.2",
|
"eth-json-rpc-errors": "^2.0.2",
|
||||||
"eth-json-rpc-filters": "^4.1.1",
|
"eth-json-rpc-filters": "^4.1.1",
|
||||||
"eth-json-rpc-infura": "^4.0.2",
|
"eth-json-rpc-infura": "^4.0.2",
|
||||||
"eth-json-rpc-middleware": "^4.4.1",
|
"eth-json-rpc-middleware": "^5.0.0",
|
||||||
"eth-keyring-controller": "^6.0.0",
|
"eth-keyring-controller": "^6.0.0",
|
||||||
"eth-method-registry": "^1.2.0",
|
"eth-method-registry": "^1.2.0",
|
||||||
"eth-phishing-detect": "^1.1.4",
|
"eth-phishing-detect": "^1.1.4",
|
||||||
|
@ -35,9 +35,33 @@ const ExtensionizerMock = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let loggerMiddlewareMock
|
||||||
|
const initializeMockMiddlewareLog = () => {
|
||||||
|
loggerMiddlewareMock = {
|
||||||
|
requests: [],
|
||||||
|
responses: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const tearDownMockMiddlewareLog = () => {
|
||||||
|
loggerMiddlewareMock = undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const createLoggerMiddlewareMock = () => (req, res, next) => {
|
||||||
|
if (loggerMiddlewareMock) {
|
||||||
|
loggerMiddlewareMock.requests.push(req)
|
||||||
|
next((cb) => {
|
||||||
|
loggerMiddlewareMock.responses.push(res)
|
||||||
|
cb()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const MetaMaskController = proxyquire('../../../../app/scripts/metamask-controller', {
|
const MetaMaskController = proxyquire('../../../../app/scripts/metamask-controller', {
|
||||||
'./controllers/threebox': { default: ThreeBoxControllerMock },
|
'./controllers/threebox': { default: ThreeBoxControllerMock },
|
||||||
'extensionizer': ExtensionizerMock,
|
'extensionizer': ExtensionizerMock,
|
||||||
|
'./lib/createLoggerMiddleware': { default: createLoggerMiddlewareMock },
|
||||||
}).default
|
}).default
|
||||||
|
|
||||||
const currentNetworkId = 42
|
const currentNetworkId = 42
|
||||||
@ -96,7 +120,6 @@ describe('MetaMaskController', function () {
|
|||||||
// add sinon method spies
|
// add sinon method spies
|
||||||
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndKeychain')
|
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndKeychain')
|
||||||
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndRestore')
|
sandbox.spy(metamaskController.keyringController, 'createNewVaultAndRestore')
|
||||||
sandbox.spy(metamaskController.txController, 'newUnapprovedTransaction')
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
@ -776,6 +799,17 @@ describe('MetaMaskController', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('#setupUntrustedCommunication', function () {
|
describe('#setupUntrustedCommunication', function () {
|
||||||
|
|
||||||
|
const mockTxParams = { from: TEST_ADDRESS }
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
initializeMockMiddlewareLog()
|
||||||
|
})
|
||||||
|
|
||||||
|
after(function () {
|
||||||
|
tearDownMockMiddlewareLog()
|
||||||
|
})
|
||||||
|
|
||||||
it('sets up phishing stream for untrusted communication', async function () {
|
it('sets up phishing stream for untrusted communication', async function () {
|
||||||
const phishingMessageSender = {
|
const phishingMessageSender = {
|
||||||
url: 'http://myethereumwalletntw.com',
|
url: 'http://myethereumwalletntw.com',
|
||||||
@ -815,7 +849,7 @@ describe('MetaMaskController', function () {
|
|||||||
const message = {
|
const message = {
|
||||||
id: 1999133338649204,
|
id: 1999133338649204,
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
params: ['mock tx params'],
|
params: [{ ...mockTxParams }],
|
||||||
method: 'eth_sendTransaction',
|
method: 'eth_sendTransaction',
|
||||||
}
|
}
|
||||||
streamTest.write({
|
streamTest.write({
|
||||||
@ -824,15 +858,12 @@ describe('MetaMaskController', function () {
|
|||||||
}, null, () => {
|
}, null, () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
metamaskController.txController.newUnapprovedTransaction.getCall(0).args,
|
loggerMiddlewareMock.requests[0],
|
||||||
[
|
|
||||||
'mock tx params',
|
|
||||||
{
|
{
|
||||||
...message,
|
...message,
|
||||||
origin: 'http://mycrypto.com',
|
origin: 'http://mycrypto.com',
|
||||||
tabId: 456,
|
tabId: 456,
|
||||||
},
|
},
|
||||||
]
|
|
||||||
)
|
)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
@ -856,7 +887,7 @@ describe('MetaMaskController', function () {
|
|||||||
const message = {
|
const message = {
|
||||||
id: 1999133338649204,
|
id: 1999133338649204,
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
params: ['mock tx params'],
|
params: [{ ...mockTxParams }],
|
||||||
method: 'eth_sendTransaction',
|
method: 'eth_sendTransaction',
|
||||||
}
|
}
|
||||||
streamTest.write({
|
streamTest.write({
|
||||||
@ -865,14 +896,11 @@ describe('MetaMaskController', function () {
|
|||||||
}, null, () => {
|
}, null, () => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
assert.deepStrictEqual(
|
assert.deepStrictEqual(
|
||||||
metamaskController.txController.newUnapprovedTransaction.getCall(0).args,
|
loggerMiddlewareMock.requests[0],
|
||||||
[
|
|
||||||
'mock tx params',
|
|
||||||
{
|
{
|
||||||
...message,
|
...message,
|
||||||
origin: 'http://mycrypto.com',
|
origin: 'http://mycrypto.com',
|
||||||
},
|
},
|
||||||
]
|
|
||||||
)
|
)
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
|
29
yarn.lock
29
yarn.lock
@ -10174,7 +10174,7 @@ eth-json-rpc-middleware@^1.5.0:
|
|||||||
promise-to-callback "^1.0.0"
|
promise-to-callback "^1.0.0"
|
||||||
tape "^4.6.3"
|
tape "^4.6.3"
|
||||||
|
|
||||||
eth-json-rpc-middleware@^4.1.4, eth-json-rpc-middleware@^4.1.5, eth-json-rpc-middleware@^4.4.1:
|
eth-json-rpc-middleware@^4.1.4, eth-json-rpc-middleware@^4.1.5:
|
||||||
version "4.4.1"
|
version "4.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-4.4.1.tgz#07d3dd0724c24a8d31e4a172ee96271da71b4228"
|
resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-4.4.1.tgz#07d3dd0724c24a8d31e4a172ee96271da71b4228"
|
||||||
integrity sha512-yoSuRgEYYGFdVeZg3poWOwAlRI+MoBIltmOB86MtpoZjvLbou9EB/qWMOWSmH2ryCWLW97VYY6NWsmWm3OAA7A==
|
integrity sha512-yoSuRgEYYGFdVeZg3poWOwAlRI+MoBIltmOB86MtpoZjvLbou9EB/qWMOWSmH2ryCWLW97VYY6NWsmWm3OAA7A==
|
||||||
@ -10194,6 +10194,26 @@ eth-json-rpc-middleware@^4.1.4, eth-json-rpc-middleware@^4.1.5, eth-json-rpc-mid
|
|||||||
pify "^3.0.0"
|
pify "^3.0.0"
|
||||||
safe-event-emitter "^1.0.1"
|
safe-event-emitter "^1.0.1"
|
||||||
|
|
||||||
|
eth-json-rpc-middleware@^5.0.0:
|
||||||
|
version "5.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eth-json-rpc-middleware/-/eth-json-rpc-middleware-5.0.0.tgz#9a202a41a2b1678c094cbffd0db0da619ec86f7b"
|
||||||
|
integrity sha512-31zLHvElrQV7opFZs47UqjMGFM4QCgMms7HJSQAZ3wjPuE3rFLxFUTeGHXwM3fAp9zh7n0M+B2gK7Ds4OUKfog==
|
||||||
|
dependencies:
|
||||||
|
btoa "^1.2.1"
|
||||||
|
clone "^2.1.1"
|
||||||
|
eth-query "^2.1.2"
|
||||||
|
eth-rpc-errors "^2.1.1"
|
||||||
|
eth-sig-util "^1.4.2"
|
||||||
|
ethereumjs-block "^1.6.0"
|
||||||
|
ethereumjs-tx "^1.3.7"
|
||||||
|
ethereumjs-util "^5.1.2"
|
||||||
|
ethereumjs-vm "^2.6.0"
|
||||||
|
fetch-ponyfill "^4.0.0"
|
||||||
|
json-rpc-engine "^5.1.3"
|
||||||
|
json-stable-stringify "^1.0.1"
|
||||||
|
pify "^3.0.0"
|
||||||
|
safe-event-emitter "^1.0.1"
|
||||||
|
|
||||||
eth-keyring-controller@^5.3.0, eth-keyring-controller@^5.6.1:
|
eth-keyring-controller@^5.3.0, eth-keyring-controller@^5.6.1:
|
||||||
version "5.6.1"
|
version "5.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/eth-keyring-controller/-/eth-keyring-controller-5.6.1.tgz#7b7268400704c8f5ce98a055910341177dd207ca"
|
resolved "https://registry.yarnpkg.com/eth-keyring-controller/-/eth-keyring-controller-5.6.1.tgz#7b7268400704c8f5ce98a055910341177dd207ca"
|
||||||
@ -10275,6 +10295,13 @@ eth-query@^2.0.2, eth-query@^2.1.0, eth-query@^2.1.2:
|
|||||||
json-rpc-random-id "^1.0.0"
|
json-rpc-random-id "^1.0.0"
|
||||||
xtend "^4.0.1"
|
xtend "^4.0.1"
|
||||||
|
|
||||||
|
eth-rpc-errors@^2.1.1:
|
||||||
|
version "2.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/eth-rpc-errors/-/eth-rpc-errors-2.1.1.tgz#00a7d6c8a9c864a8ab7d0356be20964e5bee4b13"
|
||||||
|
integrity sha512-MY3zAa5ZF8hvgQu1HOF9agaK5GgigBRGpTJ8H0oVlE0NqMu13CW6syyjLXdeIDCGQTbUeHliU1z9dVmvMKx1Tg==
|
||||||
|
dependencies:
|
||||||
|
fast-safe-stringify "^2.0.6"
|
||||||
|
|
||||||
eth-sig-util@2.3.0, eth-sig-util@^2.3.0:
|
eth-sig-util@2.3.0, eth-sig-util@^2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-2.3.0.tgz#c54a6ac8e8796f7e25f59cf436982a930e645231"
|
resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-2.3.0.tgz#c54a6ac8e8796f7e25f59cf436982a930e645231"
|
||||||
|
Loading…
Reference in New Issue
Block a user