Skip to content
This repository has been archived by the owner on Jul 8, 2022. It is now read-only.

Commit

Permalink
WIP implement SHA512
Browse files Browse the repository at this point in the history
Fixes #617
  • Loading branch information
soywiz committed May 6, 2022
1 parent 6d5011d commit 5d51b9c
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 1 deletion.
5 changes: 4 additions & 1 deletion krypto/src/commonMain/kotlin/com/soywiz/krypto/PBKDF2.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ class PBKDF2 {
fun pbkdf2WithHmacSHA256(password: ByteArray, salt: ByteArray, iterationCount: Int, keySizeInBits: Int) =
pbkdf2(password, salt, iterationCount, keySizeInBits, SHA256())

fun pbkdf2WithHmacSHA512(password: ByteArray, salt: ByteArray, iterationCount: Int, keySizeInBits: Int) =
pbkdf2(password, salt, iterationCount, keySizeInBits, SHA512())

private fun Int.toByteArray(): ByteArray {
return byteArrayOf(
(this shr 24 and 0xff).toByte(),
Expand All @@ -19,7 +22,7 @@ class PBKDF2 {
)
}

private fun pbkdf2(password: ByteArray, salt: ByteArray, iterationCount: Int, keySizeInBits: Int, hasher: Hasher): ByteArray {
fun pbkdf2(password: ByteArray, salt: ByteArray, iterationCount: Int, keySizeInBits: Int, hasher: Hasher): ByteArray {
val hLen = hasher.digestSize
val blockSize = keySizeInBits / hLen
val outSize = keySizeInBits / 8
Expand Down
185 changes: 185 additions & 0 deletions krypto/src/commonMain/kotlin/com/soywiz/krypto/SHA512.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package com.soywiz.krypto

import com.soywiz.krypto.internal.arraycopy

// https://git.suckless.org/sbase/file/libutil/sha512.c.html
// https://git.suckless.org/sbase/file/sha512.h.html
/* public domain sha512 implementation based on fips180-3 */
@OptIn(ExperimentalUnsignedTypes::class)
class SHA512 : SHA(chunkSize = 64, digestSize = 64) {
companion object : HasherFactory({ SHA512() }) {
private const val SHA512_DIGEST_LENGTH = 64

private fun ror(n: ULong, k: Int): ULong = (n shr k) or (n shl (64 - k))
private fun Ch(x: ULong, y: ULong, z: ULong) = (z xor (x and (y xor z)))
private fun Maj(x: ULong, y: ULong, z: ULong) = ((x and y) or (z and (x or y)))
private fun S0(x: ULong) = (ror(x, 28) xor ror(x, 34) xor ror(x, 39))
private fun S1(x: ULong) = (ror(x, 14) xor ror(x, 18) xor ror(x, 41))
private fun R0(x: ULong) = (ror(x, 1) xor ror(x, 8) xor (x shr 7))
private fun R1(x: ULong) = (ror(x, 19) xor ror(x, 61) xor (x shr 6))

private val K = ulongArrayOf(
0x428a2f98d728ae22uL, 0x7137449123ef65cduL, 0xb5c0fbcfec4d3b2fuL, 0xe9b5dba58189dbbcuL,
0x3956c25bf348b538uL, 0x59f111f1b605d019uL, 0x923f82a4af194f9buL, 0xab1c5ed5da6d8118uL,
0xd807aa98a3030242uL, 0x12835b0145706fbeuL, 0x243185be4ee4b28cuL, 0x550c7dc3d5ffb4e2uL,
0x72be5d74f27b896fuL, 0x80deb1fe3b1696b1uL, 0x9bdc06a725c71235uL, 0xc19bf174cf692694uL,
0xe49b69c19ef14ad2uL, 0xefbe4786384f25e3uL, 0x0fc19dc68b8cd5b5uL, 0x240ca1cc77ac9c65uL,
0x2de92c6f592b0275uL, 0x4a7484aa6ea6e483uL, 0x5cb0a9dcbd41fbd4uL, 0x76f988da831153b5uL,
0x983e5152ee66dfabuL, 0xa831c66d2db43210uL, 0xb00327c898fb213fuL, 0xbf597fc7beef0ee4uL,
0xc6e00bf33da88fc2uL, 0xd5a79147930aa725uL, 0x06ca6351e003826fuL, 0x142929670a0e6e70uL,
0x27b70a8546d22ffcuL, 0x2e1b21385c26c926uL, 0x4d2c6dfc5ac42aeduL, 0x53380d139d95b3dfuL,
0x650a73548baf63deuL, 0x766a0abb3c77b2a8uL, 0x81c2c92e47edaee6uL, 0x92722c851482353buL,
0xa2bfe8a14cf10364uL, 0xa81a664bbc423001uL, 0xc24b8b70d0f89791uL, 0xc76c51a30654be30uL,
0xd192e819d6ef5218uL, 0xd69906245565a910uL, 0xf40e35855771202auL, 0x106aa07032bbd1b8uL,
0x19a4c116b8d2d0c8uL, 0x1e376c085141ab53uL, 0x2748774cdf8eeb99uL, 0x34b0bcb5e19b48a8uL,
0x391c0cb3c5c95a63uL, 0x4ed8aa4ae3418acbuL, 0x5b9cca4f7763e373uL, 0x682e6ff3d6b2b8a3uL,
0x748f82ee5defb2fcuL, 0x78a5636f43172f60uL, 0x84c87814a1f0ab72uL, 0x8cc702081a6439ecuL,
0x90befffa23631e28uL, 0xa4506cebde82bde9uL, 0xbef9a3f7b2c67915uL, 0xc67178f2e372532buL,
0xca273eceea26619cuL, 0xd186b8c721c0c207uL, 0xeada7dd6cde0eb1euL, 0xf57d4f7fee6ed178uL,
0x06f067aa72176fbauL, 0x0a637dc5a2c898a6uL, 0x113f9804bef90daeuL, 0x1b710b35131c471buL,
0x28db77f523047d84uL, 0x32caab7b40c72493uL, 0x3c9ebe0a15c9bebcuL, 0x431d67c49c100d4cuL,
0x4cc5d4becb3e42b6uL, 0x597f299cfc657e2auL, 0x5fcb6fab3ad6faecuL, 0x6c44198c4a475817uL
)
}

var len = 0uL
val h = ULongArray(8)
val buf = UByteArray(128)

init {
coreReset()
}

override fun coreReset() {
len = 0uL
h[0] = 0x6a09e667f3bcc908uL
h[1] = 0xbb67ae8584caa73buL
h[2] = 0x3c6ef372fe94f82buL
h[3] = 0xa54ff53a5f1d36f1uL
h[4] = 0x510e527fade682d1uL
h[5] = 0x9b05688c2b3e6c1fuL
h[6] = 0x1f83d9abfb41bd6buL
h[7] = 0x5be0cd19137e2179uL
buf.fill(0u)
}

override fun coreUpdate(chunk: ByteArray) {
sha512_update(chunk.asUByteArray(), 0, chunk.size)
}

override fun coreDigest(out: ByteArray) {
sha512_sum(out.asUByteArray())
}

private fun processblock(buf: UByteArray, p: Int = 0) {
val W = ULongArray(80)

for (i in 0 until 16) {
var v = buf[p + 8 * i].toULong() shl 56
v = v or buf[p + 8 * i + 1].toULong() shl 48
v = v or buf[p + 8 * i + 2].toULong() shl 40
v = v or buf[p + 8 * i + 3].toULong() shl 32
v = v or buf[p + 8 * i + 4].toULong() shl 24
v = v or buf[p + 8 * i + 5].toULong() shl 16
v = v or buf[p + 8 * i + 6].toULong() shl 8
v = v or buf[p + 8 * i + 7].toULong()
W[i] = v
}
for (i in 16 until 80) {
W[i] = R1(W[i - 2]) + W[i - 7] + R0(W[i - 15]) + W[i - 16]
}
var a = this.h[0]
var b = this.h[1]
var c = this.h[2]
var d = this.h[3]
var e = this.h[4]
var f = this.h[5]
var g = this.h[6]
var h = this.h[7]
for (i in 0 until 80) {
val t1 = h + S1(e) + Ch(e, f, g) + K[i] + W[i]
val t2 = S0(a) + Maj(a, b, c)
h = g
g = f
f = e
e = d + t1
d = c
c = b
b = a
a = t1 + t2
}
this.h[0] += a
this.h[1] += b
this.h[2] += c
this.h[3] += d
this.h[4] += e
this.h[5] += f
this.h[6] += g
this.h[7] += h
}

private fun pad() {
var r = (this.len % 128uL).toInt()

this.buf[r++] = 0x80u
if (r > 112) {
this.buf.fill(0u, r, 128)
r = 0
processblock(buf)
}
this.buf.fill(0u, r, 120)
this.len *= 8uL
this.buf[120] = (this.len shr 56).toUByte()
this.buf[121] = (this.len shr 48).toUByte()
this.buf[122] = (this.len shr 40).toUByte()
this.buf[123] = (this.len shr 32).toUByte()
this.buf[124] = (this.len shr 24).toUByte()
this.buf[125] = (this.len shr 16).toUByte()
this.buf[126] = (this.len shr 8).toUByte()
this.buf[127] = (this.len shr 0).toUByte()
processblock(this.buf)
}

private fun sha512_sum(md: UByteArray) {
sha512_sum_n(md, 8)
}

private fun sha512_sum_n(md: UByteArray, n: Int) {
pad()
for (i in 0 until n) {
md[8 * i + 0] = (h[i] shr 56).toUByte()
md[8 * i + 1] = (h[i] shr 48).toUByte()
md[8 * i + 2] = (h[i] shr 40).toUByte()
md[8 * i + 3] = (h[i] shr 32).toUByte()
md[8 * i + 4] = (h[i] shr 24).toUByte()
md[8 * i + 5] = (h[i] shr 16).toUByte()
md[8 * i + 6] = (h[i] shr 8).toUByte()
md[8 * i + 7] = (h[i] shr 0).toUByte()
}
}

private fun sha512_update(m: UByteArray, p: Int, len: Int) {
var len = len
var p = p
val r = (len % 128)

this.len += len.toULong()
if (r != 0) {
if (len < 128 - r) {
arraycopy(m.asByteArray(), p, this.buf.asByteArray(), r, len)
return
}
arraycopy(m.asByteArray(), p, buf.asByteArray(), r, 128-r)
len -= 128 - r
p += 128 - r
processblock(this.buf)
}
while (len >= 128) {
processblock(m, p)
len -= 128
p += 128
}
arraycopy(m.asByteArray(), p, buf.asByteArray(), 0, len)
}

}
22 changes: 22 additions & 0 deletions krypto/src/commonTest/kotlin/com/soywiz/krypto/SHA512Test.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.soywiz.krypto

import kotlin.test.Test
import kotlin.test.assertEquals

class SHA512Test {
@Test
fun testEmpty() {
assertEquals(
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
SHA512.digest("".encodeToByteArray()).hexLower
)
}

@Test
fun testA() {
assertEquals(
"1f40fc92da241694750979ee6cf582f2d5d7d28e18335de05abc54d0560e0f5302860c652bf08d560252aa5e74210546f369fbbbce8c12cfc7957b2652fe9a75",
SHA512.digest("a".encodeToByteArray()).hexLower
)
}
}

0 comments on commit 5d51b9c

Please sign in to comment.