Commit 9de13ca6 authored by Him188's avatar Him188

Fix ECDH

parent 0508e8d8
...@@ -14,7 +14,9 @@ import kotlinx.io.core.ByteReadPacket ...@@ -14,7 +14,9 @@ import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.buildPacket import kotlinx.io.core.buildPacket
import kotlinx.io.core.writeFully import kotlinx.io.core.writeFully
import net.mamoe.mirai.qqandroid.network.QQAndroidClient import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.cryptor.ECDH import net.mamoe.mirai.utils.cryptor.ECDH
import net.mamoe.mirai.utils.cryptor.ECDHKeyPair
import net.mamoe.mirai.utils.io.encryptAndWrite import net.mamoe.mirai.utils.io.encryptAndWrite
import net.mamoe.mirai.utils.io.writeShortLVByteArray import net.mamoe.mirai.utils.io.writeShortLVByteArray
...@@ -65,17 +67,25 @@ inline class EncryptMethodSessionKeyLoginState3(override val sessionKey: ByteArr ...@@ -65,17 +67,25 @@ inline class EncryptMethodSessionKeyLoginState3(override val sessionKey: ByteArr
override val currentLoginState: Int get() = 3 override val currentLoginState: Int get() = 3
} }
inline class EncryptMethodECDH135(override val ecdh: ECDH) : internal inline class EncryptMethodECDH135(override val ecdh: ECDH) :
EncryptMethodECDH { EncryptMethodECDH {
override val id: Int get() = 135 override val id: Int get() = 135
} }
inline class EncryptMethodECDH7(override val ecdh: ECDH) : internal inline class EncryptMethodECDH7(override val ecdh: ECDH) :
EncryptMethodECDH { EncryptMethodECDH {
override val id: Int get() = 7 override val id: Int get() = 7
} }
internal interface EncryptMethodECDH : EncryptMethod { internal interface EncryptMethodECDH : EncryptMethod {
companion object {
operator fun invoke(ecdh: ECDH): EncryptMethodECDH {
return if (ecdh.keyPair === ECDHKeyPair.DefaultStub) {
EncryptMethodECDH135(ecdh)
} else EncryptMethodECDH7(ecdh)
}
}
val ecdh: ECDH val ecdh: ECDH
/** /**
...@@ -97,13 +107,19 @@ internal interface EncryptMethodECDH : EncryptMethod { ...@@ -97,13 +107,19 @@ internal interface EncryptMethodECDH : EncryptMethod {
// writeShortLVByteArray("04 CB 36 66 98 56 1E 93 6E 80 C1 57 E0 74 CA B1 3B 0B B6 8D DE B2 82 45 48 A1 B1 8D D4 FB 61 22 AF E1 2F E4 8C 52 66 D8 D7 26 9D 76 51 A8 EB 6F E7".hexToBytes()) // writeShortLVByteArray("04 CB 36 66 98 56 1E 93 6E 80 C1 57 E0 74 CA B1 3B 0B B6 8D DE B2 82 45 48 A1 B1 8D D4 FB 61 22 AF E1 2F E4 8C 52 66 D8 D7 26 9D 76 51 A8 EB 6F E7".hexToBytes())
writeShortLVByteArray(ecdh.keyPair.publicKey.getEncoded().drop(23).take(49).toByteArray().also { if (ecdh.keyPair === ECDHKeyPair.DefaultStub) {
// it.toUHexString().debugPrint("PUBLIC KEY") writeShortLVByteArray(ECDHKeyPair.DefaultStub.defaultPublicKey)
check(it[0].toInt() == 0x04) { "Bad publicKey generated. Expected first element=0x04, got${it[0]}" } encryptAndWrite(ECDHKeyPair.DefaultStub.defaultShareKey, body)
//check(ecdh.calculateShareKeyByPeerPublicKey(it.adjustToPublicKey()).contentEquals(ecdh.keyPair.shareKey)) { "PublicKey Validation failed" } } else {
}) MiraiLogger.info("Using custom")
writeShortLVByteArray(ecdh.keyPair.publicKey.getEncoded().drop(23).take(49).toByteArray().also {
// it.toUHexString().debugPrint("PUBLIC KEY")
check(it[0].toInt() == 0x04) { "Bad publicKey generated. Expected first element=0x04, got${it[0]}" }
//check(ecdh.calculateShareKeyByPeerPublicKey(it.adjustToPublicKey()).contentEquals(ecdh.keyPair.shareKey)) { "PublicKey Validation failed" }
})
// encryptAndWrite("26 33 BA EC 86 EB 79 E6 BC E0 20 06 5E A9 56 6C".hexToBytes(), body) // encryptAndWrite("26 33 BA EC 86 EB 79 E6 BC E0 20 06 5E A9 56 6C".hexToBytes(), body)
encryptAndWrite(ecdh.keyPair.initialShareKey, body) encryptAndWrite(ecdh.keyPair.initialShareKey, body)
}
} }
} }
\ No newline at end of file
...@@ -47,7 +47,7 @@ internal class WtLogin { ...@@ -47,7 +47,7 @@ internal class WtLogin {
ticket: String ticket: String
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) { writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) { writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(2) // subCommand writeShort(2) // subCommand
writeShort(4) // count of TLVs writeShort(4) // count of TLVs
t193(ticket) t193(ticket)
...@@ -64,7 +64,7 @@ internal class WtLogin { ...@@ -64,7 +64,7 @@ internal class WtLogin {
captchaAnswer: String captchaAnswer: String
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) { writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) { writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(2) // subCommand writeShort(2) // subCommand
writeShort(4) // count of TLVs writeShort(4) // count of TLVs
t2(captchaAnswer, captchaSign, 0) t2(captchaAnswer, captchaSign, 0)
...@@ -83,7 +83,7 @@ internal class WtLogin { ...@@ -83,7 +83,7 @@ internal class WtLogin {
t402: ByteArray t402: ByteArray
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) { writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) { writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(20) // subCommand writeShort(20) // subCommand
writeShort(4) // count of TLVs, probably ignored by server? writeShort(4) // count of TLVs, probably ignored by server?
t8(2052) t8(2052)
...@@ -103,7 +103,7 @@ internal class WtLogin { ...@@ -103,7 +103,7 @@ internal class WtLogin {
client: QQAndroidClient client: QQAndroidClient
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId, unknownHex = "01 00 00 00 00 00 00 00 00 00 01 00") { writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId, unknownHex = "01 00 00 00 00 00 00 00 00 00 01 00") {
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) { writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(8) // subCommand writeShort(8) // subCommand
writeShort(6) // count of TLVs, probably ignored by server?TODO writeShort(6) // count of TLVs, probably ignored by server?TODO
t8(2052) t8(2052)
...@@ -131,7 +131,7 @@ internal class WtLogin { ...@@ -131,7 +131,7 @@ internal class WtLogin {
client: QQAndroidClient client: QQAndroidClient
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId -> ): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) { writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) { writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
writeShort(9) // subCommand writeShort(9) // subCommand
writeShort(17) // count of TLVs, probably ignored by server? writeShort(17) // count of TLVs, probably ignored by server?
//writeShort(LoginType.PASSWORD.value.toShort()) //writeShort(LoginType.PASSWORD.value.toShort())
......
...@@ -19,13 +19,13 @@ import javax.crypto.KeyAgreement ...@@ -19,13 +19,13 @@ import javax.crypto.KeyAgreement
actual typealias ECDHPrivateKey = PrivateKey actual typealias ECDHPrivateKey = PrivateKey
actual typealias ECDHPublicKey = PublicKey actual typealias ECDHPublicKey = PublicKey
actual class ECDHKeyPair( internal actual class ECDHKeyPairImpl(
private val delegate: KeyPair private val delegate: KeyPair
) { ) : ECDHKeyPair {
actual val privateKey: ECDHPrivateKey get() = delegate.private override val privateKey: ECDHPrivateKey get() = delegate.private
actual val publicKey: ECDHPublicKey get() = delegate.public override val publicKey: ECDHPublicKey get() = delegate.public
actual val initialShareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey) override val initialShareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey)
} }
@Suppress("FunctionName") @Suppress("FunctionName")
...@@ -33,6 +33,10 @@ actual fun ECDH() = ECDH(ECDH.generateKeyPair()) ...@@ -33,6 +33,10 @@ actual fun ECDH() = ECDH(ECDH.generateKeyPair())
actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
actual companion object { actual companion object {
@Suppress("ObjectPropertyName")
private var _isECDHAvailable: Boolean = false // because `runCatching` has no contract.
actual val isECDHAvailable: Boolean get() = _isECDHAvailable
init { init {
kotlin.runCatching { kotlin.runCatching {
@SuppressLint("PrivateApi") @SuppressLint("PrivateApi")
...@@ -48,13 +52,19 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { ...@@ -48,13 +52,19 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
Security.removeProvider(providerName) Security.removeProvider(providerName)
} }
Security.addProvider(clazz.newInstance() as Provider) Security.addProvider(clazz.newInstance() as Provider)
generateKeyPair()
_isECDHAvailable = true
}.exceptionOrNull()?.let { }.exceptionOrNull()?.let {
throw IllegalStateException("cannot init BouncyCastle", it) throw IllegalStateException("cannot init BouncyCastle", it)
} }
_isECDHAvailable = false
} }
actual fun generateKeyPair(): ECDHKeyPair { actual fun generateKeyPair(): ECDHKeyPair {
return ECDHKeyPair(KeyPairGenerator.getInstance("ECDH").genKeyPair()) if (!isECDHAvailable) {
return ECDHKeyPair.DefaultStub
}
return ECDHKeyPairImpl(KeyPairGenerator.getInstance("ECDH").genKeyPair())
} }
actual fun calculateShareKey( actual fun calculateShareKey(
......
...@@ -19,7 +19,9 @@ expect interface ECDHPublicKey { ...@@ -19,7 +19,9 @@ expect interface ECDHPublicKey {
fun getEncoded(): ByteArray fun getEncoded(): ByteArray
} }
expect class ECDHKeyPair { internal expect class ECDHKeyPairImpl : ECDHKeyPair
interface ECDHKeyPair {
val privateKey: ECDHPrivateKey val privateKey: ECDHPrivateKey
val publicKey: ECDHPublicKey val publicKey: ECDHPublicKey
...@@ -27,6 +29,15 @@ expect class ECDHKeyPair { ...@@ -27,6 +29,15 @@ expect class ECDHKeyPair {
* 私匙和固定公匙([initialPublicKey]) 计算得到的 shareKey * 私匙和固定公匙([initialPublicKey]) 计算得到的 shareKey
*/ */
val initialShareKey: ByteArray val initialShareKey: ByteArray
object DefaultStub : ECDHKeyPair {
val defaultPublicKey = "020b03cf3d99541f29ffec281bebbd4ea211292ac1f53d7128".chunkedHexToBytes()
val defaultShareKey = "4da0f614fc9f29c2054c77048a6566d7".chunkedHexToBytes()
override val privateKey: Nothing get() = error("stub!")
override val publicKey: Nothing get() = error("stub!")
override val initialShareKey: ByteArray get() = defaultShareKey
}
} }
/** /**
...@@ -41,6 +52,8 @@ expect class ECDH(keyPair: ECDHKeyPair) { ...@@ -41,6 +52,8 @@ expect class ECDH(keyPair: ECDHKeyPair) {
fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray
companion object { companion object {
val isECDHAvailable: Boolean
/** /**
* 由完整的 publicKey ByteArray 得到 [ECDHPublicKey] * 由完整的 publicKey ByteArray 得到 [ECDHPublicKey]
*/ */
...@@ -60,9 +73,6 @@ expect class ECDH(keyPair: ECDHKeyPair) { ...@@ -60,9 +73,6 @@ expect class ECDH(keyPair: ECDHKeyPair) {
override fun toString(): String override fun toString(): String
} }
/**
*
*/
@Suppress("FunctionName") @Suppress("FunctionName")
expect fun ECDH(): ECDH expect fun ECDH(): ECDH
......
...@@ -9,11 +9,9 @@ ...@@ -9,11 +9,9 @@
package net.mamoe.mirai.utils.cryptor package net.mamoe.mirai.utils.cryptor
import net.mamoe.mirai.utils.io.chunkedHexToBytes
import net.mamoe.mirai.utils.md5 import net.mamoe.mirai.utils.md5
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import java.security.* import java.security.*
import java.security.spec.ECGenParameterSpec
import java.security.spec.X509EncodedKeySpec import java.security.spec.X509EncodedKeySpec
import javax.crypto.KeyAgreement import javax.crypto.KeyAgreement
...@@ -21,20 +19,13 @@ import javax.crypto.KeyAgreement ...@@ -21,20 +19,13 @@ import javax.crypto.KeyAgreement
actual typealias ECDHPrivateKey = PrivateKey actual typealias ECDHPrivateKey = PrivateKey
actual typealias ECDHPublicKey = PublicKey actual typealias ECDHPublicKey = PublicKey
actual class ECDHKeyPair( internal actual class ECDHKeyPairImpl(
private val delegate: KeyPair? private val delegate: KeyPair
) { ) : ECDHKeyPair {
actual val privateKey: ECDHPrivateKey get() = delegate?.private ?: error("ECDH is not available") override val privateKey: ECDHPrivateKey get() = delegate.private
actual val publicKey: ECDHPublicKey get() = delegate?.public ?: defaultPublicKey override val publicKey: ECDHPublicKey get() = delegate.public
actual val initialShareKey: ByteArray = if (delegate == null) { override val initialShareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey)
defaultShareKey
} else ECDH.calculateShareKey(privateKey, initialPublicKey)
companion object {
internal val defaultPublicKey = "020b03cf3d99541f29ffec281bebbd4ea211292ac1f53d7128".chunkedHexToBytes().adjustToPublicKey()
internal val defaultShareKey = "4da0f614fc9f29c2054c77048a6566d7".chunkedHexToBytes()
}
} }
@Suppress("FunctionName") @Suppress("FunctionName")
...@@ -42,33 +33,35 @@ actual fun ECDH() = ECDH(ECDH.generateKeyPair()) ...@@ -42,33 +33,35 @@ actual fun ECDH() = ECDH(ECDH.generateKeyPair())
actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
actual companion object { actual companion object {
private var isECDHAvailable = true @Suppress("ObjectPropertyName")
private val _isECDHAvailable: Boolean
actual val isECDHAvailable: Boolean get() = _isECDHAvailable
init { init {
isECDHAvailable = kotlin.runCatching { _isECDHAvailable = kotlin.runCatching {
if (Security.getProvider("BouncyCastle") != null) {
Security.removeProvider("BouncyCastle")
}
Security.addProvider(BouncyCastleProvider()) Security.addProvider(BouncyCastleProvider())
generateKeyPair() // try if it is working generateKeyPair() // try if it is working
}.isSuccess }.isSuccess
} }
actual fun generateKeyPair(): ECDHKeyPair { actual fun generateKeyPair(): ECDHKeyPair {
return if (!isECDHAvailable) { if (!isECDHAvailable) {
ECDHKeyPair(null) return ECDHKeyPair.DefaultStub
} else ECDHKeyPair(KeyPairGenerator.getInstance("EC", "BC").apply { initialize(ECGenParameterSpec("secp192k1")) }.genKeyPair()) }
return ECDHKeyPairImpl(KeyPairGenerator.getInstance("ECDH").genKeyPair())
} }
actual fun calculateShareKey( actual fun calculateShareKey(
privateKey: ECDHPrivateKey, privateKey: ECDHPrivateKey,
publicKey: ECDHPublicKey publicKey: ECDHPublicKey
): ByteArray { ): ByteArray {
return if (!isECDHAvailable) { val instance = KeyAgreement.getInstance("ECDH", "BC")
ECDHKeyPair.defaultShareKey instance.init(privateKey)
} else { instance.doPhase(publicKey, true)
val instance = KeyAgreement.getInstance("ECDH", "BC") return md5(instance.generateSecret())
instance.init(privateKey)
instance.doPhase(publicKey, true)
md5(instance.generateSecret())
}
} }
actual fun constructPublicKey(key: ByteArray): ECDHPublicKey { actual fun constructPublicKey(key: ByteArray): ECDHPublicKey {
...@@ -77,7 +70,6 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) { ...@@ -77,7 +70,6 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
} }
actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray { actual fun calculateShareKeyByPeerPublicKey(peerPublicKey: ECDHPublicKey): ByteArray {
if (!isECDHAvailable) return keyPair.initialShareKey
return calculateShareKey(keyPair.privateKey, peerPublicKey) return calculateShareKey(keyPair.privateKey, peerPublicKey)
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment