Tests cleanup

This commit is contained in:
Ugljesa Jovanovic 2019-07-21 00:55:11 +02:00
parent 187282232e
commit 995cb23176
No known key found for this signature in database
GPG Key ID: 46D004C9820EBB98
5 changed files with 345 additions and 93 deletions

View File

@ -30,6 +30,8 @@ import com.ionspin.kotlin.crypto.rotateRight
class Sha512 : Hash {
companion object {
const val BLOCK_SIZE = 1024
const val BLOCK_SIZE_IN_BYTES = 128
const val CHUNK_SIZE = 80
const val ULONG_MASK = 0xFFFFFFFFFFFFFFFFUL
val k = arrayOf(
@ -133,102 +135,29 @@ class Sha512 : Hash {
fun digest(message: Array<UByte>): Array<UByte> {
var h0 = 0x6a09e667f3bcc908UL
var h1 = 0xbb67ae8584caa73bUL
var h2 = 0x3c6ef372fe94f82bUL
var h3 = 0xa54ff53a5f1d36f1UL
var h4 = 0x510e527fade682d1UL
var h5 = 0x9b05688c2b3e6c1fUL
var h6 = 0x1f83d9abfb41bd6bUL
var h7 = 0x5be0cd19137e2179UL
var h = iv.copyOf()
val originalMessageSizeInBits = message.size * 8
//K such that L + 1 + K + 64 is a multiple of 512
val expandedRemainderOf1024 = (originalMessageSizeInBits + 130) % BLOCK_SIZE
val zeroAddAmount = when (expandedRemainderOf1024) {
0 -> 0
else -> (BLOCK_SIZE - expandedRemainderOf1024) / 8
}
val expansionArray = Array<UByte>(zeroAddAmount + 1) {
when (it) {
0 -> 0b10000000U
else -> 0U
}
}
val expansionArray = createExpansionArray(message.size)
val chunks =
(message + expansionArray + originalMessageSizeInBits.toULong().toPadded128BitByteArray()).chunked(128)
(message + expansionArray + (message.size * 8).toULong().toPadded128BitByteArray()).chunked(
BLOCK_SIZE_IN_BYTES)
chunks.forEach { chunk ->
val w = Array<ULong>(80) {
when (it) {
in 0 until 16 -> {
var collected = (chunk[(it * 8)].toULong() shl 56) +
(chunk[(it * 8) + 1].toULong() shl 48) +
(chunk[(it * 8) + 2].toULong() shl 40) +
(chunk[(it * 8) + 3].toULong() shl 32) +
(chunk[(it * 8) + 4].toULong() shl 24) +
(chunk[(it * 8) + 5].toULong() shl 16) +
(chunk[(it * 8) + 6].toULong() shl 8) +
(chunk[(it * 8) + 7].toULong())
collected
}
else -> 0UL
}
}
for (i in 16 until 80) {
val s0 = scheduleSigma0(w[i - 15])
val s1 = scheduleSigma1(w[i - 2])
w[i] = w[i - 16] + s0 + w[i - 7] + s1
}
var a = h0
var b = h1
var c = h2
var d = h3
var e = h4
var f = h5
var g = h6
var h = h7
for (i in 0 until 80) {
val s1 = compressionSigma1(e)
val ch = ch(e, f, g)
val temp1 = h + s1 + ch + k[i] + w[i]
val s0 = compressionSigma0(a)
val maj = maj(a, b, c)
val temp2 = s0 + maj
h = g
g = f
f = e
e = d + temp1
d = c
c = b
b = a
a = temp1 + temp2
}
h0 += a
h1 += b
h2 += c
h3 += d
h4 += e
h5 += f
h6 += g
h7 += h
val w = expandChunk(chunk)
mix(h, w)
}
val digest = h0.toPaddedByteArray() +
h1.toPaddedByteArray() +
h2.toPaddedByteArray() +
h3.toPaddedByteArray() +
h4.toPaddedByteArray() +
h5.toPaddedByteArray() +
h6.toPaddedByteArray() +
h7.toPaddedByteArray()
val digest =
h[0].toPaddedByteArray() +
h[1].toPaddedByteArray() +
h[2].toPaddedByteArray() +
h[3].toPaddedByteArray() +
h[4].toPaddedByteArray() +
h[5].toPaddedByteArray() +
h[6].toPaddedByteArray() +
h[7].toPaddedByteArray()
return digest
}
@ -256,6 +185,86 @@ class Sha512 : Hash {
return ((x and y) xor (x and z) xor (y and z))
}
private fun expandChunk(chunk: Array<UByte>): Array<ULong> {
val w = Array<ULong>(CHUNK_SIZE) {
when (it) {
in 0 until 16 -> {
var collected = (chunk[(it * 8)].toULong() shl 56) +
(chunk[(it * 8) + 1].toULong() shl 48) +
(chunk[(it * 8) + 2].toULong() shl 40) +
(chunk[(it * 8) + 3].toULong() shl 32) +
(chunk[(it * 8) + 4].toULong() shl 24) +
(chunk[(it * 8) + 5].toULong() shl 16) +
(chunk[(it * 8) + 6].toULong() shl 8) +
(chunk[(it * 8) + 7].toULong())
collected
}
else -> 0UL
}
}
for (i in 16 until CHUNK_SIZE) {
val s0 = scheduleSigma0(w[i - 15])
val s1 = scheduleSigma1(w[i - 2])
w[i] = w[i - 16] + s0 + w[i - 7] + s1
}
return w
}
private fun mix(h: Array<ULong>, w: Array<ULong>): Array<ULong> {
var paramA = h[0]
var paramB = h[1]
var paramC = h[2]
var paramD = h[3]
var paramE = h[4]
var paramF = h[5]
var paramG = h[6]
var paramH = h[7]
for (i in 0 until CHUNK_SIZE) {
val s1 = compressionSigma1(paramE)
val ch = ch(paramE, paramF, paramG)
val temp1 = paramH + s1 + ch + k[i] + w[i]
val s0 = compressionSigma0(paramA)
val maj = maj(paramA, paramB, paramC)
val temp2 = s0 + maj
paramH = paramG
paramG = paramF
paramF = paramE
paramE = paramD + temp1
paramD = paramC
paramC = paramB
paramB = paramA
paramA = temp1 + temp2
}
h[0] += paramA
h[1] += paramB
h[2] += paramC
h[3] += paramD
h[4] += paramE
h[5] += paramF
h[6] += paramG
h[7] += paramH
return h
}
fun createExpansionArray(originalSizeInBytes : Int) : Array<UByte> {
val originalMessageSizeInBits = originalSizeInBytes * 8
val expandedRemainderOf1024 = (originalMessageSizeInBits + 129) % BLOCK_SIZE
val zeroAddAmount = when (expandedRemainderOf1024) {
0 -> 0
else -> (BLOCK_SIZE - expandedRemainderOf1024) / 8
}
val expansionArray = Array<UByte>(zeroAddAmount + 1) {
when (it) {
0 -> 0b10000000U
else -> 0U
}
}
return expansionArray
}
private fun ULong.toPaddedByteArray(): Array<UByte> {
val byteMask = 0xFFUL
@ -294,5 +303,85 @@ class Sha512 : Hash {
}
}
var h = iv.copyOf()
var counter = 0
var bufferCounter = 0
var buffer = Array<UByte>(BLOCK_SIZE_IN_BYTES) { 0U }
@ExperimentalStdlibApi
fun update(message: String) {
return update(message.encodeToByteArray().map { it.toUByte() }.toTypedArray())
}
fun update(array: Array<UByte>) {
if (array.isEmpty()) {
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating")
}
when {
bufferCounter + array.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(array, bufferCounter)
bufferCounter + array.size >= BLOCK_SIZE_IN_BYTES -> {
val chunked = array.chunked(BLOCK_SIZE_IN_BYTES)
chunked.forEach { chunk ->
if (bufferCounter + chunk.size < BLOCK_SIZE_IN_BYTES) {
appendToBuffer(chunk, bufferCounter)
} else {
chunk.copyInto(
destination = buffer,
destinationOffset = bufferCounter,
startIndex = 0,
endIndex = BLOCK_SIZE_IN_BYTES - bufferCounter
)
counter += BLOCK_SIZE_IN_BYTES
consumeBlock(buffer)
buffer = Array<UByte>(BLOCK_SIZE_IN_BYTES) {
when (it) {
in (0 until (chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter))) -> {
chunk[it + (BLOCK_SIZE_IN_BYTES - bufferCounter)]
}
else -> {
0U
}
}
}
bufferCounter = chunk.size - (BLOCK_SIZE_IN_BYTES - bufferCounter)
}
}
}
}
}
private fun consumeBlock(block: Array<UByte>) {
val w = expandChunk(block)
mix(h, w).copyInto(h)
}
fun digest() : Array<UByte> {
val length = counter + bufferCounter
val expansionArray = createExpansionArray(length)
val finalBlock = buffer.copyOfRange(0, bufferCounter) + expansionArray + (length * 8).toULong().toPadded128BitByteArray()
finalBlock.chunked(BLOCK_SIZE_IN_BYTES).forEach {
consumeBlock(it)
}
val digest = h[0].toPaddedByteArray() +
h[1].toPaddedByteArray() +
h[2].toPaddedByteArray() +
h[3].toPaddedByteArray() +
h[4].toPaddedByteArray() +
h[5].toPaddedByteArray() +
h[6].toPaddedByteArray() +
h[7].toPaddedByteArray()
return digest
}
private fun appendToBuffer(array: Array<UByte>, start: Int) {
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
bufferCounter += array.size
}
}

View File

@ -50,4 +50,31 @@ class Sha256Test {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
}
}
@ExperimentalStdlibApi
@Test
fun testWellKnown3() { //It's good that I'm consistent with names.
val resultDoubleBlock = Sha256.digest(message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
println(resultDoubleBlock.map{ it.toString(16)}.joinToString(separator = ""))
val expectedResultForDoubleBlock = "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"
assertTrue {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
}
}
@ExperimentalStdlibApi
@Test
fun testWellKnownLong() {
val inputBuilder = StringBuilder()
for (i in 0 until 1000000) {
inputBuilder.append("a")
}
val resultDoubleBlock = Sha256.digest(message = (inputBuilder.toString()).encodeToByteArray().map { it.toUByte() }.toTypedArray())
val expectedResultForDoubleBlock = "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"
assertTrue {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
}
}
}

View File

@ -17,6 +17,7 @@
package com.ionspin.kotlin.crypto.sha
import com.ionspin.kotlin.crypto.chunked
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.test.assertTrue
@ -54,4 +55,50 @@ class Sha256UpdateableTest {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
}
}
@ExperimentalStdlibApi
@Test
fun testWellKnown3() { //It's good that I'm consistent with names.
val sha256 = Sha256()
sha256.update(message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
val resultDoubleBlock = sha256.digest()
println(resultDoubleBlock.map{ it.toString(16)}.joinToString(separator = ""))
val expectedResultForDoubleBlock = "cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"
assertTrue {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
}
}
@ExperimentalStdlibApi
@Test
fun testWellKnownLong() {
val sha256 = Sha256()
for (i in 0 until 10000) {
sha256.update("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
}
val resultDoubleBlock = sha256.digest()
val expectedResultForDoubleBlock = "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"
assertTrue {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
}
}
@Ignore()
@ExperimentalStdlibApi
@Test
fun testWellKnownLonger() {
val sha256 = Sha256()
for (i in 0 until 16_777_216) {
if (i % 10000 == 0) {
println("$i/16777216")
}
sha256.update("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno")
}
val resultDoubleBlock = sha256.digest()
val expectedResultForDoubleBlock = "50e72a0e26442fe2552dc3938ac58658228c0cbfb1d2ca872ae435266fcd055e"
assertTrue {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
}
}
//50e72a0e 26442fe2 552dc393 8ac58658 228c0cbf b1d2ca87 2ae43526 6fcd055e
}

View File

@ -44,12 +44,12 @@ class Sha512Test {
@ExperimentalUnsignedTypes
@ExperimentalStdlibApi
@Test
fun testWellKnownDoubleBlock() {
val resultDoubleBlock = Sha512.digest(message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" +
"hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
fun testWellKnown3() {
val sha512 = Sha512()
sha512.update(message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
val resultDoubleBlock = sha512.digest()
val expectedResultForDoubleBlock = "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" +
"501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"
"501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"
assertTrue {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
}

View File

@ -0,0 +1,89 @@
/*
* Copyright 2019 Ugljesa Jovanovic
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ionspin.kotlin.crypto.sha
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.test.assertTrue
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 21-Jul-2019
*/
class Sha512UpdateableTest {
@ExperimentalStdlibApi
@Test
fun testWellKnownValue() {
val sha512 = Sha512()
sha512.update("abc")
val result = sha512.digest()
val expectedResult = "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" +
"2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
assertTrue {
result.contentEquals(expectedResult.chunked(2).map { it.toUByte(16) }.toTypedArray())
}
}
@ExperimentalStdlibApi
@Test
fun testWellKnownDoubleBlock() {
val sha512 = Sha512()
sha512.update(message = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu")
val resultDoubleBlock = sha512.digest()
println(resultDoubleBlock.map{ it.toString(16)}.joinToString(separator = ""))
val expectedResultForDoubleBlock = "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" +
"501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"
assertTrue {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
}
}
@ExperimentalStdlibApi
@Test
fun testWellKnownLong() {
val sha512 = Sha512()
for (i in 0 until 10000) {
sha512.update("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
}
val resultDoubleBlock = sha512.digest()
val expectedResultForDoubleBlock = "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"
assertTrue {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
}
}
@Ignore() //Interestingly enough I'm not the only one having trouble with this test.
@ExperimentalStdlibApi
@Test
fun testWellKnownLonger() {
val sha512 = Sha512()
for (i in 0 until 16_777_216) {
if (i % 10000 == 0) {
println("$i/16777216")
}
sha512.update("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno")
}
val resultDoubleBlock = sha512.digest()
val expectedResultForDoubleBlock = "b47c933421ea2db149ad6e10fce6c7f93d0752380180ffd7f4629a712134831d77be6091b819ed352c2967a2e2d4fa5050723c9630691f1a05a7281dbe6c1086"
assertTrue {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.chunked(2).map { it.toUByte(16) }.toTypedArray())
}
}
}