Open-source multiplayer game server compatible with the RuneScape client
https://www.openrs2.org/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
289 lines
9.0 KiB
289 lines
9.0 KiB
package org.openrs2.crypto
|
|
|
|
import com.google.common.base.Preconditions
|
|
import io.netty.buffer.ByteBuf
|
|
import io.netty.buffer.ByteBufUtil
|
|
|
|
public fun ByteBuf.whirlpool(): ByteArray {
|
|
return whirlpool(readerIndex(), readableBytes())
|
|
}
|
|
|
|
public fun ByteBuf.whirlpool(index: Int, len: Int): ByteArray {
|
|
Preconditions.checkPositionIndexes(index, index + len, capacity())
|
|
|
|
return if (hasArray()) {
|
|
Whirlpool.whirlpool(array(), arrayOffset() + index, len)
|
|
} else {
|
|
Whirlpool.whirlpool(ByteBufUtil.getBytes(this, index, len, false))
|
|
}
|
|
}
|
|
|
|
public class Whirlpool {
|
|
private val bitLength = ByteArray(32)
|
|
private val buffer = ByteArray(64)
|
|
private var bufferBits = 0
|
|
private var bufferPos = 0
|
|
private val hash = LongArray(8)
|
|
private val K = LongArray(8)
|
|
private val L = LongArray(8)
|
|
private val block = LongArray(8)
|
|
private val state = LongArray(8)
|
|
|
|
private fun processBuffer() {
|
|
var j = 0
|
|
for (i in 0 until 8) {
|
|
block[i] = (buffer[j].toLong() shl 56) xor
|
|
((buffer[j + 1].toLong() and 0xFFL) shl 48) xor
|
|
((buffer[j + 2].toLong() and 0xFFL) shl 40) xor
|
|
((buffer[j + 3].toLong() and 0xFFL) shl 32) xor
|
|
((buffer[j + 4].toLong() and 0xFFL) shl 24) xor
|
|
((buffer[j + 5].toLong() and 0xFFL) shl 16) xor
|
|
((buffer[j + 6].toLong() and 0xFFL) shl 8) xor
|
|
(buffer[j + 7].toLong() and 0xFFL)
|
|
j += 8
|
|
}
|
|
|
|
for (i in 0 until 8) {
|
|
K[i] = hash[i]
|
|
state[i] = block[i] xor K[i]
|
|
}
|
|
|
|
for (r in 1..R) {
|
|
for (i in 0 until 8) {
|
|
L[i] = 0
|
|
|
|
var s = 56
|
|
for (t in 0 until 8) {
|
|
L[i] = L[i] xor C[t][(K[(i - t) and 7] ushr s).toInt() and 0xFF]
|
|
s -= 8
|
|
}
|
|
}
|
|
|
|
for (i in 0 until 8) {
|
|
K[i] = L[i]
|
|
}
|
|
|
|
K[0] = K[0] xor rc[r]
|
|
|
|
for (i in 0 until 8) {
|
|
L[i] = K[i]
|
|
|
|
var s = 56
|
|
for (t in 0 until 8) {
|
|
L[i] = L[i] xor C[t][(state[(i - t) and 7] ushr s).toInt() and 0xFF]
|
|
s -= 8
|
|
}
|
|
}
|
|
|
|
for (i in 0 until 8) {
|
|
state[i] = L[i]
|
|
}
|
|
}
|
|
|
|
for (i in 0 until 8) {
|
|
hash[i] = hash[i] xor state[i] xor block[i]
|
|
}
|
|
}
|
|
|
|
public fun NESSIEinit() {
|
|
bitLength.fill(0)
|
|
bufferBits = 0
|
|
bufferPos = 0
|
|
buffer[0] = 0
|
|
hash.fill(0)
|
|
}
|
|
|
|
public fun NESSIEadd(source: ByteArray, bits: Long) {
|
|
var sourceBits = bits
|
|
var sourcePos = 0
|
|
val sourceGap = (8 - (sourceBits.toInt() and 7)) and 7
|
|
val bufferRem = bufferBits and 7
|
|
var b: Int
|
|
|
|
var value = sourceBits
|
|
var carry = 0
|
|
for (i in 31 downTo 0) {
|
|
carry += (bitLength[i].toInt() and 0xFF) + (value.toInt() and 0xFF)
|
|
bitLength[i] = carry.toByte()
|
|
carry = carry ushr 8
|
|
value = value ushr 8
|
|
}
|
|
|
|
while (sourceBits > 8) {
|
|
b = ((source[sourcePos].toInt() shl sourceGap) and 0xFF) or
|
|
((source[sourcePos + 1].toInt() and 0xFF) ushr (8 - sourceGap))
|
|
check(b in 0..255)
|
|
|
|
buffer[bufferPos] = (buffer[bufferPos].toInt() or (b ushr bufferRem)).toByte()
|
|
bufferPos++
|
|
bufferBits += 8 - bufferRem
|
|
|
|
if (bufferBits == 512) {
|
|
processBuffer()
|
|
bufferBits = 0
|
|
bufferPos = 0
|
|
}
|
|
|
|
buffer[bufferPos] = ((b shl (8 - bufferRem)) and 0xFF).toByte()
|
|
bufferBits += bufferRem
|
|
|
|
sourceBits -= 8
|
|
sourcePos++
|
|
}
|
|
|
|
if (sourceBits > 0) {
|
|
b = (source[sourcePos].toInt() shl sourceGap) and 0xFF
|
|
buffer[bufferPos] = (buffer[bufferPos].toInt() or (b ushr bufferRem)).toByte()
|
|
} else {
|
|
b = 0
|
|
}
|
|
|
|
if (bufferRem + sourceBits < 8) {
|
|
bufferBits += sourceBits.toInt()
|
|
} else {
|
|
bufferPos++
|
|
bufferBits += 8 - bufferRem
|
|
sourceBits -= 8 - bufferRem
|
|
|
|
if (bufferBits == 512) {
|
|
processBuffer()
|
|
bufferBits = 0
|
|
bufferPos = 0
|
|
}
|
|
|
|
buffer[bufferPos] = ((b shl (8 - bufferRem)) and 0xFF).toByte()
|
|
bufferBits += sourceBits.toInt()
|
|
}
|
|
}
|
|
|
|
public fun NESSIEfinalize(digest: ByteArray) {
|
|
buffer[bufferPos] = (buffer[bufferPos].toInt() or (0x80 ushr (bufferBits and 7))).toByte()
|
|
bufferPos++
|
|
|
|
if (bufferPos > 32) {
|
|
while (bufferPos < 64) {
|
|
buffer[bufferPos++] = 0
|
|
}
|
|
|
|
processBuffer()
|
|
bufferPos = 0
|
|
}
|
|
|
|
while (bufferPos < 32) {
|
|
buffer[bufferPos++] = 0
|
|
}
|
|
|
|
bitLength.copyInto(buffer, destinationOffset = 32, startIndex = 0, endIndex = 32)
|
|
processBuffer()
|
|
|
|
var j = 0
|
|
for (i in 0 until 8) {
|
|
val h = hash[i]
|
|
digest[j] = (h ushr 56).toByte()
|
|
digest[j + 1] = (h ushr 48).toByte()
|
|
digest[j + 2] = (h ushr 40).toByte()
|
|
digest[j + 3] = (h ushr 32).toByte()
|
|
digest[j + 4] = (h ushr 24).toByte()
|
|
digest[j + 5] = (h ushr 16).toByte()
|
|
digest[j + 6] = (h ushr 8).toByte()
|
|
digest[j + 7] = h.toByte()
|
|
j += 8
|
|
}
|
|
}
|
|
|
|
public companion object {
|
|
private const val DIGESTBITS = 512
|
|
public const val DIGESTBYTES: Int = DIGESTBITS ushr 3
|
|
private const val R = 10
|
|
private const val sbox = "\u1823\uC6E8\u87B8\u014F\u36A6\uD2F5\u796F\u9152" +
|
|
"\u60BC\u9B8E\uA30C\u7B35\u1DE0\uD7C2\u2E4B\uFE57" +
|
|
"\u1577\u37E5\u9FF0\u4ADA\u58C9\u290A\uB1A0\u6B85" +
|
|
"\uBD5D\u10F4\uCB3E\u0567\uE427\u418B\uA77D\u95D8" +
|
|
"\uFBEE\u7C66\uDD17\u479E\uCA2D\uBF07\uAD5A\u8333" +
|
|
"\u6302\uAA71\uC819\u49D9\uF2E3\u5B88\u9A26\u32B0" +
|
|
"\uE90F\uD580\uBECD\u3448\uFF7A\u905F\u2068\u1AAE" +
|
|
"\uB454\u9322\u64F1\u7312\u4008\uC3EC\uDBA1\u8D3D" +
|
|
"\u9700\uCF2B\u7682\uD61B\uB5AF\u6A50\u45F3\u30EF" +
|
|
"\u3F55\uA2EA\u65BA\u2FC0\uDE1C\uFD4D\u9275\u068A" +
|
|
"\uB2E6\u0E1F\u62D4\uA896\uF9C5\u2559\u8472\u394C" +
|
|
"\u5E78\u388C\uD1A5\uE261\uB321\u9C1E\u43C7\uFC04" +
|
|
"\u5199\u6D0D\uFADF\u7E24\u3BAB\uCE11\u8F4E\uB7EB" +
|
|
"\u3C81\u94F7\uB913\u2CD3\uE76E\uC403\u5644\u7FA9" +
|
|
"\u2ABB\uC153\uDC0B\u9D6C\u3174\uF646\uAC89\u14E1" +
|
|
"\u163A\u6909\u70B6\uD0ED\uCC42\u98A4\u285C\uF886"
|
|
private val C = Array(8) { LongArray(256) }
|
|
private val rc = LongArray(R + 1)
|
|
|
|
init {
|
|
for (x in 0 until 256) {
|
|
val c = sbox[x / 2]
|
|
|
|
val v1 = if ((x and 1) == 0) {
|
|
c.code.toLong() ushr 8
|
|
} else {
|
|
c.code.toLong() and 0xFF
|
|
}
|
|
|
|
var v2 = v1 shl 1
|
|
if (v2 >= 0x100) {
|
|
v2 = v2 xor 0x11D
|
|
}
|
|
|
|
var v4 = v2 shl 1
|
|
if (v4 >= 0x100) {
|
|
v4 = v4 xor 0x11D
|
|
}
|
|
|
|
val v5 = v4 xor v1
|
|
|
|
var v8 = v4 shl 1
|
|
if (v8 >= 0x100) {
|
|
v8 = v8 xor 0x11D
|
|
}
|
|
|
|
val v9 = v8 xor v1
|
|
|
|
C[0][x] = (v1 shl 56) or (v1 shl 48) or (v4 shl 40) or (v1 shl 32) or
|
|
(v8 shl 24) or (v5 shl 16) or (v2 shl 8) or v9
|
|
|
|
for (t in 1 until 8) {
|
|
C[t][x] = (C[t - 1][x] ushr 8) or (C[t - 1][x] shl 56)
|
|
}
|
|
}
|
|
|
|
rc[0] = 0
|
|
|
|
for (r in 1..R) {
|
|
val i = 8 * (r - 1)
|
|
rc[r] = (C[0][i] and -0x100000000000000L) xor
|
|
(C[1][i + 1] and 0x00FF000000000000L) xor
|
|
(C[2][i + 2] and 0x0000FF0000000000L) xor
|
|
(C[3][i + 3] and 0x000000FF00000000L) xor
|
|
(C[4][i + 4] and 0x00000000FF000000L) xor
|
|
(C[5][i + 5] and 0x0000000000FF0000L) xor
|
|
(C[6][i + 6] and 0x000000000000FF00L) xor
|
|
(C[7][i + 7] and 0x00000000000000FFL)
|
|
}
|
|
}
|
|
|
|
public fun whirlpool(data: ByteArray, off: Int = 0, len: Int = data.size): ByteArray {
|
|
val source: ByteArray
|
|
if (off <= 0) {
|
|
source = data
|
|
} else {
|
|
source = ByteArray(len)
|
|
for (i in 0 until len) {
|
|
source[i] = data[off + i]
|
|
}
|
|
}
|
|
|
|
val whirlpool = Whirlpool()
|
|
whirlpool.NESSIEinit()
|
|
whirlpool.NESSIEadd(source, (len * 8).toLong())
|
|
|
|
val digest = ByteArray(DIGESTBYTES)
|
|
whirlpool.NESSIEfinalize(digest)
|
|
return digest
|
|
}
|
|
}
|
|
}
|
|
|