From 328d4b957e84126be6928581b4c0dfba4302c736 Mon Sep 17 00:00:00 2001 From: Infendro Date: Wed, 11 Mar 2026 18:53:00 +0100 Subject: [PATCH 1/4] Implement streaming API --- .../kotlin/com/infendro/hash/HashFunction.kt | 74 +++++++++- .../com/infendro/hash/blake/Blake-224.kt | 105 +++++++++----- .../com/infendro/hash/blake/Blake-256.kt | 126 ++++++++-------- .../com/infendro/hash/blake/Blake-384.kt | 106 +++++++++----- .../com/infendro/hash/blake/Blake-512.kt | 123 ++++++++-------- .../com/infendro/hash/blake2/Blake2b.kt | 112 +++++++++------ .../com/infendro/hash/blake2/Blake2s.kt | 115 +++++++++------ .../kotlin/com/infendro/hash/keccak/Keccak.kt | 101 +++++++------ .../com/infendro/hash/keccak/sha3/SHA3-224.kt | 2 +- .../com/infendro/hash/keccak/sha3/SHA3-256.kt | 2 +- .../com/infendro/hash/keccak/sha3/SHA3-384.kt | 2 +- .../com/infendro/hash/keccak/sha3/SHA3-512.kt | 2 +- .../kotlin/com/infendro/hash/md/MD5.kt | 134 ++++++++++-------- .../kotlin/com/infendro/hash/sha1/SHA1.kt | 75 +++++----- .../kotlin/com/infendro/hash/sha2/SHA-224.kt | 83 ++++++++--- .../kotlin/com/infendro/hash/sha2/SHA-256.kt | 91 ++++++------ .../kotlin/com/infendro/hash/sha2/SHA-384.kt | 83 ++++++++--- .../kotlin/com/infendro/hash/sha2/SHA-512.kt | 113 +++++++-------- .../kotlin/com/infendro/hash/util/Buffer.kt | 46 ++++++ .../kotlin/com/infendro/hash/util/Math.kt | 6 - .../com/infendro/hash/blake/Blake-224.kt | 2 +- .../com/infendro/hash/blake/Blake-256.kt | 2 +- .../com/infendro/hash/blake/Blake-384.kt | 2 +- .../com/infendro/hash/blake/Blake-512.kt | 2 +- .../com/infendro/hash/keccak/sha3/SHA3-224.kt | 2 +- .../com/infendro/hash/keccak/sha3/SHA3-256.kt | 2 +- .../com/infendro/hash/keccak/sha3/SHA3-384.kt | 2 +- .../com/infendro/hash/keccak/sha3/SHA3-512.kt | 2 +- .../kotlin/com/infendro/hash/md/MD5.kt | 2 +- .../kotlin/com/infendro/hash/sha1/SHA1.kt | 2 +- .../kotlin/com/infendro/hash/sha2/SHA-224.kt | 2 +- .../kotlin/com/infendro/hash/sha2/SHA-256.kt | 2 +- .../kotlin/com/infendro/hash/sha2/SHA-384.kt | 2 +- .../kotlin/com/infendro/hash/sha2/SHA-512.kt | 2 +- 34 files changed, 936 insertions(+), 593 deletions(-) create mode 100644 src/commonMain/kotlin/com/infendro/hash/util/Buffer.kt delete mode 100644 src/commonMain/kotlin/com/infendro/hash/util/Math.kt diff --git a/src/commonMain/kotlin/com/infendro/hash/HashFunction.kt b/src/commonMain/kotlin/com/infendro/hash/HashFunction.kt index f7d5ae7..ccf72ba 100644 --- a/src/commonMain/kotlin/com/infendro/hash/HashFunction.kt +++ b/src/commonMain/kotlin/com/infendro/hash/HashFunction.kt @@ -1,17 +1,79 @@ package com.infendro.hash -interface HashFunction { - val bytes: Int +import com.infendro.hash.util.Buffer + +abstract class HashFunction( + val bytes: Int, + val block: Int, +) { val bits: Int get() = bytes * 8 - val block: Int + internal val buffer = Buffer(block) + internal var length = 0L - fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int = 0) + open fun reset() { + buffer.reset() + length = 0L + } - fun hash(value: ByteArray): ByteArray { + open fun update(byte: Byte) { + buffer.append(byte) + length++ + if (buffer.isFull()) { + process() + buffer.reset() + } + } + + open fun update(bytes: ByteArray) { + var i = 0 + + // (partial) first block + val free = buffer.free + if (i + free < bytes.size) { + buffer.append(bytes, startIndex = i, endIndex = i + free) + length += free + process() + buffer.reset() + i += free + } + + // full blocks + while (i + block < bytes.size) { + buffer.append(bytes, startIndex = i, endIndex = i + block) + length += block + process() + buffer.reset() + i += block + } + + // (partial) last block + buffer.append(bytes, startIndex = i) + length += bytes.size - i + if (buffer.isFull()) { + process() + buffer.reset() + } + } + + protected abstract fun process() + + abstract fun digestInto(destination: ByteArray, destinationOffset: Int = 0) + + fun digest(): ByteArray { val result = ByteArray(bytes) - hashInto(value, result) + digestInto(result) return result } + + fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int = 0) { + update(value) + digestInto(destination, destinationOffset) + } + + fun hash(value: ByteArray): ByteArray { + update(value) + return digest() + } } diff --git a/src/commonMain/kotlin/com/infendro/hash/blake/Blake-224.kt b/src/commonMain/kotlin/com/infendro/hash/blake/Blake-224.kt index 02a462b..836a9e3 100644 --- a/src/commonMain/kotlin/com/infendro/hash/blake/Blake-224.kt +++ b/src/commonMain/kotlin/com/infendro/hash/blake/Blake-224.kt @@ -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, + ) } } diff --git a/src/commonMain/kotlin/com/infendro/hash/blake/Blake-256.kt b/src/commonMain/kotlin/com/infendro/hash/blake/Blake-256.kt index 1de86e3..caaaabe 100644 --- a/src/commonMain/kotlin/com/infendro/hash/blake/Blake-256.kt +++ b/src/commonMain/kotlin/com/infendro/hash/blake/Blake-256.kt @@ -1,82 +1,47 @@ package com.infendro.hash.blake -import com.infendro.bytes.bytearray.toIntArray +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 -object `Blake-256` : HashFunction { - override val bytes: Int - get() = 32 +class `Blake-256` : HashFunction(32, 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( - +0x6a09e667, -0x4498517b, - +0x3c6ef372, -0x5ab00ac6, - +0x510e527f, -0x64fa9774, - +0x1f83d9ab, +0x5be0cd19, - ) + override fun reset() { + super.reset() + initial.copyInto(h) + } - private val c = intArrayOf( - +0x243f6a88, -0x7a5cf72d, - +0x13198a2e, +0x03707344, - -0x5bf6c7de, +0x299f31d0, - +0x082efa98, -0x13b19377, - +0x452821e6, +0x38d01377, - -0x41ab9931, +0x34e90c6c, - -0x3f53d649, -0x3683af23, - +0x3f84d5b5, -0x4ab8f6e9, - ) + override fun process() = compress(buffer.value, length * 8L) - private val sigma = arrayOf( - intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), - intArrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), - intArrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), - intArrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), - intArrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), - intArrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), - intArrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), - intArrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), - intArrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), - intArrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0), - ) - - 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)) - compress(h, block, t) + private fun finalize() { + buffer.append(0x80.toByte()) + if (buffer.free < 9) { + process() + buffer.reset() } + buffer.jumpTo(55) + buffer.append(0x01.toByte()) + buffer.append(length * 8L) + + process() + } + + override fun digestInto(destination: ByteArray, destinationOffset: Int) { + finalize() h.copyInto(destination, destinationOffset) + reset() } - private fun pad(value: ByteArray): ByteArray { - val length = value.size * 8L - - val size = (value.size + 10).align(block) - return ByteArray(size).also { - value.copyInto(it) - it[value.size] = -0x80 - it[size - 9] = 0x01 - length.copyInto(it, size - 8) - } - } - - internal fun compress(h: IntArray, block: ByteArray, t: Int) { - val m = block.toIntArray() - val v = IntArray(16) + 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 - for (i in 0..<2) v[i + 14] = c[i + 6] + 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] @@ -107,4 +72,37 @@ object `Blake-256` : HashFunction { v[c] += v[d] v[b] = (v[b] xor v[c]).rotateRight(7) } + + companion object { + private val initial = intArrayOf( + +0x6a09e667, -0x4498517b, + +0x3c6ef372, -0x5ab00ac6, + +0x510e527f, -0x64fa9774, + +0x1f83d9ab, +0x5be0cd19, + ) + + internal val c = intArrayOf( + +0x243f6a88, -0x7a5cf72d, + +0x13198a2e, +0x03707344, + -0x5bf6c7de, +0x299f31d0, + +0x082efa98, -0x13b19377, + +0x452821e6, +0x38d01377, + -0x41ab9931, +0x34e90c6c, + -0x3f53d649, -0x3683af23, + +0x3f84d5b5, -0x4ab8f6e9, + ) + + internal val sigma = arrayOf( + intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), + intArrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), + intArrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), + intArrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), + intArrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), + intArrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), + intArrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), + intArrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), + intArrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), + intArrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0), + ) + } } diff --git a/src/commonMain/kotlin/com/infendro/hash/blake/Blake-384.kt b/src/commonMain/kotlin/com/infendro/hash/blake/Blake-384.kt index 124e0d6..b05a82e 100644 --- a/src/commonMain/kotlin/com/infendro/hash/blake/Blake-384.kt +++ b/src/commonMain/kotlin/com/infendro/hash/blake/Blake-384.kt @@ -1,46 +1,86 @@ package com.infendro.hash.blake -import com.infendro.bytes.long.copyInto +import com.infendro.bytes.bytearray.copyInto import com.infendro.bytes.longarray.copyInto import com.infendro.hash.HashFunction -import com.infendro.hash.util.align +import com.infendro.hash.blake.`Blake-512`.Companion.c +import com.infendro.hash.blake.`Blake-512`.Companion.sigma -object `Blake-384` : HashFunction { - override val bytes: Int - get() = 48 +class `Blake-384` : HashFunction(48, 128) { + private val h = initial.copyOf() - override val block: Int - get() = 128 + private val m = LongArray(16) + private val v = LongArray(16) - private val initial = longArrayOf( - -0x344462a23efa6128, +0x629a292a367cd507, - -0x6ea6fea5cf8f22e9, +0x152fecd8f70e5939, - +0x67332667ffc00b31, -0x714bb57897a7eaef, - -0x24f3d1f29b067059, +0x47b5481dbefa4fa4, - ) - - override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) { - val length = value.size * 8L - val input = pad(value) - val h = initial.copyOf() - var t = 0L - - for (i in input.indices step block) { - t += minOf(length - t, 1024) - val block = input.sliceArray(i..<(i + block)) - `Blake-512`.compress(h, block, t) - } - h.copyInto(destination, destinationOffset, endIndex = 6) + 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 + 17).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 < 16) { + process() + buffer.reset() } + buffer.jumpTo(112) + buffer.append(0L) + buffer.append(length * 8L) + + process() + } + + override fun digestInto(destination: ByteArray, destinationOffset: Int) { + finalize() + h.copyInto(destination, destinationOffset, endIndex = 6) + 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 + for (i in 0..<2) v[i + 14] = c[i + 6] + + repeat(16) { 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: LongArray, a: Int, b: Int, c: Int, d: Int, x: Long, y: Long) { + v[a] += v[b] + x + v[d] = (v[d] xor v[a]).rotateRight(32) + v[c] += v[d] + v[b] = (v[b] xor v[c]).rotateRight(25) + v[a] += v[b] + y + v[d] = (v[d] xor v[a]).rotateRight(16) + v[c] += v[d] + v[b] = (v[b] xor v[c]).rotateRight(11) + } + + companion object { + private val initial = longArrayOf( + -0x344462a23efa6128, +0x629a292a367cd507, + -0x6ea6fea5cf8f22e9, +0x152fecd8f70e5939, + +0x67332667ffc00b31, -0x714bb57897a7eaef, + -0x24f3d1f29b067059, +0x47b5481dbefa4fa4, + ) } } diff --git a/src/commonMain/kotlin/com/infendro/hash/blake/Blake-512.kt b/src/commonMain/kotlin/com/infendro/hash/blake/Blake-512.kt index 0ff7f47..2cd73df 100644 --- a/src/commonMain/kotlin/com/infendro/hash/blake/Blake-512.kt +++ b/src/commonMain/kotlin/com/infendro/hash/blake/Blake-512.kt @@ -1,78 +1,44 @@ package com.infendro.hash.blake -import com.infendro.bytes.bytearray.toLongArray -import com.infendro.bytes.long.copyInto +import com.infendro.bytes.bytearray.copyInto import com.infendro.bytes.longarray.copyInto import com.infendro.hash.HashFunction -import com.infendro.hash.util.align -object `Blake-512` : HashFunction { - override val bytes: Int - get() = 64 +class `Blake-512` : HashFunction(64, 128) { + private val h = initial.copyOf() - override val block: Int - get() = 128 + private val m = LongArray(16) + private val v = LongArray(16) - private val initial = longArrayOf( - +0x6a09e667f3bcc908L, -0x4498517a7b3558c5L, - +0x3c6ef372fe94f82bL, -0x5ab00ac5a0e2c90fL, - +0x510e527fade682d1L, -0x64fa9773d4c193e1L, - +0x1f83d9abfb41bd6bL, +0x5be0cd19137e2179L, - ) + override fun reset() { + super.reset() + initial.copyInto(h) + } - private val c = longArrayOf( - +0x243f6a8885a308d3L, +0x13198a2e03707344L, - -0x5bf6c7ddd660ce30L, +0x082efa98ec4e6c89L, - +0x452821e638d01377L, -0x41ab9930cb16f394L, - -0x3f53d6483683af23L, +0x3f84d5b5b5470917L, - -0x6de92a26768604e5L, -0x2ecef45967204a54L, - +0x2ffd72dbd01adfb7L, -0x471e501295d9816aL, - -0x45836fba0ed38067L, +0x24a19947b3916cf7L, - +0x0801f2e2858efc16L, +0x636920d871574e69L, - ) + override fun process() = compress(buffer.value, length * 8L) - private val sigma = arrayOf( - intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), - intArrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), - intArrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), - intArrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), - intArrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), - intArrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), - intArrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), - intArrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), - intArrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), - intArrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0), - ) - - override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) { - val length = value.size * 8L - val input = pad(value) - val h = initial.copyOf() - var t = 0L - - for (i in input.indices step block) { - t += minOf(length - t, 1024) - val block = input.sliceArray(i..<(i + block)) - compress(h, block, t) + private fun finalize() { + buffer.append(0x80.toByte()) + if (buffer.free < 17) { + process() + buffer.reset() } + buffer.jumpTo(111) + buffer.append(0x01.toByte()) + buffer.append(0L) + buffer.append(length * 8L) + + process() + } + + override fun digestInto(destination: ByteArray, destinationOffset: Int) { + finalize() h.copyInto(destination, destinationOffset) + reset() } - private fun pad(value: ByteArray): ByteArray { - val length = value.size * 8L - - val size = (value.size + 18).align(block) - return ByteArray(size).also { - value.copyInto(it) - it[value.size] = -0x80 - it[size - 17] = 0x01 - length.copyInto(it, size - 8) - } - } - - internal fun compress(h: LongArray, block: ByteArray, t: Long) { - val m = block.toLongArray() - val v = LongArray(16) + 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 @@ -107,4 +73,37 @@ object `Blake-512` : HashFunction { v[c] += v[d] v[b] = (v[b] xor v[c]).rotateRight(11) } + + companion object { + private val initial = longArrayOf( + +0x6a09e667f3bcc908L, -0x4498517a7b3558c5L, + +0x3c6ef372fe94f82bL, -0x5ab00ac5a0e2c90fL, + +0x510e527fade682d1L, -0x64fa9773d4c193e1L, + +0x1f83d9abfb41bd6bL, +0x5be0cd19137e2179L, + ) + + internal val c = longArrayOf( + +0x243f6a8885a308d3L, +0x13198a2e03707344L, + -0x5bf6c7ddd660ce30L, +0x082efa98ec4e6c89L, + +0x452821e638d01377L, -0x41ab9930cb16f394L, + -0x3f53d6483683af23L, +0x3f84d5b5b5470917L, + -0x6de92a26768604e5L, -0x2ecef45967204a54L, + +0x2ffd72dbd01adfb7L, -0x471e501295d9816aL, + -0x45836fba0ed38067L, +0x24a19947b3916cf7L, + +0x0801f2e2858efc16L, +0x636920d871574e69L, + ) + + internal val sigma = arrayOf( + intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), + intArrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), + intArrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), + intArrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), + intArrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), + intArrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), + intArrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), + intArrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), + intArrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), + intArrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0), + ) + } } diff --git a/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2b.kt b/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2b.kt index c9183e4..e0db34b 100644 --- a/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2b.kt +++ b/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2b.kt @@ -1,66 +1,66 @@ package com.infendro.hash.blake2 import com.infendro.bytes.ByteOrder.LITTLE_ENDIAN -import com.infendro.bytes.bytearray.toLongArray +import com.infendro.bytes.bytearray.copyInto import com.infendro.bytes.longarray.toByteArray import com.infendro.hash.HashFunction -import com.infendro.hash.util.align -class Blake2b( - override val bytes: Int, -) : HashFunction { +class Blake2b(bytes: Int) : HashFunction(bytes, 128) { init { require(bytes in 1..64) } - override val block: Int - get() = 128 + private val h = initial.copyOf() + .also { h -> + h[0] = h[0] xor (0x01010000L or bytes.toLong()) + } - private val initial = longArrayOf( - +0x6a09e667f3bcc908L, -0x4498517a7b3558c5L, - +0x3c6ef372fe94f82bL, -0x5ab00ac5a0e2c90fL, - +0x510e527fade682d1L, -0x64fa9773d4c193e1L, - +0x1f83d9abfb41bd6bL, +0x5be0cd19137e2179L, - ) + private val m = LongArray(16) + private val v = LongArray(16) - private val sigma = arrayOf( - intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), - intArrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), - intArrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), - intArrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), - intArrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), - intArrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), - intArrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), - intArrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), - intArrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), - intArrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0), - ) - - override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) { - val length = value.size.toLong() - val input = pad(value) - val h = initial.copyOf() + override fun reset() { + super.reset() + initial.copyInto(h) h[0] = h[0] xor (0x01010000L or bytes.toLong()) - var t = 0L + } - for (i in input.indices step block) { - t += minOf(length - t, 128) - val block = input.sliceArray(i..<(i + block)) - compress(h, block, t, t < 1024) + override fun update(byte: Byte) { + if (buffer.isFull()) { + process() + buffer.reset() } + buffer.append(byte) + length++ + } + + override fun update(bytes: ByteArray) { + if (buffer.isFull() && bytes.isNotEmpty()) { + process() + buffer.reset() + } + var i = 0 + while (i + buffer.free < bytes.size) { + val remaining = buffer.free + buffer.append(bytes, startIndex = i, endIndex = i + remaining) + process() + buffer.reset() + i += remaining + } + buffer.append(bytes, startIndex = i) + } + + override fun process() = compress(buffer.value, length, false) + + private fun finalize() = compress(buffer.value, length, true) + + override fun digestInto(destination: ByteArray, destinationOffset: Int) { + finalize() h.toByteArray(order = LITTLE_ENDIAN).copyInto(destination, destinationOffset, endIndex = bytes) + reset() } - private fun pad(value: ByteArray): ByteArray { - val size = maxOf(value.size.align(block), block) - return ByteArray(size).also { - value.copyInto(it) - } - } - - private fun compress(h: LongArray, block: ByteArray, t: Long, last: Boolean) { - val m = block.toLongArray() - val v = LongArray(16) + private fun compress(block: ByteArray, t: Long, last: Boolean) { + block.copyInto(m) for (i in 0..<8) v[i] = h[i] for (i in 0..<8) v[i + 8] = initial[i] v[12] = v[12] xor t @@ -95,4 +95,26 @@ class Blake2b( v[c] += v[d] v[b] = (v[b] xor v[c]).rotateRight(63) } + + companion object { + private val initial = longArrayOf( + +0x6a09e667f3bcc908L, -0x4498517a7b3558c5L, + +0x3c6ef372fe94f82bL, -0x5ab00ac5a0e2c90fL, + +0x510e527fade682d1L, -0x64fa9773d4c193e1L, + +0x1f83d9abfb41bd6bL, +0x5be0cd19137e2179L, + ) + + private val sigma = arrayOf( + intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), + intArrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), + intArrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), + intArrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), + intArrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), + intArrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), + intArrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), + intArrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), + intArrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), + intArrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0), + ) + } } diff --git a/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2s.kt b/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2s.kt index ccd276d..ed8c74d 100644 --- a/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2s.kt +++ b/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2s.kt @@ -1,69 +1,70 @@ package com.infendro.hash.blake2 import com.infendro.bytes.ByteOrder.LITTLE_ENDIAN -import com.infendro.bytes.bytearray.toIntArray +import com.infendro.bytes.bytearray.copyInto import com.infendro.bytes.intarray.toByteArray import com.infendro.hash.HashFunction -import com.infendro.hash.util.align -class Blake2s( - override val bytes: Int, -) : HashFunction { +class Blake2s(bytes: Int) : HashFunction(bytes, 64) { init { require(bytes in 1..32) } - override val block: Int - get() = 64 + private val h = initial.copyOf() + .also { h -> + h[0] = h[0] xor (0x01010000 or bytes) + } - private val initial = intArrayOf( - +0x6a09e667, -0x4498517b, - +0x3c6ef372, -0x5ab00ac6, - +0x510e527f, -0x64fa9774, - +0x1f83d9ab, +0x5be0cd19, - ) + private val m = IntArray(16) + private val v = IntArray(16) - private val sigma = arrayOf( - intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), - intArrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), - intArrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), - intArrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), - intArrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), - intArrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), - intArrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), - intArrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), - intArrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), - intArrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0), - ) - - override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) { - val length = value.size.toLong() - val input = pad(value) - val h = initial.copyOf() + override fun reset() { + super.reset() + initial.copyInto(h) h[0] = h[0] xor (0x01010000 or bytes) - var t = 0 + } - for (i in input.indices step block) { - t += minOf(length - t, 64).toInt() - val block = input.sliceArray(i..<(i + block)) - compress(h, block, t, t < 1024) + override fun update(byte: Byte) { + if (buffer.isFull()) { + process() + buffer.reset() } + buffer.append(byte) + length++ + } + + override fun update(bytes: ByteArray) { + if (buffer.isFull() && bytes.isNotEmpty()) { + process() + buffer.reset() + } + var i = 0 + while (i + buffer.free < bytes.size) { + val remaining = buffer.free + buffer.append(bytes, startIndex = i, endIndex = i + remaining) + process() + buffer.reset() + i += remaining + } + buffer.append(bytes, startIndex = i) + } + + override fun process() = compress(buffer.value, length, false) + + private fun finalize() = compress(buffer.value, length, true) + + override fun digestInto(destination: ByteArray, destinationOffset: Int) { + finalize() h.toByteArray(order = LITTLE_ENDIAN).copyInto(destination, destinationOffset, endIndex = bytes) + reset() } - private fun pad(value: ByteArray): ByteArray { - val size = maxOf(value.size.align(block), block) - return ByteArray(size).also { - value.copyInto(it) - } - } - - private fun compress(h: IntArray, block: ByteArray, t: Int, last: Boolean) { - val m = block.toIntArray() - val v = IntArray(16) + private fun compress(block: ByteArray, t: Long, last: Boolean) { + block.copyInto(m) for (i in 0..<8) v[i] = h[i] for (i in 0..<8) v[i + 8] = initial[i] - v[12] = v[12] xor t + v[12] = v[12] xor t.toInt() + v[13] = v[13] xor (t ushr 32).toInt() if (last) v[14] = v[14].inv() repeat(10) { r -> @@ -95,4 +96,26 @@ class Blake2s( v[c] += v[d] v[b] = (v[b] xor v[c]).rotateRight(7) } + + companion object { + private val initial = intArrayOf( + +0x6a09e667, -0x4498517b, + +0x3c6ef372, -0x5ab00ac6, + +0x510e527f, -0x64fa9774, + +0x1f83d9ab, +0x5be0cd19, + ) + + private val sigma = arrayOf( + intArrayOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), + intArrayOf(14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3), + intArrayOf(11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4), + intArrayOf(7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8), + intArrayOf(9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13), + intArrayOf(2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9), + intArrayOf(12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11), + intArrayOf(13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10), + intArrayOf(6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5), + intArrayOf(10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0), + ) + } } diff --git a/src/commonMain/kotlin/com/infendro/hash/keccak/Keccak.kt b/src/commonMain/kotlin/com/infendro/hash/keccak/Keccak.kt index db89247..f38a474 100644 --- a/src/commonMain/kotlin/com/infendro/hash/keccak/Keccak.kt +++ b/src/commonMain/kotlin/com/infendro/hash/keccak/Keccak.kt @@ -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), + ) + } } diff --git a/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-224.kt b/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-224.kt index 369b3c5..f4f4019 100644 --- a/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-224.kt +++ b/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-224.kt @@ -2,4 +2,4 @@ package com.infendro.hash.keccak.sha3 import com.infendro.hash.keccak.Keccak -object `SHA3-224` : Keccak(28, 144, 0x06) +class `SHA3-224` : Keccak(28, 144, 0x06) diff --git a/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-256.kt b/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-256.kt index 1b132ab..eb3c416 100644 --- a/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-256.kt +++ b/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-256.kt @@ -2,4 +2,4 @@ package com.infendro.hash.keccak.sha3 import com.infendro.hash.keccak.Keccak -object `SHA3-256` : Keccak(32, 136, 0x06) +class `SHA3-256` : Keccak(32, 136, 0x06) diff --git a/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-384.kt b/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-384.kt index e0dc589..1692a45 100644 --- a/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-384.kt +++ b/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-384.kt @@ -2,4 +2,4 @@ package com.infendro.hash.keccak.sha3 import com.infendro.hash.keccak.Keccak -object `SHA3-384` : Keccak(48, 104, 0x06) +class `SHA3-384` : Keccak(48, 104, 0x06) diff --git a/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-512.kt b/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-512.kt index c0f8207..ef2247d 100644 --- a/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-512.kt +++ b/src/commonMain/kotlin/com/infendro/hash/keccak/sha3/SHA3-512.kt @@ -2,4 +2,4 @@ package com.infendro.hash.keccak.sha3 import com.infendro.hash.keccak.Keccak -object `SHA3-512` : Keccak(64, 72, 0x06) +class `SHA3-512` : Keccak(64, 72, 0x06) diff --git a/src/commonMain/kotlin/com/infendro/hash/md/MD5.kt b/src/commonMain/kotlin/com/infendro/hash/md/MD5.kt index 89c3487..51e9d42 100644 --- a/src/commonMain/kotlin/com/infendro/hash/md/MD5.kt +++ b/src/commonMain/kotlin/com/infendro/hash/md/MD5.kt @@ -1,79 +1,44 @@ package com.infendro.hash.md import com.infendro.bytes.ByteOrder.LITTLE_ENDIAN -import com.infendro.bytes.bytearray.toIntArray +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 -object MD5 : HashFunction { - override val bytes: Int - get() = 16 +class MD5 : HashFunction(16, 64) { + private val h = initial.copyOf() - override val block: Int - get() = 64 + private val w = IntArray(16) + private val v = IntArray(4) - private val initial = intArrayOf( - +0x67452301, -0x10325477, - -0x67452302, +0x10325476, - ) + override fun reset() { + super.reset() + initial.copyInto(h) + } - private val c = intArrayOf( - -0x28955b88, -0x173848aa, +0x242070db, -0x3e423112, - -0x0a83f051, +0x4787c62a, -0x57cfb9ed, -0x02b96aff, - +0x698098d8, -0x74bb0851, -0x0000a44f, -0x76a32842, - +0x6b901122, -0x02678e6d, -0x5986bc72, +0x49b40821, - -0x09e1da9e, -0x3fbf4cc0, +0x265e5a51, -0x16493856, - -0x29d0efa3, +0x02441453, -0x275e197f, -0x182c0438, - +0x21e1cde6, -0x3cc8f82a, -0x0b2af279, +0x455a14ed, - -0x561c16fb, -0x03105c08, +0x676f02d9, -0x72d5b376, - -0x0005c6be, -0x788e097f, +0x6d9d6122, -0x021ac7f4, - -0x5b4115bc, +0x4bdecfa9, -0x0944b4a0, -0x41404390, - +0x289b7ec6, -0x155ed806, -0x2b10cf7b, +0x04881d05, - -0x262b2fc7, -0x1924661b, +0x1fa27cf8, -0x3b53a99b, - -0x0bd6ddbc, +0x432aff97, -0x546bdc59, -0x036c5fc7, - +0x655b59c3, -0x70f3336e, -0x00100b83, -0x7a7ba22f, - +0x6fa87e4f, -0x01d31920, -0x5cfebcec, +0x4e0811a1, - -0x08ac817e, -0x42c50dcb, +0x2ad7d2bb, -0x14792c6f, - ) + override fun process() = process(buffer.value) - private val shifts = intArrayOf( - 7, 12, 17, 22, 7, 12, 17, 22, - 7, 12, 17, 22, 7, 12, 17, 22, - 5, 9, 14, 20, 5, 9, 14, 20, - 5, 9, 14, 20, 5, 9, 14, 20, - 4, 11, 16, 23, 4, 11, 16, 23, - 4, 11, 16, 23, 4, 11, 16, 23, - 6, 10, 15, 21, 6, 10, 15, 21, - 6, 10, 15, 21, 6, 10, 15, 21, - ) - - override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) { - val input = pad(value) - val h = initial.copyOf() - - for (i in input.indices step block) { - val block = input.sliceArray(i..<(i + block)) - process(h, block) + private fun finalize() { + buffer.append(0x80.toByte()) + if (buffer.free < 8) { + process() + buffer.reset() } + buffer.jumpTo(56) + buffer.append(length * 8, LITTLE_ENDIAN) + + process() + } + + override fun digestInto(destination: ByteArray, destinationOffset: Int) { + finalize() h.copyInto(destination, destinationOffset, order = LITTLE_ENDIAN) + reset() } - private fun pad(value: ByteArray): ByteArray { - val length = value.size * 8L - - val size = (value.size + 9).align(block) - return ByteArray(size).also { - value.copyInto(it) - it[value.size] = -0x80 - length.copyInto(it, size - 8, order = LITTLE_ENDIAN) - } - } - - private fun process(h: IntArray, block: ByteArray) { - val w = block.toIntArray(order = LITTLE_ENDIAN) - val v = h.copyOf() + private fun process(block: ByteArray) { + block.copyInto(w, order = LITTLE_ENDIAN) + h.copyInto(v) repeat(64) { i -> var f = when (i) { @@ -99,4 +64,49 @@ object MD5 : HashFunction { h[i] += v[i] } } + + companion object { + private val initial = intArrayOf( + +0x67452301, -0x10325477, + -0x67452302, +0x10325476, + ) + + private val c = intArrayOf( + -0x28955b88, -0x173848aa, +0x242070db, -0x3e423112, + -0x0a83f051, +0x4787c62a, -0x57cfb9ed, -0x02b96aff, + +0x698098d8, -0x74bb0851, -0x0000a44f, -0x76a32842, + +0x6b901122, -0x02678e6d, -0x5986bc72, +0x49b40821, + -0x09e1da9e, -0x3fbf4cc0, +0x265e5a51, -0x16493856, + -0x29d0efa3, +0x02441453, -0x275e197f, -0x182c0438, + +0x21e1cde6, -0x3cc8f82a, -0x0b2af279, +0x455a14ed, + -0x561c16fb, -0x03105c08, +0x676f02d9, -0x72d5b376, + -0x0005c6be, -0x788e097f, +0x6d9d6122, -0x021ac7f4, + -0x5b4115bc, +0x4bdecfa9, -0x0944b4a0, -0x41404390, + +0x289b7ec6, -0x155ed806, -0x2b10cf7b, +0x04881d05, + -0x262b2fc7, -0x1924661b, +0x1fa27cf8, -0x3b53a99b, + -0x0bd6ddbc, +0x432aff97, -0x546bdc59, -0x036c5fc7, + +0x655b59c3, -0x70f3336e, -0x00100b83, -0x7a7ba22f, + +0x6fa87e4f, -0x01d31920, -0x5cfebcec, +0x4e0811a1, + -0x08ac817e, -0x42c50dcb, +0x2ad7d2bb, -0x14792c6f, + ) + + private val shifts = intArrayOf( + 7, 12, 17, 22, + 7, 12, 17, 22, + 7, 12, 17, 22, + 7, 12, 17, 22, + 5, 9, 14, 20, + 5, 9, 14, 20, + 5, 9, 14, 20, + 5, 9, 14, 20, + 4, 11, 16, 23, + 4, 11, 16, 23, + 4, 11, 16, 23, + 4, 11, 16, 23, + 6, 10, 15, 21, + 6, 10, 15, 21, + 6, 10, 15, 21, + 6, 10, 15, 21, + ) + } } diff --git a/src/commonMain/kotlin/com/infendro/hash/sha1/SHA1.kt b/src/commonMain/kotlin/com/infendro/hash/sha1/SHA1.kt index 2daabb7..c72f9e4 100644 --- a/src/commonMain/kotlin/com/infendro/hash/sha1/SHA1.kt +++ b/src/commonMain/kotlin/com/infendro/hash/sha1/SHA1.kt @@ -2,57 +2,45 @@ package com.infendro.hash.sha1 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 -object SHA1 : HashFunction { - override val bytes: Int - get() = 20 +class SHA1 : HashFunction(20, 64) { + private val h = initial.copyOf() - override val block: Int - get() = 64 + private val w = IntArray(80) + private val v = IntArray(5) - private val initial = intArrayOf( - +0x67452301, -0x10325477, - -0x67452302, +0x10325476, - -0x3c2d1e10, - ) + override fun reset() { + super.reset() + initial.copyInto(h) + } - private val k = intArrayOf( - +0x5a827999, +0x6ed9eba1, - -0x70e44324, -0x359d3e2a, - ) + override fun process() = process(buffer.value) - override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) { - val input = pad(value) - val h = initial.copyOf() - - for (i in input.indices step block) { - val block = input.sliceArray(i..<(i + block)) - process(h, block) + private fun finalize() { + buffer.append(0x80.toByte()) + if (buffer.free < 8) { + process() + buffer.reset() } + buffer.jumpTo(56) + buffer.append(length * 8) + + process() + } + + override fun digestInto(destination: ByteArray, destinationOffset: Int) { + finalize() h.copyInto(destination, destinationOffset) + reset() } - private fun pad(value: ByteArray): ByteArray { - val length = value.size * 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 process(h: IntArray, block: ByteArray) { - val w = IntArray(80) + private fun process(block: ByteArray) { block.copyInto(w) for (i in 16..<80) { w[i] = (w[i - 3] xor w[i - 8] xor w[i - 14] xor w[i - 16]).rotateLeft(1) } - val v = h.copyOf() + h.copyInto(v) repeat(80) { i -> val f = when (i) { @@ -73,4 +61,17 @@ object SHA1 : HashFunction { h[i] += v[i] } } + + companion object { + private val initial = intArrayOf( + +0x67452301, -0x10325477, + -0x67452302, +0x10325476, + -0x3c2d1e10, + ) + + private val k = intArrayOf( + +0x5a827999, +0x6ed9eba1, + -0x70e44324, -0x359d3e2a, + ) + } } diff --git a/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-224.kt b/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-224.kt index 0ba169e..72f3bc0 100644 --- a/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-224.kt +++ b/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-224.kt @@ -1,30 +1,79 @@ package com.infendro.hash.sha2 +import com.infendro.bytes.bytearray.copyInto import com.infendro.bytes.intarray.copyInto import com.infendro.hash.HashFunction +import com.infendro.hash.sha2.`SHA-256`.Companion.c -object `SHA-224` : HashFunction { - override val bytes: Int - get() = 28 +class `SHA-224` : HashFunction(28, 64) { + private val h = initial.copyOf() - override val block: Int - get() = 64 + private val w = IntArray(64) + private val v = IntArray(8) - private val initial = intArrayOf( - -0x3efa6128, +0x367cd507, - +0x3070dd17, -0x08f1a6c7, - -0x003ff4cf, +0x68581511, - +0x64f98fa7, -0x4105b05c, - ) + override fun reset() { + super.reset() + initial.copyInto(h) + } - override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) { - val input = `SHA-256`.pad(value) - val h = initial.copyOf() + override fun process() = process(buffer.value) - for (i in input.indices step block) { - val block = input.sliceArray(i..<(i + block)) - `SHA-256`.process(h, block) + private fun finalize() { + buffer.append(0x80.toByte()) + if (buffer.free < 8) { + process(buffer.value) + buffer.reset() } + buffer.jumpTo(56) + buffer.append(length * 8) + + process(buffer.value) + } + + override fun digestInto(destination: ByteArray, destinationOffset: Int) { + finalize() h.copyInto(destination, destinationOffset, endIndex = 7) + reset() + } + + private fun process(block: ByteArray) { + block.copyInto(w) + for (i in 16..<64) { + val s0 = w[i - 15].rotateRight(7) xor w[i - 15].rotateRight(18) xor (w[i - 15] ushr 3) + val s1 = w[i - 2].rotateRight(17) xor w[i - 2].rotateRight(19) xor (w[i - 2] ushr 10) + w[i] = (w[i - 16] + s0 + w[i - 7] + s1) + } + h.copyInto(v) + + repeat(64) { i -> + val s0 = v[0].rotateRight(2) xor v[0].rotateRight(13) xor v[0].rotateRight(22) + val s1 = v[4].rotateRight(6) xor v[4].rotateRight(11) xor v[4].rotateRight(25) + val ch = (v[4] and v[5]) xor (v[4].inv() and v[6]) + val temp1 = v[7] + s1 + ch + c[i] + w[i] + val maj = (v[0] and v[1]) xor (v[0] and v[2]) xor (v[1] and v[2]) + val temp2 = s0 + maj + + v[7] = v[6] + v[6] = v[5] + v[5] = v[4] + v[4] = v[3] + temp1 + v[3] = v[2] + v[2] = v[1] + v[1] = v[0] + v[0] = temp1 + temp2 + } + + for (i in 0..<8) { + h[i] += v[i] + } + } + + companion object { + private val initial = intArrayOf( + -0x3efa6128, +0x367cd507, + +0x3070dd17, -0x08f1a6c7, + -0x003ff4cf, +0x68581511, + +0x64f98fa7, -0x4105b05c, + ) } } diff --git a/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-256.kt b/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-256.kt index db5f947..732e8ce 100644 --- a/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-256.kt +++ b/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-256.kt @@ -2,72 +2,53 @@ package com.infendro.hash.sha2 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 -object `SHA-256` : HashFunction { - override val bytes: Int - get() = 32 +class `SHA-256` : HashFunction(32, 64) { + private val h = initial.copyOf() - override val block: Int - get() = 64 + private val w = IntArray(64) + private val v = IntArray(8) - private val initial = intArrayOf( - +0x6a09e667, -0x4498517b, - +0x3c6ef372, -0x5ab00ac6, - +0x510e527f, -0x64fa9774, - +0x1f83d9ab, +0x5be0cd19, - ) + override fun reset() { + super.reset() + initial.copyInto(h) + } - private val c = intArrayOf( - +0x428a2f98, +0x71374491, -0x4a3f0431, -0x164a245b, +0x3956c25b, +0x59f111f1, -0x6dc07d5c, -0x54e3a12b, - -0x27f85568, +0x12835b01, +0x243185be, +0x550c7dc3, +0x72be5d74, -0x7f214e02, -0x6423f959, -0x3e640e8c, - -0x1b64963f, -0x1041b87a, +0x0fc19dc6, +0x240ca1cc, +0x2de92c6f, +0x4a7484aa, +0x5cb0a9dc, +0x76f988da, - -0x67c1aeae, -0x57ce3993, -0x4ffcd838, -0x40a68039, -0x391ff40d, -0x2a586eb9, +0x06ca6351, +0x14292967, - +0x27b70a85, +0x2e1b2138, +0x4d2c6dfc, +0x53380d13, +0x650a7354, +0x766a0abb, -0x7e3d36d2, -0x6d8dd37b, - -0x5d40175f, -0x57e599b5, -0x3db47490, -0x3893ae5d, -0x2e6d17e7, -0x2966f9dc, -0x0bf1ca7b, +0x106aa070, - +0x19a4c116, +0x1e376c08, +0x2748774c, +0x34b0bcb5, +0x391c0cb3, +0x4ed8aa4a, +0x5b9cca4f, +0x682e6ff3, - +0x748f82ee, +0x78a5636f, -0x7b3787ec, -0x7338fdf8, -0x6f410006, -0x5baf9315, -0x41065c09, -0x398e870e, - ) + override fun process() = process(buffer.value) - override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) { - val input = pad(value) - val h = initial.copyOf() - - for (i in input.indices step block) { - val block = input.sliceArray(i..<(i + block)) - process(h, block) + private fun finalize() { + buffer.append(0x80.toByte()) + if (buffer.free < 8) { + process(buffer.value) + buffer.reset() } + buffer.jumpTo(56) + buffer.append(length * 8) + + process(buffer.value) + } + + override fun digestInto(destination: ByteArray, destinationOffset: Int) { + finalize() h.copyInto(destination, destinationOffset) + reset() } - internal fun pad(value: ByteArray): ByteArray { - val length = value.size * 8L - - val size = (value.size + 9).align(block) - return ByteArray(size).also { - value.copyInto(it) - it[value.size] = -0x80 - length.copyInto(it, size - 8) - } - } - - internal fun process(h: IntArray, block: ByteArray) { - val w = IntArray(64) + private fun process(block: ByteArray) { block.copyInto(w) for (i in 16..<64) { val s0 = w[i - 15].rotateRight(7) xor w[i - 15].rotateRight(18) xor (w[i - 15] ushr 3) val s1 = w[i - 2].rotateRight(17) xor w[i - 2].rotateRight(19) xor (w[i - 2] ushr 10) w[i] = (w[i - 16] + s0 + w[i - 7] + s1) } - val v = h.copyOf() + h.copyInto(v) repeat(64) { i -> val s0 = v[0].rotateRight(2) xor v[0].rotateRight(13) xor v[0].rotateRight(22) val s1 = v[4].rotateRight(6) xor v[4].rotateRight(11) xor v[4].rotateRight(25) val ch = (v[4] and v[5]) xor (v[4].inv() and v[6]) - val temp1 = v[7] + s1 + ch + this.c[i] + w[i] + val temp1 = v[7] + s1 + ch + c[i] + w[i] val maj = (v[0] and v[1]) xor (v[0] and v[2]) xor (v[1] and v[2]) val temp2 = s0 + maj @@ -85,4 +66,24 @@ object `SHA-256` : HashFunction { h[i] += v[i] } } + + companion object { + private val initial = intArrayOf( + +0x6a09e667, -0x4498517b, + +0x3c6ef372, -0x5ab00ac6, + +0x510e527f, -0x64fa9774, + +0x1f83d9ab, +0x5be0cd19, + ) + + internal val c = intArrayOf( + +0x428a2f98, +0x71374491, -0x4a3f0431, -0x164a245b, +0x3956c25b, +0x59f111f1, -0x6dc07d5c, -0x54e3a12b, + -0x27f85568, +0x12835b01, +0x243185be, +0x550c7dc3, +0x72be5d74, -0x7f214e02, -0x6423f959, -0x3e640e8c, + -0x1b64963f, -0x1041b87a, +0x0fc19dc6, +0x240ca1cc, +0x2de92c6f, +0x4a7484aa, +0x5cb0a9dc, +0x76f988da, + -0x67c1aeae, -0x57ce3993, -0x4ffcd838, -0x40a68039, -0x391ff40d, -0x2a586eb9, +0x06ca6351, +0x14292967, + +0x27b70a85, +0x2e1b2138, +0x4d2c6dfc, +0x53380d13, +0x650a7354, +0x766a0abb, -0x7e3d36d2, -0x6d8dd37b, + -0x5d40175f, -0x57e599b5, -0x3db47490, -0x3893ae5d, -0x2e6d17e7, -0x2966f9dc, -0x0bf1ca7b, +0x106aa070, + +0x19a4c116, +0x1e376c08, +0x2748774c, +0x34b0bcb5, +0x391c0cb3, +0x4ed8aa4a, +0x5b9cca4f, +0x682e6ff3, + +0x748f82ee, +0x78a5636f, -0x7b3787ec, -0x7338fdf8, -0x6f410006, -0x5baf9315, -0x41065c09, -0x398e870e, + ) + } } diff --git a/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-384.kt b/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-384.kt index 0684fc0..b823656 100644 --- a/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-384.kt +++ b/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-384.kt @@ -1,30 +1,79 @@ package com.infendro.hash.sha2 +import com.infendro.bytes.bytearray.copyInto import com.infendro.bytes.longarray.copyInto import com.infendro.hash.HashFunction +import com.infendro.hash.sha2.`SHA-512`.Companion.c -object `SHA-384` : HashFunction { - override val bytes: Int - get() = 48 +class `SHA-384` : HashFunction(48, 128) { + private val h = initial.copyOf() - override val block: Int - get() = 128 + private val w = LongArray(80) + private val v = LongArray(8) - private val initial = longArrayOf( - -0x344462a23efa6128, +0x629a292a367cd507, - -0x6ea6fea5cf8f22e9, +0x152fecd8f70e5939, - +0x67332667ffc00b31, -0x714bb57897a7eaef, - -0x24f3d1f29b067059, +0x47b5481dbefa4fa4, - ) + override fun reset() { + super.reset() + initial.copyInto(h) + } - override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) { - val input = `SHA-512`.pad(value) - val h = initial.copyOf() + override fun process() = process(buffer.value) - for (i in input.indices step block) { - val block = input.sliceArray(i..<(i + block)) - `SHA-512`.process(h, block) + private fun finalize() { + buffer.append(0x80.toByte()) + if (buffer.free < 16) { + process(buffer.value) + buffer.reset() } + buffer.jumpTo(112) + buffer.append(length * 8) + + process(buffer.value) + } + + override fun digestInto(destination: ByteArray, destinationOffset: Int) { + finalize() h.copyInto(destination, destinationOffset, endIndex = 6) + reset() + } + + private fun process(block: ByteArray) { + block.copyInto(w) + for (i in 16..<80) { + val s0 = w[i - 15].rotateRight(1) xor w[i - 15].rotateRight(8) xor (w[i - 15] ushr 7) + val s1 = w[i - 2].rotateRight(19) xor w[i - 2].rotateRight(61) xor (w[i - 2] ushr 6) + w[i] = (w[i - 16] + s0 + w[i - 7] + s1) + } + h.copyInto(v) + + repeat(80) { i -> + val s0 = v[0].rotateRight(28) xor v[0].rotateRight(34) xor v[0].rotateRight(39) + val s1 = v[4].rotateRight(14) xor v[4].rotateRight(18) xor v[4].rotateRight(41) + val ch = (v[4] and v[5]) xor (v[4].inv() and v[6]) + val temp1 = v[7] + s1 + ch + c[i] + w[i] + val maj = (v[0] and v[1]) xor (v[0] and v[2]) xor (v[1] and v[2]) + val temp2 = s0 + maj + + v[7] = v[6] + v[6] = v[5] + v[5] = v[4] + v[4] = v[3] + temp1 + v[3] = v[2] + v[2] = v[1] + v[1] = v[0] + v[0] = temp1 + temp2 + } + + for (i in 0..<8) { + h[i] += v[i] + } + } + + companion object { + private val initial = longArrayOf( + -0x344462a23efa6128, +0x629a292a367cd507, + -0x6ea6fea5cf8f22e9, +0x152fecd8f70e5939, + +0x67332667ffc00b31, -0x714bb57897a7eaef, + -0x24f3d1f29b067059, +0x47b5481dbefa4fa4, + ) } } diff --git a/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-512.kt b/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-512.kt index 93df272..cd2f128 100644 --- a/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-512.kt +++ b/src/commonMain/kotlin/com/infendro/hash/sha2/SHA-512.kt @@ -1,79 +1,48 @@ package com.infendro.hash.sha2 import com.infendro.bytes.bytearray.copyInto -import com.infendro.bytes.long.copyInto import com.infendro.bytes.longarray.copyInto import com.infendro.hash.HashFunction -import com.infendro.hash.util.align -object `SHA-512` : HashFunction { - override val bytes: Int - get() = 64 +class `SHA-512` : HashFunction(64, 128) { + private val h = initial.copyOf() - override val block: Int - get() = 128 + private val w = LongArray(80) + private val v = LongArray(8) - private val initial = longArrayOf( - +0x6a09e667f3bcc908, -0x4498517a7b3558c5, - +0x3c6ef372fe94f82b, -0x5ab00ac5a0e2c90f, - +0x510e527fade682d1, -0x64fa9773d4c193e1, - +0x1f83d9abfb41bd6b, +0x5be0cd19137e2179, - ) + override fun reset() { + super.reset() + initial.copyInto(h) + } - private val c = longArrayOf( - +0x428a2f98d728ae22, +0x7137449123ef65cd, -0x4a3f043013b2c4d1, -0x164a245a7e762444, - +0x3956c25bf348b538, +0x59f111f1b605d019, -0x6dc07d5b50e6b065, -0x54e3a12a25927ee8, - -0x27f855675cfcfdbe, +0x12835b0145706fbe, +0x243185be4ee4b28c, +0x550c7dc3d5ffb4e2, - +0x72be5d74f27b896f, -0x7f214e01c4e9694f, -0x6423f958da38edcb, -0x3e640e8b3096d96c, - -0x1b64963e610eb52e, -0x1041b879c7b0da1d, +0x0fc19dc68b8cd5b5, +0x240ca1cc77ac9c65, - +0x2de92c6f592b0275, +0x4a7484aa6ea6e483, +0x5cb0a9dcbd41fbd4, +0x76f988da831153b5, - -0x67c1aead11992055, -0x57ce3992d24bcdf0, -0x4ffcd8376704dec1, -0x40a680384110f11c, - -0x391ff40cc257703e, -0x2a586eb86cf558db, +0x06ca6351e003826f, +0x142929670a0e6e70, - +0x27b70a8546d22ffc, +0x2e1b21385c26c926, +0x4d2c6dfc5ac42aed, +0x53380d139d95b3df, - +0x650a73548baf63de, +0x766a0abb3c77b2a8, -0x7e3d36d1b812511a, -0x6d8dd37aeb7dcac5, - -0x5d40175eb30efc9c, -0x57e599b443bdcfff, -0x3db4748f2f07686f, -0x3893ae5cf9ab41d0, - -0x2e6d17e62910ade8, -0x2966f9dbaa9a56f0, -0x0bf1ca7aa88edfd6, +0x106aa07032bbd1b8, - +0x19a4c116b8d2d0c8, +0x1e376c085141ab53, +0x2748774cdf8eeb99, +0x34b0bcb5e19b48a8, - +0x391c0cb3c5c95a63, +0x4ed8aa4ae3418acb, +0x5b9cca4f7763e373, +0x682e6ff3d6b2b8a3, - +0x748f82ee5defb2fc, +0x78a5636f43172f60, -0x7b3787eb5e0f548e, -0x7338fdf7e59bc614, - -0x6f410005dc9ce1d8, -0x5baf9314217d4217, -0x41065c084d3986eb, -0x398e870d1c8dacd5, - -0x35d8c13115d99e64, -0x2e794738de3f3df9, -0x15258229321f14e2, -0x0a82b08011912e88, - +0x06f067aa72176fba, +0x0a637dc5a2c898a6, +0x113f9804bef90dae, +0x1b710b35131c471b, - +0x28db77f523047d84, +0x32caab7b40c72493, +0x3c9ebe0a15c9bebc, +0x431d67c49c100d4c, - +0x4cc5d4becb3e42b6, +0x597f299cfc657e2a, +0x5fcb6fab3ad6faec, +0x6c44198c4a475817, - ) + override fun process() = process(buffer.value) - override fun hashInto(value: ByteArray, destination: ByteArray, destinationOffset: Int) { - val input = pad(value) - val h = initial.copyOf() - - for (i in input.indices step block) { - val block = input.sliceArray(i..<(i + block)) - process(h, block) + private fun finalize() { + buffer.append(0x80.toByte()) + if (buffer.free < 16) { + process(buffer.value) + buffer.reset() } + buffer.jumpTo(112) + buffer.append(length * 8) + + process(buffer.value) + } + + override fun digestInto(destination: ByteArray, destinationOffset: Int) { + finalize() h.copyInto(destination, destinationOffset) + reset() } - internal fun pad(value: ByteArray): ByteArray { - val length = value.size * 8L - - val size = (value.size + 17).align(block) - return ByteArray(size).also { - value.copyInto(it) - it[value.size] = -0x80 - length.copyInto(it, size - 8) - } - } - - internal fun process(h: LongArray, block: ByteArray) { - val w = LongArray(80) + private fun process(block: ByteArray) { block.copyInto(w) for (i in 16..<80) { val s0 = w[i - 15].rotateRight(1) xor w[i - 15].rotateRight(8) xor (w[i - 15] ushr 7) val s1 = w[i - 2].rotateRight(19) xor w[i - 2].rotateRight(61) xor (w[i - 2] ushr 6) w[i] = (w[i - 16] + s0 + w[i - 7] + s1) } - val v = h.copyOf() + h.copyInto(v) repeat(80) { i -> val s0 = v[0].rotateRight(28) xor v[0].rotateRight(34) xor v[0].rotateRight(39) @@ -97,4 +66,36 @@ object `SHA-512` : HashFunction { h[i] += v[i] } } + + companion object { + private val initial = longArrayOf( + +0x6a09e667f3bcc908, -0x4498517a7b3558c5, + +0x3c6ef372fe94f82b, -0x5ab00ac5a0e2c90f, + +0x510e527fade682d1, -0x64fa9773d4c193e1, + +0x1f83d9abfb41bd6b, +0x5be0cd19137e2179, + ) + + internal val c = longArrayOf( + +0x428a2f98d728ae22, +0x7137449123ef65cd, -0x4a3f043013b2c4d1, -0x164a245a7e762444, + +0x3956c25bf348b538, +0x59f111f1b605d019, -0x6dc07d5b50e6b065, -0x54e3a12a25927ee8, + -0x27f855675cfcfdbe, +0x12835b0145706fbe, +0x243185be4ee4b28c, +0x550c7dc3d5ffb4e2, + +0x72be5d74f27b896f, -0x7f214e01c4e9694f, -0x6423f958da38edcb, -0x3e640e8b3096d96c, + -0x1b64963e610eb52e, -0x1041b879c7b0da1d, +0x0fc19dc68b8cd5b5, +0x240ca1cc77ac9c65, + +0x2de92c6f592b0275, +0x4a7484aa6ea6e483, +0x5cb0a9dcbd41fbd4, +0x76f988da831153b5, + -0x67c1aead11992055, -0x57ce3992d24bcdf0, -0x4ffcd8376704dec1, -0x40a680384110f11c, + -0x391ff40cc257703e, -0x2a586eb86cf558db, +0x06ca6351e003826f, +0x142929670a0e6e70, + +0x27b70a8546d22ffc, +0x2e1b21385c26c926, +0x4d2c6dfc5ac42aed, +0x53380d139d95b3df, + +0x650a73548baf63de, +0x766a0abb3c77b2a8, -0x7e3d36d1b812511a, -0x6d8dd37aeb7dcac5, + -0x5d40175eb30efc9c, -0x57e599b443bdcfff, -0x3db4748f2f07686f, -0x3893ae5cf9ab41d0, + -0x2e6d17e62910ade8, -0x2966f9dbaa9a56f0, -0x0bf1ca7aa88edfd6, +0x106aa07032bbd1b8, + +0x19a4c116b8d2d0c8, +0x1e376c085141ab53, +0x2748774cdf8eeb99, +0x34b0bcb5e19b48a8, + +0x391c0cb3c5c95a63, +0x4ed8aa4ae3418acb, +0x5b9cca4f7763e373, +0x682e6ff3d6b2b8a3, + +0x748f82ee5defb2fc, +0x78a5636f43172f60, -0x7b3787eb5e0f548e, -0x7338fdf7e59bc614, + -0x6f410005dc9ce1d8, -0x5baf9314217d4217, -0x41065c084d3986eb, -0x398e870d1c8dacd5, + -0x35d8c13115d99e64, -0x2e794738de3f3df9, -0x15258229321f14e2, -0x0a82b08011912e88, + +0x06f067aa72176fba, +0x0a637dc5a2c898a6, +0x113f9804bef90dae, +0x1b710b35131c471b, + +0x28db77f523047d84, +0x32caab7b40c72493, +0x3c9ebe0a15c9bebc, +0x431d67c49c100d4c, + +0x4cc5d4becb3e42b6, +0x597f299cfc657e2a, +0x5fcb6fab3ad6faec, +0x6c44198c4a475817, + ) + } } diff --git a/src/commonMain/kotlin/com/infendro/hash/util/Buffer.kt b/src/commonMain/kotlin/com/infendro/hash/util/Buffer.kt new file mode 100644 index 0000000..7f35e10 --- /dev/null +++ b/src/commonMain/kotlin/com/infendro/hash/util/Buffer.kt @@ -0,0 +1,46 @@ +package com.infendro.hash.util + +import com.infendro.bytes.ByteOrder +import com.infendro.bytes.ByteOrder.BIG_ENDIAN +import com.infendro.bytes.int.copyInto +import com.infendro.bytes.long.copyInto + +internal class Buffer(val size: Int) { + val value = ByteArray(size) + var offset = 0 + + val free: Int + get() = size - offset + + fun isEmpty(): Boolean = offset == 0 + fun isNotEmpty(): Boolean = !isEmpty() + fun isFull(): Boolean = offset == size + + fun reset() { + value.fill(0x00) + offset = 0 + } + + fun jumpTo(offset: Int) { + this.offset = offset + } + + fun append(byte: Byte) { + value[offset++] = byte + } + + fun append(bytes: ByteArray, startIndex: Int = 0, endIndex: Int = bytes.size) { + bytes.copyInto(value, offset, startIndex, endIndex) + offset += (endIndex - startIndex) + } + + fun append(n: Int, order: ByteOrder = BIG_ENDIAN) { + n.copyInto(value, offset, order = order) + offset += 4 + } + + fun append(n: Long, order: ByteOrder = BIG_ENDIAN) { + n.copyInto(value, offset, order = order) + offset += 8 + } +} diff --git a/src/commonMain/kotlin/com/infendro/hash/util/Math.kt b/src/commonMain/kotlin/com/infendro/hash/util/Math.kt deleted file mode 100644 index d0ffcfc..0000000 --- a/src/commonMain/kotlin/com/infendro/hash/util/Math.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.infendro.hash.util - -internal fun Int.align(n: Int): Int = when { - n and (n - 1) == 0 -> (this + n - 1) and (n - 1).inv() - else -> ((this + n - 1) / n) * n -} diff --git a/src/commonTest/kotlin/com/infendro/hash/blake/Blake-224.kt b/src/commonTest/kotlin/com/infendro/hash/blake/Blake-224.kt index ea032a8..8186d9f 100644 --- a/src/commonTest/kotlin/com/infendro/hash/blake/Blake-224.kt +++ b/src/commonTest/kotlin/com/infendro/hash/blake/Blake-224.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `Blake-224_Test` { - val function = `Blake-224` + val function = `Blake-224`() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/blake/Blake-256.kt b/src/commonTest/kotlin/com/infendro/hash/blake/Blake-256.kt index 6a54dc1..dbd241f 100644 --- a/src/commonTest/kotlin/com/infendro/hash/blake/Blake-256.kt +++ b/src/commonTest/kotlin/com/infendro/hash/blake/Blake-256.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `Blake-256_Test` { - val function = `Blake-256` + val function = `Blake-256`() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/blake/Blake-384.kt b/src/commonTest/kotlin/com/infendro/hash/blake/Blake-384.kt index 30c837e..9197351 100644 --- a/src/commonTest/kotlin/com/infendro/hash/blake/Blake-384.kt +++ b/src/commonTest/kotlin/com/infendro/hash/blake/Blake-384.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `Blake-384_Test` { - val function = `Blake-384` + val function = `Blake-384`() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/blake/Blake-512.kt b/src/commonTest/kotlin/com/infendro/hash/blake/Blake-512.kt index 4b4d0b5..186cb26 100644 --- a/src/commonTest/kotlin/com/infendro/hash/blake/Blake-512.kt +++ b/src/commonTest/kotlin/com/infendro/hash/blake/Blake-512.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `Blake-512_Test` { - val function = `Blake-512` + val function = `Blake-512`() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-224.kt b/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-224.kt index 10770ae..73f8a00 100644 --- a/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-224.kt +++ b/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-224.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `SHA3-224_Test` { - val function = `SHA3-224` + val function = `SHA3-224`() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-256.kt b/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-256.kt index 7f0083d..3403900 100644 --- a/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-256.kt +++ b/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-256.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `SHA3-256_Test` { - val function = `SHA3-256` + val function = `SHA3-256`() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-384.kt b/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-384.kt index 88fd9fe..593d618 100644 --- a/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-384.kt +++ b/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-384.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `SHA3-384_Test` { - val function = `SHA3-384` + val function = `SHA3-384`() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-512.kt b/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-512.kt index 41dd4d6..6e9ed02 100644 --- a/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-512.kt +++ b/src/commonTest/kotlin/com/infendro/hash/keccak/sha3/SHA3-512.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `SHA3-512_Test` { - val function = `SHA3-512` + val function = `SHA3-512`() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/md/MD5.kt b/src/commonTest/kotlin/com/infendro/hash/md/MD5.kt index d03d05e..76a1dcb 100644 --- a/src/commonTest/kotlin/com/infendro/hash/md/MD5.kt +++ b/src/commonTest/kotlin/com/infendro/hash/md/MD5.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class MD5_Test { - val function = MD5 + val function = MD5() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/sha1/SHA1.kt b/src/commonTest/kotlin/com/infendro/hash/sha1/SHA1.kt index a5d0f5a..a5b3850 100644 --- a/src/commonTest/kotlin/com/infendro/hash/sha1/SHA1.kt +++ b/src/commonTest/kotlin/com/infendro/hash/sha1/SHA1.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class SHA1_Test { - val function = SHA1 + val function = SHA1() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-224.kt b/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-224.kt index ff3351a..d40d568 100644 --- a/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-224.kt +++ b/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-224.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `SHA-224_Test` { - val function = `SHA-224` + val function = `SHA-224`() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-256.kt b/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-256.kt index 83fad5d..4c80255 100644 --- a/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-256.kt +++ b/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-256.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `SHA-256_Test` { - val function = `SHA-256` + val function = `SHA-256`() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-384.kt b/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-384.kt index 754a632..1f2b0cd 100644 --- a/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-384.kt +++ b/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-384.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `SHA-384_Test` { - val function = `SHA-384` + val function = `SHA-384`() @Test fun `bytes and block`() { diff --git a/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-512.kt b/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-512.kt index f926638..1c96dc0 100644 --- a/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-512.kt +++ b/src/commonTest/kotlin/com/infendro/hash/sha2/SHA-512.kt @@ -5,7 +5,7 @@ import kotlin.test.assertContentEquals import kotlin.test.assertEquals class `SHA-512_Test` { - val function = `SHA-512` + val function = `SHA-512`() @Test fun `bytes and block`() { -- 2.49.1 From 6df6547ef439b5b4ced868a2224c329a5c0bc64f Mon Sep 17 00:00:00 2001 From: Infendro Date: Wed, 11 Mar 2026 18:53:57 +0100 Subject: [PATCH 2/4] Bump version to 1.7.0 --- README.md | 4 ++-- build.gradle.kts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 69e1318..cc605a7 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ repositories { } dependencies { - implementation("com.infendro:hash:1.6.0") + implementation("com.infendro:hash:1.7.0") } ``` @@ -31,6 +31,6 @@ dependencies { fun main() { // hash some value using SHA-256 val value = "Hello World!".encodeToByteArray() - val hash = `SHA-256`.hash(value) + val hash = `SHA-256`().hash(value) } ``` diff --git a/build.gradle.kts b/build.gradle.kts index 1a61ef3..ac300ff 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,7 +5,7 @@ import com.infendro.plugin.token import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl group = "com.infendro" -version = "1.6.0" +version = "1.7.0" plugins { alias(libs.plugins.infendro) -- 2.49.1 From e74c35ef40ee3180e2a9e1b2d238b3f049e4b531 Mon Sep 17 00:00:00 2001 From: Infendro Date: Wed, 11 Mar 2026 21:58:51 +0100 Subject: [PATCH 3/4] Refactor --- .../com/infendro/hash/{util => }/Buffer.kt | 11 +++++---- .../kotlin/com/infendro/hash/HashFunction.kt | 24 +++++-------------- .../com/infendro/hash/blake2/Blake2b.kt | 14 +++++++---- .../com/infendro/hash/blake2/Blake2s.kt | 14 +++++++---- 4 files changed, 32 insertions(+), 31 deletions(-) rename src/commonMain/kotlin/com/infendro/hash/{util => }/Buffer.kt (79%) diff --git a/src/commonMain/kotlin/com/infendro/hash/util/Buffer.kt b/src/commonMain/kotlin/com/infendro/hash/Buffer.kt similarity index 79% rename from src/commonMain/kotlin/com/infendro/hash/util/Buffer.kt rename to src/commonMain/kotlin/com/infendro/hash/Buffer.kt index 7f35e10..81ae8ee 100644 --- a/src/commonMain/kotlin/com/infendro/hash/util/Buffer.kt +++ b/src/commonMain/kotlin/com/infendro/hash/Buffer.kt @@ -1,11 +1,12 @@ -package com.infendro.hash.util +package com.infendro.hash import com.infendro.bytes.ByteOrder -import com.infendro.bytes.ByteOrder.BIG_ENDIAN import com.infendro.bytes.int.copyInto import com.infendro.bytes.long.copyInto -internal class Buffer(val size: Int) { +class Buffer( + val size: Int, +) { val value = ByteArray(size) var offset = 0 @@ -34,12 +35,12 @@ internal class Buffer(val size: Int) { offset += (endIndex - startIndex) } - fun append(n: Int, order: ByteOrder = BIG_ENDIAN) { + fun append(n: Int, order: ByteOrder = ByteOrder.BIG_ENDIAN) { n.copyInto(value, offset, order = order) offset += 4 } - fun append(n: Long, order: ByteOrder = BIG_ENDIAN) { + fun append(n: Long, order: ByteOrder = ByteOrder.BIG_ENDIAN) { n.copyInto(value, offset, order = order) offset += 8 } diff --git a/src/commonMain/kotlin/com/infendro/hash/HashFunction.kt b/src/commonMain/kotlin/com/infendro/hash/HashFunction.kt index ccf72ba..d9acd6b 100644 --- a/src/commonMain/kotlin/com/infendro/hash/HashFunction.kt +++ b/src/commonMain/kotlin/com/infendro/hash/HashFunction.kt @@ -1,7 +1,5 @@ package com.infendro.hash -import com.infendro.hash.util.Buffer - abstract class HashFunction( val bytes: Int, val block: Int, @@ -9,8 +7,8 @@ abstract class HashFunction( val bits: Int get() = bytes * 8 - internal val buffer = Buffer(block) - internal var length = 0L + protected val buffer = Buffer(block) + protected var length = 0L open fun reset() { buffer.reset() @@ -29,28 +27,18 @@ abstract class HashFunction( open fun update(bytes: ByteArray) { var i = 0 - // (partial) first block - val free = buffer.free - if (i + free < bytes.size) { + var free = buffer.free + while (i + free < bytes.size) { buffer.append(bytes, startIndex = i, endIndex = i + free) length += free process() buffer.reset() - i += free - } - - // full blocks - while (i + block < bytes.size) { - buffer.append(bytes, startIndex = i, endIndex = i + block) - length += block - process() - buffer.reset() + free = buffer.free i += block } - // (partial) last block buffer.append(bytes, startIndex = i) - length += bytes.size - i + length += (bytes.size - i) if (buffer.isFull()) { process() buffer.reset() diff --git a/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2b.kt b/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2b.kt index e0db34b..3ec27a2 100644 --- a/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2b.kt +++ b/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2b.kt @@ -38,15 +38,21 @@ class Blake2b(bytes: Int) : HashFunction(bytes, 128) { process() buffer.reset() } + var i = 0 - while (i + buffer.free < bytes.size) { - val remaining = buffer.free - buffer.append(bytes, startIndex = i, endIndex = i + remaining) + + var free = buffer.free + while (i + free < bytes.size) { + buffer.append(bytes, startIndex = i, endIndex = i + free) + length += free process() buffer.reset() - i += remaining + free = buffer.free + i += block } + buffer.append(bytes, startIndex = i) + length += (bytes.size - i) } override fun process() = compress(buffer.value, length, false) diff --git a/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2s.kt b/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2s.kt index ed8c74d..64d3830 100644 --- a/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2s.kt +++ b/src/commonMain/kotlin/com/infendro/hash/blake2/Blake2s.kt @@ -38,15 +38,21 @@ class Blake2s(bytes: Int) : HashFunction(bytes, 64) { process() buffer.reset() } + var i = 0 - while (i + buffer.free < bytes.size) { - val remaining = buffer.free - buffer.append(bytes, startIndex = i, endIndex = i + remaining) + + var free = buffer.free + while (i + free < bytes.size) { + buffer.append(bytes, startIndex = i, endIndex = i + free) + length += free process() buffer.reset() - i += remaining + free = buffer.free + i += block } + buffer.append(bytes, startIndex = i) + length += (bytes.size - i) } override fun process() = compress(buffer.value, length, false) -- 2.49.1 From 8400f76964d482485ae881165870a987e34eae6c Mon Sep 17 00:00:00 2001 From: Infendro Date: Wed, 11 Mar 2026 22:24:59 +0100 Subject: [PATCH 4/4] Upgrade Dependency --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9678a48..5d910a3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] infendro = "1.1.0" kotlin = "2.3.0" -bytes = "1.4.0" +bytes = "1.4.1" [plugins] infendro = { id = "com.infendro.plugin", version.ref = "infendro" } -- 2.49.1