0

I have a project where users will be calling my contract from the web. I want to ensure they call the contract with exactly the parameters I give them. My strategy was to hash the combo of parameters, sign the hash with my webserver's private key and validate both the hash and signature in the contract.

This is essentially the authentication scheme described here: https://security.stackexchange.com/questions/82716/whats-to-stop-someone-from-just-mitming-a-checksum

What I can't figure out is how to validate the signature on-chain. Based on other discussions here, it seems like this is not possible because it costs too much gas. How do people normally handle this?

  • If your contract is well done, you should not have to do that. Moreover, what do you mean with "correct parameter"? – Florian Castelain Jan 14 '18 at 3:20
  • If a contract is triggered from the web, anyone can pass whatever parameters they want. A lot of time this is the intended design. For example, "I want to donate {x} to this person" and the user fills in x. However if the contract is triggered from the site with a param that shouldn't be changed, how can it be guaranteed that the param wasn't modified? For example, maybe the user is calling a generic "send {x} to person {y}" contract and the website wants the contract to verify that {y} wasn't changed before executing. – Luke Schoen Jan 14 '18 at 4:08
  • I don't hink there is a way to do that. But if someone changes the js code to donate to the wrong one, it is there problem. There is no advantage doing that from user perspective. If I want to donate to my friend, I won't try to change to code and donate to a random guy. – Florian Castelain Jan 14 '18 at 4:39
  • There are a few ways to do this, but what you described seems fine. I don't think that validating a signature on chain is particularly expensive. ecrecover is available, and from a quick test, I think it uses about 5000 gas, which is quite low. – user19510 Jan 14 '18 at 5:27
  • To use ecrecover can I only sign using an ethereum wallet private key? If so, any idea how I sign something from c# using an eth wallet's private key? – Luke Schoen Jan 14 '18 at 6:13
0

This is how you sign a message with a wallet's private key in C# and validate the signature in Solidity.

C# to sign:

using Nethereum.Signer;

[TestClass]
public class SignatureValidation
{
    [TestMethod]
    public void EthSigningVRS()
    {
        var msgSigner = new EthereumMessageSigner();
        string messageToSign = "abc";

        // Pad nulls to the right to match what will happen in Solidity when passing in "abc" to bytes32
        byte[] paddedPlain = ASCII.GetBytes(messageToSign.PadRight(32, '\0')); 

        var hashed = msgSigner.Hash(paddedPlain);
        string signed = msgSigner.HashAndSign(paddedPlain, PrivateKey);

        // -------- Demo of how to get these values from the signed string so they can be passed to the contract ---------
        // Parse v from hex to int
        string vString = signed.Substring(130); // Get the last 2 characters
        int v = int.Parse(vString, NumberStyles.HexNumber);
        v.Should().Be(28);

        var r = signed.Substring(0, 66);
        r.Should().Be("0xc39f1ff05a65b2fe1fc2729e4dc75555bf0a569dd25f6a2678cca207359c1dab");
        var s = "0x" + signed.Substring(66, 64);
        s.Should().Be("0x23b0f955b1ee306ae85142308aee5f397e5bb661c958f7694a89618cc289a1ff");
        // -------- End demo ---------

        var recovered = msgSigner.EcRecover(hashed, signed); // How to EcRecover in Nethereum
        recovered.Should().Be("0x14fdD82075C7Dd6Faf47AbFf47F2f031C8155ac6"); // This is the wallet's public key
    }
}

In solidity:

function addressOfSigned(
    bytes32 paddedOrig, 
    bytes32 managedHash, 
    uint8 v, 
    bytes32 r, 
    bytes32 s) public pure returns(bool) {

    // Validate that managedHash matches local hash
    bytes32 hashedOrig = keccak256(paddedOrig);        
    for (uint256 i=0; i < 32; i++) {
        if (hashedOrig[i] != managedHash[i]) {
            return false;
        }
    }

    bytes memory prefix = "\x19Ethereum Signed Message:\n32";
    bytes32 prefixedHash = keccak256(prefix, hashedOrig);

    // Note that the first param is essentially double-hashed.
    address recovered = ecrecover(prefixedHash, v, r, s);

    return recovered == address(0x14fdD82075C7Dd6Faf47AbFf47F2f031C8155ac6);     
}

Validation:

PS D:\Enlistments\Dapp\hash> truffle console
truffle(development)> HashCheck.deployed().then(function(x){f=x;});
undefined
truffle(development)> f.addressOfSigned("abc", "0x9b8075e3114a237714bcee811cbb0337de6d1423cb2947266772aae5963ec8e5", 28,
 "0xc39f1ff05a65b2fe1fc2729e4dc75555bf0a569dd25f6a2678cca207359c1dab", "0x23b0f955b1ee306ae85142308aee5f397e5bb661c958f7
694a89618cc289a1ff")
true

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.