139 lines
3.4 KiB
Python
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)
|
|
|