2
0
Эх сурвалжийг харах

[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 <[email protected]>
sandtechnology 2 жил өмнө
parent
commit
3ec8e552cd

+ 14 - 4
mirai-core-api/src/commonMain/kotlin/spi/SPIServiceLoader.kt

@@ -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 {

+ 4 - 0
mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt

@@ -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

+ 50 - 3
mirai-core/src/commonMain/kotlin/network/protocol/packet/Tlv.kt

@@ -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) {
+        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) {
-        writeFully(byteArrayOf(0, 0, 0, 11)) // means native throws exception
+        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
+        }
     }
 }
 

+ 9 - 1
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin10.kt

@@ -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"
+                        )
+                    }
                 }
 
             }

+ 9 - 1
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin15.kt

@@ -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)
             }
         }

+ 19 - 4
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin2.kt

@@ -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"
+                        )
+                    }
                 }
             }
         }

+ 10 - 4
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin7.kt

@@ -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"
+                        )
+                    }
                 }
             }
         }

+ 9 - 0
mirai-core/src/commonMain/kotlin/network/protocol/packet/login/wtlogin/WtLogin9.kt

@@ -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"
+                        )
+                    }
                 }
             }
         }

+ 56 - 0
mirai-core/src/commonMain/kotlin/spi/EncryptService.kt

@@ -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
+    }
+}