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.ts417
1 files changed, 0 insertions, 417 deletions
diff --git a/packages/order-utils/src/signature_utils.ts b/packages/order-utils/src/signature_utils.ts
deleted file mode 100644
index 131144d48..000000000
--- a/packages/order-utils/src/signature_utils.ts
+++ /dev/null
@@ -1,417 +0,0 @@
-import { ExchangeContract, IValidatorContract, IWalletContract } from '@0x/abi-gen-wrappers';
-import * as artifacts from '@0x/contract-artifacts';
-import { schemas } from '@0x/json-schemas';
-import { ECSignature, Order, SignatureType, SignedOrder, ValidatorSignature } from '@0x/types';
-import { Web3Wrapper } from '@0x/web3-wrapper';
-import { Provider } from 'ethereum-types';
-import * as ethUtil from 'ethereumjs-util';
-import * as _ from 'lodash';
-
-import { assert } from './assert';
-import { eip712Utils } from './eip712_utils';
-import { orderHashUtils } from './order_hash';
-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 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]
- * @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.
- */
- async isValidSignatureAsync(
- provider: Provider,
- data: string,
- signature: string,
- signerAddress: string,
- ): Promise<boolean> {
- assert.isWeb3Provider('provider', provider);
- assert.isHexString('data', data);
- assert.isHexString('signature', signature);
- assert.isETHAddressHex('signerAddress', signerAddress);
- const signatureTypeIndexIfExists = utils.getSignatureTypeIndexIfExists(signature);
- if (_.isUndefined(signatureTypeIndexIfExists)) {
- throw new Error(`Unrecognized signatureType in signature: ${signature}`);
- }
-
- switch (signatureTypeIndexIfExists) {
- case SignatureType.Illegal:
- case SignatureType.Invalid:
- return false;
-
- case SignatureType.EIP712: {
- const ecSignature = signatureUtils.parseECSignature(signature);
- return signatureUtils.isValidECSignature(data, ecSignature, signerAddress);
- }
-
- case SignatureType.EthSign: {
- const ecSignature = signatureUtils.parseECSignature(signature);
- const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data);
- return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress);
- }
-
- case SignatureType.Wallet: {
- const isValid = await signatureUtils.isValidWalletSignatureAsync(
- provider,
- data,
- signature,
- signerAddress,
- );
- return isValid;
- }
-
- case SignatureType.Validator: {
- const isValid = await signatureUtils.isValidValidatorSignatureAsync(
- provider,
- data,
- signature,
- signerAddress,
- );
- return isValid;
- }
-
- case SignatureType.PreSigned: {
- return signatureUtils.isValidPresignedSignatureAsync(provider, data, signerAddress);
- }
-
- default:
- throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`);
- }
- },
- /**
- * Verifies that the provided presigned 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 signerAddress The hex encoded address that signed the data, producing the supplied signature.
- * @return Whether the data was preSigned by the supplied signerAddress
- */
- async isValidPresignedSignatureAsync(provider: Provider, data: string, signerAddress: string): Promise<boolean> {
- assert.isWeb3Provider('provider', provider);
- assert.isHexString('data', data);
- assert.isETHAddressHex('signerAddress', signerAddress);
- const exchangeContract = new ExchangeContract(artifacts.Exchange.compilerOutput.abi, signerAddress, provider);
- const isValid = await exchangeContract.preSigned.callAsync(data, signerAddress);
- return isValid;
- },
- /**
- * Verifies that the provided wallet 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 presigned 0x Protocol signature made up of: [SignatureType.Presigned]
- * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
- * @return Whether the data was preSigned by the supplied signerAddress.
- */
- async isValidWalletSignatureAsync(
- provider: Provider,
- data: string,
- signature: string,
- signerAddress: string,
- ): Promise<boolean> {
- assert.isWeb3Provider('provider', provider);
- assert.isHexString('data', data);
- assert.isHexString('signature', signature);
- assert.isETHAddressHex('signerAddress', signerAddress);
- // tslint:disable-next-line:custom-no-magic-numbers
- const signatureWithoutType = signature.slice(0, -2);
- const walletContract = new IWalletContract(artifacts.IWallet.compilerOutput.abi, signerAddress, provider);
- const isValid = await walletContract.isValidSignature.callAsync(data, signatureWithoutType);
- return isValid;
- },
- /**
- * Verifies that the provided validator 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 presigned 0x Protocol signature made up of: [SignatureType.Presigned]
- * @param signerAddress The hex encoded address that signed the data, producing the supplied signature.
- * @return Whether the data was preSigned by the supplied signerAddress.
- */
- async isValidValidatorSignatureAsync(
- provider: Provider,
- data: string,
- signature: string,
- signerAddress: string,
- ): Promise<boolean> {
- assert.isWeb3Provider('provider', provider);
- assert.isHexString('data', data);
- assert.isHexString('signature', signature);
- assert.isETHAddressHex('signerAddress', signerAddress);
- const validatorSignature = parseValidatorSignature(signature);
- const exchangeContract = new ExchangeContract(artifacts.Exchange.compilerOutput.abi, signerAddress, provider);
- const isValidatorApproved = await exchangeContract.allowedValidators.callAsync(
- signerAddress,
- validatorSignature.validatorAddress,
- );
- if (!isValidatorApproved) {
- throw new Error(
- `Validator ${validatorSignature.validatorAddress} was not pre-approved by ${signerAddress}.`,
- );
- }
-
- const validatorContract = new IValidatorContract(
- artifacts.IValidator.compilerOutput.abi,
- signerAddress,
- provider,
- );
- const isValid = await validatorContract.isValidSignature.callAsync(
- data,
- signerAddress,
- validatorSignature.signature,
- );
- return isValid;
- },
- /**
- * Checks if the supplied elliptic curve signature corresponds to signing `data` with
- * the private key corresponding to `signerAddress`
- * @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 ECSignature is valid.
- */
- 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 msgHashBuff = ethUtil.toBuffer(data);
- try {
- const pubKey = ethUtil.ecrecover(
- msgHashBuff,
- signature.v,
- ethUtil.toBuffer(signature.r),
- ethUtil.toBuffer(signature.s),
- );
- const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
- const normalizedRetrievedAddress = retrievedAddress.toLowerCase();
- return normalizedRetrievedAddress === normalizedSignerAddress;
- } catch (err) {
- return false;
- }
- },
- /**
- * Signs an order and returns a SignedOrder. First `eth_signTypedData` is requested
- * then a fallback to `eth_sign` if not available on the supplied provider.
- * @param order The Order to sign.
- * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
- * must be available via the supplied Provider.
- * @return A SignedOrder containing the order and Elliptic curve signature with Signature Type.
- */
- async ecSignOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<SignedOrder> {
- assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]);
- try {
- const signedOrder = await signatureUtils.ecSignTypedDataOrderAsync(provider, order, signerAddress);
- return signedOrder;
- } catch (err) {
- // HACK: We are unable to handle specific errors thrown since provider is not an object
- // under our control. It could be Metamask Web3, Ethers, or any general RPC provider.
- // We check for a user denying the signature request in a way that supports Metamask and
- // Coinbase Wallet. Unfortunately for signers with a different error message,
- // they will receive two signature requests.
- if (err.message.includes('User denied message signature')) {
- throw err;
- }
- const orderHash = orderHashUtils.getOrderHashHex(order);
- const signatureHex = await signatureUtils.ecSignHashAsync(provider, orderHash, signerAddress);
- const signedOrder = {
- ...order,
- signature: signatureHex,
- };
- return signedOrder;
- }
- },
- /**
- * Signs an order using `eth_signTypedData` and returns a SignedOrder.
- * @param order The Order to sign.
- * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
- * must be available via the supplied Provider.
- * @return A SignedOrder containing the order and Elliptic curve signature with Signature Type.
- */
- async ecSignTypedDataOrderAsync(provider: Provider, order: Order, signerAddress: string): Promise<SignedOrder> {
- assert.isWeb3Provider('provider', provider);
- assert.isETHAddressHex('signerAddress', signerAddress);
- assert.doesConformToSchema('order', order, schemas.orderSchema, [schemas.hexSchema]);
- const web3Wrapper = new Web3Wrapper(provider);
- await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
- const normalizedSignerAddress = signerAddress.toLowerCase();
- const typedData = eip712Utils.createOrderTypedData(order);
- try {
- const signature = await web3Wrapper.signTypedDataAsync(normalizedSignerAddress, typedData);
- const ecSignatureRSV = parseSignatureHexAsRSV(signature);
- const signatureBuffer = Buffer.concat([
- ethUtil.toBuffer(ecSignatureRSV.v),
- ethUtil.toBuffer(ecSignatureRSV.r),
- ethUtil.toBuffer(ecSignatureRSV.s),
- ethUtil.toBuffer(SignatureType.EIP712),
- ]);
- const signatureHex = `0x${signatureBuffer.toString('hex')}`;
- return {
- ...order,
- signature: signatureHex,
- };
- } catch (err) {
- // Detect if Metamask to transition users to the MetamaskSubprovider
- if ((provider as any).isMetaMask) {
- throw new Error(OrderError.InvalidMetamaskSigner);
- } else {
- throw err;
- }
- }
- },
- /**
- * Signs a hash using `eth_sign` and returns its elliptic curve signature and signature type.
- * @param msgHash Hex encoded message to sign.
- * @param signerAddress The hex encoded Ethereum address you wish to sign it with. This address
- * must be available via the supplied Provider.
- * @return A hex encoded string containing the Elliptic curve signature generated by signing the msgHash and the Signature Type.
- */
- async ecSignHashAsync(provider: Provider, msgHash: string, signerAddress: string): Promise<string> {
- assert.isWeb3Provider('provider', provider);
- assert.isHexString('msgHash', msgHash);
- assert.isETHAddressHex('signerAddress', signerAddress);
- const web3Wrapper = new Web3Wrapper(provider);
- await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
- const normalizedSignerAddress = signerAddress.toLowerCase();
- const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHash);
- const prefixedMsgHashHex = signatureUtils.addSignedMessagePrefix(msgHash);
-
- // HACK: There is no consensus on whether the signatureHex string should be formatted as
- // 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);
- return convertedSignatureHex;
- }
- }
- const ecSignatureVRS = parseSignatureHexAsVRS(signature);
- if (_.includes(validVParamValues, ecSignatureVRS.v)) {
- const isValidVRSSignature = signatureUtils.isValidECSignature(
- prefixedMsgHashHex,
- ecSignatureVRS,
- normalizedSignerAddress,
- );
- if (isValidVRSSignature) {
- const convertedSignatureHex = signatureUtils.convertECSignatureToSignatureHex(ecSignatureVRS);
- return convertedSignatureHex;
- }
- }
- // Detect if Metamask to transition users to the MetamaskSubprovider
- if ((provider as any).isMetaMask) {
- throw new Error(OrderError.InvalidMetamaskSigner);
- } else {
- throw new Error(OrderError.InvalidSignature);
- }
- },
- /**
- * Combines ECSignature with V,R,S and the EthSign signature type for use in 0x protocol
- * @param ecSignature The ECSignature of the signed data
- * @return Hex encoded string of signature (v,r,s) with Signature Type
- */
- convertECSignatureToSignatureHex(ecSignature: ECSignature): string {
- const signatureBuffer = Buffer.concat([
- ethUtil.toBuffer(ecSignature.v),
- ethUtil.toBuffer(ecSignature.r),
- ethUtil.toBuffer(ecSignature.s),
- ]);
- const signatureHex = `0x${signatureBuffer.toString('hex')}`;
- const signatureWithType = signatureUtils.convertToSignatureWithType(signatureHex, SignatureType.EthSign);
- 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, 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
- * @return Prefixed message
- */
- addSignedMessagePrefix(message: string): string {
- assert.isString('message', message);
- const msgBuff = ethUtil.toBuffer(message);
- const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff);
- const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
- return prefixedMsgHex;
- },
- /**
- * Parse a 0x protocol hex-encoded signature string into its ECSignature components
- * @param signature A hex encoded ecSignature 0x Protocol signature
- * @return An ECSignature object with r,s,v parameters
- */
- parseECSignature(signature: string): ECSignature {
- assert.isHexString('signature', signature);
- const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712];
- assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes);
-
- // tslint:disable-next-line:custom-no-magic-numbers
- const vrsHex = signature.slice(0, -2);
- const ecSignature = parseSignatureHexAsVRS(vrsHex);
-
- return ecSignature;
- },
-};
-
-function parseValidatorSignature(signature: string): ValidatorSignature {
- assert.isOneOfExpectedSignatureTypes(signature, [SignatureType.Validator]);
- // tslint:disable:custom-no-magic-numbers
- const validatorSignature = {
- validatorAddress: signature.slice(-22, -2),
- signature: signature.slice(0, -22),
- };
- // tslint:enable:custom-no-magic-numbers
- return validatorSignature;
-}
-
-function parseSignatureHexAsVRS(signatureHex: string): ECSignature {
- const signatureBuffer = ethUtil.toBuffer(signatureHex);
- let v = signatureBuffer[0];
- // HACK: Sometimes v is returned as [0, 1] and sometimes as [27, 28]
- // If it is returned as [0, 1], add 27 to both so it becomes [27, 28]
- const lowestValidV = 27;
- const isProperlyFormattedV = v >= lowestValidV;
- if (!isProperlyFormattedV) {
- v += lowestValidV;
- }
- // signatureBuffer contains vrs
- const vEndIndex = 1;
- const rsIndex = 33;
- const r = signatureBuffer.slice(vEndIndex, rsIndex);
- const sEndIndex = 65;
- const s = signatureBuffer.slice(rsIndex, sEndIndex);
- const ecSignature: ECSignature = {
- v,
- r: ethUtil.bufferToHex(r),
- s: ethUtil.bufferToHex(s),
- };
- return ecSignature;
-}
-
-function parseSignatureHexAsRSV(signatureHex: string): ECSignature {
- const { v, r, s } = ethUtil.fromRpcSig(signatureHex);
- const ecSignature: ECSignature = {
- v,
- r: ethUtil.bufferToHex(r),
- s: ethUtil.bufferToHex(s),
- };
- return ecSignature;
-}