Files
hash.kt/src/commonMain/kotlin/com/infendro/hash/keccak/Keccak.kt
2026-03-11 18:53:00 +01:00

128 lines
3.6 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.infendro.hash.keccak
import com.infendro.bytes.ByteOrder.LITTLE_ENDIAN
import com.infendro.bytes.bytearray.copyInto
import com.infendro.bytes.long.copyInto
import com.infendro.hash.HashFunction
open class Keccak(
bytes: Int,
private val rate: Int,
private val domain: Byte,
) : HashFunction(bytes, rate) {
init {
require(bytes > 0)
require(rate in 8..192 step 8)
}
private var state = LongArray(25)
private val w = LongArray(block / 8)
private val b = LongArray(25)
private val c = LongArray(5)
private val d = LongArray(5)
override fun reset() {
super.reset()
state.fill(0L)
}
override fun process() {
absorb(buffer.value)
permute()
}
private fun finalize() {
buffer.append(domain)
if (buffer.free < 1) {
process()
buffer.reset()
}
buffer.jumpTo(block - 1)
buffer.append(0x80.toByte())
process()
}
override fun digestInto(destination: ByteArray, destinationOffset: Int) {
finalize()
squeeze(destination, destinationOffset)
reset()
}
private fun absorb(block: ByteArray) {
block.copyInto(w, order = LITTLE_ENDIAN)
for (i in w.indices) {
state[i] = state[i] xor w[i]
}
}
private fun squeeze(destination: ByteArray, destinationOffset: Int) {
val buffer = ByteArray(8)
val threshold = rate / 8
var consumed = 0
var i = 0
while (consumed < bytes) {
if (i == threshold) {
permute()
i = 0
}
val length = minOf(bytes - consumed, 8)
state[i].copyInto(buffer, order = LITTLE_ENDIAN)
buffer.copyInto(destination, destinationOffset + consumed, endIndex = length)
consumed += length
i++
}
}
private fun permute() = repeat(24) { round ->
val a = state
// θ
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]
}
companion object {
private val rc = longArrayOf(
+0x0000000000000001, +0x0000000000008082, -0x7fffffffffff7f76, -0x7fffffff7fff8000,
+0x000000000000808b, +0x0000000080000001, -0x7fffffff7fff7f7f, -0x7fffffffffff7ff7,
+0x000000000000008a, +0x0000000000000088, +0x0000000080008009, +0x000000008000000a,
+0x000000008000808b, -0x7fffffffffffff75, -0x7fffffffffff7f77, -0x7fffffffffff7ffd,
-0x7fffffffffff7ffe, -0x7fffffffffffff80, +0x000000000000800a, -0x7fffffff7ffffff6,
-0x7fffffff7fff7f7f, -0x7fffffffffff7f80, +0x0000000080000001, -0x7fffffff7fff7ff8,
)
private val rotation = arrayOf(
intArrayOf(0, 36, 3, 41, 18),
intArrayOf(1, 44, 10, 45, 2),
intArrayOf(62, 6, 43, 15, 61),
intArrayOf(28, 55, 25, 21, 56),
intArrayOf(27, 20, 39, 8, 14),
)
}
}