def gen_DSA_primes(L, N, seedlen, Hash, outlen): acceptable_pairs = [(1024, 160), (2048, 224), (2048, 256), (3072, 256)] if (L, N) not in acceptable_pairs: return False, None, None if seedlen < N: return False, None, None n = int(math.ceil(float(L) / float(outlen)) - 1) b = L - 1 - n * outlen while True: domain_parameter_seed = utils.string_to_integer(os.urandom(seedlen / 8)) U = utils.string_to_integer( Hash(utils.integer_to_string(domain_parameter_seed))) % 2**(N - 1) q = 2**(N - 1) + U + 1 - (U % 2) if not is_prime(q): continue offset = 1 for counter in range(4 * L): V = {} for j in range(n + 1): V[j] = utils.string_to_integer( Hash( utils.integer_to_string( (domain_parameter_seed + offset + j) % 2**seedlen))) W = sum([V[j] * 2**(j * outlen) for j in range(n - 1)]) + (V[n] % 2**b) * 2**(n * outlen) X = W + 2**(L - 1) c = X % (2 * q) p = X - (c - 1) if p < 2**(L - 1): offset = offset + n + 1 continue if is_prime(p): return True, p, q offset = offset + n + 1 return False, None, None
def test_sample_signature(): # DSA domain parameters p = 0x800000000000000089e1855218a0e7dac38136ffafa72eda7859f2171e25e65eac698c1702578b07dc2a1076da241c76c62d374d8389ea5aeffd3226a0530cc565f3bf6b50929139ebeac04f48c3c84afb796d61e5a4f9a8fda812ab59494232c7d2b4deb50aa18ee9e132bfa85ac4374d7f9091abc3d015efc871a584471bb1 q = 0xf4f47f05794b256174bba6e9b396a7707e563c5b g = 0x5958c9d3898b224b12672c0b98e06c60df923cb8bc999d119458fef538b8fa4046c8db53039db620c094c9fa077ef389b5322a559946a71903f990f1f7e0e025e2d7f7cf494aff1a0470f5b64c36b625a097f1651fe775323556fe00b3608c887892878480e99041be601a62166ca6894bdd41a7054ec89f756ba9fc95302291 # DSA public key y = 0x84ad4719d044495496a3201c8ff484feb45b962e7302e56a392aee4abab3e4bdebf2955b4736012f21a08084056b19bcd7fee56048e004e44984e2f411788efdc837a0d2e5abb7b555039fd243ac01f0fb2ed1dec568280ce678e931868d23eb095fde9d3779191b8c0299d6e07bbb283e6633451e535c45513b2d33c99ea17 # Message msg = "For those that envy a MC it can be hazardous to your health\nSo be friendly, a matter of life and death, just like a etch-a-sketch\n" # DSA Signature r = 548099063082341131477253921760299949438196259240 s = 857042759984254168557880549501802188789837994940 # Check that message hash is correct. if SHA1_HASH(msg).encode( 'hex') != "d2d0714f014a9784047eaeccf956520045c45265": print "Wrong SHA-1 hash for the sample message." return x, k = recover_private_key(p, q, g, r, s, msg, SHA1_HASH, hashlib.sha1().block_size * 8) if x == None: print "Failed to recover DSA private key for sample message." return # Check that this x, k pair produces the same signature. status, r1, s1 = DSA_gen_signature_weak(p, q, g, x, msg, SHA1_HASH, hashlib.sha1().block_size * 8, k) if status == False or r1 != r or s1 != s: print "Failed to recover DSA private key for sample message." return # Check agains SHA-1 hash provided. if SHA1_HASH(utils.integer_to_string(x).encode('hex')).encode( 'hex') != "0954edd5e0afe5542a4adf012611a91912a3ec16": print "Failed to recover DSA private key for sample message." return print "DSA private key for sample message: x = " + utils.integer_to_string( x).encode('hex')
def gen_DSA_primes(L, N, seedlen, Hash, outlen): acceptable_pairs = [(1024, 160), (2048, 224), (2048, 256), (3072, 256)] if (L, N) not in acceptable_pairs: return False, None, None if seedlen < N: return False, None, None n = int(math.ceil(float(L) / float(outlen)) - 1) b = L - 1 - n * outlen while True: domain_parameter_seed = utils.string_to_integer(os.urandom(seedlen / 8)) U = utils.string_to_integer(Hash(utils.integer_to_string(domain_parameter_seed))) % 2 ** (N - 1) q = 2 ** (N - 1) + U + 1 - (U % 2) if not is_prime(q): continue offset = 1 for counter in range(4 * L): V = {} for j in range(n + 1): V[j] = utils.string_to_integer(Hash(utils.integer_to_string((domain_parameter_seed + offset + j) % 2 ** seedlen))) W = sum([V[j] * 2 ** (j * outlen) for j in range(n - 1)]) + (V[n] % 2 ** b) * 2 ** (n * outlen) X = W + 2 ** (L - 1) c = X % (2 * q) p = X - (c - 1) if p < 2 ** (L - 1): offset = offset + n + 1 continue if is_prime(p): return True, p, q offset = offset + n + 1 return False, None, None
def RSASSA_PKCS1_v1_5_SIGN(n, d, M): k = (len(bin(n)[2:]) + 7) / 8 # Length of n in octets try: EM = EMSA_PKCS1_v1_5_ENCODE(M, k) except: raise Exception("RSA modulus too short") m = utils.string_to_integer(EM) s = utils.RSA_decrypt(m, n, d) S = utils.integer_to_string(s) return S
def recover_plaintext(self): ciphertext = self.client.intercept_ciphertext() self.fetch_pubkey() message = self.submit_ciphertext(ciphertext) if message != None: print "Direct decryption worked... wtf?" return message c = utils.string_to_integer(ciphertext) s = random.randint(2, self.n - 1) new_c = (pow(s, self.e, self.n) * c) % self.n new_ciphertext = utils.integer_to_string(new_c) new_message = self.submit_ciphertext(new_ciphertext) new_p = utils.string_to_integer(new_message) p = (new_p * utils.invmod(s, self.n)) % self.n message = utils.integer_to_string(p) return message
def crack_ciphertext(server, ciphertext, n, e): k = 0 nbits = len(bin(n)[2:].strip('L')) for i in range(nbits): m = ((k + 1) * n) >> i printstr = to_printable(utils.integer_to_string(m)) sys.stdout.write(printstr) sys.stdout.flush() ciphertext = double_plaintext(ciphertext, n, e) if server.parity_oracle(ciphertext) == True: k = 2 * k else: k = 2 * k + 1 # Used for the "Hollywood" style printing sys.stdout.write("\b" * len(printstr)) sys.stdout.write(" " * len(printstr)) sys.stdout.write("\b" * len(printstr)) sys.stdout.flush() m = ((k + 1) * n) >> nbits return utils.integer_to_string(m)
def forge_signature(data, n, e): k = (len(bin(n)[2:]) + 7) / 8 b = 8 * k H = SHA256_HASH(data) ASN1_GOOP = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20" D = utils.string_to_integer(ASN1_GOOP + H) N = 2**416 - D # 32 (HASH) + 19 (ASN1_GOOP) + 1 (\x00) = 52 ---> 416 bits X = b - 440 # N * 2^X < 2^(b - 15 - 8) (Need 8 bits padding "\xff" at least), and need multiple of 8. MAX_GARBAGE = 2**X - 1 # By picking MAX_GARBAGE as high as possible, we are most likely to find a cube root # whose cube is within the range that starts with the desired bytes. MAX_S3 = 2**(b - 15) - N * 2**X + MAX_GARBAGE s = cube_root(MAX_S3) return utils.integer_to_string(s, k)
def forge_signature(data, n, e): k = (len(bin(n)[2:]) + 7) / 8 b = 8 * k H = SHA256_HASH(data) ASN1_GOOP = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20" D = utils.string_to_integer(ASN1_GOOP + H) N = 2 ** 416 - D # 32 (HASH) + 19 (ASN1_GOOP) + 1 (\x00) = 52 ---> 416 bits X = b - 440 # N * 2^X < 2^(b - 15 - 8) (Need 8 bits padding "\xff" at least), and need multiple of 8. MAX_GARBAGE = 2 ** X - 1 # By picking MAX_GARBAGE as high as possible, we are most likely to find a cube root # whose cube is within the range that starts with the desired bytes. MAX_S3 = 2 ** (b - 15) - N * 2 ** X + MAX_GARBAGE s = cube_root(MAX_S3) return utils.integer_to_string(s, k)
def check_signature_weak(n, e, M, S): k = (len(bin(n)[2:]) + 7) / 8 # Length of n in octets if len(S) != k: return False s = utils.string_to_integer(S) m = pow(s, e, n) try: EM = utils.integer_to_string(m, k) except: return False H = SHA256_HASH(M) ASN1_GOOP = "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20" T = ASN1_GOOP + H if re.match("\x00\x01" + "[\xff]+" + "\x00" + re.escape(T), EM): return True return False
def RSASSA_PKCS1_v1_5_VERIFY(n, e, M, S): k = (len(bin(n)[2:]) + 7) / 8 # Length of n in octets if len(S) != k: return False s = utils.string_to_integer(S) m = pow(s, e, n) try: EM_ = utils.integer_to_string(m, k) except: return False try: EM = EMSA_PKCS1_v1_5_ENCODE(M, k) except: raise Exception("RSA modulus too short") if EM == EM_: return True return False
def test_weak_signature(): msg = "Talk to the hand." status, p, q, g = dsa.DSA_gen_domain_params(2048, 256, 2048, SHA1_HASH, hashlib.sha1().block_size * 8) if status == False: print "Failed to generate DSA domain parameters." return status, x, y = dsa.DSA_gen_per_user_keys(p, q, g) if status == False: print "Failed to generate DSA per-user keys." return status, r, s = DSA_gen_signature_weak(p, q, g, x, msg, SHA1_HASH, hashlib.sha1().block_size * 8) if status == False: print "Failed to generate weak DSA signature." return if dsa.DSA_verify_signature(p, q, g, y, msg, r, s, SHA1_HASH, hashlib.sha1().block_size * 8) == False: print "Failed to verify DSA signature." return x1, k = recover_private_key(p, q, g, r, s, msg, SHA1_HASH, hashlib.sha1().block_size * 8) if x == None or x1 != x: print "Could not recover DSA private key for weak signature." return print "Successfully recovered DSA private key for weak signature: x = " + utils.integer_to_string( x1).encode('hex')
def recover_x_from_k(q, k, r, s, msg, Hash, outlen): N = len(bin(q)[2:]) x = (((s * k) - utils.string_to_integer(Hash(msg)[: min(N, outlen) / 8])) * utils.invmod(r, q)) % q return x # DSA domain parameters p = 0x800000000000000089e1855218a0e7dac38136ffafa72eda7859f2171e25e65eac698c1702578b07dc2a1076da241c76c62d374d8389ea5aeffd3226a0530cc565f3bf6b50929139ebeac04f48c3c84afb796d61e5a4f9a8fda812ab59494232c7d2b4deb50aa18ee9e132bfa85ac4374d7f9091abc3d015efc871a584471bb1 q = 0xf4f47f05794b256174bba6e9b396a7707e563c5b g = 0x5958c9d3898b224b12672c0b98e06c60df923cb8bc999d119458fef538b8fa4046c8db53039db620c094c9fa077ef389b5322a559946a71903f990f1f7e0e025e2d7f7cf494aff1a0470f5b64c36b625a097f1651fe775323556fe00b3608c887892878480e99041be601a62166ca6894bdd41a7054ec89f756ba9fc95302291 # DSA public key y = 0x2d026f4bf30195ede3a088da85e398ef869611d0f68f0713d51c9c1a3a26c95105d915e2d8cdf26d056b86b8a7b85519b1c23cc3ecdc6062650462e3063bd179c2a6581519f674a61f1d89a1fff27171ebc1b93d4dc57bceb7ae2430f98a6a4d83d8279ee65d71c1203d2c96d65ebbf7cce9d32971c3de5084cce04a2e147821 f = open('44.txt', 'r') data = f.read() f.close() signatures = parse_signatures(data) repeated_sigs = find_signatures_with_same_k(signatures) k = find_k_from_repeated_sigs(q, repeated_sigs) r = repeated_sigs[0]['r'] s = repeated_sigs[0]['s'] msg = repeated_sigs[0]['msg'] x = recover_x_from_k(q, k, r, s, msg, SHA1_HASH, hashlib.sha1().block_size * 8) if SHA1_HASH(utils.integer_to_string(x).encode('hex')).encode('hex') == "ca8f6f7c66fa362d40760d135b763eb8527d3d52": print "Found private key x = " + utils.integer_to_string(x).encode('hex') else: print "Failed to find private key."
def Bleichenbacher_attack(server, ciphertext, n, e): k = (len(bin(n)[2:].strip('L')) + 7) / 8 B = 2 ** (8 * (k - 2)) c = utils.string_to_integer(ciphertext) # Step 1 (skip blinding) si = 1 c0 = c Mi = [[2 * B, 3 * B - 1]] i = 1 while True: si_1 = si Mi_1 = Mi # Step 2 if i == 1: # Step 2.a si = (n + 3 * B - 1) / (3 * B) # ceil(n / (3B)) while server.padding_oracle(utils.integer_to_string((c0 * pow(si, e, n)) % n)) != True: si += 1 elif len(Mi) >= 2: # Step 2.b si = si_1 + 1 while server.padding_oracle(utils.integer_to_string((c0 * pow(si, e, n)) % n)) != True: si += 1 else: # Step 2.c a = Mi[0][0] b = Mi[0][1] ri = (2 * b * si_1 - 2 * B + n - 1) / n # ceil((2 * b * si_1 - 2 * B) / n) conforming = False while not conforming: for si in range((2 * B + ri * n + b - 1) / b, (3 * B - 1 + ri * n) / a + 1): if server.padding_oracle(utils.integer_to_string((c0 * pow(si, e, n)) % n)) == True: conforming = True break else: ri += 1 # Step 3 Mi = [] for Ir in Mi_1: a = Ir[0] b = Ir[1] for r in range((a * si - 3 * B + 1 + n - 1) / n, (b * si - 2 * B) / n + 1): new_a = max(a, (2 * B + r * n + si - 1) / si) # to get the ceiling new_b = min(b, (3 * B - 1 + r * n) / si) if new_b >= new_a: Mi.append([new_a, new_b]) # Step 4 if len(Mi) == 1 and Mi[0][0] == Mi[0][1]: m = Mi[0][0] break i += 1 return PKCS1_v1_5_Unpad(utils.integer_to_string(m, k))
def double_plaintext(cipher, n, e): c = utils.string_to_integer(cipher) c = (c * pow(2, e, n)) % n return utils.integer_to_string(c)