aboutsummaryrefslogtreecommitdiffstats
path: root/packages/order-utils/src/signature_utils.ts
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2018-08-15 05:21:47 +0800
committerFabio Berger <me@fabioberger.com>2018-08-15 05:21:47 +0800
commit2f2582a0da3095d61a99ef09744dc0995677558e (patch)
tree54989565919038c42d495dafb7426d4148605e84 /packages/order-utils/src/signature_utils.ts
parent8169155a6547fb0283cd0f5362aad3c0b173b00b (diff)
parentfadd292ecf367e42154856509d0ea0c20b23f2f1 (diff)
downloaddexon-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.ts128
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}`);
}
},
/**