0

From the same private key in ethers.js and secp256k1 I get the same eth address. But if I sign a message I get a different (wrong) signature in ethers.js. What is wrong with my code?

import { createKeyPair, sign } from '@erebos/secp256k1'
import { pubKeyToAddress } from '@erebos/keccak256'
import { ethers, Wallet } from 'ethers';

async function testsig(message){

// ethers:

var ethersprivateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';

var etherswallet = new ethers.Wallet(ethersprivateKey);
console.log(etherswallet.address)
//0x14791697260E4c9A71f18484C9f997B308e59325

var s_ethers= await etherswallet.signMessage(message)
console.log( ethers.utils.arrayify(s_ethers) ) 
//int8Array(65) [166, 122, 63, 76, 122, 170, 175, 139, 106, 144, 156, 80, 245, 237, 145, 126, 24, 132, 20, 93, 157, 42, 208, 8, 158, 115, 23, 196, 253, 183, 157, 249, 8, 20, 82, 203, 147, 90, 139, 78, 10, 22, 10, 133, 199, 161, 228, 217, 234, 204, 197, 207, 9, 37, 149, 16, 151, 230, 159, 30, 25, 171, 24, 176, 27]




// secp256k1:

var secp256k1privateKey = '0123456789012345678901234567890123456789012345678901234567890123';

let secp256k1keyPair = createKeyPair(secp256k1privateKey);
const secp256k1user = pubKeyToAddress(secp256k1keyPair.getPublic('array'))
console.log(secp256k1user)
//0x14791697260e4c9a71f18484c9f997b308e59325


var s_secp256k1=sign(message, secp256k1keyPair)
console.log( s_secp256k1)
//(65) [93, 182, 197, 217, 33, 175, 143, 115, 43, 243, 207, 2, 160, 182, 26, 117, 218, 181, 239, 132, 22, 133, 182, 155, 86, 25, 151, 186, 76, 167, 48, 228, 23, 78, 253, 193, 86, 243, 164, 96, 149, 68, 209, 43, 122, 6, 186, 68, 147, 74, 110, 71, 175, 197, 222, 132, 208, 223, 168, 84, 46, 81, 141, 73, 1]
}
testsig("abc")
1

Ethers.js prefixes the signature with \x19Ethereum Signed Message:\n<message length> before signing it, and signs a hash of the message. This is equivalent to the personal_sign JSON-RPC method. To match the behaviour of Ethers.js with the secp256k1 library, you have to:

  1. Get a hash of the message (e) using Keccak256(message).
  2. Hash e, using Keccak256("\x19Ethereum Signed Message:\n32" + e). We can hardcode a length of 32 here, because Keccak-256 hashes are always 32 bytes (= 256 bits) long.
  3. Sign the resulting hash with a private key.

The signature {r, s, v} should be a valid Ethereum signature, where r and s are the first 32 bytes and second 32 bytes respectively, and v is the last byte (also called recovery ID). Note that you may have to add 27 to the v parameter, to get a v of 27 or 28 (as used by Ethereum).

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.