diff --git a/multiplatform-crypto-delegated/src/nativeTest/kotlin/com/ionspin/kotlin/crypto/HelperTest.kt b/multiplatform-crypto-delegated/src/nativeTest/kotlin/com/ionspin/kotlin/crypto/HelperTest.kt new file mode 100644 index 0000000..c6cef5d --- /dev/null +++ b/multiplatform-crypto-delegated/src/nativeTest/kotlin/com/ionspin/kotlin/crypto/HelperTest.kt @@ -0,0 +1,75 @@ +package com.ionspin.kotlin.crypto + +import com.ionspin.kotlin.crypto.hash.encodeToUByteArray +import com.ionspin.kotlin.crypto.util.toHexString +import kotlinx.cinterop.* +import libsodium.* +import platform.posix.free +import kotlin.test.Ignore +import kotlin.test.Test + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 13-Jul-2020 + */ +class HelperTest { + @Ignore //Just used for debugging pure implementation + @Test + fun longSha256() { + for (target in 0L until 10L) { + generateForRounds256(target) + } + for (target in 0L until 16_777_216L step 1_000_000L) { + generateForRounds256(target) + } + generateForRounds256(16_777_216L) + } + + fun generateForRounds256(target: Long) { + val updateValue = + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno".encodeToUByteArray().toCValues() + val state = platform.linux.malloc(crypto_hash_sha256_state.size.convert())!! + .reinterpret() + + crypto_hash_sha256_init(state) + for (i in 0 until target) { + crypto_hash_sha256_update(state, updateValue, updateValue.size.convert()) + } + val result = UByteArray(32) + val resultPinned = result.pin() + crypto_hash_sha256_final(state, resultPinned.addressOf(0)) + println("$target to \"${result.toHexString()}\",") + free(state) + } + @Ignore //Just used for debugging pure implementation + @Test + fun longSha512() { + + for (target in 0L until 10L) { + generateForRounds512(target) + } + for (target in 0L until 16_777_216L step 1_000_000L) { + generateForRounds512(target) + } + generateForRounds512(16_777_216L) + } + + fun generateForRounds512(target: Long) { + println("Wut") + val updateValue = + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno".encodeToUByteArray().toCValues() + val state = platform.linux.malloc(crypto_hash_sha512_state.size.convert())!! + .reinterpret() + + crypto_hash_sha512_init(state) + for (i in 0 until target) { + crypto_hash_sha512_update(state, updateValue, updateValue.size.convert()) + } + val result = UByteArray(32) + val resultPinned = result.pin() + crypto_hash_sha512_final(state, resultPinned.addressOf(0)) + println("$target to \"${result.toHexString()}\",") + free(state) + } +} diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/Crypto.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/Crypto.kt index fa0068d..385aaec 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/Crypto.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/Crypto.kt @@ -135,7 +135,7 @@ class MultipartAuthenticatedEncryptor internal constructor(val key : SymmetricKe } override fun startEncryption(): MultipartEncryptionHeader { - return header + return header.copy() } override fun cleanup() { diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Pure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Pure.kt index 4b4a33a..0d5dad8 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Pure.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/XChaCha20Poly1305Pure.kt @@ -84,8 +84,6 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray) { calcNonce[1] = 0U calcNonce[2] = 0U calcNonce[3] = 0U - key.overwriteWithZeroes() - nonce.overwriteWithZeroes() println("Calckey-------=") calcKey.hexColumsPrint() println("Calckey-------=") diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256Pure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256Pure.kt index b09df53..eda11e8 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256Pure.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256Pure.kt @@ -69,7 +69,7 @@ class Sha256Pure : Sha256 { var h = iv.copyOf() - val expansionArray = createExpansionArray(inputMessage.size) + val expansionArray = createExpansionArray(inputMessage.size.toLong()) val chunks = ( inputMessage + @@ -179,15 +179,15 @@ class Sha256Pure : Sha256 { } - fun createExpansionArray(originalSizeInBytes: Int): UByteArray { + fun createExpansionArray(originalSizeInBytes: Long): UByteArray { val originalMessageSizeInBits = originalSizeInBytes * 8 //K such that L + 1 + K + 64 is a multiple of 512 val expandedRemainderOf512 = (originalMessageSizeInBits + BLOCK_SIZE_IN_BYTES + 1) % BLOCK_SIZE val zeroAddAmount = when (expandedRemainderOf512) { - 0 -> 0 - else -> (BLOCK_SIZE - expandedRemainderOf512) / 8 + 0L -> 0 + else -> ((BLOCK_SIZE - expandedRemainderOf512) / 8).toInt() } val expansionArray = UByteArray(zeroAddAmount + 1) { when (it) { @@ -231,12 +231,16 @@ class Sha256Pure : Sha256 { } var h = iv.copyOf() - var counter = 0 + var counter = 0L var bufferCounter = 0 var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U } + var digested = false fun update(data: String) { + if (digested) { + throw RuntimeException("This instance of updateable SHA256 was already finished once. You should use new instance") + } return update(data.encodeToUByteArray()) } @@ -247,6 +251,14 @@ class Sha256Pure : Sha256 { when { bufferCounter + data.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(data, bufferCounter) + bufferCounter + data.size == BLOCK_SIZE_IN_BYTES -> { + counter += BLOCK_SIZE_IN_BYTES + consumeBlock(data) + } + bufferCounter + data.size == BLOCK_SIZE_IN_BYTES -> { + counter += BLOCK_SIZE_IN_BYTES + consumeBlock(data) + } bufferCounter + data.size >= BLOCK_SIZE_IN_BYTES -> { val chunked = data.chunked(BLOCK_SIZE_IN_BYTES) chunked.forEach { chunk -> @@ -303,6 +315,7 @@ class Sha256Pure : Sha256 { h[5].toPaddedByteArray() + h[6].toPaddedByteArray() + h[7].toPaddedByteArray() + digested = true return digest } diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512Pure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512Pure.kt index ba0954e..2aba3af 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512Pure.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512Pure.kt @@ -137,7 +137,7 @@ class Sha512Pure : Sha512Multipart { var h = iv.copyOf() - val expansionArray = createExpansionArray(inputMessage.size) + val expansionArray = createExpansionArray(inputMessage.size.toLong()) val chunks = (inputMessage + expansionArray + (inputMessage.size * 8).toULong().toPadded128BitByteArray()).chunked( @@ -249,13 +249,13 @@ class Sha512Pure : Sha512Multipart { return h } - fun createExpansionArray(originalSizeInBytes: Int): UByteArray { + fun createExpansionArray(originalSizeInBytes: Long): UByteArray { val originalMessageSizeInBits = originalSizeInBytes * 8 val expandedRemainderOf1024 = (originalMessageSizeInBits + 129) % BLOCK_SIZE val zeroAddAmount = when (expandedRemainderOf1024) { - 0 -> 0 - else -> (BLOCK_SIZE - expandedRemainderOf1024) / 8 + 0L -> 0 + else -> ((BLOCK_SIZE - expandedRemainderOf1024) / 8).toInt() } val expansionArray = UByteArray(zeroAddAmount + 1) { when (it) { @@ -305,9 +305,10 @@ class Sha512Pure : Sha512Multipart { } var h = iv.copyOf() - var counter = 0 + var counter = 0L var bufferCounter = 0 var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U } + var digested = false fun update(data: String) { @@ -318,6 +319,9 @@ class Sha512Pure : Sha512Multipart { if (data.isEmpty()) { throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating") } + if (digested) { + throw RuntimeException("This instance of updateable SHA256 was already finished once. You should use new instance") + } when { bufferCounter + data.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(data, bufferCounter) @@ -377,6 +381,7 @@ class Sha512Pure : Sha512Multipart { h[5].toPaddedByteArray() + h[6].toPaddedByteArray() + h[7].toPaddedByteArray() + digested = true return digest } diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256UpdateableTest.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256UpdateableTest.kt index 1fbb800..8e18be7 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256UpdateableTest.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256UpdateableTest.kt @@ -16,7 +16,9 @@ package com.ionspin.kotlin.crypto.hash.sha +import com.ionspin.kotlin.crypto.hash.encodeToUByteArray import com.ionspin.kotlin.crypto.util.hexStringToUByteArray +import com.ionspin.kotlin.crypto.util.toHexString import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertTrue @@ -74,28 +76,59 @@ class Sha256UpdatableTest { for (i in 0 until 10000) { sha256.update("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") } - val resultDoubleBlock = sha256.digest() - val expectedResultForDoubleBlock = "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0" + val result = sha256.digest() + val expectedResult = "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0" assertTrue { - resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.hexStringToUByteArray()) + result.contentEquals(expectedResult.hexStringToUByteArray()) } } - @Ignore() - + @Ignore // Takes too long on native and js, but it now finishes correctly (and surprisingly quickly) on JVM @Test fun testWellKnownLonger() { - val sha256 = Sha256Pure() - for (i in 0 until 16_777_216) { - if (i % 10000 == 0) { - println("$i/16777216") + + //Obtained from libsodium runs + val roundsExpectedMap = mapOf( + 0 to "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + 1 to "2ff100b36c386c65a1afc462ad53e25479bec9498ed00aa5a04de584bc25301b", + 2 to "a9161e25f5e45eddf9a4cfa8b23456ba66be8098e474dc145bfceb2a99808b89", + 3 to "7bd37dc113762cb012b69105b6302638d414ebfcabde205669aab2b8bbe1f3c4", + 4 to "8fa352dc79b2482d1b55777b4932aff67f52e87bafdf9b9d45d57405a672de7a", + 5 to "a229ab9b1bce3f3281b9d06a592aadb7e765029472a6b9ebf5ecd6c45a31f2f6", + 6 to "09b49600c40293a902e52f89478d4886cf5771faabe38f53a63f3e9051b23e32", + 7 to "02372493fd93330a8371f1dea20f5c29e5411baa1b08ba6cb33c6ca745c364a1", + 8 to "e38b73d311ac89575d4e69a965763cdc618d0879142574231268f2b014fd0fec", + 9 to "37ed309f368eef8560c8cbfb1d8b619f3cc5460689984b8c687f46373ff62087", + 0 to "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + 1000000 to "7d9d6bcd4ffc9eb37899bbd7b5810e1762c55e4104f1b26b061349dbc67b5500", + 2000000 to "6b324148d357b3bb6549d339cec1a2a9ce45543eca0efcece78eecb3337097c2", + 3000000 to "3a52d0fb3383bca84dab55b2f41f0827d08325dbc65e3db4f15264597a8ac828", + 4000000 to "afd4587cf84fe7b6ffd7146a702d9b5dd4f0b754971eef36b7e0a6c4c3e84d58", + 5000000 to "d2b74e1a88652cce4450c3040decf40f9496ec92ad5e3443045922952db7894d", + 6000000 to "6e27932f14dfbbce11c9c9523e8b5a11616fd7864504d607639a98f81d5c0701", + 7000000 to "06e320b651981e13fb192805ff12a848815671d15885b636f99148b0089efabe", + 8000000 to "acc52c42ee92c046430f6e5454670e4f82da684a157eddd393a74c6883c881a5", + 9000000 to "493a7d3a40f3575bd5e5b4a5fec005f37392f3ce44565da314d173a062f499be", + 10000000 to "b3c6a178986ae0f5c81875ed68da2c72c8be961e9abc05e02982f723bd52a489", + 11000000 to "48387a098f9e919ea15ae5d1a347857f1043116e24bf9224b718120e0bfdaa9f", + 12000000 to "3ec9b9e64073f1c89f6d1c817a75610294d32c33d089af45540a352634e4a74a", + 13000000 to "cfb7c272408acdabf35c5cf1e2c0e2e81092d4e86bf8d51575a8a3baea2e7f8c", + 14000000 to "aa678e14a7d9a160177d52f153f30edd20c7f8a25fe36a3500fc1e11bd99029f", + 15000000 to "77c0a6256b17d6fdfd0926312ab168c6ac7357b11e93933e31f2f5fd5aefd400", + 16000000 to "28598fc683ac23a324e732ae398e093b5b80b03f01bdcf2f2d1b8e6bc76d183e", + 16777216 to "50e72a0e26442fe2552dc3938ac58658228c0cbfb1d2ca872ae435266fcd055e", + ) + val encoded = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno".encodeToUByteArray() + for (roundsExpected in roundsExpectedMap) { + val sha256 = Sha256Pure() + for (i in 0 until roundsExpected.key) { + sha256.update(encoded) + } + val result = sha256.digest() + assertTrue("Failed on ${roundsExpected.key} rounds, expected ${roundsExpected.value}, but got result ${result.toHexString()}") { + result.contentEquals(roundsExpected.value.hexStringToUByteArray()) } - sha256.update("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno") - } - val resultDoubleBlock = sha256.digest() - val expectedResultForDoubleBlock = "50e72a0e26442fe2552dc3938ac58658228c0cbfb1d2ca872ae435266fcd055e" - assertTrue { - resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.hexStringToUByteArray()) } + } } diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512UpdateableTest.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512UpdateableTest.kt index 91ad1d9..38aa37e 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512UpdateableTest.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512UpdateableTest.kt @@ -70,21 +70,18 @@ class Sha512UpdatableTest { } } - @Ignore() //Interestingly enough I'm not the only one having trouble with this test. + @Ignore // Takes too long on native and js, but it now finishes correctly (and surprisingly quickly) on JVM @Test fun testWellKnownLonger() { val sha512 = Sha512Pure() 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" + val result = sha512.digest() + val expectedResult = "b47c933421ea2db149ad6e10fce6c7f93d0752380180ffd7f4629a712134831d77be6091b819ed352c2967a2e2d4fa5050723c9630691f1a05a7281dbe6c1086" assertTrue { - resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.hexStringToUByteArray()) + result.contentEquals(expectedResult.hexStringToUByteArray()) } } }