# How to format a ciphertext

CMS/PKCS#7 parsers in OpenSSL, wolfSSL, Bouncy Castle and gpgsm trust attacker-controlled AES-GCM tag length, allowing forgery with 1/256 probability per attempt.

- Payout: Research
- Severity: medium
- Program: OpenSSL
- Found by (vendor credit): Asim Viladi Oglu Manizada, Alex Gaynor (Anthropic), Ying Dong, Haiyang Huang, Calif.io in collaboration with Claude and Anthropic Research
- Write-up by: Thai Duong
- Disclosed: 2026-06-17
- CVE: CVE-2026-34182, CVE-2026-5500
- Write-up quality: 8/10
- Original: https://blog.calif.io/p/how-to-format-a-ciphertext
- Library page: https://bugbountylibrary.com/r/blog%3Ablog-calif-io-p-how-to-format-a-ciphertext
- Tags: area:oss domain:oss impact:integrity kind:research platform:research sev:medium technique:parser-differential technique:source-review vuln:crypto vuln:signature-replay

**Target:** CMS/PKCS#7 AuthEnvelopedData AES-GCM/CCM decryption in OpenSSL, wolfSSL, Bouncy Castle, GnuPG gpgsm
**Root cause:** The AES-GCM tag length (aes-ICVlen) and/or the mac OCTET STRING length are taken from the attacker-controlled ciphertext and used as the expected tag length without a lower bound, so a 1-byte tag is compared instead of 12-16 bytes.
**Steps:**
1. Craft a CMS AuthEnvelopedData message with AES-GCM.
2. OpenSSL: set `aes-ICVlen` to 1 in GCMParameters; DER decoders don't enforce the [12,16] value-range constraint, so it passes.
3. wolfSSL/gpgsm: leave aes-ICVlen alone and re-encode the mac field as `04 01 XX` (one-byte OCTET STRING); the receiver uses the mac field length as the tag length.
4. Submit forged messages repeatedly; each attempt succeeds with probability 1/256 (OpenSSL/wolfSSL) or 1/2^32 (gpgsm via libgcrypt's 4-byte floor).
5. Bouncy Castle: use AES-CCM with `aes-ICVlen = 0`; the CCM engine only validates tag length on encrypt, not decrypt.
**How found:** After CVE-2026-34182 was publicized, Duong asked Claude whether anything else shared the pattern; it surfaced gpgsm and Bouncy Castle. The original wolfSSL/OpenSSL bugs came from prior Anthropic Research collaboration. The reasoning: the tag length is a key/algorithm property that should never travel in the ciphertext.
**Exploitation:** An attacker who can submit ciphertexts to a decrypting service brute-forces the truncated tag: 256 attempts for a one-byte tag, ~4 billion for a four-byte tag, to forge an authentic message without the key.
**Insight:** Audit every ciphertext field the receiver uses to make a decision (tag length, algorithm id, key id) and check whether the parser enforces value-range constraints the ASN.1 schema nominally declares; DER decoders silently skip them, and encrypt-only validation leaves decrypt paths unchecked.

## Proof of concept (copied exactly from the original report)

**CMS GCMParameters ASN.1 with attacker-controlled tag length**

```
GCMParameters ::= SEQUENCE {
 aes-nonce OCTET STRING,
 aes-ICVlen AES-GCM-ICVlen DEFAULT 12 }

AES-GCM-ICVlen ::= INTEGER (12 | 13 | 14 | 15 | 16)
```

**wolfSSL one-byte mac re-encoding payload**

```
re-encode the mac octet string as 04 01 XX, a one-byte string, and the receiver again checks a single byte.
```

**Recommended key_id || ciphertext format**

```
key_id || ciphertext

The key_id should be sufficient to look up the raw key material
```

