def print_texts(self): print("key {} texts:".format(self.identifier)) for pair in self.texts: if 'cipher' in pair: print("Ciphertext: {}".format(hex(pair['cipher'])), end=", ") else: print("Ciphertext: null", end=", ") if 'plain' in pair: print("Plaintext: {} (\"{}\")".format( hex(pair['plain']), i2b(pair['plain'], size=self.size))) else: print("Plaintext: null")
def verify_bleichenbacher_middle(message, signature, key, hash_function='sha1'): """00 01 garbage 00 ANS1 HASH""" hash_msg = getattr(hashlib, hash_function)(message).digest() asn1 = hash_asn1[hash_function] signature = b2i(signature) plain = i2b(key.encrypt(signature), size=1024) try: plain_hash = plain[plain.index(bytes(b'\x00'), 2) + 1:] # have ASN1 HASH except: return False if plain[:2] == bytes(b'\x00\x01') and plain_hash == asn1 + hash_msg: return True return False
def md5(data, initial_state=None, padding=None): """Compute MD5 Args: data(string) initial_state(list of ints) padding(string/None): if None, padding will be computed Returns: digest(string) """ if initial_state is None: initial_state = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476] if not padding: data = add_md_padding(data, endian='little') else: data += padding final_state = merkle_damgard(data, initial_state, compression_function_md5) return bytes(b''.join( [i2b(x, size=32, endian='little') for x in final_state]))
def sha1(data, initial_state=None, padding=None): """Compute SHA1 Args: data(string) initial_state(list of ints) padding(string/None): if None, padding will be computed Returns: digest(string) """ if initial_state is None: initial_state = [ 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 ] if not padding: data = add_md_padding(data, endian='big') else: data += padding final_state = merkle_damgard(data, initial_state, compression_function_sha1) return bytes(b''.join([i2b(x, size=32) for x in final_state]))
def bleichenbacher_signature_forgery(key, garbage='suffix', hash_function='sha1'): """Bleichenbacher's signature forgery based on bug in verify implementation Args: key(RSAKey): with small e and at least one plaintext garbage(string): middle: 00 01 ff garbage 00 ASN.1 HASH suffix: 00 01 ff 00 ASN.1 HASH garbage hash_function(string) Returns: dict: forged signatures, signatures[no] == signature(key.texts[no]['plain']) update key texts """ hash_asn1 = { 'md5': bytes( b'\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10' ), 'sha1': bytes(b'\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'), 'sha256': bytes( b'\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20' ), 'sha384': bytes( b'\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30' ), 'sha512': bytes( b'\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40' ) } if garbage not in ['suffix', 'middle']: log.critical_error("Bad garbage position, must be suffix or middle") if hash_function not in list(hash_asn1.keys()): log.critical_error( "Hash function {} not implemented".format(hash_function)) if key.e > 3: log.debug("May not work, because e > 3") signatures = {} if garbage == 'suffix': for text_no in range(len(key.texts)): if 'plain' in key.texts[text_no] and 'cipher' not in key.texts[ text_no]: log.info("Forge for plaintext no {} ({})".format( text_no, key.texts[text_no]['plain'])) hash_callable = getattr(hashlib, hash_function)(i2b( key.texts[text_no] ['plain'])).digest() # hack to call hashlib.hash_function plaintext_prefix = bytes(b'\x00\x01\xff\x00') + hash_asn1[ hash_function] + hash_callable plaintext = plaintext_prefix + bytes( b'\x00' * (key.size // 8 - len(plaintext_prefix))) plaintext = b2i(plaintext) for round_error in range(-5, 5): signature, _ = gmpy2.iroot(plaintext, key.e) signature = int(signature + round_error) test_prefix = i2b(gmpy2.powmod(signature, key.e, key.n), size=key.size)[:len(plaintext_prefix)] if test_prefix == plaintext_prefix: log.info("Got signature: {}".format(signature)) log.debug("signature**e % n == {}".format( i2h(gmpy2.powmod(signature, key.e, key.n), size=key.size))) key.texts[text_no]['cipher'] = signature signatures[text_no] = signature break else: log.error( "Something wrong, can't compute correct signature") return signatures elif garbage == 'middle': for text_no in range(len(key.texts)): if 'plain' in key.texts[text_no] and 'cipher' not in key.texts[ text_no]: log.info("Forge for plaintext no {} ({})".format( text_no, key.texts[text_no]['plain'])) hash_callable = getattr(hashlib, hash_function)(i2b( key.texts[text_no] ['plain'])).digest() # hack to call hashlib.hash_function plaintext_suffix = bytes( b'\x00') + hash_asn1[hash_function] + hash_callable if b2i(plaintext_suffix) & 1 != 1: log.error( "Plaintext suffix is even, can't compute signature") continue # compute suffix signature_suffix = 0b1 for b in range(len(plaintext_suffix) * 8): if (signature_suffix** 3) & (1 << b) != b2i(plaintext_suffix) & (1 << b): signature_suffix |= 1 << b signature_suffix = i2b( signature_suffix)[-len(plaintext_suffix):] # compute prefix while True: plaintext_prefix = bytes(b'\x00\x01\xff') + random_bytes( key.size // 8 - 3) signature_prefix, _ = gmpy2.iroot(b2i(plaintext_prefix), key.e) signature_prefix = i2b( int(signature_prefix), size=key.size)[:-len(signature_suffix)] signature = b2i(signature_prefix + signature_suffix) test_plaintext = i2b(gmpy2.powmod(signature, key.e, key.n), size=key.size) if bytes(b'\x00' ) not in test_plaintext[2:-len(plaintext_suffix)]: if test_plaintext[:3] == plaintext_prefix[:3] and test_plaintext[ -len(plaintext_suffix):] == plaintext_suffix: log.info("Got signature: {}".format(signature)) key.texts[text_no]['cipher'] = signature signatures[text_no] = signature break else: log.error("Something wrong, signature={}," " signature**{}%{} is {}".format( signature, key.e, key.n, [(test_plaintext)])) break return signatures
def sign(message, key): # message = add_rsa_signature_padding(message, size=key.size, hash_function='sha1') message = b2i(message) signature = pow(message, key.d, key.n) return i2b(signature)
def decrypt(ciphertext, key): ciphertext = b2i(ciphertext) plaintext = pow(ciphertext, key.d, key.n) return i2b(plaintext)
def encrypt(plaintext, key): plaintext = b2i(plaintext) ciphertext = pow(plaintext, key.e, key.n) return i2b(ciphertext)
#!/usr/bin/env python from CryptoAttacks.PublicKey import rsa from CryptoAttacks.Utils import i2b from gmpy2 import iroot with open('../flag_enc.txt', 'rb') as f: flag = f.read() flag = int.from_bytes(flag, 'big') key = rsa.RSAKey.import_key('../key.pem') f = flag for i in range(0, 300): x = iroot(f, key.e) if x[1]: print(i, i2b(int(x[0]))) break f += key.n