aescbc.py provided a pure-Python Rijndael/AES-CBC fallback consumed only by alfcrypto.AES_CBC, whose sole caller (kindlekey's macOS CryptUnprotectData) never assigns self.crp and so raised AttributeError before reaching it. The live crypto path uses Cryptodome, so delete the dead module and the unused wrapper. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# crypto library mainly by some_updates
|
|
|
|
# pbkdf2.py pbkdf2 code taken from pbkdf2.py
|
|
# pbkdf2.py Copyright © 2004 Matt Johnston <matt @ ucc asn au>
|
|
# pbkdf2.py Copyright © 2009 Daniel Holth <dholth@fastmail.fm>
|
|
# pbkdf2.py This code may be freely used and modified for any purpose.
|
|
|
|
import hmac
|
|
from struct import pack
|
|
import hashlib
|
|
|
|
class Pukall_Cipher(object):
|
|
def __init__(self):
|
|
self.key = None
|
|
|
|
def PC1(self, key, src, decryption=True):
|
|
sum1 = 0;
|
|
sum2 = 0;
|
|
keyXorVal = 0;
|
|
if len(key)!=16:
|
|
raise Exception("PC1: Bad key length")
|
|
wkey = []
|
|
for i in range(8):
|
|
wkey.append(key[i*2]<<8 | key[i*2+1])
|
|
dst = bytearray(len(src))
|
|
for i in range(len(src)):
|
|
temp1 = 0;
|
|
byteXorVal = 0;
|
|
for j in range(8):
|
|
temp1 ^= wkey[j]
|
|
sum2 = (sum2+j)*20021 + sum1
|
|
sum1 = (temp1*346)&0xFFFF
|
|
sum2 = (sum2+sum1)&0xFFFF
|
|
temp1 = (temp1*20021+1)&0xFFFF
|
|
byteXorVal ^= temp1 ^ sum2
|
|
|
|
curByte = src[i]
|
|
|
|
if not decryption:
|
|
keyXorVal = curByte * 257;
|
|
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
|
|
if decryption:
|
|
keyXorVal = curByte * 257;
|
|
for j in range(8):
|
|
wkey[j] ^= keyXorVal;
|
|
|
|
dst[i] = curByte
|
|
|
|
|
|
return bytes(dst)
|
|
|
|
class Topaz_Cipher(object):
|
|
def __init__(self):
|
|
self._ctx = None
|
|
|
|
def ctx_init(self, key):
|
|
ctx1 = 0x0CAFFE19E
|
|
if isinstance(key, str):
|
|
key = key.encode('latin-1')
|
|
for keyByte in key:
|
|
ctx2 = ctx1
|
|
ctx1 = ((((ctx1 >>2) * (ctx1 >>7))&0xFFFFFFFF) ^ (keyByte * keyByte * 0x0F902007)& 0xFFFFFFFF )
|
|
self._ctx = [ctx1, ctx2]
|
|
return [ctx1,ctx2]
|
|
|
|
def decrypt(self, data, ctx=None):
|
|
if ctx == None:
|
|
ctx = self._ctx
|
|
ctx1 = ctx[0]
|
|
ctx2 = ctx[1]
|
|
plainText = ""
|
|
if isinstance(data, str):
|
|
data = data.encode('latin-1')
|
|
for dataByte in data:
|
|
m = (dataByte ^ ((ctx1 >> 3) &0xFF) ^ ((ctx2<<3) & 0xFF)) &0xFF
|
|
ctx2 = ctx1
|
|
ctx1 = (((ctx1 >> 2) * (ctx1 >> 7)) &0xFFFFFFFF) ^((m * m * 0x0F902007) &0xFFFFFFFF)
|
|
plainText += chr(m)
|
|
return plainText
|
|
|
|
class KeyIVGen(object):
|
|
# this only exists in openssl so we will use pure python implementation instead
|
|
# PKCS5_PBKDF2_HMAC_SHA1 = F(c_int, 'PKCS5_PBKDF2_HMAC_SHA1',
|
|
# [c_char_p, c_ulong, c_char_p, c_ulong, c_ulong, c_ulong, c_char_p])
|
|
def pbkdf2(self, passwd, salt, iter, keylen):
|
|
|
|
def xorbytes( a, b ):
|
|
if len(a) != len(b):
|
|
raise Exception("xorbytes(): lengths differ")
|
|
return bytes(bytearray([x ^ y for x, y in zip(a, b)]))
|
|
|
|
def prf( h, data ):
|
|
hm = h.copy()
|
|
hm.update( data )
|
|
return hm.digest()
|
|
|
|
def pbkdf2_F( h, salt, itercount, blocknum ):
|
|
U = prf( h, salt + pack('>i',blocknum ) )
|
|
T = U
|
|
for i in range(2, itercount+1):
|
|
U = prf( h, U )
|
|
T = xorbytes( T, U )
|
|
return T
|
|
|
|
sha = hashlib.sha1
|
|
digest_size = sha().digest_size
|
|
# l - number of output blocks to produce
|
|
l = keylen // digest_size
|
|
if keylen % digest_size != 0:
|
|
l += 1
|
|
h = hmac.new( passwd, None, sha )
|
|
T = b""
|
|
for i in range(1, l+1):
|
|
T += pbkdf2_F( h, salt, iter, i )
|
|
return T[0: keylen]
|
|
|
|
|