mirror of
https://github.com/yuzono/anime-extensions.git
synced 2026-10-20 10:51:36 +00:00
Use core decodeHex/rc4/toHex instead of local copies
Replace hand-rolled hex decoding, RC4 and hex encoding with the shared keiyoushi.utils helpers, following keiyoushi/extensions-source#15640. The output is identical for every valid input, so no version bumps. - decodeHex: lib/m3u8server, lib/vidguardextractor, animepahe, uniquestream, gotaku, tioanimeh - rc4: anikototheme, lib/vidsrcextractor, aniwaves - toHex: lib/googledriveepisodes, googledrive, missav, serienstream, hanime, hexawatch, kayoanime, kickassanime, funanimetv, tranimeizle, cycity, iyf, xfani commit a96c552e11b22ef6bedb34b097de4a101e9455cf from keiyoushi/extensions-source#15640
This commit is contained in:
@@ -26,6 +26,7 @@ import keiyoushi.utils.LazyMutable
|
||||
import keiyoushi.utils.delegate
|
||||
import keiyoushi.utils.getPreferencesLazy
|
||||
import keiyoushi.utils.parseAs
|
||||
import keiyoushi.utils.rc4
|
||||
import keiyoushi.utils.tryParse
|
||||
import keiyoushi.utils.useAsJsoup
|
||||
import okhttp3.CacheControl
|
||||
@@ -41,8 +42,6 @@ import java.math.RoundingMode
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
|
||||
abstract class AnikotoTheme(
|
||||
@@ -1079,10 +1078,7 @@ private object AnikotoUtils {
|
||||
}
|
||||
|
||||
private fun rc4Encrypt(key: String, input: String): String {
|
||||
val rc4Key = SecretKeySpec(key.toByteArray(), "RC4")
|
||||
val cipher = Cipher.getInstance("RC4")
|
||||
cipher.init(Cipher.ENCRYPT_MODE, rc4Key, cipher.parameters)
|
||||
val output = cipher.doFinal(input.toByteArray())
|
||||
val output = rc4(key.toByteArray(), input.toByteArray())
|
||||
return Base64.encode(output, Base64.URL_SAFE or Base64.NO_WRAP).toString(Charsets.UTF_8)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import keiyoushi.utils.formatBytes
|
||||
import keiyoushi.utils.parseAs
|
||||
import keiyoushi.utils.toHex
|
||||
import kotlinx.serialization.Serializable
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
@@ -118,7 +119,7 @@ class GoogleDriveEpisodes(private val client: OkHttpClient, private val headers:
|
||||
val sapisidhash = MessageDigest
|
||||
.getInstance("SHA-1")
|
||||
.digest("$timeNow $SAPISID $origin".toByteArray())
|
||||
.joinToString("") { "%02x".format(it) }
|
||||
.toHex()
|
||||
return "SAPISIDHASH ${timeNow}_$sapisidhash"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package aniyomi.lib.m3u8server
|
||||
|
||||
import android.util.Log
|
||||
import keiyoushi.utils.decodeHex
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -362,18 +363,21 @@ class M3u8HttpServer(
|
||||
if (normalizedIv.length != 32) {
|
||||
throw IOException("Invalid AES-128 IV length: ${normalizedIv.length}")
|
||||
}
|
||||
val ivBytes = try {
|
||||
normalizedIv.decodeHex()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw IOException("Invalid AES-128 IV", e)
|
||||
}
|
||||
return try {
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
cipher.init(
|
||||
Cipher.DECRYPT_MODE,
|
||||
SecretKeySpec(key, "AES"),
|
||||
IvParameterSpec(normalizedIv.hexToByteArray()),
|
||||
IvParameterSpec(ivBytes),
|
||||
)
|
||||
cipher.doFinal(data)
|
||||
} catch (e: GeneralSecurityException) {
|
||||
throw IOException("Failed to decrypt AES-128 segment", e)
|
||||
} catch (e: NumberFormatException) {
|
||||
throw IOException("Invalid AES-128 IV", e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,10 +385,6 @@ class M3u8HttpServer(
|
||||
|
||||
private fun String.normalizeHlsIv(): String = removePrefix("0x").removePrefix("0X").padStart(32, '0')
|
||||
|
||||
private fun String.hexToByteArray(): ByteArray = ByteArray(length / 2) { index ->
|
||||
substring(index * 2, index * 2 + 2).toInt(16).toByte()
|
||||
}
|
||||
|
||||
/**
|
||||
* Health check
|
||||
*/
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.util.Log
|
||||
import aniyomi.lib.playlistutils.PlaylistUtils
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import keiyoushi.utils.decodeHex
|
||||
import keiyoushi.utils.parseAs
|
||||
import keiyoushi.utils.useAsJsoup
|
||||
import kotlinx.serialization.Serializable
|
||||
@@ -37,8 +38,8 @@ class VidGuardExtractor(private val client: OkHttpClient) {
|
||||
|
||||
private fun sigDecode(url: String): String {
|
||||
val sig = url.split("sig=")[1].split("&")[0]
|
||||
val t = sig.chunked(2)
|
||||
.joinToString("") { (Integer.parseInt(it, 16) xor 2).toChar().toString() }
|
||||
val t = sig.decodeHex()
|
||||
.joinToString("") { ((it.toInt() and 0xFF) xor 2).toChar().toString() }
|
||||
.let {
|
||||
val padding = when (it.length % 4) {
|
||||
2 -> "=="
|
||||
|
||||
@@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.animesource.model.Track
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import keiyoushi.utils.parseAs
|
||||
import keiyoushi.utils.rc4
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
@@ -15,8 +16,6 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.net.URLDecoder
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.spec.SecretKeySpec
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
class VidsrcExtractor(private val client: OkHttpClient, private val headers: Headers) {
|
||||
@@ -76,10 +75,8 @@ class VidsrcExtractor(private val client: OkHttpClient, private val headers: Hea
|
||||
}
|
||||
|
||||
private fun encodeID(videoID: String, key: String): String {
|
||||
val rc4Key = SecretKeySpec(key.toByteArray(), "RC4")
|
||||
val cipher = Cipher.getInstance("RC4")
|
||||
cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
|
||||
return Base64.encode(cipher.doFinal(videoID.toByteArray()), Base64.DEFAULT)
|
||||
val encrypted = rc4(key.toByteArray(), videoID.toByteArray())
|
||||
return Base64.encode(encrypted, Base64.DEFAULT)
|
||||
.toString(Charsets.UTF_8)
|
||||
.replace("+", "-")
|
||||
.replace("/", "_")
|
||||
@@ -98,11 +95,7 @@ class VidsrcExtractor(private val client: OkHttpClient, private val headers: Hea
|
||||
}
|
||||
|
||||
private fun vrfDecrypt(input: String): String {
|
||||
var vrf = Base64.decode(input.toByteArray(), Base64.URL_SAFE)
|
||||
val rc4Key = SecretKeySpec(DECRYPTION_KEY.toByteArray(), "RC4")
|
||||
val cipher = Cipher.getInstance("RC4")
|
||||
cipher.init(Cipher.DECRYPT_MODE, rc4Key, cipher.parameters)
|
||||
vrf = cipher.doFinal(vrf)
|
||||
val vrf = rc4(DECRYPTION_KEY.toByteArray(), Base64.decode(input.toByteArray(), Base64.URL_SAFE))
|
||||
return URLDecoder.decode(vrf.toString(Charsets.UTF_8), "utf-8")
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import keiyoushi.utils.commonEmptyRequestBody
|
||||
import keiyoushi.utils.formatBytes
|
||||
import keiyoushi.utils.getPreferencesLazy
|
||||
import keiyoushi.utils.parseAs
|
||||
import keiyoushi.utils.toHex
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
@@ -510,7 +511,7 @@ class GoogleDrive :
|
||||
val sapisidhash = MessageDigest
|
||||
.getInstance("SHA-1")
|
||||
.digest("$timeNow $SAPISID $origin".toByteArray())
|
||||
.joinToString("") { "%02x".format(it) }
|
||||
.toHex()
|
||||
return "SAPISIDHASH ${timeNow}_$sapisidhash"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.animeextension.all.missav
|
||||
|
||||
import android.util.Log
|
||||
import keiyoushi.utils.toHex
|
||||
import kotlinx.serialization.json.JsonObjectBuilder
|
||||
import kotlinx.serialization.json.add
|
||||
import kotlinx.serialization.json.put
|
||||
@@ -94,7 +95,7 @@ object MissAvApi {
|
||||
mac.init(secretKeySpec)
|
||||
|
||||
val hashBytes = mac.doFinal(data.toByteArray(StandardCharsets.UTF_8))
|
||||
val hexString = hashBytes.joinToString("") { "%02x".format(it) }
|
||||
val hexString = hashBytes.toHex()
|
||||
|
||||
"$data&frontend_sign=$hexString"
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import keiyoushi.utils.delegate
|
||||
import keiyoushi.utils.getPreferencesLazy
|
||||
import keiyoushi.utils.parallelCatchingFlatMapBlocking
|
||||
import keiyoushi.utils.parallelMapNotNullBlocking
|
||||
import keiyoushi.utils.toHex
|
||||
import keiyoushi.utils.useAsJsoup
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
@@ -443,7 +444,7 @@ class Serienstream :
|
||||
}
|
||||
for (i in 0..maxnumber) {
|
||||
val hash = md.digest((salt + i.toString()).toByteArray())
|
||||
val hex = hash.joinToString("") { "%02x".format(it.toInt() and 0xFF) }
|
||||
val hex = hash.toHex()
|
||||
if (hex == challenge) return i
|
||||
md.reset()
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.animeextension.en.animepahe
|
||||
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import keiyoushi.utils.decodeHex
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
@@ -339,18 +340,22 @@ object AnimePaheHlsServer : NanoHTTPD(0) {
|
||||
throw IOException("Invalid AES-128 IV length: ${normalizedIv.length}")
|
||||
}
|
||||
|
||||
val ivBytes = try {
|
||||
normalizedIv.decodeHex()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw IOException("Invalid AES-128 IV", e)
|
||||
}
|
||||
|
||||
return try {
|
||||
val cipher = Cipher.getInstance("AES/CBC/NoPadding")
|
||||
cipher.init(
|
||||
Cipher.DECRYPT_MODE,
|
||||
SecretKeySpec(key, "AES"),
|
||||
IvParameterSpec(normalizedIv.hexToByteArray()),
|
||||
IvParameterSpec(ivBytes),
|
||||
)
|
||||
cipher.doFinal(data)
|
||||
} catch (e: GeneralSecurityException) {
|
||||
throw IOException("Failed to decrypt AES-128 segment", e)
|
||||
} catch (e: NumberFormatException) {
|
||||
throw IOException("Invalid AES-128 IV", e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,8 +364,4 @@ object AnimePaheHlsServer : NanoHTTPD(0) {
|
||||
private fun String.normalizeHlsIv(): String = removePrefix("0x")
|
||||
.removePrefix("0X")
|
||||
.padStart(32, '0')
|
||||
|
||||
private fun String.hexToByteArray(): ByteArray = ByteArray(length / 2) { index ->
|
||||
substring(index * 2, index * 2 + 2).toInt(16).toByte()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,18 @@
|
||||
package eu.kanade.tachiyomi.animeextension.en.aniwaves
|
||||
|
||||
import android.util.Base64
|
||||
import keiyoushi.utils.rc4
|
||||
import java.net.URLEncoder
|
||||
|
||||
internal class VrfCodec {
|
||||
|
||||
fun encrypt(input: String): String {
|
||||
val rc4Result = rc4(KEY, input)
|
||||
val base64 = Base64.encodeToString(rc4Result.toByteArray(Charsets.ISO_8859_1), Base64.NO_WRAP)
|
||||
val rc4Result = rc4(KEY, input.toByteArray(Charsets.ISO_8859_1))
|
||||
val base64 = Base64.encodeToString(rc4Result, Base64.NO_WRAP)
|
||||
return URLEncoder.encode(base64, "UTF-8")
|
||||
}
|
||||
|
||||
private fun rc4(key: String, data: String): String {
|
||||
val s = IntArray(256) { it }
|
||||
var j = 0
|
||||
for (i in 0..255) {
|
||||
j = (j + s[i] + key[i % key.length].code) % 256
|
||||
s[i] = s[j].also { s[j] = s[i] }
|
||||
}
|
||||
var i = 0
|
||||
j = 0
|
||||
val result = StringBuilder(data.length)
|
||||
for (k in data.indices) {
|
||||
i = (i + 1) % 256
|
||||
j = (j + s[i]) % 256
|
||||
s[i] = s[j].also { s[j] = s[i] }
|
||||
result.append((data[k].code xor s[(s[i] + s[j]) % 256]).toChar())
|
||||
}
|
||||
return result.toString()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val KEY = "simple-hash"
|
||||
val KEY = "simple-hash".toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.animeextension.en.gotaku
|
||||
|
||||
import android.util.Base64
|
||||
import keiyoushi.utils.decodeHex
|
||||
import kotlinx.serialization.json.buildJsonObject
|
||||
import kotlinx.serialization.json.put
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
@@ -34,9 +35,7 @@ object Crypto {
|
||||
167, 208, 189, 122, 150, 242, 60, 1, 70, 177, 253, 150, 158, 124, 253, 47,
|
||||
).map { it.toByte() }.toByteArray()
|
||||
|
||||
private val MANIFEST_SEED = hexToBytes(
|
||||
"458e5403c40a73332706df406193dd6b040a2ea97a6bb59b52b294a61006e6ef",
|
||||
)
|
||||
private val MANIFEST_SEED = "458e5403c40a73332706df406193dd6b040a2ea97a6bb59b52b294a61006e6ef".decodeHex()
|
||||
|
||||
/** HKDF-SHA256(ikm=API_SEED, salt=∅, info="api-seal|v1", L=32) — derived once. */
|
||||
private val apiKeySpec by lazy {
|
||||
@@ -266,6 +265,4 @@ object Crypto {
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
fun hexToBytes(hex: String): ByteArray = ByteArray(hex.length / 2) { i -> hex.substring(i * 2, i * 2 + 2).toInt(16).toByte() }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.animeextension.en.hanime
|
||||
|
||||
import keiyoushi.utils.toHex
|
||||
import java.security.MessageDigest
|
||||
|
||||
/**
|
||||
@@ -68,7 +69,7 @@ open class NativeSignatureProvider : SignatureProvider {
|
||||
val input = "$timestamp,$SALT_1,$ORIGIN,$SALT_2,$timestamp"
|
||||
val digest = MessageDigest.getInstance("SHA-256")
|
||||
val hashBytes = digest.digest(input.toByteArray(Charsets.UTF_8))
|
||||
val hex = hashBytes.joinToString("") { "%02x".format(it) }
|
||||
val hex = hashBytes.toHex()
|
||||
return Signature(signature = hex, time = timestamp.toString())
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import keiyoushi.utils.getPreferencesLazy
|
||||
import keiyoushi.utils.parallelFlatMap
|
||||
import keiyoushi.utils.parallelMapNotNull
|
||||
import keiyoushi.utils.parseAs
|
||||
import keiyoushi.utils.toHex
|
||||
import keiyoushi.utils.toJsonRequestBody
|
||||
import keiyoushi.utils.toJsonString
|
||||
import kotlinx.coroutines.CancellationException
|
||||
@@ -516,7 +517,7 @@ class HexaWatch :
|
||||
private class HosterData(val requestUrl: String, val server: String, val key: String, val url: String?)
|
||||
|
||||
private fun randomHex(bytes: Int) = ByteArray(bytes).also(SECURE_RANDOM::nextBytes)
|
||||
.joinToString("") { "%02x".format(it) }
|
||||
.toHex()
|
||||
|
||||
// Stands in for the site's hashed screen/UA/canvas fingerprint; only needs to be stable.
|
||||
private val clientFingerprint by lazy { randomHex(4).toLong(16).toString(36) }
|
||||
|
||||
@@ -18,6 +18,7 @@ import keiyoushi.utils.ParsedAnimeHttpLegacySource
|
||||
import keiyoushi.utils.formatBytes
|
||||
import keiyoushi.utils.getPreferencesLazy
|
||||
import keiyoushi.utils.parseAs
|
||||
import keiyoushi.utils.toHex
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.FormBody
|
||||
@@ -455,7 +456,7 @@ class Kayoanime :
|
||||
val sapisidhash = MessageDigest
|
||||
.getInstance("SHA-1")
|
||||
.digest("$timeNow $SAPISID $origin".toByteArray())
|
||||
.joinToString("") { "%02x".format(it) }
|
||||
.toHex()
|
||||
return "SAPISIDHASH ${timeNow}_$sapisidhash"
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.network.awaitSuccess
|
||||
import keiyoushi.lib.cryptoaes.CryptoAES
|
||||
import keiyoushi.utils.bodyString
|
||||
import keiyoushi.utils.decodeHex
|
||||
import keiyoushi.utils.toHex
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
@@ -254,7 +255,7 @@ class KickAssAnimeExtractor(
|
||||
private fun sha1sum(value: String): String = try {
|
||||
val md = MessageDigest.getInstance("SHA-1")
|
||||
val bytes = md.digest(value.toByteArray())
|
||||
bytes.joinToString("") { "%02x".format(it) }
|
||||
bytes.toHex()
|
||||
} catch (_: Exception) {
|
||||
throw Exception("Attempt to create the signature failed.")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.animeextension.en.uniquestream
|
||||
|
||||
import android.util.Base64
|
||||
import keiyoushi.utils.decodeHex
|
||||
import okhttp3.Dns
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
@@ -258,18 +259,22 @@ object UniqueStreamHlsServer : NanoHTTPD("127.0.0.1", 0) {
|
||||
throw IOException("Invalid AES-128 key length: ${key.size}")
|
||||
}
|
||||
|
||||
val ivBytes = try {
|
||||
iv.decodeHex()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw IOException("Invalid AES-128 IV", e)
|
||||
}
|
||||
|
||||
return try {
|
||||
val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
||||
cipher.init(
|
||||
Cipher.DECRYPT_MODE,
|
||||
SecretKeySpec(key, "AES"),
|
||||
IvParameterSpec(iv.hexToByteArray()),
|
||||
IvParameterSpec(ivBytes),
|
||||
)
|
||||
cipher.doFinal(data)
|
||||
} catch (e: GeneralSecurityException) {
|
||||
throw IOException("Failed to decrypt AES-128 segment", e)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw IOException("Invalid AES-128 IV", e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -352,8 +357,4 @@ object UniqueStreamHlsServer : NanoHTTPD("127.0.0.1", 0) {
|
||||
private fun String.normalizeHlsIv(): String = removePrefix("0x").removePrefix("0X").padStart(32, '0')
|
||||
|
||||
private fun encode(value: String): String = URLEncoder.encode(value, Charsets.UTF_8.name())
|
||||
|
||||
private fun String.hexToByteArray(): ByteArray = ByteArray(length / 2) { index ->
|
||||
substring(index * 2, index * 2 + 2).toInt(16).toByte()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import android.webkit.WebViewClient
|
||||
import eu.kanade.tachiyomi.animesource.model.Video
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import keiyoushi.utils.decodeHex
|
||||
import okhttp3.Headers
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
@@ -105,8 +106,8 @@ class VidGuardExtractor(private val client: OkHttpClient) {
|
||||
private fun fixUrl(url: String): String {
|
||||
val httpUrl = url.toHttpUrl()
|
||||
val originalSign = httpUrl.queryParameter("sig")!!
|
||||
val newSign = originalSign.chunked(2).joinToString("") {
|
||||
Char(it.toInt(16) xor 2).toString()
|
||||
val newSign = originalSign.decodeHex().joinToString("") {
|
||||
Char((it.toInt() and 0xFF) xor 2).toString()
|
||||
}
|
||||
.let { String(Base64.decode(it, Base64.DEFAULT)) }
|
||||
.substring(5)
|
||||
|
||||
@@ -19,6 +19,7 @@ import keiyoushi.network.rateLimit
|
||||
import keiyoushi.utils.AnimeHttpLegacySource
|
||||
import keiyoushi.utils.getPreferencesLazy
|
||||
import keiyoushi.utils.parseAs
|
||||
import keiyoushi.utils.toHex
|
||||
import keiyoushi.utils.toJsonString
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
@@ -334,7 +335,7 @@ class FunAnimeTV :
|
||||
fun md5(input: String): String {
|
||||
val md = MessageDigest.getInstance("MD5")
|
||||
val digest = md.digest(input.toByteArray(Charsets.UTF_8))
|
||||
return digest.joinToString(separator = "") { "%02x".format(it) }
|
||||
return digest.toHex()
|
||||
}
|
||||
|
||||
private fun createRequestJson(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.animeextension.pt.funanimetv.dto
|
||||
|
||||
import android.util.Base64
|
||||
import keiyoushi.utils.toHex
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
@@ -24,9 +25,7 @@ class FunAnimeTVRequest(
|
||||
val sign: String = run {
|
||||
MessageDigest.getInstance("MD5")
|
||||
.digest("$singsalt$salt".toByteArray(Charsets.UTF_8))
|
||||
.joinToString("") {
|
||||
"%02x".format(it)
|
||||
}
|
||||
.toHex()
|
||||
}
|
||||
|
||||
fun toBase64(json: Json): String = Base64.encodeToString(json.encodeToString(this).toByteArray(), Base64.NO_PADDING)
|
||||
|
||||
@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.animeextension.tr.tranimeizle
|
||||
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.POST
|
||||
import keiyoushi.utils.toHex
|
||||
import okhttp3.FormBody
|
||||
import okhttp3.Headers
|
||||
import okhttp3.Interceptor
|
||||
@@ -65,5 +66,5 @@ class ShittyCaptchaInterceptor(private val baseUrl: String, private val headers:
|
||||
|
||||
private fun md5Hash(byteArray: ByteArray) = MessageDigest.getInstance("MD5")
|
||||
.digest(byteArray)
|
||||
.joinToString("") { "%02x".format(it) } // create hex
|
||||
.toHex()
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import keiyoushi.lib.cryptoaes.CryptoAES
|
||||
import keiyoushi.utils.AnimeHttpLegacySource
|
||||
import keiyoushi.utils.getPreferencesLazy
|
||||
import keiyoushi.utils.parseAs
|
||||
import keiyoushi.utils.toHex
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
@@ -75,7 +76,7 @@ class Cycity :
|
||||
val prefix = CharArray(k2.length)
|
||||
k1.indices.forEach { prefix[k1[it] - '0'] = k2[it] }
|
||||
val txt = "${prefix.joinToString("")}YLwJVbXw77pk2eOrAnFdBo2c3mWkLtodMni2wk81GCnP94ZltW"
|
||||
val a = md5.digest(txt.toByteArray()).joinToString("") { "%02x".format(it) }
|
||||
val a = md5.digest(txt.toByteArray()).toHex()
|
||||
val ivBytes = a.take(16).toByteArray(Charsets.UTF_8)
|
||||
val keyBytes = a.substring(16).toByteArray(Charsets.UTF_8)
|
||||
return CryptoAES.decrypt(url, keyBytes, ivBytes)
|
||||
|
||||
@@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.network.POST
|
||||
import eu.kanade.tachiyomi.util.asJsoup
|
||||
import keiyoushi.utils.AnimeHttpLegacySource
|
||||
import keiyoushi.utils.parseAs
|
||||
import keiyoushi.utils.toHex
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.jsonArray
|
||||
@@ -237,7 +238,7 @@ class Iyf : AnimeHttpLegacySource() {
|
||||
private fun signature(query: String, pConfig: PConfig): String {
|
||||
val s = "${pConfig.publicKey}&${query.lowercase()}&${pConfig.privateKey}"
|
||||
return MessageDigest.getInstance("MD5").digest(s.toByteArray())
|
||||
.joinToString(separator = "") { "%02x".format(it) }
|
||||
.toHex()
|
||||
}
|
||||
|
||||
override fun getFilterList(): AnimeFilterList = AnimeFilterList(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.animeextension.zh.xfani
|
||||
|
||||
import keiyoushi.utils.toHex
|
||||
import java.security.MessageDigest
|
||||
|
||||
private const val UID = "DCC147D11943AF75"
|
||||
@@ -9,5 +10,5 @@ internal fun generateKey(time: Long): String = "DS${time}$UID".md5()
|
||||
internal fun String.md5(): String {
|
||||
val md = MessageDigest.getInstance("MD5")
|
||||
val digest = md.digest(this.toByteArray())
|
||||
return digest.joinToString(separator = "") { eachByte -> "%02x".format(eachByte) }
|
||||
return digest.toHex()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user