# Signing Messages

Signing messages can be used for various method of authentication and off-chain operations, which can be put on-chain if necessary.

## Signing a String Message

By allowing a user to sign a string, which can be verified on-chain, interesting forms of authentication on-chain can be achieved. This is a quick example of how an arbitrary string can be signed by a private key, and verified on-chain. The Contract can be called by another Contract, for example, before unlocking functionality by the caller.

Solidity Contract

    contract Verifier {
        // Returns the address that signed a given string message
        function verifyString(string message, uint8 v, bytes32 r,
                     bytes32 s) public pure returns (address signer) {

            // The message header; we will fill in the length next
            string memory header = "\x19Ethereum Signed Message:\n000000";

            uint256 lengthOffset;
            uint256 length;
            assembly {
                // The first word of a string is its length
                length := mload(message)
                // The beginning of the base-10 message length in the prefix
                lengthOffset := add(header, 57)
            }

            // Maximum length we support
            require(length <= 999999);

            // The length of the message's length in base-10
            uint256 lengthLength = 0;

            // The divisor to get the next left-most message length digit
            uint256 divisor = 100000;

            // Move one digit of the message length to the right at a time
            while (divisor != 0) {

                // The place value at the divisor
                uint256 digit = length / divisor;
                if (digit == 0) {
                    // Skip leading zeros
                    if (lengthLength == 0) {
                        divisor /= 10;
                        continue;
                    }
                }

                // Found a non-zero digit or non-leading zero digit
                lengthLength++;

                // Remove this digit from the message length's current value
                length -= digit * divisor;

                // Shift our base-10 divisor over
                divisor /= 10;

                // Convert the digit to its ASCII representation (man ascii)
                digit += 0x30;
                // Move to the next character and write the digit
                lengthOffset++;

                assembly {
                    mstore8(lengthOffset, digit)
                }
            }

            // The null string requires exactly 1 zero (unskip 1 leading 0)
            if (lengthLength == 0) {
                lengthLength = 1 + 0x19 + 1;
            } else {
                lengthLength += 1 + 0x19;
            }

            // Truncate the tailing zeros from the header
            assembly {
                mstore(header, lengthLength)
            }

            // Perform the elliptic curve recover operation
            bytes32 check = keccak256(header, message);

            return ecrecover(check, v, r, s);
        }
    }

JavaScript

    let abi = [
        "function verifyString(string, uint8, bytes32, bytes32) public pure returns (address)"
    ];

    let provider = ethers.getDefaultProvider('ropsten');

    // Create a wallet to sign the message with
    let privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
    let wallet = new ethers.Wallet(privateKey);

    console.log(wallet.address);
    // "0x14791697260E4c9A71f18484C9f997B308e59325"

    let contractAddress = '0x80F85dA065115F576F1fbe5E14285dA51ea39260';
    let contract = new Contract(contractAddress, abi, provider);

    let message = "Hello World";

    // Sign the string message
    let flatSig = await wallet.signMessage(message);

    // For Solidity, we need the expanded-format of a signature
    let sig = ethers.utils.splitSignature(flatSig);

    // Call the verifyString function
    let recovered = await contract.verifyString(message, sig.v, sig.r, sig.s);

    console.log(recovered);
    // "0x14791697260E4c9A71f18484C9f997B308e59325"

## Signing a Digest Hash

Signing a digest can be far more space efficient than signing an arbitrary string (as you probably notice when comparing the length of the Solidity source code), however, with this method, many Wallet UI would not be able to fully inform the user what they are about to sign, so this method should only be used in quite specific cases, such as in custom Wallet applications.

Solidity Contract

    contract Verifier {
        function verifyHash(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public pure
                     returns (address signer) {

            bytes32 messageDigest = keccak256("\x19Ethereum Signed Message:\n32", hash);

            return ecrecover(messageDigest, v, r, s);
        }
    }

JavaScript

    let abi = [
        "function verifyHash(bytes32, uint8, bytes32, bytes32) public pure returns (address)"
    ];

    let provider = ethers.getDefaultProvider('ropsten');

    // Create a wallet to sign the hash with
    let privateKey = '0x0123456789012345678901234567890123456789012345678901234567890123';
    let wallet = new ethers.Wallet(privateKey);

    console.log(wallet.address);
    // "0x14791697260E4c9A71f18484C9f997B308e59325"

    let contractAddress = '0x80F85dA065115F576F1fbe5E14285dA51ea39260';
    let contract = new ethers.Contract(contractAddress, abi, provider);

    // The hash we wish to sign and verify
    let messageHash = ethers.utils.id("Hello World");

    // Note: messageHash is a string, that is 66-bytes long, to sign the
    //       binary value, we must convert it to the 32 byte Array that
    //       the string represents
    //
    // i.e.
    //   // 66-byte string
    //   "0x592fa743889fc7f92ac2a37bb1f5ba1daf2a5c84741ca0e0061d243a2e6707ba"
    //
    //   ... vs ...
    //
    //  // 32 entry Uint8Array
    //  [ 89, 47, 167, 67, 136, 159, 199, 249, 42, 194, 163,
    //    123, 177, 245, 186, 29, 175, 42, 92, 132, 116, 28,
    //    160, 224, 6, 29, 36, 58, 46, 103, 7, 186]

    let messageHashBytes = ethers.utils.arrayify(messageHash)

    // Sign the binary data
    let flatSig = await wallet.signMessage(messageHashBytes);

    // For Solidity, we need the expanded-format of a signature
    let sig = ethers.utils.splitSignature(flatSig);

    // Call the verifyHash function
    let recovered = await contract.verifyHash(messageHash, sig.v, sig.r, sig.s);

    console.log(recovered);
    // "0x14791697260E4c9A71f18484C9f997B308e59325"