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")
Best Answer
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 thepersonal_sign
JSON-RPC method. To match the behaviour of Ethers.js with the secp256k1 library, you have to:e
) usingKeccak256(message)
.e
, usingKeccak256("\x19Ethereum Signed Message:\n32" + e)
. We can hardcode a length of 32 here, because Keccak-256 hashes are always 32 bytes (= 256 bits) long.The signature
{r, s, v}
should be a valid Ethereum signature, wherer
ands
are the first 32 bytes and second 32 bytes respectively, andv
is the last byte (also called recovery ID). Note that you may have to add 27 to thev
parameter, to get av
of 27 or 28 (as used by Ethereum).