Removing direct aes cbc and ctr, to reduce foot shooting incidents, introducing aes256-gcm

This commit is contained in:
Ugljesa Jovanovic 2020-06-14 12:49:46 +02:00 committed by Ugljesa Jovanovic
parent 0b30215143
commit 5c10d3abf4
No known key found for this signature in database
GPG Key ID: 178E6DFCECCB0E0F
15 changed files with 136 additions and 923 deletions

View File

@ -65,17 +65,13 @@ It's not peer reviewed, not guaranteed to be bug free, and not guaranteed to be
* 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

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -0,0 +1,7 @@
package com.ionspin.kotlin.crypto.authenticated
/**
* Created by Ugljesa Jovanovic
* ugljesa.jovanovic@ionspin.com
* on 14-Jun-2020
*/

View File

@ -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<UByteArray>(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<UByteArray> = preparedOutput.reversed() as List<UByteArray>
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
}
}
}
}

View File

@ -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<UByteArray>(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<UByteArray> = output.reversed() as List<UByteArray>
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
}
}

View File

@ -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<UByteArray> = (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<UByteArray> = 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<UByteArray> = (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<UByteArray> = (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<UByteArray> {
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<UByteArray>) {
if (!debug) {
return
}
println()
specific.forEach {
println(it.joinToString(separator = " ") { it.toString(16) })
}
}
}

View File

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

View File

@ -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)

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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<UByteArray> = 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])

View File

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

View File

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

View File

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