From 95c97b8399ee97c4e06ec219a5280fae6018ba84 Mon Sep 17 00:00:00 2001 From: Graham Date: Tue, 4 Aug 2020 11:16:17 +0100 Subject: [PATCH] Add XteaKey inline class with ZERO "constant" and isZero method Unfortunately we can't use the compiler to guarantee k isn't changed, though making it internal will help. When the JVM (and Kotlin) get value types, we might be able to improve on this (e.g. by making it an inline class of four integers). Signed-off-by: Graham --- build.gradle.kts | 2 +- .../src/main/java/dev/openrs2/crypto/Xtea.kt | 25 +++++++++++++------ .../test/java/dev/openrs2/crypto/XteaTest.kt | 4 +-- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7c71301b..7a987906 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -56,7 +56,7 @@ allprojects { tasks.withType { kotlinOptions { jvmTarget = "11" - freeCompilerArgs = listOf("-Xjsr305=strict") + freeCompilerArgs = listOf("-Xinline-classes", "-Xjsr305=strict") } } diff --git a/crypto/src/main/java/dev/openrs2/crypto/Xtea.kt b/crypto/src/main/java/dev/openrs2/crypto/Xtea.kt index 672aa68e..ece879d3 100644 --- a/crypto/src/main/java/dev/openrs2/crypto/Xtea.kt +++ b/crypto/src/main/java/dev/openrs2/crypto/Xtea.kt @@ -7,8 +7,17 @@ private const val ROUNDS = 32 private const val BLOCK_SIZE = 8 private const val BLOCK_SIZE_MASK = BLOCK_SIZE - 1 -fun ByteBuf.xteaEncrypt(index: Int, length: Int, key: IntArray) { - require(key.size == 4) +inline class XteaKey(internal val k: IntArray) { + val isZero: Boolean + get() = k[0] == 0 && k[1] == 0 && k[2] == 0 && k[3] == 0 + + companion object { + val ZERO = XteaKey(IntArray(4)) + } +} + +fun ByteBuf.xteaEncrypt(index: Int, length: Int, key: XteaKey) { + require(key.k.size == 4) val end = index + (length and BLOCK_SIZE_MASK.inv()) for (i in index until end step BLOCK_SIZE) { @@ -17,9 +26,9 @@ fun ByteBuf.xteaEncrypt(index: Int, length: Int, key: IntArray) { var v1 = getInt(i + 4) for (j in 0 until ROUNDS) { - v0 += (((v1 shl 4) xor (v1 ushr 5)) + v1) xor (sum + key[sum and 3]) + v0 += (((v1 shl 4) xor (v1 ushr 5)) + v1) xor (sum + key.k[sum and 3]) sum += GOLDEN_RATIO - v1 += (((v0 shl 4) xor (v0 ushr 5)) + v0) xor (sum + key[(sum ushr 11) and 3]) + v1 += (((v0 shl 4) xor (v0 ushr 5)) + v0) xor (sum + key.k[(sum ushr 11) and 3]) } setInt(i, v0) @@ -27,8 +36,8 @@ fun ByteBuf.xteaEncrypt(index: Int, length: Int, key: IntArray) { } } -fun ByteBuf.xteaDecrypt(index: Int, length: Int, key: IntArray) { - require(key.size == 4) +fun ByteBuf.xteaDecrypt(index: Int, length: Int, key: XteaKey) { + require(key.k.size == 4) val end = index + (length and BLOCK_SIZE_MASK.inv()) for (i in index until end step BLOCK_SIZE) { @@ -38,9 +47,9 @@ fun ByteBuf.xteaDecrypt(index: Int, length: Int, key: IntArray) { var v1 = getInt(i + 4) for (j in 0 until ROUNDS) { - v1 -= (((v0 shl 4) xor (v0 ushr 5)) + v0) xor (sum + key[(sum ushr 11) and 3]) + v1 -= (((v0 shl 4) xor (v0 ushr 5)) + v0) xor (sum + key.k[(sum ushr 11) and 3]) sum -= GOLDEN_RATIO - v0 -= (((v1 shl 4) xor (v1 ushr 5)) + v1) xor (sum + key[sum and 3]) + v0 -= (((v1 shl 4) xor (v1 ushr 5)) + v1) xor (sum + key.k[sum and 3]) } setInt(i, v0) diff --git a/crypto/src/test/java/dev/openrs2/crypto/XteaTest.kt b/crypto/src/test/java/dev/openrs2/crypto/XteaTest.kt index 9fcf6898..1ca07176 100644 --- a/crypto/src/test/java/dev/openrs2/crypto/XteaTest.kt +++ b/crypto/src/test/java/dev/openrs2/crypto/XteaTest.kt @@ -8,9 +8,9 @@ import kotlin.test.assertEquals object XteaTest { private class TestVector(key: String, plaintext: String, ciphertext: String) { - val key = IntArray(4) { + val key = XteaKey(IntArray(4) { Integer.parseUnsignedInt(key, it * 8, it * 8 + 8, 16) - } + }) val plaintext: ByteArray = ByteBufUtil.decodeHexDump(plaintext) val ciphertext: ByteArray = ByteBufUtil.decodeHexDump(ciphertext) }