Implement streaming API
This commit is contained in:
@@ -4,68 +4,60 @@ 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 com.infendro.hash.util.align
|
||||
|
||||
open class Keccak(
|
||||
override val bytes: Int,
|
||||
bytes: Int,
|
||||
private val rate: Int,
|
||||
private val domain: Byte,
|
||||
) : HashFunction {
|
||||
) : HashFunction(bytes, rate) {
|
||||
init {
|
||||
require(bytes > 0)
|
||||
require(rate in 8..192 step 8)
|
||||
}
|
||||
|
||||
override val block: Int
|
||||
get() = rate
|
||||
private var state = LongArray(25)
|
||||
|
||||
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 w = LongArray(block / 8)
|
||||
private val b = LongArray(25)
|
||||
private val c = LongArray(5)
|
||||
private val d = LongArray(5)
|
||||
|
||||
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),
|
||||
)
|
||||
|
||||
override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) {
|
||||
val input = pad(value)
|
||||
val state = LongArray(25)
|
||||
|
||||
for (i in input.indices step block) {
|
||||
val block = input.sliceArray(i..<(i + block))
|
||||
absorb(state, block)
|
||||
permute(state)
|
||||
}
|
||||
squeeze(state, destination, destinationOffset)
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
state.fill(0L)
|
||||
}
|
||||
|
||||
private fun pad(value: ByteArray): ByteArray {
|
||||
val size = (value.size + 2).align(block)
|
||||
return ByteArray(size).also {
|
||||
value.copyInto(it)
|
||||
it[value.size] = domain
|
||||
it[size - 1] = -0x80
|
||||
}
|
||||
override fun process() {
|
||||
absorb(buffer.value)
|
||||
permute()
|
||||
}
|
||||
|
||||
private fun absorb(state: LongArray, block: ByteArray) {
|
||||
val w = LongArray(this.block / 8)
|
||||
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(state: LongArray, destination: ByteArray, destinationOffset: Int) {
|
||||
private fun squeeze(destination: ByteArray, destinationOffset: Int) {
|
||||
val buffer = ByteArray(8)
|
||||
val threshold = rate / 8
|
||||
|
||||
@@ -73,7 +65,7 @@ open class Keccak(
|
||||
var i = 0
|
||||
while (consumed < bytes) {
|
||||
if (i == threshold) {
|
||||
permute(state)
|
||||
permute()
|
||||
i = 0
|
||||
}
|
||||
|
||||
@@ -86,10 +78,8 @@ open class Keccak(
|
||||
}
|
||||
}
|
||||
|
||||
private fun permute(a: LongArray) = repeat(24) { round ->
|
||||
val b = LongArray(25)
|
||||
val c = LongArray(5)
|
||||
val d = LongArray(5)
|
||||
private fun permute() = repeat(24) { round ->
|
||||
val a = state
|
||||
|
||||
// θ
|
||||
for (x in 0..4) {
|
||||
@@ -115,4 +105,23 @@ open class Keccak(
|
||||
// ι
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user