[core] Add encryption SPI & T544 support (#2566)

* Initial design for encryption SPI & T544 support

* Fix missed vararg

* Move EncryptWorkerService to internal spi

* Add useAndroid val to QQAndroidClient and rebase to the latest dev branch

* Remove docs and annotations (Throws and MiraiExperimentalApi)

* Remove unwanted changes

* Remove redundant runBlocking in tlv544

* Tweak the file annotation location

* Remove suspend modifier for doTLVEncrypt

* Disable DebuggingProperties

* Fix format

* Remove empty line

* Remove empty line again

* Use instance field directly

* Add Volatile annotation to instance

* Use atomicRef instead of field

* Use kotlin lambda for readability

* wip

* Improve EncryptService API

* Add possible values

* remove docs

* Add key BOT_PROTOCOL

---------

Co-authored-by: Him188 <Him188@mamoe.net>
This commit is contained in:
sandtechnology 2023-06-04 07:01:54 +08:00 committed by GitHub
parent 68c991c078
commit 3ec8e552cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 180 additions and 17 deletions

View File

@ -41,6 +41,7 @@ internal fun <T : BaseService> SpiServiceLoader(
internal interface SpiServiceLoader<T : BaseService?> {
val service: T
val allServices: List<T & Any>
}
internal class SpiServiceLoaderImpl<T : BaseService?>(
@ -52,9 +53,15 @@ internal class SpiServiceLoaderImpl<T : BaseService?>(
}
private val lock = SynchronizedObject()
override val service: T get() = _service
override val service: T get() = _service.bestService
override val allServices: List<T & Any> get() = _service.allServices
private var _service: T by lateinitMutableProperty {
private class Loaded<T>(
val bestService: T,
val allServices: List<T & Any>,
)
private var _service: Loaded<T> by lateinitMutableProperty {
synchronized(lock) {
reloadAndSelect()
}
@ -66,9 +73,12 @@ internal class SpiServiceLoaderImpl<T : BaseService?>(
}
}
private fun reloadAndSelect(): T {
private fun reloadAndSelect(): Loaded<T> {
val allServices = loadServices(serviceType).toList()
@Suppress("UNCHECKED_CAST")
return (loadServices(serviceType).minByOrNull { it.priority } ?: defaultInstance) as T
val bestService = (allServices.minByOrNull { it.priority } ?: defaultInstance) as T
return Loaded(bestService, allServices)
}
companion object {

View File

@ -82,6 +82,10 @@ internal open class QQAndroidClient(
override var outgoingPacketSessionId: ByteArray = 0x02B05B8B.toByteArray()
override var loginState = 0
val useAndroid by lazy {
bot.configuration.protocol == BotConfiguration.MiraiProtocol.ANDROID_PHONE ||
bot.configuration.protocol == BotConfiguration.MiraiProtocol.ANDROID_PAD
}
var onlineStatus: OnlineStatus = OnlineStatus.ONLINE
var fileStoragePushFSSvcList: FileStoragePushFSSvcList? = null

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
* Copyright 2019-2023 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -14,6 +14,8 @@ package net.mamoe.mirai.internal.network.protocol.packet
import io.ktor.utils.io.core.*
import net.mamoe.mirai.internal.network.*
import net.mamoe.mirai.internal.network.protocol.LoginType
import net.mamoe.mirai.internal.spi.EncryptService
import net.mamoe.mirai.internal.spi.EncryptServiceContext
import net.mamoe.mirai.internal.utils.GuidSource
import net.mamoe.mirai.internal.utils.MacOrAndroidIdChangeFlag
import net.mamoe.mirai.internal.utils.NetworkType
@ -961,10 +963,55 @@ internal fun TlvMapWriter.t548(
}
}
internal fun TlvMapWriter.t544( // 1334
internal fun TlvMapWriter.t544ForToken( // 1348
uin: Long,
guid: ByteArray,
sdkVersion: String,
subCommandId: Int,
commandStr: String
) {
val service = EncryptService.instance ?: return
tlv(0x544) {
writeFully(byteArrayOf(0, 0, 0, 11)) // means native throws exception
buildPacket {
writeFully(buildPacket {
writeLong(uin)
}.readBytes(4))
writeShortLVByteArray(guid)
writeShortLVString(sdkVersion)
writeInt(subCommandId)
writeInt(0)
}.use { dataIn ->
service.encryptTlv(EncryptServiceContext(uin, buildTypeSafeMap {
set(EncryptServiceContext.KEY_COMMAND_STR, commandStr)
}), 0x544, dataIn.readBytes())
}.let { result ->
writeFully(result ?: "".toByteArray()) // Empty str means native throws exception
}
}
}
internal fun TlvMapWriter.t544ForVerify( // 1348
uin: Long,
guid: ByteArray,
sdkVersion: String,
subCommandId: Int,
commandStr: String
) {
val service = EncryptService.instance ?: return
tlv(0x544) {
buildPacket {
writeLong(uin)
writeShortLVByteArray(guid)
writeShortLVString(sdkVersion)
writeInt(subCommandId)
}.use { dataIn ->
service.encryptTlv(EncryptServiceContext(uin, buildTypeSafeMap {
set(EncryptServiceContext.KEY_COMMAND_STR, commandStr)
}), 0x544, dataIn.readBytes())
}.let { result ->
writeFully(result ?: "".toByteArray()) // Empty str means native throws exception
}
}
}

View File

@ -85,7 +85,15 @@ internal object WtLogin10 : WtLoginExt {
t194(client.device.imsiMd5)
t511()
t202(client.device.wifiBSSID, client.device.wifiSSID)
//t544()
if (client.useAndroid) {
t544ForToken(
uin = client.uin,
guid = client.device.guid,
sdkVersion = client.sdkVersion,
subCommandId = 10,
commandStr = "810_a"
)
}
}
}

View File

@ -135,7 +135,15 @@ internal object WtLogin15 : WtLoginExt {
t521() // new
t525(client.loginExtraData) // new
//t544() // new 810_f
if (client.useAndroid) {
t544ForToken(
uin = client.uin,
guid = client.device.guid,
sdkVersion = client.sdkVersion,
subCommandId = 15,
commandStr = "810_f"
)
}
t545(client.qimei16 ?: client.device.imei)
}
}

View File

@ -10,12 +10,9 @@
package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin
import io.ktor.utils.io.core.*
import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.miscBitMap
import net.mamoe.mirai.internal.network.*
import net.mamoe.mirai.internal.network.protocol.packet.*
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
import net.mamoe.mirai.internal.network.subAppId
import net.mamoe.mirai.internal.network.subSigMap
import net.mamoe.mirai.utils._writeTlvMap
@ -34,6 +31,15 @@ internal object WtLogin2 : WtLoginExt {
t104(client.t104)
t116(client.miscBitMap, client.subSigMap)
client.t547?.let { t547(it) }
if (client.useAndroid) {
t544ForVerify(
uin = client.uin,
guid = client.device.guid,
sdkVersion = client.sdkVersion,
subCommandId = 2,
commandStr = "810_2"
)
}
}
}
}
@ -54,6 +60,15 @@ internal object WtLogin2 : WtLoginExt {
t104(client.t104)
t116(client.miscBitMap, client.subSigMap)
client.t547?.let { t547(it) }
if (client.useAndroid) {
t544ForVerify(
uin = client.uin,
guid = client.device.guid,
sdkVersion = client.sdkVersion,
subCommandId = 2,
commandStr = "810_2"
)
}
}
}
}

View File

@ -10,12 +10,9 @@
package net.mamoe.mirai.internal.network.protocol.packet.login.wtlogin
import io.ktor.utils.io.core.*
import net.mamoe.mirai.internal.network.QQAndroidClient
import net.mamoe.mirai.internal.network.miscBitMap
import net.mamoe.mirai.internal.network.*
import net.mamoe.mirai.internal.network.protocol.packet.*
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
import net.mamoe.mirai.internal.network.subAppId
import net.mamoe.mirai.internal.network.subSigMap
import net.mamoe.mirai.utils.DeviceVerificationRequests
import net.mamoe.mirai.utils._writeTlvMap
@ -44,6 +41,15 @@ internal object WtLogin7 : WtLoginExt {
t17c(code.encodeToByteArray())
t401(client.G)
t198()
if (client.useAndroid) {
t544ForVerify(
uin = client.uin,
guid = client.device.guid,
sdkVersion = client.sdkVersion,
subCommandId = 7,
commandStr = "810_7"
)
}
}
}
}

View File

@ -134,6 +134,15 @@ internal object WtLogin9 : WtLoginExt {
// this.build().debugPrint("傻逼")
// ignored t318 because not logging in by QR
if (client.useAndroid) {
t544ForToken(
uin = client.uin,
guid = client.device.guid,
sdkVersion = client.sdkVersion,
subCommandId = 9,
commandStr = "810_9"
)
}
}
}
}

View File

@ -0,0 +1,56 @@
/*
* Copyright 2019-2023 Mamoe Technologies and contributors.
*
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
package net.mamoe.mirai.internal.spi
import net.mamoe.mirai.Bot
import net.mamoe.mirai.spi.BaseService
import net.mamoe.mirai.spi.SpiServiceLoader
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.MiraiInternalApi
import net.mamoe.mirai.utils.TypeKey
import net.mamoe.mirai.utils.TypeSafeMap
/**
* @since 2.15.0
*/
public class EncryptServiceContext @MiraiInternalApi constructor(
/**
* [Bot.id]
*/
public val id: Long,
public val extraArgs: TypeSafeMap = TypeSafeMap.EMPTY
) {
public companion object {
public val KEY_COMMAND_STR: TypeKey<String> = TypeKey("KEY_COMMAND_STR")
public val KEY_BOT_PROTOCOL: TypeKey<BotConfiguration.MiraiProtocol> = TypeKey("BOT_PROTOCOL")
}
}
/**
* @since 2.15.0
*/
public interface EncryptService : BaseService {
/**
* Returns `null` if not supported.
*/
public fun encryptTlv(
context: EncryptServiceContext,
tlvType: Int,
payload: ByteArray, // Do not write to payload
): ByteArray?
public companion object {
private val loader = SpiServiceLoader(EncryptService::class)
internal val instance: EncryptService? get() = loader.service
}
}