From cc7f35519ea7cc03518a57dc2ee90d024f63be0e Mon Sep 17 00:00:00 2001 From: sandtechnology <20417547+sandtechnology@users.noreply.github.com> Date: Wed, 18 Jan 2023 17:41:21 +0800 Subject: [PATCH] Update login protocol (#2433) * Update login protocol Still need testing * Turn off debug option and make t547 null when failed * Fix wrong convert method and improve tips * Remove unused part and improve tips * Fix typo * Inline resultStatus for performance * Rename pow to PoW, the name should be "Proof of Work" * Add shadow and deps-test for kt-bignum * Try to fix deps-test * Fix deps-test again --- buildSrc/src/main/kotlin/Versions.kt | 3 + mirai-core-all/build.gradle.kts | 1 + .../src/commonMain/kotlin/ByteArrayOp.kt | 5 +- .../src/jvmBaseMain/kotlin/ByteArrayOp.kt | 12 +- .../src/nativeMain/kotlin/ByteArrayOp.kt | 5 + .../src/nativeMain/kotlin/CommonDigest.kt | 76 +++++++++++ mirai-core/build.gradle.kts | 9 ++ .../kotlin/network/QQAndroidClient.kt | 1 + .../kotlin/network/protocol/packet/Tlv.kt | 9 ++ .../network/protocol/packet/login/WtLogin.kt | 1 + .../packet/login/wtlogin/WtLogin10.kt | 3 +- .../protocol/packet/login/wtlogin/WtLogin2.kt | 22 +++- .../packet/login/wtlogin/WtLoginExt.kt | 123 ++++++++++++++++++ .../test/CoreShadowRelocationTest.kt | 8 ++ 14 files changed, 273 insertions(+), 5 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index ffb10d3cf..9b91d9f89 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -267,6 +267,9 @@ const val `netty-transport` = "io.netty:netty-transport:${Versions.netty}" const val `netty-buffer` = "io.netty:netty-buffer:${Versions.netty}" const val `bouncycastle` = "org.bouncycastle:bcprov-jdk15on:${Versions.bouncycastle}" +const val `kt-bignum` = "com.ionspin.kotlin:bignum:0.3.7" +val `kt-bignum_relocated` = RelocatedDependency(`kt-bignum`, "com.ionspin.kotlin.bignum") + const val `maven-resolver-api` = "org.apache.maven.resolver:maven-resolver-api:${Versions.mavenArtifactResolver}" const val `maven-resolver-impl` = "org.apache.maven.resolver:maven-resolver-impl:${Versions.mavenArtifactResolver}" const val `maven-resolver-connector-basic` = diff --git a/mirai-core-all/build.gradle.kts b/mirai-core-all/build.gradle.kts index 57ec98838..6f061caf7 100644 --- a/mirai-core-all/build.gradle.kts +++ b/mirai-core-all/build.gradle.kts @@ -25,6 +25,7 @@ dependencies { api(project(":mirai-core-utils")) implementation(`slf4j-api`) // Required by mirai-console + relocateImplementation(project, `kt-bignum_relocated`) relocateImplementation(project, `ktor-client-core_relocated`) relocateImplementation(project, `ktor-client-okhttp_relocated`) relocateImplementation(project, `ktor-io_relocated`) diff --git a/mirai-core-utils/src/commonMain/kotlin/ByteArrayOp.kt b/mirai-core-utils/src/commonMain/kotlin/ByteArrayOp.kt index 8ae8a476c..f80103bf2 100644 --- a/mirai-core-utils/src/commonMain/kotlin/ByteArrayOp.kt +++ b/mirai-core-utils/src/commonMain/kotlin/ByteArrayOp.kt @@ -24,6 +24,9 @@ public fun String.sha1(): ByteArray = toByteArray().sha1() public expect fun ByteArray.sha1(offset: Int = 0, length: Int = size - offset): ByteArray +public fun String.sha256(): ByteArray = toByteArray().sha256() + +public expect fun ByteArray.sha256(offset: Int = 0, length: Int = size - offset): ByteArray /////////////////////////////////////////////////////////////////////////// // How to choose 'inflate', 'inflateAllAvailable', 'InflateInput'? @@ -112,4 +115,4 @@ public expect fun DeflateInput(source: Input): Input /** * @see DeflateInput */ -public fun Input.deflateInput(): Input = DeflateInput(this) +public fun Input.deflateInput(): Input = DeflateInput(this) \ No newline at end of file diff --git a/mirai-core-utils/src/jvmBaseMain/kotlin/ByteArrayOp.kt b/mirai-core-utils/src/jvmBaseMain/kotlin/ByteArrayOp.kt index 9dbbdfb9b..ca094f99b 100644 --- a/mirai-core-utils/src/jvmBaseMain/kotlin/ByteArrayOp.kt +++ b/mirai-core-utils/src/jvmBaseMain/kotlin/ByteArrayOp.kt @@ -13,7 +13,7 @@ package net.mamoe.mirai.utils import io.ktor.utils.io.core.* -import io.ktor.utils.io.streams.asInput +import io.ktor.utils.io.streams.* import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStream @@ -50,6 +50,10 @@ public fun InputStream.sha1(): ByteArray { return digest("SHA-1") } +public fun InputStream.sha256(): ByteArray { + return digest("SHA-256") +} + public actual fun ByteArray.md5(offset: Int, length: Int): ByteArray { checkOffsetAndLength(offset, length) return MessageDigest.getInstance("MD5").apply { update(this@md5, offset, length) }.digest() @@ -62,6 +66,12 @@ public actual fun ByteArray.sha1(offset: Int, length: Int): ByteArray { return MessageDigest.getInstance("SHA-1").apply { update(this@sha1, offset, length) }.digest() } +@JvmOverloads +public actual fun ByteArray.sha256(offset: Int, length: Int): ByteArray { + checkOffsetAndLength(offset, length) + return MessageDigest.getInstance("SHA-256").apply { update(this@sha256, offset, length) }.digest() +} + @JvmOverloads public actual fun ByteArray.gzip(offset: Int, length: Int): ByteArray { ByteArrayOutputStream().use { buf -> diff --git a/mirai-core-utils/src/nativeMain/kotlin/ByteArrayOp.kt b/mirai-core-utils/src/nativeMain/kotlin/ByteArrayOp.kt index 170284331..e923d95b8 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/ByteArrayOp.kt +++ b/mirai-core-utils/src/nativeMain/kotlin/ByteArrayOp.kt @@ -31,6 +31,11 @@ public actual fun ByteArray.sha1(offset: Int, length: Int): ByteArray = SHA1.cre return digest().bytes } +public actual fun ByteArray.sha256(offset: Int, length: Int): ByteArray = SHA256.create().run { + update(this@sha256, offset, length) + return digest().bytes +} + /** * WARNING: DO NOT SET THIS BUFFER TOO SMALL, OR YOU WILL SEE COMPRESSION ERROR. */ diff --git a/mirai-core-utils/src/nativeMain/kotlin/CommonDigest.kt b/mirai-core-utils/src/nativeMain/kotlin/CommonDigest.kt index ea53c3d3a..63562ca1a 100644 --- a/mirai-core-utils/src/nativeMain/kotlin/CommonDigest.kt +++ b/mirai-core-utils/src/nativeMain/kotlin/CommonDigest.kt @@ -272,3 +272,79 @@ internal class SHA1 : SHA(chunkSize = 64, digestSize = 20) { for (n in out.indices) out[n] = (h[n / 4] ushr (24 - 8 * (n % 4))).toByte() } } + +internal class SHA256 : SHA(chunkSize = 64, digestSize = 32) { + companion object : HasherFactory({ SHA256() }) { + private val H = intArrayOf( + 0x6a09e667, -0x4498517b, 0x3c6ef372, -0x5ab00ac6, + 0x510e527f, -0x64fa9774, 0x1f83d9ab, 0x5be0cd19 + ) + + private val K = intArrayOf( + 0x428a2f98, 0x71374491, -0x4a3f0431, -0x164a245b, + 0x3956c25b, 0x59f111f1, -0x6dc07d5c, -0x54e3a12b, + -0x27f85568, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, -0x7f214e02, -0x6423f959, -0x3e640e8c, + -0x1b64963f, -0x1041b87a, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + -0x67c1aeae, -0x57ce3993, -0x4ffcd838, -0x40a68039, + -0x391ff40d, -0x2a586eb9, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, -0x7e3d36d2, -0x6d8dd37b, + -0x5d40175f, -0x57e599b5, -0x3db47490, -0x3893ae5d, + -0x2e6d17e7, -0x2966f9dc, -0xbf1ca7b, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, -0x7b3787ec, -0x7338fdf8, + -0x6f410006, -0x5baf9315, -0x41065c09, -0x398e870e + ) + } + + + private val h = IntArray(8) + private val r = IntArray(8) + private val w = IntArray(64) + + init { + coreReset() + } + + override fun coreReset() { + arraycopy(H, 0, h, 0, 8) + } + + override fun coreUpdate(chunk: ByteArray) { + arraycopy(h, 0, r, 0, 8) + + for (j in 0 until 16) w[j] = chunk.readS32_be(j * 4) + for (j in 16 until 64) { + val s0 = w[j - 15].rotateRight(7) xor w[j - 15].rotateRight(18) xor w[j - 15].ushr(3) + val s1 = w[j - 2].rotateRight(17) xor w[j - 2].rotateRight(19) xor w[j - 2].ushr(10) + w[j] = w[j - 16] + s0 + w[j - 7] + s1 + } + + for (j in 0 until 64) { + val s1 = r[4].rotateRight(6) xor r[4].rotateRight(11) xor r[4].rotateRight(25) + val ch = r[4] and r[5] xor (r[4].inv() and r[6]) + val t1 = r[7] + s1 + ch + K[j] + w[j] + val s0 = r[0].rotateRight(2) xor r[0].rotateRight(13) xor r[0].rotateRight(22) + val maj = r[0] and r[1] xor (r[0] and r[2]) xor (r[1] and r[2]) + val t2 = s0 + maj + r[7] = r[6] + r[6] = r[5] + r[5] = r[4] + r[4] = r[3] + t1 + r[3] = r[2] + r[2] = r[1] + r[1] = r[0] + r[0] = t1 + t2 + + } + for (j in 0 until 8) h[j] += r[j] + } + + override fun coreDigest(out: ByteArray) { + for (n in out.indices) out[n] = (h[n / 4] ushr (24 - 8 * (n % 4))).toByte() + } +} + diff --git a/mirai-core/build.gradle.kts b/mirai-core/build.gradle.kts index cf2479bd0..df38a1f59 100644 --- a/mirai-core/build.gradle.kts +++ b/mirai-core/build.gradle.kts @@ -41,6 +41,7 @@ kotlin { api(`kotlinx-serialization-json`) api(`kotlinx-coroutines-core`) + implementation(`kt-bignum`) implementation(project(":mirai-core-utils")) implementation(`kotlinx-serialization-protobuf`) implementation(`kotlinx-atomicfu`) @@ -108,6 +109,14 @@ kotlin { } + // Kt bignum + findByName("jvmBaseMain")?.apply { + dependencies { + relocateImplementation(`kt-bignum_relocated`) + } + } + + // Ktor findByName("commonMain")?.apply { diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt index 2974589f9..5fbcf81c9 100644 --- a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt +++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt @@ -166,6 +166,7 @@ internal open class QQAndroidClient( var reserveUinInfo: ReserveUinInfo? = null var t402: ByteArray? = null lateinit var t104: ByteArray + var t547: ByteArray? = null } internal val QQAndroidClient.apkId: ByteArray get() = "com.tencent.mobileqq".toByteArray() diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt index 4c8937852..4ce77c137 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt @@ -275,6 +275,15 @@ internal fun BytePacketBuilder.t104( } } +internal fun BytePacketBuilder.t547( + t547Data: ByteArray +) { + writeShort(0x547) + writeShortLVPacket { + writeFully(t547Data) + } +} + internal fun BytePacketBuilder.t174( t174Data: ByteArray ) { diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt index 724fe1d7f..53a9cad81 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/WtLogin.kt @@ -210,6 +210,7 @@ internal class WtLogin { tlvMap[0x161]?.let { bot.client.analysisTlv161(it) } tlvMap[0x403]?.let { bot.client.randSeed = it } tlvMap[0x402]?.let { bot.client.t402 = it } + tlvMap[0x546]?.let { bot.client.analysisTlv546(it) } // tlvMap[0x402]?.let { t402 -> // bot.client.G = buildPacket { // writeFully(bot.client.device.guid) diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin10.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin10.kt index 38d457bd0..9f6b00a4a 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin10.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin10.kt @@ -43,7 +43,7 @@ internal object WtLogin10 : WtLoginExt { 0x0810 ) { writeShort(11) // subCommand - writeShort(17) + writeShort(18) t100(appId, subAppId, client.appClientVersion, client.ssoVersion, mainSigMap) t10a(client.wLoginSigInfo.tgt) t116(client.miscBitMap, client.subSigMap) @@ -80,6 +80,7 @@ internal object WtLogin10 : WtLoginExt { t188(client.device.androidId) t194(client.device.imsiMd5) t511() + t202(client.device.wifiBSSID, client.device.wifiSSID) //t544() } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin2.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin2.kt index 312d85735..4c8faa900 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin2.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin2.kt @@ -26,11 +26,20 @@ internal object WtLogin2 : WtLoginExt { writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) { writeOicqRequestPacket(client, commandId = 0x0810) { writeShort(2) // subCommand - writeShort(4) // count of TLVs + writeShort( + if (client.t547 == null) { + 4 + } else { + 5 + } + ) // count of TLVs t193(ticket) t8(2052) t104(client.t104) t116(client.miscBitMap, client.subSigMap) + client.t547?.let { + t547(it) + } } } } @@ -43,11 +52,20 @@ internal object WtLogin2 : WtLoginExt { writeSsoPacket(client, client.subAppId, WtLogin.Login.commandName, sequenceId = sequenceId) { writeOicqRequestPacket(client, commandId = 0x0810) { writeShort(2) // subCommand - writeShort(4) // count of TLVs + writeShort( + if (client.t547 == null) { + 4 + } else { + 5 + } + ) // count of TLVs t2(captchaAnswer, captchaSign, 0) t8(2052) t104(client.t104) t116(client.miscBitMap, client.subSigMap) + client.t547?.let { + t547(it) + } } } } diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt index 6e27be447..1ec5f8252 100644 --- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt +++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLoginExt.kt @@ -9,6 +9,9 @@ package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin +import com.ionspin.kotlin.bignum.integer.BigInteger +import com.ionspin.kotlin.bignum.integer.util.fromTwosComplementByteArray +import com.ionspin.kotlin.bignum.integer.util.toTwosComplementByteArray import io.ktor.utils.io.core.* import net.mamoe.mirai.internal.QQAndroidBot import net.mamoe.mirai.internal.network.LoginExtraData @@ -171,6 +174,126 @@ internal interface WtLoginExt { // so as not to register to global extension this.t150 = Tlv(t150) } + fun QQAndroidClient.analysisTlv546(t546: ByteArray) { + val version: Byte + val algorithmType: Byte + val hashType: Byte + val maxIndex: Short + val reserveBytes: ByteArray + val inputBigNumArr: ByteArray + val targetHashArr: ByteArray + val reserveHashArr: ByteArray + var resultArr: ByteArray = EMPTY_BYTE_ARRAY; + var costTimeMS: Int = 0; + var recursiveDepth: Int = 0; + var failed = false + + fun getPadRemaining(bigNumArr: ByteArray, bound: Short): Int { + if (bound > 32) { + return 1 + } + var maxLoopCount = 255 + var index = 0 + while (maxLoopCount >= 0 && index < bound) { + if (bigNumArr[maxLoopCount / 8].toInt() and (1 shl maxLoopCount) % 8 != 0) { + return 2 + } + maxLoopCount-- + index++ + } + return 0 + } + + fun calcType1(bigNumArrIn: ByteArray, maxLength: Short) { + var bigIntArrClone = bigNumArrIn.copyOf() + val originLength = bigIntArrClone.size + var bigInteger = BigInteger.fromTwosComplementByteArray(bigIntArrClone) + while (true) { + if (getPadRemaining(bigIntArrClone.sha256().copyOf(32), maxLength) == 0) { + resultArr = bigIntArrClone + return + } + recursiveDepth++ + bigInteger = bigInteger.add(BigInteger.ONE) + bigIntArrClone = bigInteger.toTwosComplementByteArray() + if (bigIntArrClone.size > originLength) { + failed = true + return + } + } + } + + fun calcType2(bigNumArrIn: ByteArray, hashTarget: ByteArray) { + var bigIntArrClone = bigNumArrIn.copyOf() + val originLength = bigIntArrClone.size + var bigInteger = BigInteger.fromTwosComplementByteArray(bigIntArrClone) + while (true) { + if (bigIntArrClone.sha256().copyOf(32).contentEquals(hashTarget)) { + resultArr = bigIntArrClone + return + } + recursiveDepth++ + bigInteger = bigInteger.add(BigInteger.ONE) + bigIntArrClone = bigInteger.toTwosComplementByteArray() + if (bigIntArrClone.size > originLength) { + failed = true + return + } + + } + } + + t546.toReadPacket().apply { + version = readByte() + algorithmType = readByte() + hashType = readByte() + readByte() // Ignore resultStatus since it's useless + maxIndex = readShort() + reserveBytes = readBytes(2) + inputBigNumArr = readBytes(readShort().toInt()) + targetHashArr = readBytes(readShort().toInt()) + reserveHashArr = readBytes(readShort().toInt()) + } + val startTimeMS: Long = currentTimeMillis() + costTimeMS = 0 + recursiveDepth = 0 + if (hashType == 1.toByte()) { + bot.logger.info("Calculating type $algorithmType PoW, it can take some time....") + when (algorithmType.toInt()) { + 1 -> calcType1(inputBigNumArr, maxIndex) + 2 -> calcType2(inputBigNumArr, targetHashArr) + else -> { + failed = true + bot.logger.warning("Unsupported tlv546 algorithm type:${algorithmType}") + } + } + } else { + failed = true + bot.logger.warning("Unsupported tlv546 hash type:${hashType}") + } + if (!failed) { + costTimeMS = (currentTimeMillis() - startTimeMS).toInt() + bot.logger.info("Got PoW result, cost: $costTimeMS ms") + this.t547 = buildPacket { + writeByte(version) + writeByte(algorithmType) + writeByte(hashType) + writeByte(1) //resultStatus + writeShort(maxIndex) + writeFully(reserveBytes) + writeShortLVByteArray(inputBigNumArr) + writeShortLVByteArray(targetHashArr) + writeShortLVByteArray(reserveHashArr) + writeShortLVByteArray(resultArr) + writeInt(costTimeMS) + writeInt(recursiveDepth) + }.readBytes() + } else { + bot.logger.warning("Failed to get PoW result, login may fail with error 0x6!") + } + + } + fun QQAndroidClient.analysisTlv161(t161: ByteArray) { val tlv = t161.toReadPacket().apply { discardExact(2) }.withUse { _readTLVMap() } diff --git a/mirai-deps-test/test/CoreShadowRelocationTest.kt b/mirai-deps-test/test/CoreShadowRelocationTest.kt index 6781ab2c9..5bb43b8c7 100644 --- a/mirai-deps-test/test/CoreShadowRelocationTest.kt +++ b/mirai-deps-test/test/CoreShadowRelocationTest.kt @@ -23,6 +23,7 @@ class CoreShadowRelocationTest : AbstractTest() { private const val KtorOkHttp = "io.ktor.client.engine.okhttp.OkHttp" private const val OkHttp = "okhttp3.OkHttp" private const val OkIO = "okio.ByteString" + private const val BigInteger = "com.ionspin.kotlin.bignum.integer.BigInteger" fun relocated(string: String): String { return "net.mamoe.mirai.internal.deps.$string" @@ -38,6 +39,7 @@ class CoreShadowRelocationTest : AbstractTest() { -both(`ktor-client-okhttp`) -both(`okhttp3-okhttp`) -both(okio) + -both(`kt-bignum`) } applyCodeFragment(fragment) buildFile.appendText( @@ -59,6 +61,7 @@ class CoreShadowRelocationTest : AbstractTest() { -both(`ktor-client-okhttp`) -both(`okhttp3-okhttp`) -both(okio) + -both(`kt-bignum`) +relocated(`ExternalResource-input`) } applyCodeFragment(fragment) @@ -82,6 +85,7 @@ class CoreShadowRelocationTest : AbstractTest() { +relocated(`okhttp3-okhttp`) +relocated(okio) +relocated(`ExternalResource-input`) + +relocated(`kt-bignum`) } applyCodeFragment(fragment) buildFile.appendText( @@ -106,6 +110,7 @@ class CoreShadowRelocationTest : AbstractTest() { +relocated(`ktor-client-okhttp`) +relocated(`okhttp3-okhttp`) +relocated(okio) + +relocated(`kt-bignum`) } applyCodeFragment(fragment) buildFile.appendText( @@ -130,6 +135,7 @@ class CoreShadowRelocationTest : AbstractTest() { -both(`ktor-client-okhttp`) -both(`okhttp3-okhttp`) -both(okio) + -both(`kt-bignum`) // +relocated(`ExternalResource-input`) // Will fail with no class def found error because there is no runtime ktor-io } applyCodeFragment(fragment) @@ -155,6 +161,7 @@ class CoreShadowRelocationTest : AbstractTest() { +relocated(`okhttp3-okhttp`) +relocated(okio) +relocated(`ExternalResource-input`) + +relocated(`kt-bignum`) } applyCodeFragment(fragment) @@ -231,6 +238,7 @@ class CoreShadowRelocationTest : AbstractTest() { val `ktor-client-okhttp` = ClassTestCase("ktor-client-core OkHttp", KtorOkHttp) val `okhttp3-okhttp` = ClassTestCase("okhttp3 OkHttp", OkHttp) val okio = ClassTestCase("okio ByteString", OkIO) + val `kt-bignum` = ClassTestCase("kt-bignum BigInteger", BigInteger) val `ExternalResource-input` = FunctionTestCase( "ExternalResource_input",