I need to implement file decryption in php. As a result of my attempts, I get a broken archive
I use the code as a basis https://github.com/samloader/samloader/tree/master
I'm interested in the part
elif args.command == "decrypt":
getkey = crypt.getv4key if args.enc_ver == 4 else crypt.getv2key
key = getkey(args.fw_ver, args.dev_model, args.dev_region)
length = os.stat(args.in_file).st_size
with open(args.in_file, "rb") as inf:
with open(args.out_file, "wb") as outf:
crypt.decrypt_progress(inf, outf, key, length)
and
def decrypt_progress(inf, outf, key, length):
""" Decrypt a stream of data while showing a progress bar. """
cipher = AES.new(key, AES.MODE_ECB)
if length % 16 != 0:
raise Exception("invalid input block size")
chunks = length//4096+1
pbar = tqdm(total=length, unit="B", unit_scale=True)
for i in range(chunks):
block = inf.read(4096)
if not block:
break
decblock = cipher.decrypt(block)
if i == chunks - 1:
outf.write(unpad(decblock))
else:
outf.write(decblock)
pbar.update(4096)
def getv4key(version, model, region):
""" Retrieve the AES key for V4 encryption. """
client = fusclient.FUSClient()
version = versionfetch.normalizevercode(version)
req = request.binaryinform(version, model, region, client.nonce)
resp = client.makereq("NF_DownloadBinaryInform.do", req)
root = ET.fromstring(resp)
fwver = root.find("./FUSBody/Results/LATEST_FW_VERSION/Data").text
logicval = root.find("./FUSBody/Put/LOGIC_VALUE_FACTORY/Data").text
deckey = request.getlogiccheck(fwver, logicval)
return hashlib.md5(deckey.encode()).digest()
I rewrote it, there are doubts about this line
hashlib.md5(deckey.encode()).digest()
I got it like this `$deckey = 'AU77D7K3SAU/D3UU';
$key = hash('md5', $deckey, true);`
This is right?
And the part of the code that is responsible for my decryption
<?php
$source = 'file.zip.enc4';
$dest = 'file.zip';
$source = fopen($source, 'r');
$dest = fopen($dest, 'w');
$chunkSize = 4096;
while (!feof($source)) {
$chunk = fread($source, $chunkSize + 1);
if (!$chunk) {
break;
}
$encryptedChunk = openssl_decrypt($chunk, 'aes-128-ecb', $key, OPENSSL_RAW_DATA);
fwrite($dest, $encryptedChunk);
}
fclose($source);
fclose($dest);
Help me figure out what I'm doing wrong
Edit: ...in my code I used openssl_decrypt
, there was an error when I wrote here. The problem seems to be with php
$source = 'file.zip.enc4';
$dest = 'file.zip';
$source = fopen($source, 'r');
$dest = fopen($dest, 'w');
$chunkSize = 4096;
while (!feof($source)) {
$chunk = fread($source, $chunkSize + 1);
$encryptedChunk = openssl_decrypt($chunk, 'aes-128-ecb', $key, OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING);
var_dump(openssl_error_string());
fwrite($dest, $encryptedChunk);}
fclose($source);
fclose($dest);
i have errors
string(58) "error:1C80006B:Provider routines::wrong final block length"
openssl works through the console. I'll do it via exec('openssl...', $res);
The porting of the key derivation via MD5 that you suggested is correct.
However, there is an issue with the padding: From the Python code, it can be concluded that the PKCS#7 padding is disabled for all chunks except the last one during encryption. This must be taken into account when decrypting with the PHP code.
A possible PHP implementation (based on your code) is:
$source = 'file.zip.enc4';
$dest = 'file.zip';
$deckey = "AU77D7K3SAU/D3UU";
$key = hash('md5', $deckey, true);
$chunkSize = 4096;
$fileSize = filesize($source);
$source = fopen($source, 'rb');
$dest = fopen($dest, 'wb');
$totalBytesRead = 0;
while (!feof($source)) {
$chunk = fread($source, $chunkSize);
if (!$chunk) {
break;
}
$totalBytesRead += strlen($chunk);
$padding = ($totalBytesRead < $fileSize) ? OPENSSL_ZERO_PADDING : 0;
$decryptedChunk = openssl_decrypt($chunk, 'aes-128-ecb', $key, OPENSSL_RAW_DATA | $padding);
fwrite($dest, $decryptedChunk);
}
fclose($source);
fclose($dest);
Explanation: OPENSSL_ZERO_PADDING
disables the default PKCS#7 padding in PHP/OpenSSL (note that the name of the flag is misleading and could assume the enabling of Zero padding; but in fact this flag disables the default padding).
The code above determines the total number of bytes read so far. If this is less than the file size, the chunk just read is not the last one, so the default padding must be disabled, otherwise it is the last chunk and the default padding remains enabled.
The decrypted file file.zip (4337718623 bytes) is one byte smaller than the encrypted file file.zip.enc4 (4337718624 bytes), which is due to the padding of the last chunk.
The decrypted file can be successfully extracted and provides an archive with 5 .tar.md5-files.