From 233ee1bf555b1636c6ddbc338081676e738ee67e Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Tue, 23 Jun 2020 19:54:30 +0200 Subject: [PATCH] Multipart API continuation --- .../AuthenticatedSymmetricEncryption.kt | 36 ++++++++++++ .../crypto/hash/{Hash.kt => HashFunction.kt} | 6 +- .../kotlin/crypto/hash/blake2b/Blake2b.kt | 8 +-- .../ionspin/kotlin/crypto/hash/sha/Sha256.kt | 8 +-- .../ionspin/kotlin/crypto/hash/sha/Sha512.kt | 8 +-- .../kotlin/crypto/util/ConversionUtil.kt | 7 +++ .../ionspin/kotlin/crypto/util/CryptoUtil.kt | 18 ++++++ .../ionspin/kotlin/crypto/util/StringUtil.kt | 56 ++++++++++++++++++ .../com/ionspin/kotlin/crypto/util/Util.kt | 46 --------------- .../ionspin/kotlin/crypto/CryptoProvider.kt | 57 ++++++++++++++++--- .../authenticated/XChaCha20Poly1305Pure.kt | 49 +++++++++++----- .../kotlin/crypto/symmetric/XChaCha20Pure.kt | 4 +- .../com/ionspin/kotlin/crypto/util/Util.kt | 11 ---- .../kotlin/crypto/symmetric/XChaCha20Test.kt | 14 ++--- 14 files changed, 226 insertions(+), 102 deletions(-) create mode 100644 multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/AuthenticatedSymmetricEncryption.kt rename multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/{Hash.kt => HashFunction.kt} (90%) create mode 100644 multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/ConversionUtil.kt create mode 100644 multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/CryptoUtil.kt create mode 100644 multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/StringUtil.kt diff --git a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/AuthenticatedSymmetricEncryption.kt b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/AuthenticatedSymmetricEncryption.kt new file mode 100644 index 0000000..00f6865 --- /dev/null +++ b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/AuthenticatedSymmetricEncryption.kt @@ -0,0 +1,36 @@ +package com.ionspin.kotlin.crypto.authenticated + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 22-Jun-2020 + */ +interface AuthenticatedEncryption { + fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, additionalData: UByteArray) : UByteArray + fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, additionalData: UByteArray) : UByteArray + +} + +data class EncryptedDataPart(val data : UByteArray) +data class DecryptedDataPart(val data : UByteArray) + +data class MultipartEncryptedDataDescriptor(val data: UByteArray, val nonce: UByteArray) + + + + +interface MultipartAuthenticatedVerification { + fun verifyPartialData(data: EncryptedDataPart) + fun finalizeVerificationAndPrepareDecryptor() : MultipartAuthenticatedDecryption +} + +interface MultipartAuthenticatedDecryption { + fun decryptPartialData(data: EncryptedDataPart) : DecryptedDataPart +} + +interface MultipartAuthenticatedEncryption { + fun encryptPartialData(data: UByteArray) : EncryptedDataPart + fun finish() : MultipartEncryptedDataDescriptor + +} + diff --git a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/Hash.kt b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/HashFunction.kt similarity index 90% rename from multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/Hash.kt rename to multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/HashFunction.kt index 8d9ebc3..1f0a49e 100644 --- a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/Hash.kt +++ b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/HashFunction.kt @@ -22,12 +22,12 @@ package com.ionspin.kotlin.crypto.hash * ugljesa.jovanovic@ionspin.com * on 20-Jul-2019 */ -interface Hash { +interface HashFunction { val MAX_HASH_BYTES : Int } -interface UpdatableHash : Hash { +interface MultiPartHash : HashFunction { fun update(data : UByteArray) fun digest() : UByteArray @@ -35,7 +35,7 @@ interface UpdatableHash : Hash { } -interface StatelessHash : Hash { +interface Hash : HashFunction { } diff --git a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2b.kt b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2b.kt index cac59c5..eeff721 100644 --- a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2b.kt +++ b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/blake2b/Blake2b.kt @@ -1,7 +1,7 @@ package com.ionspin.kotlin.crypto.hash.blake2b -import com.ionspin.kotlin.crypto.hash.StatelessHash -import com.ionspin.kotlin.crypto.hash.UpdatableHash +import com.ionspin.kotlin.crypto.hash.Hash +import com.ionspin.kotlin.crypto.hash.MultiPartHash /** * Created by Ugljesa Jovanovic @@ -13,12 +13,12 @@ object Blake2bProperties { const val MAX_HASH_BYTES = 64 } -interface Blake2b : UpdatableHash { +interface Blake2b : MultiPartHash { override val MAX_HASH_BYTES: Int get() = Blake2bProperties.MAX_HASH_BYTES } -interface Blake2bStateless : StatelessHash { +interface Blake2bStateless : Hash { override val MAX_HASH_BYTES: Int get() = Blake2bProperties.MAX_HASH_BYTES diff --git a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256.kt b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256.kt index 6097f3a..f806b80 100644 --- a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256.kt +++ b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha256.kt @@ -1,7 +1,7 @@ package com.ionspin.kotlin.crypto.hash.sha -import com.ionspin.kotlin.crypto.hash.StatelessHash -import com.ionspin.kotlin.crypto.hash.UpdatableHash +import com.ionspin.kotlin.crypto.hash.Hash +import com.ionspin.kotlin.crypto.hash.MultiPartHash /** * Created by Ugljesa Jovanovic @@ -12,11 +12,11 @@ object Sha256Properties { const val MAX_HASH_BYTES = 32 } -interface Sha256 : UpdatableHash { +interface Sha256 : MultiPartHash { override val MAX_HASH_BYTES: Int get() = Sha256Properties.MAX_HASH_BYTES } -interface StatelessSha256 : StatelessHash { +interface StatelessSha256 : Hash { override val MAX_HASH_BYTES: Int get() = Sha256Properties.MAX_HASH_BYTES diff --git a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512.kt b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512.kt index f60e92d..a5c672e 100644 --- a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512.kt +++ b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/hash/sha/Sha512.kt @@ -1,7 +1,7 @@ package com.ionspin.kotlin.crypto.hash.sha -import com.ionspin.kotlin.crypto.hash.StatelessHash -import com.ionspin.kotlin.crypto.hash.UpdatableHash +import com.ionspin.kotlin.crypto.hash.Hash +import com.ionspin.kotlin.crypto.hash.MultiPartHash /** * Created by Ugljesa Jovanovic @@ -11,11 +11,11 @@ import com.ionspin.kotlin.crypto.hash.UpdatableHash object Sha512Properties { const val MAX_HASH_BYTES = 64 } -interface Sha512 : UpdatableHash { +interface Sha512 : MultiPartHash { override val MAX_HASH_BYTES: Int get() = Sha256Properties.MAX_HASH_BYTES } -interface StatelessSha512 : StatelessHash { +interface StatelessSha512 : Hash { override val MAX_HASH_BYTES: Int get() = Sha512Properties.MAX_HASH_BYTES diff --git a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/ConversionUtil.kt b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/ConversionUtil.kt new file mode 100644 index 0000000..87807db --- /dev/null +++ b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/ConversionUtil.kt @@ -0,0 +1,7 @@ +package com.ionspin.kotlin.crypto.util + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 22-Jun-2020 + */ diff --git a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/CryptoUtil.kt b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/CryptoUtil.kt new file mode 100644 index 0000000..2fe2d7c --- /dev/null +++ b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/CryptoUtil.kt @@ -0,0 +1,18 @@ +package com.ionspin.kotlin.crypto.util + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 22-Jun-2020 + */ +fun UByteArray.overwriteWithZeroes() { + for (i in 0 until size) { + this[i] = 0U + } +} + +fun UIntArray.overwriteWithZeroes() { + for (i in 0 until size) { + this[i] = 0U + } +} \ No newline at end of file diff --git a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/StringUtil.kt b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/StringUtil.kt new file mode 100644 index 0000000..efeb7f2 --- /dev/null +++ b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/StringUtil.kt @@ -0,0 +1,56 @@ +package com.ionspin.kotlin.crypto.util + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 22-Jun-2020 + */ +fun Array.hexColumsPrint() { + val printout = this.map { it.toString(16) }.chunked(16) + printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) } +} + +fun Array.hexColumsPrint(chunk : Int = 16) { + val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk) + printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) } +} + +fun UByteArray.hexColumsPrint(chunk : Int = 16) { + val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk) + printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) } +} + +fun Array.hexColumsPrint(chunk: Int = 3) { + val printout = this.map { it.toString(16) }.chunked(chunk) + printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) } +} + +fun String.hexStringToTypedUByteArray() : Array { + return this.chunked(2).map { it.toUByte(16) }.toTypedArray() +} + + +fun String.hexStringToUByteArray() : UByteArray { + return this.chunked(2).map { it.toUByte(16) }.toUByteArray() +} + +fun Array.toHexString() : String { + return this.joinToString(separator = "") { + if (it <= 0x0FU) { + "0${it.toString(16)}" + } else { + it.toString(16) + } + } +} + + +fun UByteArray.toHexString() : String { + return this.joinToString(separator = "") { + if (it <= 0x0FU) { + "0${it.toString(16)}" + } else { + it.toString(16) + } + } +} \ No newline at end of file diff --git a/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt b/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt index 28e9232..6b8eb10 100644 --- a/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt +++ b/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt @@ -23,25 +23,7 @@ package com.ionspin.kotlin.crypto.util * ugljesa.jovanovic@ionspin.com * on 15-Jul-2019 */ -fun Array.hexColumsPrint() { - val printout = this.map { it.toString(16) }.chunked(16) - printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) } -} -fun Array.hexColumsPrint(chunk : Int = 16) { - val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk) - printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) } -} - -fun UByteArray.hexColumsPrint(chunk : Int = 16) { - val printout = this.map { it.toString(16).padStart(2, '0') }.chunked(chunk) - printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) } -} - -fun Array.hexColumsPrint(chunk: Int = 3) { - val printout = this.map { it.toString(16) }.chunked(chunk) - printout.forEach { println(it.joinToString(separator = " ") { it.toUpperCase() }) } -} inline fun Array.chunked(sliceSize: Int): Array> { val last = this.size % sliceSize @@ -89,36 +71,8 @@ infix fun UByteArray.xor(other : UByteArray) : UByteArray { } -fun String.hexStringToTypedUByteArray() : Array { - return this.chunked(2).map { it.toUByte(16) }.toTypedArray() -} -fun String.hexStringToUByteArray() : UByteArray { - return this.chunked(2).map { it.toUByte(16) }.toUByteArray() -} - - -fun Array.toHexString() : String { - return this.joinToString(separator = "") { - if (it <= 0x0FU) { - "0${it.toString(16)}" - } else { - it.toString(16) - } - } -} - - -fun UByteArray.toHexString() : String { - return this.joinToString(separator = "") { - if (it <= 0x0FU) { - "0${it.toString(16)}" - } else { - it.toString(16) - } - } -} // UInt / Array utils diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/CryptoProvider.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/CryptoProvider.kt index ec2eccd..0c1745c 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/CryptoProvider.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/CryptoProvider.kt @@ -1,7 +1,6 @@ package com.ionspin.kotlin.crypto -import com.ionspin.kotlin.crypto.authenticated.XChaCha20Poly1305Pure -import com.ionspin.kotlin.crypto.hash.UpdatableHash +import com.ionspin.kotlin.crypto.authenticated.* import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bProperties import com.ionspin.kotlin.crypto.hash.blake2b.Blake2bPure import com.ionspin.kotlin.crypto.hash.encodeToUByteArray @@ -115,17 +114,17 @@ data class EncryptedData internal constructor(val ciphertext: UByteArray, val no object PublicApi { - object Hash { + object Hashing { fun hash(data: UByteArray, key : UByteArray = ubyteArrayOf()) : HashedData { return HashedData(Blake2bPureStateless.digest(data, key)) } - fun updateableHash(key: UByteArray? = null) : UpdatableHash { + fun multipartHash(key: UByteArray? = null) : com.ionspin.kotlin.crypto.hash.MultiPartHash { return Blake2bPure(key) } } - object Symmetric { - fun encrypt(key: SymmetricKey, data : Encryptable<*>, additionalData : UByteArray = ubyteArrayOf()) : EncryptedData { + object Encryption { + fun authenticatedEncryption(key: SymmetricKey, data : Encryptable<*>, additionalData : UByteArray = ubyteArrayOf()) : EncryptedData { if (key.value.size != 32) { throw RuntimeException("Invalid key size! Required 32, supplied ${key.value.size}") } @@ -138,5 +137,49 @@ object PublicApi { return byteArrayDeserializer(XChaCha20Poly1305Pure.decrypt(key.value, encryptedData.nonce, encryptedData.ciphertext, additionalData)) } + + fun multipartAuthenticatedEncrypt(key: SymmetricKey, additionalData: UByteArray) : MultipartAuthenticatedEncryption { + return MultipartAuthenticatedEncryptor(key, additionalData) + } + + fun getMultipartVerificator(key: SymmetricKey, dataDescriptor: MultipartEncryptedDataDescriptor, additionalData: UByteArray) : MultipartAuthenticatedVerification { + return MultiplatformAuthenticatedVerificator(key, dataDescriptor, additionalData) + } } -} \ No newline at end of file +} + +class MultipartAuthenticatedEncryptor internal constructor(val key : SymmetricKey, additionalData: UByteArray) : MultipartAuthenticatedEncryption { + val primitive = XChaCha20Poly1305Pure(key.value, additionalData) + override fun encryptPartialData(data: UByteArray): EncryptedDataPart { + return EncryptedDataPart(primitive.encryptPartialData(data)) + } + + override fun finish(): MultipartEncryptedDataDescriptor { + val finished = primitive.finish() + return MultipartEncryptedDataDescriptor(finished.first, finished.second) + } + +} + +class MultiplatformAuthenticatedVerificator internal constructor(key: SymmetricKey, multipartEncryptedDataDescriptor: MultipartEncryptedDataDescriptor, additionalData: UByteArray) : MultipartAuthenticatedVerification { + val primitive = XChaCha20Poly1305Pure(key.value, additionalData) + val tag = multipartEncryptedDataDescriptor.data.sliceArray( + multipartEncryptedDataDescriptor.data.size - 16 until multipartEncryptedDataDescriptor.data.size + ) + override fun verifyPartialData(data: EncryptedDataPart) { + primitive.encryptPartialData(data.data) + } + + override fun finalizeVerificationAndPrepareDecryptor(): MultipartAuthenticatedDecryption { + primitive.finalizeVerificationAndPrepareDecryptor(tag) + return MultipartAuthenticatedDecryptor(primitive) + } + +} + +class MultipartAuthenticatedDecryptor internal constructor(val encryptor: XChaCha20Poly1305Pure) : MultipartAuthenticatedDecryption { + override fun decryptPartialData(data: EncryptedDataPart): DecryptedDataPart { + encryptor.decrypt(data.data) + } + +} 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 00ac080..a7b3708 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 @@ -1,5 +1,6 @@ package com.ionspin.kotlin.crypto.authenticated +import com.ionspin.kotlin.crypto.SRNG import com.ionspin.kotlin.crypto.mac.Poly1305 import com.ionspin.kotlin.crypto.symmetric.ChaCha20Pure import com.ionspin.kotlin.crypto.symmetric.XChaCha20Pure @@ -10,10 +11,10 @@ import com.ionspin.kotlin.crypto.util.toLittleEndianUByteArray * ugljesa.jovanovic@ionspin.com * on 17-Jun-2020 */ -class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val additionalData: UByteArray) { - companion object { +class XChaCha20Poly1305Pure(val key: UByteArray, val additionalData: UByteArray) : { + companion object : AuthenticatedEncryption { - fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, additionalData: UByteArray) : UByteArray { + override fun encrypt(key: UByteArray, nonce: UByteArray, message: UByteArray, additionalData: UByteArray) : UByteArray { val subKey = XChaCha20Pure.hChacha(key, nonce) val authKey = ChaCha20Pure.encrypt( @@ -36,7 +37,7 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi return cipherText + tag } - fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, additionalData: UByteArray) : UByteArray { + override fun decrypt(key: UByteArray, nonce: UByteArray, cipherText: UByteArray, additionalData: UByteArray) : UByteArray { val subKey = XChaCha20Pure.hChacha(key, nonce) val authKey = ChaCha20Pure.encrypt( @@ -62,8 +63,11 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi //4. Decrypt data return XChaCha20Pure.xorWithKeystream(key, nonce, cipherTextWithoutTag, 1U) } + } + val nonce = SRNG.getRandomBytes(24) + private val updateableEncryptionPrimitive = XChaCha20Pure(key, nonce, initialCounter = 1U) private val updateableMacPrimitive : Poly1305 @@ -93,11 +97,34 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi fun encryptPartialData(data: UByteArray) : UByteArray { processedBytes += data.size - val encrypted = updateableEncryptionPrimitive.encryptPartialData(data) + val encrypted = updateableEncryptionPrimitive.xorWithKeystream(data) processPolyBytes(encrypted) return encrypted } + fun verifyPartialData(data: UByteArray) { + processPolyBytes(data) + } + + fun finalizeVerificationAndPrepareDecryptor(expectedTag: UByteArray): MultipartAuthenticatedDecryption { + val cipherTextPad = UByteArray(16 - processedBytes % 16) { 0U } + val macData = cipherTextPad + + additionalData.size.toULong().toLittleEndianUByteArray() + + processedBytes.toULong().toLittleEndianUByteArray() + processPolyBytes(macData) + val tag = updateableMacPrimitive.finalizeMac() + if (!tag.contentEquals(expectedTag)) { + throw RuntimeException("Invalid tag") //TODO Replace with proper exception + } + } + + fun decrypt(data: UByteArray) : UByteArray { + processedBytes += data.size + val decrypted = updateableEncryptionPrimitive.xorWithKeystream(data) + processPolyBytes(decrypted) + return decrypted + } + private fun processPolyBytes(data: UByteArray) { if (polyBufferByteCounter == 0) { val polyBlocks = data.size / 16 @@ -139,12 +166,7 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi } } - - - - - - fun finish() : UByteArray { + fun finish() : Pair { val cipherTextPad = UByteArray(16 - processedBytes % 16) { 0U } val macData = cipherTextPad + @@ -152,9 +174,8 @@ class XChaCha20Poly1305Pure(val key: UByteArray, val nonce: UByteArray, val addi processedBytes.toULong().toLittleEndianUByteArray() processPolyBytes(macData) val tag = updateableMacPrimitive.finalizeMac() - return tag + return Pair(tag, nonce) } - -} \ No newline at end of file +} diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Pure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Pure.kt index b2747f4..a7e0256 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Pure.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Pure.kt @@ -132,7 +132,7 @@ class XChaCha20Pure(key: UByteArray, nonce: UByteArray, initialCounter: UInt = 0 hChaChaKey.overwriteWithZeroes() } - fun encryptPartialData(data: UByteArray) : UByteArray { + fun xorWithKeystream(data: UByteArray) : UByteArray { val ciphertext = UByteArray(data.size) { 0U } //First use remaining keystream var processedBytes = 0 @@ -177,4 +177,4 @@ class XChaCha20Pure(key: UByteArray, nonce: UByteArray, initialCounter: UInt = 0 return ciphertext } -} \ No newline at end of file +} diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt index 4d6bbcb..4cac54d 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/util/Util.kt @@ -344,17 +344,6 @@ fun Array.fromBigEndianArrayToUInt() : UInt { operator fun UInt.plus(other : UByteArray) : UByteArray { return this.toLittleEndianUByteArray() + other } -fun UByteArray.overwriteWithZeroes() { - for (i in 0 until size) { - this[i] = 0U - } -} - -fun UIntArray.overwriteWithZeroes() { - for (i in 0 until size) { - this[i] = 0U - } -} //AES Flatten fun Collection.flattenToUByteArray(): UByteArray { diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Test.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Test.kt index 89b8f83..dc3d9ed 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Test.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/XChaCha20Test.kt @@ -236,9 +236,9 @@ class XChaCha20Test { ) val xChaCha = XChaCha20Pure(key, nonce, 1U) - val firstChunk = xChaCha.encryptPartialData(message.sliceArray(0 until 5)) - val secondChunk = xChaCha.encryptPartialData(message.sliceArray(5 until 90)) - val thirdChunk = xChaCha.encryptPartialData(message.sliceArray(90 until message.size)) + val firstChunk = xChaCha.xorWithKeystream(message.sliceArray(0 until 5)) + val secondChunk = xChaCha.xorWithKeystream(message.sliceArray(5 until 90)) + val thirdChunk = xChaCha.xorWithKeystream(message.sliceArray(90 until message.size)) assertTrue { (firstChunk + secondChunk + thirdChunk).contentEquals(expected) @@ -325,9 +325,9 @@ class XChaCha20Test { ) val xChaCha = XChaCha20Pure(key, nonce, 1U) - val firstChunk = xChaCha.encryptPartialData(message.sliceArray(0 until 50)) - val secondChunk = xChaCha.encryptPartialData(message.sliceArray(50 until 200)) - val thirdChunk = xChaCha.encryptPartialData(message.sliceArray(200 until message.size)) + val firstChunk = xChaCha.xorWithKeystream(message.sliceArray(0 until 50)) + val secondChunk = xChaCha.xorWithKeystream(message.sliceArray(50 until 200)) + val thirdChunk = xChaCha.xorWithKeystream(message.sliceArray(200 until message.size)) val result = (firstChunk + secondChunk + thirdChunk) assertTrue { result.contentEquals(expected) @@ -337,4 +337,4 @@ class XChaCha20Test { } -} \ No newline at end of file +}