Validating Bitmessage public keys
Posted
Bitmessage is a decentralized, encrypted, peer-to-peer, trustless communication protocol. I wrote a little bit about it some time ago.
Today I looked into a code base if I could come up with some obvious bug in the encryption part of implementation.
I didn’t find anything in the OpenSSL wrapper, so created a simple script that will check if public keys in the inventory are valid elliptic curve points according to a secp256k1
parameters.
Firstly, I have checked how many public keys are flowing through the network:
addr_versions: {
3: 36,
4: 2453
}
As it’s turned out, there are only 36 public keys of version 3 or lower, and most of the network is already using fourth generation, which is a good sign. The distinction between those versions is that v4 public keys are encrypted by a symmetric key, that can be constructed only if you already know the corresponding Bitmessage recipient’s address. This prevents network participants to just crawl the public keys.
Now let’s check if those 36 keys are valid secp256k1
curve points using fastecdsa python library:
from __future__ import absolute_import
import sqlite3
from binascii import hexlify
from time import strftime, localtime
conn = sqlite3.connect('/home/localhost/.config/PyBitmessage/messages.dat')
conn.text_factory = str
cur = conn.cursor()
# PyBitmessage/src/addresses.py file
from addresses import decodeVarint
OBJECT_PUBKEY = 1
def check_pubkey(keys):
from fastecdsa.point import Point
from fastecdsa.curve import secp256k1
readPosition = 4
publicSigningKey = keys[readPosition:readPosition + 64]
readPosition += 64
publicEncryptionKey = keys[readPosition:readPosition + 64]
sig_pubkey = hexlify(publicSigningKey)
sig_x, sig_y = int(sig_pubkey[:64], 16), int(sig_pubkey[64:], 16)
enc_pubkey = hexlify(publicEncryptionKey)
enc_x, enc_y = int(enc_pubkey[:64], 16), int(enc_pubkey[64:], 16)
try:
Point(sig_x, sig_y, curve=secp256k1)
Point(enc_x, enc_y, curve=secp256k1)
print("valid keys")
except Exception as e:
print("NOT valid keys")
# modified code from PyBitmessage/src/message_data_reader.py
def readInventory():
"""Print each row from inventory table"""
item = '''select hash, objecttype, streamnumber, payload, expirestime from inventory'''
parameters = ''
cur.execute(item, parameters)
output = cur.fetchall()
addr_versions = {}
for row in output:
obj_hash, objecttype, streamnumber, payload, expirestime = row
if objecttype == OBJECT_PUBKEY:
readPosition = 20 # bypass the nonce, time, and object type
addressVersion, varintLength = decodeVarint(
payload[readPosition:readPosition + 10])
readPosition += varintLength
streamNumber, varintLength = decodeVarint(
payload[readPosition:readPosition + 10])
readPosition += varintLength
if addressVersion in addr_versions:
addr_versions[addressVersion] += 1
else: addr_versions[addressVersion] = 1
if addressVersion == 3:
check_pubkey(payload[readPosition:])
print "addr_versions: ", addr_versions
if __name__ == '__main__':
readInventory()
$ python message_data_reader.py
valid keys
valid keys
...
valid keys
valid keys
valid keys
valid keys
addr_versions: {3: 36, 4: 2454}
We can see that all v3 keys are valid elliptic curve points.