The fingerprint binding¶
The single fact that shapes how these packages are built, tested and released: the key derivation binds the recipient's fingerprint, so assembling a certificate and decrypting a message are one problem wearing two hats.
What the KDF actually hashes¶
RFC 6637 §8 derives the key-encryption key as:
Param = curve_OID_len || curve_OID || public_key_alg_ID || 03 || 01
|| KDF_hash_ID || KEK_alg_ID || "Anonymous Sender "
|| recipient_fingerprint
MB = Hash( 00 || 00 || 00 || 01 || ZB || Param )
ZB is the shared secret. Everything after it is Param, and Param ends with
the recipient's fingerprint.
Why that couples two capabilities¶
A fingerprint is a hash of the public-key packet — its version, creation time, curve, point, and KDF parameters. Change any of them and the fingerprint changes.
So the certificate you emit decides the key that will open messages sent to it. Get one octet wrong in the packet — a creation time, an MPI length, a reserved octet — and the certificate still looks fine, still parses, still verifies, and nothing addressed to it will ever open.
There is no error message for this. The decryption simply fails its integrity check, which is indistinguishable from "this message was for someone else".
The consequences¶
A certificate emitted without a decryption path to check it against is a certificate nobody can prove is usable. The first successful decrypt against a self-assembled certificate is the first real test of the assembly.
That is why the acceptance test for these packages is a round trip — assemble, have an independent implementation encrypt to it, decrypt — rather than two separate suites. Testing them apart would pass while the pair was broken.
It also means the creation time is load-bearing configuration, not metadata. See assembling a certificate.
The practical rule¶
Take the KDF parameters from the certificate the sender used, never from constants that happen to match. Constants drift, certificates do not — and when they disagree, the failure blames the message.