Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.lagradost.cloudstream3.extractors.helper

import com.lagradost.cloudstream3.Prerelease
import com.lagradost.cloudstream3.base64DecodeArray
import com.lagradost.cloudstream3.base64Encode
import dev.whyoleg.cryptography.CryptographyProvider
Expand All @@ -14,7 +15,8 @@ import kotlin.math.min
*/
// see https://gist.github.com/thackerronak/554c985c3001b16810af5fc0eb5c358f
@Suppress("unused", "FunctionName", "SameParameterValue")
object CryptoJS {
@Prerelease
object CryptoJSHelper {
private const val KEY_SIZE = 256
private const val IV_SIZE = 128

Expand All @@ -32,15 +34,15 @@ object CryptoJS {
* @param plainText plain string
*/
@OptIn(DelicateCryptographyApi::class)
fun encrypt(password: String, plainText: String): String {
suspend fun encrypt(password: String, plainText: String): String {
val saltBytes = generateSalt(8)
val key = ByteArray(KEY_SIZE / 8)
val iv = ByteArray(IV_SIZE / 8)
evpkdf(password.encodeToByteArray(), KEY_SIZE, IV_SIZE, saltBytes, 1, key, iv)

val aesKey = aesCbc.keyDecoder().decodeFromByteArrayBlocking(AES.Key.Format.RAW, key)
val aesKey = aesCbc.keyDecoder().decodeFromByteArray(AES.Key.Format.RAW, key)
val cipher = aesKey.cipher(padding = true)
val cipherText = cipher.encryptWithIvBlocking(iv, plainText.encodeToByteArray())
val cipherText = cipher.encryptWithIv(iv, plainText.encodeToByteArray())

// Create CryptoJS-like encrypted: "Salted__" || salt || ciphertext
val sBytes = APPEND.encodeToByteArray()
Expand All @@ -58,6 +60,132 @@ object CryptoJS {
* @param password passphrase
* @param cipherText encrypted string
*/
@OptIn(DelicateCryptographyApi::class)
suspend fun decrypt(password: String, cipherText: String): String {
val ctBytes = base64DecodeArray(cipherText)
val saltBytes = ctBytes.copyOfRange(8, 16)
val cipherTextBytes = ctBytes.copyOfRange(16, ctBytes.size)

val key = ByteArray(KEY_SIZE / 8)
val iv = ByteArray(IV_SIZE / 8)
evpkdf(password.encodeToByteArray(), KEY_SIZE, IV_SIZE, saltBytes, 1, key, iv)

val aesKey = aesCbc.keyDecoder().decodeFromByteArray(AES.Key.Format.RAW, key)
val cipher = aesKey.cipher(padding = true)
val plainText = cipher.decryptWithIv(iv, cipherTextBytes)
return plainText.decodeToString()
}

private fun evpkdf(
password: ByteArray,
keySize: Int,
ivSize: Int,
salt: ByteArray,
iterations: Int,
resultKey: ByteArray,
resultIv: ByteArray,
): ByteArray {
val keySize = keySize / 32
val ivSize = ivSize / 32
val targetKeySize = keySize + ivSize
val derivedBytes = ByteArray(targetKeySize * 4)
var numberOfDerivedWords = 0
var block: ByteArray? = null

while (numberOfDerivedWords < targetKeySize) {
val hashFn = md5Hasher.createHashFunction()
if (block != null) hashFn.update(block)
hashFn.update(password)
hashFn.update(salt)
block = hashFn.hashToByteArray()

for (i in 1 until iterations) {
val iterFn = md5Hasher.createHashFunction()
iterFn.update(block!!)
block = iterFn.hashToByteArray()
}

block.copyInto(
destination = derivedBytes,
destinationOffset = numberOfDerivedWords * 4,
startIndex = 0,
endIndex = min(block.size, (targetKeySize - numberOfDerivedWords) * 4),
)

numberOfDerivedWords += block.size / 4
}

derivedBytes.copyInto(
destination = resultKey,
destinationOffset = 0,
startIndex = 0,
endIndex = keySize * 4,
)

derivedBytes.copyInto(
destination = resultIv,
destinationOffset = 0,
startIndex = keySize * 4,
endIndex = (keySize + ivSize) * 4,
)

return derivedBytes // key + iv
}

private fun generateSalt(length: Int): ByteArray =
CryptographyRandom.nextBytes(length)
}

// Deprecate after next stable
/* @Deprecated(
message = "Renamed to CryptoJSHelper",
level = DeprecationLevel.WARNING,
replaceWith = ReplaceWith("CryptoJSHelper"),
) */
@Suppress("unused", "FunctionName", "SameParameterValue")
object CryptoJS {
// Use this after next stable, once CryptoJSHelper is no longer @Prerelease,
// and delete everything else in this object:
/*
fun encrypt(password: String, plainText: String): String =
runBlocking { CryptoJSHelper.encrypt(password, plainText) }

fun decrypt(password: String, cipherText: String): String =
runBlocking { CryptoJSHelper.decrypt(password, cipherText) }
*/

private const val KEY_SIZE = 256
private const val IV_SIZE = 128

// Seriously crypto-js, what's wrong with you?
private const val APPEND = "Salted__"

private val provider = CryptographyProvider.Default
private val aesCbc = provider.get(AES.CBC)
@OptIn(DelicateCryptographyApi::class)
private val md5Hasher = provider.get(MD5).hasher()

@OptIn(DelicateCryptographyApi::class)
fun encrypt(password: String, plainText: String): String {
val saltBytes = generateSalt(8)
val key = ByteArray(KEY_SIZE / 8)
val iv = ByteArray(IV_SIZE / 8)
evpkdf(password.encodeToByteArray(), KEY_SIZE, IV_SIZE, saltBytes, 1, key, iv)

val aesKey = aesCbc.keyDecoder().decodeFromByteArrayBlocking(AES.Key.Format.RAW, key)
val cipher = aesKey.cipher(padding = true)
val cipherText = cipher.encryptWithIvBlocking(iv, plainText.encodeToByteArray())

// Create CryptoJS-like encrypted: "Salted__" || salt || ciphertext
val sBytes = APPEND.encodeToByteArray()
val b = ByteArray(sBytes.size + saltBytes.size + cipherText.size)
sBytes.copyInto(destination = b, destinationOffset = 0)
saltBytes.copyInto(destination = b, destinationOffset = sBytes.size)
cipherText.copyInto(destination = b, destinationOffset = sBytes.size + saltBytes.size)

return base64Encode(b)
}

@OptIn(DelicateCryptographyApi::class)
fun decrypt(password: String, cipherText: String): String {
val ctBytes = base64DecodeArray(cipherText)
Expand Down Expand Up @@ -107,14 +235,26 @@ object CryptoJS {
destination = derivedBytes,
destinationOffset = numberOfDerivedWords * 4,
startIndex = 0,
endIndex = min(block.size, (targetKeySize - numberOfDerivedWords) * 4)
endIndex = min(block.size, (targetKeySize - numberOfDerivedWords) * 4),
)

numberOfDerivedWords += block.size / 4
}

derivedBytes.copyInto(destination = resultKey, destinationOffset = 0, startIndex = 0, endIndex = keySize * 4)
derivedBytes.copyInto(destination = resultIv, destinationOffset = 0, startIndex = keySize * 4, endIndex = (keySize + ivSize) * 4)
derivedBytes.copyInto(
destination = resultKey,
destinationOffset = 0,
startIndex = 0,
endIndex = keySize * 4,
)

derivedBytes.copyInto(
destination = resultIv,
destinationOffset = 0,
startIndex = keySize * 4,
endIndex = (keySize + ivSize) * 4,
)

return derivedBytes // key + iv
}

Expand Down