Added aes cbc
This commit is contained in:
parent
d3ed17b1c0
commit
1345125252
@ -150,7 +150,6 @@ kotlin {
|
|||||||
val nativeMain by creating {
|
val nativeMain by creating {
|
||||||
dependsOn(commonMain)
|
dependsOn(commonMain)
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(Deps.Native.coroutines)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val nativeTest by creating {
|
val nativeTest by creating {
|
||||||
|
@ -72,3 +72,19 @@ infix fun Array<UByte>.xor(other : Array<UByte>) : Array<UByte> {
|
|||||||
}
|
}
|
||||||
return this.copyOf().mapIndexed { index, it -> it xor other[index] }.toTypedArray()
|
return this.copyOf().mapIndexed { index, it -> it xor other[index] }.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
|
fun String.hexStringToUByteArray() : Array<UByte> {
|
||||||
|
return this.chunked(2).map { it.toUByte(16) }.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
|
fun Array<UByte>.toHexString() : String {
|
||||||
|
return this.joinToString(separator = "") {
|
||||||
|
if (it <= 0x0FU) {
|
||||||
|
"0${it.toString(16)}"
|
||||||
|
} else {
|
||||||
|
it.toString(16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@
|
|||||||
package com.ionspin.kotlin.crypto.symmetric
|
package com.ionspin.kotlin.crypto.symmetric
|
||||||
|
|
||||||
import com.ionspin.kotlin.crypto.SRNG
|
import com.ionspin.kotlin.crypto.SRNG
|
||||||
|
import com.ionspin.kotlin.crypto.chunked
|
||||||
import com.ionspin.kotlin.crypto.xor
|
import com.ionspin.kotlin.crypto.xor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -25,86 +26,133 @@ import com.ionspin.kotlin.crypto.xor
|
|||||||
* on 21-Sep-2019
|
* on 21-Sep-2019
|
||||||
*/
|
*/
|
||||||
@ExperimentalUnsignedTypes
|
@ExperimentalUnsignedTypes
|
||||||
class AesCbc (val aesKey: AesKey) {
|
class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializationVector: Array<UByte>? = null) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val BLOCK_BYTES = 16
|
const val BLOCK_BYTES = 16
|
||||||
|
|
||||||
|
fun encrypt(aesKey: AesKey, data: Array<UByte>): Array<UByte> {
|
||||||
|
val aesCbc = AesCbc(aesKey, Mode.ENCRYPT)
|
||||||
|
aesCbc.addData(data)
|
||||||
|
return aesCbc.encrypt()
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
val iv = SRNG.getRandomBytes(16)
|
var previousEncrypted: Array<UByte> = arrayOf()
|
||||||
|
val iv = initializationVector ?: SRNG.getRandomBytes(16)
|
||||||
|
|
||||||
val output = MutableList<Array<UByte>>(0) { arrayOf() }
|
val output = MutableList<Array<UByte>>(0) { arrayOf() }
|
||||||
|
|
||||||
val buffer : Array<UByte> = UByteArray(16) { 0U }.toTypedArray()
|
var buffer: Array<UByte> = UByteArray(16) { 0U }.toTypedArray()
|
||||||
var bufferCounter = 0
|
var bufferCounter = 0
|
||||||
|
|
||||||
|
fun addData(data: Array<UByte>) {
|
||||||
|
//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, bufferCounter)
|
||||||
|
} else {
|
||||||
|
chunk.copyInto(
|
||||||
|
destination = buffer,
|
||||||
|
destinationOffset = bufferCounter,
|
||||||
|
startIndex = 0,
|
||||||
|
endIndex = BLOCK_BYTES - bufferCounter
|
||||||
|
)
|
||||||
|
output += consumeBlock(buffer)
|
||||||
|
buffer = Array<UByte>(BLOCK_BYTES) {
|
||||||
|
when (it) {
|
||||||
|
in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
||||||
|
chunk[it + (BLOCK_BYTES - bufferCounter)]
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
0U
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fun addData(data : UByteArray) {
|
}
|
||||||
// //Padding
|
bufferCounter = chunk.size - (BLOCK_BYTES - bufferCounter)
|
||||||
// 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, bufferCounter)
|
}
|
||||||
// } else {
|
|
||||||
// chunk.copyInto(
|
fun encrypt(): Array<UByte> {
|
||||||
// destination = buffer,
|
if (bufferCounter > 0) {
|
||||||
// destinationOffset = bufferCounter,
|
val lastBlockPadded = padToBlock(buffer)
|
||||||
// startIndex = 0,
|
if (lastBlockPadded.size > BLOCK_BYTES) {
|
||||||
// endIndex = BLOCK_BYTES - bufferCounter
|
val chunks = lastBlockPadded.chunked(BLOCK_BYTES)
|
||||||
// )
|
output += consumeBlock(chunks[0])
|
||||||
// counter += BLOCK_BYTES
|
output += consumeBlock(chunks[1])
|
||||||
// consumeBlock(buffer)
|
} else {
|
||||||
// buffer = Array<UByte>(BLOCK_BYTES) {
|
output += consumeBlock(lastBlockPadded)
|
||||||
// when (it) {
|
}
|
||||||
// in (0 until (chunk.size - (BLOCK_BYTES - bufferCounter))) -> {
|
}
|
||||||
// chunk[it + (BLOCK_BYTES - bufferCounter)]
|
return output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes }
|
||||||
// }
|
}
|
||||||
// else -> {
|
|
||||||
// 0U
|
fun decrypt(): Array<UByte> {
|
||||||
// }
|
return output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes }
|
||||||
// }
|
}
|
||||||
//
|
|
||||||
// }
|
|
||||||
// bufferCounter = chunk.size - (BLOCK_BYTES - bufferCounter)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// data.size < 16 -> {
|
|
||||||
// val paddingSize = 16 - data.size
|
|
||||||
// val padding = UByteArray(16 - data.size) { paddingSize.toUByte() }
|
|
||||||
// output += processBlock(data + padding)
|
|
||||||
// }
|
|
||||||
// data.size == 16 -> {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// data.size > 16 -> {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (data.size < 16) {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
private fun appendToBuffer(array: Array<UByte>, start: Int) {
|
private fun appendToBuffer(array: Array<UByte>, start: Int) {
|
||||||
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
|
||||||
bufferCounter += array.size
|
bufferCounter += array.size
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processBlock(data : Array<UByte>) : Array<UByte> {
|
private fun consumeBlock(data: Array<UByte>): Array<UByte> {
|
||||||
if (currentOutput.isEmpty()) {
|
return when (mode) {
|
||||||
currentOutput = Aes.encrypt(aesKey, data xor iv)
|
Mode.ENCRYPT -> {
|
||||||
|
currentOutput = if (currentOutput.isEmpty()) {
|
||||||
|
Aes.encrypt(aesKey, data xor iv)
|
||||||
} else {
|
} else {
|
||||||
currentOutput = Aes.encrypt(aesKey, data xor currentOutput)
|
Aes.encrypt(aesKey, data xor currentOutput)
|
||||||
|
}
|
||||||
|
currentOutput
|
||||||
|
}
|
||||||
|
Mode.DECRYPT -> {
|
||||||
|
if (currentOutput.isEmpty()) {
|
||||||
|
currentOutput = Aes.decrypt(aesKey, data) xor iv
|
||||||
|
previousEncrypted = data
|
||||||
|
} else {
|
||||||
|
currentOutput = Aes.decrypt(aesKey, data) xor previousEncrypted
|
||||||
|
}
|
||||||
|
currentOutput
|
||||||
}
|
}
|
||||||
return currentOutput
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -16,13 +16,19 @@
|
|||||||
|
|
||||||
package com.ionspin.kotlin.crypto
|
package com.ionspin.kotlin.crypto
|
||||||
|
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ugljesa Jovanovic
|
* Created by Ugljesa Jovanovic
|
||||||
* ugljesa.jovanovic@ionspin.com
|
* ugljesa.jovanovic@ionspin.com
|
||||||
* on 21-Sep-2019
|
* on 21-Sep-2019
|
||||||
*/
|
*/
|
||||||
actual object SRNG {
|
class SRNGTest {
|
||||||
actual fun getRandomBytes(amount: Int): Array<UByte> {
|
@Test
|
||||||
TODO("not implemented yet")
|
fun testSrng() {
|
||||||
|
val randomBytes1 = SRNG.getRandomBytes(10)
|
||||||
|
val randomBytes2 = SRNG.getRandomBytes(10)
|
||||||
|
assertTrue { !randomBytes1.contentEquals(randomBytes2) }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
/*
|
||||||
|
* 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.hexStringToUByteArray
|
||||||
|
import com.ionspin.kotlin.crypto.toHexString
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Ugljesa Jovanovic
|
||||||
|
* ugljesa.jovanovic@ionspin.com
|
||||||
|
* on 21-Sep-2019
|
||||||
|
*/
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
|
class AesCbcTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCbcEncryption() {
|
||||||
|
val key = "4278b840fb44aaa757c1bf04acbe1a3e"
|
||||||
|
val iv = "57f02a5c5339daeb0a2908a06ac6393f"
|
||||||
|
val plaintext = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5"
|
||||||
|
val expectedCipherText = "479c89ec14bc98994e62b2c705b5014e175bd7832e7e60a1e92aac568a861eb7"
|
||||||
|
val aesCbc = AesCbc(AesKey.Aes128Key(key), mode = Mode.ENCRYPT, initializationVector = iv.hexStringToUByteArray())
|
||||||
|
aesCbc.addData(plaintext.hexStringToUByteArray())
|
||||||
|
val encrypted = aesCbc.encrypt()
|
||||||
|
println("Decrypted: ${encrypted.toHexString()}")
|
||||||
|
assertTrue {
|
||||||
|
expectedCipherText == encrypted.toHexString()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCbcDecryption() {
|
||||||
|
val key = "4278b840fb44aaa757c1bf04acbe1a3e"
|
||||||
|
val iv = "57f02a5c5339daeb0a2908a06ac6393f"
|
||||||
|
val cipherText = "479c89ec14bc98994e62b2c705b5014e175bd7832e7e60a1e92aac568a861eb7"
|
||||||
|
val expectedPlainText = "3c888bbbb1a8eb9f3e9b87acaad986c466e2f7071c83083b8a557971918850e5"
|
||||||
|
val aesCbc = AesCbc(AesKey.Aes128Key(key), mode = Mode.DECRYPT, initializationVector = iv.hexStringToUByteArray())
|
||||||
|
aesCbc.addData(cipherText.hexStringToUByteArray())
|
||||||
|
val decrypted = aesCbc.decrypt()
|
||||||
|
println("Encrypted: ${decrypted.toHexString()}")
|
||||||
|
assertTrue {
|
||||||
|
expectedPlainText == decrypted.toHexString()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -22,7 +22,8 @@ package com.ionspin.kotlin.crypto
|
|||||||
* on 21-Sep-2019
|
* on 21-Sep-2019
|
||||||
*/
|
*/
|
||||||
actual object SRNG {
|
actual object SRNG {
|
||||||
|
var counter = 0
|
||||||
actual fun getRandomBytes(amount: Int): Array<UByte> {
|
actual fun getRandomBytes(amount: Int): Array<UByte> {
|
||||||
TODO("not implemented yet")
|
return arrayOf((counter++).toUByte()) // TODO Wow. Such random. Very entropy.
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,13 +16,19 @@
|
|||||||
|
|
||||||
package com.ionspin.kotlin.crypto
|
package com.ionspin.kotlin.crypto
|
||||||
|
|
||||||
|
import java.security.SecureRandom
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ugljesa Jovanovic
|
* Created by Ugljesa Jovanovic
|
||||||
* ugljesa.jovanovic@ionspin.com
|
* ugljesa.jovanovic@ionspin.com
|
||||||
* on 21-Sep-2019
|
* on 21-Sep-2019
|
||||||
*/
|
*/
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
actual object SRNG {
|
actual object SRNG {
|
||||||
|
val secureRandom = SecureRandom()
|
||||||
actual fun getRandomBytes(amount: Int): Array<UByte> {
|
actual fun getRandomBytes(amount: Int): Array<UByte> {
|
||||||
TODO("not implemented yet")
|
val byteArray = ByteArray(amount)
|
||||||
|
secureRandom.nextBytes(byteArray)
|
||||||
|
return byteArray.toUByteArray().toTypedArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,13 +16,26 @@
|
|||||||
|
|
||||||
package com.ionspin.kotlin.crypto
|
package com.ionspin.kotlin.crypto
|
||||||
|
|
||||||
|
import kotlinx.cinterop.*
|
||||||
|
import platform.posix.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Ugljesa Jovanovic
|
* Created by Ugljesa Jovanovic
|
||||||
* ugljesa.jovanovic@ionspin.com
|
* ugljesa.jovanovic@ionspin.com
|
||||||
* on 21-Sep-2019
|
* on 21-Sep-2019
|
||||||
*/
|
*/
|
||||||
actual object SRNG {
|
actual object SRNG {
|
||||||
|
@Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
|
||||||
actual fun getRandomBytes(amount: Int): Array<UByte> {
|
actual fun getRandomBytes(amount: Int): Array<UByte> {
|
||||||
TODO("not implemented yet")
|
memScoped {
|
||||||
|
val array = allocArray<UByteVar>(amount)
|
||||||
|
val urandomFile = fopen("/dev/urandom", "rb")
|
||||||
|
if (urandomFile != null) {
|
||||||
|
fread(array, 1, amount.convert(), urandomFile)
|
||||||
|
}
|
||||||
|
return Array(amount) {
|
||||||
|
array[it]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user