Is it possible to do an ecrecover() of an Ethereum address in a Cadence contract?

Hi All,
I am wanting to bind an Ethereum account using a resource. To do this I would like to sign some data using and Ethereum EOA.

I then would like to validate the public key address that the data signed with the private key belongs to.

Some pitfalls might be the use of keccak256 for hash the public key address. I could be wrong about that though. Thank you.

Hi there,

Cadence contains a crypto contract, which allows verifying signatures of different kinds (various signature algorithms and hash algorithms).

The Cadence Language Reference contains an example for how to use it, see https://docs.onflow.org/cadence/language/crypto/.

Let me know if you have any further questions

Note that isValid prefixes the message to verify with certain โ€œtagโ€, Crypto.domainSeparationTagUser, so that the verification succeeds only if the signer has prefixed the message with that same tag when they signed.

Hi,
To complement turbolentโ€™s answer, isValid requires knowing the full public key (and not just the Ethereum address which is the hash of the public key). It verifies the signature against that public key and returns a boolean.
This is a different approach than verifying signatures using ecrecover.

what will be the procedure if we want like ecrecover function.
We need a function like ecrecover in our cadence contract which gives us the address of the signer whose signed the message.
Thank you.

1 Like

In flow, account addresses are not derived from public keys, so this kind of process is not really possible.

1 Like

okay, thank you for your reply we find a solution what we want.

Is there any example of leveraging the js-sdk and Crypto contract to do message signing and signature verification? The docs actually confuse meโ€ฆ Thank you

@qvvg Do you know of an example for this? Thanks!

@dryrunner Maybe this can help: It is working like that it takes your transaction hash and gets the results from your transaction hash, then concatenate that result and sign that message using own private key and then call isValid function of crypto contract and give you the result whether it is true or false.

import fcl from โ€œ@onflow/fclโ€
import t from โ€˜@onflow/typesโ€™;
import { SHA3 } from โ€˜sha3โ€™;
import express from โ€˜expressโ€™;
import config from โ€œ./config.jsโ€
import elliptic from โ€˜ellipticโ€™;
const EdDSA = elliptic.ec;
const ec = new EdDSA(โ€˜p256โ€™);

const app = express();
const port = 5000;
//===Take Three global variables to store values and then concat them to make a string to sign that string
let amount;
let trxhash;

const verify = async (txhash) => {
let msg;
let tx = await fcl.send([
fcl.getTransaction(txhash),

]).then(fcl.decode);
// console.log("transaction", tx);
// console.log("trx", tx.script);

//====Assigns the values got from the transaction hash to the globaly declared variables
let trxhash = txhash;
let amount = tx.args[0].value;



console.log("trx amount", tx.args[0].value);
console.log("trx address", tx.args[1].value);
//==== concatenate the message for signing
msg = amount.concat(ethAddress).concat(trxhash);
//=====convert the different data types toString
// const mesStr = message.toString();
return msg

}

const toBytesWithTag = (str) => {
// Tag: โ€˜464c4f572d56302e302d75736572000000000000000000000000000000000000โ€™
// ref: https://github.com/onflow/flow-go-sdk/blob/9bb50d/sign.go
const tagBytes = Buffer.alloc(32);
Buffer.from(โ€˜FLOW-V0.0-userโ€™).copy(tagBytes);
const strBytes = Buffer.from(str);
return Buffer.concat([tagBytes, strBytes]);
}

const hashMsg = (msg) => {
const sha = new SHA3(256);
return sha.update(toBytesWithTag(msg)).digest();
};

const sign = (privKey, msg) => {
const key = ec.keyFromPrivate(Buffer.from(privKey, โ€˜hexโ€™));
const sig = key.sign(hashMsg(msg));
const n = 32;
const r = sig.r.toArrayLike(Buffer, โ€˜beโ€™, n);
const s = sig.s.toArrayLike(Buffer, โ€˜beโ€™, n);
return Buffer.concat([r, s]).toString(โ€˜hexโ€™);
};

const toHexStr = (str) => {
return Buffer.from(str).toString(โ€˜hexโ€™);
}

const verifySig = async (pubKey, msg, sig) => {

fcl.config().put('accessNode.api', 'http://127.0.0.1:8000');
fcl.config().put("accessNode.api", "http://localhost:8000")
fcl.config().put("accessNode.api", "https://access-testnet.onflow.org");


//===Read the scripts data file avaialbe in the directory===
const script = fs.readFileSync(path.join('./scripts/verify_sig.cdc'), 'utf8');
const response = await fcl.send([
    fcl.script`${script}`,
    fcl.args([  ])
]);
return await fcl.decode(response);

}

const main = async () => {
//==call verify function and pass the transaction hash to get the required data

verify("your transaction hash").then(async (msg) => {
    console.log(msg);
    const pubKey = 'your public key';
    const privKey = 'yout private key';

    const sig = sign(privKey, msg);
    const isValid = await verifySig(pubKey, msg, sig);
    console.log({ pubKey, privKey, msg, sig, isValid });
});

};

main().catch(e => console.error(e));