Dual_EC_DRBG backdoor: a proof of concept
blog.0xbadc0de.be
blog.0xbadc0de.be
n = 115792089210356248762697446949407573529996955224135760342422259061068512044369
p = 2^256 - 2^224 + 2^192 + 2^96 - 1
a = -3
b = 41058363725152142129326129780047268409114441015993725554835256314039467401291
E = EllipticCurve(GF(p), [a,b])
P = E(48439561293906451759052585252797914202762949526041747995844080717082404635286,
36134250956749795798585127919587881956611106672985015071877198253568414405109)
"""
Q = E(91120319633256209954638481795610364441930342474826146651283703640232629993874,
80764272623998874743522585409326200078679332703816718187804498579075161456710)
"""
e = 0x12345678
ei = inverse_mod(e, n)
Q = e*P
def to_bin(x): return ("%060x" % x).decode('hex')
def from_bin(x): return int(x.encode('hex'), 16)
def dual_ec_drbg(s, len):
out = ''
for i in range(0, len, 30):
x, y = (s*P).xy()
s = Integer(x)
x, y = (s*Q).xy()
r = Integer(x) % 2**240
out += to_bin(r)
return out[0:len]
def recover_s(s0, s1):
for i in range(2**16): # For all possible 256-bit x
r = i*2**240 + s0 #
if E.is_x_coord(r): # is valid x?
R = E.lift_x(r) # Lift it to a point
x, y = (R*ei).xy() # Undo s*Q
s = Integer(x)
x, y = (s*Q).xy() # Check if it matches next observed block
if (Integer(x) % 2**240) == s1:
return s # done
return None
import os
# Get 3 blocks: 2 are for the break, the other to confirm prediction
s = dual_ec_drbg(from_bin(os.urandom(16)), 30*3)
s0 = from_bin(s[ 0:30])
s1 = from_bin(s[30:60])
s2 = from_bin(s[60:90])
# Recover internal state
s = recover_s(s0, s1)
# Now try predicting something
t = dual_ec_drbg(s, 30)
assert( from_bin(t) == s2 )
print "Done"I found this note interesting. How big is the secret value used to compute the Q constant? Is it a single static value or does it vary? Would it be possible to brute force this? I'm not a crypto expert and want to understand this a bit better.
One of the big arguments about the NSA introducing weaknesses into these algorithms is "this makes them insecure for everyone and flaws exploited by the government could be exploited by anyone", but this makes it sound like ONLY the NSA could exploit this.
I'm not saying this is better. I just think, if true, it's an interesting discussion point in the debate.
[Cross-posted and answered at /r/netsec]: http://www.reddit.com/r/netsec/comments/1u5jvw/dual_ec_drbg_...
The point I was trying to make is that if you suspect someone has compromised your certificate, you can revoke it to reestablish secure communications. If you suspect your random number generator has been compromised, you can likewise just change the configuration (assuming it's not hardcoded into some piece of cryptographic equipment).
Introducing weaknesses which adversaries can exploit versus ones which they cannot? It is much better.
Exactly right, and that's why NSA can still claim with a straight face that they were not trying to go around breaking crypto in a generic fashion. Like Clipper and Lotus Notes's initial crypto support, NSA was trying to make the crypto safe against everyone but NSA.
Now this is one area where I disagree totally with NSA's actions, but it is true that the way they went about weakening Dual EC DRGB is more nuanced than simply inserting a secret backdoor that anyone could exploit.
[1] http://marc.info/?l=openssl-announce&m=138747119822324&w=2&x...
[2] Discussion: https://news.ycombinator.com/item?id=6949652
Why wasn't this bug caught in the FIPS 140-2 validation testing?
- ---------------------------------------------------------------
Not only the original validation (#1747) but many subsequent validations
and platforms have successfully passed the CAVP[5] algorithm tests ...
several hundred times now. That's a lot of fail.
In test mode the implementation works fine both with and without
additional data. In free running mode the bug is triggered by additional
data on the first call, which is done automatically by the "FIPS capable"
OpenSSL.
Frankly the FIPS 140-2 validation testing isn't very useful for catching
"real world" problems.Has that changed ?
I do not use the reseed functionality either, because I only generate two or three output blocs and never call an explicit reseed.
EDIT: Oh, he's a fellow Greek. That explains that.
I find myself adding a "n" to "a" quite a bit since a lot of times it seems to flow better than using "a".