sealed-website

🔏 sealed.website / Blockchain based website signed verification

View on GitHub

🔏 Sealed website

A sealed website is a site that has been verified by a unique identifier that works as a seal. The seal is a Bitcoin wallet public address tied to a webpage, which can be verified by WHOIS, and then used to retrieved the latest checksum of the website.

How it works?

Generate a new Bitcoin wallet.

Use any Wallet generator that shows you the private key in Wallet Import Format (WIF). A simple to use is the Warp Wallet by Keybase.io

Warp Wallet

NOTE: Make sure to use your own passphrase.

Additionallty, you can generate one with the JavaScript bitcoinjs-lib library:

const bitcoin = require("bitcoinjs-lib")

// generate random keyPair
const keyPair = bitcoin.ECPair.makeRandom()
const address = keyPair.getAddress()
const wif = keyPair.toWIF()

Keep the WIF in a secure place, we will need it to sign transactions later.

Proceed to register a domain w/seal

Register a domain with the bitcoin public key address in the field “Address2”. Be careful of which domain provider you use, and some (Godaddy) will truncate the field to 30 characters, and 99% of Bitcoin wallets are 34 (33 without the 1).

Namecheap.com

sealed.website was registered with Namecheap.com.

NOTE: Although other fields can be used to register the seal, “Address2” is almost never used and probably the most suitable.

Add money to your wallet

The data we want to validate is going to be stored in transactions, so we need to have some money in the wallet in order to perform transactions to pay the bitcoin miner’s fee. Use any provider to add money to your newly generated bitcoin wallet.

Bittcoin receipt

Store the website checksum in a transaction

Retrieve the SHA-512 checksum of your website. You can easily do this from the command line with openssl:

$ curl https://jjperezaguinaga.com | openssl dgst -sha512

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4786    0  4786    0     0   9416      0 --:--:-- --:--:-- --:--:--  9439

d257206f08808bd0bc2a2e6320e47de3d5eb26f673e55c6a6bdc875de121c8e1257f5d518c3ce89f6bf56dd7e98fc41c261551a977d41b83d40327147f586826

NOTE: We will use the domain https://jjperezaguinaga.com for this example.

With the checksum in hexadecimal data, proceed to perform a transaction including this data. There are many ways to write data to the blockchain, one of them using the JavaScript package blockchain-anchor from Tierion and Blockchain nodes from Blockcypher.

var blockchainAnchor = require('blockchain-anchor');

var privateKeyWIF = '<YOUR_WIF>';
var anchorOptions = {
  blockchainServiceName: 'blockcypher', // optional, defaults to 'Any'
  blockcypherToken: '<YOUR_BLOCKCYPHERTOKEN>', // required if using 'blockcypher' service
  feeSatoshi: 10000 // optional, defaults to 10000
};

var anchor = new blockchainAnchor(privateKeyWIF, anchorOptions);
var hexData = "d257206f08808bd0bc2a2e6320e47de3d5eb26f673e55c6a6bdc875de121c8e1257f5d518c3ce89f6bf56dd7e98fc41c261551a977d41b83d40327147f586826"

anchor.embed(hexData, function (err, transactionId, rawTransaction) {
  if(err) {
    console.log('Err', err)
  } else {
    console.log('New transaction Id = ' + transactionId);
    console.log('Raw tx = ' + rawTransaction);
  }
});

On success, you can explore the transaction using any Blockchain explorer. In this case, we can verify the transaction here

Blockcypher confirmation

We can then verify the contents of a website based on the last transaction using the command line and jq.

$ curl https://api.blockcypher.com/v1/btc/main/addrs/18RRxeEvy9FNmBZhjjZPZMuBXwYYBu7adX/full\?limit\=50 | jq .txs[0].outputs[0].data_hex

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  4520    0  4520    0     0   3872      0 --:--:--  0:00:01 --:--:--  3876

"d257206f08808bd0bc2a2e6320e47de3d5eb26f673e55c6a6bdc875de121c8e1257f5d518c3ce89f6bf56dd7e98fc41c261551a977d41b83d40327147f586826"

The data should then match the equivalent Base64 value of the webpage. You can check that through JavaScript with the following code:

fetch('https://jjperezaguinaga.com')
    .then((response) => response.text())
    .then((text) => 
        crypto.subtle.digest('SHA-512', new TextEncoder().encode(text))
            .then((digestBuffer) => 
                console.log(btoa(Array.prototype.map.call(
                    (new Uint8Array(digestBuffer)), ch => 
                        String.fromCharCode(ch)).join('')))))

NOTE: Because we can verify this in JavaScript, we can then create Browser Extensions that perform this check everytime we browse a website.

ANEXUS A: Identifying incongruencies

If the command line sha-512 of your website doesn’t match the JavaScript sha-512 version of it, make sure your website is actually returning the same content. In the example, the stored sha-512 hex data was the following:

d257206f08808bd0bc2a2e6320e47de3d5eb26f673e55c6a6bdc875de121c8e1257f5d518c3ce89f6bf56dd7e98fc41c261551a977d41b83d40327147f586826

which translates to the following value in base64

0lcgbwiAi9C8Ki5jIOR949XrJvZz5Vxqa9yHXeEhyOElf11RjDzon2v1bdfpj8QcJhVRqXfUG4PUAycUf1hoJg==

However, when running the JavaScript equivalent function, I got the following sha-512 from the website:

EkKcIbDosn0gqnuNax6cq4ZmYzLv6f5gDJ5MvbLv7QwdpAiBHWBQvu9SryHibd35d2oHYwqAuytQUkgIazTUYQ==

This is because some CDN providers like Cloudflare will have Email Address Obfuscation. Each request the webpage will return a different value, rendering the entire validation process useless.

To verify this, you can compare two downloads of the same website and compare against them with diff and colordiff. Ideally, store them in hexadecimal value with hexdump so you can compare them and find the issue.

$ curl https://jjperezaguinaga.com > curl.web
$ vim fetch.web # value stored manually from JavaScript fetch query
$ hexdump curl.web > curl.hex
$ hexdump fetch.web > fetch.hex

$ diff curl.hex fetch.hex | colordiff
244,250c244,250
< 0000f30 72 6f 74 65 63 74 69 6f 6e 23 64 31 62 63 62 34
< 0000f40 39 31 62 62 62 62 61 31 62 34 61 33 62 34 61 62
< 0000f50 62 30 62 36 61 34 62 38 62 66 62 30 62 36 62 30
< 0000f60 66 66 62 32 62 65 62 63 65 65 61 32 61 34 62 33
< 0000f70 62 62 62 34 62 32 61 35 65 63 39 32 62 65 62 66
< 0000f80 61 35 62 30 62 32 61 35 66 34 65 33 65 31 38 33
< 0000f90 62 34 61 30 61 34 62 34 61 32 61 35 22 3e 45 6d
---
> 0000f30 72 6f 74 65 63 74 69 6f 6e 23 66 64 39 30 39 38
> 0000f40 62 64 39 37 39 37 38 64 39 38 38 66 39 38 38 37
> 0000f50 39 63 39 61 38 38 39 34 39 33 39 63 39 61 39 63
> 0000f60 64 33 39 65 39 32 39 30 63 32 38 65 38 38 39 66
> 0000f70 39 37 39 38 39 65 38 39 63 30 62 65 39 32 39 33
> 0000f80 38 39 39 63 39 65 38 39 64 38 63 66 63 64 61 66
> 0000f90 39 38 38 63 38 38 39 38 38 65 38 39 22 3e 45 6d

ANEXUS B: Inspiration

The concept of “seal” comes from the Symbolus used by the Romans to seal letters; probably used before then, the idea came when inspecting Orbis Pictus Latinus

Symbolus in Orbis Pictus Latinus

Using today’s cryptography techniques and blockchain technologies, we can create unique “seals” and use them to ensure our websites are ours, and that the content hasn’t been tampered with.