From e2df506b944d8e938a9d47bcd497f705efe4fd85 Mon Sep 17 00:00:00 2001 From: Sam Date: Sat, 11 Nov 2023 19:04:47 -0500 Subject: [PATCH] fix(en/aniwave): Fix VRF encrypt/decrypt (#2504) --- src/en/aniwave/build.gradle | 2 +- .../en/nineanime/AniwaveUtils.kt | 69 +++++++++++++++---- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/en/aniwave/build.gradle b/src/en/aniwave/build.gradle index 8303f9c8ab..b9fe8c46d1 100644 --- a/src/en/aniwave/build.gradle +++ b/src/en/aniwave/build.gradle @@ -8,7 +8,7 @@ ext { extName = 'Aniwave' pkgNameSuffix = 'en.nineanime' extClass = '.Aniwave' - extVersionCode = 57 + extVersionCode = 58 libVersion = '13' } diff --git a/src/en/aniwave/src/eu/kanade/tachiyomi/animeextension/en/nineanime/AniwaveUtils.kt b/src/en/aniwave/src/eu/kanade/tachiyomi/animeextension/en/nineanime/AniwaveUtils.kt index 09f4570a50..05c18e19ca 100644 --- a/src/en/aniwave/src/eu/kanade/tachiyomi/animeextension/en/nineanime/AniwaveUtils.kt +++ b/src/en/aniwave/src/eu/kanade/tachiyomi/animeextension/en/nineanime/AniwaveUtils.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.animeextension.en.nineanime +import android.util.Base64 import eu.kanade.tachiyomi.AppInfo import eu.kanade.tachiyomi.animeextension.BuildConfig import eu.kanade.tachiyomi.network.GET @@ -10,6 +11,9 @@ import okhttp3.Headers import okhttp3.OkHttpClient import okhttp3.Response import uy.kohesive.injekt.injectLazy +import java.net.URLDecoder +import javax.crypto.Cipher +import javax.crypto.spec.SecretKeySpec class AniwaveUtils(private val client: OkHttpClient, private val headers: Headers) { @@ -37,22 +41,10 @@ class AniwaveUtils(private val client: OkHttpClient, private val headers: Header headers = userAgent, ), ).execute().parseAs().rawURL + } else if (action == "decrypt") { + vrfDecrypt(query) } else { - client.newCall( - GET("https://9anime.eltik.net/$action?query=$query&apikey=aniyomi", userAgent), - ).execute().use { - val body = it.body.string() - when (action) { - "decrypt" -> { - json.decodeFromString(body).url - } - else -> { - json.decodeFromString(body).let { vrf -> - "${vrf.vrfQuery}=${java.net.URLEncoder.encode(vrf.url, "utf-8")}" - } - } - } - } + "vrf=${java.net.URLEncoder.encode(vrfEncrypt(query), "utf-8")}" } } @@ -60,4 +52,51 @@ class AniwaveUtils(private val client: OkHttpClient, private val headers: Header val responseBody = use { it.body.string() } return json.decodeFromString(responseBody) } + + private fun vrfEncrypt(input: String): String { + val rc4Key = SecretKeySpec("ysJhV6U27FVIjjuk".toByteArray(), "RC4") + val cipher = Cipher.getInstance("RC4") + cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters) + + var vrf = cipher.doFinal(input.toByteArray()) + vrf = Base64.encode(vrf, Base64.URL_SAFE or Base64.NO_WRAP) + vrf = Base64.encode(vrf, Base64.DEFAULT or Base64.NO_WRAP) + vrf = vrfShift(vrf) + vrf = Base64.encode(vrf, Base64.DEFAULT) + vrf = rot13(vrf) + + return vrf.toString(Charsets.UTF_8) + } + + private fun vrfDecrypt(input: String): String { + var vrf = input.toByteArray() + vrf = Base64.decode(vrf, Base64.URL_SAFE) + + val rc4Key = SecretKeySpec("hlPeNwkncH0fq9so".toByteArray(), "RC4") + val cipher = Cipher.getInstance("RC4") + cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters) + vrf = cipher.doFinal(vrf) + + return URLDecoder.decode(vrf.toString(Charsets.UTF_8), "utf-8") + } + + private fun rot13(vrf: ByteArray): ByteArray { + for (i in vrf.indices) { + val byte = vrf[i] + if (byte in 'A'.code..'Z'.code) { + vrf[i] = ((byte - 'A'.code + 13) % 26 + 'A'.code).toByte() + } else if (byte in 'a'.code..'z'.code) { + vrf[i] = ((byte - 'a'.code + 13) % 26 + 'a'.code).toByte() + } + } + return vrf + } + + private fun vrfShift(vrf: ByteArray): ByteArray { + for (i in vrf.indices) { + val shift = arrayOf(-3, 3, -4, 2, -2, 5, 4, 5)[i % 8] + vrf[i] = vrf[i].plus(shift).toByte() + } + return vrf + } }