Module refinery.lib.unrar.crypt
Encryption and decryption for all RAR format versions.
Expand source code Browse git
"""
Encryption and decryption for all RAR format versions.
"""
from __future__ import annotations
import hashlib
import hmac
import struct
from refinery.lib.types import buf
from refinery.lib.unrar.crc import crc_table as _get_crc_table
from refinery.lib.unrar.headers import (
CRYPT5_KDF_LG2_COUNT_MAX,
CRYPT_BLOCK_SIZE,
SIZE_PSWCHECK,
CryptMethod,
)
class CryptRar13:
"""
RAR 1.3 trivial XOR encryption.
"""
def __init__(self, password: str):
key = [0, 0, 0]
for ch in password:
b = ord(ch) & 0xFF
key[0] = (key[0] + b) & 0xFF
key[1] = (key[1] ^ b) & 0xFF
key[2] = (key[2] + b) & 0xFF
key[2] = ((key[2] << 1) | (key[2] >> 7)) & 0xFF # rotls(Key13[2], 1, 8)
self._key = list(key)
def decrypt(self, data: buf) -> bytearray:
key = list(self._key)
out = bytearray(len(data))
for i, b in enumerate(data):
key[1] = (key[1] + key[2]) & 0xFF
key[0] = (key[0] + key[1]) & 0xFF
out[i] = (b - key[0]) & 0xFF
return out
class CryptRar15:
"""
RAR 1.5 CRC-based XOR stream cipher.
"""
def __init__(self, password: str):
crc_tab = _get_crc_table()
pw_bytes = password.encode('latin-1', errors='replace')
# CRC32 of the full password
psw_crc = 0xFFFFFFFF
for b in pw_bytes:
psw_crc = (psw_crc >> 8) ^ crc_tab[(psw_crc ^ b) & 0xFF]
self._crc_tab = crc_tab
self._key = [
(psw_crc >> 0x00) & 0xFFFF, # Key15[0]
(psw_crc >> 0x10) & 0xFFFF, # Key15[1]
0, # Key15[2]
0, # Key15[3]
]
for b in pw_bytes:
self._key[2] = (self._key[2] ^ (b ^ crc_tab[b])) & 0xFFFF
self._key[3] = (self._key[3] + b + (crc_tab[b] >> 16)) & 0xFFFF
def decrypt(self, data: buf) -> bytearray:
crc_tab = self._crc_tab
key = list(self._key)
out = bytearray(data)
for i in range(len(out)):
key[0] = (key[0] + 0x1234) & 0xFFFF
idx = (key[0] & 0x1FE) >> 1
crc_val = crc_tab[idx]
key[1] = (key[1] ^ crc_val) & 0xFFFF
key[2] = (key[2] - (crc_val >> 16)) & 0xFFFF
key[0] = (key[0] ^ key[2]) & 0xFFFF
key[3] = (((key[3] >> 1) | (key[3] << 15)) & 0xFFFF) ^ key[1]
key[3] = ((key[3] >> 1) | (key[3] << 15)) & 0xFFFF
key[0] = (key[0] ^ key[3]) & 0xFFFF
out[i] ^= (key[0] >> 8) & 0xFF
return out
_INIT_SUBST_TABLE_20 = bytes((
215, 19, 149, 35, 73, 197, 192, 205, 249, 28, 16, 119, 48, 221, 2, 42,
232, 1, 177, 233, 14, 88, 219, 25, 223, 195, 244, 90, 87, 239, 153, 137,
255, 199, 147, 70, 92, 66, 246, 13, 216, 40, 62, 29, 217, 230, 86, 6,
71, 24, 171, 196, 101, 113, 218, 123, 93, 91, 163, 178, 202, 67, 44, 235,
107, 250, 75, 234, 49, 167, 125, 211, 83, 114, 157, 144, 32, 193, 143, 36,
158, 124, 247, 187, 89, 214, 141, 47, 121, 228, 61, 130, 213, 194, 174, 251,
97, 110, 54, 229, 115, 57, 152, 94, 105, 243, 212, 55, 209, 245, 63, 11,
164, 200, 31, 156, 81, 176, 227, 21, 76, 99, 139, 188, 127, 17, 248, 51,
207, 120, 189, 210, 8, 226, 41, 72, 183, 203, 135, 165, 166, 60, 98, 7,
122, 38, 155, 170, 69, 172, 252, 238, 39, 134, 59, 128, 236, 27, 240, 80,
131, 3, 85, 206, 145, 79, 154, 142, 159, 220, 201, 133, 74, 64, 20, 129,
224, 185, 138, 103, 173, 182, 43, 34, 254, 82, 198, 151, 231, 180, 58, 10,
118, 26, 102, 12, 50, 132, 22, 191, 136, 111, 162, 179, 45, 4, 148, 108,
161, 56, 78, 126, 242, 222, 15, 175, 146, 23, 33, 241, 181, 190, 77, 225,
0, 46, 169, 186, 68, 95, 237, 65, 53, 208, 253, 168, 9, 18, 100, 52,
116, 184, 160, 96, 109, 37, 30, 106, 140, 104, 150, 5, 204, 117, 112, 84,
))
_M32 = 0xFFFFFFFF
_NROUNDS = 32
def _rotl32(v: int, n: int) -> int:
return ((v << n) | (v >> (32 - n))) & _M32
class CryptRar20:
"""
RAR 2.0 substitution table + Feistel block cipher.
"""
def __init__(self, password: str, salt: bytes = b''):
self._subst = bytearray(_INIT_SUBST_TABLE_20)
self._key = [0xD3A3B879, 0x3F6D12F7, 0x7515A235, 0xA4E7F123]
self._set_key(password)
def _set_key(self, password: str):
crc_tab = _get_crc_table()
pw_bytes = password.encode('latin-1', errors='replace')
pw_len = len(pw_bytes)
for j in range(256):
for i in range(0, pw_len, 2):
n1 = crc_tab[(pw_bytes[i] - j) & 0xFF] & 0xFF
i1 = i + 1 if i + 1 < pw_len else i
n2 = crc_tab[(pw_bytes[i1] + j) & 0xFF] & 0xFF
k = 1
while n1 != n2:
self._subst[n1], self._subst[(n1 + i + k) & 0xFF] = (
self._subst[(n1 + i + k) & 0xFF], self._subst[n1])
n1 = (n1 + 1) & 0xFF
k += 1
psw = bytearray(pw_bytes)
if pw_len & (CRYPT_BLOCK_SIZE - 1):
padded = (pw_len | (CRYPT_BLOCK_SIZE - 1)) + 1
psw.extend(b'\x00' * (padded - pw_len))
for i in range(0, len(psw), CRYPT_BLOCK_SIZE):
self._encrypt_block(psw, i)
def _subst_long(self, val: int) -> int:
t = self._subst
b0 = t[(val) & 0xFF]
b1 = t[(val >> 8) & 0xFF]
b2 = t[(val >> 16) & 0xFF]
b3 = t[(val >> 24) & 0xFF]
return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
def _encrypt_block(self, data: bytearray, offset: int = 0):
key = self._key
A = struct.unpack_from('<I', data, offset + 0)[0] ^ key[0]
B = struct.unpack_from('<I', data, offset + 4)[0] ^ key[1]
C = struct.unpack_from('<I', data, offset + 8)[0] ^ key[2]
D = struct.unpack_from('<I', data, offset + 12)[0] ^ key[3]
for i in range(_NROUNDS):
T = ((C + _rotl32(D, 11)) ^ key[i & 3]) & _M32
TA = A ^ self._subst_long(T)
T = (((D ^ _rotl32(C, 17)) + key[i & 3]) & _M32)
TB = B ^ self._subst_long(T)
A = C
B = D
C = TA
D = TB
struct.pack_into('<4I', data, offset, C ^ key[0], D ^ key[1], A ^ key[2], B ^ key[3])
self._upd_keys(data[offset:offset + 16])
def _decrypt_block(self, data: bytearray, offset: int = 0):
key = self._key
in_buf = data[offset:offset + 16]
A = struct.unpack_from('<I', data, offset + 0)[0] ^ key[0]
B = struct.unpack_from('<I', data, offset + 4)[0] ^ key[1]
C = struct.unpack_from('<I', data, offset + 8)[0] ^ key[2]
D = struct.unpack_from('<I', data, offset + 12)[0] ^ key[3]
for i in range(_NROUNDS - 1, -1, -1):
T = ((C + _rotl32(D, 11)) ^ key[i & 3]) & _M32
TA = A ^ self._subst_long(T)
T = (((D ^ _rotl32(C, 17)) + key[i & 3]) & _M32)
TB = B ^ self._subst_long(T)
A = C
B = D
C = TA
D = TB
struct.pack_into('<4I', data, offset, C ^ key[0], D ^ key[1], A ^ key[2], B ^ key[3])
self._upd_keys(in_buf)
def decrypt(self, data: buf) -> bytearray:
out = bytearray(data)
for i in range(0, len(out) - 15, 16):
self._decrypt_block(out, i)
return out
def _upd_keys(self, block: bytes | bytearray):
crc_tab = _get_crc_table()
for i in range(0, 16, 4):
self._key[0] ^= crc_tab[block[i] & 0xFF]
self._key[1] ^= crc_tab[block[i + 1] & 0xFF]
self._key[2] ^= crc_tab[block[i + 2] & 0xFF]
self._key[3] ^= crc_tab[block[i + 3] & 0xFF]
def rar3_kdf(password: str, salt: buf) -> tuple[bytes, bytes]:
"""
RAR 3.0 key derivation function.
SHA-1 based, 0x40000 iterations.
Returns (key_16_bytes, iv_16_bytes).
"""
ROUNDS = 0x40000
pw_utf16 = password.encode('utf-16-le')
raw_data = pw_utf16 + bytes(salt)
iv = bytearray(16)
sha1 = hashlib.sha1()
for i in range(ROUNDS):
sha1.update(raw_data)
sha1.update(struct.pack('<I', i)[:3])
if i % (ROUNDS // 16) == 0:
iv_idx = i // (ROUNDS // 16)
if iv_idx < 16:
iv_digest = sha1.copy().digest()
iv[iv_idx] = iv_digest[19]
key_digest = sha1.digest()
key = bytearray(16)
for i in range(4):
for j in range(4):
key[i * 4 + j] = key_digest[i * 4 + 3 - j]
return bytes(key), bytes(iv)
class CryptRar30:
"""
RAR 3.0 AES-128-CBC encryption.
"""
def __init__(self, password: str, salt: buf):
key, iv = rar3_kdf(password, salt)
self._key = key
self._iv = iv
def decrypt(self, data: buf) -> bytes:
from Cryptodome.Cipher import AES
pad_len = (CRYPT_BLOCK_SIZE - (len(data) % CRYPT_BLOCK_SIZE)) % CRYPT_BLOCK_SIZE
if pad_len:
data = bytearray(data)
data.extend(b'\x00' * pad_len)
cipher = AES.new(self._key, AES.MODE_CBC, iv=self._iv)
return cipher.decrypt(data)
def rar5_pbkdf2(password: str, salt: buf, lg2_count: int) -> tuple[bytes, bytes, bytes]:
"""
RAR 5.0 key derivation. Custom PBKDF2 producing:
32 Bytes Key
32 Bytes HashKeyValue
32 Bytes PswCheckValue
Returns (key, hash_key_value, psw_check_value). This is a single PBKDF2 block with intermediate
value extraction.
"""
lg2_count = min(lg2_count, CRYPT5_KDF_LG2_COUNT_MAX)
pw_bytes = password.encode('utf-8')
count = 1 << lg2_count
salt_data = bytes(salt) + b'\0\0\0\01'
u = hmac.new(pw_bytes, salt_data, hashlib.sha256).digest()
fn = bytearray(u)
cur_counts = (count - 1, 16, 16)
results = []
for phase in range(3):
for _ in range(cur_counts[phase]):
u = hmac.new(pw_bytes, u, hashlib.sha256).digest()
for k in range(32):
fn[k] ^= u[k]
results.append(bytes(fn))
return tuple(results)
def rar5_psw_check(psw_check_value: bytes) -> bytes:
"""
XOR-fold 32-byte PswCheckValue down to 8 bytes.
"""
result = bytearray(SIZE_PSWCHECK)
for i in range(len(psw_check_value)):
result[i % SIZE_PSWCHECK] ^= psw_check_value[i]
return bytes(result)
class CryptRar50:
"""
RAR 5.0 AES-256-CBC encryption.
"""
def __init__(
self,
password: str,
salt: buf,
lg2_count: int,
iv: buf,
use_psw_check: bool = False,
expected_psw_check: buf = b'',
):
key, hash_key_value, psw_check_value = rar5_pbkdf2(password, salt, lg2_count)
self._key = key
self._iv = iv
self._hash_key = hash_key_value
if use_psw_check and expected_psw_check:
computed = rar5_psw_check(psw_check_value)
if computed != expected_psw_check:
from refinery.lib.unrar import RarInvalidPassword
raise RarInvalidPassword
@property
def hash_key(self) -> bytes:
return self._hash_key
def decrypt(self, data: buf) -> bytes:
from Cryptodome.Cipher import AES
pad_len = (CRYPT_BLOCK_SIZE - (len(data) % CRYPT_BLOCK_SIZE)) % CRYPT_BLOCK_SIZE
if pad_len:
data = bytearray(data)
data.extend(b'\x00' * pad_len)
cipher = AES.new(self._key, AES.MODE_CBC, iv=self._iv)
return cipher.decrypt(data)
def make_decryptor(
crypt_method: int,
password: str,
salt: buf = b'',
iv: buf = b'',
lg2_count: int = 0,
use_psw_check: bool = False,
psw_check: buf = b'',
):
"""
Create the appropriate decryptor for the given encryption method.
"""
if crypt_method == CryptMethod.CRYPT_NONE:
return None
elif crypt_method == CryptMethod.CRYPT_RAR13:
return CryptRar13(password)
elif crypt_method == CryptMethod.CRYPT_RAR15:
return CryptRar15(password)
elif crypt_method == CryptMethod.CRYPT_RAR20:
return CryptRar20(password, salt)
elif crypt_method == CryptMethod.CRYPT_RAR30:
return CryptRar30(password, salt)
elif crypt_method == CryptMethod.CRYPT_RAR50:
return CryptRar50(password, salt, lg2_count, iv, use_psw_check, psw_check)
else:
raise ValueError(F'Unknown encryption method: {crypt_method}')
Functions
def rar3_kdf(password, salt)-
RAR 3.0 key derivation function. SHA-1 based, 0x40000 iterations. Returns (key_16_bytes, iv_16_bytes).
Expand source code Browse git
def rar3_kdf(password: str, salt: buf) -> tuple[bytes, bytes]: """ RAR 3.0 key derivation function. SHA-1 based, 0x40000 iterations. Returns (key_16_bytes, iv_16_bytes). """ ROUNDS = 0x40000 pw_utf16 = password.encode('utf-16-le') raw_data = pw_utf16 + bytes(salt) iv = bytearray(16) sha1 = hashlib.sha1() for i in range(ROUNDS): sha1.update(raw_data) sha1.update(struct.pack('<I', i)[:3]) if i % (ROUNDS // 16) == 0: iv_idx = i // (ROUNDS // 16) if iv_idx < 16: iv_digest = sha1.copy().digest() iv[iv_idx] = iv_digest[19] key_digest = sha1.digest() key = bytearray(16) for i in range(4): for j in range(4): key[i * 4 + j] = key_digest[i * 4 + 3 - j] return bytes(key), bytes(iv) def rar5_pbkdf2(password, salt, lg2_count)-
RAR 5.0 key derivation. Custom PBKDF2 producing: 32 Bytes Key 32 Bytes HashKeyValue 32 Bytes PswCheckValue Returns (key, hash_key_value, psw_check_value). This is a single PBKDF2 block with intermediate value extraction.
Expand source code Browse git
def rar5_pbkdf2(password: str, salt: buf, lg2_count: int) -> tuple[bytes, bytes, bytes]: """ RAR 5.0 key derivation. Custom PBKDF2 producing: 32 Bytes Key 32 Bytes HashKeyValue 32 Bytes PswCheckValue Returns (key, hash_key_value, psw_check_value). This is a single PBKDF2 block with intermediate value extraction. """ lg2_count = min(lg2_count, CRYPT5_KDF_LG2_COUNT_MAX) pw_bytes = password.encode('utf-8') count = 1 << lg2_count salt_data = bytes(salt) + b'\0\0\0\01' u = hmac.new(pw_bytes, salt_data, hashlib.sha256).digest() fn = bytearray(u) cur_counts = (count - 1, 16, 16) results = [] for phase in range(3): for _ in range(cur_counts[phase]): u = hmac.new(pw_bytes, u, hashlib.sha256).digest() for k in range(32): fn[k] ^= u[k] results.append(bytes(fn)) return tuple(results) def rar5_psw_check(psw_check_value)-
XOR-fold 32-byte PswCheckValue down to 8 bytes.
Expand source code Browse git
def rar5_psw_check(psw_check_value: bytes) -> bytes: """ XOR-fold 32-byte PswCheckValue down to 8 bytes. """ result = bytearray(SIZE_PSWCHECK) for i in range(len(psw_check_value)): result[i % SIZE_PSWCHECK] ^= psw_check_value[i] return bytes(result) def make_decryptor(crypt_method, password, salt=b'', iv=b'', lg2_count=0, use_psw_check=False, psw_check=b'')-
Create the appropriate decryptor for the given encryption method.
Expand source code Browse git
def make_decryptor( crypt_method: int, password: str, salt: buf = b'', iv: buf = b'', lg2_count: int = 0, use_psw_check: bool = False, psw_check: buf = b'', ): """ Create the appropriate decryptor for the given encryption method. """ if crypt_method == CryptMethod.CRYPT_NONE: return None elif crypt_method == CryptMethod.CRYPT_RAR13: return CryptRar13(password) elif crypt_method == CryptMethod.CRYPT_RAR15: return CryptRar15(password) elif crypt_method == CryptMethod.CRYPT_RAR20: return CryptRar20(password, salt) elif crypt_method == CryptMethod.CRYPT_RAR30: return CryptRar30(password, salt) elif crypt_method == CryptMethod.CRYPT_RAR50: return CryptRar50(password, salt, lg2_count, iv, use_psw_check, psw_check) else: raise ValueError(F'Unknown encryption method: {crypt_method}')
Classes
class CryptRar13 (password)-
RAR 1.3 trivial XOR encryption.
Expand source code Browse git
class CryptRar13: """ RAR 1.3 trivial XOR encryption. """ def __init__(self, password: str): key = [0, 0, 0] for ch in password: b = ord(ch) & 0xFF key[0] = (key[0] + b) & 0xFF key[1] = (key[1] ^ b) & 0xFF key[2] = (key[2] + b) & 0xFF key[2] = ((key[2] << 1) | (key[2] >> 7)) & 0xFF # rotls(Key13[2], 1, 8) self._key = list(key) def decrypt(self, data: buf) -> bytearray: key = list(self._key) out = bytearray(len(data)) for i, b in enumerate(data): key[1] = (key[1] + key[2]) & 0xFF key[0] = (key[0] + key[1]) & 0xFF out[i] = (b - key[0]) & 0xFF return outMethods
def decrypt(self, data)-
Expand source code Browse git
def decrypt(self, data: buf) -> bytearray: key = list(self._key) out = bytearray(len(data)) for i, b in enumerate(data): key[1] = (key[1] + key[2]) & 0xFF key[0] = (key[0] + key[1]) & 0xFF out[i] = (b - key[0]) & 0xFF return out
class CryptRar15 (password)-
RAR 1.5 CRC-based XOR stream cipher.
Expand source code Browse git
class CryptRar15: """ RAR 1.5 CRC-based XOR stream cipher. """ def __init__(self, password: str): crc_tab = _get_crc_table() pw_bytes = password.encode('latin-1', errors='replace') # CRC32 of the full password psw_crc = 0xFFFFFFFF for b in pw_bytes: psw_crc = (psw_crc >> 8) ^ crc_tab[(psw_crc ^ b) & 0xFF] self._crc_tab = crc_tab self._key = [ (psw_crc >> 0x00) & 0xFFFF, # Key15[0] (psw_crc >> 0x10) & 0xFFFF, # Key15[1] 0, # Key15[2] 0, # Key15[3] ] for b in pw_bytes: self._key[2] = (self._key[2] ^ (b ^ crc_tab[b])) & 0xFFFF self._key[3] = (self._key[3] + b + (crc_tab[b] >> 16)) & 0xFFFF def decrypt(self, data: buf) -> bytearray: crc_tab = self._crc_tab key = list(self._key) out = bytearray(data) for i in range(len(out)): key[0] = (key[0] + 0x1234) & 0xFFFF idx = (key[0] & 0x1FE) >> 1 crc_val = crc_tab[idx] key[1] = (key[1] ^ crc_val) & 0xFFFF key[2] = (key[2] - (crc_val >> 16)) & 0xFFFF key[0] = (key[0] ^ key[2]) & 0xFFFF key[3] = (((key[3] >> 1) | (key[3] << 15)) & 0xFFFF) ^ key[1] key[3] = ((key[3] >> 1) | (key[3] << 15)) & 0xFFFF key[0] = (key[0] ^ key[3]) & 0xFFFF out[i] ^= (key[0] >> 8) & 0xFF return outMethods
def decrypt(self, data)-
Expand source code Browse git
def decrypt(self, data: buf) -> bytearray: crc_tab = self._crc_tab key = list(self._key) out = bytearray(data) for i in range(len(out)): key[0] = (key[0] + 0x1234) & 0xFFFF idx = (key[0] & 0x1FE) >> 1 crc_val = crc_tab[idx] key[1] = (key[1] ^ crc_val) & 0xFFFF key[2] = (key[2] - (crc_val >> 16)) & 0xFFFF key[0] = (key[0] ^ key[2]) & 0xFFFF key[3] = (((key[3] >> 1) | (key[3] << 15)) & 0xFFFF) ^ key[1] key[3] = ((key[3] >> 1) | (key[3] << 15)) & 0xFFFF key[0] = (key[0] ^ key[3]) & 0xFFFF out[i] ^= (key[0] >> 8) & 0xFF return out
class CryptRar20 (password, salt=b'')-
RAR 2.0 substitution table + Feistel block cipher.
Expand source code Browse git
class CryptRar20: """ RAR 2.0 substitution table + Feistel block cipher. """ def __init__(self, password: str, salt: bytes = b''): self._subst = bytearray(_INIT_SUBST_TABLE_20) self._key = [0xD3A3B879, 0x3F6D12F7, 0x7515A235, 0xA4E7F123] self._set_key(password) def _set_key(self, password: str): crc_tab = _get_crc_table() pw_bytes = password.encode('latin-1', errors='replace') pw_len = len(pw_bytes) for j in range(256): for i in range(0, pw_len, 2): n1 = crc_tab[(pw_bytes[i] - j) & 0xFF] & 0xFF i1 = i + 1 if i + 1 < pw_len else i n2 = crc_tab[(pw_bytes[i1] + j) & 0xFF] & 0xFF k = 1 while n1 != n2: self._subst[n1], self._subst[(n1 + i + k) & 0xFF] = ( self._subst[(n1 + i + k) & 0xFF], self._subst[n1]) n1 = (n1 + 1) & 0xFF k += 1 psw = bytearray(pw_bytes) if pw_len & (CRYPT_BLOCK_SIZE - 1): padded = (pw_len | (CRYPT_BLOCK_SIZE - 1)) + 1 psw.extend(b'\x00' * (padded - pw_len)) for i in range(0, len(psw), CRYPT_BLOCK_SIZE): self._encrypt_block(psw, i) def _subst_long(self, val: int) -> int: t = self._subst b0 = t[(val) & 0xFF] b1 = t[(val >> 8) & 0xFF] b2 = t[(val >> 16) & 0xFF] b3 = t[(val >> 24) & 0xFF] return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24) def _encrypt_block(self, data: bytearray, offset: int = 0): key = self._key A = struct.unpack_from('<I', data, offset + 0)[0] ^ key[0] B = struct.unpack_from('<I', data, offset + 4)[0] ^ key[1] C = struct.unpack_from('<I', data, offset + 8)[0] ^ key[2] D = struct.unpack_from('<I', data, offset + 12)[0] ^ key[3] for i in range(_NROUNDS): T = ((C + _rotl32(D, 11)) ^ key[i & 3]) & _M32 TA = A ^ self._subst_long(T) T = (((D ^ _rotl32(C, 17)) + key[i & 3]) & _M32) TB = B ^ self._subst_long(T) A = C B = D C = TA D = TB struct.pack_into('<4I', data, offset, C ^ key[0], D ^ key[1], A ^ key[2], B ^ key[3]) self._upd_keys(data[offset:offset + 16]) def _decrypt_block(self, data: bytearray, offset: int = 0): key = self._key in_buf = data[offset:offset + 16] A = struct.unpack_from('<I', data, offset + 0)[0] ^ key[0] B = struct.unpack_from('<I', data, offset + 4)[0] ^ key[1] C = struct.unpack_from('<I', data, offset + 8)[0] ^ key[2] D = struct.unpack_from('<I', data, offset + 12)[0] ^ key[3] for i in range(_NROUNDS - 1, -1, -1): T = ((C + _rotl32(D, 11)) ^ key[i & 3]) & _M32 TA = A ^ self._subst_long(T) T = (((D ^ _rotl32(C, 17)) + key[i & 3]) & _M32) TB = B ^ self._subst_long(T) A = C B = D C = TA D = TB struct.pack_into('<4I', data, offset, C ^ key[0], D ^ key[1], A ^ key[2], B ^ key[3]) self._upd_keys(in_buf) def decrypt(self, data: buf) -> bytearray: out = bytearray(data) for i in range(0, len(out) - 15, 16): self._decrypt_block(out, i) return out def _upd_keys(self, block: bytes | bytearray): crc_tab = _get_crc_table() for i in range(0, 16, 4): self._key[0] ^= crc_tab[block[i] & 0xFF] self._key[1] ^= crc_tab[block[i + 1] & 0xFF] self._key[2] ^= crc_tab[block[i + 2] & 0xFF] self._key[3] ^= crc_tab[block[i + 3] & 0xFF]Methods
def decrypt(self, data)-
Expand source code Browse git
def decrypt(self, data: buf) -> bytearray: out = bytearray(data) for i in range(0, len(out) - 15, 16): self._decrypt_block(out, i) return out
class CryptRar30 (password, salt)-
RAR 3.0 AES-128-CBC encryption.
Expand source code Browse git
class CryptRar30: """ RAR 3.0 AES-128-CBC encryption. """ def __init__(self, password: str, salt: buf): key, iv = rar3_kdf(password, salt) self._key = key self._iv = iv def decrypt(self, data: buf) -> bytes: from Cryptodome.Cipher import AES pad_len = (CRYPT_BLOCK_SIZE - (len(data) % CRYPT_BLOCK_SIZE)) % CRYPT_BLOCK_SIZE if pad_len: data = bytearray(data) data.extend(b'\x00' * pad_len) cipher = AES.new(self._key, AES.MODE_CBC, iv=self._iv) return cipher.decrypt(data)Methods
def decrypt(self, data)-
Expand source code Browse git
def decrypt(self, data: buf) -> bytes: from Cryptodome.Cipher import AES pad_len = (CRYPT_BLOCK_SIZE - (len(data) % CRYPT_BLOCK_SIZE)) % CRYPT_BLOCK_SIZE if pad_len: data = bytearray(data) data.extend(b'\x00' * pad_len) cipher = AES.new(self._key, AES.MODE_CBC, iv=self._iv) return cipher.decrypt(data)
class CryptRar50 (password, salt, lg2_count, iv, use_psw_check=False, expected_psw_check=b'')-
RAR 5.0 AES-256-CBC encryption.
Expand source code Browse git
class CryptRar50: """ RAR 5.0 AES-256-CBC encryption. """ def __init__( self, password: str, salt: buf, lg2_count: int, iv: buf, use_psw_check: bool = False, expected_psw_check: buf = b'', ): key, hash_key_value, psw_check_value = rar5_pbkdf2(password, salt, lg2_count) self._key = key self._iv = iv self._hash_key = hash_key_value if use_psw_check and expected_psw_check: computed = rar5_psw_check(psw_check_value) if computed != expected_psw_check: from refinery.lib.unrar import RarInvalidPassword raise RarInvalidPassword @property def hash_key(self) -> bytes: return self._hash_key def decrypt(self, data: buf) -> bytes: from Cryptodome.Cipher import AES pad_len = (CRYPT_BLOCK_SIZE - (len(data) % CRYPT_BLOCK_SIZE)) % CRYPT_BLOCK_SIZE if pad_len: data = bytearray(data) data.extend(b'\x00' * pad_len) cipher = AES.new(self._key, AES.MODE_CBC, iv=self._iv) return cipher.decrypt(data)Instance variables
var hash_key-
Expand source code Browse git
@property def hash_key(self) -> bytes: return self._hash_key
Methods
def decrypt(self, data)-
Expand source code Browse git
def decrypt(self, data: buf) -> bytes: from Cryptodome.Cipher import AES pad_len = (CRYPT_BLOCK_SIZE - (len(data) % CRYPT_BLOCK_SIZE)) % CRYPT_BLOCK_SIZE if pad_len: data = bytearray(data) data.extend(b'\x00' * pad_len) cipher = AES.new(self._key, AES.MODE_CBC, iv=self._iv) return cipher.decrypt(data)