Becoming Satoshi: The susceptibility of the crypto-ecosystem to misinformation

At Albacore we develop tools to improve financial transparency. We believe that more can be done to provide users with information about the organisations they trust with their money.


A few weeks ago, the crypto-community was abuzz amidst fresh claims of “Satoshis’ second coming”. Claims like these are not new, with Craig Wright being the most recent cult

This new claim came with “evidence”, a digital signature that had seemingly been made using an address known to have belonged to Satoshi. At face value the signature appeared to valid, appearing to confirm that this was indeed Satoshi Nakamoto.

Craig Wright’s extensive rap sheet of fraudulent claims (https://en.bitcoin.it/wiki/Craig_Wright)

Unsurprisingly, this turned out to be yet another fraudulent claim; however, it did succeed in highlighting something about the cryptocommunity. Despite all the interest and hype in blockchains, there is still a serious lack of technical understanding held by the majority of people in the community about the fundamentals that underpin this technology. This leads to situations where the merest mention of crypto-esque jargon like digital signatures or hashing is convincing enough. Exacerbating this are news outlets such as CNBC or BBC, with zero technical understanding of the technology, yet are willing to report frauds as truth.

The aim of this blog post is to unpack the fraud in two ways: A high level explanation outlining how the fraud was conducted, and a technical recreation of the forgery.

Despite all the interest and hype in blockchains, there is still a serious lack of technical understanding held by the majority of people in the community about the fundamentals that underpin this technology.

What is in a signature?

At the very core of this fraud is the forgery, specifically an existential forgery, of an ECDSA signature. ECDSA (and digital signatures in general) are used in the modern world for a variety of applications, such as ensuring data integrity during transmission. The increased efficiency of ECDSA over RSA has also seen it become the signature algorithm of choice for browser certificates.

In blockchains, ECDSA is used to spend transaction outputs locked to addresses by allowing you to prove that you know the private key to that address without having to publish the key for everyone (who would subsequently steal all your coins) to see. Similarly, a common use case is to sign an arbitrary message you received from someone to verify that you know the private key to an address at a given time.

There are two main operations in ECDSA that we are interested in, generating the signature and verifying the signature (as shown below).

The inputs and outputs of the ECDSA signature and verification operations. Note that in both, the input is the plaintext message that is then hashed within the boxes (the ECDSA algorithm)

The red outlined box is important to note and forms the focal point of the fraud. The accepted standard for ECDSA signing is to employ the “hash-then-sign” paradigm where a plaintext message (i.e. not a hash) is passed into the ECDSA operation, hashed within, and then it is the hash that is signed. This is the same in the verification step where the message input is hashed within the ECDSA function and then verified (“hash-then-verify”) . As an additional level of security, the message should be something that is not selected by the signer.

Forging a signature

As alluded to before, the fraud requires the use of a non-standard ECDSA verification operation — that is, it foregoes the “hash-then-verify” paradigm thereby tricking unsuspecting verifiers as to its legitimate construction.

According to the twitter post, the inputs provided for verification are the signature itself (split into its r & s components) and the hash of the message. Notice how the hash of the message is provided without the actual plaintext message? The only way to verify this signature is to use a non-standard ECDSA verifier that does not internally hash the message but instead accepts the hash of the message as an input.

The inputs provided as “evidence”

So why would the scammer provide the hash of a message and not the message itself? Because they do not know the message. That would involve them actually knowing the private key and generating a legitimate signature. In fact, the message for the hash provided by them is most likely gobbledygook ( no one will be able to figure it what it is anyway — given the one-way nature of cryptographic hashes)

The only way to verify this signature is to use a non-standard ECDSA verifier that does not internally hash the message but instead accepts the hash of the message as an input.

The scammer instead back-solves for a hash of a message that would result in a valid signature (given a non-standard verification function) against Satoshis’ address. This signature is meaningless without being able to provide the plaintext message should have been passed as an input.

While the difference between standard and non-standard seems very minor (the input is the message vs the hash of the message) it is exactly this nuance that can mean the difference between a cryptographic principle being secure or insecure.

Before delving into the technical recreation of the forgery, this is a good moment to reflect on what we, as non-cryptography experts, can do to combat the types of frauds that only serve to further divide the ecosystem.

  1. Education: By far the best way is to learn and understand more about the core technology (e.g. ECDSA, Merkle trees), instead of merely superficial examinations of the latest trends in blockchains (e.g. DAGs, DPoS).
  2. Skepticism: Outrageous claims should be met with upfront and overwhelming skepticism. Many of these scammers have ulterior motives who use these claims to bolster their own offerings or FUD others. The fact that so many in the community were so ready and willing to accept the falsified evidence just shows how much more skepticism is needed.

Faketoshi, let’s all be Satoshi

The code presented below is in Javascript and is used in our interactive forger-er you can check out here . An implementation in Haskell is also available at our Github.

Now that we have an idea of how the forgery is done let’s make one ourselves.

The first thing we need is a verify function for ECDSA. Remember that we can’t use the verify signature provided by something like Bitcoin-core (or most other libraries in fact) since they only accept plaintext messages as inputs. We need one that will (foolishly) accept a hash of the message; thankfully, the elliptic js library provides us with one.

import * as  elliptic from 'elliptic';
import * as bigInt from "big-integer";
import * as BN from 'bn.js';
let ec = new elliptic.ec('secp256k1');
// The public key in the Bitcoin genesis block
x='678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb6'
y='49f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f'
const pub_arr = {x:x, y:y};
const pubkey = ec.keyFromPublic(pub_arr, 'hex');
pubkey.verify(msgHash, signature)// this accepts a hash as input

Now we have our insecure verification code, we can back-solve for a specific hash (of a fictitious message) that will result in a valid signature.

As pointed out by Pieter Wuille, it’s actually pretty easy:

  1. Pick a random x, such that x< n, where n is the order of the elliptic curve.
  2. The public key P is then xG, where G is the generator of the curve and the operation is a point multiplication.
  3. Now we can calculate R = aG + bP where a & b are non-zero values (<n).
  4. A valid Signature tuple (r,s) is then (R_x, R_x/b) for the hash message (R_x*a/b), where R_x is the x coordinate of the R point.
function generate_forgery (crv,pub) {
 // Get n, the order of secp256k1
const secp_order_b1 =
bigInt('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141',16)
 // Generate random a - could just use 1 but lets make it random
const a_rand = bigInt.randBetween(1,secp_order_b1)
// Turn the int to hex so we can pass it into new BN
const a_rand_hex = dec2hex(a_rand)
  // Generate random b - could just use 1 but lets make it random
const b_rand = bigInt.randBetween(1,secp_order_b1)
const b_rand_inv = b_rand.invm(secp_order)
const b_rand_hex = dec2hex(b_rand)
  // Generate the forgery
const aG= crv.g.mul(new BN(a_rand_hex,16))
const bP = pub.getPublic().mul(new BN(b_rand_hex,16))
const r = aG.add(bP)
const rx = r.getX().toString(16)
const rx_int = new BN(rx,16)
const signature = {
r: rx ,
s:dec2hex(rx_int.mul(b_rand_inv).mod(secp_order))
};
const msg =
dec2hex(rx_int.mul(a_rand).mul(b_rand_inv).mod(secp_order));
  const faktoshi_creds = {sig: signature, msg_hash: msg};
  if (pub.verify(msg, signature)) return faktoshi_creds
  else return "Something went wrong"
}

To see why this works it helps to understand how verification works under the hood. Given a signature tuple (r,s), we can check if it is valid using the following steps:

Now if we wanted to find a H(message) that would make the verification true, we can just rearrange the equations like so.

That first line should look familiar from the start of this section, to make the signature valid, we just need to set (r,s) = (Rx,R_x/b) and if you substitute those values into the above equations you’ll find that everything cancels out nicely and the equalities hold.

If you made it to the end, congratulations! You now have the ability to construct forged signatures for any addresses you want (provided the verifiers know very little about ECDSA). For the long term benefit of the crypto-ecosystem it is critical that the community becomes more knowledgeable as a whole, and dismisses fraudulent behaviour to focus on things that advance the space as a whole.


Keep up-to-date with our progress and other interesting things we get up to by following us on Medium and Twitter or check out our Github.