commit
						55456600d0
					
				@ -1,6 +1,9 @@
 | 
				
			|||||||
## Descriptive changelog
 | 
					## Descriptive changelog
 | 
				
			||||||
(All dates are DD.MM.YYYY)
 | 
					(All dates are DD.MM.YYYY)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#### AES - 0.0.3-SNAPSHOT - 25.9.2019
 | 
				
			||||||
 | 
					- Added AES with CBC and CTR modes
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Updatable SHA hash implementation - 0.0.2 - 21.7.2019
 | 
					#### Updatable SHA hash implementation - 0.0.2 - 21.7.2019
 | 
				
			||||||
- Added "updatable" version for SHA
 | 
					- Added "updatable" version for SHA
 | 
				
			||||||
- Moved sha and blake to hash package
 | 
					- Moved sha and blake to hash package
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										78
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								README.md
									
									
									
									
									
								
							@ -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**
 | 
				
			||||||
@ -17,22 +20,37 @@ After that tenative plan is to add 25519 curve based signing and key exchange ne
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
## Should I use this in production?
 | 
					## Should I use this in production?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
No, it's untested and unproven. 
 | 
					No.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Should I use this in code that is critical in any way, shape or form?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					No.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Why?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Integration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Supported
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Hashing functions
 | 
					## Hashing functions
 | 
				
			||||||
* Blake2b
 | 
					* Blake2b
 | 
				
			||||||
* SHA512
 | 
					* SHA512
 | 
				
			||||||
* SHA256
 | 
					* SHA256
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Symmetric cipher (Currently only available only in 0.0.3-SNAPSHOT)
 | 
				
			||||||
 | 
					* AES
 | 
				
			||||||
 | 
					  * Modes: CBC, CTR
 | 
				
			||||||
 | 
					
 | 
				
			||||||
More to come.
 | 
					More to come.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Integration
 | 
					## Integration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Gradle
 | 
					#### Gradle
 | 
				
			||||||
```kotlin
 | 
					```kotlin
 | 
				
			||||||
implementation("com.ionspin.kotlin:crypto:0.0.1")
 | 
					implementation("com.ionspin.kotlin:crypto:0.0.2")
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#### Snapshot builds
 | 
					#### Snapshot builds
 | 
				
			||||||
@ -42,7 +60,7 @@ repositories {
 | 
				
			|||||||
        url = uri("https://oss.sonatype.org/content/repositories/snapshots")
 | 
					        url = uri("https://oss.sonatype.org/content/repositories/snapshots")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
implementation("com.ionspin.kotlin:crypto:0.0.1-SNAPSHOT")
 | 
					implementation("com.ionspin.kotlin:crypto:0.0.3-SNAPSHOT")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -117,6 +135,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()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -16,16 +16,16 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
object Versions {
 | 
					object Versions {
 | 
				
			||||||
    val klock = "1.1.1"
 | 
					    val klock = "1.1.1"
 | 
				
			||||||
    val kotlinCoroutines = "1.3.0-M2"
 | 
					    val kotlinCoroutines = "1.3.0"
 | 
				
			||||||
    val timber = "5.0.0-SNAPSHOT"
 | 
					    val timber = "5.0.0-SNAPSHOT"
 | 
				
			||||||
    val oshi = "3.12.0"
 | 
					    val oshi = "3.12.0"
 | 
				
			||||||
    val kotlin = "1.3.40"
 | 
					    val kotlin = "1.3.50"
 | 
				
			||||||
    val ktor = "1.1.1"
 | 
					    val ktor = "1.1.1"
 | 
				
			||||||
    val kotlinSerialization = "0.11.1"
 | 
					    val kotlinSerialization = "0.11.1"
 | 
				
			||||||
    val nodePlugin = "1.3.0"
 | 
					    val nodePlugin = "1.3.0"
 | 
				
			||||||
    val dokkaPlugin = "0.9.18"
 | 
					    val dokkaPlugin = "0.9.18"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val kotlinBigNumVersion = "0.1.0-SNAPSHOT"
 | 
					    val kotlinBigNumVersion = "0.1.1-SNAPSHOT"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								gradle/wrapper/gradle-wrapper.properties
									
									
									
									
										vendored
									
									
								
							@ -16,6 +16,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
distributionBase=GRADLE_USER_HOME
 | 
					distributionBase=GRADLE_USER_HOME
 | 
				
			||||||
distributionPath=wrapper/dists
 | 
					distributionPath=wrapper/dists
 | 
				
			||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-bin.zip
 | 
					distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-bin.zip
 | 
				
			||||||
zipStoreBase=GRADLE_USER_HOME
 | 
					zipStoreBase=GRADLE_USER_HOME
 | 
				
			||||||
zipStorePath=wrapper/dists
 | 
					zipStorePath=wrapper/dists
 | 
				
			||||||
 | 
				
			|||||||
@ -149,9 +149,6 @@ kotlin {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        val nativeMain by creating {
 | 
					        val nativeMain by creating {
 | 
				
			||||||
            dependsOn(commonMain)
 | 
					            dependsOn(commonMain)
 | 
				
			||||||
            dependencies {
 | 
					 | 
				
			||||||
                implementation(Deps.Native.coroutines)
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        val nativeTest by creating {
 | 
					        val nativeTest by creating {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *    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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Created by Ugljesa Jovanovic
 | 
				
			||||||
 | 
					 * ugljesa.jovanovic@ionspin.com
 | 
				
			||||||
 | 
					 * on 21-Sep-2019
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					expect object SRNG {
 | 
				
			||||||
 | 
					    fun getRandomBytes(amount : Int) : Array<UByte>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -64,3 +64,27 @@ infix fun UInt.rotateRight(places: Int): UInt {
 | 
				
			|||||||
infix fun ULong.rotateRight(places: Int): ULong {
 | 
					infix fun ULong.rotateRight(places: Int): ULong {
 | 
				
			||||||
    return (this shr places) xor (this shl (64 - places))
 | 
					    return (this shr places) xor (this shl (64 - places))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@ExperimentalUnsignedTypes
 | 
				
			||||||
 | 
					infix fun Array<UByte>.xor(other : Array<UByte>) : Array<UByte> {
 | 
				
			||||||
 | 
					    if (this.size != other.size) {
 | 
				
			||||||
 | 
					        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)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,377 @@
 | 
				
			|||||||
 | 
					package com.ionspin.kotlin.crypto.symmetric
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 07/Sep/2019
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@ExperimentalUnsignedTypes
 | 
				
			||||||
 | 
					internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UByte>) {
 | 
				
			||||||
 | 
					    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: Array<UByte>): Array<UByte> {
 | 
				
			||||||
 | 
					            return Aes(aesKey, input).encrypt()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fun decrypt(aesKey: AesKey, input: Array<UByte>): Array<UByte> {
 | 
				
			||||||
 | 
					            return Aes(aesKey, input).decrypt()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val state: Array<Array<UByte>> = (0 until 4).map { outerCounter ->
 | 
				
			||||||
 | 
					        Array<UByte>(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<Array<UByte>> = 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] = arrayOf(state[0][0], state[0][1], state[0][2], state[0][3])
 | 
				
			||||||
 | 
					        state[1] = arrayOf(state[1][1], state[1][2], state[1][3], state[1][0])
 | 
				
			||||||
 | 
					        state[2] = arrayOf(state[2][2], state[2][3], state[2][0], state[2][1])
 | 
				
			||||||
 | 
					        state[3] = arrayOf(state[3][3], state[3][0], state[3][1], state[3][2])
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun inversShiftRows() {
 | 
				
			||||||
 | 
					        state[0] = arrayOf(state[0][0], state[0][1], state[0][2], state[0][3])
 | 
				
			||||||
 | 
					        state[1] = arrayOf(state[1][3], state[1][0], state[1][1], state[1][2])
 | 
				
			||||||
 | 
					        state[2] = arrayOf(state[2][2], state[2][3], state[2][0], state[2][1])
 | 
				
			||||||
 | 
					        state[3] = arrayOf(state[3][1], state[3][2], state[3][3], state[3][0])
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun mixColumns() {
 | 
				
			||||||
 | 
					        val stateMixed: Array<Array<UByte>> = (0 until 4).map {
 | 
				
			||||||
 | 
					            Array<UByte>(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<Array<UByte>> = (0 until 4).map {
 | 
				
			||||||
 | 
					            Array<UByte>(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<Array<UByte>> {
 | 
				
			||||||
 | 
					        val expandedKey = (0 until 4 * (numberOfRounds + 1)).map {
 | 
				
			||||||
 | 
					            Array<UByte>(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]
 | 
				
			||||||
 | 
					            }.toTypedArray()
 | 
				
			||||||
 | 
					            clearArray(temp)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return expandedKey
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun encrypt(): Array<UByte> {
 | 
				
			||||||
 | 
					        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 ->
 | 
				
			||||||
 | 
					            Array<UByte>(4) { 0U }
 | 
				
			||||||
 | 
					        }.toTypedArray()
 | 
				
			||||||
 | 
					        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.flatten().toTypedArray()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun decrypt(): Array<UByte> {
 | 
				
			||||||
 | 
					        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 ->
 | 
				
			||||||
 | 
					            Array<UByte>(4) { 0U }
 | 
				
			||||||
 | 
					        }.toTypedArray()
 | 
				
			||||||
 | 
					        for (i in 0 until 4) {
 | 
				
			||||||
 | 
					            for (j in 0 until 4) {
 | 
				
			||||||
 | 
					                transposedMatrix[i][j] = state[j][i]
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        printState(transposedMatrix)
 | 
				
			||||||
 | 
					        state.forEach { clearArray(it) }
 | 
				
			||||||
 | 
					        completed = true
 | 
				
			||||||
 | 
					        return transposedMatrix.flatten().toTypedArray()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun clearArray(array : Array<UByte>) {
 | 
				
			||||||
 | 
					        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 : Array<Array<UByte>>) {
 | 
				
			||||||
 | 
					        if (!debug) {
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        println()
 | 
				
			||||||
 | 
					        specific.forEach {
 | 
				
			||||||
 | 
					            println(it.joinToString(separator = " ") { it.toString(16) })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					sealed class AesKey(val key: String, val keyLength: Int) {
 | 
				
			||||||
 | 
					    val keyArray: Array<UByte> = key.chunked(2).map { it.toUByte(16) }.toTypedArray()
 | 
				
			||||||
 | 
					    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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init {
 | 
				
			||||||
 | 
					        checkKeyLength(key, keyLength)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun checkKeyLength(key: String, expectedLength: Int) {
 | 
				
			||||||
 | 
					        if ((key.length / 2) != expectedLength / 8) {
 | 
				
			||||||
 | 
					            throw RuntimeException("Invalid key length")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,241 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *    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.chunked
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					 * ugljesa.jovanovic@ionspin.com
 | 
				
			||||||
 | 
					 * on 21-Sep-2019
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@ExperimentalUnsignedTypes
 | 
				
			||||||
 | 
					class AesCbc internal constructor(val aesKey: AesKey, val mode: Mode, initializationVector: Array<UByte>? = 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) : 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)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * Bulk encryption, returns encrypted data and a random initialization vector
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        fun encrypt(aesKey: AesKey, data: Array<UByte>): EncryptedDataAndInitializationVector {
 | 
				
			||||||
 | 
					            val aesCbc = AesCbc(aesKey, Mode.ENCRYPT)
 | 
				
			||||||
 | 
					            aesCbc.addData(data)
 | 
				
			||||||
 | 
					            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> {
 | 
				
			||||||
 | 
					            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 previousEncrypted: Array<UByte> = arrayOf()
 | 
				
			||||||
 | 
					    val initVector = if (initializationVector.isNullOrEmpty()) {
 | 
				
			||||||
 | 
					        SRNG.getRandomBytes(16)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        initializationVector
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val output = MutableList<Array<UByte>>(0) { arrayOf() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        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)
 | 
				
			||||||
 | 
					                output += consumeBlock(chunks[0])
 | 
				
			||||||
 | 
					                output += consumeBlock(chunks[1])
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                output += consumeBlock(lastBlockPadded)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return EncryptedDataAndInitializationVector(
 | 
				
			||||||
 | 
					            output.reversed().foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
 | 
				
			||||||
 | 
					            initVector
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Decrypt data
 | 
				
			||||||
 | 
					     * @return Decrypted data
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    fun decrypt(): Array<UByte> {
 | 
				
			||||||
 | 
					        val removePaddingCount = output.last().last()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val removedPadding = if (removePaddingCount > 0U && removePaddingCount < 16U) {
 | 
				
			||||||
 | 
					            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
 | 
				
			||||||
 | 
					        val reversed : List<Array<UByte>> = preparedOutput.reversed() as List<Array<UByte>>
 | 
				
			||||||
 | 
					        val folded : Array<UByte> = reversed.foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc ->
 | 
				
			||||||
 | 
					            acc + arrayOfUBytes }
 | 
				
			||||||
 | 
					        return folded
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun appendToBuffer(array: Array<UByte>, start: Int) {
 | 
				
			||||||
 | 
					        array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
 | 
				
			||||||
 | 
					        bufferCounter += array.size
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun consumeBlock(data: Array<UByte>): Array<UByte> {
 | 
				
			||||||
 | 
					        return when (mode) {
 | 
				
			||||||
 | 
					            Mode.ENCRYPT -> {
 | 
				
			||||||
 | 
					                currentOutput = if (currentOutput.isEmpty()) {
 | 
				
			||||||
 | 
					                    println("IV: $initVector")
 | 
				
			||||||
 | 
					                    Aes.encrypt(aesKey, data xor initVector)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Aes.encrypt(aesKey, data xor currentOutput)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                currentOutput
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Mode.DECRYPT -> {
 | 
				
			||||||
 | 
					                if (currentOutput.isEmpty()) {
 | 
				
			||||||
 | 
					                    currentOutput = Aes.decrypt(aesKey, data) xor initVector
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    currentOutput = Aes.decrypt(aesKey, data) xor previousEncrypted
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                previousEncrypted = data
 | 
				
			||||||
 | 
					                currentOutput
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@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
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,209 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *    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.chunked
 | 
				
			||||||
 | 
					import com.ionspin.kotlin.crypto.symmetric.AesCtr.Companion.encrypt
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					 * ugljesa.jovanovic@ionspin.com
 | 
				
			||||||
 | 
					 * on 22-Sep-2019
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@ExperimentalUnsignedTypes
 | 
				
			||||||
 | 
					class AesCtr internal constructor(val aesKey: AesKey, val mode: Mode, initialCounter: Array<UByte>? = 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) : 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()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var currentOutput: Array<UByte> = arrayOf()
 | 
				
			||||||
 | 
					    var previousEncrypted: Array<UByte> = arrayOf()
 | 
				
			||||||
 | 
					    val counterStart = if (initialCounter.isNullOrEmpty()) {
 | 
				
			||||||
 | 
					        SRNG.getRandomBytes(16)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        initialCounter
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var blockCounter = modularCreator.fromBigInteger(BigInteger.fromUByteArray(counterStart, Endianness.BIG))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val output = MutableList<Array<UByte>>(0) { arrayOf() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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, blockCounter)
 | 
				
			||||||
 | 
					                        blockCounter += 1
 | 
				
			||||||
 | 
					                        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)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc -> acc + arrayOfUBytes },
 | 
				
			||||||
 | 
					            counterStart
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Decrypt data
 | 
				
			||||||
 | 
					     * @return Decrypted data
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    fun decrypt(): Array<UByte> {
 | 
				
			||||||
 | 
					        if (bufferCounter > 0) {
 | 
				
			||||||
 | 
					            output += consumeBlock(buffer, blockCounter)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        //JS compiler freaks out here if we don't supply exact type
 | 
				
			||||||
 | 
					        val reversed: List<Array<UByte>> = output.reversed() as List<Array<UByte>>
 | 
				
			||||||
 | 
					        val folded: Array<UByte> = reversed.foldRight(Array<UByte>(0) { 0U }) { arrayOfUBytes, acc ->
 | 
				
			||||||
 | 
					            acc + arrayOfUBytes
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return folded
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun appendToBuffer(array: Array<UByte>, start: Int) {
 | 
				
			||||||
 | 
					        array.copyInto(destination = buffer, destinationOffset = start, startIndex = 0, endIndex = array.size)
 | 
				
			||||||
 | 
					        bufferCounter += array.size
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun consumeBlock(data: Array<UByte>, blockCount: ModularBigInteger): Array<UByte> {
 | 
				
			||||||
 | 
					        val blockCountAsByteArray = blockCount.toUByteArray(Endianness.BIG).expandCounterTo16Bytes()
 | 
				
			||||||
 | 
					        return when (mode) {
 | 
				
			||||||
 | 
					            Mode.ENCRYPT -> {
 | 
				
			||||||
 | 
					                Aes.encrypt(aesKey, blockCountAsByteArray) xor data
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            Mode.DECRYPT -> {
 | 
				
			||||||
 | 
					                Aes.encrypt(aesKey, blockCountAsByteArray) xor data
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun Array<UByte>.expandCounterTo16Bytes() : Array<UByte> {
 | 
				
			||||||
 | 
					        return if (this.size < 16) {
 | 
				
			||||||
 | 
					            println("Expanding")
 | 
				
			||||||
 | 
					            val diff = 16 - this.size
 | 
				
			||||||
 | 
					            val pad = Array<UByte>(diff) { 0U }
 | 
				
			||||||
 | 
					            pad + this
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            this
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@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
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *    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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Created by Ugljesa Jovanovic
 | 
				
			||||||
 | 
					 * ugljesa.jovanovic@ionspin.com
 | 
				
			||||||
 | 
					 * on 18-Sep-2019
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum class Mode {
 | 
				
			||||||
 | 
					    ENCRYPT, DECRYPT
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *    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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlin.test.Test
 | 
				
			||||||
 | 
					import kotlin.test.assertTrue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Created by Ugljesa Jovanovic
 | 
				
			||||||
 | 
					 * ugljesa.jovanovic@ionspin.com
 | 
				
			||||||
 | 
					 * on 21-Sep-2019
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class SRNGTest {
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testSrng() {
 | 
				
			||||||
 | 
					        val randomBytes1 = SRNG.getRandomBytes(10)
 | 
				
			||||||
 | 
					        val randomBytes2 = SRNG.getRandomBytes(10)
 | 
				
			||||||
 | 
					        assertTrue { !randomBytes1.contentEquals(randomBytes2) }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *    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() {
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            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("Encrypted: ${encrypted.encryptedData.toHexString()}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expectedCipherText == encrypted.encryptedData.toHexString() &&
 | 
				
			||||||
 | 
					                    iv == encrypted.initilizationVector.toHexString()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testEncryptionApi() {
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					    fun testCbcDecryption() {
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            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("Decrypted: ${decrypted.toHexString()}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expectedPlainText == decrypted.toHexString()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testDecryptionApi() {
 | 
				
			||||||
 | 
					        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()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *    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 AesCtrTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testCtrEncryption() {
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val key = "2b7e151628aed2a6abf7158809cf4f3c"
 | 
				
			||||||
 | 
					            val ic = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
 | 
				
			||||||
 | 
					            val plaintext =
 | 
				
			||||||
 | 
					                "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"
 | 
				
			||||||
 | 
					            val expectedCipherText =
 | 
				
			||||||
 | 
					                "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee"
 | 
				
			||||||
 | 
					            val aesCtr = AesCtr(AesKey.Aes128Key(key), mode = Mode.ENCRYPT, initialCounter = ic.hexStringToUByteArray())
 | 
				
			||||||
 | 
					            aesCtr.addData(
 | 
				
			||||||
 | 
					                plaintext.hexStringToUByteArray()
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            val encrypted = aesCtr.encrypt()
 | 
				
			||||||
 | 
					            println("Encrypted: ${encrypted.encryptedData.toHexString()}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            expectedCipherText == encrypted.encryptedData.toHexString() &&
 | 
				
			||||||
 | 
					                    ic == encrypted.initialCounter.toHexString()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testEncryptionApi() {
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            plainText == decrypted.toHexString()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testCtrDecryption() {
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val key = "2b7e151628aed2a6abf7158809cf4f3c"
 | 
				
			||||||
 | 
					            val ic = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
 | 
				
			||||||
 | 
					            val cipherText =
 | 
				
			||||||
 | 
					                "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee"
 | 
				
			||||||
 | 
					            val expectedPlainText =
 | 
				
			||||||
 | 
					                "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710"
 | 
				
			||||||
 | 
					            val aesCtr = AesCtr(AesKey.Aes128Key(key), mode = Mode.DECRYPT, initialCounter = ic.hexStringToUByteArray())
 | 
				
			||||||
 | 
					            aesCtr.addData(cipherText.hexStringToUByteArray())
 | 
				
			||||||
 | 
					            val decrypted = aesCtr.decrypt()
 | 
				
			||||||
 | 
					            println("Decrypted: ${decrypted.toHexString()}")
 | 
				
			||||||
 | 
					            expectedPlainText == decrypted.toHexString()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testCtrDecryptionApi() {
 | 
				
			||||||
 | 
					                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()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,269 @@
 | 
				
			|||||||
 | 
					package com.ionspin.kotlin.crypto.symmetric
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import kotlin.test.Test
 | 
				
			||||||
 | 
					import kotlin.test.assertTrue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 10/Sep/2019
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					@ExperimentalUnsignedTypes
 | 
				
			||||||
 | 
					class AesTest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val irrelevantKey = "01234567890123345678901234567890"
 | 
				
			||||||
 | 
					    val irrelevantInput = UByteArray(16) { 0U }.toTypedArray()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testSubBytes() {
 | 
				
			||||||
 | 
					        val fakeState = arrayOf(
 | 
				
			||||||
 | 
					            ubyteArrayOf(0x53U, 0U, 0U, 0U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0U, 0U, 0U, 0U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0U, 0U, 0U, 0U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0U, 0U, 0U, 0U).toTypedArray()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput)
 | 
				
			||||||
 | 
					        fakeState.copyInto(aes.state)
 | 
				
			||||||
 | 
					        aes.subBytes()
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            aes.state[0][0] == 0xEDU.toUByte()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testShiftRows() {
 | 
				
			||||||
 | 
					        val fakeState = arrayOf(
 | 
				
			||||||
 | 
					            ubyteArrayOf(0U, 1U, 2U, 3U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0U, 1U, 2U, 3U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0U, 1U, 2U, 3U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0U, 1U, 2U, 3U).toTypedArray()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        val expectedState = arrayOf(
 | 
				
			||||||
 | 
					            ubyteArrayOf(0U, 1U, 2U, 3U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(1U, 2U, 3U, 0U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(2U, 3U, 0U, 1U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(3U, 0U, 1U, 2U).toTypedArray()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput)
 | 
				
			||||||
 | 
					        fakeState.copyInto(aes.state)
 | 
				
			||||||
 | 
					        aes.shiftRows()
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            aes.state.contentDeepEquals(expectedState)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testGaloisMultiply() {
 | 
				
			||||||
 | 
					        //Samples from FIPS-197
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val a = 0x57U
 | 
				
			||||||
 | 
					            val b = 0x83U
 | 
				
			||||||
 | 
					            val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput)
 | 
				
			||||||
 | 
					            val c = aes.galoisFieldMultiply(a.toUByte(), b.toUByte())
 | 
				
			||||||
 | 
					            c == 0xC1U.toUByte()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val a = 0x57U
 | 
				
			||||||
 | 
					            val b = 0x13U
 | 
				
			||||||
 | 
					            val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput)
 | 
				
			||||||
 | 
					            val c = aes.galoisFieldMultiply(a.toUByte(), b.toUByte())
 | 
				
			||||||
 | 
					            c == 0xFEU.toUByte()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testMixColumns() {
 | 
				
			||||||
 | 
					        //Test vectors from wikipedia
 | 
				
			||||||
 | 
					        val fakeState = arrayOf(
 | 
				
			||||||
 | 
					            ubyteArrayOf(0xdbU, 0xf2U, 0x01U, 0xc6U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0x13U, 0x0aU, 0x01U, 0xc6U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0x53U, 0x22U, 0x01U, 0xc6U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0x45U, 0x5cU, 0x01U, 0xc6U).toTypedArray()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val expectedState = arrayOf(
 | 
				
			||||||
 | 
					            ubyteArrayOf(0x8eU, 0x9fU, 0x01U, 0xc6U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0x4dU, 0xdcU, 0x01U, 0xc6U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0xa1U, 0x58U, 0x01U, 0xc6U).toTypedArray(),
 | 
				
			||||||
 | 
					            ubyteArrayOf(0xbcU, 0x9dU, 0x01U, 0xc6U).toTypedArray()
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput)
 | 
				
			||||||
 | 
					        fakeState.copyInto(aes.state)
 | 
				
			||||||
 | 
					        aes.mixColumns()
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            aes.state.contentDeepEquals(expectedState)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testKeyExpansion() {
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val key = "2b7e151628aed2a6abf7158809cf4f3c"
 | 
				
			||||||
 | 
					            val expectedExpandedKey = uintArrayOf(
 | 
				
			||||||
 | 
					                // @formatter:off
 | 
				
			||||||
 | 
					                0x2b7e1516U, 0x28aed2a6U, 0xabf71588U, 0x09cf4f3cU, 0xa0fafe17U, 0x88542cb1U,
 | 
				
			||||||
 | 
					                0x23a33939U, 0x2a6c7605U, 0xf2c295f2U, 0x7a96b943U, 0x5935807aU, 0x7359f67fU,
 | 
				
			||||||
 | 
					                0x3d80477dU, 0x4716fe3eU, 0x1e237e44U, 0x6d7a883bU, 0xef44a541U, 0xa8525b7fU,
 | 
				
			||||||
 | 
					                0xb671253bU, 0xdb0bad00U, 0xd4d1c6f8U, 0x7c839d87U, 0xcaf2b8bcU, 0x11f915bcU,
 | 
				
			||||||
 | 
					                0x6d88a37aU, 0x110b3efdU, 0xdbf98641U, 0xca0093fdU, 0x4e54f70eU, 0x5f5fc9f3U,
 | 
				
			||||||
 | 
					                0x84a64fb2U, 0x4ea6dc4fU, 0xead27321U, 0xb58dbad2U, 0x312bf560U, 0x7f8d292fU,
 | 
				
			||||||
 | 
					                0xac7766f3U, 0x19fadc21U, 0x28d12941U, 0x575c006eU, 0xd014f9a8U, 0xc9ee2589U,
 | 
				
			||||||
 | 
					                0xe13f0cc8U, 0xb6630ca6U
 | 
				
			||||||
 | 
					            // @formatter:on
 | 
				
			||||||
 | 
					            ).toTypedArray()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val aes = Aes(AesKey.Aes128Key(key), irrelevantInput)
 | 
				
			||||||
 | 
					            val result = aes.expandedKey.map {
 | 
				
			||||||
 | 
					                it.foldIndexed(0U) { index, acc, uByte ->
 | 
				
			||||||
 | 
					                    acc + (uByte.toUInt() shl (24 - index * 8))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }.toTypedArray()
 | 
				
			||||||
 | 
					            expectedExpandedKey.contentEquals(result)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b"
 | 
				
			||||||
 | 
					            val expectedExpandedKey = uintArrayOf(
 | 
				
			||||||
 | 
					                // @formatter:off
 | 
				
			||||||
 | 
					                0x8e73b0f7U, 0xda0e6452U, 0xc810f32bU, 0x809079e5U, 0x62f8ead2U, 0x522c6b7bU,
 | 
				
			||||||
 | 
					                0xfe0c91f7U, 0x2402f5a5U, 0xec12068eU, 0x6c827f6bU, 0x0e7a95b9U, 0x5c56fec2U, 0x4db7b4bdU, 0x69b54118U,
 | 
				
			||||||
 | 
					                0x85a74796U, 0xe92538fdU, 0xe75fad44U, 0xbb095386U, 0x485af057U, 0x21efb14fU, 0xa448f6d9U, 0x4d6dce24U,
 | 
				
			||||||
 | 
					                0xaa326360U, 0x113b30e6U, 0xa25e7ed5U, 0x83b1cf9aU, 0x27f93943U, 0x6a94f767U, 0xc0a69407U, 0xd19da4e1U,
 | 
				
			||||||
 | 
					                0xec1786ebU, 0x6fa64971U, 0x485f7032U, 0x22cb8755U, 0xe26d1352U, 0x33f0b7b3U, 0x40beeb28U, 0x2f18a259U,
 | 
				
			||||||
 | 
					                0x6747d26bU, 0x458c553eU, 0xa7e1466cU, 0x9411f1dfU, 0x821f750aU, 0xad07d753U, 0xca400538U, 0x8fcc5006U,
 | 
				
			||||||
 | 
					                0x282d166aU, 0xbc3ce7b5U, 0xe98ba06fU, 0x448c773cU, 0x8ecc7204U, 0x01002202U
 | 
				
			||||||
 | 
					            // @formatter:on
 | 
				
			||||||
 | 
					            ).toTypedArray()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val aes = Aes(AesKey.Aes192Key(key), irrelevantInput)
 | 
				
			||||||
 | 
					            val result = aes.expandedKey.map {
 | 
				
			||||||
 | 
					                it.foldIndexed(0U) { index, acc, uByte ->
 | 
				
			||||||
 | 
					                    acc + (uByte.toUInt() shl (24 - index * 8))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }.toTypedArray()
 | 
				
			||||||
 | 
					            expectedExpandedKey.contentEquals(result)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val key = "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val expectedExpandedKey = uintArrayOf(
 | 
				
			||||||
 | 
					                // @formatter:off
 | 
				
			||||||
 | 
					                0x603deb10U, 0x15ca71beU, 0x2b73aef0U, 0x857d7781U, 0x1f352c07U, 0x3b6108d7U, 0x2d9810a3U, 0x0914dff4U,
 | 
				
			||||||
 | 
					                0x9ba35411U, 0x8e6925afU, 0xa51a8b5fU, 0x2067fcdeU, 0xa8b09c1aU, 0x93d194cdU, 0xbe49846eU, 0xb75d5b9aU,
 | 
				
			||||||
 | 
					                0xd59aecb8U, 0x5bf3c917U, 0xfee94248U, 0xde8ebe96U, 0xb5a9328aU, 0x2678a647U, 0x98312229U, 0x2f6c79b3U,
 | 
				
			||||||
 | 
					                0x812c81adU, 0xdadf48baU, 0x24360af2U, 0xfab8b464U, 0x98c5bfc9U, 0xbebd198eU, 0x268c3ba7U, 0x09e04214U,
 | 
				
			||||||
 | 
					                0x68007bacU, 0xb2df3316U, 0x96e939e4U, 0x6c518d80U, 0xc814e204U, 0x76a9fb8aU, 0x5025c02dU, 0x59c58239U,
 | 
				
			||||||
 | 
					                0xde136967U, 0x6ccc5a71U, 0xfa256395U, 0x9674ee15U, 0x5886ca5dU, 0x2e2f31d7U, 0x7e0af1faU, 0x27cf73c3U,
 | 
				
			||||||
 | 
					                0x749c47abU, 0x18501ddaU, 0xe2757e4fU, 0x7401905aU, 0xcafaaae3U, 0xe4d59b34U, 0x9adf6aceU, 0xbd10190dU,
 | 
				
			||||||
 | 
					                0xfe4890d1U, 0xe6188d0bU, 0x046df344U, 0x706c631eU
 | 
				
			||||||
 | 
					            // @formatter:on
 | 
				
			||||||
 | 
					            ).toTypedArray()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            val aes = Aes(AesKey.Aes256Key(key), irrelevantInput)
 | 
				
			||||||
 | 
					            val result = aes.expandedKey.map {
 | 
				
			||||||
 | 
					                it.foldIndexed(0U) { index, acc, uByte ->
 | 
				
			||||||
 | 
					                    acc + (uByte.toUInt() shl (24 - index * 8))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }.toTypedArray()
 | 
				
			||||||
 | 
					            expectedExpandedKey.contentEquals(result)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testEncryption() {
 | 
				
			||||||
 | 
					        val input = "3243f6a8885a308d313198a2e0370734"
 | 
				
			||||||
 | 
					        val key = "2b7e151628aed2a6abf7158809cf4f3c"
 | 
				
			||||||
 | 
					        val expectedResult = "3925841d02dc09fbdc118597196a0b32"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val aes = Aes(AesKey.Aes128Key(key), input.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray())
 | 
				
			||||||
 | 
					        val result = aes.encrypt()
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            result.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Test
 | 
				
			||||||
 | 
					    fun testEncryptionAndDecryption() {
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val input = "3243f6a8885a308d313198a2e0370734"
 | 
				
			||||||
 | 
					            val key = "2b7e151628aed2a6abf7158809cf4f3c"
 | 
				
			||||||
 | 
					            val expectedResult = "3925841d02dc09fbdc118597196a0b32"
 | 
				
			||||||
 | 
					            val original = input.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray()
 | 
				
			||||||
 | 
					            val aes = Aes(AesKey.Aes128Key(key), original)
 | 
				
			||||||
 | 
					            val encrypted = aes.encrypt()
 | 
				
			||||||
 | 
					            assertTrue {
 | 
				
			||||||
 | 
					                encrypted.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            val decrypted = Aes.decrypt(AesKey.Aes128Key(key), encrypted)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            decrypted.contentEquals(original)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val input = "00112233445566778899aabbccddeeff"
 | 
				
			||||||
 | 
					            val key = "000102030405060708090a0b0c0d0e0f"
 | 
				
			||||||
 | 
					            val expectedResult = "69c4e0d86a7b0430d8cdb78070b4c55a"
 | 
				
			||||||
 | 
					            val original = input.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray()
 | 
				
			||||||
 | 
					            val aes = Aes(AesKey.Aes128Key(key), original)
 | 
				
			||||||
 | 
					            val encrypted = aes.encrypt()
 | 
				
			||||||
 | 
					            assertTrue {
 | 
				
			||||||
 | 
					                encrypted.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            val aesDec = Aes(AesKey.Aes128Key(key), encrypted)
 | 
				
			||||||
 | 
					            val decrypted = aesDec.decrypt()
 | 
				
			||||||
 | 
					            assertTrue {
 | 
				
			||||||
 | 
					                aesDec.expandedKey.contentDeepEquals(aes.expandedKey)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            decrypted.contentDeepEquals(original)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val input = "00112233445566778899aabbccddeeff"
 | 
				
			||||||
 | 
					            val key = "000102030405060708090a0b0c0d0e0f"
 | 
				
			||||||
 | 
					            val expectedResult = "69c4e0d86a7b0430d8cdb78070b4c55a"
 | 
				
			||||||
 | 
					            val original = input.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray()
 | 
				
			||||||
 | 
					            val encrypted = Aes.encrypt(AesKey.Aes128Key(key), original)
 | 
				
			||||||
 | 
					            assertTrue {
 | 
				
			||||||
 | 
					                encrypted.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            val decrypted = Aes.decrypt(AesKey.Aes128Key(key), encrypted)
 | 
				
			||||||
 | 
					            decrypted.contentDeepEquals(original)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val input = "00112233445566778899aabbccddeeff"
 | 
				
			||||||
 | 
					            val key = "000102030405060708090a0b0c0d0e0f1011121314151617"
 | 
				
			||||||
 | 
					            val expectedResult = "dda97ca4864cdfe06eaf70a0ec0d7191"
 | 
				
			||||||
 | 
					            val original = input.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray()
 | 
				
			||||||
 | 
					            val encrypted = Aes.encrypt(AesKey.Aes192Key(key), original)
 | 
				
			||||||
 | 
					            assertTrue {
 | 
				
			||||||
 | 
					                encrypted.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            val decrypted = Aes.decrypt(AesKey.Aes192Key(key), encrypted)
 | 
				
			||||||
 | 
					            decrypted.contentDeepEquals(original)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        assertTrue {
 | 
				
			||||||
 | 
					            val input = "00112233445566778899aabbccddeeff"
 | 
				
			||||||
 | 
					            val key = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
 | 
				
			||||||
 | 
					            val expectedResult = "8ea2b7ca516745bfeafc49904b496089"
 | 
				
			||||||
 | 
					            val original = input.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray()
 | 
				
			||||||
 | 
					            val encrypted = Aes.encrypt(AesKey.Aes256Key(key), original)
 | 
				
			||||||
 | 
					            assertTrue {
 | 
				
			||||||
 | 
					                encrypted.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray())
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            val decrypted = Aes.decrypt(AesKey.Aes256Key(key), encrypted)
 | 
				
			||||||
 | 
					            decrypted.contentDeepEquals(original)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *    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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Created by Ugljesa Jovanovic
 | 
				
			||||||
 | 
					 * ugljesa.jovanovic@ionspin.com
 | 
				
			||||||
 | 
					 * on 21-Sep-2019
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					actual object SRNG {
 | 
				
			||||||
 | 
					    var counter = 0
 | 
				
			||||||
 | 
					    @ExperimentalUnsignedTypes
 | 
				
			||||||
 | 
					    actual fun getRandomBytes(amount: Int): Array<UByte> {
 | 
				
			||||||
 | 
					//        val runningOnNode = js("(typeof window === 'undefined')").unsafeCast<Boolean>()
 | 
				
			||||||
 | 
					//        if (runningOnNode) {
 | 
				
			||||||
 | 
					//            js("var crypto = require('crypto')").asDynamic().randomBytes(amount)
 | 
				
			||||||
 | 
					//        } else {
 | 
				
			||||||
 | 
					//            throw RuntimeException("Secure random not supported yet for non-nodejs environment")
 | 
				
			||||||
 | 
					//        }
 | 
				
			||||||
 | 
					        return Array<UByte>(amount) { (counter++).toUByte() } // TODO Wow. Such random. Very entropy.
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,34 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *    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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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> {
 | 
				
			||||||
 | 
					        val byteArray = ByteArray(amount)
 | 
				
			||||||
 | 
					        secureRandom.nextBytes(byteArray)
 | 
				
			||||||
 | 
					        return byteArray.toUByteArray().toTypedArray()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *    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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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> {
 | 
				
			||||||
 | 
					        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