Cherry referring to my last name kirschju.re Forward and Reverse Engineering

Deriving Safety Numbers from Signal Ed25519 Keys

Just in case anybody needs this (for whatever odd reason), the following is a small python script deriving the fingerprint from the public part of a Signal identity key and the phone number it is tied to. Two per-identity fingerprints are sorted and concatenated to form the per-conversation safety number that is displayed in the verification screen in the app. The following python code is in accordance with the implementation in fingerprint/NumericFingerprintGenerator.java of libsignal-protocol-java on github:

#!/usr/bin/env python3

import hashlib, base64

DJB_TYPE = 5
FINGERPRINT_VERSION = 0

def fingerprint_hash(pub_key, stable_id):
    assert len(pub_key) == 1 + 32 # 1 byte type + 256 bit key
    assert pub_key[0] == DJB_TYPE

    md = int.to_bytes(FINGERPRINT_VERSION, 2, "big") + pub_key + stable_id
    for _ in range(5200):
        md = hashlib.sha512(md + pub_key).digest()
    return md

def to_disp_hash(md):
    return [ int.from_bytes(md[5*off:5*off+5], "big") % 100000 for off in range(6) ]

if __name__ == "__main__":
    # get this from a Signal database (exported backup or desktop client),
    # typically a base64 string starting with an uppercase 'B'
    pub_key = base64.b64decode("BYN0Y1LdkF98c3GOCZRkhAObwMSr6Yg0WlDH43qLwrZi")
    # "+<country-code><phone-number>"
    stable_id = b"+491749091317"

    disp_hash = to_disp_hash(fingerprint_hash(pub_key, stable_id))

    print(" ".join([ f"{h:05}" for h in disp_hash ]))