103 lines
3.4 KiB
Kotlin
103 lines
3.4 KiB
Kotlin
package com.infendro.hash.md
|
|
|
|
import com.infendro.bytes.ByteOrder.LITTLE_ENDIAN
|
|
import com.infendro.bytes.bytearray.toIntArray
|
|
import com.infendro.bytes.intarray.copyInto
|
|
import com.infendro.bytes.ulong.copyInto
|
|
import com.infendro.hash.HashFunction
|
|
import com.infendro.hash.util.align
|
|
|
|
object MD5 : HashFunction {
|
|
override val bytes: Int
|
|
get() = 16
|
|
|
|
override val block: Int
|
|
get() = 64
|
|
|
|
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,
|
|
)
|
|
|
|
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)
|
|
}
|
|
h.copyInto(destination, destinationOffset, order = LITTLE_ENDIAN)
|
|
}
|
|
|
|
private fun pad(value: ByteArray): ByteArray {
|
|
val length = value.size.toULong() * 8UL
|
|
|
|
val size = (value.size + 9).align(block)
|
|
return ByteArray(size).also {
|
|
value.copyInto(it)
|
|
it[value.size] = 0x80U.toByte()
|
|
length.copyInto(it, size - 8, order = LITTLE_ENDIAN)
|
|
}
|
|
}
|
|
|
|
private fun process(h: IntArray, block: ByteArray) {
|
|
val w = block.toIntArray(LITTLE_ENDIAN)
|
|
val v = h.copyOf()
|
|
|
|
repeat(64) { i ->
|
|
var f = when (i) {
|
|
in 0..<16 -> (v[1] and v[2]) or (v[1].inv() and v[3])
|
|
in 16..<32 -> (v[3] and v[1]) or (v[3].inv() and v[2])
|
|
in 32..<48 -> v[1] xor v[2] xor v[3]
|
|
else -> v[2] xor (v[1] or v[3].inv())
|
|
}
|
|
val g = when (i) {
|
|
in 0..<16 -> i
|
|
in 16..<32 -> (5 * i + 1) % 16
|
|
in 32..<48 -> (3 * i + 5) % 16
|
|
else -> (7 * i) % 16
|
|
}
|
|
f += v[0] + c[i] + w[g]
|
|
v[0] = v[3]
|
|
v[3] = v[2]
|
|
v[2] = v[1]
|
|
v[1] += f.rotateLeft(shifts[i])
|
|
}
|
|
|
|
for (i in 0..<4) {
|
|
h[i] += v[i]
|
|
}
|
|
}
|
|
}
|