def test_46(self): plain = cu.base64_to_bytes( 'VGhhdCdzIHdoeSBJIGZvdW5kIHlvdSBkb24ndCBwbGF5IGFyb3VuZCB3aXRoIHRoZSBGdW5reSBDb2xkIE1lZGluYQ==' ) plain_int = int.from_bytes(plain, 'big') public, is_even = get_parity_oracle() e, n = public cipher = ck.cipher_RSA(plain_int, public) # constant which will allow us to double the plaintext double_plain = pow(2, e, n) max_iter = ceil(log(n, 2)) """casting the bounds as Decimal type, and setting the precision to have as many bits as the modulus, is *super* important. If we don't do this rounding errors will screw us, and trying to do this with integer division without messing up the last byte is annoying""" lower_bound, upper_bound = Decimal(0), Decimal(n) getcontext().prec = max_iter for _ in range(max_iter): cipher = double_plain * cipher % n if is_even(cipher): upper_bound = (lower_bound + upper_bound) / 2 else: lower_bound = (lower_bound + upper_bound) / 2 if int(upper_bound) == int(lower_bound): break decrypted = cu.int_to_bytes(int(upper_bound)) self.assertEqual(plain, decrypted)
def PKCS1v1p5(plain, N): num_bytes = N // 8 plain_bytes = cu.int_to_bytes(plain) num_pad = num_bytes - 3 - len(plain_bytes) padding = cu.random_bytes(num_pad) padded = bytes([0, 2]) + padding + bytes([0]) + plain_bytes return int.from_bytes(padded, 'big')
def login(self, server, DH_keys=None): if DH_keys is None: public, private = ck.gen_DH_keys(p=self.N, g=self.g) else: public, private = DH_keys simple = server.simple response = server.login(self.email, public) self.server = server if simple: self.salt, server_public, scramble = response else: self.salt, server_public = response scramble = srp_scrambler(public, server_public) m = sha256() m.update(self.salt + bytes(self.password, 'utf-8')) xH = m.digest() x = int.from_bytes(xH, byteorder='big') if simple: S = pow(server_public, private + scramble * x, self.N) else: S = pow(self.g, x, self.N) S = pow(server_public - self.k * S, private + scramble * x, self.N) m = sha256() m.update(cu.int_to_bytes(S)) self.session_key = m.digest()
def test_PKCS(self): N = 1024 public, oracle = get_PKCS_oracle(N=N) plain = randbelow(2**64) padded = PKCS1v1p5(plain, N) self.assertEqual(len(cu.int_to_bytes(padded)), ceil(N / 8) - 1) cipher = ck.cipher_RSA(padded, public) self.assertTrue(oracle(cipher))
def test_37(self): """Since SRP, server-side, multiplies by the client's public key prior to modexp and hashing, passing any multiple of N (including 0!) will result in key == SHA256(0). Note that we don't need to know g, N, k, the password. NOTHING. Just pass (email, 0) and SHA256(0) is your key!!!""" self.client.login(self.server, DH_keys=(0, 0)) m = sha256() m.update(cu.int_to_bytes(0)) self.client.session_key = m.digest() self.assertTrue(self.client.validate())
def _srp_key_simple(self, email, client_public): salt, verifier = self.login_info[email] public, private = ck.gen_DH_keys(p=self.N, g=self.g) # compute and store session key scramble = randbelow(2**128) S = pow(verifier, scramble, self.N) S = pow(client_public * S, private, self.N) m = sha256() m.update(cu.int_to_bytes(S)) self.session_keys[email] = m.digest() return salt, public, scramble
def _srp_key_standard(self, email, client_public): salt, verifier = self.login_info[email] public, private = ck.gen_DH_keys(p=self.N, g=self.g) public = (public + self.k * verifier) % self.N # compute and store session key scramble = srp_scrambler(client_public, public) S = pow(verifier, scramble, self.N) S = pow(client_public * S, private, self.N) m = sha256() m.update(cu.int_to_bytes(S)) self.session_keys[email] = m.digest() return salt, public
def cipher_RSA(data, key): """Process data using the given RSA key (exponent, modulus) Input data can be an int or bytes-like (interpreted as big-endian). We'll return either an int or bytes, respectively """ byte_input = False if isinstance(data, bytes) or isinstance(data, bytearray): byte_input = True data = int.from_bytes(data, 'big') cipher = pow(data, key[0], key[1]) if byte_input: cipher = int_to_bytes(cipher) return cipher
def key_exchange(self, p, g, public): if self.mod_g is False: """Parameter-injection. Make both parties think the public-key is p, yielding a secret of 0 (empty bytes)""" self.server.key_exchange(p, g, p) return p """Malicious g-value, but same idea. Control the estimated public-key so that the secret is known a priori""" new_g = choice([1, p, p - 1]) if new_g == p: fake_public, secret = 0, 0 else: fake_public, secret = 1, 1 self.key = cbc_keygen(cu.int_to_bytes(secret)) return self.server.key_exchange(p, new_g, fake_public)
def crack(self, email): public, salt, scramble = self.login_info[email] validation = self.validations[email] for pw in WEAK_PASSWORDS: m = sha256() m.update(salt + bytes(pw, 'utf-8')) xH = m.digest() x = int.from_bytes(xH, byteorder='big') S = pow(self.g, scramble * x, self.N) S = public * S % self.N m = sha256() m.update(cu.int_to_bytes(S)) key_guess = m.digest() expected = hmac.new(key_guess, salt, sha256).digest() if hmac.compare_digest(validation, expected): self.passwords[email] = pw return True return False
def test_42(self): # we only know the public key, so we have to forge the signature public, _ = ck.gen_RSA_keys(N=3072) message = b'hi mom' m = sha1() m.update(message) message_hash = m.digest() block = bytes([0]) + SHA1_ASN + message_hash """Bleichenbacher's 'pencil and paper' attack strategy doesn't work for smaller moduli (e.g., N=1024), and via brute-forcing I couldn't find a zero-padded forged signature that worked, so I'm saying screw it and doing this for 3072 bit RSA""" D = int.from_bytes(block, 'big') M = 2**288 - D F = 2**1019 - 2**34 * M // 3 forged = cu.int_to_bytes(F) self.assertTrue(verify_RSA(message_hash, forged, public))
def srp_scrambler(client_public, server_public): """compute the scrambling integer used in the SRP protocal, using two ephemeral public keys""" m = sha256() m.update(cu.int_to_bytes(client_public) + cu.int_to_bytes(server_public)) return int.from_bytes(m.digest(), byteorder='big')
def gen_DH_secret(public, private, p=DH_P): """Return a Diffie-Hellman secret key, as a bytes-like""" return int_to_bytes(pow(public, private, p))