Aes implemented

This commit is contained in:
Ugljesa Jovanovic 2019-09-23 23:14:55 +02:00 committed by Ugljesa Jovanovic
parent 69c81ec8e9
commit ec3b87db49
No known key found for this signature in database
GPG Key ID: 33A5F353387711A5
5 changed files with 302 additions and 111 deletions

View File

@ -7,6 +7,9 @@ Kotlin Multiplatform Crypto is a library for various cryptographic applications.
This is an extremely early release, currently only consisting of Blake2b and SHA256 and 512. This is an extremely early release, currently only consisting of Blake2b and SHA256 and 512.
API is very opinionated, ment to be used on both encrypting and decrypting side. The idea is that API leaves less room for
errors when using it.
## Notes & Roadmap ## Notes & Roadmap
**The API will move fast and break often until v1.0** **The API will move fast and break often until v1.0**
@ -28,8 +31,6 @@ No.
This is an experimental implementation, mostly for expanding personal understanding of cryptography. This is an experimental implementation, mostly for expanding personal understanding of cryptography.
It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be secure. It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be secure.
## Supported
## Hashing functions ## Hashing functions
* Blake2b * Blake2b
* SHA512 * SHA512
@ -37,6 +38,7 @@ It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be
## Symmetric cipher ## Symmetric cipher
* AES * AES
* Modes: CBC, CTR
More to come. More to come.
@ -129,6 +131,58 @@ val sha512 = Sha512()
sha512.update("abc") sha512.update("abc")
val result = sha512.digest() val result = sha512.digest()
``` ```
### Symmetric encryption
#### AES
Aes is available with CBC and CTR mode through `AesCbc` and `AesCtr` classes/objects.
Similarly to hashes you can either use stateless or updateable version.
Initialization vector, or counter states are chosen by the SDK automaticaly, and returned alongside encrypted data
##### Stateless AesCbc and AesCtr
AesCtr
```kotlin
val keyString = "4278b840fb44aaa757c1bf04acbe1a3e"
val key = AesKey.Aes128Key(keyString)
val plainText = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"
val encryptedDataAndInitializationVector = AesCtr.encrypt(key, plainText.hexStringToUByteArray())
val decrypted = AesCtr.decrypt(
key,
encryptedDataAndInitializationVector.encryptedData,
encryptedDataAndInitializationVector.initialCounter
)
plainText == decrypted.toHexString()
```
AesCbc
```kotlin
val keyString = "4278b840fb44aaa757c1bf04acbe1a3e"
val key = AesKey.Aes128Key(keyString)
val plainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5"
val encryptedDataAndInitializationVector = AesCbc.encrypt(key, plainText.hexStringToUByteArray())
val decrypted = AesCbc.decrypt(
key,
encryptedDataAndInitializationVector.encryptedData,
encryptedDataAndInitializationVector.initilizationVector
)
plainText == decrypted.toHexString()
```

View File

@ -21,6 +21,12 @@ import com.ionspin.kotlin.crypto.chunked
import com.ionspin.kotlin.crypto.xor import com.ionspin.kotlin.crypto.xor
/** /**
* Advanced encryption standard with cipher block chaining and PKCS #5
*
* For bulk encryption/decryption use [AesCbc.encrypt] and [AesCbc.decrypt]
*
* To get an instance of AesCbc and then feed it data sequentially with [addData] use [createEncryptor] and [createDecryptor]
*
* Created by Ugljesa Jovanovic * Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com * ugljesa.jovanovic@ionspin.com
* on 21-Sep-2019 * on 21-Sep-2019
@ -30,13 +36,39 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
companion object { companion object {
const val BLOCK_BYTES = 16 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) : AesCbc {
return AesCbc(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) : AesCbc {
return AesCbc(aesKey, Mode.DECRYPT)
}
fun encrypt(aesKey: AesKey, data: Array<UByte>): Array<UByte> { /**
* Bulk encryption, returns encrypted data and a random initialization vector
*/
fun encrypt(aesKey: AesKey, data: Array<UByte>): EncryptedDataAndInitializationVector {
val aesCbc = AesCbc(aesKey, Mode.ENCRYPT) val aesCbc = AesCbc(aesKey, Mode.ENCRYPT)
aesCbc.addData(data) aesCbc.addData(data)
return aesCbc.encrypt() return aesCbc.encrypt()
} }
/**
* Bulk decryption, returns decrypted data
*/
fun decrypt(aesKey: AesKey, data: Array<UByte>, initialCounter: Array<UByte>? = null): Array<UByte> {
val aesCbc = AesCbc(aesKey, Mode.DECRYPT, initialCounter)
aesCbc.addData(data)
return aesCbc.decrypt()
}
private fun padToBlock(unpadded: Array<UByte>): Array<UByte> { private fun padToBlock(unpadded: Array<UByte>): Array<UByte> {
val paddingSize = 16 - unpadded.size val paddingSize = 16 - unpadded.size
if (unpadded.size == BLOCK_BYTES) { if (unpadded.size == BLOCK_BYTES) {
@ -109,7 +141,11 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
} }
fun encrypt(): Array<UByte> { /**
* Encrypt fed data and return it alongside the randomly chosen initialization vector
* @return Encrypted data and initialization vector
*/
fun encrypt(): EncryptedDataAndInitializationVector {
if (bufferCounter > 0) { if (bufferCounter > 0) {
val lastBlockPadded = padToBlock(buffer) val lastBlockPadded = padToBlock(buffer)
if (lastBlockPadded.size > BLOCK_BYTES) { if (lastBlockPadded.size > BLOCK_BYTES) {
@ -120,9 +156,16 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
output += consumeBlock(lastBlockPadded) output += consumeBlock(lastBlockPadded)
} }
} }
return output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes } return EncryptedDataAndInitializationVector(
output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
iv
)
} }
/**
* Decrypt data
* @return Decrypted data
*/
fun decrypt(): Array<UByte> { fun decrypt(): Array<UByte> {
val removePaddingCount = output.last().last() val removePaddingCount = output.last().last()
@ -170,3 +213,24 @@ class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializa
} }
} }
@ExperimentalUnsignedTypes
data class EncryptedDataAndInitializationVector(val encryptedData : Array<UByte>, val initilizationVector : Array<UByte>) {
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 (!initilizationVector.contentEquals(other.initilizationVector)) return false
return true
}
override fun hashCode(): Int {
var result = encryptedData.contentHashCode()
result = 31 * result + initilizationVector.contentHashCode()
return result
}
}

View File

@ -21,9 +21,17 @@ import com.ionspin.kotlin.bignum.integer.BigInteger
import com.ionspin.kotlin.bignum.modular.ModularBigInteger import com.ionspin.kotlin.bignum.modular.ModularBigInteger
import com.ionspin.kotlin.crypto.SRNG import com.ionspin.kotlin.crypto.SRNG
import com.ionspin.kotlin.crypto.chunked import com.ionspin.kotlin.crypto.chunked
import com.ionspin.kotlin.crypto.symmetric.AesCtr.Companion.encrypt
import com.ionspin.kotlin.crypto.xor import com.ionspin.kotlin.crypto.xor
/** /**
*
* Advanced encryption standard with counter mode
*
* For bulk encryption/decryption use [AesCtr.encrypt] and [AesCtr.decrypt]
*
* To get an instance of AesCtr and then feed it data sequentially with [addData] use [createEncryptor] and [createDecryptor]
*
* Created by Ugljesa Jovanovic * Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com * ugljesa.jovanovic@ionspin.com
* on 22-Sep-2019 * on 22-Sep-2019
@ -34,38 +42,38 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
companion object { companion object {
const val BLOCK_BYTES = 16 const val BLOCK_BYTES = 16
val modularCreator = ModularBigInteger.creatorForModulo(BigInteger.ONE.shl(129) - 1) val modularCreator = ModularBigInteger.creatorForModulo(BigInteger.ONE.shl(128) - 1)
/**
fun encrypt(aesKey: AesKey, data: Array<UByte>): Array<UByte> { * Creates and returns AesCtr instance that can be fed data using [addData]. Once you have submitted all
val aesCbc = AesCbc(aesKey, Mode.ENCRYPT) * data call [encrypt]
aesCbc.addData(data) */
return aesCbc.encrypt() fun createEncryptor(aesKey: AesKey) : AesCtr {
return AesCtr(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) : AesCtr {
return AesCtr(aesKey, Mode.DECRYPT)
}
/**
* Bulk encryption, returns encrypted data and a random initial counter
*/
fun encrypt(aesKey: AesKey, data: Array<UByte>): EncryptedDataAndInitialCounter {
val aesCtr = AesCtr(aesKey, Mode.ENCRYPT)
aesCtr.addData(data)
return aesCtr.encrypt()
}
/**
* Bulk decryption, returns decrypted data
*/
fun decrypt(aesKey: AesKey, data: Array<UByte>, initialCounter: Array<UByte>? = null): Array<UByte> {
val aesCtr = AesCtr(aesKey, Mode.DECRYPT, initialCounter)
aesCtr.addData(data)
return aesCtr.decrypt()
} }
private fun padToBlock(unpadded: Array<UByte>): Array<UByte> {
val paddingSize = 16 - unpadded.size
if (unpadded.size == BLOCK_BYTES) {
return unpadded
}
if (unpadded.size == BLOCK_BYTES) {
return Array(BLOCK_BYTES) {
BLOCK_BYTES.toUByte()
}
}
if (unpadded.size > BLOCK_BYTES) {
throw IllegalStateException("Block larger than 128 bytes")
}
return Array(BLOCK_BYTES) {
when (it) {
in unpadded.indices -> unpadded[it]
else -> paddingSize.toUByte()
}
}
}
} }
var currentOutput: Array<UByte> = arrayOf() var currentOutput: Array<UByte> = arrayOf()
@ -115,34 +123,32 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
} }
} }
/**
fun encrypt(): Array<UByte> { * 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) { if (bufferCounter > 0) {
val lastBlockPadded = padToBlock(buffer) output += consumeBlock(buffer, blockCounter)
if (lastBlockPadded.size > BLOCK_BYTES) {
val chunks = lastBlockPadded.chunked(BLOCK_BYTES)
output += consumeBlock(chunks[0], blockCounter)
blockCounter += 1
output += consumeBlock(chunks[1], blockCounter)
} else {
output += consumeBlock(lastBlockPadded, blockCounter)
} }
return EncryptedDataAndInitialCounter(
output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
counterStart
)
} }
return output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes } /**
} * Decrypt data
* @return Decrypted data
*/
fun decrypt(): Array<UByte> { fun decrypt(): Array<UByte> {
val removePaddingCount = output.last().last() if (bufferCounter > 0) {
val removedPadding = if (removePaddingCount > 0U && removePaddingCount < 16U) { output += consumeBlock(buffer, blockCounter)
output.last().dropLast(removePaddingCount.toInt() and 0x7F)
} else {
output.last().toList()
} }
val preparedOutput = output.dropLast(1).toTypedArray() + removedPadding.toTypedArray()
//JS compiler freaks out here if we don't supply exact type //JS compiler freaks out here if we don't supply exact type
val reversed : List<Array<UByte>> = preparedOutput.reversed() as List<Array<UByte>> val reversed: List<Array<UByte>> = output.reversed() as List<Array<UByte>>
val folded: Array<UByte> = reversed.foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> val folded: Array<UByte> = reversed.foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc ->
acc + arrayOfUBytes } acc + arrayOfUBytes
}
return folded return folded
} }
@ -164,3 +170,24 @@ class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCou
} }
} }
@ExperimentalUnsignedTypes
data class EncryptedDataAndInitialCounter(val encryptedData : Array<UByte>, val initialCounter : Array<UByte>) {
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
}
}

View File

@ -31,43 +31,67 @@ class AesCbcTest {
@Test @Test
fun testCbcEncryption() { fun testCbcEncryption() {
assertTrue {
val key = "4278b840fb44aaa757c1bf04acbe1a3e" val key = "4278b840fb44aaa757c1bf04acbe1a3e"
val iv = "57f02a5c5339daeb0a2908a06ac6393f" val iv = "57f02a5c5339daeb0a2908a06ac6393f"
val plaintext = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5" val plaintext = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5"
val expectedCipherText = "479c89ec14bc98994e62b2c705b5014e175bd7832e7e60a1e92aac568a861eb7" val expectedCipherText = "479c89ec14bc98994e62b2c705b5014e175bd7832e7e60a1e92aac568a861eb7"
val aesCbc = AesCbc(AesKey.Aes128Key(key), mode = Mode.ENCRYPT, initializationVector = iv.hexStringToUByteArray()) val aesCbc =
AesCbc(AesKey.Aes128Key(key), mode = Mode.ENCRYPT, initializationVector = iv.hexStringToUByteArray())
aesCbc.addData(plaintext.hexStringToUByteArray()) aesCbc.addData(plaintext.hexStringToUByteArray())
val encrypted = aesCbc.encrypt() val encrypted = aesCbc.encrypt()
println("Encrypted: ${encrypted.toHexString()}") println("Encrypted: ${encrypted.encryptedData.toHexString()}")
assertTrue {
expectedCipherText == encrypted.toHexString() expectedCipherText == encrypted.encryptedData.toHexString() &&
iv == encrypted.initilizationVector.toHexString()
} }
assertTrue {
val keyString = "4278b840fb44aaa757c1bf04acbe1a3e"
val key = AesKey.Aes128Key(keyString)
val plainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5"
val encryptedDataAndInitializationVector = AesCbc.encrypt(key, plainText.hexStringToUByteArray())
val decrypted = AesCbc.decrypt(
key,
encryptedDataAndInitializationVector.encryptedData,
encryptedDataAndInitializationVector.initilizationVector
)
plainText == decrypted.toHexString()
}
} }
@Test @Test
fun testCbcDecryption() { fun testCbcDecryption() {
assertTrue {
val key = "4278b840fb44aaa757c1bf04acbe1a3e" val key = "4278b840fb44aaa757c1bf04acbe1a3e"
val iv = "57f02a5c5339daeb0a2908a06ac6393f" val iv = "57f02a5c5339daeb0a2908a06ac6393f"
val cipherText = "479c89ec14bc98994e62b2c705b5014e175bd7832e7e60a1e92aac568a861eb7" val cipherText = "479c89ec14bc98994e62b2c705b5014e175bd7832e7e60a1e92aac568a861eb7"
val expectedPlainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5" val expectedPlainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5"
val aesCbc = AesCbc(AesKey.Aes128Key(key), mode = Mode.DECRYPT, initializationVector = iv.hexStringToUByteArray()) val aesCbc =
AesCbc(AesKey.Aes128Key(key), mode = Mode.DECRYPT, initializationVector = iv.hexStringToUByteArray())
aesCbc.addData(cipherText.hexStringToUByteArray()) aesCbc.addData(cipherText.hexStringToUByteArray())
val decrypted = aesCbc.decrypt() val decrypted = aesCbc.decrypt()
println("Decrypted: ${decrypted.toHexString()}") println("Decrypted: ${decrypted.toHexString()}")
expectedPlainText == decrypted.toHexString()
}
assertTrue { assertTrue {
val key = "4278b840fb44aaa757c1bf04acbe1a3e"
val iv = "57f02a5c5339daeb0a2908a06ac6393f"
val cipherText = "479c89ec14bc98994e62b2c705b5014e175bd7832e7e60a1e92aac568a861eb7"
val expectedPlainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5"
val decrypted = AesCbc.decrypt(AesKey.Aes128Key(key), cipherText.hexStringToUByteArray(), iv.hexStringToUByteArray())
println("Decrypted: ${decrypted.toHexString()}")
expectedPlainText == decrypted.toHexString() expectedPlainText == decrypted.toHexString()
} }
} }
} }

View File

@ -31,45 +31,67 @@ class AesCtrTest {
@Test @Test
fun testCtrEncryption() { fun testCtrEncryption() {
assertTrue {
val key = "2b7e151628aed2a6abf7158809cf4f3c" val key = "2b7e151628aed2a6abf7158809cf4f3c"
val ic = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" val ic = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
val plaintext = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710" val plaintext =
val expectedCipherText = "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee" "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"
val aesCbc = AesCtr(AesKey.Aes128Key(key), mode = Mode.ENCRYPT, initialCounter = ic.hexStringToUByteArray()) val expectedCipherText =
aesCbc.addData( "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee"
val aesCtr = AesCtr(AesKey.Aes128Key(key), mode = Mode.ENCRYPT, initialCounter = ic.hexStringToUByteArray())
aesCtr.addData(
plaintext.hexStringToUByteArray() plaintext.hexStringToUByteArray()
) )
val encrypted = aesCbc.encrypt() val encrypted = aesCtr.encrypt()
println("Encrypted: ${encrypted.toHexString()}") println("Encrypted: ${encrypted.encryptedData.toHexString()}")
expectedCipherText == encrypted.encryptedData.toHexString() &&
ic == encrypted.initialCounter.toHexString()
}
assertTrue { assertTrue {
expectedCipherText == encrypted.toHexString() val keyString = "4278b840fb44aaa757c1bf04acbe1a3e"
val key = AesKey.Aes128Key(keyString)
val plainText = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"
val encryptedDataAndInitializationVector = AesCtr.encrypt(key, plainText.hexStringToUByteArray())
val decrypted = AesCtr.decrypt(
key,
encryptedDataAndInitializationVector.encryptedData,
encryptedDataAndInitializationVector.initialCounter
)
plainText == decrypted.toHexString()
} }
} }
@Test @Test
fun testCtrDecryption() { fun testCtrDecryption() {
assertTrue {
val key = "2b7e151628aed2a6abf7158809cf4f3c" val key = "2b7e151628aed2a6abf7158809cf4f3c"
val ic = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" val ic = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
val cipherText = "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee" val cipherText =
val expectedPlainText = "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710" "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee"
val aesCbc = AesCtr(AesKey.Aes128Key(key), mode = Mode.DECRYPT, initialCounter = ic.hexStringToUByteArray()) val expectedPlainText =
aesCbc.addData(cipherText.hexStringToUByteArray()) "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"
val decrypted = aesCbc.decrypt() val aesCtr = AesCtr(AesKey.Aes128Key(key), mode = Mode.DECRYPT, initialCounter = ic.hexStringToUByteArray())
aesCtr.addData(cipherText.hexStringToUByteArray())
val decrypted = aesCtr.decrypt()
println("Decrypted: ${decrypted.toHexString()}") println("Decrypted: ${decrypted.toHexString()}")
expectedPlainText == decrypted.toHexString()
}
assertTrue { assertTrue {
val key = "2b7e151628aed2a6abf7158809cf4f3c"
val ic = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
val cipherText =
"874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee"
val expectedPlainText =
"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"
val decrypted = AesCtr.decrypt(AesKey.Aes128Key(key), cipherText.hexStringToUByteArray(), ic.hexStringToUByteArray())
println("Decrypted: ${decrypted.toHexString()}")
expectedPlainText == decrypted.toHexString() expectedPlainText == decrypted.toHexString()
} }
} }
} }