Make signature_util into an object literal so related functions are rendered together in the docs
This commit is contained in:
@@ -13,288 +13,307 @@ import { IWalletContract } from './generated_contract_wrappers/i_wallet';
|
||||
import { MessagePrefixOpts, MessagePrefixType, OrderError } from './types';
|
||||
import { utils } from './utils';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export async function 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 = parseECSignature(signature);
|
||||
return isValidECSignature(data, ecSignature, signerAddress);
|
||||
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]
|
||||
* @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}`);
|
||||
}
|
||||
|
||||
case SignatureType.EthSign: {
|
||||
const ecSignature = parseECSignature(signature);
|
||||
const prefixedMessageHex = addSignedMessagePrefix(data, MessagePrefixType.EthSign);
|
||||
return isValidECSignature(prefixedMessageHex, ecSignature, signerAddress);
|
||||
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, MessagePrefixType.EthSign);
|
||||
return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress);
|
||||
}
|
||||
|
||||
case SignatureType.Caller:
|
||||
// HACK: We currently do not "validate" the caller signature type.
|
||||
// It can only be validated during Exchange contract execution.
|
||||
throw new Error('Caller signature type cannot be validated off-chain');
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
case SignatureType.Trezor: {
|
||||
const prefixedMessageHex = signatureUtils.addSignedMessagePrefix(data, MessagePrefixType.Trezor);
|
||||
const ecSignature = signatureUtils.parseECSignature(signature);
|
||||
return signatureUtils.isValidECSignature(prefixedMessageHex, ecSignature, signerAddress);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`);
|
||||
}
|
||||
|
||||
case SignatureType.Caller:
|
||||
// HACK: We currently do not "validate" the caller signature type.
|
||||
// It can only be validated during Exchange contract execution.
|
||||
throw new Error('Caller signature type cannot be validated off-chain');
|
||||
|
||||
case SignatureType.Wallet: {
|
||||
const isValid = await isValidWalletSignatureAsync(provider, data, signature, signerAddress);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
case SignatureType.Validator: {
|
||||
const isValid = await isValidValidatorSignatureAsync(provider, data, signature, signerAddress);
|
||||
return isValid;
|
||||
}
|
||||
|
||||
case SignatureType.PreSigned: {
|
||||
return isValidPresignedSignatureAsync(provider, data, signerAddress);
|
||||
}
|
||||
|
||||
case SignatureType.Trezor: {
|
||||
const prefixedMessageHex = addSignedMessagePrefix(data, MessagePrefixType.Trezor);
|
||||
const ecSignature = parseECSignature(signature);
|
||||
return isValidECSignature(prefixedMessageHex, ecSignature, signerAddress);
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unhandled SignatureType: ${signatureTypeIndexIfExists}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the provided presigned 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 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.
|
||||
*/
|
||||
export async function 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 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.
|
||||
*/
|
||||
export async function 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(-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 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.
|
||||
*/
|
||||
export async function 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.
|
||||
*/
|
||||
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 msgHashBuff = ethUtil.toBuffer(data);
|
||||
try {
|
||||
const pubKey = ethUtil.ecrecover(
|
||||
msgHashBuff,
|
||||
signature.v,
|
||||
ethUtil.toBuffer(signature.r),
|
||||
ethUtil.toBuffer(signature.s),
|
||||
},
|
||||
/**
|
||||
* 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(-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,
|
||||
);
|
||||
const retrievedAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey));
|
||||
return retrievedAddress === signerAddress;
|
||||
} catch (err) {
|
||||
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
|
||||
* @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 hashPrefixOpts 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.
|
||||
*/
|
||||
export async function ecSignOrderHashAsync(
|
||||
provider: Provider,
|
||||
orderHash: string,
|
||||
signerAddress: string,
|
||||
messagePrefixOpts: MessagePrefixOpts,
|
||||
): Promise<ECSignature> {
|
||||
assert.isWeb3Provider('provider', provider);
|
||||
assert.isHexString('orderHash', orderHash);
|
||||
assert.isETHAddressHex('signerAddress', signerAddress);
|
||||
const web3Wrapper = new Web3Wrapper(provider);
|
||||
await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
|
||||
const normalizedSignerAddress = signerAddress.toLowerCase();
|
||||
|
||||
let msgHashHex = orderHash;
|
||||
const prefixedMsgHashHex = addSignedMessagePrefix(orderHash, messagePrefixOpts.prefixType);
|
||||
if (messagePrefixOpts.shouldAddPrefixBeforeCallingEthSign) {
|
||||
msgHashHex = prefixedMsgHashHex;
|
||||
}
|
||||
const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHashHex);
|
||||
|
||||
// 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.
|
||||
// tslint:disable-next-line:custom-no-magic-numbers
|
||||
const validVParamValues = [27, 28];
|
||||
const ecSignatureVRS = parseSignatureHexAsVRS(signature);
|
||||
if (_.includes(validVParamValues, ecSignatureVRS.v)) {
|
||||
const isValidVRSSignature = isValidECSignature(prefixedMsgHashHex, ecSignatureVRS, normalizedSignerAddress);
|
||||
if (isValidVRSSignature) {
|
||||
return ecSignatureVRS;
|
||||
}
|
||||
}
|
||||
|
||||
const ecSignatureRSV = parseSignatureHexAsRSV(signature);
|
||||
if (_.includes(validVParamValues, ecSignatureRSV.v)) {
|
||||
const isValidRSVSignature = isValidECSignature(prefixedMsgHashHex, ecSignatureRSV, normalizedSignerAddress);
|
||||
if (isValidRSVSignature) {
|
||||
return ecSignatureRSV;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(OrderError.InvalidSignature);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* specific message prefixes.
|
||||
* @return Prefixed message
|
||||
*/
|
||||
export function addSignedMessagePrefix(message: string, messagePrefixType: MessagePrefixType): string {
|
||||
assert.isString('message', message);
|
||||
assert.doesBelongToStringEnum('messagePrefixType', messagePrefixType, MessagePrefixType);
|
||||
switch (messagePrefixType) {
|
||||
case MessagePrefixType.None:
|
||||
return message;
|
||||
|
||||
case MessagePrefixType.EthSign: {
|
||||
const msgBuff = ethUtil.toBuffer(message);
|
||||
const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff);
|
||||
const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
|
||||
return prefixedMsgHex;
|
||||
if (!isValidatorApproved) {
|
||||
throw new Error(
|
||||
`Validator ${validatorSignature.validatorAddress} was not pre-approved by ${signerAddress}.`,
|
||||
);
|
||||
}
|
||||
|
||||
case MessagePrefixType.Trezor: {
|
||||
const msgBuff = ethUtil.toBuffer(message);
|
||||
const prefixedMsgBuff = hashTrezorPersonalMessage(msgBuff);
|
||||
const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
|
||||
return prefixedMsgHex;
|
||||
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 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));
|
||||
return retrievedAddress === signerAddress;
|
||||
} catch (err) {
|
||||
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
|
||||
* @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
|
||||
* 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.
|
||||
*/
|
||||
async ecSignOrderHashAsync(
|
||||
provider: Provider,
|
||||
orderHash: string,
|
||||
signerAddress: string,
|
||||
messagePrefixOpts: MessagePrefixOpts,
|
||||
): Promise<ECSignature> {
|
||||
assert.isWeb3Provider('provider', provider);
|
||||
assert.isHexString('orderHash', orderHash);
|
||||
assert.isETHAddressHex('signerAddress', signerAddress);
|
||||
const web3Wrapper = new Web3Wrapper(provider);
|
||||
await assert.isSenderAddressAsync('signerAddress', signerAddress, web3Wrapper);
|
||||
const normalizedSignerAddress = signerAddress.toLowerCase();
|
||||
|
||||
let msgHashHex = orderHash;
|
||||
const prefixedMsgHashHex = signatureUtils.addSignedMessagePrefix(orderHash, messagePrefixOpts.prefixType);
|
||||
if (messagePrefixOpts.shouldAddPrefixBeforeCallingEthSign) {
|
||||
msgHashHex = prefixedMsgHashHex;
|
||||
}
|
||||
const signature = await web3Wrapper.signMessageAsync(normalizedSignerAddress, msgHashHex);
|
||||
|
||||
// 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.
|
||||
// tslint:disable-next-line:custom-no-magic-numbers
|
||||
const validVParamValues = [27, 28];
|
||||
const ecSignatureVRS = parseSignatureHexAsVRS(signature);
|
||||
if (_.includes(validVParamValues, ecSignatureVRS.v)) {
|
||||
const isValidVRSSignature = signatureUtils.isValidECSignature(
|
||||
prefixedMsgHashHex,
|
||||
ecSignatureVRS,
|
||||
normalizedSignerAddress,
|
||||
);
|
||||
if (isValidVRSSignature) {
|
||||
return ecSignatureVRS;
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unrecognized MessagePrefixType: ${messagePrefixType}`);
|
||||
}
|
||||
}
|
||||
const ecSignatureRSV = parseSignatureHexAsRSV(signature);
|
||||
if (_.includes(validVParamValues, ecSignatureRSV.v)) {
|
||||
const isValidRSVSignature = signatureUtils.isValidECSignature(
|
||||
prefixedMsgHashHex,
|
||||
ecSignatureRSV,
|
||||
normalizedSignerAddress,
|
||||
);
|
||||
if (isValidRSVSignature) {
|
||||
return ecSignatureRSV;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a 0x protocol hex-encoded signature string into it's ECSignature components
|
||||
* @param signature A hex encoded ecSignature 0x Protocol signature
|
||||
* @return An ECSignature object with r,s,v parameters
|
||||
*/
|
||||
export function parseECSignature(signature: string): ECSignature {
|
||||
assert.isHexString('signature', signature);
|
||||
const ecSignatureTypes = [SignatureType.EthSign, SignatureType.EIP712, SignatureType.Trezor];
|
||||
assert.isOneOfExpectedSignatureTypes(signature, ecSignatureTypes);
|
||||
throw new Error(OrderError.InvalidSignature);
|
||||
},
|
||||
/**
|
||||
* 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
|
||||
* specific message prefixes.
|
||||
* @return Prefixed message
|
||||
*/
|
||||
addSignedMessagePrefix(message: string, messagePrefixType: MessagePrefixType): string {
|
||||
assert.isString('message', message);
|
||||
assert.doesBelongToStringEnum('messagePrefixType', messagePrefixType, MessagePrefixType);
|
||||
switch (messagePrefixType) {
|
||||
case MessagePrefixType.None:
|
||||
return message;
|
||||
|
||||
// tslint:disable-next-line:custom-no-magic-numbers
|
||||
const vrsHex = signature.slice(0, -2);
|
||||
const ecSignature = parseSignatureHexAsVRS(vrsHex);
|
||||
case MessagePrefixType.EthSign: {
|
||||
const msgBuff = ethUtil.toBuffer(message);
|
||||
const prefixedMsgBuff = ethUtil.hashPersonalMessage(msgBuff);
|
||||
const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
|
||||
return prefixedMsgHex;
|
||||
}
|
||||
|
||||
return ecSignature;
|
||||
}
|
||||
case MessagePrefixType.Trezor: {
|
||||
const msgBuff = ethUtil.toBuffer(message);
|
||||
const prefixedMsgBuff = hashTrezorPersonalMessage(msgBuff);
|
||||
const prefixedMsgHex = ethUtil.bufferToHex(prefixedMsgBuff);
|
||||
return prefixedMsgHex;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new Error(`Unrecognized MessagePrefixType: ${messagePrefixType}`);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Parse a 0x protocol hex-encoded signature string into it's 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, SignatureType.Trezor];
|
||||
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 hashTrezorPersonalMessage(message: Buffer): Buffer {
|
||||
const prefix = ethUtil.toBuffer('\x19Ethereum Signed Message:\n' + String.fromCharCode(message.byteLength));
|
||||
|
Reference in New Issue
Block a user