Files
ctf/2025/glacier/rev/c2/dec_rc5_chatgpt.py
2025-11-28 12:20:39 +01:00

139 lines
3.4 KiB
Python

import hashlib
import struct
# ----------------------------
# RC5 core parameters
# ----------------------------
W = 32 # word size in bits
R = 12 # rounds
B = 16 # key bytes (RC5-32/12/16)
WORD_MASK = 0xFFFFFFFF
BLOCK_SIZE = 8 # 2 * 32-bit words
# ----------------------------
# Utility: Rotate left/right
# ----------------------------
def rotl(x, n):
return ((x << n) & WORD_MASK) | (x >> (32 - n))
def rotr(x, n):
return ((x >> n) | (x << (32 - n))) & WORD_MASK
# ----------------------------
# Key Expansion
# ----------------------------
def expand_key(key: bytes):
# Constants for RC5 with w=32
P32 = 0xB7E15163
Q32 = 0x9E3779B9
# Convert key into c 32-bit words L[0..c-1]
c = max(1, B // 4)
L = list(struct.unpack("<" + "I" * c, key))
# Initialize S-array
t = 2 * (R + 1)
S = [0] * t
S[0] = P32
for i in range(1, t):
S[i] = (S[i - 1] + Q32) & WORD_MASK
# Mix key into S
i = j = 0
A = Bv = 0
for _ in range(3 * max(t, c)):
A = S[i] = rotl((S[i] + A + Bv) & WORD_MASK, 3)
Bv = L[j] = rotl((L[j] + A + Bv) & WORD_MASK, (A + Bv) & 31)
i = (i + 1) % t
j = (j + 1) % c
return S
# ----------------------------
# Block Encrypt/Decrypt
# ----------------------------
def rc5_encrypt_block(block: bytes, S):
A, B = struct.unpack("<II", block)
A = (A + S[0]) & WORD_MASK
B = (B + S[1]) & WORD_MASK
for i in range(1, R + 1):
A = (rotl(A ^ B, B & 31) + S[2 * i]) & WORD_MASK
B = (rotl(B ^ A, A & 31) + S[2 * i + 1]) & WORD_MASK
return struct.pack("<II", A, B)
def rc5_decrypt_block(block: bytes, S):
A, B = struct.unpack("<II", block)
for i in range(R, 0, -1):
B = rotr((B - S[2 * i + 1]) & WORD_MASK, A & 31) ^ A
A = rotr((A - S[2 * i]) & WORD_MASK, B & 31) ^ B
B = (B - S[1]) & WORD_MASK
A = (A - S[0]) & WORD_MASK
return struct.pack("<II", A, B)
# ----------------------------
# CBC mode + PKCS#7
# ----------------------------
def rc5_cbc_encrypt(key: bytes, iv: bytes, plaintext: bytes) -> bytes:
if len(iv) != BLOCK_SIZE:
raise ValueError("IV must be 8 bytes")
S = expand_key(key)
blocks = []
prev = iv
for i in range(0, len(plaintext), BLOCK_SIZE):
block = plaintext[i:i + BLOCK_SIZE]
x = bytes(a ^ b for a, b in zip(block, prev))
enc = rc5_encrypt_block(x, S)
blocks.append(enc)
prev = enc
return b"".join(blocks)
def rc5_cbc_decrypt(key: bytes, iv: bytes, ciphertext: bytes) -> bytes:
if len(iv) != BLOCK_SIZE:
raise ValueError("IV must be 8 bytes")
if len(ciphertext) % BLOCK_SIZE != 0:
raise ValueError("Ciphertext not aligned to block size")
S = expand_key(key)
blocks = []
prev = iv
for i in range(0, len(ciphertext), BLOCK_SIZE):
block = ciphertext[i:i + BLOCK_SIZE]
dec = rc5_decrypt_block(block, S)
x = bytes(a ^ b for a, b in zip(dec, prev))
blocks.append(x)
prev = block
return b"".join(blocks)
KEY = bytes.fromhex("99 90 9c 9d d5 8c 81 82 91 98 94 95 dd 84 89 9a")
ITERATIONS = 85454
key_bytes = hashlib.pbkdf2_hmac("sha256", KEY, b"", ITERATIONS, dklen=16)
print(f"[+] Key: {key_bytes.hex()}")
iv = bytes.fromhex("bb7cdd2e104c3410")
with open("./output/85454_bb7cdd2e104c3410.enc", "rb") as f:
ct = f.read()
dec = rc5_cbc_decrypt(key_bytes, iv, ct)
print("decrypted:", dec)