Alex Dowad Computes
Explorations in the world of code

Understanding Manger’s Padding Oracle Attack on RSA Encryption

The previous article discussed Bleichenbacher’s 1998 attack on RSA encryption, which relies on access to a PKCS#1v15 padding oracle. Just a few years later, in 2001, James Manger published details of a different padding oracle attack on RSA encryption. Manger’s attack is similar to Bleichenbacher’s attack, but relies on a different type of padding oracle. Manger’s paper is shorter and easier to read than Bleichenbacher’s, but even so, understanding the attack may be easier if you read this article first rather than jumping directly into the paper. Further, I explain some points of interest which are not mentioned in the paper.

By the way, if you aren’t familiar with Bleichenbacher’s padding oracle attack, read the previous article before this one (or before reading Manger’s research paper, for that matter).

How does a Manger padding oracle differ from a Bleichenbacher padding oracle?

Bleichenbacher’s attack requires an oracle which tells the attacker whether any attacker-chosen ciphertext decrypts to a plaintext with valid PKCS#1v15 padding. To refresh your memory, this is what PKCS#1v15 padding looks like:

0x00 0x02 8 OR MORE RANDOM, NON-ZERO BYTES 0x00 DATA...

In contrast, Manger’s attack requires an oracle which tells the attacker just one thing: a Manger oracle takes any attacker-chosen ciphertext, decrypts it, and then tells the attacker whether the first (most significant) byte is zero or not.

It might sound like a Manger oracle is “weaker” than a Bleichenbacher oracle, in the sense of providing less information to the attacker. But in fact, a Manger oracle is much more useful to an attacker than a Bleichenbacher oracle; using a Manger oracle, one can crack RSA encryption with far fewer oracle queries. Let’s see how.

What doesn’t change from Bleichenbacher’s attack

In Bleichenbacher’s attack, we multiply the ciphertext by some integer , which multiplies the (unknown) plaintext by , and submit the result to the padding oracle. If it returns true, we derive some information about the value of ; specifically, we derive a set of ranges which it might be in. The intersection of all sets gives a set , which contains all possible values of consistent with what the attacker knows so far. When the set is narrowed down to just one value, then the encryption has been cracked.

All of this applies equally to Manger’s attack, but take note of these differences:

The three stages of Manger’s attack

When starting the attack, we assume that the first byte of the plaintext is zero. (More later on what can be done if that’s not the case.)

In Step 1, we check how many high-order bits in are zero. With very few oracle queries, this gives rough bounds on , with an upper bound which is less than double the true value of .

Do it like this: Try ; that left-shifts by one bit position. If the oracle returns true (meaning the high 8 bits of are zero), then the left-shift didn’t push a 1 bit up into the top byte, so the next bit after the top byte must be 0. In that case, try ; that left-shifts by two bit positions, and tells whether the next bit in is 0 or not. Continue until the oracle returns false; then you know which bit is the highest-order 1-bit in .

In Step 2, we take advantage of our new knowledge of the highest-order 1-bit in , together with our knowledge of the RSA modulus .

With those two pieces of information, we can calculate a multiplier , larger than , with these properties: If is in the highest part of its possible range, will wrap the modulus , but not go so high as to reach and make the oracle return false. But if is in the lower part of its possible range, will not wrap the modulus , and because is larger than , that high-end 1-bit will go into the top byte, and the oracle will return false. The formula is:

If the oracle returns false, chop down the upper bound on accordingly, and calculate a new value of , which (again) is just big enough so that values of in the higher part of its possible range will wrap the modulus but not go as high as (so the oracle will return true). And again, values of in the lower part of its possible range will not cause to wrap the modulus , and the oracle will return false. Repeat until the oracle returns true.

For each iteration of this procedure, the new value of is the old one plus .

After Step 2, we now have a multiplier which maps into the range , meaning . Depending on the value of , that upper bound may be around 1.005 times the lower bound. That’s a big improvement from where we were after Step 1, where the upper bound was roughly double the lower bound.

In Step 3, we systematically chop down by around half with each successive oracle query.

In Step 1, would not wrap the modulus at all. In Step 2, would wrap the modulus either 0 or 1 times. But now in Step 3, we will use larger multiplier values , which will wrap the modulus multiple times. However, we will choose so that we are sure of exactly how many times goes into .

Further, we will make just big enough so that around half of the possible values of will be less than , and the other half greater (or equal). The formulas are:

Repeat Step 3 until is narrowed down to a single value.

Note that there is a mistake in Step 3.5b of Manger’s paper; it says to use the floor function when updating . That would be OK if was an inclusive bound (though the bound would not always be as tight as possible), but Step 3.1 clearly shows it’s intended to be an exclusive bound. For an exclusive bound, using the floor function is wrong and can cause the attack to fail.

Comments on the three stages

Take note of why Manger’s Step 1 wouldn’t work with a Bleichenbacher oracle; since a PKCS#1v15-padded message starts with 0x00 0x02, multiplying such a message by 2 and submitting it to a Bleichenbacher oracle would be useless. The high bytes of would be 0x00 0x04 or 0x00 0x05, and the oracle would definitely return false. To get any information at all out of a Bleichenbacher oracle, you must use a multiplier big enough to make 0x00 0x02 wrap the modulus . But because that starting multiplier is so large, only a tiny fraction of the possible values of will land in the target region, where the Bleichenbacher oracle has a chance of returning true. That causes the Bleichenbacher attack to use many oracle queries when searching for .

In Manger’s Step 1, each oracle query gives 1 bit of information on the plaintext . Again, in Step 3, each query gives close to 1 bit of information. So could we skip Step 2, where each query gives us much less information than that? No; Step 3 only works when the range of possible values is small enough, and Step 2 narrows it down just enough for Step 3 to work.

The total number of oracle queries required for Manger’s attack is about 10% more than the number of bits in the plaintext. This is far, far less than a Bleichenbacher attack.

A toy example of Manger’s attack

Was my description of the three stages clear enough? If not, maybe a concrete example (using unrealistically small numbers for intelligibility) will help.

Pick two 12-bit primes to start:

p =
q =

That gives us the modulus ; . How would 1990349 do as private exponent ? (If you don’t like it, suit yourself; click here and I’ll switch it up.) That would mean the public exponent .

Now give me an 16-bit unsigned integer as message : . Encode that message in 24 bits (big-endian), and the top byte will be zero; just what we need for Manger’s attack. is 65536, and the RSA ciphertext is .

OK, now forget that you already know and . Also forget that is so small that it could be factored in microseconds. But, fortuituously, there is a Manger oracle which will gladly decrypt chosen ciphertexts for you and check their leading byte. For fun, we’ll visualize the Manger oracle as a fuzzy creature named Marley.

All we know to start is . In the 16-bit value , we will call the least significant bit , and the most significant bit .

Step 1:

Find out how many high-end bits of the 16-bit value are zero.

3 oracle queries used to determine . Click to see details.

With , . Marley, is the top byte of zero?

Marley Yes!

OK, so , and .

With , . Marley, is the top byte of zero?

Marley Yes!

OK, so , and .

With , . Marley, is the top byte of zero?

Marley No!

OK, so , and . We can move on to Step 2.

Step 2:

Use multipliers no greater than to reduce the size of until .

48 oracle queries used to determine . Click to see details.

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley No!

OK, so . Continue Step 2, with a larger value of .

With , . Marley, is the top byte of zero?

Marley Yes!

OK, so . We can move on to Step 3.

Step 3:

Use larger multipliers to cut the remaining range of possibilities in half with each query.

5 oracle queries used to determine . Click to see details.

With , . Marley, is the top byte of zero?

Marley No!

OK, so .

With , . Marley, is the top byte of zero?

Marley Yes!

OK, so .

With , . Marley, is the top byte of zero?

Marley No!

OK, so .

With , . Marley, is the top byte of zero?

Marley No!

OK, so .

With , . Marley, is the top byte of zero?

Marley Yes!

OK, so .

If you like, try tweaking some of the above parameters and see how the resulting trace of attack steps changes.

How Manger’s attack relates to OAEP padding

The research paper in which Manger published his attack was entitled “A Chosen Ciphertext Attack on RSA Optimal Asymmetric Encryption Padding (OAEP) as Standardized in PKCS #1 v2.0”. Much of the paper talks about the OAEP padding scheme (which I’ll describe in a moment). So it may seem strange that I’ve described the entire attack without referring to OAEP yet. Here is why:

Manger’s attack really has nothing to do with OAEP padding. It’s a chosen-ciphertext attack which relies on being able to find out whether the top byte of an RSA plaintext is zero or not; no more, no less.

The connection with OAEP comes in because careless implementations of OAEP may (potentially) leak information on whether the top plaintext byte is zero. In other words, the nature of OAEP creates danger of exposing a Manger oracle to attackers. But it is certainly possible for RSA implementations not using OAEP to expose a Manger oracle. In fact, the authors of the paper “The 9 Lives of Bleichenbacher’s CAT” found Manger oracles in four different TLS libraries (including OpenSSL), none of which were using OAEP. (Rather, they were using PKCS#1v15 padding for RSA messages.)

Nonetheless, this article wouldn’t be complete without a brief overview of OAEP. Let’s do that now.

OAEP in brief

Here’s a slightly simplified diagram. Note that the overall bit length is the same as the RSA modulus . The symbol ⨁ represents XOR.

0x00 RANDOM SEED SEED MASK 0x00 BYTES 0x01 DATA... DATA MASK

Take note of these features:

Was the last point confusing? I thought so. Let me break it down:

RSA encryption is “malleable”, in that you can multiply an RSA ciphertext by (for example) , and the corresponding plaintext will be multiplied by 2. That property is the basis for both Bleichenbacher’s and Manger’s attacks. Of course, the same is true of an OAEP-padded RSA ciphertext/plaintext. You can multiply an OAEP-padded ciphertext by , and the OAEP-padded plaintext will be multiplied by 2. But, after the OAEP padding is removed, including reversing the seed mask, reversing the data mask, and pulling out the payload bytes (shown as “DATA…” in the above diagram), those payload bytes will not simply by multiplied by 2. They will be completely and irreversibly scrambled.

That is a big part of what OAEP padding is all about. It means that even if you create a service which (ignoring all good judgement) uses RSA encryption to protect confidential data, and even if your service carelessly exposes an oracle on the format of those “DATA…” bytes, attackers can’t mount adaptive chosen-ciphertext attacks[2] against you.

The one fly in the ointment, the bruise on the apple, the hair in the soup, the ugly blotch on this otherwise rosy picture, is that RSA malleability still does apply to an OAEP-padded message before the OAEP padding is reversed. That is what makes Manger’s attack possible against RSA-OAEP, if the implementation is not careful to defend against it.

Manger’s attack against non-OAEP plaintexts

Manger’s paper doesn’t describe how to use his attack against messages which don’t start with a zero byte. But it’s easy; just use the same idea as Step 1 of Bleichenbacher’s attack. Generate a random RSA blinding value , and use it to blind the target ciphertext:

Submit that to the oracle to see whether the blinded plaintext starts with a zero byte or not. If it doesn’t, try again until you find a blinding value which works. Use Manger’s attack to decrypt the blinded ciphertext , then remove the blinding:

Just as for Bleichenbacher’s attack, with this trick, Manger’s attack can be used to forge RSA signatures.

One more point about using Manger’s attack against non-OAEP messages; if the plaintext is a relatively small number (many high-order bytes are zero), then Step 3 sometimes gets stuck in an infinite loop. This is because when is small and the range is also very small, it can happen that multiplying by maps the entire range to one side of ; then the oracle always returns the same value, regardless of where actually is in that range, and Step 3 becomes unable to trim the size of down further. This can be avoided by validating and fiddling with it when necessary; another solution is to do a brute-force search of once it gets very small, rather than using oracle queries to run all the way down to a single value. (This doesn’t happen with OAEP-padded messages; no wonder Manger didn’t mention it in his paper.)

Generalizing the idea of a Manger oracle

Manger’s attack is described in terms of an oracle which reveals whether the first byte (8 bits) of an RSA plaintext is zero. This is because of the property of OAEP padding whereby the first byte of the plaintext is expected to be zero. But there is nothing magic about “8 bits”; if an RSA implementation exposed an oracle revealing whether the first 7 bits were zero, or whether the first 9 bits were zero, it could still be exploited with Manger’s attack. Just adjust the value of accordingly; should be the smallest number with a single 1-bit in the region checked by the oracle.

If the oracle only reveals whether one bit (the most-significant bit) is zero or not, Manger’s attack needs some adjustment. Also, if the RSA modulus is unusually small, such that there is only a single bit in the region checked by the oracle which isn’t also greater than the modulus, the same problem arises. Section 3.2 of Manger’s paper briefly describes how this situation affects his attack, but doesn’t develop an adjusted algorithm in detail.

The ideas behind Manger’s attack can also be adapted to an oracle which reveals whether falls in some interval , where is not a power of 2, and is smaller than . Actually, very little changes about the Manger attack algorithm in that case.

Lessons?


[1] This might seem like an obvious deficiency in Bleichenbacher’s attack, but it’s not. A Bleichenbacher oracle is expected to return false most of the time, so there is very little marginal information gain which an attacker could possibly derive from a false return. Further, as explained in the previous article, a Bleichenbacher oracle may return false for various reasons, irrespective of whether is in the “target” range or not. In contrast, in Manger’s attack, there is usually around a 50% chance of getting back either true or false from the padding oracle, so the attacker can derive close to one bit of information from each oracle query. And Manger oracles return true if and only if is inside the target range .

[2] If you don’t know the term “adaptive chosen-ciphertext attack”, think of it as “attacks roughly like Bleichenbacher’s or Manger’s”.

This post left you burning to speak your mind? Reach out and e-mail the author! Selected replies will be published here.