Added aes cbc
This commit is contained in:
		
							parent
							
								
									d3ed17b1c0
								
							
						
					
					
						commit
						1345125252
					
				@ -150,7 +150,6 @@ kotlin {
 | 
			
		||||
        val nativeMain by creating {
 | 
			
		||||
            dependsOn(commonMain)
 | 
			
		||||
            dependencies {
 | 
			
		||||
                implementation(Deps.Native.coroutines)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        val nativeTest by creating {
 | 
			
		||||
 | 
			
		||||
@ -71,4 +71,20 @@ infix fun Array<UByte>.xor(other : Array<UByte>) : Array<UByte> {
 | 
			
		||||
        throw RuntimeException("Operands of different sizes are not supported yet")
 | 
			
		||||
    }
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
import com.ionspin.kotlin.crypto.SRNG
 | 
			
		||||
import com.ionspin.kotlin.crypto.chunked
 | 
			
		||||
import com.ionspin.kotlin.crypto.xor
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@ -25,86 +26,133 @@ import com.ionspin.kotlin.crypto.xor
 | 
			
		||||
 * on 21-Sep-2019
 | 
			
		||||
 */
 | 
			
		||||
@ExperimentalUnsignedTypes
 | 
			
		||||
class AesCbc (val aesKey: AesKey) {
 | 
			
		||||
class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializationVector: Array<UByte>? = null) {
 | 
			
		||||
 | 
			
		||||
    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()
 | 
			
		||||
    val iv = SRNG.getRandomBytes(16)
 | 
			
		||||
    var currentOutput: Array<UByte> = arrayOf()
 | 
			
		||||
    var previousEncrypted: Array<UByte> = arrayOf()
 | 
			
		||||
    val iv = initializationVector ?: SRNG.getRandomBytes(16)
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
//        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
 | 
			
		||||
//                        )
 | 
			
		||||
//                        counter += BLOCK_BYTES
 | 
			
		||||
//                        consumeBlock(buffer)
 | 
			
		||||
//                        buffer = Array<UByte>(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)
 | 
			
		||||
//                    }
 | 
			
		||||
//                }
 | 
			
		||||
//
 | 
			
		||||
//            }
 | 
			
		||||
//            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) {
 | 
			
		||||
//
 | 
			
		||||
//        }
 | 
			
		||||
//
 | 
			
		||||
//    }
 | 
			
		||||
                        }
 | 
			
		||||
                        bufferCounter = chunk.size - (BLOCK_BYTES - bufferCounter)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun encrypt(): Array<UByte> {
 | 
			
		||||
        if (bufferCounter > 0) {
 | 
			
		||||
            val lastBlockPadded = padToBlock(buffer)
 | 
			
		||||
            if (lastBlockPadded.size > BLOCK_BYTES) {
 | 
			
		||||
                val chunks = lastBlockPadded.chunked(BLOCK_BYTES)
 | 
			
		||||
                output += consumeBlock(chunks[0])
 | 
			
		||||
                output += consumeBlock(chunks[1])
 | 
			
		||||
            } else {
 | 
			
		||||
                output += consumeBlock(lastBlockPadded)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun decrypt(): Array<UByte> {
 | 
			
		||||
        return output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun appendToBuffer(array: Array<UByte>, start: Int) {
 | 
			
		||||
        array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
 | 
			
		||||
        bufferCounter += array.size
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun processBlock(data : Array<UByte>) : Array<UByte> {
 | 
			
		||||
        if (currentOutput.isEmpty()) {
 | 
			
		||||
            currentOutput = Aes.encrypt(aesKey, data xor iv)
 | 
			
		||||
        } else {
 | 
			
		||||
            currentOutput = Aes.encrypt(aesKey, data xor currentOutput)
 | 
			
		||||
    private fun consumeBlock(data: Array<UByte>): Array<UByte> {
 | 
			
		||||
        return when (mode) {
 | 
			
		||||
            Mode.ENCRYPT -> {
 | 
			
		||||
                currentOutput = if (currentOutput.isEmpty()) {
 | 
			
		||||
                    Aes.encrypt(aesKey, data xor iv)
 | 
			
		||||
                } else {
 | 
			
		||||
                    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
 | 
			
		||||
 | 
			
		||||
import kotlin.test.Test
 | 
			
		||||
import kotlin.test.assertTrue
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Created by Ugljesa Jovanovic
 | 
			
		||||
 * ugljesa.jovanovic@ionspin.com
 | 
			
		||||
 * on 21-Sep-2019
 | 
			
		||||
 */
 | 
			
		||||
actual object SRNG {
 | 
			
		||||
    actual fun getRandomBytes(amount: Int): Array<UByte> {
 | 
			
		||||
        TODO("not implemented yet")
 | 
			
		||||
class SRNGTest {
 | 
			
		||||
    @Test
 | 
			
		||||
    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
 | 
			
		||||
 */
 | 
			
		||||
actual object SRNG {
 | 
			
		||||
    var counter = 0
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
import java.security.SecureRandom
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Created by Ugljesa Jovanovic
 | 
			
		||||
 * ugljesa.jovanovic@ionspin.com
 | 
			
		||||
 * on 21-Sep-2019
 | 
			
		||||
 */
 | 
			
		||||
@ExperimentalUnsignedTypes
 | 
			
		||||
actual object SRNG {
 | 
			
		||||
    val secureRandom = SecureRandom()
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
import kotlinx.cinterop.*
 | 
			
		||||
import platform.posix.*
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Created by Ugljesa Jovanovic
 | 
			
		||||
 * ugljesa.jovanovic@ionspin.com
 | 
			
		||||
 * on 21-Sep-2019
 | 
			
		||||
 */
 | 
			
		||||
actual object SRNG {
 | 
			
		||||
    @Suppress("EXPERIMENTAL_UNSIGNED_LITERALS")
 | 
			
		||||
    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