diff options
author | Fabio Berger <me@fabioberger.com> | 2018-08-15 05:21:47 +0800 |
---|---|---|
committer | Fabio Berger <me@fabioberger.com> | 2018-08-15 05:21:47 +0800 |
commit | 2f2582a0da3095d61a99ef09744dc0995677558e (patch) | |
tree | 54989565919038c42d495dafb7426d4148605e84 /packages/order-utils/src/signature_utils.ts | |
parent | 8169155a6547fb0283cd0f5362aad3c0b173b00b (diff) | |
parent | fadd292ecf367e42154856509d0ea0c20b23f2f1 (diff) | |
download | dexon-0x-contracts-2f2582a0da3095d61a99ef09744dc0995677558e.tar.gz dexon-0x-contracts-2f2582a0da3095d61a99ef09744dc0995677558e.tar.zst dexon-0x-contracts-2f2582a0da3095d61a99ef09744dc0995677558e.zip |
Merge development
Diffstat (limited to 'packages/order-utils/src/signature_utils.ts')
-rw-r--r-- | packages/order-utils/src/signature_utils.ts | 128 |
1 files changed, 87 insertions, 41 deletions
diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts index 2dc465256..d466bb00d 100644 --- a/packages/order-utils/src/signature_utils.ts +++ b/packages/order-utils/src/signature_utils.ts @@ -1,5 +1,5 @@ import { schemas } from '@0xproject/json-schemas'; -import { ECSignature, SignatureType, ValidatorSignature } from '@0xproject/types'; +import { ECSignature, SignatureType, SignerType, ValidatorSignature } from '@0xproject/types'; import { Web3Wrapper } from '@0xproject/web3-wrapper'; import { Provider } from 'ethereum-types'; import * as ethUtil from 'ethereumjs-util'; @@ -10,13 +10,12 @@ import { assert } from './assert'; import { ExchangeContract } from './generated_contract_wrappers/exchange'; import { IValidatorContract } from './generated_contract_wrappers/i_validator'; import { IWalletContract } from './generated_contract_wrappers/i_wallet'; -import { MessagePrefixOpts, MessagePrefixType, OrderError } from './types'; +import { OrderError } from './types'; import { utils } from './utils'; export const signatureUtils = { /** * Verifies that the provided signature is valid according to the 0x Protocol smart contracts - * @param provider Web3 provider to use for all JSON RPC requests * @param data The hex encoded data signed by the supplied signature. * @param signature A hex encoded 0x Protocol signature made up of: [TypeSpecificData][SignatureType]. * E.g [vrs][SignatureType.EIP712] @@ -50,7 +49,7 @@ export const signatureUtils = { case SignatureType.EthSign: { const ecSignature = signatureUtils.parseECSignature(signature); - const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, MessagePrefixType.EthSign); + const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, SignerType.EthSign); return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); } @@ -84,7 +83,7 @@ export const signatureUtils = { } case SignatureType.Trezor: { - const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, MessagePrefixType.Trezor); + const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, SignerType.Trezor); const ecSignature = signatureUtils.parseECSignature(signature); return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress); } @@ -202,23 +201,21 @@ export const signatureUtils = { } }, /** - * Signs an orderHash and returns it's elliptic curve signature. + * Signs an orderHash and returns it's elliptic curve signature and signature type. * This method currently supports TestRPC, Geth and Parity above and below V1.6.6 - * @param provider The provider to use for JSON RPC calls - * @param orderHash Hex encoded orderHash to sign. - * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address + * @param orderHash Hex encoded orderHash to sign. + * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address * must be available via the Provider supplied to 0x.js. - * @param messagePrefixOpts Different signers add/require different prefixes be appended to the message being signed. - * Since we cannot know ahead of time which signer you are using, you must supply both a prefixType and - * whether it must be added before calling `eth_sign` (some signers add it themselves) - * @return An object containing the Elliptic curve signature parameters generated by signing the orderHash. + * @param signerType Different signers add/require different prefixes to be prepended to the message being signed. + * Since we cannot know ahead of time which signer you are using, you must supply a SignerType. + * @return A hex encoded string containing the Elliptic curve signature generated by signing the orderHash and the Signature Type. */ async ecSignOrderHashAsync( provider: Provider, orderHash: string, signerAddress: string, - messagePrefixOpts: MessagePrefixOpts, - ): Promise<ECSignature> { + signerType: SignerType, + ): Promise<string> { assert.isWeb3Provider('provider', provider); assert.isHexString('orderHash', orderHash); assert.isETHAddressHex('signerAddress', signerAddress); @@ -227,8 +224,10 @@ export const signatureUtils = { const normalizedSignerAddress = signerAddress.toLowerCase(); let msgHashHex = orderHash; - const prefixedMsgHashHex = signatureUtils.addSignedMessagePrefix(orderHash, messagePrefixOpts.prefixType); - if (messagePrefixOpts.shouldAddPrefixBeforeCallingEthSign) { + const prefixedMsgHashHex = signatureUtils.addSignedMessagePrefix(orderHash, signerType); + // Metamask incorrectly implements eth_sign and does not prefix the message as per the spec + // Source: https://github.com/MetaMask/metamask-extension/commit/a9d36860bec424dcee8db043d3e7da6a5ff5672e + if (signerType === SignerType.Metamask) { msgHashHex = prefixedMsgHashHex; } const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHashHex); @@ -237,8 +236,24 @@ export const signatureUtils = { // v + r + s OR r + s + v, and different clients (even different versions of the same client) // return the signature params in different orders. In order to support all client implementations, // we parse the signature in both ways, and evaluate if either one is a valid signature. + // r + s + v is the most prevalent format from eth_sign, so we attempt this first. // tslint:disable-next-line:custom-no-magic-numbers const validVParamValues = [27, 28]; + const ecSignatureRSV = parseSignatureHexAsRSV(signature); + if (_.includes(validVParamValues, ecSignatureRSV.v)) { + const isValidRSVSignature = signatureUtils.isValidECSignature( + prefixedMsgHashHex, + ecSignatureRSV, + normalizedSignerAddress, + ); + if (isValidRSVSignature) { + const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex( + ecSignatureRSV, + signerType, + ); + return convertedSignatureHex; + } + } const ecSignatureVRS = parseSignatureHexAsVRS(signature); if (_.includes(validVParamValues, ecSignatureVRS.v)) { const isValidVRSSignature = signatureUtils.isValidECSignature( @@ -247,54 +262,85 @@ export const signatureUtils = { normalizedSignerAddress, ); if (isValidVRSSignature) { - return ecSignatureVRS; + const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex( + ecSignatureVRS, + signerType, + ); + return convertedSignatureHex; } } - const ecSignatureRSV = parseSignatureHexAsRSV(signature); - if (_.includes(validVParamValues, ecSignatureRSV.v)) { - const isValidRSVSignature = signatureUtils.isValidECSignature( - prefixedMsgHashHex, - ecSignatureRSV, - normalizedSignerAddress, - ); - if (isValidRSVSignature) { - return ecSignatureRSV; + throw new Error(OrderError.InvalidSignature); + }, + /** + * Combines ECSignature with V,R,S and the relevant signature type for use in 0x protocol + * @param ecSignature The ECSignature of the signed data + * @param signerType The SignerType of the signed data + * @return Hex encoded string of signature (v,r,s) with Signature Type + */ + convertECSignatureToSignatureHex(ecSignature: ECSignature, signerType: SignerType): string { + const signatureBuffer = Buffer.concat([ + ethUtil.toBuffer(ecSignature.v), + ethUtil.toBuffer(ecSignature.r), + ethUtil.toBuffer(ecSignature.s), + ]); + const signatureHex = `0x${signatureBuffer.toString('hex')}`; + let signatureType; + switch (signerType) { + case SignerType.Metamask: + case SignerType.Ledger: + case SignerType.Default: { + signatureType = SignatureType.EthSign; + break; } + case SignerType.Trezor: { + signatureType = SignatureType.Trezor; + break; + } + default: + throw new Error(`Unrecognized SignerType: ${signerType}`); } - - throw new Error(OrderError.InvalidSignature); + const signatureWithType = signatureUtils.convertToSignatureWithType(signatureHex, signatureType); + return signatureWithType; + }, + /** + * Combines the signature proof and the Signature Type. + * @param signature The hex encoded signature proof + * @param signatureType The signature type, i.e EthSign, Trezor, Wallet etc. + * @return Hex encoded string of signature proof with Signature Type + */ + convertToSignatureWithType(signature: string, signatureType: SignatureType): string { + const signatureBuffer = Buffer.concat([ethUtil.toBuffer(signature), ethUtil.toBuffer(signatureType)]); + const signatureHex = `0x${signatureBuffer.toString('hex')}`; + return signatureHex; }, /** * Adds the relevant prefix to the message being signed. * @param message Message to sign - * @param messagePrefixType The type of message prefix to add. Different signers expect + * @param signerType The type of message prefix to add for a given SignerType. Different signers expect * specific message prefixes. * @return Prefixed message */ - addSignedMessagePrefix(message: string, messagePrefixType: MessagePrefixType): string { + addSignedMessagePrefix(message: string, signerType: SignerType = SignerType.Default): string { assert.isString('message', message); - assert.doesBelongToStringEnum('messagePrefixType', messagePrefixType, MessagePrefixType); - switch (messagePrefixType) { - case MessagePrefixType.None: - return message; - - case MessagePrefixType.EthSign: { + assert.doesBelongToStringEnum('signerType', signerType, SignerType); + switch (signerType) { + case SignerType.Metamask: + case SignerType.Ledger: + case SignerType.Default: { const msgBuff = ethUtil.toBuffer(message); const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff); const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); return prefixedMsgHex; } - - case MessagePrefixType.Trezor: { + case SignerType.Trezor: { const msgBuff = ethUtil.toBuffer(message); const prefixedMsgBuff = hashTrezorPersonalMessage(msgBuff); const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff); return prefixedMsgHex; } - default: - throw new Error(`Unrecognized MessagePrefixType: ${messagePrefixType}`); + throw new Error(`Unrecognized SignerType: ${signerType}`); } }, /** |