I was asked on Twitter for more details on my solution for verifying the authenticity of a message signed with an Ethereum private key. This was discussed in this blog post which provides an overview of what I was trying to do and how i conceptually achieved it. It is worth reading that post first for context.

Signing a message (Javascript)

We start off by signing a message. In this case I am going to use Javascript and the ethereumjs-util library.

There is a Stack Exchange question on this topic too: How can I sign a piece of data with the private key of an Ethereum address?

You can hash a message using hashPersonalMessage.

//Hash a known message
const message           = "Known message";
const messageBuffer     = Buffer.from(message);
const messageHashBuffer = utils.hashPersonalMessage(messageBuffer);

You can sign this message with a private key and the ecsign method.

const privateKeyBuffer  = new Buffer(privateKey, 'hex');
const signature = utils.ecsign(messageHashBuffer, privateKeyBuffer);

The signature contains the r, s, and v values:

signature.r.toString('hex'),
signature.s.toString('hex'),
signature.v

For further explanation of these values it is worth reading this post ECDSA: (v, r, s), what is v?.

Verifying a message (PHP)

For this I used the Signature class from the CryptoCurrencyPHP library.

pack, unpack, and hex2bin are outlined in the original post. The GMP functions I found in the Signature class and then read up on. They are for doing complex maths.

$messageHex       = $hashedMessageHex;
$messageByteArray = unpack('C*', hex2bin($messageHex));
$messageGmp       = gmp_init("0x" . $messageHex);

$r = $rHex;		//hex string without 0x
$s = $sHex; 	//hex string without 0x
$v = $vValue; 	//27 or 28

//with hex2bin it gives the same byte array as the javascript
$rByteArray = unpack('C*', hex2bin($r));
$sByteArray = unpack('C*', hex2bin($s));

$rGmp = gmp_init("0x" . $r);
$sGmp = gmp_init("0x" . $s);

$signature = array_merge($rByteArray, $sByteArray);

$recovery = $v - 27;
if ($recovery !== 0 && $recovery !== 1) {

    throw new Exception('Invalid signature v value');
}

$publicKey = Signature::recoverPublicKey($rGmp, $sGmp, $messageGmp, $recovery);

$publicKeyString = $publicKey["x"] . $publicKey["y"];

$match = $publicKeyString == $knownPublicKeyHex;

As mentioned in the initial post, a lot of this was trial and error. Learning my doing, and playing until I got the answers that I expected.

Elliptical curve cryptography is inherently hard. Thanks to some awesome opens source library developers producing this was significantly easier than it good have been.

Also worth noting that as of yet no-one can really agree on a standard for Ethereum message signing.

Enjoy playing.