0

For my use case I need to verify a ECDSA signature from a signed Ethereum message.

From the front end, the message is first converted into a Sha3/keccac256 hash using web3.js with below method: https://web3js.readthedocs.io/en/v1.2.11/web3-utils.html#sha3

My message now becomes: 0xd9eba16ed0ecae432b71fe008c98cc872bb4cc214d3220a36f365326cf807d68

Then a signature is generated with a private key from the method web3.eth.personal.sig: https://web3js.readthedocs.io/en/v1.2.11/web3-eth-personal.html#id15

My signature becomes: 0x7f21a0fbf7bd236f0c76df69803ec39d50da8e84756886844538c7b913f2cdb93fdea40bf14f25166206416d8a0e3c3e05c59e07df254cf2f36d6b1edc8830d91c

Using the ECDSA algorithm I should be able to distract the public key from the message hash and signature, my public key is:

0xe6712f6f5053277db9c37c5ada15559e9f3084db

My requirement it to distract this public key using Apex, I found there is a verify method on the Crypto class that can verify ECDSA-SHA256 signatures: https://developer.salesforce.com/docs/atlas.en-us.apexref.meta/apexref/apex_classes_restful_crypto.htm#apex_System_Crypto_generateDigest

I tried to verify my signature with the message hash but I get an error: 'System.SecurityException: Invalid Crypto Key'

Also tried to convert the key and message / signature to blob with EncodingUtil.base64Decode.

The code I used:

Blob message = Blob.valueOf('0xd9eba16ed0ecae432b71fe008c98cc872bb4cc214d3220a36f365326cf807d68');
Blob signature = Blob.valueOf('0x7f21a0fbf7bd236f0c76df69803ec39d50da8e84756886844538c7b913f2cdb93fdea40bf14f25166206416d8a0e3c3e05c59e07df254cf2f36d6b1edc8830d91c');
Blob publicKey = Blob.valueOf('0xe6712f6f5053277db9c37c5ada15559e9f3084db');
System.debug(Crypto.verify('ECDSA-SHA256', message, signature, publicKey));

I think the Blob format of my signature / message might not be correct and should use some sort of Hash Blob type and maybe this is not supported with this method, anyone has any suggestions how I could verify this in Apex?

Here is an article explaining more about the ECDSA signature Ethereum uses: https://medium.com/mycrypto/the-magic-of-digital-signatures-on-ethereum-98fe184dc9c7

3
  • You have a typo: ECDSA-SHA2566 should be ECDSA-SHA256
    – identigral
    Mar 21, 2022 at 16:57
  • Thank you for spotting that @identigral, getting a step further! Now I am getting error: System.SecurityException: Invalid Crypto Key Also tried to convert the key and message / signature to blob with EncodingUtil.base64Decode.
    – 0xFlem
    Mar 21, 2022 at 20:20
  • SHA3 (and keccak256 as one of its flavors) is not supported by Apex's Crypto class. You can try implementing this in a Function and calling it from Apex.
    – identigral
    Mar 22, 2022 at 1:22

1 Answer 1

0

As @identigral mentioned I discovered this is indeed not possible unless you create a ECDSA decryption algorithm from scratch which supports keccak256.

Functions could also be an option, I found however another workaround as I could not include functions for this project.

I implemented a solidity smart contract and deployed it to the blockchain, I can call the smart contract with eth_call from the JSON RPC API and this does not cost any gas fees since it is not mutating any data in the contract.

I uploaded the source code on GitHub if anyone else needs this solution in the future: https://github.com/flemx/Solidity-Verifyer

Here is the code I used:

/**
 * @description       : Verify web3 signatures from signed messages and retrieve the public key
 * @author            : Damien Fleminks
 * @Co-Author         : Used code examples from:  https://solidity-by-example.org/signature/ 
 * @last modified on  : 03-23-2022
 * @last modified by  : Damien Fleminks
**/
pragma solidity ^0.8.0;

import "hardhat/console.sol";

contract VerifySig {

    /**
        Get wallet address by the message and 
     */
    function recoverSigner(string memory _message, bytes memory _signature)
        public
        pure
        returns (address)
    {
        (bytes32 r, bytes32 s, uint8 v) = splitSignature(_signature);

        return ecrecover(hashMessage(_message), v, r, s);
    }

    function splitSignature(bytes memory sig)
        public
        pure
        returns (
            bytes32 r,
            bytes32 s,
            uint8 v
        )
    {
        require(sig.length == 65, "invalid signature length");

        assembly {
            /*
            First 32 bytes stores the length of the signature

            add(sig, 32) = pointer of sig + 32
            effectively, skips first 32 bytes of signature

            mload(p) loads next 32 bytes starting at the memory address p into memory
            */
            // first 32 bytes, after the length prefix
            r := mload(add(sig, 32))
            // second 32 bytes
            s := mload(add(sig, 64))
            // final byte (first byte of the next 32 bytes)
            v := byte(0, mload(add(sig, 96)))
        }

    }

    function hashMessage(string memory _message)
        private
        pure
        returns (bytes32)
    {
        /** Return keccak256 hashed ethereum compatible message sha, assuming 32 characters _message string  **/
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32",_message));
    }
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .