mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-25 03:30:15 +08:00
[core] decode SsoEstablishShareKey
This commit is contained in:
parent
ff96dce35d
commit
1dacce61cd
@ -18,6 +18,7 @@ import net.mamoe.mirai.internal.network.components.PacketCodecException.Kind.*
|
|||||||
import net.mamoe.mirai.internal.network.handler.selector.NetworkException
|
import net.mamoe.mirai.internal.network.handler.selector.NetworkException
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.*
|
import net.mamoe.mirai.internal.network.protocol.packet.*
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
|
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.packet.sso.PREFIX_TRPC_SSO
|
||||||
import net.mamoe.mirai.internal.utils.crypto.Ecdh
|
import net.mamoe.mirai.internal.utils.crypto.Ecdh
|
||||||
import net.mamoe.mirai.internal.utils.crypto.TEA
|
import net.mamoe.mirai.internal.utils.crypto.TEA
|
||||||
import net.mamoe.mirai.utils.*
|
import net.mamoe.mirai.utils.*
|
||||||
@ -95,6 +96,17 @@ internal class PacketCodecException(
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal enum class IncomingPacketType(val value: Int) {
|
||||||
|
Login(0x0A),
|
||||||
|
Simple(0x0B),
|
||||||
|
Unknown(-1)
|
||||||
|
;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
internal fun of(value: Int) = enumValues<IncomingPacketType>().find { it.value == value } ?: Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class PacketCodecImpl : PacketCodec {
|
internal class PacketCodecImpl : PacketCodec {
|
||||||
|
|
||||||
override fun decodeRaw(
|
override fun decodeRaw(
|
||||||
@ -102,15 +114,15 @@ internal class PacketCodecImpl : PacketCodec {
|
|||||||
input: ByteReadPacket
|
input: ByteReadPacket
|
||||||
): RawIncomingPacket = input.run {
|
): RawIncomingPacket = input.run {
|
||||||
// packet type
|
// packet type
|
||||||
val type = readInt()
|
val packetType = IncomingPacketType.of(readInt())
|
||||||
|
|
||||||
PacketLogger.verbose { "开始处理一个包" }
|
PacketLogger.verbose { "开始处理一个包" }
|
||||||
|
|
||||||
val encryptMethod = readByte().toInt()
|
val encryptMethod = PacketEncryptType.of(readByte().toInt())
|
||||||
val flag3 = readByte().toInt()
|
val flag3 = readByte().toInt()
|
||||||
val flag3Exception = if (flag3 != 0) {
|
val flag3Exception = if (flag3 != 0) {
|
||||||
PacketCodecException(
|
PacketCodecException(
|
||||||
"Illegal flag3. Expected 0, whereas got $flag3. packet type=$type, encrypt method=$encryptMethod. ",
|
"Illegal flag3. Expected 0, whereas got $flag3. packet type=$packetType, encrypt method=$encryptMethod. ",
|
||||||
kind = PROTOCOL_UPDATED
|
kind = PROTOCOL_UPDATED
|
||||||
)
|
)
|
||||||
} else null
|
} else null
|
||||||
@ -122,10 +134,8 @@ internal class PacketCodecImpl : PacketCodec {
|
|||||||
|
|
||||||
val raw = try {
|
val raw = try {
|
||||||
when (encryptMethod) {
|
when (encryptMethod) {
|
||||||
// empty key
|
PacketEncryptType.Empty -> TEA.decrypt(buffer, DECRYPTER_16_ZERO, size)
|
||||||
2 -> TEA.decrypt(buffer, DECRYPTER_16_ZERO, size)
|
PacketEncryptType.D2 -> {
|
||||||
// d2 key
|
|
||||||
1 -> {
|
|
||||||
TEA.decrypt(buffer, kotlin.runCatching { client.wLoginSigInfo.d2Key }.getOrElse {
|
TEA.decrypt(buffer, kotlin.runCatching { client.wLoginSigInfo.d2Key }.getOrElse {
|
||||||
throw PacketCodecException(
|
throw PacketCodecException(
|
||||||
"Received packet needed d2Key to decrypt but d2Key doesn't existed, ignoring. Please report to https://github.com/mamoe/mirai/issues/new/choose if you see anything abnormal",
|
"Received packet needed d2Key to decrypt but d2Key doesn't existed, ignoring. Please report to https://github.com/mamoe/mirai/issues/new/choose if you see anything abnormal",
|
||||||
@ -133,17 +143,18 @@ internal class PacketCodecImpl : PacketCodec {
|
|||||||
)
|
)
|
||||||
}, size)
|
}, size)
|
||||||
}
|
}
|
||||||
// no encrypt
|
|
||||||
0 -> buffer
|
PacketEncryptType.NoEncrypt -> buffer
|
||||||
else -> throw PacketCodecException("Unknown encrypt type=$encryptMethod", PROTOCOL_UPDATED)
|
else -> throw PacketCodecException("Unknown encrypt type=$encryptMethod", PROTOCOL_UPDATED)
|
||||||
}.let { decryptedData ->
|
}.let { decryptedData ->
|
||||||
when (type) {
|
when (packetType) {
|
||||||
// login
|
IncomingPacketType.Login -> parseSsoFrame(client, decryptedData)
|
||||||
0x0A -> parseSsoFrame(client, decryptedData)
|
IncomingPacketType.Simple -> parseSsoFrame(
|
||||||
// simple
|
client,
|
||||||
0x0B -> parseSsoFrame(client, decryptedData) // 这里可能是 uni?? 但测试时候发现结构跟 sso 一样.
|
decryptedData
|
||||||
|
) // 这里可能是 uni?? 但测试时候发现结构跟 sso 一样.
|
||||||
else -> throw PacketCodecException(
|
else -> throw PacketCodecException(
|
||||||
"unknown packet type: ${type.toByte().toUHexString()}",
|
"unknown packet type: ${packetType.value.toUHexString()}",
|
||||||
PROTOCOL_UPDATED
|
PROTOCOL_UPDATED
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -163,25 +174,39 @@ internal class PacketCodecImpl : PacketCodec {
|
|||||||
"which may means protocol is updated.",
|
"which may means protocol is updated.",
|
||||||
flag3Exception
|
flag3Exception
|
||||||
)
|
)
|
||||||
|
} else if (raw.commandName.startsWith(PREFIX_TRPC_SSO)) {
|
||||||
|
PacketLogger.verbose { "received a trpc native packet: ${raw.commandName}" }
|
||||||
} else {
|
} else {
|
||||||
throw flag3Exception
|
throw flag3Exception
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
when (encryptMethod) {
|
when (encryptMethod) {
|
||||||
0, 1 -> RawIncomingPacket(raw.commandName, raw.sequenceId, raw.body.readBytes())
|
PacketEncryptType.NoEncrypt,
|
||||||
2 -> RawIncomingPacket(
|
PacketEncryptType.D2 -> RawIncomingPacket(
|
||||||
raw.commandName,
|
raw.commandName,
|
||||||
raw.sequenceId,
|
raw.sequenceId,
|
||||||
raw.body.withUse {
|
raw.body.readBytes()
|
||||||
try {
|
|
||||||
parseOicqResponse(client, raw.commandName)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
throw PacketCodecException(e, PacketCodecException.Kind.OTHER)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
PacketEncryptType.Empty -> {
|
||||||
|
RawIncomingPacket(
|
||||||
|
raw.commandName,
|
||||||
|
raw.sequenceId,
|
||||||
|
raw.body.withUse {
|
||||||
|
if (raw.commandName.startsWith(PREFIX_TRPC_SSO)) {
|
||||||
|
readBytes()
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
parseOicqResponse(client, raw.commandName)
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
throw PacketCodecException(e, PacketCodecException.Kind.OTHER)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
else -> error("unreachable")
|
else -> error("unreachable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ import net.mamoe.mirai.internal.network.*
|
|||||||
import net.mamoe.mirai.internal.network.components.EcdhInitialPublicKeyUpdater
|
import net.mamoe.mirai.internal.network.components.EcdhInitialPublicKeyUpdater
|
||||||
import net.mamoe.mirai.internal.network.protocol.data.proto.SSOReserveField
|
import net.mamoe.mirai.internal.network.protocol.data.proto.SSOReserveField
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
|
import net.mamoe.mirai.internal.network.protocol.packet.chat.receive.MessageSvcPbSendMsg
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
|
import net.mamoe.mirai.internal.network.protocol.packet.sso.SsoEstablishShareKey
|
||||||
import net.mamoe.mirai.internal.spi.EncryptService
|
import net.mamoe.mirai.internal.spi.EncryptService
|
||||||
import net.mamoe.mirai.internal.spi.EncryptServiceContext
|
import net.mamoe.mirai.internal.spi.EncryptServiceContext
|
||||||
import net.mamoe.mirai.internal.utils.io.encryptAndWrite
|
import net.mamoe.mirai.internal.utils.io.encryptAndWrite
|
||||||
@ -75,25 +75,34 @@ internal class IncomingPacket private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum class PacketEncryptType {
|
internal enum class PacketEncryptType(val value: Int) {
|
||||||
NoEncrypt { // 0x00
|
NoEncrypt(0x00) { // 0x00
|
||||||
override fun defaultKey(client: QQAndroidClient): ByteArray = NO_ENCRYPT
|
override fun defaultKey(client: QQAndroidClient): ByteArray = NO_ENCRYPT
|
||||||
},
|
},
|
||||||
D2 { //0x01
|
D2(0x01) { //0x01
|
||||||
override fun defaultKey(client: QQAndroidClient): ByteArray {
|
override fun defaultKey(client: QQAndroidClient): ByteArray {
|
||||||
return client.wLoginSigInfo.d2Key
|
return client.wLoginSigInfo.d2Key
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Empty { // 16 zeros,// 0x02
|
Empty(0x02) { // 16 zeros,// 0x02
|
||||||
override fun defaultKey(client: QQAndroidClient): ByteArray {
|
override fun defaultKey(client: QQAndroidClient): ByteArray {
|
||||||
return KEY_16_ZEROS
|
return KEY_16_ZEROS
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Unknown(-1) {
|
||||||
|
override fun defaultKey(client: QQAndroidClient): ByteArray {
|
||||||
|
error("unreachable")
|
||||||
|
}
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
inline val codec: Byte get() = ordinal.toByte()
|
inline val codec: Byte get() = ordinal.toByte()
|
||||||
|
|
||||||
abstract fun defaultKey(client: QQAndroidClient): ByteArray
|
abstract fun defaultKey(client: QQAndroidClient): ByteArray
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
internal fun of(value: Int) = enumValues<PacketEncryptType>().find { it.value == value } ?: Unknown
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -251,9 +260,9 @@ internal fun createChannelProxy(client: QQAndroidClient): EncryptService.Channel
|
|||||||
uin: Long,
|
uin: Long,
|
||||||
data: ByteArray
|
data: ByteArray
|
||||||
): EncryptService.ChannelResult? {
|
): EncryptService.ChannelResult? {
|
||||||
if (commandName == "trpc.o3.ecdh_access.EcdhAccess.SsoEstablishShareKey") {
|
if (commandName == SsoEstablishShareKey.commandName) {
|
||||||
val packet = client.bot.network.sendAndExpect<Packet>(
|
val packet = client.bot.network.sendAndExpect(
|
||||||
WtLogin.Login.buildLoginOutgoingPacket(
|
SsoEstablishShareKey.buildLoginOutgoingPacket(
|
||||||
client = client,
|
client = client,
|
||||||
encryptMethod = PacketEncryptType.Empty,
|
encryptMethod = PacketEncryptType.Empty,
|
||||||
uin = uin.toString(),
|
uin = uin.toString(),
|
||||||
@ -268,7 +277,7 @@ internal fun createChannelProxy(client: QQAndroidClient): EncryptService.Channel
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
TODO("parse packet to ChannelResult")
|
return EncryptService.ChannelResult(SsoEstablishShareKey.commandName, packet.data)
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -26,11 +26,11 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.ConfigPushSvc
|
|||||||
import net.mamoe.mirai.internal.network.protocol.packet.login.Heartbeat
|
import net.mamoe.mirai.internal.network.protocol.packet.login.Heartbeat
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
|
import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
|
import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.packet.sso.SsoEstablishShareKey
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.summarycard.ChangeFriendRemark
|
import net.mamoe.mirai.internal.network.protocol.packet.summarycard.ChangeFriendRemark
|
||||||
import net.mamoe.mirai.internal.network.protocol.packet.summarycard.SummaryCard
|
import net.mamoe.mirai.internal.network.protocol.packet.summarycard.SummaryCard
|
||||||
import net.mamoe.mirai.utils.DeprecatedSinceMirai
|
import net.mamoe.mirai.utils.DeprecatedSinceMirai
|
||||||
import net.mamoe.mirai.utils.MiraiLoggerWithSwitch
|
import net.mamoe.mirai.utils.MiraiLoggerWithSwitch
|
||||||
import kotlin.jvm.JvmName
|
|
||||||
|
|
||||||
internal sealed class PacketFactory<TPacket : Packet?> {
|
internal sealed class PacketFactory<TPacket : Packet?> {
|
||||||
/**
|
/**
|
||||||
@ -132,6 +132,7 @@ internal object KnownPacketFactories {
|
|||||||
WtLogin.Login,
|
WtLogin.Login,
|
||||||
WtLogin.ExchangeEmp,
|
WtLogin.ExchangeEmp,
|
||||||
WtLogin.TransEmp,
|
WtLogin.TransEmp,
|
||||||
|
SsoEstablishShareKey,
|
||||||
StatSvc.Register,
|
StatSvc.Register,
|
||||||
StatSvc.GetOnlineStatus,
|
StatSvc.GetOnlineStatus,
|
||||||
StatSvc.SimpleGet,
|
StatSvc.SimpleGet,
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.internal.network.protocol.packet.sso
|
||||||
|
|
||||||
|
import io.ktor.utils.io.core.*
|
||||||
|
import net.mamoe.mirai.internal.QQAndroidBot
|
||||||
|
import net.mamoe.mirai.internal.network.Packet
|
||||||
|
import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacketFactory
|
||||||
|
|
||||||
|
internal object SsoEstablishShareKey : OutgoingPacketFactory<SsoEstablishShareKey.RawData>(
|
||||||
|
"trpc.o3.ecdh_access.EcdhAccess.SsoEstablishShareKey"
|
||||||
|
) {
|
||||||
|
internal class RawData(val data: ByteArray) : Packet
|
||||||
|
|
||||||
|
override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): RawData {
|
||||||
|
return RawData(readBytes())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
package net.mamoe.mirai.internal.network.protocol.packet.sso
|
||||||
|
|
||||||
|
internal const val PREFIX_TRPC_SSO = "trpc.o3"
|
@ -72,8 +72,6 @@ public interface EncryptService : BaseService {
|
|||||||
public class ChannelResult(
|
public class ChannelResult(
|
||||||
public val cmd: String,
|
public val cmd: String,
|
||||||
public val data: ByteArray,
|
public val data: ByteArray,
|
||||||
public val success: Int,
|
|
||||||
public val callbackId: Long
|
|
||||||
)
|
)
|
||||||
|
|
||||||
public interface ChannelProxy {
|
public interface ChannelProxy {
|
||||||
|
Loading…
Reference in New Issue
Block a user