Morning Musings

I'm not ready to wake up yet...

Blockchain Identity

| Comments

I stumbled upon Christopher Ellis’ “World Citizenship” Blockchain ID concept and was immediately intrigued. His idea is to create proof of an individual’s existence by storing their PGP identity in the blockchain.

The trick is that the blockchain acts as an immutable database that includes full history tracking, meaning that a record can be proven to exist in some state at some time. These services are similar to this concept:

This post will detail how to create a Blockchain Identity.

Requirements

To begin, you will need:

  • GPG key with signing abilities
  • Bitcoin wallet with a small balance
  • Keybase.io account

Keybase.io is currently alpha, and you need an invite to get an account, but you can probably snag an invite from Reddit (that’s where I got mine).

I will be using Debian Linux for this tutorial. I recommend the following packages:

  • gpg : For signing files
  • electrum : For managing bitcoins
  • ruby : For running the scripts below

First you need to create a GPG key and an Electrum Bitcoin wallet. I won’t dive into the details of how to create these in this post. The reason we are using Electrum is because it has the ability to choose the address to send funds from, unlike some other wallets. We will need to know this sending address later.

You also need to get a small amount of Bitcoin into your Electrum wallet (at least 0.0003 BTC)

Gathering the Data

Time to collect the data. Open up a text editor and create a Yaml file named key.yml

First, lets get the GPG key details. Run the following, substituting your UID:

gpg --fingerprint jrruethe
pub   4096R/40B935FE 2014-06-14 [expires: 2015-06-14]
      Key fingerprint = 4F40 99F8 276B DBA5 475A  8446 4630 BEDC 40B9 35FE
uid                  Joseph Ruether <jrruethe@gmail.com>

Fill out your key.yml file like so:

Name: Joseph Ruether
Email: jrruethe@gmail.com
Type: RSA
Size: 4096
Created: 2014-06-14
Fingerprint: 4F40 99F8 276B DBA5 475A 8446 4630 BEDC 40B9 35FE

Next, go to your Electrum wallet and pick an address that contains a small amount of funds. You will need at least 0.0003 BTC. Add this address to your yaml file:

Address: 1LXWthRW8aqQSBddifWxwTDGPycT6Lom2Q

Go to Blockchain.info and find the latest block that has been added to the main chain. Add the block number and Merkle Root to your yaml file:

Block: 345546
Merkle Root: ea31869aa04e3608450b45068b257ee396a6d2f6724f96593cd8d2c7f30a39d9

At this point, your yaml file has all of your GPG key metadata, the address you will use to store the data in the blockchain, and a random number that proves this entry could not have been created earlier in time.

Preparing the Data

Now we need to put this data into a standard, machine readable format and sign it. First, use the following script to convert your yaml file into a normalized json file:

(yaml-to-normalized-json.rb) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#!/usr/bin/env ruby

# yaml-to-normalized-json.rb
# Copyright (C) 2015 Joe Ruether jrruethe@gmail.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

require 'yaml'
require 'json'

class String

   # Add a snakecase function to the String class
   def snakecase
      self.gsub(/[:\-]/, "").gsub(" ", "_").downcase
   end

   # Strip spaces and downcase
   def normalize
      self.gsub(/[: \-]/, "").downcase
   end

   # Check if a string is a hex value
   def is_hex?
      /^[0-9A-F]+$/i === self.normalize
   end
end

# Load the yaml file
h = YAML.load_file ARGV[0]

# Convert all keys to snakecase
h = Hash[h.map{|k, v| [k.snakecase, v] }]

# Normalize all hex values
h.each do |k, v|
   if v.is_a?(String) && v.is_hex?
      h[k] = v.normalize
   end
end

# Sort the keys
h = Hash[*h.sort.flatten]

# Pretty print
puts JSON.pretty_generate(h)

Run it like so:

./yaml-to-normalized-json.rb key.yml > key.json

Then use GPG to clearsign this file:

gpg --clearsign key.json

The result should look something like this:

(key.json.asc) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

{
  "address": "1LXWthRW8aqQSBddifWxwTDGPycT6Lom2Q",
  "block": 345546,
  "created": "2014-06-14",
  "email": "jrruethe@gmail.com",
  "fingerprint": "4f4099f8276bdba5475a84464630bedc40b935fe",
  "merkle_root": "ea31869aa04e3608450b45068b257ee396a6d2f6724f96593cd8d2c7f30a39d9",
  "name": "Joseph Ruether",
  "size": 4096,
  "type": "RSA"
}
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2

iQEcBAEBCAAGBQJU8dS0AAoJEB7MsgHDevApMCEH/3yFMcNErb92AtEdwShsvb1v
IVWxL4aTqrs8AD7CxHEUNhxcZ56C2AyFW/8HkOMSCpbq9wG4Jxk8pohIiyQKYfTl
TGGEYGELNDPgx/Pvlhb0jLBh8McnE8XdYseH2OXa3lfq4xOCqYKNiBOIcUmNZSar
9fk4i3/Z47oN1Nwg7NohsCkoqNo9JBKH3Dc3ec2mlLktl83TQ7OcOfBCazdOaHOR
+kk3WZb0wTyG+B6u5Kl8e0VJh+inDB83ol96hli/Ofhuvf29280VP3n784nDpLwY
vg6pukSc3STD3em4hYOYU7OuBydbRe27I5w//E6IwntPoOG+dahzNPJcpFfvD4A=
=/daq
-----END PGP SIGNATURE-----

Sending the Data to the Blockchain

Now that we have our signed data file, we want to send it to the blockchain. It is important to note that the blockchain will only contain the sha256 hash of your key.json.asc, therefore you need to keep the original data around for validation. Later on, we will create an identification card with a QR code containing the original data.

There is a clever trick we are going to use. If you look at the Technical Background of Bitcoin Addresses, you will see that the address is generated from the hash of the public key. We can replace that hash (at step 3) with the hash of our file before continuing to generate the address. By sending Bitcoins to the address we create, we store the file’s hash in the blockchain. Of course, those coins will be lost forever, because the private key for that address does not exist. That is why we only send a small amount.

Now, use the following script to obtain the Bitcoin address that is associated with the hash of your signed key.json:

(file-to-address.rb) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#!/usr/bin/env ruby

# file-to-address.rb
# Copyright (C) 2015 Joe Ruether jrruethe@gmail.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

require 'digest'

# Get the filename
filename = ARGV[0]

# Hash the file
hash = Digest::SHA256.hexdigest File.read filename

# Take only the first 20 bytes
hash = hash[0..39]

# Prepend 0x00 to the hash
hash = "00" + hash

# Calculate the checksum
checksum = Digest::SHA256.hexdigest [hash].pack("H*")
checksum = Digest::SHA256.hexdigest [checksum].pack("H*")

# Pull out the first 4 bytes
checksum = checksum[0..7]

def encode_base58(hex)
   int_val = hex.to_i(16)
   alpha = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
   base58_val, base = '', alpha.size
   while int_val > 0
      int_val, remainder = int_val.divmod(base)
      base58_val = alpha[remainder] + base58_val
   end
   leading_zero_bytes  = (hex.match(/^([0]+)/) ? $1 : '').size / 2
   ("1"*leading_zero_bytes) + base58_val
end

# Generate the address
address = encode_base58(hash + checksum)

puts address

Run it like this:

./file-to-address.rb key.json.asc
1BESV3iP1x1HAMDGhsYiQE3do6aiywGZ3K

For some basic sanity checks, visit this website: Base58 Decoder
Here you can validate that the Bitcoin address is legitimate, as well as decode the address to hex. Here is what I get:

00703BF01D7DF0A110C9B2CE1E8984F545831BFFAA5556057E

Next, get the sha256 hash of key.json.asc:

sha256sum key.json.asc 
703bf01d7df0a110c9b2ce1e8984f545831bffaa08042e78b470b3b1464faada  key.json.asc

Compare the two outputs to make sure they match:

00 703BF01D7DF0A110C9B2CE1E8984F545831BFFAA 5556057E
   703bf01d7df0a110c9b2ce1e8984f545831bffaa 08042e78b470b3b1464faada

All that is left to do is send some bitcoin to that address. Use Blockchain.info to capture the transaction details as proof of the file’s existence:

Transaction: 68cbd46b5b1b5ac4ce3369c04a0366da733182b6a7b329317aa1c87feb46f96d

Comments