Validating Bitmessage public keys

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 secp256k1parameters.

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.