Replaced CryptoJS with javax.crypto for Comico (#13572)

This commit is contained in:
pratyush3757 2022-09-25 08:04:07 +05:30 committed by GitHub
parent 6147a40686
commit 144d9bb004
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 54 additions and 25 deletions

View File

@ -11,7 +11,6 @@ import javax.crypto.spec.SecretKeySpec
/** /**
* Conforming with CryptoJS AES method * Conforming with CryptoJS AES method
*/ */
// see https://gist.github.com/thackerronak/554c985c3001b16810af5fc0eb5c358f
@Suppress("unused", "FunctionName") @Suppress("unused", "FunctionName")
object CryptoAES { object CryptoAES {
@ -22,10 +21,12 @@ object CryptoAES {
private const val KDF_DIGEST = "MD5" private const val KDF_DIGEST = "MD5"
/** /**
* Decrypt * Decrypt using CryptoJS defaults compatible method.
* Thanks Artjom B. for this: http://stackoverflow.com/a/29152379/4405051 * Uses KDF equivalent to OpenSSL's EVP_BytesToKey function
*
* http://stackoverflow.com/a/29152379/4405051
* @param cipherText base64 encoded ciphertext
* @param password passphrase * @param password passphrase
* @param cipherText encrypted string
*/ */
fun decrypt(cipherText: String, password: String): String { fun decrypt(cipherText: String, password: String): String {
try { try {
@ -34,19 +35,52 @@ object CryptoAES {
val cipherTextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.size) val cipherTextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.size)
val md5: MessageDigest = MessageDigest.getInstance("MD5") val md5: MessageDigest = MessageDigest.getInstance("MD5")
val keyAndIV = generateKeyAndIV(32, 16, 1, saltBytes, password.toByteArray(Charsets.UTF_8), md5) val keyAndIV = generateKeyAndIV(32, 16, 1, saltBytes, password.toByteArray(Charsets.UTF_8), md5)
val cipher = Cipher.getInstance(HASH_CIPHER) return decryptAES(cipherTextBytes,
val keyS = SecretKeySpec(keyAndIV!![0], AES) keyAndIV?.get(0) ?: ByteArray(32),
cipher.init(Cipher.DECRYPT_MODE, keyS, IvParameterSpec(keyAndIV!![1])) keyAndIV?.get(1) ?: ByteArray(16))
return cipher.doFinal(cipherTextBytes).toString(Charsets.UTF_8)
} catch (e: Exception) { } catch (e: Exception) {
return "" return ""
} }
} }
/**
* Decrypt using CryptoJS defaults compatible method.
*
* @param cipherText base64 encoded ciphertext
* @param keyBytes key as a bytearray
* @param ivBytes iv as a bytearray
*/
fun decrypt(cipherText: String, keyBytes: ByteArray, ivBytes: ByteArray): String {
return try {
val cipherTextBytes = Base64.decode(cipherText, Base64.DEFAULT)
decryptAES(cipherTextBytes, keyBytes, ivBytes)
} catch (e: Exception) {
""
}
}
/**
* Decrypt using CryptoJS defaults compatible method.
*
* @param cipherTextBytes encrypted text as a bytearray
* @param keyBytes key as a bytearray
* @param ivBytes iv as a bytearray
*/
private fun decryptAES(cipherTextBytes: ByteArray, keyBytes: ByteArray, ivBytes: ByteArray): String {
return try {
val cipher = Cipher.getInstance(HASH_CIPHER)
val keyS = SecretKeySpec(keyBytes, AES)
cipher.init(Cipher.DECRYPT_MODE, keyS, IvParameterSpec(ivBytes))
cipher.doFinal(cipherTextBytes).toString(Charsets.UTF_8)
} catch (e: Exception) {
""
}
}
/** /**
* Generates a key and an initialization vector (IV) with the given salt and password. * Generates a key and an initialization vector (IV) with the given salt and password.
* *
* Thanks to @Codo on Stackoverflow (https://stackoverflow.com/a/41434590) * https://stackoverflow.com/a/41434590
* This method is equivalent to OpenSSL's EVP_BytesToKey function * This method is equivalent to OpenSSL's EVP_BytesToKey function
* (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c). * (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c).
* By default, OpenSSL uses a single iteration, MD5 as the algorithm and UTF-8 encoded password data. * By default, OpenSSL uses a single iteration, MD5 as the algorithm and UTF-8 encoded password data.

View File

@ -6,8 +6,12 @@ ext {
extName = 'Comico' extName = 'Comico'
pkgNameSuffix = 'all.comico' pkgNameSuffix = 'all.comico'
extClass = '.ComicoFactory' extClass = '.ComicoFactory'
extVersionCode = 4 extVersionCode = 5
isNsfw = true isNsfw = true
} }
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"
dependencies {
implementation(project(':lib-cryptoaes'))
}

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.extension.all.comico package eu.kanade.tachiyomi.extension.all.comico
import android.webkit.CookieManager import android.webkit.CookieManager
import app.cash.quickjs.QuickJs import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
@ -44,10 +44,6 @@ open class Comico(
private val cookieManager by lazy { CookieManager.getInstance() } private val cookieManager by lazy { CookieManager.getInstance() }
private val cryptoJs by lazy {
client.newCall(GET(CRYPTOJS)).execute().body!!.string()
}
private val imgHeaders by lazy { private val imgHeaders by lazy {
headersBuilder().set("Accept", ACCEPT_IMAGE).build() headersBuilder().set("Accept", ACCEPT_IMAGE).build()
} }
@ -166,14 +162,8 @@ open class Comico(
private fun paginate(route: String, page: Int) = private fun paginate(route: String, page: Int) =
GET("$apiUrl/$route?pageNo=${page - 1}&pageSize=25", apiHeaders) GET("$apiUrl/$route?pageNo=${page - 1}&pageSize=25", apiHeaders)
private fun String.decrypt() = QuickJs.create().use { private fun String.decrypt() =
// javax.crypto.Cipher does not support empty IV CryptoAES.decrypt(this, keyBytes, ivBytes)
val script = """
const key = CryptoJS.enc.Utf8.parse('$AES_KEY'), iv = {words: []}
CryptoJS.AES.decrypt('$this', key, {iv}).toString(CryptoJS.enc.Utf8)
"""
it.evaluate(cryptoJs + script).toString()
}
private val Response.data: JsonElement? private val Response.data: JsonElement?
get() = json.parseToJsonElement(body!!.string()).jsonObject.also { get() = json.parseToJsonElement(body!!.string()).jsonObject.also {
@ -206,8 +196,9 @@ open class Comico(
private const val AES_KEY = "a7fc9dc89f2c873d79397f8a0028a4cd" private const val AES_KEY = "a7fc9dc89f2c873d79397f8a0028a4cd"
private const val CRYPTOJS = private val keyBytes = AES_KEY.toByteArray(Charsets.UTF_8)
"https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"
private val ivBytes = ByteArray(16) // Zero filled array as IV
private const val ACCEPT_IMAGE = private const val ACCEPT_IMAGE =
"image/avif,image/jxl,image/webp,image/*,*/*" "image/avif,image/jxl,image/webp,image/*,*/*"