How to format a ciphertext - by Thai Duong - Calif
Calif
SubscribeSign in
How to format a ciphertext<br>What's cooler than a crypto bug? A crypto bug that affects OpenSSL, wolfSSL, Bouncy Castle, and GnuPG.
Thai Duong<br>Jun 17, 2026
Share
A few nights ago Thomas Ptacek shared a link to CVE-2026-34182 in OpenSSL with the note:<br>one-byte tag vulnerability, everyone has to take a drink, that's the rule.
The same bug turned out to be in wolfSSL (CVE-2026-5500), Bouncy Castle, and GnuPG's S/MIME tool gpgsm. Four independent crypto stacks all got it wrong in exactly the same place.<br>The place is PKCS#7 / CMS parsing, and the bug is almost too dumb to believe. So let me use it as an excuse to talk about something I've been ranting about for years: how to format a ciphertext. It sounds trivial. It is not. Almost everything anyone has ever added to a ciphertext has, sooner or later, led to a vulnerability.<br>Full disclosure on disclosure: the wolfSSL and OpenSSL bugs were discovered back in the spring, in our collaboration with Anthropic Research. We reported the wolfSSL one because we were already working with wolfSSL on other findings. The OpenSSL one we sat on, because it didn't clear the severity bar we'd set for ourselves. We try not to flood open-source maintainers with medium-severity paperwork. When Thomas linked the OpenSSL CVE, I went back and asked Claude whether anything else had the same pattern, and it came back with GnuPG's gpgsm plus Bouncy Castle. We've sent reports to Bouncy Castle and GnuPG, noting that the bugs are considered public, because anyone with a decent LLM can easily discover them now that the OpenSSL and wolfSSL bugs have been disclosed. None of this is critical, but the story behind them is still pretty fun to share.<br>The one-byte tag
CMS (the Cryptographic Message Syntax, the descendant of PKCS#7) lets you wrap a message in AuthEnvelopedData using an AEAD like AES-GCM. AES-GCM produces an authentication tag, normally 16 bytes, and that tag is the only thing standing between you and an attacker who wants to forge or tamper with the message. Verify the tag, the message is authentic. Skip it, you have no integrity at all.<br>Here's the catch. The CMS format for AES-GCM (RFC 5084) puts the tag length inside the message, as a field the sender controls:<br>GCMParameters ::= SEQUENCE {<br>aes-nonce OCTET STRING,<br>aes-ICVlen AES-GCM-ICVlen DEFAULT 12 }
AES-GCM-ICVlen ::= INTEGER (12 | 13 | 14 | 15 | 16)
aes-ICVlen is the tag length in bytes, and the structure actually hands an attacker two ways to shrink the tag: this parameter, and the length of the outer mac OCTET STRING that carries the tag itself. Across these libraries, both fields got trusted.<br>OpenSSL takes aes-ICVlen at face value and passes it to the AEAD as the expected tag length, with no lower bound. Set it to 1 and the receiver compares a single byte. The ASN.1 nominally constrains the value to [12, 16], but DER decoders don't enforce value-range constraints, so the 1 sails straight through.<br>wolfSSL got there by a different route. It ignores aes-ICVlen entirely and uses the length of the mac field as the tag length, so you leave the parameter alone and re-encode the mac octet string as 04 01 XX, a one-byte string, and the receiver again checks a single byte.<br>Either way, a one-byte tag lets an attacker forge a valid message by brute force with probability 1/256 per attempt, which is no protection at all against anyone who can keep submitting messages.<br>Bouncy Castle manages to be both better and worse. Its GCM engine has a hard floor of 4 bytes, so for AES-GCM the attacker can't get below a four-byte tag. But CMS also allows AES-CCM, which carries the same aes-ICVlen field, and Bouncy Castle's CCM engine only validates the tag length on encrypt. On decrypt the range check is skipped entirely, and even aes-ICVlen = 0 is accepted.<br>GnuPG's gpgsm takes the wolfSSL route (the length of the mac field becomes the tag length) but gets partially saved one layer down. libgcrypt, the primitive library underneath, rejects GCM tag lengths outside the NIST-approved set. Unfortunately that set goes down to 4 bytes, so the attacker's floor is a four-byte tag rather than a one-byte one, roughly four billion tries per forgery instead of 256. That's a much higher bar, but it's still well short of the 12 bytes the spec calls for, and gpgsm accepts it silently.<br>I did hope the spec would warn against this, but it doesn't. RFC 5084 says only that aes-ICVlen "MUST match the size in octets of the value in the AuthEnvelopedData mac field," and that "a length of 12 octets is RECOMMENDED." That is the whole of the guidance, with nothing about the danger of a short tag, no hint that the field is attacker-controlled, and no warning that a one-octet ICV reduces authentication to a single byte.<br>Thomas's verdict, which I'm stealing for the rest of this post: "it is one of the all-time crypto format misfeatures."<br>The real bug is the format
The tag length is a property of...