package com.infendro.hash.sha1 import com.infendro.bytes.bytearray.copyInto import com.infendro.bytes.intarray.copyInto import com.infendro.bytes.ulong.copyInto import com.infendro.hash.HashFunction import com.infendro.hash.util.ceilPow2 object SHA1 : HashFunction { override val bytes: Int get() = 20 override val block: Int get() = 64 private val initial = uintArrayOf( 0x67452301U, 0xefcdaB89U, 0x98badcfeU, 0x10325476U, 0xc3d2e1f0U, ).asIntArray() private val k = uintArrayOf( 0x5a827999U, 0x6ed9eba1U, 0x8f1bbcdcU, 0xca62c1d6U, ).asIntArray() override fun hashInto(value: ByteArray, destination: ByteArray) { val input = pad(value) val h = initial.copyOf() val w = IntArray(80) val v = IntArray(5) for (i in input.indices step block) { process(h, input, i, w, v) } h.copyInto(destination) } private fun pad(value: ByteArray): ByteArray { val length = value.size.toULong() * 8UL val size = ceilPow2(value.size + 9, block) val result = ByteArray(size) value.copyInto(result) result[value.size] = 0x80U.toByte() length.copyInto(result, result.size - 8) return result } private fun process(h: IntArray, value: ByteArray, offset: Int, w: IntArray, v: IntArray) { value.copyInto(w, startIndex = offset, endIndex = offset + block) for (i in 16..<80) { w[i] = (w[i - 3] xor w[i - 8] xor w[i - 14] xor w[i - 16]).rotateLeft(1) } h.copyInto(v) repeat(80) { i -> val f = when (i) { in 0..<20 -> (v[1] and v[2]) or (v[1].inv() and v[3]) in 20..<40 -> v[1] xor v[2] xor v[3] in 40..<60 -> (v[1] and v[2]) or (v[1] and v[3]) or (v[2] and v[3]) else -> v[1] xor v[2] xor v[3] } val temp = v[0].rotateLeft(5) + f + v[4] + k[i / 20] + w[i] v[4] = v[3] v[3] = v[2] v[2] = v[1].rotateLeft(30) v[1] = v[0] v[0] = temp } for (i in 0..<5) { h[i] += v[i] } } }