mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-09 01:30:17 +08:00
Fix ECDH
This commit is contained in:
parent
0508e8d894
commit
9de13ca6fd
@ -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
|
|||||||
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 {
|
|||||||
|
|
||||||
// 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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
|
|||||||
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())
|
|||||||
|
|
||||||
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) {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
* 私匙和固定公匙([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) {
|
|||||||
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) {
|
|||||||
override fun toString(): String
|
override fun toString(): String
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
expect fun ECDH(): ECDH
|
expect fun ECDH(): ECDH
|
||||||
|
|
||||||
|
@ -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
|
|||||||
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())
|
|||||||
|
|
||||||
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) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user