mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-08 09:10:11 +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.writeFully
|
||||
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.ECDHKeyPair
|
||||
import net.mamoe.mirai.utils.io.encryptAndWrite
|
||||
import net.mamoe.mirai.utils.io.writeShortLVByteArray
|
||||
|
||||
@ -65,17 +67,25 @@ inline class EncryptMethodSessionKeyLoginState3(override val sessionKey: ByteArr
|
||||
override val currentLoginState: Int get() = 3
|
||||
}
|
||||
|
||||
inline class EncryptMethodECDH135(override val ecdh: ECDH) :
|
||||
internal inline class EncryptMethodECDH135(override val ecdh: ECDH) :
|
||||
EncryptMethodECDH {
|
||||
override val id: Int get() = 135
|
||||
}
|
||||
|
||||
inline class EncryptMethodECDH7(override val ecdh: ECDH) :
|
||||
internal inline class EncryptMethodECDH7(override val ecdh: ECDH) :
|
||||
EncryptMethodECDH {
|
||||
override val id: Int get() = 7
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
/**
|
||||
@ -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(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" }
|
||||
})
|
||||
if (ecdh.keyPair === ECDHKeyPair.DefaultStub) {
|
||||
writeShortLVByteArray(ECDHKeyPair.DefaultStub.defaultPublicKey)
|
||||
encryptAndWrite(ECDHKeyPair.DefaultStub.defaultShareKey, body)
|
||||
} 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(ecdh.keyPair.initialShareKey, 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ internal class WtLogin {
|
||||
ticket: String
|
||||
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
|
||||
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
|
||||
writeShort(2) // subCommand
|
||||
writeShort(4) // count of TLVs
|
||||
t193(ticket)
|
||||
@ -64,7 +64,7 @@ internal class WtLogin {
|
||||
captchaAnswer: String
|
||||
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
|
||||
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
|
||||
writeShort(2) // subCommand
|
||||
writeShort(4) // count of TLVs
|
||||
t2(captchaAnswer, captchaSign, 0)
|
||||
@ -83,7 +83,7 @@ internal class WtLogin {
|
||||
t402: ByteArray
|
||||
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
|
||||
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
|
||||
writeShort(20) // subCommand
|
||||
writeShort(4) // count of TLVs, probably ignored by server?
|
||||
t8(2052)
|
||||
@ -103,7 +103,7 @@ internal class WtLogin {
|
||||
client: QQAndroidClient
|
||||
): 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") {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
|
||||
writeShort(8) // subCommand
|
||||
writeShort(6) // count of TLVs, probably ignored by server?TODO
|
||||
t8(2052)
|
||||
@ -131,7 +131,7 @@ internal class WtLogin {
|
||||
client: QQAndroidClient
|
||||
): OutgoingPacket = buildLoginOutgoingPacket(client, bodyType = 2) { sequenceId ->
|
||||
writeSsoPacket(client, subAppId, commandName, sequenceId = sequenceId) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH7(client.ecdh), 0x0810) {
|
||||
writeOicqRequestPacket(client, EncryptMethodECDH(client.ecdh), 0x0810) {
|
||||
writeShort(9) // subCommand
|
||||
writeShort(17) // count of TLVs, probably ignored by server?
|
||||
//writeShort(LoginType.PASSWORD.value.toShort())
|
||||
|
@ -19,13 +19,13 @@ import javax.crypto.KeyAgreement
|
||||
actual typealias ECDHPrivateKey = PrivateKey
|
||||
actual typealias ECDHPublicKey = PublicKey
|
||||
|
||||
actual class ECDHKeyPair(
|
||||
internal actual class ECDHKeyPairImpl(
|
||||
private val delegate: KeyPair
|
||||
) {
|
||||
actual val privateKey: ECDHPrivateKey get() = delegate.private
|
||||
actual val publicKey: ECDHPublicKey get() = delegate.public
|
||||
) : ECDHKeyPair {
|
||||
override val privateKey: ECDHPrivateKey get() = delegate.private
|
||||
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")
|
||||
@ -33,6 +33,10 @@ actual fun ECDH() = ECDH(ECDH.generateKeyPair())
|
||||
|
||||
actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
|
||||
actual companion object {
|
||||
@Suppress("ObjectPropertyName")
|
||||
private var _isECDHAvailable: Boolean = false // because `runCatching` has no contract.
|
||||
actual val isECDHAvailable: Boolean get() = _isECDHAvailable
|
||||
|
||||
init {
|
||||
kotlin.runCatching {
|
||||
@SuppressLint("PrivateApi")
|
||||
@ -48,13 +52,19 @@ actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
|
||||
Security.removeProvider(providerName)
|
||||
}
|
||||
Security.addProvider(clazz.newInstance() as Provider)
|
||||
generateKeyPair()
|
||||
_isECDHAvailable = true
|
||||
}.exceptionOrNull()?.let {
|
||||
throw IllegalStateException("cannot init BouncyCastle", it)
|
||||
}
|
||||
_isECDHAvailable = false
|
||||
}
|
||||
|
||||
actual fun generateKeyPair(): ECDHKeyPair {
|
||||
return ECDHKeyPair(KeyPairGenerator.getInstance("ECDH").genKeyPair())
|
||||
if (!isECDHAvailable) {
|
||||
return ECDHKeyPair.DefaultStub
|
||||
}
|
||||
return ECDHKeyPairImpl(KeyPairGenerator.getInstance("ECDH").genKeyPair())
|
||||
}
|
||||
|
||||
actual fun calculateShareKey(
|
||||
|
@ -19,7 +19,9 @@ expect interface ECDHPublicKey {
|
||||
fun getEncoded(): ByteArray
|
||||
}
|
||||
|
||||
expect class ECDHKeyPair {
|
||||
internal expect class ECDHKeyPairImpl : ECDHKeyPair
|
||||
|
||||
interface ECDHKeyPair {
|
||||
val privateKey: ECDHPrivateKey
|
||||
val publicKey: ECDHPublicKey
|
||||
|
||||
@ -27,6 +29,15 @@ expect class ECDHKeyPair {
|
||||
* 私匙和固定公匙([initialPublicKey]) 计算得到的 shareKey
|
||||
*/
|
||||
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
|
||||
|
||||
companion object {
|
||||
val isECDHAvailable: Boolean
|
||||
|
||||
/**
|
||||
* 由完整的 publicKey ByteArray 得到 [ECDHPublicKey]
|
||||
*/
|
||||
@ -60,9 +73,6 @@ expect class ECDH(keyPair: ECDHKeyPair) {
|
||||
override fun toString(): String
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Suppress("FunctionName")
|
||||
expect fun ECDH(): ECDH
|
||||
|
||||
|
@ -9,11 +9,9 @@
|
||||
|
||||
package net.mamoe.mirai.utils.cryptor
|
||||
|
||||
import net.mamoe.mirai.utils.io.chunkedHexToBytes
|
||||
import net.mamoe.mirai.utils.md5
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
||||
import java.security.*
|
||||
import java.security.spec.ECGenParameterSpec
|
||||
import java.security.spec.X509EncodedKeySpec
|
||||
import javax.crypto.KeyAgreement
|
||||
|
||||
@ -21,20 +19,13 @@ import javax.crypto.KeyAgreement
|
||||
actual typealias ECDHPrivateKey = PrivateKey
|
||||
actual typealias ECDHPublicKey = PublicKey
|
||||
|
||||
actual class ECDHKeyPair(
|
||||
private val delegate: KeyPair?
|
||||
) {
|
||||
actual val privateKey: ECDHPrivateKey get() = delegate?.private ?: error("ECDH is not available")
|
||||
actual val publicKey: ECDHPublicKey get() = delegate?.public ?: defaultPublicKey
|
||||
internal actual class ECDHKeyPairImpl(
|
||||
private val delegate: KeyPair
|
||||
) : ECDHKeyPair {
|
||||
override val privateKey: ECDHPrivateKey get() = delegate.private
|
||||
override val publicKey: ECDHPublicKey get() = delegate.public
|
||||
|
||||
actual val initialShareKey: ByteArray = if (delegate == null) {
|
||||
defaultShareKey
|
||||
} else ECDH.calculateShareKey(privateKey, initialPublicKey)
|
||||
|
||||
companion object {
|
||||
internal val defaultPublicKey = "020b03cf3d99541f29ffec281bebbd4ea211292ac1f53d7128".chunkedHexToBytes().adjustToPublicKey()
|
||||
internal val defaultShareKey = "4da0f614fc9f29c2054c77048a6566d7".chunkedHexToBytes()
|
||||
}
|
||||
override val initialShareKey: ByteArray = ECDH.calculateShareKey(privateKey, initialPublicKey)
|
||||
}
|
||||
|
||||
@Suppress("FunctionName")
|
||||
@ -42,33 +33,35 @@ actual fun ECDH() = ECDH(ECDH.generateKeyPair())
|
||||
|
||||
actual class ECDH actual constructor(actual val keyPair: ECDHKeyPair) {
|
||||
actual companion object {
|
||||
private var isECDHAvailable = true
|
||||
@Suppress("ObjectPropertyName")
|
||||
private val _isECDHAvailable: Boolean
|
||||
actual val isECDHAvailable: Boolean get() = _isECDHAvailable
|
||||
|
||||
init {
|
||||
isECDHAvailable = kotlin.runCatching {
|
||||
_isECDHAvailable = kotlin.runCatching {
|
||||
if (Security.getProvider("BouncyCastle") != null) {
|
||||
Security.removeProvider("BouncyCastle")
|
||||
}
|
||||
Security.addProvider(BouncyCastleProvider())
|
||||
generateKeyPair() // try if it is working
|
||||
}.isSuccess
|
||||
}
|
||||
|
||||
actual fun generateKeyPair(): ECDHKeyPair {
|
||||
return if (!isECDHAvailable) {
|
||||
ECDHKeyPair(null)
|
||||
} else ECDHKeyPair(KeyPairGenerator.getInstance("EC", "BC").apply { initialize(ECGenParameterSpec("secp192k1")) }.genKeyPair())
|
||||
if (!isECDHAvailable) {
|
||||
return ECDHKeyPair.DefaultStub
|
||||
}
|
||||
return ECDHKeyPairImpl(KeyPairGenerator.getInstance("ECDH").genKeyPair())
|
||||
}
|
||||
|
||||
actual fun calculateShareKey(
|
||||
privateKey: ECDHPrivateKey,
|
||||
publicKey: ECDHPublicKey
|
||||
): ByteArray {
|
||||
return if (!isECDHAvailable) {
|
||||
ECDHKeyPair.defaultShareKey
|
||||
} else {
|
||||
val instance = KeyAgreement.getInstance("ECDH", "BC")
|
||||
instance.init(privateKey)
|
||||
instance.doPhase(publicKey, true)
|
||||
md5(instance.generateSecret())
|
||||
}
|
||||
val instance = KeyAgreement.getInstance("ECDH", "BC")
|
||||
instance.init(privateKey)
|
||||
instance.doPhase(publicKey, true)
|
||||
return md5(instance.generateSecret())
|
||||
}
|
||||
|
||||
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 {
|
||||
if (!isECDHAvailable) return keyPair.initialShareKey
|
||||
return calculateShareKey(keyPair.privateKey, peerPublicKey)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user