From 5c10d3abf4e041934032b77e0595c73052133353 Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Sun, 14 Jun 2020 12:49:46 +0200 Subject: [PATCH] Removing direct aes cbc and ctr, to reduce foot shooting incidents, introducing aes256-gcm --- README.md | 11 +- .../kotlin/crypto/authenticated/Aes256Gcm.kt | 37 ++ .../kotlin/crypto/symmetric/aes/AesCtr.kt | 69 ---- .../authenticated/DelegatedAes256Gcm.kt | 7 + .../kotlin/crypto/symmetric/AesCbcPure.kt | 222 ----------- .../kotlin/crypto/symmetric/AesCtrPure.kt | 209 ---------- .../kotlin/crypto/symmetric/AesPure.kt | 360 ------------------ .../crypto/authenticated/Aes256GcmPure.kt | 18 + .../ionspin/kotlin/crypto/symmetric}/Aes.kt | 10 +- .../kotlin/crypto/symmetric/AesCbcPure.kt | 17 +- .../kotlin/crypto/symmetric/AesCtrPure.kt | 31 +- .../kotlin/crypto/symmetric/AesPure.kt | 14 +- .../kotlin/crypto/symmetric/AesCbcTest.kt | 8 +- .../kotlin/crypto/symmetric/AesCtrTest.kt | 8 +- .../kotlin/crypto/symmetric/AesTest.kt | 38 +- 15 files changed, 136 insertions(+), 923 deletions(-) create mode 100644 multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/Aes256Gcm.kt delete mode 100644 multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/aes/AesCtr.kt create mode 100644 multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/DelegatedAes256Gcm.kt delete mode 100644 multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcPure.kt delete mode 100644 multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrPure.kt delete mode 100644 multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesPure.kt create mode 100644 multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/Aes256GcmPure.kt rename {multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/aes => multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric}/Aes.kt (60%) diff --git a/README.md b/README.md index 497e3fc..901857d 100644 --- a/README.md +++ b/README.md @@ -64,18 +64,14 @@ It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be * Blake2b * SHA512 * SHA256 - -### Symmetric cipher -* AES - * Modes: CBC, CTR ### Key Derivation * Argon2 -### AEAD +### Authenticated symmetric encryption (AEAD) -TODO() +* TODO ### Delegated flavor dependancy table @@ -86,8 +82,7 @@ The following table describes which library is used for particular cryptographic | Blake2b | LazySodium | libsodium.js | libsodium | | SHA256 | LazySodium | libsodium.js | libsodium | | SHA512 | LazySodium | libsodium.js | libsodium | -| AES-CBC | LazySodium | libsodium.js | libsodium | -| AES-CTR | LazySodium | libsodium.js | libsodium | + ## Integration diff --git a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/Aes256Gcm.kt b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/Aes256Gcm.kt new file mode 100644 index 0000000..6b06777 --- /dev/null +++ b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/Aes256Gcm.kt @@ -0,0 +1,37 @@ +package com.ionspin.kotlin.crypto.authenticated + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 14-Jun-2020 + */ +interface Aes256GcmStateless { + /** + * Nonce autogenerated, key autogenerated + */ + fun encrypt(message: UByteArray, additionalData: UByteArray, rawData : UByteArray) : Aes256GcmEncryptionResult +} + +data class Aes256GcmEncryptionResult(val cyphertext : UByteArray, val additionalData: UByteArray, val nonce: UByteArray, val tag: UByteArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Aes256GcmEncryptionResult + + if (cyphertext != other.cyphertext) return false + if (additionalData != other.additionalData) return false + if (nonce != other.nonce) return false + if (tag != other.tag) return false + + return true + } + + override fun hashCode(): Int { + var result = cyphertext.hashCode() + result = 31 * result + additionalData.hashCode() + result = 31 * result + nonce.hashCode() + result = 31 * result + tag.hashCode() + return result + } +} diff --git a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/aes/AesCtr.kt b/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/aes/AesCtr.kt deleted file mode 100644 index fae3eda..0000000 --- a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/aes/AesCtr.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.ionspin.kotlin.crypto.symmetric.aes - -/** - * Created by Ugljesa Jovanovic - * ugljesa.jovanovic@ionspin.com - * on 13-Jun-2020 - */ -interface SimpleUpdateableAesCtr { - fun update(data: UByteArray) - - fun process() : UByteArray - -} - -interface AdvancedUpdateableAesCtr : SimpleUpdateableAesCtr { - fun update(data: UByteArray, counter : UByteArray) : UByteArray -} - -interface SimpleStatelessAesCtr { - - fun encrypt(aesKey: AesKey, data: UByteArray) : EncryptedDataAndInitialCounter - - fun decrypt(aesKey: AesKey, encryptedDataAndInitialCounter: EncryptedDataAndInitialCounter) : UByteArray - -} - -interface AdvancedStatelessAesCtr : SimpleStatelessAesCtr { - fun encrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray) : EncryptedDataAndInitialCounter -} - -data class EncryptedDataAndInitialCounter(val encryptedData : UByteArray, val initialCounter : UByteArray) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - - other as EncryptedDataAndInitialCounter - - if (!encryptedData.contentEquals(other.encryptedData)) return false - if (!initialCounter.contentEquals(other.initialCounter)) return false - - return true - } - - override fun hashCode(): Int { - var result = encryptedData.contentHashCode() - result = 31 * result + initialCounter.contentHashCode() - return result - } -} - -data class EncryptedDataAndInitializationVector(val encryptedData : UByteArray, val initializationVector : UByteArray) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - - other as EncryptedDataAndInitializationVector - - if (!encryptedData.contentEquals(other.encryptedData)) return false - if (!initializationVector.contentEquals(other.initializationVector)) return false - - return true - } - - override fun hashCode(): Int { - var result = encryptedData.contentHashCode() - result = 31 * result + initializationVector.contentHashCode() - return result - } -} diff --git a/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/DelegatedAes256Gcm.kt b/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/DelegatedAes256Gcm.kt new file mode 100644 index 0000000..055b82f --- /dev/null +++ b/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/DelegatedAes256Gcm.kt @@ -0,0 +1,7 @@ +package com.ionspin.kotlin.crypto.authenticated + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 14-Jun-2020 + */ diff --git a/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcPure.kt b/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcPure.kt deleted file mode 100644 index 0d58f8f..0000000 --- a/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcPure.kt +++ /dev/null @@ -1,222 +0,0 @@ -/* - * 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.symmetric - -import com.ionspin.kotlin.crypto.SRNG -import com.ionspin.kotlin.crypto.util.xor - -/** - * Advanced encryption standard with cipher block chaining and PKCS #5 - * - * For bulk encryption/decryption use [AesCbcDelegated.encrypt] and [AesCbcDelegated.decrypt] - * - * To get an instance of AesCbc and then feed it data sequentially with [addData] use [createEncryptor] and [createDecryptor] - * - * Created by Ugljesa Jovanovic - * ugljesa.jovanovic@ionspin.com - * on 21-Sep-2019 - */ - -class AesCbcDelegated internal constructor(val aesKey: AesKey, val mode: Mode, initializationVector: UByteArray? = null) { - - companion object { - const val BLOCK_BYTES = 16 - /** - * Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all - * data call [encrypt] - */ - fun createEncryptor(aesKey: AesKey) : AesCbcDelegated { - return AesCbcDelegated(aesKey, Mode.ENCRYPT) - } - /** - * Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all - * data call [decrypt] - */ - fun createDecryptor(aesKey : AesKey) : AesCbcDelegated { - return AesCbcDelegated(aesKey, Mode.DECRYPT) - } - - /** - * Bulk encryption, returns encrypted data and a random initialization vector - */ - fun encrypt(aesKey: AesKey, data: UByteArray): EncryptedDataAndInitializationVector { - val aesCbc = AesCbcDelegated(aesKey, Mode.ENCRYPT) - aesCbc.addData(data) - return aesCbc.encrypt() - } - - /** - * Bulk decryption, returns decrypted data - */ - fun decrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray { - val aesCbc = AesCbcDelegated(aesKey, Mode.DECRYPT, initialCounter) - aesCbc.addData(data) - return aesCbc.decrypt() - } - - private fun padToBlock(unpadded: UByteArray): UByteArray { - val paddingSize = 16 - unpadded.size - if (unpadded.size == BLOCK_BYTES) { - return unpadded - } - - if (unpadded.size == BLOCK_BYTES) { - return UByteArray(BLOCK_BYTES) { - BLOCK_BYTES.toUByte() - } - } - - if (unpadded.size > BLOCK_BYTES) { - throw IllegalStateException("Block larger than 128 bytes") - } - - return UByteArray(BLOCK_BYTES) { - when (it) { - in unpadded.indices -> unpadded[it] - else -> paddingSize.toUByte() - } - } - - } - } - - var currentOutput: UByteArray = ubyteArrayOf() - var previousEncrypted: UByteArray = ubyteArrayOf() - val initVector = if (initializationVector.isNullOrEmpty()) { - SRNG.getRandomBytes(16) - } else { - initializationVector - } - - val output = MutableList(0) { ubyteArrayOf() } - - var buffer: UByteArray = UByteArray(16) { 0U } - var bufferCounter = 0 - - fun addData(data: UByteArray) { - //Padding - when { - bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter) - bufferCounter + data.size >= BLOCK_BYTES -> { - val chunked = data.chunked(BLOCK_BYTES) - chunked.forEach { chunk -> - if (bufferCounter + chunk.size < BLOCK_BYTES) { - appendToBuffer(chunk.toUByteArray(), bufferCounter) - } else { - chunk.toUByteArray().copyInto( - destination = buffer, - destinationOffset = bufferCounter, - startIndex = 0, - endIndex = BLOCK_BYTES - bufferCounter - ) - output += consumeBlock(buffer) - buffer = UByteArray(BLOCK_BYTES) { - when (it) { - in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> { - chunk[it + (BLOCK_BYTES - bufferCounter)] - } - else -> { - 0U - } - } - - } - bufferCounter = chunk.size - (BLOCK_BYTES - bufferCounter) - } - } - - } - } - - } - - /** - * Encrypt fed data and return it alongside the randomly chosen initialization vector - * @return Encrypted data and initialization vector - */ - fun encrypt(): EncryptedDataAndInitializationVector { - if (bufferCounter > 0) { - val lastBlockPadded = padToBlock(buffer) - if (lastBlockPadded.size > BLOCK_BYTES) { - val chunks = lastBlockPadded.chunked(BLOCK_BYTES).map { it.toUByteArray() } - output += consumeBlock(chunks[0]) - output += consumeBlock(chunks[1]) - } else { - output += consumeBlock(lastBlockPadded) - } - } - return EncryptedDataAndInitializationVector( - output.reversed().foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes }, - initVector - ) - } - - /** - * Decrypt data - * @return Decrypted data - */ - fun decrypt(): UByteArray { - val removePaddingCount = output.last().last() - - - val removedPadding = if (removePaddingCount > 0U && removePaddingCount < 16U) { - output.last().dropLast(removePaddingCount.toInt() and 0x7F) - } else { - output.last().toList() - }.toUByteArray() - val preparedOutput = (output.dropLast(1) + listOf(removedPadding)) - //JS compiler freaks out here if we don't supply exact type - val reversed : List = preparedOutput.reversed() as List - val folded : UByteArray = reversed.foldRight(UByteArray(0) { 0U }) { uByteArray, acc -> - acc + uByteArray - } - return folded - - } - - private fun appendToBuffer(array: UByteArray, start: Int) { - array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size) - bufferCounter += array.size - } - - private fun consumeBlock(data: UByteArray): UByteArray { - return when (mode) { - Mode.ENCRYPT -> { - currentOutput = if (currentOutput.isEmpty()) { - println("IV: $initVector") - AesDelegated.encrypt(aesKey, data xor initVector) - } else { - AesDelegated.encrypt(aesKey, data xor currentOutput) - } - currentOutput - } - Mode.DECRYPT -> { - if (currentOutput.isEmpty()) { - currentOutput = AesDelegated.decrypt(aesKey, data) xor initVector - } else { - currentOutput = AesDelegated.decrypt(aesKey, data) xor previousEncrypted - } - previousEncrypted = data - currentOutput - } - } - - } - -} - - diff --git a/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrPure.kt b/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrPure.kt deleted file mode 100644 index 031412d..0000000 --- a/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrPure.kt +++ /dev/null @@ -1,209 +0,0 @@ -/* - * 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.symmetric - -import com.ionspin.kotlin.bignum.Endianness -import com.ionspin.kotlin.bignum.integer.BigInteger -import com.ionspin.kotlin.bignum.modular.ModularBigInteger -import com.ionspin.kotlin.crypto.SRNG -import com.ionspin.kotlin.crypto.symmetric.AesCtrDelegated.Companion.encrypt -import com.ionspin.kotlin.crypto.symmetric.aes.AesKey -import com.ionspin.kotlin.crypto.util.xor - -/** - * - * Advanced encryption standard with counter mode - * - * For bulk encryption/decryption use [AesCtrDelegated.encrypt] and [AesCtrDelegated.decrypt] - * - * To get an instance of AesCtr and then feed it data sequentially with [addData] use [createEncryptor] and [createDecryptor] - * - * Created by Ugljesa Jovanovic - * ugljesa.jovanovic@ionspin.com - * on 22-Sep-2019 - */ - -class AesCtrDelegated internal constructor(val aesKey: AesKey, val mode: Mode, initialCounter: UByteArray? = null) { - - companion object { - const val BLOCK_BYTES = 16 - - val modularCreator = ModularBigInteger.creatorForModulo(BigInteger.ONE.shl(128) - 1) - /** - * Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all - * data call [encrypt] - */ - fun createEncryptor(aesKey: AesKey) : AesCtrDelegated { - return AesCtrDelegated(aesKey, Mode.ENCRYPT) - } - /** - * Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all - * data call [decrypt] - */ - fun createDecryptor(aesKey : AesKey) : AesCtrDelegated { - return AesCtrDelegated(aesKey, Mode.DECRYPT) - } - /** - * Bulk encryption, returns encrypted data and a random initial counter - */ - fun encrypt(aesKey: AesKey, data: UByteArray): EncryptedDataAndInitialCounter { - val aesCtr = AesCtrDelegated(aesKey, Mode.ENCRYPT) - aesCtr.addData(data) - return aesCtr.encrypt() - } - /** - * Bulk decryption, returns decrypted data - */ - fun decrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray { - val aesCtr = AesCtrDelegated(aesKey, Mode.DECRYPT, initialCounter) - aesCtr.addData(data) - return aesCtr.decrypt() - } - - } - - var currentOutput: UByteArray = ubyteArrayOf() - var previousEncrypted: UByteArray = ubyteArrayOf() - val counterStart = if (initialCounter.isNullOrEmpty()) { - SRNG.getRandomBytes(16) - } else { - initialCounter - } - var blockCounter = modularCreator.fromBigInteger(BigInteger.fromUByteArray(counterStart.toTypedArray(), Endianness.BIG)) - - val output = MutableList(0) { ubyteArrayOf() } - - var buffer: UByteArray = UByteArray(16) { 0U } - var bufferCounter = 0 - - fun addData(data: UByteArray) { - //Padding - when { - bufferCounter + data.size < BLOCK_BYTES -> appendToBuffer(data, bufferCounter) - bufferCounter + data.size >= BLOCK_BYTES -> { - val chunked = data.chunked(BLOCK_BYTES) - chunked.forEach { chunk -> - if (bufferCounter + chunk.size < BLOCK_BYTES) { - appendToBuffer(chunk.toUByteArray(), bufferCounter) - } else { - chunk.toUByteArray().copyInto( - destination = buffer, - destinationOffset = bufferCounter, - startIndex = 0, - endIndex = BLOCK_BYTES - bufferCounter - ) - output += consumeBlock(buffer, blockCounter) - blockCounter += 1 - buffer = UByteArray(BLOCK_BYTES) { - when (it) { - in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> { - chunk[it + (BLOCK_BYTES - bufferCounter)] - } - else -> { - 0U - } - } - - } - bufferCounter = chunk.size - (BLOCK_BYTES - bufferCounter) - } - } - - } - } - - } - /** - * Encrypt fed data and return it alongside the randomly chosen initial counter state - * @return Encrypted data and initial counter state - */ - fun encrypt(): EncryptedDataAndInitialCounter { - if (bufferCounter > 0) { - output += consumeBlock(buffer, blockCounter) - } - return EncryptedDataAndInitialCounter( - output.reversed().foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes }, - counterStart - ) - } - /** - * Decrypt data - * @return Decrypted data - */ - fun decrypt(): UByteArray { - if (bufferCounter > 0) { - output += consumeBlock(buffer, blockCounter) - } - //JS compiler freaks out here if we don't supply exact type - val reversed: List = output.reversed() as List - val folded: UByteArray = reversed.foldRight(UByteArray(0) { 0U }) { arrayOfUBytes, acc -> - acc + arrayOfUBytes - } - return folded - } - - private fun appendToBuffer(array: UByteArray, start: Int) { - array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size) - bufferCounter += array.size - } - - private fun consumeBlock(data: UByteArray, blockCount: ModularBigInteger): UByteArray { - val blockCountAsByteArray = blockCount.toUByteArray(Endianness.BIG).toUByteArray().expandCounterTo16Bytes() - return when (mode) { - Mode.ENCRYPT -> { - AesDelegated.encrypt(aesKey, blockCountAsByteArray) xor data - } - Mode.DECRYPT -> { - AesDelegated.encrypt(aesKey, blockCountAsByteArray) xor data - } - } - - } - - private fun UByteArray.expandCounterTo16Bytes() : UByteArray { - return if (this.size < 16) { - println("Expanding") - val diff = 16 - this.size - val pad = UByteArray(diff) { 0U } - pad + this - } else { - this - } - } - -} - - -data class EncryptedDataAndInitialCounter(val encryptedData : UByteArray, val initialCounter : UByteArray) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || this::class != other::class) return false - - other as EncryptedDataAndInitializationVector - - if (!encryptedData.contentEquals(other.encryptedData)) return false - if (!initialCounter.contentEquals(other.initilizationVector)) return false - - return true - } - - override fun hashCode(): Int { - var result = encryptedData.contentHashCode() - result = 31 * result + initialCounter.contentHashCode() - return result - } -} \ No newline at end of file diff --git a/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesPure.kt b/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesPure.kt deleted file mode 100644 index 955ced5..0000000 --- a/multiplatform-crypto-delegated/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesPure.kt +++ /dev/null @@ -1,360 +0,0 @@ -package com.ionspin.kotlin.crypto.symmetric - -import com.ionspin.kotlin.crypto.symmetric.aes.AesKey -import com.ionspin.kotlin.crypto.util.flattenToUByteArray - -/** - * Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 07/Sep/2019 - */ - -internal class AesDelegated internal constructor(val aesKey: AesKey, val input: UByteArray) { - companion object { - private val debug = false - - private val sBox: UByteArray = - ubyteArrayOf( - // @formatter:off - 0x63U, 0x7cU, 0x77U, 0x7bU, 0xf2U, 0x6bU, 0x6fU, 0xc5U, 0x30U, 0x01U, 0x67U, 0x2bU, 0xfeU, 0xd7U, 0xabU, 0x76U, - 0xcaU, 0x82U, 0xc9U, 0x7dU, 0xfaU, 0x59U, 0x47U, 0xf0U, 0xadU, 0xd4U, 0xa2U, 0xafU, 0x9cU, 0xa4U, 0x72U, 0xc0U, - 0xb7U, 0xfdU, 0x93U, 0x26U, 0x36U, 0x3fU, 0xf7U, 0xccU, 0x34U, 0xa5U, 0xe5U, 0xf1U, 0x71U, 0xd8U, 0x31U, 0x15U, - 0x04U, 0xc7U, 0x23U, 0xc3U, 0x18U, 0x96U, 0x05U, 0x9aU, 0x07U, 0x12U, 0x80U, 0xe2U, 0xebU, 0x27U, 0xb2U, 0x75U, - 0x09U, 0x83U, 0x2cU, 0x1aU, 0x1bU, 0x6eU, 0x5aU, 0xa0U, 0x52U, 0x3bU, 0xd6U, 0xb3U, 0x29U, 0xe3U, 0x2fU, 0x84U, - 0x53U, 0xd1U, 0x00U, 0xedU, 0x20U, 0xfcU, 0xb1U, 0x5bU, 0x6aU, 0xcbU, 0xbeU, 0x39U, 0x4aU, 0x4cU, 0x58U, 0xcfU, - 0xd0U, 0xefU, 0xaaU, 0xfbU, 0x43U, 0x4dU, 0x33U, 0x85U, 0x45U, 0xf9U, 0x02U, 0x7fU, 0x50U, 0x3cU, 0x9fU, 0xa8U, - 0x51U, 0xa3U, 0x40U, 0x8fU, 0x92U, 0x9dU, 0x38U, 0xf5U, 0xbcU, 0xb6U, 0xdaU, 0x21U, 0x10U, 0xffU, 0xf3U, 0xd2U, - 0xcdU, 0x0cU, 0x13U, 0xecU, 0x5fU, 0x97U, 0x44U, 0x17U, 0xc4U, 0xa7U, 0x7eU, 0x3dU, 0x64U, 0x5dU, 0x19U, 0x73U, - 0x60U, 0x81U, 0x4fU, 0xdcU, 0x22U, 0x2aU, 0x90U, 0x88U, 0x46U, 0xeeU, 0xb8U, 0x14U, 0xdeU, 0x5eU, 0x0bU, 0xdbU, - 0xe0U, 0x32U, 0x3aU, 0x0aU, 0x49U, 0x06U, 0x24U, 0x5cU, 0xc2U, 0xd3U, 0xacU, 0x62U, 0x91U, 0x95U, 0xe4U, 0x79U, - 0xe7U, 0xc8U, 0x37U, 0x6dU, 0x8dU, 0xd5U, 0x4eU, 0xa9U, 0x6cU, 0x56U, 0xf4U, 0xeaU, 0x65U, 0x7aU, 0xaeU, 0x08U, - 0xbaU, 0x78U, 0x25U, 0x2eU, 0x1cU, 0xa6U, 0xb4U, 0xc6U, 0xe8U, 0xddU, 0x74U, 0x1fU, 0x4bU, 0xbdU, 0x8bU, 0x8aU, - 0x70U, 0x3eU, 0xb5U, 0x66U, 0x48U, 0x03U, 0xf6U, 0x0eU, 0x61U, 0x35U, 0x57U, 0xb9U, 0x86U, 0xc1U, 0x1dU, 0x9eU, - 0xe1U, 0xf8U, 0x98U, 0x11U, 0x69U, 0xd9U, 0x8eU, 0x94U, 0x9bU, 0x1eU, 0x87U, 0xe9U, 0xceU, 0x55U, 0x28U, 0xdfU, - 0x8cU, 0xa1U, 0x89U, 0x0dU, 0xbfU, 0xe6U, 0x42U, 0x68U, 0x41U, 0x99U, 0x2dU, 0x0fU, 0xb0U, 0x54U, 0xbbU, 0x16U - // @formatter:on - ) - - private val inverseSBox: UByteArray = - ubyteArrayOf( - // @formatter:off - 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, - 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, - 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, - 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, - 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, - 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, - 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, - 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, - 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, - 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, - 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, - 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, - 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, - 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, - 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, - 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU - // @formatter:on - ) - - val rcon: UByteArray = ubyteArrayOf(0x8DU, 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1BU, 0x36U) - - fun encrypt(aesKey: AesKey, input: UByteArray): UByteArray { - return AesDelegated(aesKey, input).encrypt() - } - - fun decrypt(aesKey: AesKey, input: UByteArray): UByteArray { - return AesDelegated(aesKey, input).decrypt() - } - - } - - val state: Array = (0 until 4).map { outerCounter -> - UByteArray(4) { innerCounter -> input[innerCounter * 4 + outerCounter] } - }.toTypedArray() - - val numberOfRounds = when (aesKey) { - is AesKey.Aes128Key -> 10 - is AesKey.Aes192Key -> 12 - is AesKey.Aes256Key -> 14 - } - - val expandedKey: Array = expandKey() - - - var round = 0 - var completed : Boolean = false - private set - - fun subBytes() { - state.forEachIndexed { indexRow, row -> - row.forEachIndexed { indexColumn, element -> - state[indexRow][indexColumn] = getSBoxValue(element) - } - } - } - - fun getSBoxValue(element: UByte): UByte { - val firstDigit = (element / 16U).toInt() - val secondDigit = (element % 16U).toInt() - return sBox[firstDigit * 16 + secondDigit] - } - - fun inverseSubBytes() { - state.forEachIndexed { indexRow, row -> - row.forEachIndexed { indexColumn, element -> - state[indexRow][indexColumn] = getInverseSBoxValue(element) - } - } - } - - fun getInverseSBoxValue(element: UByte): UByte { - val firstDigit = (element / 16U).toInt() - val secondDigit = (element % 16U).toInt() - return inverseSBox[firstDigit * 16 + secondDigit] - } - - fun shiftRows() { - state[0] = ubyteArrayOf(state[0][0], state[0][1], state[0][2], state[0][3]) - state[1] = ubyteArrayOf(state[1][1], state[1][2], state[1][3], state[1][0]) - state[2] = ubyteArrayOf(state[2][2], state[2][3], state[2][0], state[2][1]) - state[3] = ubyteArrayOf(state[3][3], state[3][0], state[3][1], state[3][2]) - } - - fun inversShiftRows() { - state[0] = ubyteArrayOf(state[0][0], state[0][1], state[0][2], state[0][3]) - state[1] = ubyteArrayOf(state[1][3], state[1][0], state[1][1], state[1][2]) - state[2] = ubyteArrayOf(state[2][2], state[2][3], state[2][0], state[2][1]) - state[3] = ubyteArrayOf(state[3][1], state[3][2], state[3][3], state[3][0]) - } - - fun mixColumns() { - val stateMixed: Array = (0 until 4).map { - UByteArray(4) { 0U } - }.toTypedArray() - for (c in 0..3) { - - stateMixed[0][c] = (2U gfm state[0][c]) xor (3U gfm state[1][c]) xor state[2][c] xor state[3][c] - stateMixed[1][c] = state[0][c] xor (2U gfm state[1][c]) xor (3U gfm state[2][c]) xor state[3][c] - stateMixed[2][c] = state[0][c] xor state[1][c] xor (2U gfm state[2][c]) xor (3U gfm state[3][c]) - stateMixed[3][c] = 3U gfm state[0][c] xor state[1][c] xor state[2][c] xor (2U gfm state[3][c]) - } - stateMixed.copyInto(state) - } - - fun inverseMixColumns() { - val stateMixed: Array = (0 until 4).map { - UByteArray(4) { 0U } - }.toTypedArray() - for (c in 0..3) { - stateMixed[0][c] = - (0x0eU gfm state[0][c]) xor (0x0bU gfm state[1][c]) xor (0x0dU gfm state[2][c]) xor (0x09U gfm state[3][c]) - stateMixed[1][c] = - (0x09U gfm state[0][c]) xor (0x0eU gfm state[1][c]) xor (0x0bU gfm state[2][c]) xor (0x0dU gfm state[3][c]) - stateMixed[2][c] = - (0x0dU gfm state[0][c]) xor (0x09U gfm state[1][c]) xor (0x0eU gfm state[2][c]) xor (0x0bU gfm state[3][c]) - stateMixed[3][c] = - (0x0bU gfm state[0][c]) xor (0x0dU gfm state[1][c]) xor (0x09U gfm state[2][c]) xor (0x0eU gfm state[3][c]) - } - stateMixed.copyInto(state) - } - - fun galoisFieldAdd(first: UByte, second: UByte): UByte { - return first xor second - } - - fun galoisFieldMultiply(first: UByte, second: UByte): UByte { - var result: UInt = 0U - var firstInt = first.toUInt() - var secondInt = second.toUInt() - var carry: UInt = 0U - for (i in 0..7) { - if (secondInt and 0x01U == 1U) { - result = result xor firstInt - } - carry = firstInt and 0x80U - firstInt = firstInt shl 1 - if (carry == 0x80U) { - firstInt = firstInt xor 0x001BU - } - secondInt = secondInt shr 1 - firstInt = firstInt and 0xFFU - } - return result.toUByte() - } - - fun addRoundKey() { - - for (i in 0 until 4) { - state[0][i] = state[0][i] xor expandedKey[round * 4 + i][0] - state[1][i] = state[1][i] xor expandedKey[round * 4 + i][1] - state[2][i] = state[2][i] xor expandedKey[round * 4 + i][2] - state[3][i] = state[3][i] xor expandedKey[round * 4 + i][3] - } - round++ - } - - fun inverseAddRoundKey() { - for (i in 0 until 4) { - state[0][i] = state[0][i] xor expandedKey[round * 4 + i][0] - state[1][i] = state[1][i] xor expandedKey[round * 4 + i][1] - state[2][i] = state[2][i] xor expandedKey[round * 4 + i][2] - state[3][i] = state[3][i] xor expandedKey[round * 4 + i][3] - } - round-- - } - - infix fun UInt.gfm(second: UByte): UByte { - return galoisFieldMultiply(this.toUByte(), second) - } - - fun expandKey(): Array { - val expandedKey = (0 until 4 * (numberOfRounds + 1)).map { - UByteArray(4) { 0U } - }.toTypedArray() - // First round - for (i in 0 until aesKey.numberOf32BitWords) { - expandedKey[i][0] = aesKey.keyArray[i * 4 + 0] - expandedKey[i][1] = aesKey.keyArray[i * 4 + 1] - expandedKey[i][2] = aesKey.keyArray[i * 4 + 2] - expandedKey[i][3] = aesKey.keyArray[i * 4 + 3] - } - - for (i in aesKey.numberOf32BitWords until 4 * (numberOfRounds + 1)) { - val temp = expandedKey[i - 1].copyOf() - if (i % aesKey.numberOf32BitWords == 0) { - //RotWord - val tempTemp = temp[0] - temp[0] = temp[1] - temp[1] = temp[2] - temp[2] = temp[3] - temp[3] = tempTemp - - //SubWord - temp[0] = getSBoxValue(temp[0]) - temp[1] = getSBoxValue(temp[1]) - temp[2] = getSBoxValue(temp[2]) - temp[3] = getSBoxValue(temp[3]) - - temp[0] = temp[0] xor rcon[i / aesKey.numberOf32BitWords] - - } else if (aesKey is AesKey.Aes256Key && i % aesKey.numberOf32BitWords == 4) { - temp[0] = getSBoxValue(temp[0]) - temp[1] = getSBoxValue(temp[1]) - temp[2] = getSBoxValue(temp[2]) - temp[3] = getSBoxValue(temp[3]) - } - expandedKey[i] = expandedKey[i - aesKey.numberOf32BitWords].mapIndexed { index, it -> - it xor temp[index] - }.toUByteArray() - clearArray(temp) - } - return expandedKey - } - - fun encrypt(): UByteArray { - if (completed) { - throw RuntimeException("Encrypt can only be called once per Aes instance, since the state is cleared at the " + - "end of the operation") - } - printState() - addRoundKey() - printState() - for (i in 0 until numberOfRounds - 1) { - subBytes() - printState() - shiftRows() - printState() - mixColumns() - printState() - addRoundKey() - printState() - } - - subBytes() - printState() - shiftRows() - printState() - addRoundKey() - printState() - val transposedMatrix = (0 until 4).map { outerCounter -> - UByteArray(4) { 0U } - } - for (i in 0 until 4) { - for (j in 0 until 4) { - transposedMatrix[i][j] = state[j][i] - } - } - state.forEach { clearArray(it) } - completed = true - return transposedMatrix.flattenToUByteArray() - } - - fun decrypt(): UByteArray { - if (completed) { - throw RuntimeException("Decrypt can only be called once per Aes instance, since the state is cleared at the " + - "end of the operation") - } - round = numberOfRounds - printState() - inverseAddRoundKey() - printState() - for (i in 0 until numberOfRounds - 1) { - inversShiftRows() - printState() - inverseSubBytes() - printState() - inverseAddRoundKey() - printState() - inverseMixColumns() - printState() - } - - inversShiftRows() - printState() - inverseSubBytes() - printState() - inverseAddRoundKey() - printState() - - val transposedMatrix = (0 until 4).map { outerCounter -> - UByteArray(4) { 0U } - } - for (i in 0 until 4) { - for (j in 0 until 4) { - transposedMatrix[i][j] = state[j][i] - } - } - state.forEach { clearArray(it) } - completed = true - return transposedMatrix.flattenToUByteArray() - } - - private fun clearArray(array : UByteArray) { - array.indices.forEach { array[it] = 0U } - } - - - - private fun printState() { - if (!debug) { - return - } - println() - state.forEach { - println(it.joinToString(separator = " ") { it.toString(16) }) - } - } - - private fun printState(specific : List) { - if (!debug) { - return - } - println() - specific.forEach { - println(it.joinToString(separator = " ") { it.toString(16) }) - } - } - - -} - - diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/Aes256GcmPure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/Aes256GcmPure.kt new file mode 100644 index 0000000..da554fe --- /dev/null +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/authenticated/Aes256GcmPure.kt @@ -0,0 +1,18 @@ +package com.ionspin.kotlin.crypto.authenticated + +/** + * Created by Ugljesa Jovanovic + * ugljesa.jovanovic@ionspin.com + * on 14-Jun-2020 + */ +class Aes256GcmStatelessPure : Aes256GcmStateless { + /** + * Nonce autogenerated + */ + override fun encrypt(message: UByteArray, additionalData: UByteArray, rawData : UByteArray, key:) : Aes256GcmEncryptionResult { + + TODO() + + } + +} \ No newline at end of file diff --git a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/aes/Aes.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/Aes.kt similarity index 60% rename from multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/aes/Aes.kt rename to multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/Aes.kt index e1db995..780939e 100644 --- a/multiplatform-crypto-api/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/aes/Aes.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/Aes.kt @@ -1,17 +1,17 @@ -package com.ionspin.kotlin.crypto.symmetric.aes +package com.ionspin.kotlin.crypto.symmetric /** * Created by Ugljesa Jovanovic * ugljesa.jovanovic@ionspin.com * on 13-Jun-2020 */ -sealed class AesKey(val key: String, val keyLength: Int) { +internal sealed class InternalAesKey(val key: String, val keyLength: Int) { val keyArray: UByteArray = key.chunked(2).map { it.toUByte(16) }.toUByteArray() val numberOf32BitWords = keyLength / 32 - class Aes128Key(key: String) : AesKey(key, 128) - class Aes192Key(key: String) : AesKey(key, 192) - class Aes256Key(key: String) : AesKey(key, 256) + class Aes128Key(key: String) : InternalAesKey(key, 128) + class Aes192Key(key: String) : InternalAesKey(key, 192) + class Aes256Key(key: String) : InternalAesKey(key, 256) init { checkKeyLength(key, keyLength) diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcPure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcPure.kt index 479f128..94cbf76 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcPure.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcPure.kt @@ -17,7 +17,6 @@ package com.ionspin.kotlin.crypto.symmetric import com.ionspin.kotlin.crypto.SRNG -import com.ionspin.kotlin.crypto.symmetric.aes.AesKey import com.ionspin.kotlin.crypto.util.xor /** @@ -32,7 +31,7 @@ import com.ionspin.kotlin.crypto.util.xor * on 21-Sep-2019 */ -class AesCbcPure internal constructor(val aesKey: AesKey, val mode: Mode, initializationVector: UByteArray? = null) { +internal class AesCbcPure internal constructor(val aesKey: InternalAesKey, val mode: Mode, initializationVector: UByteArray? = null) { companion object { const val BLOCK_BYTES = 16 @@ -40,21 +39,21 @@ class AesCbcPure internal constructor(val aesKey: AesKey, val mode: Mode, initia * Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all * data call [encrypt] */ - fun createEncryptor(aesKey: AesKey) : AesCbcPure { + fun createEncryptor(aesKey: InternalAesKey) : AesCbcPure { return AesCbcPure(aesKey, Mode.ENCRYPT) } /** * Creates and returns AesCbc instance that can be fed data using [addData]. Once you have submitted all * data call [decrypt] */ - fun createDecryptor(aesKey : AesKey) : AesCbcPure { + fun createDecryptor(aesKey : InternalAesKey) : AesCbcPure { return AesCbcPure(aesKey, Mode.DECRYPT) } /** * Bulk encryption, returns encrypted data and a random initialization vector */ - fun encrypt(aesKey: AesKey, data: UByteArray): EncryptedDataAndInitializationVector { + fun encrypt(aesKey: InternalAesKey, data: UByteArray): EncryptedDataAndInitializationVector { val aesCbc = AesCbcPure(aesKey, Mode.ENCRYPT) aesCbc.addData(data) return aesCbc.encrypt() @@ -63,7 +62,7 @@ class AesCbcPure internal constructor(val aesKey: AesKey, val mode: Mode, initia /** * Bulk decryption, returns decrypted data */ - fun decrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray { + fun decrypt(aesKey: InternalAesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray { val aesCbc = AesCbcPure(aesKey, Mode.DECRYPT, initialCounter) aesCbc.addData(data) return aesCbc.decrypt() @@ -221,7 +220,7 @@ class AesCbcPure internal constructor(val aesKey: AesKey, val mode: Mode, initia } -data class EncryptedDataAndInitializationVector(val encryptedData : UByteArray, val initilizationVector : UByteArray) { +data class EncryptedDataAndInitializationVector(val encryptedData : UByteArray, val initializationVector : UByteArray) { override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || this::class != other::class) return false @@ -229,14 +228,14 @@ data class EncryptedDataAndInitializationVector(val encryptedData : UByteArray, other as EncryptedDataAndInitializationVector if (!encryptedData.contentEquals(other.encryptedData)) return false - if (!initilizationVector.contentEquals(other.initilizationVector)) return false + if (!initializationVector.contentEquals(other.initializationVector)) return false return true } override fun hashCode(): Int { var result = encryptedData.contentHashCode() - result = 31 * result + initilizationVector.contentHashCode() + result = 31 * result + initializationVector.contentHashCode() return result } } \ No newline at end of file diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrPure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrPure.kt index 963923d..229a6a8 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrPure.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrPure.kt @@ -21,8 +21,6 @@ import com.ionspin.kotlin.bignum.integer.BigInteger import com.ionspin.kotlin.bignum.modular.ModularBigInteger import com.ionspin.kotlin.crypto.SRNG import com.ionspin.kotlin.crypto.symmetric.AesCtrPure.Companion.encrypt -import com.ionspin.kotlin.crypto.symmetric.aes.AesKey -import com.ionspin.kotlin.crypto.symmetric.aes.EncryptedDataAndInitialCounter import com.ionspin.kotlin.crypto.util.xor /** @@ -38,7 +36,7 @@ import com.ionspin.kotlin.crypto.util.xor * on 22-Sep-2019 */ -class AesCtrPure internal constructor(val aesKey: AesKey, val mode: Mode, initialCounter: UByteArray? = null) { +internal class AesCtrPure internal constructor(val aesKey: InternalAesKey, val mode: Mode, initialCounter: UByteArray? = null) { companion object { const val BLOCK_BYTES = 16 @@ -48,20 +46,20 @@ class AesCtrPure internal constructor(val aesKey: AesKey, val mode: Mode, initia * Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all * data call [encrypt] */ - fun createEncryptor(aesKey: AesKey) : AesCtrPure { + fun createEncryptor(aesKey: InternalAesKey) : AesCtrPure { return AesCtrPure(aesKey, Mode.ENCRYPT) } /** * Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all * data call [decrypt] */ - fun createDecryptor(aesKey : AesKey) : AesCtrPure { + fun createDecryptor(aesKey : InternalAesKey) : AesCtrPure { return AesCtrPure(aesKey, Mode.DECRYPT) } /** * Bulk encryption, returns encrypted data and a random initial counter */ - fun encrypt(aesKey: AesKey, data: UByteArray): EncryptedDataAndInitialCounter { + fun encrypt(aesKey: InternalAesKey, data: UByteArray): EncryptedDataAndInitialCounter { val aesCtr = AesCtrPure(aesKey, Mode.ENCRYPT) aesCtr.addData(data) return aesCtr.encrypt() @@ -69,7 +67,7 @@ class AesCtrPure internal constructor(val aesKey: AesKey, val mode: Mode, initia /** * Bulk decryption, returns decrypted data */ - fun decrypt(aesKey: AesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray { + fun decrypt(aesKey: InternalAesKey, data: UByteArray, initialCounter: UByteArray? = null): UByteArray { val aesCtr = AesCtrPure(aesKey, Mode.DECRYPT, initialCounter) aesCtr.addData(data) return aesCtr.decrypt() @@ -188,4 +186,23 @@ class AesCtrPure internal constructor(val aesKey: AesKey, val mode: Mode, initia } +data class EncryptedDataAndInitialCounter(val encryptedData : UByteArray, val initialCounter : UByteArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as EncryptedDataAndInitialCounter + + if (!encryptedData.contentEquals(other.encryptedData)) return false + if (!initialCounter.contentEquals(other.initialCounter)) return false + + return true + } + + override fun hashCode(): Int { + var result = encryptedData.contentHashCode() + result = 31 * result + initialCounter.contentHashCode() + return result + } +} diff --git a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesPure.kt b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesPure.kt index 2a47a24..d20b8e4 100644 --- a/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesPure.kt +++ b/multiplatform-crypto/src/commonMain/kotlin/com/ionspin/kotlin/crypto/symmetric/AesPure.kt @@ -6,7 +6,7 @@ import com.ionspin.kotlin.crypto.util.flattenToUByteArray * Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 07/Sep/2019 */ -internal class AesPure internal constructor(val aesKey: AesKey, val input: UByteArray) { +internal class AesPure internal constructor(val aesKey: InternalAesKey, val input: UByteArray) { companion object { private val debug = false @@ -56,11 +56,11 @@ internal class AesPure internal constructor(val aesKey: AesKey, val input: UByte val rcon: UByteArray = ubyteArrayOf(0x8DU, 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1BU, 0x36U) - fun encrypt(aesKey: AesKey, input: UByteArray): UByteArray { + fun encrypt(aesKey: InternalAesKey, input: UByteArray): UByteArray { return AesPure(aesKey, input).encrypt() } - fun decrypt(aesKey: AesKey, input: UByteArray): UByteArray { + fun decrypt(aesKey: InternalAesKey, input: UByteArray): UByteArray { return AesPure(aesKey, input).decrypt() } @@ -71,9 +71,9 @@ internal class AesPure internal constructor(val aesKey: AesKey, val input: UByte }.toTypedArray() val numberOfRounds = when (aesKey) { - is AesKey.Aes128Key -> 10 - is AesKey.Aes192Key -> 12 - is AesKey.Aes256Key -> 14 + is InternalAesKey.Aes128Key -> 10 + is InternalAesKey.Aes192Key -> 12 + is InternalAesKey.Aes256Key -> 14 } val expandedKey: Array = expandKey() @@ -235,7 +235,7 @@ internal class AesPure internal constructor(val aesKey: AesKey, val input: UByte temp[0] = temp[0] xor rcon[i / aesKey.numberOf32BitWords] - } else if (aesKey is AesKey.Aes256Key && i % aesKey.numberOf32BitWords == 4) { + } else if (aesKey is InternalAesKey.Aes256Key && i % aesKey.numberOf32BitWords == 4) { temp[0] = getSBoxValue(temp[0]) temp[1] = getSBoxValue(temp[1]) temp[2] = getSBoxValue(temp[2]) diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcTest.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcTest.kt index 4196616..d2b25b3 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcTest.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCbcTest.kt @@ -37,7 +37,7 @@ class AesCbcTest { val plaintext = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5" val expectedCipherText = "479c89ec14bc98994e62b2c705b5014e175bd7832e7e60a1e92aac568a861eb7" val aesCbc = - AesCbcPure(AesKey.Aes128Key(key), mode = Mode.ENCRYPT, initializationVector = iv.hexStringToUByteArray()) + AesCbcPure(InternalAesKey.Aes128Key(key), mode = Mode.ENCRYPT, initializationVector = iv.hexStringToUByteArray()) aesCbc.addData(plaintext.hexStringToUByteArray()) val encrypted = aesCbc.encrypt() println("Encrypted: ${encrypted.encryptedData.toHexString()}") @@ -54,7 +54,7 @@ class AesCbcTest { fun testEncryptionApi() { assertTrue { val keyString = "4278b840fb44aaa757c1bf04acbe1a3e" - val key = AesKey.Aes128Key(keyString) + val key = InternalAesKey.Aes128Key(keyString) val plainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5" @@ -76,7 +76,7 @@ class AesCbcTest { val cipherText = "479c89ec14bc98994e62b2c705b5014e175bd7832e7e60a1e92aac568a861eb7" val expectedPlainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5" val aesCbc = - AesCbcPure(AesKey.Aes128Key(key), mode = Mode.DECRYPT, initializationVector = iv.hexStringToUByteArray()) + AesCbcPure(InternalAesKey.Aes128Key(key), mode = Mode.DECRYPT, initializationVector = iv.hexStringToUByteArray()) aesCbc.addData(cipherText.hexStringToUByteArray()) val decrypted = aesCbc.decrypt() println("Decrypted: ${decrypted.toHexString()}") @@ -95,7 +95,7 @@ class AesCbcTest { val iv = "57f02a5c5339daeb0a2908a06ac6393f" val cipherText = "479c89ec14bc98994e62b2c705b5014e175bd7832e7e60a1e92aac568a861eb7" val expectedPlainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5" - val decrypted = AesCbcPure.decrypt(AesKey.Aes128Key(key), cipherText.hexStringToUByteArray(), iv.hexStringToUByteArray()) + val decrypted = AesCbcPure.decrypt(InternalAesKey.Aes128Key(key), cipherText.hexStringToUByteArray(), iv.hexStringToUByteArray()) println("Decrypted: ${decrypted.toHexString()}") expectedPlainText == decrypted.toHexString() diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrTest.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrTest.kt index 480374a..457e59c 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrTest.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesCtrTest.kt @@ -38,7 +38,7 @@ class AesCtrTest { "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710" val expectedCipherText = "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee" - val aesCtr = AesCtrPure(AesKey.Aes128Key(key), mode = Mode.ENCRYPT, initialCounter = ic.hexStringToUByteArray()) + val aesCtr = AesCtrPure(InternalAesKey.Aes128Key(key), mode = Mode.ENCRYPT, initialCounter = ic.hexStringToUByteArray()) aesCtr.addData( plaintext.hexStringToUByteArray() ) @@ -54,7 +54,7 @@ class AesCtrTest { @Test fun testEncryptionApi() { val keyString = "4278b840fb44aaa757c1bf04acbe1a3e" - val key = AesKey.Aes128Key(keyString) + val key = InternalAesKey.Aes128Key(keyString) val plainText = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710" val encryptedDataAndInitializationVector = AesCtrPure.encrypt(key, plainText.hexStringToUByteArray()) @@ -77,7 +77,7 @@ class AesCtrTest { "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee" val expectedPlainText = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710" - val aesCtr = AesCtrPure(AesKey.Aes128Key(key), mode = Mode.DECRYPT, initialCounter = ic.hexStringToUByteArray()) + val aesCtr = AesCtrPure(InternalAesKey.Aes128Key(key), mode = Mode.DECRYPT, initialCounter = ic.hexStringToUByteArray()) aesCtr.addData(cipherText.hexStringToUByteArray()) val decrypted = aesCtr.decrypt() println("Decrypted: ${decrypted.toHexString()}") @@ -97,7 +97,7 @@ class AesCtrTest { "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee" val expectedPlainText = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710" - val decrypted = AesCtrPure.decrypt(AesKey.Aes128Key(key), cipherText.hexStringToUByteArray(), ic.hexStringToUByteArray()) + val decrypted = AesCtrPure.decrypt(InternalAesKey.Aes128Key(key), cipherText.hexStringToUByteArray(), ic.hexStringToUByteArray()) println("Decrypted: ${decrypted.toHexString()}") expectedPlainText == decrypted.toHexString() } diff --git a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesTest.kt b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesTest.kt index e733cb0..c898346 100644 --- a/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesTest.kt +++ b/multiplatform-crypto/src/commonTest/kotlin/com/ionspin/kotlin/crypto/symmetric/AesTest.kt @@ -20,7 +20,7 @@ class AesTest { ubyteArrayOf(0U, 0U, 0U, 0U), ubyteArrayOf(0U, 0U, 0U, 0U) ) - val aes = AesPure(AesKey.Aes128Key(irrelevantKey), irrelevantInput) + val aes = AesPure(InternalAesKey.Aes128Key(irrelevantKey), irrelevantInput) fakeState.copyInto(aes.state) aes.subBytes() assertTrue { @@ -42,7 +42,7 @@ class AesTest { ubyteArrayOf(2U, 3U, 0U, 1U), ubyteArrayOf(3U, 0U, 1U, 2U) ) - val aes = AesPure(AesKey.Aes128Key(irrelevantKey), irrelevantInput) + val aes = AesPure(InternalAesKey.Aes128Key(irrelevantKey), irrelevantInput) fakeState.copyInto(aes.state) aes.shiftRows() assertTrue { @@ -56,7 +56,7 @@ class AesTest { assertTrue { val a = 0x57U val b = 0x83U - val aes = AesPure(AesKey.Aes128Key(irrelevantKey), irrelevantInput) + val aes = AesPure(InternalAesKey.Aes128Key(irrelevantKey), irrelevantInput) val c = aes.galoisFieldMultiply(a.toUByte(), b.toUByte()) c == 0xC1U.toUByte() } @@ -64,7 +64,7 @@ class AesTest { assertTrue { val a = 0x57U val b = 0x13U - val aes = AesPure(AesKey.Aes128Key(irrelevantKey), irrelevantInput) + val aes = AesPure(InternalAesKey.Aes128Key(irrelevantKey), irrelevantInput) val c = aes.galoisFieldMultiply(a.toUByte(), b.toUByte()) c == 0xFEU.toUByte() } @@ -89,7 +89,7 @@ class AesTest { ubyteArrayOf(0xbcU, 0x9dU, 0x01U, 0xc6U) ) - val aes = AesPure(AesKey.Aes128Key(irrelevantKey), irrelevantInput) + val aes = AesPure(InternalAesKey.Aes128Key(irrelevantKey), irrelevantInput) fakeState.copyInto(aes.state) aes.mixColumns() assertTrue { @@ -116,7 +116,7 @@ class AesTest { ).toTypedArray() - val aes = AesPure(AesKey.Aes128Key(key), irrelevantInput) + val aes = AesPure(InternalAesKey.Aes128Key(key), irrelevantInput) val result = aes.expandedKey.map { it.foldIndexed(0U) { index, acc, uByte -> acc + (uByte.toUInt() shl (24 - index * 8)) @@ -140,7 +140,7 @@ class AesTest { ).toTypedArray() - val aes = AesPure(AesKey.Aes192Key(key), irrelevantInput) + val aes = AesPure(InternalAesKey.Aes192Key(key), irrelevantInput) val result = aes.expandedKey.map { it.foldIndexed(0U) { index, acc, uByte -> acc + (uByte.toUInt() shl (24 - index * 8)) @@ -166,7 +166,7 @@ class AesTest { ).toTypedArray() - val aes = AesPure(AesKey.Aes256Key(key), irrelevantInput) + val aes = AesPure(InternalAesKey.Aes256Key(key), irrelevantInput) val result = aes.expandedKey.map { it.foldIndexed(0U) { index, acc, uByte -> acc + (uByte.toUInt() shl (24 - index * 8)) @@ -183,7 +183,7 @@ class AesTest { val key = "2b7e151628aed2a6abf7158809cf4f3c" val expectedResult = "3925841d02dc09fbdc118597196a0b32" - val aes = AesPure(AesKey.Aes128Key(key), input.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray()) + val aes = AesPure(InternalAesKey.Aes128Key(key), input.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray()) val result = aes.encrypt() assertTrue { result.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray()) @@ -197,12 +197,12 @@ class AesTest { val key = "2b7e151628aed2a6abf7158809cf4f3c" val expectedResult = "3925841d02dc09fbdc118597196a0b32" val original = input.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray() - val aes = AesPure(AesKey.Aes128Key(key), original) + val aes = AesPure(InternalAesKey.Aes128Key(key), original) val encrypted = aes.encrypt() assertTrue { encrypted.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray()) } - val decrypted = AesPure.decrypt(AesKey.Aes128Key(key), encrypted) + val decrypted = AesPure.decrypt(InternalAesKey.Aes128Key(key), encrypted) decrypted.contentEquals(original) } @@ -211,12 +211,12 @@ class AesTest { val key = "000102030405060708090a0b0c0d0e0f" val expectedResult = "69c4e0d86a7b0430d8cdb78070b4c55a" val original = input.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray() - val aes = AesPure(AesKey.Aes128Key(key), original) + val aes = AesPure(InternalAesKey.Aes128Key(key), original) val encrypted = aes.encrypt() assertTrue { encrypted.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray()) } - val aesDec = AesPure(AesKey.Aes128Key(key), encrypted) + val aesDec = AesPure(InternalAesKey.Aes128Key(key), encrypted) val decrypted = aesDec.decrypt() assertTrue { aesDec.expandedKey.contentDeepEquals(aes.expandedKey) @@ -229,11 +229,11 @@ class AesTest { val key = "000102030405060708090a0b0c0d0e0f" val expectedResult = "69c4e0d86a7b0430d8cdb78070b4c55a" val original = input.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray() - val encrypted = AesPure.encrypt(AesKey.Aes128Key(key), original) + val encrypted = AesPure.encrypt(InternalAesKey.Aes128Key(key), original) assertTrue { encrypted.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray()) } - val decrypted = AesPure.decrypt(AesKey.Aes128Key(key), encrypted) + val decrypted = AesPure.decrypt(InternalAesKey.Aes128Key(key), encrypted) decrypted.contentEquals(original) } @@ -242,11 +242,11 @@ class AesTest { val key = "000102030405060708090a0b0c0d0e0f1011121314151617" val expectedResult = "dda97ca4864cdfe06eaf70a0ec0d7191" val original = input.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray() - val encrypted = AesPure.encrypt(AesKey.Aes192Key(key), original) + val encrypted = AesPure.encrypt(InternalAesKey.Aes192Key(key), original) assertTrue { encrypted.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray()) } - val decrypted = AesPure.decrypt(AesKey.Aes192Key(key), encrypted) + val decrypted = AesPure.decrypt(InternalAesKey.Aes192Key(key), encrypted) decrypted.contentEquals(original) } @@ -255,11 +255,11 @@ class AesTest { val key = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" val expectedResult = "8ea2b7ca516745bfeafc49904b496089" val original = input.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray() - val encrypted = AesPure.encrypt(AesKey.Aes256Key(key), original) + val encrypted = AesPure.encrypt(InternalAesKey.Aes256Key(key), original) assertTrue { encrypted.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toUByteArray()) } - val decrypted = AesPure.decrypt(AesKey.Aes256Key(key), encrypted) + val decrypted = AesPure.decrypt(InternalAesKey.Aes256Key(key), encrypted) decrypted.contentEquals(original) } }