aboutsummaryrefslogtreecommitdiffstats
path: root/packages/order-utils/src/signature_utils.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/order-utils/src/signature_utils.ts')
-rw-r--r--packages/order-utils/src/signature_utils.ts121
1 files changed, 112 insertions, 9 deletions
diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts
index ebd636b20..106bbf4e8 100644
--- a/packages/order-utils/src/signature_utils.ts
+++ b/packages/order-utils/src/signature_utils.ts
@@ -1,28 +1,97 @@
import { schemas } from '@0xproject/json-schemas';
-import { ECSignature, Provider } from '@0xproject/types';
+import { ECSignature, Provider, SignatureType } from '@0xproject/types';
import { Web3Wrapper } from '@0xproject/web3-wrapper';
import * as ethUtil from 'ethereumjs-util';
import * as _ from 'lodash';
+import { artifacts } from './artifacts';
import { assert } from './assert';
+import { ExchangeContract } from './generated_contract_wrappers/exchange';
+import { ISignerContract } from './generated_contract_wrappers/i_signer';
import { OrderError } from './types';
/**
- * Verifies that the elliptic curve signature `signature` was generated
- * by signing `data` with the private key corresponding to the `signerAddress` address.
+ * Verifies that the provided signature is valid according to the 0x Protocol smart contracts
* @param data The hex encoded data signed by the supplied signature.
* @param signature An object containing the elliptic curve signature parameters.
* @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
* @return Whether the signature is valid for the supplied signerAddress and data.
*/
-export function isValidSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
+export async function isValidSignatureAsync(
+ provider: Provider,
+ data: string,
+ signature: string,
+ signerAddress: string,
+): Promise<boolean> {
+ const signatureTypeIndexIfExists = getSignatureTypeIndexIfExists(signature);
+ if (_.isUndefined(signatureTypeIndexIfExists)) {
+ throw new Error(`Unrecognized signatureType in signature: ${signature}`);
+ }
+
+ switch (signatureTypeIndexIfExists) {
+ case SignatureType.Illegal:
+ case SignatureType.Invalid:
+ return false;
+
+ // Question: Does it make sense to handle this?
+ case SignatureType.Caller:
+ return true;
+
+ // TODO: Rename this type to `EthSign` b/c multiple of the signature
+ // types use ECRecover...
+ case SignatureType.Ecrecover: {
+ const ecSignature = parseECSignature(signature);
+ const dataBuff = ethUtil.toBuffer(data);
+ const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
+ const msgHash = ethUtil.bufferToHex(msgHashBuff);
+ return isValidECSignature(msgHash, ecSignature, signerAddress);
+ }
+
+ case SignatureType.EIP712: {
+ const ecSignature = parseECSignature(signature);
+ return isValidECSignature(data, ecSignature, signerAddress);
+ }
+
+ case SignatureType.Trezor: {
+ const dataBuff = ethUtil.toBuffer(data);
+ const msgHashBuff = hashTrezorPersonalMessage(dataBuff);
+ const msgHash = ethUtil.bufferToHex(msgHashBuff);
+ const ecSignature = parseECSignature(signature);
+ return isValidECSignature(msgHash, ecSignature, signerAddress);
+ }
+
+ // TODO: Rename Contract -> Wallet
+ case SignatureType.Contract: {
+ const signerContract = new ISignerContract(artifacts.ISigner.abi, signerAddress, provider);
+ const isValid = await signerContract.isValidSignature.callAsync(data, signature);
+ return isValid;
+ }
+
+ case SignatureType.PreSigned: {
+ const exchangeContract = new ExchangeContract(artifacts.Exchange.abi, signerAddress, provider);
+ const isValid = await exchangeContract.preSigned.callAsync(data, signerAddress);
+ return true;
+ }
+
+ default:
+ throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`);
+ }
+}
+
+export function getVRSHexString(ecSignature: ECSignature): string {
+ const vrs = `0x${intToHex(ecSignature.v)}${ethUtil.stripHexPrefix(ecSignature.r)}${ethUtil.stripHexPrefix(
+ ecSignature.s,
+ )}`;
+ return vrs;
+}
+
+export function isValidECSignature(data: string, signature: ECSignature, signerAddress: string): boolean {
assert.isHexString('data', data);
assert.doesConformToSchema('signature', signature, schemas.ecSignatureSchema);
assert.isETHAddressHex('signerAddress', signerAddress);
const normalizedSignerAddress = signerAddress.toLowerCase();
- const dataBuff = ethUtil.toBuffer(data);
- const msgHashBuff = ethUtil.hashPersonalMessage(dataBuff);
+ const msgHashBuff = ethUtil.toBuffer(data);
try {
const pubKey = ethUtil.ecrecover(
msgHashBuff,
@@ -36,6 +105,7 @@ export function isValidSignature(data: string, signature: ECSignature, signerAdd
return false;
}
}
+
/**
* Signs an orderHash and returns it's elliptic curve signature.
* This method currently supports TestRPC, Geth and Parity above and below V1.6.6
@@ -48,7 +118,7 @@ export function isValidSignature(data: string, signature: ECSignature, signerAdd
* before sending the request.
* @return An object containing the Elliptic curve signature parameters generated by signing the orderHash.
*/
-export async function signOrderHashAsync(
+export async function ecSignOrderHashAsync(
provider: Provider,
orderHash: string,
signerAddress: string,
@@ -76,7 +146,7 @@ export async function signOrderHashAsync(
const validVParamValues = [27, 28];
const ecSignatureVRS = parseSignatureHexAsVRS(signature);
if (_.includes(validVParamValues, ecSignatureVRS.v)) {
- const isValidVRSSignature = isValidSignature(orderHash, ecSignatureVRS, normalizedSignerAddress);
+ const isValidVRSSignature = isValidECSignature(orderHash, ecSignatureVRS, normalizedSignerAddress);
if (isValidVRSSignature) {
return ecSignatureVRS;
}
@@ -84,7 +154,7 @@ export async function signOrderHashAsync(
const ecSignatureRSV = parseSignatureHexAsRSV(signature);
if (_.includes(validVParamValues, ecSignatureRSV.v)) {
- const isValidRSVSignature = isValidSignature(orderHash, ecSignatureRSV, normalizedSignerAddress);
+ const isValidRSVSignature = isValidECSignature(orderHash, ecSignatureRSV, normalizedSignerAddress);
if (isValidRSVSignature) {
return ecSignatureRSV;
}
@@ -93,6 +163,39 @@ export async function signOrderHashAsync(
throw new Error(OrderError.InvalidSignature);
}
+function hashTrezorPersonalMessage(message: Buffer): Buffer {
+ const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + String.fromCharCode(message.length));
+ return ethUtil.sha3(Buffer.concat([prefix, message]));
+}
+
+function parseECSignature(signature: string): ECSignature {
+ const signatureTypeIndexIfExists = getSignatureTypeIndexIfExists(signature);
+ const ecSignatureTypes = [SignatureType.Ecrecover, SignatureType.EIP712, SignatureType.Trezor];
+ const isECSignatureType = _.includes(ecSignatureTypes, signatureTypeIndexIfExists);
+ if (!isECSignatureType) {
+ throw new Error(`Cannot parse non-ECSignature type: ${signatureTypeIndexIfExists}`);
+ }
+
+ // tslint:disable-next-line:custom-no-magic-numbers
+ const vrsHex = `0x${signature.substr(4)}`;
+ const ecSignature = parseSignatureHexAsVRS(vrsHex);
+
+ return ecSignature;
+}
+
+function intToHex(i: number): string {
+ const hex = ethUtil.bufferToHex(ethUtil.toBuffer(i));
+ return hex;
+}
+
+function getSignatureTypeIndexIfExists(signature: string): number {
+ const unprefixedSignature = ethUtil.stripHexPrefix(signature);
+ const signatureTypeHex = unprefixedSignature.substr(0, 2);
+ const base = 16;
+ const signatureTypeInt = parseInt(signatureTypeHex, base);
+ return signatureTypeInt;
+}
+
function parseSignatureHexAsVRS(signatureHex: string): ECSignature {
const signatureBuffer = ethUtil.toBuffer(signatureHex);
let v = signatureBuffer[0];