Posts

Changing Recovery Account with dsteem API: Possible Precursor to Hardware Wallet

avatar of @edicted
25
@edicted
·
0 views
·
7 min read

I am very nervous about the security of my account right now. @steem (Steemit Inc) can not be trusted to recover my account should my owner/master key get compromised.

If my owner/master key gets stolen right now, that's it, I lose everything.
The keys for both platforms are the same, so I would lose both.

The simple solution is to change the recovery account asap. However, to change the recovery account one ironically has to sign with their owner key. I maintain that this is a foolish way to handle account security.

It already takes 30 days for recovery accounts to officially migrate. If someone hacks my active key and tries to change the recovery account, I have more than enough time to fix the issue. Active key security should be the only requirement for changing the recovery account. I'm very annoyed right now.

To the point that I'm even powering down my account just so I won't be risking so many coins in the process of changing my recovery account. This wouldn't be a big deal if I knew how to sign transactions with my private key offline and then broadcast the public transaction later on another machine. That is simply not the case. I have no idea how to do this or even if it is possible with the current tech.

Would I trade all my coins for my reputation?

An interesting aside here, if I could choose between losing all my coins and keeping my account vs losing my account and keeping all my coins, what would I do? I really have no idea. Like @theycallmedan has stated before:

You can't buy time.

The reputation and social network I've garnered here can't be bought with dollars. It is possible the the USD value of my account today could be generated in a couple weeks just from a dozen blog posts during an epic bull run the likes of which we saw during 2017. Food for thought.

I don't have the owner key for my account.

Yep. I never wrote down the owner key. I never understood the point of it until now. Turns out, back in day the frontends I was using were actually using my master key to generate the other keys in the background and using those keys to sign transactions. The same frontends of today don't allow this because it's a pretty major security issue.

So basically not only do I need to change my recovery account, I also need to generate my Owner key so I can sign that transaction in the first place. Anyone who does this on a frontend like PeakD or steemworld is trusting the centralized owner of said websites. Not saying they aren't trustworthy, only that we're supposed to be building trustless contracts. When it comes to a situation as serious as this frontends should not be the default option.

Here's where I'm at so far:

So I was looking at the dsteem API wondering how I was going to broadcast the transaction myself. The majority of the things I've done with dsteem have involved either the BroadcastAPI or the DatabaseAPI. At first I thought I'd be able to use the DatabaseAPI call() function to get this done, but then I realized that's not going to work because database functions don't get signed with a key, it's just a query from the node you're connected to.

When I checked BroadcastAPI it wasn't there either. This is a good example of how poor our documentation is. There are very few examples and sometimes it can take forever just to figure out what the syntax of certain statements are supposed to look like. This is especially true for me personally because I am not running a node or using Node.JS, so a lot of the examples that do exist do not apply to me because I'm trying to do everything using pure JavaScript and basic HTML.


So I scrolled down and found what I was looking for.


However, what I see is a bit confusing.

It appears as though this operation is a "generic operation", and I've never broadcasted a generic operation before.

Apparently there are a lot of random generic operations. It appears that a generic operation is an array with 2 parts. The first part (0) is a string that tells the platform which generic operation is being called (in this case 'change_recovery_account'). The second part is an object with attributes. 'change_recovery_account' has three attributes:

  • account_to_recover
  • extensions
  • new_recovery_account

To confirm all this, I took a look away from dsteem documentation and into Hive documentation.

I found what I was looking for.


But I still didn't know how to broadcast a generic operation!

So I looked back into the Broastcast API and found this:

Hey now! This looks promising!

It appears as though sendOperations() can broadcast multiple operations at the same time. Pretty cool... because I was actually wondering how it was possible to send more than one operation on a single block. This is it. However, in this case I'm just sending one operation so I'll just use an array of length one... which is redundant but I've all ready done so much work for so little payoff I don't give a fuck.

So yesterday I wrote a script:

<script src="https://unpkg.com/dsteem@^0.10.0/dist/dsteem.js"></script> 

Account: 
<input type="text" id="account" value="smartasscards"><br> 
Owner Key: 
<input type="password" id="key_string" 
value=''><br> 
New recovery account: 
<input type="text" id="recovery" value='v4vapid'><br> 
<input type="button" id="braodcast" value='Braodcast Transaction'><br> 

<p id='confirmation'> Confirmation displayed here. </p> 

<script> 

let client = new dsteem.Client('https://anyx.io') 

function setRecovery(){ 
    let account = document.getElementById('account').value 
    let recovery = document.getElementById('recovery').value 
    let key_string = document.getElementById('key_string').value 
    let key = dsteem.PrivateKey.fromString(key_string) 
    let obj = { 
        account_to_recover: account, 
        new_recovery_account: recovery, 
        extensions: [] 
    } 
    let op1 = ['change_recovery_account', obj] 
    client.broadcast.sendOperations([op1], key).then(function(result){ 
        document.getElementById('confirmation').innerHTML = 
                account + ' changed recovery account to: ' + recovery 
    }) 
} 

document.getElementById("braodcast").onclick = setRecovery; 

</script> 

And I was actually really fucking surprised that I got it working so quickly considering how much trouble I've had with stuff like this in the past.

I must be getting better at reading the documentation.

In any case, you can copy/paste this code into a text file and save the extension as .html

You should be able to open the file and it automatically pops up in your default web browser.
You can then enter the three requirements (your account, new recovery account, owner key) and hit the broastcast button.

"Why would I do it this way?"

Well, you don't have to trust a frontend with your owner key.

<script src="https://unpkg.com/dsteem@^0.10.0/dist/dsteem.js"></script> 

This code imports dsteem directly to your computer, which allows you to sign the transaction locally on your machine. Once the transaction is signed the client connects to @anyx's server https://anyx.io and this server broadcasts it to the network.

 let client = new dsteem.Client('https://anyx.io') 

If you wanted to change your recovery account on Steem you'd connect to a Steem node instead (like 'https://api.steemit.com'

I just now tested the above code (pasted into test.html on my desktop). It works in Chrome and in Brave.

In fact, we can see that I've broadcasted the same transaction 6 times now, setting my alt account @smartasscards's recovery account to @v4vapid. Remember, it doesn't become official until 30 days after it's been broadcast.

Still not secure.

This takes the middleman frontend out of the equation (Peakd / Hive.io) but if your local machine was compromised (keylogger) your owner key would still be stolen. I may be doing some more work on this going forward (as most of us trust Peakd and Hive.io regardless and are more worried about basic operating system security.

This documentation looks 'promising'.

It appears as though you can create a transaction and sign a transaction without receiving a "promise" in return. This is good because receiving a promise would imply that you connected to an outside server and got a response in the form of said promise.

Even more promising!

Once you create the signed transaction you can send() it (aka broadcast) to the network to be confirmed by the witnesses.

So what?

Well, as we can see, the send() operation doesn't require a private key, because we already used the private key to sign in the sign() function. This implies that it is possible for us to sign a transaction with an offline device, port the public signed transaction onto an online device, and use the online device to broadcast the transaction.

This is effectively the precursor to a hardware wallet.

I have already described such a vision in great detail:

Steem Airgap Hardware Wallet

I guess I need to buy myself a Raspberry Pi and get to work!

At first, for simplicity, I would simply port the public transaction from an offline Raspberry Pi to my main computer with a flash drive. However, the ultimate goal is to turn the public transaction into a QR code that phones can scan with the camera, broadcasting the public transaction through a completely unhackable airgap.

I feel like I don't have to explain why something like this would be super badass for our platform. It would allow all users on the network to rest easy knowing it is pretty much 100% impossible for their master/owner key to get compromised by a remote hacker, as they were never exposed to the Internet at any point in time.

Conclusion

Account security is the #1 issue for many users.
At a moment like now when recovery accounts are at risk, this is the time to act.
I have a lot of work to do, but hopefully it will be worth it.

Would you bet your life on the 'fact' that Ned and/or the NSA and/or Justin Sun doesn't have access to your keys?

I wouldn't. I technically generated my keys on Steemit.com, after all.