{"abstract":null,"author":"Mark Esler","content":"A function returns Ok on valid padding and Err on invalid padding. That is a padding oracle. Bleichenbacher published the attack in 1998. It still works, and it does not care how fast your code runs.\nThe timing variant, Marvin, leaks tens of nanoseconds under microseconds of jitter. Kario demonstrated it remotely over a network, but pulling the signal out takes statistics over many samples. The behavioral version needs none of that. Böck, Somorovsky, and Young\u0026rsquo;s ROBOT (disclosed 2017, USENIX Security 2018) re-found Bleichenbacher\u0026rsquo;s oracle across at least seven vendors (and several open-source projects) by reading their responses, not their clocks. Its authors were candid about how little that took:\nSo… ROBOT doesn\u0026rsquo;t add a whole lot, right? That\u0026rsquo;s correct. The surprising fact is that our research was very straightforward. We used minor variations of the original attack and were successful. This issue was hiding in plain sight. […] neither the vendors of the affected products nor security researchers have investigated this before, although it\u0026rsquo;s a very classic and well-known attack.\nthe ROBOT FAQ\nThis blog post uses even less: the textbook 1998 attack, unmodified, against a current crate.\nThe oracle is one bit PKCS#1 v1.5 decryption recovers an integer and checks its structure: leading bytes 00 02, then ≥8 nonzero padding bytes, a 00 separator, then the message. The attacker chooses the ciphertexts and reads whether it conformed.\nRSA is multiplicatively homomorphic: multiply a ciphertext c by sᵉ mod n and the underlying plaintext is multiplied by s. So the attacker submits c · sᵉ for a chosen s and asks the oracle: is m · s still a conforming block? Each \u0026ldquo;yes\u0026rdquo; pins m into a narrower band. Most of the work is the sequential hunt for each conforming s; once a single interval survives, each new conforming s roughly halves that interval until it collapses to a single value: the plaintext.\nDemonstrated against a current implementation A ~300-line standalone proof of concept. Its only cryptographic dependency is the published rsa = \u0026quot;=0.10.0-rc.18\u0026quot; crate; the rest is attacker-side bignum and SHA-256. It mounts the attack with nothing but this:\n// The whole oracle. One bit. No timing. fn query(\u0026amp;mut self, s: \u0026amp;BigUint) -\u0026gt; bool { let c = (\u0026amp;self.c0 * s.modpow(\u0026amp;self.e, \u0026amp;self.n)) % \u0026amp;self.n; let ct = uint_to_be_padded(\u0026amp;c, self.k); self.key.decrypt(Pkcs1v15Encrypt, \u0026amp;ct).is_ok() // Ok =\u0026gt; conforming, Err =\u0026gt; not } Results over 100 distinct deterministic RSA-4096 keys (untuned 1998 attack: every key recovered and forged):\nCapability Result Oracle queries Plaintext recovery 100/100 recovered (integer and message) median 41.9k · IQR 25.7k–63.3k · max 1.16M Signature forgery forged^e mod n == EMSA(msg) (100/100 verify) median 107k · IQR 67.9k–173k · max 897k The forgery uses the identical machinery, since signing is RSA \u0026ldquo;decryption\u0026rdquo; of a message representative: the same oracle that recovers a plaintext also forges a signature over an attacker-chosen message. The attacker never recovers the private key in either case. A bigger modulus only makes each query slower.\nThe implementation\u0026rsquo;s own source comment warns of exactly this: an attacker who observes the padding-check outcome \u0026ldquo;can decrypt and forge signatures as if they had the private key.\u0026rdquo;\nThe cure: stop using RSA encryption ROBOT, published in 2018, under a heading that is just \u0026ldquo;Disable RSA encryption!\u0026rdquo;:\nWe believe RSA encryption modes are so risky that the only safe course of action is to disable them.\nThey scope it:\nBy disabling RSA encryption we mean all ciphers that start with TLS_RSA. It does not include the ciphers that use RSA signatures and include DHE or ECDHE in their name.\nAlicja Kario\u0026rsquo;s Marvin attack showed Bleichenbacher\u0026rsquo;s timing channel is still common in current libraries, and she authors the CFRG\u0026rsquo;s RSA-guidance draft. The draft is normative:\nCurrent protocol deployments MUST NOT use encryption with RSAES-PKCS-v1_5 padding.\nSupport for RSAES-PKCS-v1_5 SHOULD be disabled in default configuration of any implementation of RSA cryptosystem.\nOn the Marvin page:\nStop using RSA PKCS#1 v1.5 encryption!\nWe have had 25 years of people trying to patch this fundamentally broken padding mode.\nHer advice to library authors is to \u0026ldquo;deprecate and disable support for PKCS#1 v1.5 padding for encryption.\u0026rdquo; If you truly cannot drop RSA encryption, use RSA-OAEP, but only with a constant-time decode, because OAEP has its own oracle (Manger).\nWhen you cannot drop v1.5 yet, implicit rejection is the stopgap. Return a fixed-length value for every ciphertext, valid or not, derived from the private key and the ciphertext: the CFRG §7.2 \u0026ldquo;Implicit rejection\u0026rdquo; construction, which says to \u0026ldquo;use the private key and the provided ciphertext to derive a static, but unknown to the attacker, random value.\u0026rdquo; No Ok/Err to read.\ndecrypt(ct) -\u0026gt; Ok(variable-length msg) | Err // a one-bit oracle decrypt_session_key(ct, len) -\u0026gt; always len bytes, valid or not // nothing to observe Implicit rejection is easy to get wrong. The fixed-length value has to be produced in constant time, and you have to measure that it is. Skip the measurement and the same bit can leak through timing instead. That\u0026rsquo;s a Marvin-style timing channel.\nThis was one library. Many that roll their own v1.5 still hand the bit over. The companion survey of 27 RSA libraries maps the rest.\n","date":"2026-06-06","description":"RSA PKCS#1 v1.5 decryption that returns Ok on valid padding and Err on invalid is a Bleichenbacher padding oracle. A ~300-line proof of concept recovers a full plaintext and forges a signature against a current implementation using only that one bit per query, no timing, no key. The fix is to stop using RSA PKCS#1 v1.5 encryption.","lastmod":"2026-06-07","readingTime":5,"section":"datagrams","title":"An Ok/Err Result Is a Bleichenbacher Oracle","url":"https://hexproof.dev/datagrams/ok-err-is-a-padding-oracle/","wordCount":854}