Fail-fast decryption and encryption

This commit is contained in:
Him188 2019-10-19 13:18:57 +08:00
parent 8353a6ae8d
commit 244996ac28
2 changed files with 28 additions and 38 deletions

View File

@ -1,10 +1,10 @@
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import kotlinx.io.core.IoBuffer import kotlinx.io.core.IoBuffer
import kotlinx.io.core.readBytes
import kotlin.jvm.JvmStatic import kotlin.jvm.JvmStatic
internal expect object TEA { //TODO 优化为 buffer
expect object TEA {
internal fun doOption(data: ByteArray, key: ByteArray, encrypt: Boolean): ByteArray internal fun doOption(data: ByteArray, key: ByteArray, encrypt: Boolean): ByteArray
@JvmStatic @JvmStatic
@ -12,14 +12,18 @@ expect object TEA {
@JvmStatic @JvmStatic
fun decrypt(source: ByteArray, key: ByteArray): ByteArray fun decrypt(source: ByteArray, key: ByteArray): ByteArray
@JvmStatic
fun decrypt(source: ByteArray, key: IoBuffer): ByteArray
@JvmStatic
fun decrypt(source: ByteArray, keyHex: String): ByteArray
} }
fun ByteArray.decryptBy(key: ByteArray): ByteArray = TEA.decrypt(this, key) fun ByteArray.decryptBy(key: ByteArray): ByteArray = TEA.decrypt(checkLength(), key)
fun ByteArray.decryptBy(key: IoBuffer): ByteArray = TEA.decrypt(this, key) fun ByteArray.decryptBy(key: IoBuffer): ByteArray = TEA.decrypt(checkLength(), key.readBytes())
fun ByteArray.decryptBy(key: String): ByteArray = TEA.decrypt(this, key) fun ByteArray.decryptBy(keyHex: String): ByteArray = TEA.decrypt(checkLength(), keyHex.hexToBytes())
fun ByteArray.encryptBy(key: ByteArray): ByteArray = TEA.encrypt(checkLength(), key)
fun ByteArray.encryptBy(keyHex: String): ByteArray = TEA.encrypt(checkLength(), keyHex.hexToBytes())
private fun ByteArray.checkLength(): ByteArray {
size.let {
require(it % 8 == 0 && it >= 16) { "data must len % 8 == 0 && len >= 16 but given $it" }
}
return this
}

View File

@ -1,7 +1,5 @@
package net.mamoe.mirai.utils package net.mamoe.mirai.utils
import kotlinx.io.core.IoBuffer
import kotlinx.io.core.readBytes
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.util.* import java.util.*
import kotlin.experimental.and import kotlin.experimental.and
@ -12,7 +10,7 @@ import kotlin.experimental.xor
* *
* @author iweiz https://github.com/iweizime/StepChanger/blob/master/app/src/main/java/me/iweizi/stepchanger/qq/Cryptor.java * @author iweiz https://github.com/iweizime/StepChanger/blob/master/app/src/main/java/me/iweizi/stepchanger/qq/Cryptor.java
*/ */
actual object TEA { internal actual object TEA {
private const val UINT32_MASK = 0xffffffffL private const val UINT32_MASK = 0xffffffffL
internal actual fun doOption(data: ByteArray, key: ByteArray, encrypt: Boolean): ByteArray { internal actual fun doOption(data: ByteArray, key: ByteArray, encrypt: Boolean): ByteArray {
@ -163,14 +161,14 @@ actual object TEA {
return mOutput return mOutput
} }
fun decrypt(cipherText: ByteArray, offset: Int, len: Int): ByteArray? { fun decrypt(cipherText: ByteArray, offset: Int, len: Int): ByteArray {
require(!(len % 8 != 0 || len < 16)) { "data must len % 8 == 0 && len >= 16 but given $len" } require(!(len % 8 != 0 || len < 16)) { "data must len % 8 == 0 && len >= 16 but given $len" }
mIV = decode(cipherText, offset) mIV = decode(cipherText, offset)
mIndexPos = (mIV[0] and 7).toInt() mIndexPos = (mIV[0] and 7).toInt()
var plen = len - mIndexPos - 10 var plen = len - mIndexPos - 10
isFirstBlock = true isFirstBlock = true
if (plen < 0) { if (plen < 0) {
return null fail()
} }
mOutput = ByteArray(plen) mOutput = ByteArray(plen)
mPreOutPos = 0 mPreOutPos = 0
@ -185,7 +183,7 @@ actual object TEA {
if (mIndexPos == 8) { if (mIndexPos == 8) {
isFirstBlock = false isFirstBlock = false
if (!decodeOneBlock(cipherText, offset, len)) { if (!decodeOneBlock(cipherText, offset, len)) {
throw RuntimeException("Unable to dataDecode") fail()
} }
} }
} }
@ -203,7 +201,7 @@ actual object TEA {
mPreOutPos = mOutPos - 8 mPreOutPos = mOutPos - 8
isFirstBlock = false isFirstBlock = false
if (!decodeOneBlock(cipherText, offset, len)) { if (!decodeOneBlock(cipherText, offset, len)) {
throw RuntimeException("Unable to dataDecode") fail()
} }
} }
plen-- plen--
@ -212,7 +210,7 @@ actual object TEA {
while (g < 7) { while (g < 7) {
if (mIndexPos < 8) { if (mIndexPos < 8) {
if (cipherText[mPreOutPos + offset + mIndexPos].xor(mIV[mIndexPos]).toInt() != 0) { if (cipherText[mPreOutPos + offset + mIndexPos].xor(mIV[mIndexPos]).toInt() != 0) {
throw RuntimeException() fail()
} else { } else {
++mIndexPos ++mIndexPos
} }
@ -221,7 +219,7 @@ actual object TEA {
if (mIndexPos == 8) { if (mIndexPos == 8) {
mPreOutPos = mOutPos mPreOutPos = mOutPos
if (!decodeOneBlock(cipherText, offset, len)) { if (!decodeOneBlock(cipherText, offset, len)) {
throw RuntimeException("Unable to dataDecode") fail()
} }
} }
g++ g++
@ -232,16 +230,12 @@ actual object TEA {
return if (encrypt) { return if (encrypt) {
encrypt(data, 0, data.size) encrypt(data, 0, data.size)
} else { } else {
try { decrypt(data, 0, data.size)
return decrypt(data, 0, data.size)!!
} catch (e: Exception) {
//println("Source: " + data.toUHexString(" "))
// println("Key: " + key.toUHexString(" "))
throw e
}
} }
} }
private fun fail(): Nothing = throw DecryptionFailedException()
@JvmStatic @JvmStatic
actual fun encrypt(source: ByteArray, key: ByteArray): ByteArray { actual fun encrypt(source: ByteArray, key: ByteArray): ByteArray {
return doOption(source, key, true) return doOption(source, key, true)
@ -252,16 +246,6 @@ actual object TEA {
return doOption(source, key, false) return doOption(source, key, false)
} }
@JvmStatic
actual fun decrypt(source: ByteArray, key: IoBuffer): ByteArray {
return doOption(source, key.readBytes(), false)
}
@JvmStatic
actual fun decrypt(source: ByteArray, keyHex: String): ByteArray {
return decrypt(source, keyHex.hexToBytes())
}
@Suppress("SameParameterValue") @Suppress("SameParameterValue")
private fun pack(bytes: ByteArray, offset: Int, len: Int): Long { private fun pack(bytes: ByteArray, offset: Int, len: Int): Long {
var result: Long = 0 var result: Long = 0
@ -271,4 +255,6 @@ actual object TEA {
} }
return result shr 32 or (result and UINT32_MASK) return result shr 32 or (result and UINT32_MASK)
} }
} }
class DecryptionFailedException : Exception()