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 ]))