commit 897272d7c17c1149480c13203f4fce97226ecf4e Author: Emile Clark-Boman Date: Sat Jun 21 17:42:00 2025 +1000 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c18dd8d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/NOTES b/NOTES new file mode 100644 index 0000000..3022073 --- /dev/null +++ b/NOTES @@ -0,0 +1,5 @@ +Ideas: + 1. Given an empty string of bytes, bcrypt('') == H == 184467... + So is there any way to "force rotate" back to H modulo M? + 2. Can I get h % M == 0? If so then could potentially control the + next iteration value? diff --git a/bcrypt.ctf.py b/bcrypt.ctf.py new file mode 100644 index 0000000..cc79d65 --- /dev/null +++ b/bcrypt.ctf.py @@ -0,0 +1,29 @@ +def hashfn(x: bytes) -> int: + h = 18446744073709551614 + + for (i, b) in enumerate(x): + h *= h * (b + 1) + k = 59275109328752 * (i + 1) + for j in range(8): + k ^= b << (j * j) + h += k + h %= (2 ** 64) + + return h + + +print("It's not even worth trying, because bcrypt is perfect!") +print("Whatever, it's your time that you're wasting anyway...") +a = bytes(input("Message 1: "), 'utf-8') +b = bytes(input("Message 2: "), 'utf-8') + +if a != b and hashfn(a) == hashfn(b): + flag = open('flag.txt').read() + print( + f"Congrats! Here's {len(flag)} characters of " + f"text for your hours of hard work: {flag}" + ) +elif a == b: + print("Those are the same message...") +else: + print("Trivially false!") diff --git a/bcrypt/bcrypt.py b/bcrypt/bcrypt.py new file mode 100644 index 0000000..29a373b --- /dev/null +++ b/bcrypt/bcrypt.py @@ -0,0 +1,109 @@ +from math import gcd +from random import randint, randbytes + +H = 18446744073709551614 +K = 59275109328752 +M = 2**64 + +def Rjb(j: int, b: int) -> int: + return b << j**2 + +def Rb(b: int) -> int: + Rb = 0 + for j in range(8): + Rb ^= Rjb(j, b) + return Rb + +''' +Returns a hashmap (python dictionary) of b -> R(b) +for all b such that: min < b < max +''' +def precompute_R(min: int, + max: int) -> dict[int, int]: + b = min + R_table = {} + while b <= max: + R_table[b] = Rb(b) + b += 1 + return R_table + + +def hashfn(x: bytes, R_table: dict[int, int]) -> int: + h = -2 + for i in range(len((x))): + b = x[i] + h = h**2 * (b+1) + ((K * (i+1)) ^ R_table[b]) + h %= M + return h % M + +def bcrypt(x: bytes) -> int: + h = 18446744073709551614 + + for (i, b) in enumerate(x): + h *= h * (b + 1) + k = 59275109328752 * (i + 1) + for j in range(8): + k ^= b << (j * j) + h += k + h %= (2 ** 64) + + return h + +def debug_hashes_eq(x: bytes, R_table: dict[int, int]) -> bool: + return hashfn(x, R_table) == bcrypt(x) + +def debug_test_random_hashes(trials: int, + max_bytes: int = 16, + quiet: bool = False) -> bytes | None: + R_table = precompute_R(0, 255) + for i in range(trials): + # generate random bytes + num_bytes = randint(0, max_bytes) + x = randbytes(num_bytes) + + # test the modified bcrypt with the original + hash_test = hashfn(x, R_table) + hash_bcrypt = bcrypt(x) + if hash_test != hash_bcrypt: + if not quiet: + print(f'Your hashfn sucks, big mistake bucko!! (iter: {i})') + print(hash_test) + print(hash_bcrypt) + print([str(b) for b in x]) + return x + + if not quiet: + print('Impeccable hashfn holy moly!!') + return None + + +def main() -> None: + print(f'gcd(H,K): {gcd(H,K)}') + print(f'gcd(H,M): {gcd(H,M)}') + print(f'gcd(K,M): {gcd(K,M)}') + if debug_test_random_hashes(10000) != None: + R_table = precompute_R(0, 255) + x = bytes(input('x: '), 'utf-8') + hash_test = hashfn(x, R_table) + hash_bcrypt = bcrypt(x) + print(f'hashfn: {hash_test}') + print(f'bcrypt: {hash_bcrypt}') + + # a = bytes(input("A: "), 'utf-8') + # b = bytes(input("B: "), 'utf-8') + + # if a != b and hashfn(a) == hashfn(b): + # print('*** YOU WIN ***') + # elif a == b: + # print('Idiot those are the same') + # else: + # print("Trivially false!") + + +if __name__ == '__main__': + try: + main() + except KeyboardInterrupt: + print('\n[!] Received SIGINT') + except EOFError: + print('\n[!] Reached EOF') diff --git a/bcrypt/working/shifts.py b/bcrypt/working/shifts.py new file mode 100644 index 0000000..dcdbe37 --- /dev/null +++ b/bcrypt/working/shifts.py @@ -0,0 +1,23 @@ +from typing import Any + +def clamp_pos(x: int): + return x if x > 0 else 0 + +def lpad(x: Any, n: int, pad: chr = ' '): + x = str(x) + return clamp_pos(n - len(x))*pad + x + +def debug_R(B: int): + # B = int('1'*7, 2) + for j in range(8): + j_sq = j**2 + R_j = B << j_sq + lpadbin_R_j = lpad(bin(R_j)[2:], 64, pad='0') + print(f'{lpad(j_sq, 2)}: {lpadbin_R_j} {R_j}') + +def main(): + B = int(input('B: '), 2) + debug_R(B) + +if __name__ == '__main__': + main()