diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt index 978af6ccd..bdfcda989 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt @@ -29,7 +29,6 @@ import net.mamoe.mirai.qqandroid.utils.toRichTextElems import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.cryptor.contentToString import net.mamoe.mirai.utils.currentTimeSeconds -import net.mamoe.mirai.utils.io.hexToBytes import kotlin.math.absoluteValue import kotlin.random.Random @@ -228,11 +227,15 @@ internal class MessageSvc { elems = message.toRichTextElems() ) ), + + //.apply { add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags( + // pbReserve = "78 00 F8 01 00 C8 02 00".hexToBytes() + // ))) } msgSeq = seq, - msgRand = Random.nextInt().absoluteValue, - syncCookie = "08 A0 C2 C4 F1 05 10 A0 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 E4 C2 B1 95 03 48 A1 9F E0 C7 08 58 D3 C2 8F A0 09 60 1D 68 A0 C2 C4 F1 05 70 00".hexToBytes() - ?: SyncCookie(time = currentTimeSeconds + client.timeDifference).toByteArray(SyncCookie.serializer()), - msgVia = 0 + msgRand = Random.nextInt().absoluteValue//, + // syncCookie = ByteArray(0) + // ?: SyncCookie(time = currentTimeSeconds + client.timeDifference).toByteArray(SyncCookie.serializer()), + // msgVia = 1 ) ) } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/MessageQQA.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/MessageQQA.kt index f6d5ff9fc..80e969fd7 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/MessageQQA.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/MessageQQA.kt @@ -19,7 +19,64 @@ internal fun NotOnlineImageFromFile.toJceData(): ImMsgBody.NotOnlineImage { ) } +internal fun CustomFaceFromFile.toJceData(): ImMsgBody.CustomFace { + return ImMsgBody.CustomFace( + filePath = this.filepath, + fileId = this.fileId, + serverIp = this.serverIp, + serverPort = this.serverPort, + fileType = this.fileType, + signature = this.signature, + useful = this.useful, + md5 = this.md5, + bizType = this.bizType, + imageType = this.imageType, + width = this.width, + height = this.height, + source = this.source, + size = this.size, + pbReserve = this.pbReserve + ) +} + /* +customFace=CustomFace#2050019814 { + guid=<Empty ByteArray> + filePath=5F6C522DEAC4F36C0ED8EF362660EFD6.png + shortcut= + buffer=<Empty ByteArray> + flag=<Empty ByteArray> + oldData=<Empty ByteArray> + fileId=0xB40AF10E(-1274351346) + serverIp=0xB703E13A(-1224482502) + serverPort=0x00000050(80) + fileType=0x00000042(66) + signature=6B 44 61 76 72 79 68 79 57 67 70 52 41 45 78 49 + useful=0x00000001(1) + md5=5F 6C 52 2D EA C4 F3 6C 0E D8 EF 36 26 60 EF D6 + thumbUrl= + bigUrl= + origUrl= + bizType=0x00000005(5) + repeatIndex=0x00000000(0) + repeatImage=0x00000000(0) + imageType=0x000003E9(1001) + index=0x00000000(0) + width=0x0000005F(95) + height=0x00000054(84) + source=0x00000067(103) + size=0x000006E2(1762) + origin=0x00000000(0) + thumbWidth=0x00000000(0) + thumbHeight=0x00000000(0) + showLen=0x00000000(0) + downloadLen=0x00000000(0) + _400Url= + _400Width=0x00000000(0) + _400Height=0x00000000(0) + pbReserve=08 01 10 00 32 00 4A 0E 5B E5 8A A8 E7 94 BB E8 A1 A8 E6 83 85 5D 50 00 78 05 +} + notOnlineImage=NotOnlineImage#2050019814 { filePath=41AEF2D4B5BD24CF3791EFC5FEB67D60.jpg fileLen=0x00000350(848) @@ -57,26 +114,39 @@ internal fun MessageChain.toRichTextElems(): MutableList<ImMsgBody.Elem> { this.forEach { when (it) { - is PlainText -> { - elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue))) - } + is PlainText -> elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue))) is At -> { } - is NotOnlineImageFromServer -> { - elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate)) - // elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00".hexToBytes()))) - } - is NotOnlineImageFromFile -> { - elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData())) - // elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00".hexToBytes()))) - } + is CustomFaceFromFile -> elements.add(ImMsgBody.Elem(customFace = it.toJceData())) + is CustomFaceFromServer -> elements.add(ImMsgBody.Elem(customFace = it.delegate)) + is NotOnlineImageFromServer -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate)) + is NotOnlineImageFromFile -> elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData())) } } return elements } +internal class CustomFaceFromServer( + internal val delegate: ImMsgBody.CustomFace +) : CustomFace() { + override val filepath: String get() = delegate.filePath + override val fileId: Int get() = delegate.fileId + override val serverIp: Int get() = delegate.serverIp + override val serverPort: Int get() = delegate.serverPort + override val fileType: Int get() = delegate.fileType + override val signature: ByteArray get() = delegate.signature + override val useful: Int get() = delegate.useful + override val md5: ByteArray get() = delegate.md5 + override val bizType: Int get() = delegate.bizType + override val imageType: Int get() = delegate.imageType + override val width: Int get() = delegate.width + override val height: Int get() = delegate.height + override val source: Int get() = delegate.source + override val size: Int get() = delegate.size + override val pbReserve: ByteArray get() = delegate.pbReserve +} internal class NotOnlineImageFromServer( internal val delegate: ImMsgBody.NotOnlineImage @@ -107,22 +177,8 @@ internal fun ImMsgBody.RichText.toMessageChain(): MessageChain { elems.forEach { when { - it.notOnlineImage != null -> message.add( - NotOnlineImageFromServer(it.notOnlineImage) - ) - it.customFace != null -> message.add( - NotOnlineImageFromFile( - it.customFace.filePath, - it.customFace.md5, - it.customFace.origUrl, - it.customFace.downloadLen, - it.customFace.height, - it.customFace.width, - it.customFace.bizType, - it.customFace.imageType, - it.customFace.filePath - ) - ) + it.notOnlineImage != null -> message.add(NotOnlineImageFromServer(it.notOnlineImage)) + it.customFace != null -> message.add(CustomFaceFromServer(it.customFace)) it.text != null -> message.add(it.text.str.toMessage()) } } diff --git a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt index 0159bb6a7..f6616b841 100644 --- a/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt +++ b/mirai-core-qqandroid/src/jvmTest/kotlin/androidPacketTests/clientToServer.kt @@ -68,10 +68,12 @@ internal val t163 = "2C 7A 7B 23 4E 24 3F 24 24 47 62 6B 69 2E 47 50".hexToBytes var ecdhPrivateKeyS = "97a52992cb7a2110413629af94a3c249c68a3b731510caa8" internal val shareKeyCalculatedByConstPubKey - get() = ECDH.calculateShareKey( - loadPrivateKey(ecdhPrivateKeyS), - initialPublicKey - ) + by lazy { + ECDH.calculateShareKey( + loadPrivateKey(ecdhPrivateKeyS), + initialPublicKey + ) + } var passwordMd5: ByteArray = byteArrayOf() var uin: Long = 0L @@ -137,132 +139,141 @@ fun ByteReadPacket.analysisOneFullPacket(): ByteReadPacket = debugIfFail("Failed println("uin=" + readString(readInt() - 4)) println("// 解密 body") - readRemainingBytes().tryDecrypt().toReadPacket().debugPrintThis("outer body decrypted").apply { - when (flag1) { - 0x0A -> decodeSso() - 0x0B -> decodeUni() - else -> error("unknown flag1: $flag1") - } + val encrypted = readRemainingBytes() - when (flag2) { + val decrypted = encrypted.tryDecryptOrNull() + if (decrypted == null) { + println("cannot decrypt: ${encrypted.toUHexString()}") + error("cannot decrypt: ${encrypted.toUHexString()}") + } else { + decrypted.toReadPacket().debugPrintThis("outer body decrypted").apply { + when (flag1) { + 0x0A -> decodeSso() + 0x0B -> decodeUni() + else -> error("unknown flag1: $flag1") + } - 2 -> { + when (flag2) { - this.debugPrintThis("Oicq Request").apply { - /* - byte 2 // head flag - short 27 + 2 + remaining.length - ushort client.protocolVersion // const 8001 - ushort 0x0001 // const0 - uint client.uin - byte 3 // const1 - ubyte encryptMethod.value // [EncryptMethod] - byte 0 // const2 - int 2 // const3 - int client.appClientVersion - int 0 // const4 - */ - discardExact(3) - readShort().toInt().takeIf { it != 8001 }?.let { - println("这个包不是 oicqRequest") - return@debugIfFail this - println(" got new protocolVersion=$it") - } - val commandId = readUShort().toInt() - println(" commandId=0x${commandId.toShort().toUHexString()}") - readUShort().toInt().takeIf { it != 1 }?.let { - println(" got new const0=$it") - } - println(" uin=${readUInt()}") - readByte().toInt().takeIf { it != 3 }?.let { - println(" got new const1=$it") - } - val encryptionMethod = readUByte().toInt() - readByte().toInt().takeIf { it != 0 }?.let { - println(" got new const2=$it") - } - readInt().takeIf { it != 2 }?.let { - println(" got new const3=$it") - } - readInt().takeIf { it != 0 }?.let { - println(" got new appClientVersion=$it") - } - readInt().takeIf { it != 0 }?.let { - println(" got new const4=$it") - } + 2 -> { - - discardExact(1) - discardExact(1) - val randomKey = readBytes(16) - println("randomKey= ${randomKey.toUHexString()}") - readUShort().toInt().takeIf { it != 258 }?.let { - println(" got new const in ECDH head(originally=258)=$it") - } - val publicKey = readBytes(readShort().toInt()) - println("ecdh publicKey=" + publicKey.toUHexString()) - - - val encrypt = when (encryptionMethod) { - 135, 7 -> { - ECDH.calculateShareKey( - loadPrivateKey(ecdhPrivateKeyS), - //"04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7".chunkedHexToBytes().adjustToPublicKey() // QQ: 04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7 - ECDH.constructPublicKey("30 46 30 10 06 07 2A 86 48 CE 3D 02 01 06 05 2B 81 04 00 1F 03 32 00".hexToBytes() + publicKey) - ) + this.debugPrintThis("Oicq Request").apply { + /* + byte 2 // head flag + short 27 + 2 + remaining.length + ushort client.protocolVersion // const 8001 + ushort 0x0001 // const0 + uint client.uin + byte 3 // const1 + ubyte encryptMethod.value // [EncryptMethod] + byte 0 // const2 + int 2 // const3 + int client.appClientVersion + int 0 // const4 + */ + discardExact(3) + readShort().toInt().takeIf { it != 8001 }?.let { + println("这个包不是 oicqRequest") + return@debugIfFail this + println(" got new protocolVersion=$it") + } + val commandId = readUShort().toInt() + println(" commandId=0x${commandId.toShort().toUHexString()}") + readUShort().toInt().takeIf { it != 1 }?.let { + println(" got new const0=$it") + } + println(" uin=${readUInt()}") + readByte().toInt().takeIf { it != 3 }?.let { + println(" got new const1=$it") + } + val encryptionMethod = readUByte().toInt() + readByte().toInt().takeIf { it != 0 }?.let { + println(" got new const2=$it") + } + readInt().takeIf { it != 2 }?.let { + println(" got new const3=$it") + } + readInt().takeIf { it != 0 }?.let { + println(" got new appClientVersion=$it") + } + readInt().takeIf { it != 0 }?.let { + println(" got new const4=$it") } - 69 -> { - error("encryptionMethod 69") + + discardExact(1) + discardExact(1) + val randomKey = readBytes(16) + println("randomKey= ${randomKey.toUHexString()}") + readUShort().toInt().takeIf { it != 258 }?.let { + println(" got new const in ECDH head(originally=258)=$it") } - else -> error("unknown encryptionMethod=$encryptionMethod") - } + val publicKey = readBytes(readShort().toInt()) + println("ecdh publicKey=" + publicKey.toUHexString()) - val encryptedBody = readBytes((remaining - 1).toInt()) - val decrypted = kotlin.runCatching { - encryptedBody.decryptBy(encrypt).also { println("first by calculatedShareKey or sessionKey(method=7)") } - }.getOrElse { - encryptedBody.decryptBy(shareKeyCalculatedByConstPubKey).also { println("first by shareKeyCalculatedByConstPubKey") } - }.let { firstDecrypted -> - runCatching { - firstDecrypted.decryptBy(encrypt).also { println("second by calculatedShareKey") } + val encrypt = when (encryptionMethod) { + 135, 7 -> { + ECDH.calculateShareKey( + loadPrivateKey(ecdhPrivateKeyS), + //"04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7".chunkedHexToBytes().adjustToPublicKey() // QQ: 04cb366698561e936e80c157e074cab13b0bb68ddeb2824548a1b18dd4fb6122afe12fe48c5266d8d7269d7651a8eb6fe7 + ECDH.constructPublicKey("30 46 30 10 06 07 2A 86 48 CE 3D 02 01 06 05 2B 81 04 00 1F 03 32 00".hexToBytes() + publicKey) + ) + } + + 69 -> { + error("encryptionMethod 69") + } + else -> error("unknown encryptionMethod=$encryptionMethod") + } + + val encryptedBody = readBytes((remaining - 1).toInt()) + + val decrypted = kotlin.runCatching { + encryptedBody.decryptBy(encrypt).also { println("first by calculatedShareKey or sessionKey(method=7)") } }.getOrElse { - kotlin.runCatching { - firstDecrypted.decryptBy(shareKeyCalculatedByConstPubKey) - }.getOrDefault(firstDecrypted) - } - } - - PacketLogger.info("Real body=" + decrypted.toUHexString()) - decrypted.toReadPacket().apply { - if (commandId == 0x0810) { - DebugLogger.info("发送 login!! 正在获取 tgtgtKey") - try { - discardExact(4) - val tlvMap = readTLVMap() - tlvMap.printTLVMap() - tlvMap[0x106] - ?.also { DebugLogger.info("找到了 0x106") } - ?.decryptBy(md5(passwordMd5 + ByteArray(4) + uin.toInt().toByteArray())) - ?.read { - discardExact(2 + 4 * 4 + 8 + 4 + 4 + 1 + 16) - tgtgtKey = readBytes(16) - DebugLogger.info("获取 tgtgtKey=${tgtgtKey.toUHexString()}") - } ?: DebugLogger.info("找不到 0x106") - } catch (e: Exception) { - e.printStackTrace() + encryptedBody.decryptBy(shareKeyCalculatedByConstPubKey).also { println("first by shareKeyCalculatedByConstPubKey") } + }.let { firstDecrypted -> + runCatching { + firstDecrypted.decryptBy(encrypt).also { println("second by calculatedShareKey") } + }.getOrElse { + kotlin.runCatching { + firstDecrypted.decryptBy(shareKeyCalculatedByConstPubKey) + }.getOrDefault(firstDecrypted) } } + PacketLogger.info("Real body=" + decrypted.toUHexString()) + decrypted.toReadPacket().apply { + if (commandId == 0x0810) { + DebugLogger.info("发送 login!! 正在获取 tgtgtKey") + try { + discardExact(4) + val tlvMap = readTLVMap() + tlvMap.printTLVMap() + tlvMap[0x106] + ?.also { DebugLogger.info("找到了 0x106") } + ?.decryptBy(md5(passwordMd5 + ByteArray(4) + uin.toInt().toByteArray())) + ?.read { + discardExact(2 + 4 * 4 + 8 + 4 + 4 + 1 + 16) + tgtgtKey = readBytes(16) + DebugLogger.info("获取 tgtgtKey=${tgtgtKey.toUHexString()}") + } ?: DebugLogger.info("找不到 0x106") + } catch (e: Exception) { + e.printStackTrace() + } + } + + } } } - } - else -> { - this.debugPrintThis("uni packet") + else -> { + this.debugPrintThis("uni packet") + } } } } + } fun ByteReadPacket.decodeUni() { @@ -354,8 +365,8 @@ val keys: Map<String, ByteArray> "shareKeyCalculatedByConstPubKey" to shareKeyCalculatedByConstPubKey, "t108" to t108, "t10c" to t10c, - "t163" to t163 -) + "t163" to t163 + ) fun ByteArray.tryDecrypt(): ByteArray { return this.tryDecryptOrNull() ?: error("Cannot decrypt. Encrypted data=" + this.toUHexString()) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt index b3a9a9be6..a5c0f436f 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt @@ -2,19 +2,110 @@ package net.mamoe.mirai.message.data +import kotlinx.serialization.Serializable + sealed class Image : Message { - abstract val resourceId: String + abstract val md5: ByteArray abstract override fun toString(): String - final companion object Key : Message.Key<Image> + companion object Key : Message.Key<Image> abstract override fun eq(other: Message): Boolean } +abstract class CustomFace : Image() { + abstract val filepath: String + abstract val fileId: Int + abstract val serverIp: Int + abstract val serverPort: Int + abstract val fileType: Int + abstract val signature: ByteArray + abstract val useful: Int + abstract override val md5: ByteArray + abstract val bizType: Int + abstract val imageType: Int + abstract val width: Int + abstract val height: Int + abstract val source: Int + abstract val size:Int + abstract val pbReserve: ByteArray + + override fun toString(): String { + return "[CustomFace]" + } + + override fun eq(other: Message): Boolean { + return this.toString() == other.toString() + } +} + +@Serializable +data class CustomFaceFromFile( + override val filepath: String, + override val fileId: Int, + override val serverIp: Int, + override val serverPort: Int, + override val fileType: Int, + override val signature: ByteArray, + override val useful: Int, + override val md5: ByteArray, + override val bizType: Int, + override val imageType: Int, + override val width: Int, + override val height: Int, + override val source: Int, + override val size: Int, + override val pbReserve: ByteArray +) : CustomFace() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as CustomFaceFromFile + + if (filepath != other.filepath) return false + if (fileId != other.fileId) return false + if (serverIp != other.serverIp) return false + if (serverPort != other.serverPort) return false + if (fileType != other.fileType) return false + if (!signature.contentEquals(other.signature)) return false + if (useful != other.useful) return false + if (!md5.contentEquals(other.md5)) return false + if (bizType != other.bizType) return false + if (imageType != other.imageType) return false + if (width != other.width) return false + if (height != other.height) return false + if (source != other.source) return false + if (size != other.size) return false + if (!pbReserve.contentEquals(other.pbReserve)) return false + + return true + } + + override fun hashCode(): Int { + var result = filepath.hashCode() + result = 31 * result + fileId + result = 31 * result + serverIp + result = 31 * result + serverPort + result = 31 * result + fileType + result = 31 * result + signature.contentHashCode() + result = 31 * result + useful + result = 31 * result + md5.contentHashCode() + result = 31 * result + bizType + result = 31 * result + imageType + result = 31 * result + width + result = 31 * result + height + result = 31 * result + source + result = 31 * result + size + result = 31 * result + pbReserve.contentHashCode() + return result + } +} + abstract class NotOnlineImage : Image() { - abstract override val resourceId: String - abstract val md5: ByteArray + abstract val resourceId: String + abstract override val md5: ByteArray abstract val filepath: String abstract val fileLength: Int abstract val height: Int @@ -24,7 +115,7 @@ abstract class NotOnlineImage : Image() { open val downloadPath: String get() = resourceId override fun toString(): String { - return "[$resourceId]" + return "[NotOnlineImage $resourceId]" } override fun eq(other: Message): Boolean { @@ -32,7 +123,7 @@ abstract class NotOnlineImage : Image() { } } -open class NotOnlineImageFromFile( +data class NotOnlineImageFromFile( override val resourceId: String, override val md5: ByteArray, override val filepath: String, @@ -42,4 +133,36 @@ open class NotOnlineImageFromFile( override val bizType: Int = 0, override val imageType: Int = 1000, override val downloadPath: String = resourceId -) : NotOnlineImage() \ No newline at end of file +) : NotOnlineImage() { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as NotOnlineImageFromFile + + if (resourceId != other.resourceId) return false + if (!md5.contentEquals(other.md5)) return false + if (filepath != other.filepath) return false + if (fileLength != other.fileLength) return false + if (height != other.height) return false + if (width != other.width) return false + if (bizType != other.bizType) return false + if (imageType != other.imageType) return false + if (downloadPath != other.downloadPath) return false + + return true + } + + override fun hashCode(): Int { + var result = resourceId.hashCode() + result = 31 * result + md5.contentHashCode() + result = 31 * result + filepath.hashCode() + result = 31 * result + fileLength + result = 31 * result + height + result = 31 * result + width + result = 31 * result + bizType + result = 31 * result + imageType + result = 31 * result + downloadPath.hashCode() + return result + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/ECDH.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/ECDH.kt index e93a21355..6e49c700b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/ECDH.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/cryptor/ECDH.kt @@ -1,7 +1,6 @@ package net.mamoe.mirai.utils.cryptor import net.mamoe.mirai.utils.io.chunkedHexToBytes -import net.mamoe.mirai.utils.io.toUHexString expect interface ECDHPrivateKey { fun getEncoded(): ByteArray @@ -65,8 +64,11 @@ private val commonHeadForNot02 = "3046301006072A8648CE3D020106052B8104001F033200 private const val constantHead = "3046301006072A8648CE3D020106052B8104001F03320004" private val byteArray_04 = byteArrayOf(0x04) -fun ByteArray.adjustToPublicKey(): ECDHPublicKey { - val head = if(this.size<30) "302E301006072A8648CE3D020106052B8104001F031A00" else "3046301006072A8648CE3D020106052B8104001F03320004" - return ECDH.constructPublicKey((head + this.toUHexString("")).chunkedHexToBytes()) +private val head1 = "302E301006072A8648CE3D020106052B8104001F031A00".chunkedHexToBytes() +private val head2 = "3046301006072A8648CE3D020106052B8104001F03320004".chunkedHexToBytes() +fun ByteArray.adjustToPublicKey(): ECDHPublicKey { + val head = if (this.size < 30) head1 else head2 + + return ECDH.constructPublicKey(head + this) } \ No newline at end of file diff --git a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt index cca3e8f43..422ae5956 100644 --- a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt +++ b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt @@ -23,6 +23,7 @@ import net.mamoe.mirai.message.data.buildXMLMessage import net.mamoe.mirai.message.data.getValue import net.mamoe.mirai.message.sendAsImageTo import net.mamoe.mirai.utils.ContextImpl +import net.mamoe.mirai.utils.io.toUHexString import java.io.File import java.util.* import javax.swing.filechooser.FileSystemView @@ -130,7 +131,7 @@ suspend fun main() { try { image.downloadTo(newTestTempFile(suffix = ".png").also { reply("Temp file: ${it.absolutePath}") }) - reply(image.resourceId + " downloaded") + reply(image.md5.toUHexString() + " downloaded") } catch (e: Exception) { e.printStackTrace() reply(e.message ?: e::class.java.simpleName)