In response to the GPG.Fail attacks, a Hacker News user made this claim about the 64-bit “Long Key IDs” used by OpenPGP and GnuPG, while responding to an answer I gave to someone else’s question:
OK, to be clear, I am specifically contending that a key fingerprint does not include collisions. My proof is empirical, that no one has come up with an attack on 64 bit PGP key fingerprints.
Hacker News Thread
This was a stupid thing to say to me, of all people.
And thus:
Save this file as pubkey1.asc:
-----BEGIN PGP PUBLIC KEY BLOCK----- xjQEZVQvDBYKCSsGAQQB2kcPAQEHQPlChumZ771BEWmLgtsrm2QUf3YE4xSbpiRr wGelIDITzShDb2xsaXNpb24gS2V5IDEgPGtleTFAY29sbGlzaW9uLmV4YW1wbGU+ =8+QC -----END PGP PUBLIC KEY BLOCK-----
Save this file as pubkey2.asc:
-----BEGIN PGP PUBLIC KEY BLOCK----- xjQEZVRJOxYKCSsGAQQB2kcPAQEHQE5YOa8jzfZ1IAUmqaxKvrGdq3RJWQ1QBfh4 9ffaD/S3zShDb2xsaXNpb24gS2V5IDIgPGtleTJAY29sbGlzaW9uLmV4YW1wbGU+ =Ah4C -----END PGP PUBLIC KEY BLOCK-----
Run the following commands, and despair:
gpg --list-packets pubkey1.asc | grep keyid gpg --list-packets pubkey2.asc | grep keyid
You will observe the following:
$ gpg --list-packets pubkey1.asc | grep keyid
keyid: 948F9326DD647C78
$ gpg --list-packets pubkey2.asc | grep keyid
keyid: 948F9326DD647C78
However, the public keys and full fingerprints are different for each public key.
This was the result of a Birthday attack against the 64-bit size of the “Long Key ID” feature of OpenPGP.
I’ve written about the Birthday Bound before. But generally:
…then you only need about or
inputs before your collision probability approaches 50%.
Since the output space of a Long Key ID is 64 bits, there are possible Long Key IDs. However, you expect a collision with about 50% probability after only
Long Key IDs are generated.
This is a widely known fact about cryptography, and the crux of the attack.
EDIT: Apparently it was also done before. In 2019.
The full attack took about 3 days on a laptop, running in the background while I was doing other work.
This is within an order of magnitude of the same runtime needed to break Rainbow, for comparison.
Because it was a memory-constrained device, the strategy looked roughly like this:
sort command on this enormous file (~57 hours).
I could have done it faster if I felt like using a cloud provider, but I didn’t want to put too much work into this.
Virtually no cryptography expert worth listening to will be surprised by this.
private1.asc
-----BEGIN PGP PRIVATE KEY BLOCK----- xVkEZVQvDBYKCSsGAQQB2kcPAQEHQPlChumZ771BEWmLgtsrm2QUf3YE4xSbpiRr wGelIDITAAEAX7B7GVQBGE9VxroU6ilaSYp7D0QrZFRgbLDBM+uVTxEMis0dVGVz dCBLZXkgMSA8a2V5MUBleGFtcGxlLmNvbT4= =hStH -----END PGP PRIVATE KEY BLOCK-----
private2.asc
-----BEGIN PGP PRIVATE KEY BLOCK----- xVkEZVRJOxYKCSsGAQQB2kcPAQEHQE5YOa8jzfZ1IAUmqaxKvrGdq3RJWQ1QBfh4 9ffaD/S3AAEAA6ztnLShhUmlWLdG8TLgtyT6SsW+EibmRMuMOzUK5iMQN80dVGVz dCBLZXkgMiA8a2V5MkBleGFtcGxlLmNvbT4= =Tdrc -----END PGP PRIVATE KEY BLOCK-----
Do not make stupid “empirical” claims about the security of cryptosystems, especially when the cost to disprove you is so low.
Header art by Kyume.