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:
		
							parent
							
								
									52b6a4ad8e
								
							
						
					
					
						commit
						1dc423a509
					
				| @ -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) | ||||
|     } | ||||
| } | ||||
| @ -135,7 +135,7 @@ class MultipartAuthenticatedEncryptor internal constructor(val key : SymmetricKe | ||||
|     } | ||||
| 
 | ||||
|     override fun startEncryption(): MultipartEncryptionHeader { | ||||
|         return header | ||||
|         return header.copy() | ||||
|     } | ||||
| 
 | ||||
|     override fun cleanup() { | ||||
|  | ||||
| @ -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-------=") | ||||
|  | ||||
| @ -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 | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -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()) | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -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()) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user