Skip to content

Instantly share code, notes, and snippets.

@AdamISZ
Created September 13, 2023 00:40
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save AdamISZ/8dacbbab7525af07c0ca3f12e2262c72 to your computer and use it in GitHub Desktop.
How to prove you're Satoshi
# A reminder of how to "prove" you're Satoshi.
# ("reminder" - this was done (with tongue in cheek, presumably)
# by someone on Twitter a few years ago).
# 1. We need the public key of the receiving address of (e.g.) block 1.
# it is on the blockchain in uncompressed form (P2PK):
block1_uncompressed_output_key_hex = "0496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858ee"
# convert the key to compressed because software complains about mixed formats:
block1_compressed_output_key_hex = "02" + block1_uncompressed_output_key_hex[2:66]
# And ... that's kind of all we need :)
# Steps of the algorithm:
# 1. Generate a random tweak alpha
# 2. Set the nonce point to R = P + alpha * G
# 3. Calculate the corresponding x coordinate R(x), call that t.
# 4. Set the signature value s = t.
# 5. Set the signature hash value h ("H(message)" supposedly) to h = s * alpha
# 6. Publish (t, s) as the signature, which validates against Satoshi's public key.
# 7. Promise that you will reveal the mysterious message later, and voila, you're Satoshi.
from jmbitcoin import btc # you could just use the python-bitcointx package with a bit of messing around
import os
import base64
from jmbase import bintohex, hextobin
#1
P = btc.CPubKey.fromhex(block1_compressed_output_key_hex)
alpha_bytes = os.urandom(32)
#2
R = btc.add_pubkeys([P, btc.privkey_to_pubkey(alpha_bytes + b"\x01")])
# 3,4
assert isinstance(R, btc.CPubKey)
t = R[1:]
assert len(t) == 32
s = t
#5
sint, alphaint = [int.from_bytes(x, byteorder="big") for x in [s, alpha_bytes]]
hint = sint * alphaint % btc.N
h = int.to_bytes(hint, 32, byteorder="big")
#6
# We need to construct the signature in the expected DER encoding for ECDSA,
# hence the below extra chunk of code
def der_encode_sig(r: int, s: int) -> bytes:
# modified from some ancient pybitcointools code
# (because everything nowadays calls libsecp...)
def encode(some_integer, maxlength, minlength=1):
c = int.to_bytes(some_integer, maxlength, "big")
stripped_c = c.lstrip(b"\x00")
if len(stripped_c) < minlength:
stripped_c = b"\x00" * (minlength - len(stripped_c)) + stripped_c
return stripped_c
s = btc.N - s if s > btc.N // 2 else s # BIP62 low s
b1, b2 = encode(r, 32), encode(s,32)
if bytearray(b1)[
0] & 0x80: # add null bytes if leading byte interpreted as negative
b1 = b'\x00' + b1
if bytearray(b2)[0] & 0x80:
b2 = b'\x00' + b2
left = b'\x02' + encode(len(b1), 1) + b1
right = b'\x02' + encode(len(b2), 1) + b2
return b'\x30' + encode(
len(left + right), 1) + left + right
sig = der_encode_sig(sint, sint)
encoded_sig = base64.b64encode(sig)
print("got sig: ", encoded_sig)
print("and message hash is: ", bintohex(h))
# Do the "verification": check that (R, s) is a valid signature on message hash h, for key P:
verification_result = btc.ecdsa_raw_verify(h, P, sig, rawmsg=True)
print("Is the signature valid on Satoshi's key from block 1?: ", verification_result)
"""
Here is some example output:
(jmvenv) waxwing@here:~/code$python fakeforgery.py
got sig: b'MEUCIQDOfh42gE4SqQHtp6SUZOaVaDRDrMpGXr7Yr6UOZu2USwIgMYHhyX+x7Vb+Elhba5sZaVJ6mTnlAkF85yK5fmlIrPY='
and message hash is: f888cabdf88abee1ee9369a91578c3efbde119959df18f4e46a3631929338251
Is the signature valid on Satoshi's key from block 1?: True
Note: this kind of verification is usually not possible with user-level tools;
and that's a *VERY* good thing! Because the ECDSA algorithm is not valid if you don't
provide, as part of the verification, the preimage of the message hash.
That's basically the entire point of this "trick".
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment