This is a proof of concept implementation of backdoored ECDSA signatures. There are many ways to backdoor signatures; this is a very simple and rough example. The basic idea is to cook the ECDSA nonce (aka ephemeral key) in a way that an attacker can recover a few bits from the public r value -- without significantly increasing signing time.
The code here leaks a few bits from a secret on each signature. After enough signatures are collected (even if computed under different keys), this full secret is recovered. This secret does not need to be the long-term signing private key, it can be something else. We do not require synchronizing the subliminal sender/receiver -- the implementation is resistant to loss of some signatures. The current implementation mimics the behavior of a deterministic ECDSA signatures in case it helps your cover-up narrative 🕵
A simplified description is this: name k the secret ECDSA nonce. (The x-coordinate r of k*G is part of the signature.) Pick k as the product of a small value s (to be leaked) times a long, secret scalar b (known to the backdoor designer), k=s*b. It's very easy to recover b from k*G by solving a degenerate discrete log. There're some details left out in this explanation (how to deal with synchronization; how to combine bits from different signatures; how to make sure that only the intended thief can steal the long term secret, ...) but that's the basic idea. The whole thing can be vastly improved 🤡
This is a more detailed description:
- Notation:
(r,s)is the ECDSA signature,kis the secret ECDSA per-signing secret nonce. The signer and the backdoor designer share a secret scalarb. The signer wants to leak a value S. - The basic version (easier to explain) works like this: emit a few signatures where the nonces
kare chosen ask := b*s_iwheres_iare a small integers (say, 1 <s_i< 2^16). Thes_iare just "chunks" of S (eachs_iis just a few bits of S). - Then the backdoor designer, upon collecting enough signature pairs
(r,s), recovers the correspondings_iby just sweeping over every value, one by one. Since the backdoor designer knowsb, this is easy. - We can improve several aspects of this basic version:
- First, instead of
s_ibe just chunks of S, we sets_ito be the result of a vector-matrix multiplication over GF(2)s_i := S*[M]. Here[M]is a public matrix constructed from the message being signedm, with dimensions chosen so thats_iis only a few bits long. To reconstruct S froms_i, the backdoor designer just solves a linear system of equations over GF(2). The advantage here is that this method is resistant to loss of somes_i, and the order in whichs_iare recovered does not matter. Also, the backdoor designer gets immediate feedback on how many bits are still to be guessed from S! - Second, instead of repeating
bover and over for different signatures, we use a per-message secretb_i := h(b, m)wherehis a hash function andmis the message being signed. The backdoor designer can still reconstructb_iand use this to loop over possible values fors_i.
- First, instead of
Some concrete figures: the code right can be used to leak a ~256-bit secret S in about 12 signatures by leaking 20 bits per signature. Recovering each s_i takes about 18 seconds in the dumbest implementation ever on a standard laptop.
- Implementation of the nonce generation in a real language
- Better efficiency: leak more bits per signature.
- Leak a public-key encrypted secret, so that if
bis eventually revealed, S can't be recovered from signatures. Here we'd need a public-key scheme with very compact ciphertext -- can we do better than ECIES? - Good cover story. This is a fun exercise in creative writing, and there are good examples to learn from 😉