import { NativeSigner , serializeSignature , Signature , Signer } from '@celo/base/lib/signatureUtils' ; import * as Web3Utils from 'web3-utils' ; import { ensureLeading0x , eqAddress , privateKeyToAddress , trimLeading0x } from './address' ; import { EIP712TypedData , generateTypedDataHash } from './sign-typed-data-utils' ; export { NativeSigner , POP_SIZE , serializeSignature , Signature , Signer , } from '@celo/base/lib/signatureUtils' ; const ethjsutil = require ( 'ethereumjs-util' ) ; function messageLength ( message ) { if ( Web3Utils . isHexStrict ( message ) ) { return ( message . length - 2 ) / 2 ; } return message . length ; } export function hashMessageWithPrefix ( message ) { const prefix = '\x19Ethereum Signed Message:\n' + messageLength ( message ) ; return Web3Utils . soliditySha3 ( prefix , message ) ! ; } export function hashMessage ( message ) { return Web3Utils . soliditySha3 ( { type : 'string' , value : message } ) ! ; } export async function addressToPublicKey ( signer , signFn ) { const msg = Buffer . from ( 'dummy_msg_data' ) ; const data = '0x' + msg . toString ( 'hex' ) ; const sig = await signFn ( data , signer ) ; const rawsig = ethjsutil . fromRpcSig ( sig ) ; const prefixedMsg = hashMessageWithPrefix ( data ) ; const pubKey = ethjsutil . ecrecover ( Buffer . from ( prefixedMsg . slice ( 2 ) , 'hex' ) , rawsig . v , rawsig . r , rawsig . s ) ; const computedAddr = ethjsutil . pubToAddress ( pubKey ) . toString ( 'hex' ) ; if ( ! eqAddress ( computedAddr , signer ) ) { throw new Error ( 'computed address !== signer' ) ; } return '0x' + pubKey . toString ( 'hex' ) ; } export function LocalSigner ( privateKey ) { return { sign : async ( message ) => Promise . resolve ( serializeSignature ( signMessage ( message , privateKey , privateKeyToAddress ( privateKey ) ) ) ) } ; } export function signedMessageToPublicKey ( message , v , r , s ) { const pubKeyBuf = ethjsutil . ecrecover ( Buffer . from ( message . slice ( 2 ) , 'hex' ) , v , Buffer . from ( r . slice ( 2 ) , 'hex' ) , Buffer . from ( s . slice ( 2 ) , 'hex' ) ) ; return '0x' + pubKeyBuf . toString ( 'hex' ) ; } export function signMessage ( message , privateKey , address ) { return signMessageWithoutPrefix ( hashMessageWithPrefix ( message ) , ensureLeading0x ( privateKey ) , address ) ; } export function signMessageWithoutPrefix ( messageHash , privateKey , address ) { const publicKey = ethjsutil . privateToPublic ( ethjsutil . toBuffer ( privateKey ) ) ; const derivedAddress = ethjsutil . bufferToHex ( ethjsutil . pubToAddress ( publicKey ) ) ; if ( derivedAddress . toLowerCase ( ) !== address . toLowerCase ( ) ) { throw new Error ( 'Provided private key does not match address of intended signer' ) ; } const { r , s , v } = ethjsutil . ecsign ( ethjsutil . toBuffer ( messageHash ) , ethjsutil . toBuffer ( privateKey ) ) ; if ( ! isValidSignature ( address , messageHash , v , ethjsutil . bufferToHex ( r ) , ethjsutil . bufferToHex ( s ) ) ) { throw new Error ( 'Unable to validate signature' ) ; } return { v , r : ethjsutil . bufferToHex ( r ) , s : ethjsutil . bufferToHex ( s ) } ; } export function verifySignature ( message , signature , signer ) { try { parseSignature ( message , signature , signer ) ; return true ; } catch ( error ) { return false ; } } export function parseSignature ( message , signature , signer ) { return parseSignatureWithoutPrefix ( hashMessageWithPrefix ( message ) , signature , signer ) ; } export function parseSignatureWithoutPrefix ( messageHash , signature , signer ) { let { r , s , v } = parseSignatureAsRsv ( signature . slice ( 2 ) ) ; if ( isValidSignature ( signer , messageHash , v , r , s ) ) { return { v , r , s } ; } ; ( { r , s , v } = parseSignatureAsVrs ( signature . slice ( 2 ) ) ) ; if ( isValidSignature ( signer , messageHash , v , r , s ) ) { return { v , r , s } ; } throw new Error ( `Unable to parse signature (expected signer ${ signer })` ) ; } function recoverEIP712TypedDataSigner ( typedData , signature , parseFunction ) { const dataBuff = generateTypedDataHash ( typedData ) ; const { r , s , v } = parseFunction ( trimLeading0x ( signature ) ) ; const publicKey = ethjsutil . ecrecover ( ethjsutil . toBuffer ( dataBuff ) , v , ethjsutil . toBuffer ( r ) , ethjsutil . toBuffer ( s ) ) ; return ethjsutil . bufferToHex ( ethjsutil . pubToAddress ( publicKey ) ) ; } param typedData param signature returns export function recoverEIP712TypedDataSignerRsv ( typedData , signature ) { return recoverEIP712TypedDataSigner ( typedData , signature , parseSignatureAsRsv ) ; } param typedData param signature returns export function recoverEIP712TypedDataSignerVrs ( typedData , signature ) { return recoverEIP712TypedDataSigner ( typedData , signature , parseSignatureAsVrs ) ; } param typedData param signature param signer returns export function verifyEIP712TypedDataSigner ( typedData , signature , signer ) { for ( const recover of [ recoverEIP712TypedDataSignerVrs , recoverEIP712TypedDataSignerRsv ] ) { try { if ( eqAddress ( recover ( typedData , signature ) , signer ) ) { return true ; } } catch ( e ) { } } return false ; } export function guessSigner ( message , signature ) { const messageHash = hashMessageWithPrefix ( message ) ; const { r , s , v } = parseSignatureAsRsv ( signature . slice ( 2 ) ) ; const publicKey = ethjsutil . ecrecover ( ethjsutil . toBuffer ( messageHash ) , v , ethjsutil . toBuffer ( r ) , ethjsutil . toBuffer ( s ) ) ; return ethjsutil . bufferToHex ( ethjsutil . pubToAddress ( publicKey ) ) ; } function parseSignatureAsVrs ( signature ) { let v = parseInt ( signature . slice ( 0 , 2 ) , 16 ) ; const r = `0x${ signature . slice ( 2 , 66 ) }` ; const s = `0x${ signature . slice ( 66 , 130 ) }` ; if ( v < 27 ) { v += 27 ; } return { v , r , s } ; } function parseSignatureAsRsv ( signature ) { const r = `0x${ signature . slice ( 0 , 64 ) }` ; const s = `0x${ signature . slice ( 64 , 128 ) }` ; let v = parseInt ( signature . slice ( 128 , 130 ) , 16 ) ; if ( v < 27 ) { v += 27 ; } return { r , s , v } ; } function isValidSignature ( signer , message , v , r , s ) { try { const publicKey = ethjsutil . ecrecover ( ethjsutil . toBuffer ( message ) , v , ethjsutil . toBuffer ( r ) , ethjsutil . toBuffer ( s ) ) ; const retrievedAddress = ethjsutil . bufferToHex ( ethjsutil . pubToAddress ( publicKey ) ) ; return eqAddress ( retrievedAddress , signer ) ; } catch ( err ) { return false ; } } export const SignatureUtils = { NativeSigner , LocalSigner , signMessage , signMessageWithoutPrefix , parseSignature , parseSignatureWithoutPrefix , serializeSignature , recoverEIP712TypedDataSignerRsv , recoverEIP712TypedDataSignerVrs , verifyEIP712TypedDataSigner } ;