Wow! Signing a Message on Opera on Android With a Private Key
By: Ronnie Paskin
Tags: Solidity, Tutorial, Android, Ethereum, JavaScript

After figuring out how to use Opera on Android with Ethereum, I wanted to know if I can actually sign a message using a private key. Can we do it? What do you think?

To figure it out, let’s first use our extremely complex ™️ page:

<!DOCTYPE html>

<html>

<head>

<title>Test Opera</title>

<script src="//cdn.jsdelivr.net/gh/ethereum/web3.js@1.0.0-beta.34/dist/web3.min.js" type="text/javascript"></script>

</head>

<body>Working!</body>

</html>

We deploy that to a server, open Opera on Android, connect to it with a USB cable and via adb on a Windows Powershell

.\adb.exe forward tcp:9222 localabstract:com.opera.browser

.devtools

and use the console by going to chrome:\\inspect on Opera on Windows. Looking good!

As we’ve seen, we can now play with the console on the right-hand and it will run on the Android Browser. Handy. Let’s get to it.

First, we need to connect to a web3js websocket provider:

We can check if we access the current account, we should already have one.

Dope. But can we create a new account? Let’s try:

Whoa, that was easy 🙌! We have a web3js account object.

Never, ever, publish a private key publicly like I just did above! This is a test. No one should will be using this private key unless they don’t know what they’re doing. Or they’re writing this.

So, at this point, for some reason my adb connection is flaky. Maybe it’s my USB cable, I don’t know. To reset the connection just kill the adb server (.\adb.exe kill-server), disconnect and reconnect the cable, and start it again, it should be ok. Except that you have to restart your debugging session… So my account object is no more.

We have two options, we can do this the easy way (create another account object) or the hard way (recreate the account since we have the private key). Of course, let’s do things the hard way and learn. Yay for teachers 👨‍🏫!

First, instead of connection to web3js again, let’s add this to our source code. Man, this is some complicated code (hint: being sarcastic here!).

<!DOCTYPE html>

<html>

<head>

<title>Test Opera</title>

<script src="//cdn.jsdelivr.net/gh/ethereum/web3.js@1.0.0-beta.34/dist/web3.min.js" type="text/javascript"></script>

<script type="text/javascript">

const web3 = new Web3(ethereum);

</script>

</head>

<body>

Working!

</body>

</html>

Now we have web3 already initialized, we don’t need to redo this every time the page reloads.

Ok, let’s recreate the account by using privateKeyToAccount:

Yeah, I had to type the private key we had generated before, it wasn’t saved anywhere except that image. Fun facts: The private key is really just a random number (in hexadecimal).

Let’s check the public key it calculated. It starts with E4F4 and ends with 7af4, which is exactly what we had before! The reason is the public key is derived mathematically from the private key. Neat, huh? Also, we don’t really need to check the whole number, these two beginning bytes and two ending bytes should suffice. The odds of typing a random private key and getting matching 4 bytes are astronomically low! Seriously, if you manage that, drop me a message with the alternate private key and I’ll send you a gift 🎁.

So, I think that’s pretty darn cool. We used the same private key and got the public key back again. Now we have a new instance of the same account to play with, because math! What else now, but to pause for an ode to our muse: How beautiful art thou, Lady Mathematica, how beautiful art thou! thy eyes are doves’ eyes, besides what is hid within. Thy hair is as flocks of goats… Goats? Ok, this is getting weird. What is it with olden days and goats? Sheesh! Let’s move on.

Next step is, we want to sign a message. ECDSA (Elliptic Curve Digital Signature Algorithm) signing means to create a signed package proving that you’re the holder of the private key that corresponds to a known public key. We could sign for example an Ethereum transaction sending some ETH to someone, but let’s use our private key to sign an arbitrary string using the accounts.sign method. What string to use, I dost wonderst?

So we signed our message, using the sign method from our acct object. We could sign it with the private key directly too, by using the eth.accounts.sign method:

The signed package here is divided into three components: r, s and v, seen above. This is fairly complex, but all you really need to know is that by knowing the r, s, v values and the original message string, someone else could calculate who was it that signed this message, by using the recover method, like so:

So we have our original account 0xE4F4… Oh… F**k. Not really, what’s happening here?

Notice the recover method uses the messageHash, not the original message. The message hash is a keccak256 (SHA3, but not really SHA3 as it was implemented during a transition phase, it seems… anyhow) hash of the message. If you notice above, it said that in the output from the sign method, there was a messageHash property. Let’s do it the hard way™️ and reconstruct that message. We’ll try and use the soliditySha3 method.

Notice that doesn’t match the output of the sign method above (starts with 0xdea7a). WTF is happening here? Unfortunately, that’s not trivial. To make it easier on us, we can use a handy method to create this hash for us, hashMessage:

Oooh, it matches what we wanted! But why is that any different from the SHA3 we tried to use before…? Cool thing about open source projects like web3js is… they’re open source! We can actually look at what the code is doing behind the scenes😎. Let’s take a looksee 🕶 and see what the hashMessage method does.

What the heck is that? Here’s the thing. Remember how we said you could sign an Ethereum transaction sending some ETH to someone? Some nasty hacker could trick a user into unwittingly signing that sort of thing 😱. That would be bad. So when you’re signing an arbitrary message, it tacks on that 25 character (19 hex) prefix “ \u0019Ethereum Signed Message:

”, followed by the length of the message (in our case 150 characters, go ahead, count it), to the beginning of the string. That way the user can’t sign something he shouldn’t. We can see how this matches:

Let’s finally get back to verifying the signed r, s, v package:

We got the correct value now. Oh yeah!

We could even be on a completely different browser, or even programming language, the recovered account should match: