def rsa_echo(self, ctext): # Eve is actually MITMing, not just eavesdropping. # To simulate an eavesdrop, have Eve discard the plaintext. self.parley("echo", ctext) # Steve will reject repeat submissions. assert self.parley("echo", ctext) is None # Eve cracks the ciphertext on her own. C = from_bytes(ctext) E, N = self.remote_pubkey S = random.randint(2, N - 1) C_ = (C * modexp(S, E, N)) % N ptext_ = self.parley("echo", to_bytes(C_)) P_ = from_bytes(ptext_) P = (P_ * invmod(S, N)) % N ptext = to_bytes(P) print("Eve cracked Carol's plaintext:") print() print(" " * 4 + ptext.decode()) print() # Carol won't notice a thing :P return ptext
def srp_authn(self, email, input_hmac): account = self.accounts[email] salt = account["salt"] salt_bytes = to_bytes(salt) A, b, u = account["A"], account["b"], account["u"] for password in WORDS: # Find x as in Steven's `hello` step/Carla's `kexch` step. x_bytes = sha256(salt_bytes + password.encode()).digest() x = from_bytes(x_bytes) # Find v as in Steven's `hello` step. v = modexp(self.g, x, self.n) # Find S and K as in Steven's `kexch` step. S = modexp(A * modexp(v, u, self.n), b, self.n) K = sha256(to_bytes(S)).digest() if hmac_sha256(K, salt_bytes) == input_hmac: return f"{email}, your password is '{password}'. Muahahaha!" # Password wasn't found in the dictionary. return f"These are not the droids you're looking for."
def srp_kexch(self): a = random.getrandbits(1536) A = modexp(self.g, a, self.n) # Carol now receives u from Steve. self.salt, B, u = self.parley("kexch", self.email, A) x_bytes = sha256(to_bytes(self.salt) + self.password.encode()).digest() x = from_bytes(x_bytes) # k is not used anymore. S = modexp(B, a + u * x, self.n) self.K = sha256(to_bytes(S)).digest()
def srp_kexch(self): a = random.getrandbits(1536) A = modexp(self.g, a, self.n) self.salt, B = self.parley("kexch", self.email, A) u_bytes = sha256(to_bytes(A, B)).digest() u = from_bytes(u_bytes) x_bytes = sha256(to_bytes(self.salt) + self.password.encode()).digest() x = from_bytes(x_bytes) S = modexp(B - self.k * modexp(self.g, x, self.n), a + u * x, self.n) self.K = sha256(to_bytes(S)).digest()
def decryptor(ctext, pubkey, oracle, show=True): e, n = pubkey two = modexp(2, e, n) # Decryption algorithm as described in the challenge. # Mostly works, but usually fails for the last character. # # lower, upper, ptext = 0, n, b"" # while lower < upper: # mid = (upper + lower) // 2 # if oracle(ctext): # upper -= mid # else: # lower += mid # return to_bytes(upper) lower, upper, bit_length = 0, 1, n.bit_length() for i in range(1, bit_length + 1): diff, lower, upper = upper - lower, lower << 1, upper << 1 ctext = (ctext * two) % n if oracle(ctext): upper -= diff else: lower += diff print("\r" + to_str((upper * n) >> i) + "\033[K", end="", flush=True) else: print() return to_bytes((upper * n) >> bit_length)
def srp_kexch(self, email, A): account = self.accounts[email] b = random.getrandbits(1536) B = self.k * account["v"] + modexp(self.g, b, self.n) u_bytes = sha256(to_bytes(A, B)).digest() u = from_bytes(u_bytes) # We can't simply calculate the `A * v ** u` part of # `S = (A * v ** u) ** b % n`; the result is huge. But it seems # that all arithmetic is performed modulo n of late. *hint hint* S = modexp(A * modexp(account["v"], u, self.n), b, self.n) account["K"] = sha256(to_bytes(S)).digest() return account["salt"], B
def srp_hello(self, email, password): salt = random.getrandbits(32) x_bytes = sha256(to_bytes(salt) + password.encode()).digest() x = from_bytes(x_bytes) v = modexp(self.g, x, self.n) self.accounts[email] = {"password": password, "salt": salt, "v": v} return self.n, self.g, self.k
def main(): # NOTE BEFORE STARTING: Do not be fooled! Signatures are created using # private keys, notwithstanding the (perhaps intentionally misleading?) # wording in this and the previous challenge that might imply otherwise. lines = loader("44.txt", lambda l: l.rstrip("\n").split(": ")) msgs = [] for i in range(0, len(lines), 4): block = lines[i:i + 4] # After the rstrip() and split() above, a block looks like: # # [["msg", "Listen for me, you better listen for me now. "], # ["s", "1267396447369736888040262262183731677867615804316"], # ["r", "1105520928110492191417703162650245113664610474875"], # ["m", "a4db3de27e2db3e5ef085ced2bced91b82e0df19"]] # # We have a message, the components of a DSA signature, and the SHA1 # hash of the message. Everything here's a `str` at the moment, so # we'll transform the values. # # There's an error in "m" for one of the blocks in the challenge data, # but we can just calculate the hash ourselves. block[0][1] = block[0][1].encode() block[1][1] = int(block[1][1]) block[2][1] = int(block[2][1]) block[3][1] = from_bytes(SHA1(block[0][1]).digest()) msgs.append({data[0]: data[1] for data in block}) print(f"Loaded {len(msgs)} DSA-signed messages.") print("Recovering private key from the first repeated nonce we detect.") for msg1, msg2 in combinations(msgs, 2): if msg1["r"] != msg2["r"]: continue m1, m2 = msg1["m"], msg2["m"] s1, s2 = msg1["s"], msg2["s"] k = (((m1 - m2) % Q) * invmod(s1 - s2, Q)) % Q privkey = recover_dsa_privkey(k, msg1["r"], s1, m1) digest = SHA1(to_hexstring(to_bytes(privkey))).hexdigest() assert digest == "ca8f6f7c66fa362d40760d135b763eb8527d3d52" print() print("Recovered key:", privkey) break else: print("Failed to recover key!")
def forge_rsa_e3(bs, pubkey, hash_cls=SHA1): e, n = pubkey n_byte_length = byte_length(n) # Создание дополненного хэша, который начинается с "0x0001ff00 <ASN.1> <ptext_digest>", # заканчивается практически чем угодно, и имеет такой же размер, как и модуль. phash = b"\x00\x01\xff" + ASN_1 + hash_cls(bs).digest() phash = phash.ljust(n_byte_length, b"\xff") # Найти целое число от корня этого хэша; это не должно быть идеально. eth_root = nth_root(from_bytes(phash), e) forged_sig = to_bytes(eth_root) # Окончательная поддельная подпись корректна по праву и равна модулю. return forged_sig.rjust(n_byte_length, b"\x00")
def srp_kexch(self, email, A): account = self.accounts[email] b = random.getrandbits(1536) B = modexp(self.g, b, self.n) # u is now simply a random uint128. u = random.getrandbits(128) S = modexp(A * modexp(account["v"], u, self.n), b, self.n) account["K"] = sha256(to_bytes(S)).digest() # Steven now sends u to Carl. return account["salt"], B, u
def forge_rsa_e3(bs, pubkey, hash_cls=SHA1): e, n = pubkey n_byte_length = byte_length(n) # Craft a padded hash that begins with "0x0001ff00<ASN.1><ptext_digest>", # ends with pretty much anything, and is as long as the modulus. phash = b"\x00\x01\xff" + ASN_1 + hash_cls(bs).digest() phash = phash.ljust(n_byte_length, b"\xff") # Find the integer e'th root of this hash; it doesn't need to be perfect. eth_root = nth_root(from_bytes(phash), e) forged_sig = to_bytes(eth_root) # Final forged signature is right-justified to be as long as the modulus. return forged_sig.rjust(n_byte_length, b"\x00")
def decryptor(ctext, pubkey, oracle, show=True): e, n = pubkey two = modexp(2, e, n) lower, upper, bit_length = 0, 1, n.bit_length() for i in range(1, bit_length + 1): diff, lower, upper = upper - lower, lower << 1, upper << 1 ctext = (ctext * two) % n if oracle(ctext): upper -= diff else: lower += diff print("\r" + to_str((upper * n) >> i) + "\033[K", end="", flush=True) else: print() return to_bytes((upper * n) >> bit_length)
def main(): print("First, we'll test our DSA implementation.") _test_dsa() print() print("Now, let's bruteforce a DSA private key from a 16-bit subkey.") print() ptext = ( b"For those that envy a MC it can be hazardous to your health\n" b"So be friendly, a matter of life and death, just like a etch-a-sketch\n" ) sig = [ 548099063082341131477253921760299949438196259240, 857042759984254168557880549501802188789837994940, ] print(f"Plaintext:") print_indent(*ptext.split(b"\n"), width=70, as_hex=False) print(f"Bruteforced private key:") privkey = bruteforce_dsa_privkey(ptext, sig) print_indent(privkey, as_hex=False) # This part is a little convoluted, but the SHA-1 hashes mentioned # in the challenge must've been mentioned ~for a reason~!! known_sha1s = [ "d2d0714f014a9784047eaeccf956520045c45265", "0954edd5e0afe5542a4adf012611a91912a3ec16", ] sha1s = [ SHA1(ptext).hexdigest(), SHA1(to_hexstring(to_bytes(privkey))).hexdigest() ] print( "Calculated hashes match for plaintext and private key:", all(a == b for a, b in zip(known_sha1s, sha1s)), )
def main(): ptext = random.choice(PTEXTS) print("Generating 3 public RSA keys and encrypting a plaintext.") print() n, c = [], [] for i in range(3): pubkey, privkey = make_rsa_keys() ctext = rsa(ptext, pubkey) assert rsa(ctext, privkey) == ptext print(f"Ciphertext {i}:") print_indent(ctext) n.append(pubkey[1]) c.append(from_bytes(ctext)) if len(set(c)) == 1: print("The ciphertexts are sometimes identical.") print("This is fine; we also rely upon the public keys differing.") print() result, n_012 = 0, 1 for i in range(3): ms = n[(i + 1) % 3] * n[(i + 2) % 3] result += c[i] * ms * invmod(ms, n[i]) n_012 *= n[i] result = nth_root(result % n_012, 3) print("Cracked plaintext:") print_indent(to_bytes(result), as_hex=False)
def srp_authn(self, email, input_hmac): account = self.accounts[email] hmac = hmac_sha256(account["K"], to_bytes(account["salt"])) return f"Login {'OK' if hmac == input_hmac else 'FAIL'}, {email}."