By the time I had a look into Craig Wright’s blog post that seemed to imply that he is Satoshi, others had already pointed out that the signature was copied from a 2009 transaction. The contents of the “Sartre” file, however, were still a mystery. Dan Kaminsky had a blog post up analyzing the commands from CW’s post, but hadn’t been able to figure that bit out, so he asked me to have a look.

Following a screenshot of the output of sha256sum Sartre, there’s a screenshot purportedly the file being displayed through a program called more, but only the first 14% can be seen, making it impossible to verify. How convenient. It’s also important to note that what CW signs is the raw sha256 of “Sartre” rather than the file itself. OpenSSL will sha256 that data it’s signing or verifying anyway, so this would normally be unnecessary step. More on that in a bit.

Given that the signature is valid, there are really very few possible explanations of what’s going on.

  • CW has a computationally feasible preimage attack on sha256
  • CW is Satoshi and has a been sitting on computationally feasible collision attack on sha256 since 2009
  • CW is some sort of actual wizard who enjoys trolling cryptocurrency geeks.
  • CW has pulled some sort of digital slight-of-hand

So, about that last one. Obviously, the signature presented was valid for that Bitcoin transaction, but where did the value 479f9dff0155c045da78402177855fdb4f0f396dc0d2c24f7376dd56e2e68b05 come from? Finding out requires digging a bit into the innards of Bitcoin. I was not feeling quite masochist enough to slog through a bunch of C++ code this morning, so I pulled up Vitalik Buterin’s pybitcointools. We need to know exactly what is being passed into ECDSA to verify a transaction. The function verify_tx_input contains:

1def verify_tx_input(tx, i, script, sig, pub):
2 if re.match('^[0-9a-fA-F]*$', tx):
3 tx = binascii.unhexlify(tx)
4 if re.match('^[0-9a-fA-F]*$', script):
5 script = binascii.unhexlify(script)
6 if not re.match('^[0-9a-fA-F]*$', sig):
7 sig = safe_hexlify(sig)
8 hashcode = decode(sig[-2:], 16)
9 modtx = signature_form(tx, int(i), script, hashcode)
10 return ecdsa_tx_verify(modtx, sig, pub, hashcode)

...the second to last line in this function being the critical one. What does signature_form do?

1def signature_form(tx, i, script, hashcode=SIGHASH_ALL):
2 i, hashcode = int(i), int(hashcode)
3 if isinstance(tx, string_or_bytes_types):
4 return serialize(signature_form(deserialize(tx), i, script, hashcode))
5 newtx = copy.deepcopy(tx)
6 for inp in newtx["ins"]:
7 inp["script"] = ""
8 newtx["ins"][i]["script"] = script
9 if hashcode == SIGHASH_NONE:
10 newtx["outs"] = []
11 elif hashcode == SIGHASH_SINGLE:
12 newtx["outs"] = newtx["outs"][:len(newtx["ins"])]
13 for out in newtx["outs"][:len(newtx["ins"]) - 1]:
14 out['value'] = 2**64 - 1
15 out['script'] = ""
16 elif hashcode == SIGHASH_ANYONECANPAY:
17 newtx["ins"] = [newtx["ins"][i]]
18 else:
19 pass
20 return newtx

It turns out that the actual data signed does not exactly match the transaction that is recorded in the blockchain. There are three reasons for this. The first is that it is not, in general[1], possible for a signature to contain itself. The second has to do with these “SIGHASH” flags. The details of how those work can be found in the documentation for OP_CHECKSIG, but it’s not important to understand them for the relevant transaction. The third is that since the signature is spending a previous transaction’s output, it needs to include information about it. That’s what the parameters i (identifies the input number for this transaction) and script (the “scriptPubKey” from the transaction output being spent) are for.

Feeding the to the signature_form function:

1# pybitcointools https://github.com/vbuterin/pybitcointools
2from bitcoin import *
3
4# output of
5# `bitcoin-cli getrawtransaction 828ef3b079f9c23829c56fe86e85b4a69d9e06e5b54ea597eef5fb3ffef509fe`
6tx = '0100000001ba91c1d5e55a9e2fab4e41f55b862a73b24719aad13a527d169c1fad3b63'+\
7 'b5120100000049483045022100c12a7d54972f26d14cb311339b5122f8c187417dde1e'+\
8 '8efb6841f55c34220ae0022066632c5cd4161efa3a2837764eee9eb84975dd54c2de28'+\
9 '65e9752585c53e7cce01ffffffff0200ca9a3b00000000434104bed827d37474beffb3'+\
10 '7efe533701ac1f7c600957a4487be8b371346f016826ee6f57ba30d88a472a0e4ecd2f'+\
11 '07599a795f1f01de78d791b382e65ee1c58b4508ac00d2496b0000000043410411db93'+\
12 'e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84'+\
13 'ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000'
14
15# from
16# `bitcoin-cli getrawtransaction 12b5633bad1f9c167d523ad1aa1947b2732a865bf5414eab2f9e5ae5d5c191ba 1`
17spk = '410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c'+\
18 'b2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac'
19
20# create signature verification 'modified transaction'
21modtx = signature_form(tx, 0, spk, SIGHASH_ALL)
22
23# append the hashcode - in this case SIGHASH_ALL which is just 1 as a little-endian uint32
24# see the txhash function
25modtx += hexlify(encode(SIGHASH_ALL, 256, 4)[::-1])
26
27print modtx
28# 0100000001ba91c1d5e55a9e2fab4e41f55b862a73b24719aad13a527d169c1fad3b63b5120100
29# 000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0
30# eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3acffffffff0200ca9a
31# 3b00000000434104bed827d37474beffb37efe533701ac1f7c600957a4487be8b371346f016826
32# ee6f57ba30d88a472a0e4ecd2f07599a795f1f01de78d791b382e65ee1c58b4508ac00d2496b00
33# 00000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2
34# e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000010000
35# 00
36
37# un-hex
38bin_modtx = changebase(modtx, 16, 256)
39
40print sha256(bin_modtx)
41# 479f9dff0155c045da78402177855fdb4f0f396dc0d2c24f7376dd56e2e68b05
42
43with open('Sartre', 'w') as f:
44 f.write(bin_modtx)

The “Sartre” file is, of course, available for download. I also posted a gist with the decoded transaction data from this file.

I mentioned that normally, when using ECDSA to sign or verify a file, it is unnecessary to hash it manually. This is where CW’s slight-of-hand lies. ECDSA computes the signature operation on a 256 bit integer referred to as z. Normally this is computed as sha256(message), but Bitcoin does sha256(sha256(modtx)). CW showed the signature verification using OpenSSL’s ECDSA on sha256(modtx). OpenSSL does another sha256 on the data, which makes the z value match.

[1]It’s possible to construct ECDSA signatures that contain themselves, but the algorithm to do this works, essentially, by randomly generating a signature, then working backwards to compute a keypair for it. For reasons outside the scope of this blog post, this cannot be used for Bitcoin transactions.