implement optimizations

This commit is contained in:
2026-01-15 22:06:28 +01:00
parent ab036f4f9f
commit d40df04e2e
26 changed files with 463 additions and 855 deletions

View File

@@ -1,15 +1,15 @@
package com.infendro.hash.keccak
import com.infendro.bytearray.ByteOrder.LITTLE_ENDIAN
import com.infendro.bytearray.toUByteArray
import com.infendro.bytearray.toULongArray
import com.infendro.bytes.ByteOrder.LITTLE_ENDIAN
import com.infendro.bytes.bytearray.copyInto
import com.infendro.bytes.long.copyInto
import com.infendro.hash.HashFunction
import kotlin.math.min
import com.infendro.hash.util.ceil
abstract class Keccak(
override val bytes: Int,
private val rate: Int,
private val domain: UByte,
private val domain: Byte,
) : HashFunction {
override val block: Int
get() = rate
@@ -21,7 +21,7 @@ abstract class Keccak(
0x000000008000808bUL, 0x800000000000008bUL, 0x8000000000008089UL, 0x8000000000008003UL,
0x8000000000008002UL, 0x8000000000000080UL, 0x000000000000800aUL, 0x800000008000000aUL,
0x8000000080008081UL, 0x8000000000008080UL, 0x0000000080000001UL, 0x8000000080008008UL,
)
).asLongArray()
private val rotation = arrayOf(
intArrayOf(0, 36, 3, 41, 18),
@@ -31,93 +31,79 @@ abstract class Keccak(
intArrayOf(27, 20, 39, 8, 14),
)
override fun hash(
value: UByteArray,
): UByteArray {
val state = ULongArray(25)
override fun hashInto(value: ByteArray, destination: ByteArray) {
val input = pad(value)
val state = LongArray(25)
val w = LongArray(block / 8)
val b = LongArray(25)
val c = LongArray(5)
val d = LongArray(5)
val blocks = pad(value)
.chunked(rate) { it.toUByteArray() }
for (block in blocks) {
absorb(state, block)
for (i in input.indices step block) {
absorb(state, input, i, w)
permute(state, b, c, d)
}
return squeeze(state)
squeeze(state, b, c, d, destination)
}
private fun pad(
value: UByteArray,
): UByteArray {
return buildList {
addAll(value)
add(domain)
while ((size % rate) != (rate - 1)) {
add(0x00U)
}
add(0x80U)
}.toUByteArray()
private fun pad(value: ByteArray): ByteArray {
val size = ceil(value.size + 2, block)
val result = ByteArray(size)
value.copyInto(result)
result[value.size] = domain
result[result.lastIndex] = 0x80U.toByte()
return result
}
private fun absorb(
state: ULongArray,
block: UByteArray,
) {
val numbers = block.toULongArray(LITTLE_ENDIAN)
for ((i, number) in numbers.withIndex()) {
state[i] = state[i] xor number
}
permute(state)
}
private fun permute(
a: ULongArray,
) {
val b = ULongArray(25)
val c = ULongArray(5)
val d = ULongArray(5)
repeat(24) { round ->
// θ
for (x in 0..4) {
c[x] = a[x] xor a[x + 5] xor a[x + 10] xor a[x + 15] xor a[x + 20]
}
for (x in 0..4) {
d[x] = c[(x + 4) % 5] xor c[(x + 1) % 5].rotateLeft(1)
}
for (x in 0..4) for (y in 0..4) {
a[x + 5 * y] = a[x + 5 * y] xor d[x]
}
// ρ and π
for (x in 0..4) for (y in 0..4) {
b[y + 5 * ((2 * x + 3 * y) % 5)] = a[x + 5 * y].rotateLeft(rotation[x][y])
}
// χ
for (x in 0..4) for (y in 0..4) {
a[x + 5 * y] = b[x + 5 * y] xor ((b[((x + 1) % 5) + 5 * y].inv()) and b[((x + 2) % 5) + 5 * y])
}
// ι
a[0] = a[0] xor rc[round]
private fun absorb(state: LongArray, value: ByteArray, offset: Int, w: LongArray) {
value.copyInto(w, startIndex = offset, endIndex = offset + block, order = LITTLE_ENDIAN)
for (i in w.indices) {
state[i] = state[i] xor w[i]
}
}
private fun squeeze(
state: ULongArray,
): UByteArray {
return buildList {
var i = 0
while (size < bytes) {
if (i == rate / 8) {
permute(state)
i = 0
}
addAll(
state[i++].toUByteArray(LITTLE_ENDIAN)
.sliceArray(0..<min(bytes - size, 8))
)
private fun squeeze(state: LongArray, b: LongArray, c: LongArray, d: LongArray, destination: ByteArray) {
val buffer = ByteArray(8)
var consumed = 0
var i = 0
while (consumed < bytes) {
if (i == rate / 8) {
permute(state, b, c, d)
i = 0
}
}.toUByteArray()
val length = minOf(bytes - consumed, 8)
state[i].copyInto(buffer, order = LITTLE_ENDIAN)
buffer.copyInto(destination, consumed, endIndex = length)
consumed += length
i++
}
}
private fun permute(a: LongArray, b: LongArray, c: LongArray, d: LongArray) = repeat(24) { round ->
// θ
for (x in 0..4) {
c[x] = a[x] xor a[x + 5] xor a[x + 10] xor a[x + 15] xor a[x + 20]
}
for (x in 0..4) {
d[x] = c[(x + 4) % 5] xor c[(x + 1) % 5].rotateLeft(1)
}
for (x in 0..4) for (y in 0..4) {
a[x + 5 * y] = a[x + 5 * y] xor d[x]
}
// ρ and π
for (x in 0..4) for (y in 0..4) {
b[y + 5 * ((2 * x + 3 * y) % 5)] = a[x + 5 * y].rotateLeft(rotation[x][y])
}
// χ
for (x in 0..4) for (y in 0..4) {
a[x + 5 * y] = b[x + 5 * y] xor ((b[((x + 1) % 5) + 5 * y].inv()) and b[((x + 2) % 5) + 5 * y])
}
// ι
a[0] = a[0] xor rc[round]
}
}