Improve decryption performance

- Generate only cypher once per file
- Make Decryptor instance per thread
This commit is contained in:
DJDoubleD
2024-07-24 19:32:35 +02:00
parent 41743a873a
commit 318f980027
2 changed files with 50 additions and 13 deletions

View File

@@ -9,13 +9,32 @@ import java.security.MessageDigest;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
public class DeezerDecryptor {
private final Cipher cipher;
public static void decryptFile(String trackId, String inputFilename, String outputFilename) throws IOException {
/**
* Constructor initializes the key and cipher for the given track ID.
* @param trackId Track ID used to generate decryption key
* @throws Exception If there is an issue initializing the cipher
*/
public DeezerDecryptor(String trackId) throws Exception {
this.cipher = Cipher.getInstance("Blowfish/CBC/NoPadding");
byte[] IV = {00, 01, 02, 03, 04, 05, 06, 07};
SecretKeySpec Skey = new SecretKeySpec(getKey(trackId), "Blowfish");
cipher.init(Cipher.DECRYPT_MODE, Skey, new IvParameterSpec(IV));
}
/**
* Decrypts a file by reading it in chunks and decrypting every 3rd chunk of exactly 2048 bytes.
* @param inputFilename The input file to decrypt
* @param outputFilename The output file to write the decrypted data
* @throws IOException If an I/O error occurs
*/
public void decryptFile(String inputFilename, String outputFilename) throws IOException {
try (FileInputStream fis = new FileInputStream(inputFilename);
FileOutputStream fos = new FileOutputStream(outputFilename)) {
byte[] key = getKey(trackId);
byte[] buffer = new byte[2048];
int bytesRead;
int chunkCounter = 0;
@@ -23,7 +42,7 @@ public class DeezerDecryptor {
while ((bytesRead = fis.read(buffer)) != -1) {
// Only every 3rd chunk of exactly 2048 bytes should be decrypted
if (bytesRead == 2048 && (chunkCounter % 3) == 0) {
buffer = decryptChunk(key, buffer);
buffer = decryptChunk(buffer);
}
fos.write(buffer, 0, bytesRead);
chunkCounter++;
@@ -33,6 +52,11 @@ public class DeezerDecryptor {
}
}
/**
* Converts a byte array to a hexadecimal string.
* @param bytes Byte array to convert
* @return Hexadecimal string representation of the byte array
*/
public static String bytesToHex(byte[] bytes) {
final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
char[] hexChars = new char[bytes.length * 2];
@@ -47,7 +71,7 @@ public class DeezerDecryptor {
/**
* Generates the Track decryption key based on the provided track ID and a secret.
* @param id Track ID used to generate decryption key
* @return Decryption key for Track
* @return Decryption key for Track
*/
static byte[] getKey(String id) {
final String secret = "g4el58wc0zvf9na1";
@@ -57,11 +81,11 @@ public class DeezerDecryptor {
byte[] md5id = md5.digest();
String idmd5 = bytesToHex(md5id).toLowerCase();
String key = "";
for(int i=0; i<16; i++) {
for (int i = 0; i < 16; i++) {
int s0 = idmd5.charAt(i);
int s1 = idmd5.charAt(i+16);
int s1 = idmd5.charAt(i + 16);
int s2 = secret.charAt(i);
key += (char)(s0^s1^s2);
key += (char) (s0 ^ s1 ^ s2);
}
return key.getBytes();
} catch (Exception e) {
@@ -70,25 +94,37 @@ public class DeezerDecryptor {
}
}
/**
* Decrypts a 2048-byte chunk of data using the pre-initialized Blowfish cipher.
* @param data 2048-byte chunk of data to decrypt
* @return Decrypted 2048-byte chunk
*/
private byte[] decryptChunk(byte[] data) {
try {
return cipher.doFinal(data);
} catch (Exception e) {
Log.e("D", e.toString());
return new byte[0];
}
}
/**
* Decrypts a 2048-byte chunk of data using the Blowfish algorithm in CBC mode with no padding.
* The decryption key and the initial vector (IV) are used to decrypt the data.
* @param key Track key
* @param data 2048-byte chunk of data to decrypt
* @return Decrypted 2048-byte chunk
*
*/
static byte[] decryptChunk(byte[] key, byte[] data) {
public static byte[] decryptChunk(byte[] key, byte[] data) {
try {
byte[] IV = {00, 01, 02, 03, 04, 05, 06, 07};
SecretKeySpec Skey = new SecretKeySpec(key, "Blowfish");
Cipher cipher = Cipher.getInstance("Blowfish/CBC/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, Skey, new javax.crypto.spec.IvParameterSpec(IV));
cipher.init(Cipher.DECRYPT_MODE, Skey, new IvParameterSpec(IV));
return cipher.doFinal(data);
}catch (Exception e) {
} catch (Exception e) {
Log.e("D", e.toString());
return new byte[0];
}
}
}

View File

@@ -447,7 +447,8 @@ public class DownloadService extends Service {
if (qualityInfo.encrypted) {
try {
File decFile = new File(tmpFile.getPath() + ".DEC");
DeezerDecryptor.decryptFile(download.streamTrackId, tmpFile.getPath(), decFile.getPath());
DeezerDecryptor decryptor = new DeezerDecryptor(download.streamTrackId);
decryptor.decryptFile(tmpFile.getPath(), decFile.getPath());
tmpFile.delete();
tmpFile = decFile;
} catch (Exception e) {