xgess / keybase_merkle_prover

dockerized, deployable python keybase chat bot that OpenTimestamps Keybase's merkle roots
MIT License
12 stars 1 forks source link

Keybase-OpenTimestamps Merkle Root Prover

currently running on keybase @kbhonest

What is this

a fun (for me) combo of a few of my favorite things. It is

To whom is this useful

Why

I really like that the Keybase security model includes pinning data to Bitcoin. It's an elegant solution to a very specific security issue (what if the Keybase servers collude with, for example, someone who has stolen your recently revoked device) that makes for a compelling, non-monetary use of the blockchain. Timestamping in general is interesting, but anchoring the security of an application platform to guarantee no one is missing any updates they need is just

chef kiss.

If my running bot is the only instance of anything posting these proofs, and if the Keybase servers know it's happening, then they could just withhold these messages from you, which would admittedly defeat the purpose. On the other hand, if multiple people were running it, they would have a harder time identifying all of them. Better yet, if you ran the bot inside a private team (i.e. all of the messages were sent to a channel that could only be read by team members), then the members of the team could check against the published proofs for their own private guarantee.

Verifying

If you want to peep my work (which would be super cool of you), I definitely recommend skimming my code to make sure it's doing what you think, especially all the PGP and hashing stuff (which is in merkle_root.py). Suggestions welcome. If you want to use the ots CLI to see these proofs in action, here's some shell commands to do that. If you have a Bitcoin node and Keybase running locally, then it would look something like this:

# fetch the contents of my most recent `VERIFIABLE` proof
keybase chat api -m '{"method": "read", "params": {"options": {"channel": {"name": "kbhonest", "public": true}, "pagination": {"num":20}}}}' | jq '.result.messages | map(.msg.content.edit.body) | map(select(. != null)) | map(fromjson | select(.status=="VERIFIABLE"))[0]' > msg.json

# pluck out and decode the data that was timestamped
cat msg.json | jq -r '.root.b64stamped' | base64 --decode > ./sighash.dat

# pluck out and decode the OTS data
cat msg.json | jq -r '.ots' | base64 --decode > ./sighash.dat.ots

# do the ots verification
ots verify ./sighash.dat.ots

If you aren't running Bitcoin on this machine and you don't want to wire up an RPC connection, you could do this instead of the default verify:

ots --no-bitcoin verify ./sig_hash.dat.ots
ots info ./sig_hash.dat.ots

And you'll see the Merkle path and block height.

Implementation

What it's doing

  1. Every SOME_TIME_INTERVAL (currently 20 minutes but I might change it without updating here), the running bot will fetch from Keybase the latest merkle root which comes with a bunch of other details: a seqno so this specific root can be ordered and fetched again deterministically, skip sequences to help validation go faster, sub-components that each have payloads, a PGP signature, ...
  2. I'm doing a bunch of validation on this specific root (not auditing the tree):
    • that the PGP signature is valid for Keybase's published key,
    • that the subject of the signature matches the payload,
    • that the actual merkle root matches,
    • ...
  3. Now that we know the PGP message is good, take a sha512 hash of it. do an ots stamp with those bytes. This creates a "preliminary" timestamp proof, and submits it to a bunch of calendar servers. It's not actually in the Bitcoin blockchain yet though.
  4. Compose some JSON with:
    • base64 encoded data that we submitted to OpenTimestamps,
    • base64 encoded data that we got back from OpenTimestamps,
    • the actual, global merkle root,
    • the seqno of this root, which we can use to fetch it deterministically in the future,
    • a status field so we can track whether or not the OTS proof has made it to the blockchain (PRELIMINARY or VERIFIABLE)
    • some other metadata that's not relevant for a simple readme.
  5. Publish the JSON to my public channel (with PRELIMINARY status) so everyone in the world can read it.
  6. Periodically pull back all of my recently published, PRELIMINARY messages.
  7. Try to ots upgrade them. If it works (i.e. the OTS proof has made it to the blockchain), edit the keybase message's JSON to have the new OTS proof, and change the status to VERIFIABLE.

BuiltWith

It's a dockerized python3 chatbot using pykeybasebot.

  1. using pipfile locally and vanilla pip inside docker. async and await. a teeny bit of typing (i really should do more) where it seems most valuable to me as the developer.
  2. username and paperkey for the bot are in ./env_file
  3. the bot responds to any chat messages with a really long description of what it's doing, so don't expect a great conversation.
  4. i try to run everything through make, so if you're wondering how something works, I suggest starting there.

Setup

Locally:

Deployed:

Related Links

TODO