Implement streaming API
This commit is contained in:
@@ -1,46 +1,85 @@
|
||||
package com.infendro.hash.blake
|
||||
|
||||
import com.infendro.bytes.bytearray.copyInto
|
||||
import com.infendro.bytes.intarray.copyInto
|
||||
import com.infendro.bytes.long.copyInto
|
||||
import com.infendro.hash.HashFunction
|
||||
import com.infendro.hash.util.align
|
||||
import com.infendro.hash.blake.`Blake-256`.Companion.c
|
||||
import com.infendro.hash.blake.`Blake-256`.Companion.sigma
|
||||
|
||||
object `Blake-224` : HashFunction {
|
||||
override val bytes: Int
|
||||
get() = 28
|
||||
class `Blake-224` : HashFunction(28, 64) {
|
||||
private val h = initial.copyOf()
|
||||
|
||||
override val block: Int
|
||||
get() = 64
|
||||
private val m = IntArray(16)
|
||||
private val v = IntArray(16)
|
||||
|
||||
private val initial = intArrayOf(
|
||||
-0x3efa6128, +0x367cd507,
|
||||
+0x3070dd17, -0x08f1a6c7,
|
||||
-0x003ff4cf, +0x68581511,
|
||||
+0x64f98fa7, -0x4105b05c,
|
||||
)
|
||||
|
||||
override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) {
|
||||
val length = value.size * 8L
|
||||
val input = pad(value)
|
||||
val h = initial.copyOf()
|
||||
var t = 0
|
||||
|
||||
for (i in input.indices step block) {
|
||||
t += minOf(length - t, 512).toInt()
|
||||
val block = input.sliceArray(i..<(i + block))
|
||||
`Blake-256`.compress(h, block, t)
|
||||
}
|
||||
h.copyInto(destination, destinationOffset, endIndex = 7)
|
||||
override fun reset() {
|
||||
super.reset()
|
||||
initial.copyInto(h)
|
||||
}
|
||||
|
||||
private fun pad(value: ByteArray): ByteArray {
|
||||
val length = value.size * 8L
|
||||
override fun process() = compress(buffer.value, length * 8L)
|
||||
|
||||
val size = (value.size + 9).align(block)
|
||||
return ByteArray(size).also {
|
||||
value.copyInto(it)
|
||||
it[value.size] = -0x80
|
||||
length.copyInto(it, size - 8)
|
||||
private fun finalize() {
|
||||
buffer.append(0x80.toByte())
|
||||
if (buffer.free < 8) {
|
||||
process()
|
||||
buffer.reset()
|
||||
}
|
||||
buffer.jumpTo(56)
|
||||
buffer.append(length * 8L)
|
||||
|
||||
process()
|
||||
}
|
||||
|
||||
override fun digestInto(destination: ByteArray, destinationOffset: Int) {
|
||||
finalize()
|
||||
h.copyInto(destination, destinationOffset, endIndex = 7)
|
||||
reset()
|
||||
}
|
||||
|
||||
private fun compress(block: ByteArray, t: Long) {
|
||||
block.copyInto(m)
|
||||
for (i in 0..<8) v[i] = h[i]
|
||||
for (i in 0..<4) v[i + 8] = c[i]
|
||||
for (i in 0..<2) v[i + 12] = c[i + 4] xor t.toInt()
|
||||
for (i in 0..<2) v[i + 14] = c[i + 6] xor (t ushr 32).toInt()
|
||||
|
||||
repeat(14) { r ->
|
||||
val s = sigma[r % 10]
|
||||
|
||||
g(v, 0, 4, 8, 12, m[s[0]] xor c[s[1]], m[s[1]] xor c[s[0]])
|
||||
g(v, 1, 5, 9, 13, m[s[2]] xor c[s[3]], m[s[3]] xor c[s[2]])
|
||||
g(v, 2, 6, 10, 14, m[s[4]] xor c[s[5]], m[s[5]] xor c[s[4]])
|
||||
g(v, 3, 7, 11, 15, m[s[6]] xor c[s[7]], m[s[7]] xor c[s[6]])
|
||||
|
||||
g(v, 0, 5, 10, 15, m[s[8]] xor c[s[9]], m[s[9]] xor c[s[8]])
|
||||
g(v, 1, 6, 11, 12, m[s[10]] xor c[s[11]], m[s[11]] xor c[s[10]])
|
||||
g(v, 2, 7, 8, 13, m[s[12]] xor c[s[13]], m[s[13]] xor c[s[12]])
|
||||
g(v, 3, 4, 9, 14, m[s[14]] xor c[s[15]], m[s[15]] xor c[s[14]])
|
||||
}
|
||||
|
||||
for (i in 0..<8) {
|
||||
h[i] = h[i] xor v[i] xor v[i + 8]
|
||||
}
|
||||
}
|
||||
|
||||
private fun g(v: IntArray, a: Int, b: Int, c: Int, d: Int, x: Int, y: Int) {
|
||||
v[a] += v[b] + x
|
||||
v[d] = (v[d] xor v[a]).rotateRight(16)
|
||||
v[c] += v[d]
|
||||
v[b] = (v[b] xor v[c]).rotateRight(12)
|
||||
v[a] += v[b] + y
|
||||
v[d] = (v[d] xor v[a]).rotateRight(8)
|
||||
v[c] += v[d]
|
||||
v[b] = (v[b] xor v[c]).rotateRight(7)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val initial = intArrayOf(
|
||||
-0x3efa6128, +0x367cd507,
|
||||
+0x3070dd17, -0x08f1a6c7,
|
||||
-0x003ff4cf, +0x68581511,
|
||||
+0x64f98fa7, -0x4105b05c,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user