Fixed sha256/512 implementation updateable version counter overflowing because it was int instead of long, fixed nonce overwriting in pure xchacha20poly1305 implementation

This commit is contained in:
Ugljesa Jovanovic 2020-07-13 23:39:36 +02:00 committed by Ugljesa Jovanovic
parent 52b6a4ad8e
commit 1dc423a509
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
7 changed files with 156 additions and 35 deletions

View File

@ -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_state>()
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_state>()
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)
}
}

View File

@ -135,7 +135,7 @@ class MultipartAuthenticatedEncryptor internal constructor(val key : SymmetricKe
} }
override fun startEncryption(): MultipartEncryptionHeader { override fun startEncryption(): MultipartEncryptionHeader {
return header return header.copy()
} }
override fun cleanup() { override fun cleanup() {

View File

@ -84,8 +84,6 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray) {
calcNonce[1] = 0U calcNonce[1] = 0U
calcNonce[2] = 0U calcNonce[2] = 0U
calcNonce[3] = 0U calcNonce[3] = 0U
key.overwriteWithZeroes()
nonce.overwriteWithZeroes()
println("Calckey-------=") println("Calckey-------=")
calcKey.hexColumsPrint() calcKey.hexColumsPrint()
println("Calckey-------=") println("Calckey-------=")

View File

@ -69,7 +69,7 @@ class Sha256Pure : Sha256 {
var h = iv.copyOf() var h = iv.copyOf()
val expansionArray = createExpansionArray(inputMessage.size) val expansionArray = createExpansionArray(inputMessage.size.toLong())
val chunks = ( val chunks = (
inputMessage + inputMessage +
@ -179,15 +179,15 @@ class Sha256Pure : Sha256 {
} }
fun createExpansionArray(originalSizeInBytes: Int): UByteArray { fun createExpansionArray(originalSizeInBytes: Long): UByteArray {
val originalMessageSizeInBits = originalSizeInBytes * 8 val originalMessageSizeInBits = originalSizeInBytes * 8
//K such that L + 1 + K + 64 is a multiple of 512 //K such that L + 1 + K + 64 is a multiple of 512
val expandedRemainderOf512 = (originalMessageSizeInBits + BLOCK_SIZE_IN_BYTES + 1) % BLOCK_SIZE val expandedRemainderOf512 = (originalMessageSizeInBits + BLOCK_SIZE_IN_BYTES + 1) % BLOCK_SIZE
val zeroAddAmount = when (expandedRemainderOf512) { val zeroAddAmount = when (expandedRemainderOf512) {
0 -> 0 0L -> 0
else -> (BLOCK_SIZE - expandedRemainderOf512) / 8 else -> ((BLOCK_SIZE - expandedRemainderOf512) / 8).toInt()
} }
val expansionArray = UByteArray(zeroAddAmount + 1) { val expansionArray = UByteArray(zeroAddAmount + 1) {
when (it) { when (it) {
@ -231,12 +231,16 @@ class Sha256Pure : Sha256 {
} }
var h = iv.copyOf() var h = iv.copyOf()
var counter = 0 var counter = 0L
var bufferCounter = 0 var bufferCounter = 0
var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U } var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U }
var digested = false
fun update(data: String) { 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()) return update(data.encodeToUByteArray())
} }
@ -247,6 +251,14 @@ class Sha256Pure : Sha256 {
when { when {
bufferCounter + data.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(data, bufferCounter) 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 -> { bufferCounter + data.size >= BLOCK_SIZE_IN_BYTES -> {
val chunked = data.chunked(BLOCK_SIZE_IN_BYTES) val chunked = data.chunked(BLOCK_SIZE_IN_BYTES)
chunked.forEach { chunk -> chunked.forEach { chunk ->
@ -303,6 +315,7 @@ class Sha256Pure : Sha256 {
h[5].toPaddedByteArray() + h[5].toPaddedByteArray() +
h[6].toPaddedByteArray() + h[6].toPaddedByteArray() +
h[7].toPaddedByteArray() h[7].toPaddedByteArray()
digested = true
return digest return digest
} }

View File

@ -137,7 +137,7 @@ class Sha512Pure : Sha512Multipart {
var h = iv.copyOf() var h = iv.copyOf()
val expansionArray = createExpansionArray(inputMessage.size) val expansionArray = createExpansionArray(inputMessage.size.toLong())
val chunks = val chunks =
(inputMessage + expansionArray + (inputMessage.size * 8).toULong().toPadded128BitByteArray()).chunked( (inputMessage + expansionArray + (inputMessage.size * 8).toULong().toPadded128BitByteArray()).chunked(
@ -249,13 +249,13 @@ class Sha512Pure : Sha512Multipart {
return h return h
} }
fun createExpansionArray(originalSizeInBytes: Int): UByteArray { fun createExpansionArray(originalSizeInBytes: Long): UByteArray {
val originalMessageSizeInBits = originalSizeInBytes * 8 val originalMessageSizeInBits = originalSizeInBytes * 8
val expandedRemainderOf1024 = (originalMessageSizeInBits + 129) % BLOCK_SIZE val expandedRemainderOf1024 = (originalMessageSizeInBits + 129) % BLOCK_SIZE
val zeroAddAmount = when (expandedRemainderOf1024) { val zeroAddAmount = when (expandedRemainderOf1024) {
0 -> 0 0L -> 0
else -> (BLOCK_SIZE - expandedRemainderOf1024) / 8 else -> ((BLOCK_SIZE - expandedRemainderOf1024) / 8).toInt()
} }
val expansionArray = UByteArray(zeroAddAmount + 1) { val expansionArray = UByteArray(zeroAddAmount + 1) {
when (it) { when (it) {
@ -305,9 +305,10 @@ class Sha512Pure : Sha512Multipart {
} }
var h = iv.copyOf() var h = iv.copyOf()
var counter = 0 var counter = 0L
var bufferCounter = 0 var bufferCounter = 0
var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U } var buffer = UByteArray(BLOCK_SIZE_IN_BYTES) { 0U }
var digested = false
fun update(data: String) { fun update(data: String) {
@ -318,6 +319,9 @@ class Sha512Pure : Sha512Multipart {
if (data.isEmpty()) { if (data.isEmpty()) {
throw RuntimeException("Updating with empty array is not allowed. If you need empty hash, just call digest without updating") 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 { when {
bufferCounter + data.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(data, bufferCounter) bufferCounter + data.size < BLOCK_SIZE_IN_BYTES -> appendToBuffer(data, bufferCounter)
@ -377,6 +381,7 @@ class Sha512Pure : Sha512Multipart {
h[5].toPaddedByteArray() + h[5].toPaddedByteArray() +
h[6].toPaddedByteArray() + h[6].toPaddedByteArray() +
h[7].toPaddedByteArray() h[7].toPaddedByteArray()
digested = true
return digest return digest
} }

View File

@ -16,7 +16,9 @@
package com.ionspin.kotlin.crypto.hash.sha 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.hexStringToUByteArray
import com.ionspin.kotlin.crypto.util.toHexString
import kotlin.test.Ignore import kotlin.test.Ignore
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertTrue import kotlin.test.assertTrue
@ -74,28 +76,59 @@ class Sha256UpdatableTest {
for (i in 0 until 10000) { for (i in 0 until 10000) {
sha256.update("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") sha256.update("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
} }
val resultDoubleBlock = sha256.digest() val result = sha256.digest()
val expectedResultForDoubleBlock = "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0" val expectedResult = "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"
assertTrue { 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 @Test
fun testWellKnownLonger() { fun testWellKnownLonger() {
val sha256 = Sha256Pure()
for (i in 0 until 16_777_216) { //Obtained from libsodium runs
if (i % 10000 == 0) { val roundsExpectedMap = mapOf(
println("$i/16777216") 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())
} }
} }
} }

View File

@ -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 @Test
fun testWellKnownLonger() { fun testWellKnownLonger() {
val sha512 = Sha512Pure() val sha512 = Sha512Pure()
for (i in 0 until 16_777_216) { for (i in 0 until 16_777_216) {
if (i % 10000 == 0) {
println("$i/16777216")
}
sha512.update("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno") sha512.update("abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno")
} }
val resultDoubleBlock = sha512.digest() val result = sha512.digest()
val expectedResultForDoubleBlock = "b47c933421ea2db149ad6e10fce6c7f93d0752380180ffd7f4629a712134831d77be6091b819ed352c2967a2e2d4fa5050723c9630691f1a05a7281dbe6c1086" val expectedResult = "b47c933421ea2db149ad6e10fce6c7f93d0752380180ffd7f4629a712134831d77be6091b819ed352c2967a2e2d4fa5050723c9630691f1a05a7281dbe6c1086"
assertTrue { assertTrue {
resultDoubleBlock.contentEquals(expectedResultForDoubleBlock.hexStringToUByteArray()) result.contentEquals(expectedResult.hexStringToUByteArray())
} }
} }
} }