-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
238bd02
commit 501ff71
Showing
42 changed files
with
3,557 additions
and
51 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
plugins { | ||
kotlin("multiplatform") | ||
id("com.android.library") | ||
} | ||
|
||
kotlin { | ||
android { | ||
compilations.all { | ||
kotlinOptions { | ||
jvmTarget = "1.8" | ||
} | ||
} | ||
} | ||
|
||
listOf( | ||
iosX64(), | ||
iosArm64(), | ||
iosSimulatorArm64() | ||
).forEach { | ||
it.binaries.framework { | ||
baseName = "cipher" | ||
} | ||
} | ||
|
||
sourceSets { | ||
val commonMain by getting { | ||
dependencies { | ||
implementation(projects.crypto.secureRandom) | ||
} | ||
} | ||
val commonTest by getting { | ||
dependencies { | ||
implementation(kotlin("test")) | ||
} | ||
} | ||
val androidMain by getting | ||
val androidUnitTest by getting | ||
val iosX64Main by getting | ||
val iosArm64Main by getting | ||
val iosSimulatorArm64Main by getting | ||
val iosMain by creating { | ||
dependsOn(commonMain) | ||
iosX64Main.dependsOn(this) | ||
iosArm64Main.dependsOn(this) | ||
iosSimulatorArm64Main.dependsOn(this) | ||
} | ||
val iosX64Test by getting | ||
val iosArm64Test by getting | ||
val iosSimulatorArm64Test by getting | ||
val iosTest by creating { | ||
dependsOn(commonTest) | ||
iosX64Test.dependsOn(this) | ||
iosArm64Test.dependsOn(this) | ||
iosSimulatorArm64Test.dependsOn(this) | ||
} | ||
} | ||
} | ||
|
||
android { | ||
namespace = "io.spherelabs.crypto.cipher" | ||
compileSdk = 33 | ||
defaultConfig { | ||
minSdk = 24 | ||
} | ||
} |
217 changes: 217 additions & 0 deletions
217
crypto/cipher/src/commonMain/kotlin/io/spherelabs/crypto/cipher/AES.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
package io.spherelabs.crypto.cipher | ||
|
||
/** | ||
* Based on CryptoJS v3.1.2 | ||
* code.google.com/p/crypto-js | ||
* (c) 2009-2013 by Jeff Mott. All rights reserved. | ||
* code.google.com/p/crypto-js/wiki/License | ||
* | ||
* https://github.com/korlibs/korge/blob/main/korlibs-crypto/src/korlibs/crypto/AES.kt | ||
*/ | ||
class AES(val keyWords: IntArray) : Cipher { | ||
override val blockSize: Int get() = BLOCK_SIZE | ||
|
||
private val keySize = keyWords.size | ||
private val numRounds = keySize + 6 | ||
private val ksRows = (numRounds + 1) * 4 | ||
private val keySchedule = IntArray(ksRows).apply { | ||
for (ksRow in indices) { | ||
this[ksRow] = when { | ||
ksRow < keySize -> keyWords[ksRow] | ||
else -> { | ||
var t = this[ksRow - 1] | ||
if (0 == (ksRow % keySize)) { | ||
t = (t shl 8) or (t ushr 24) | ||
t = (SBOX[t.ext8(24)] shl 24) or (SBOX[t.ext8(16)] shl 16) or (SBOX[t.ext8(8)] shl 8) or SBOX[t and 0xff] | ||
t = t xor (RCON[(ksRow / keySize) or 0] shl 24) | ||
} else if (keySize > 6 && ksRow % keySize == 4) { | ||
t = (SBOX[t.ext8(24)] shl 24) or (SBOX[t.ext8(16)] shl 16) or (SBOX[t.ext8(8)] shl 8) or SBOX[t and 0xff] | ||
} | ||
this[ksRow - keySize] xor t | ||
} | ||
} | ||
} | ||
} | ||
private val invKeySchedule = IntArray(ksRows).apply { | ||
for (invKsRow in indices) { | ||
val ksRow = ksRows - invKsRow | ||
val t = if ((invKsRow % 4) != 0) keySchedule[ksRow] else keySchedule[ksRow - 4] | ||
this[invKsRow] = if (invKsRow < 4 || ksRow <= 4) t else INV_SUB_MIX_0[SBOX[t.ext8(24)]] xor INV_SUB_MIX_1[SBOX[t.ext8(16)]] xor INV_SUB_MIX_2[SBOX[t.ext8(8)]] xor INV_SUB_MIX_3[SBOX[t and 0xff]] | ||
} | ||
} | ||
|
||
constructor(key: ByteArray) : this(key.toIntArray()) | ||
|
||
override fun encrypt(data: ByteArray, offset: Int, len: Int) { | ||
for (n in 0 until len step BLOCK_SIZE) encryptBlock(data, offset + n) | ||
} | ||
|
||
override fun decrypt(data: ByteArray, offset: Int, len: Int) { | ||
for (n in 0 until len step BLOCK_SIZE) decryptBlock(data, offset + n) | ||
} | ||
|
||
fun encryptBlock(M: ByteArray, offset: Int) { | ||
this.doCryptBlock(M, offset, this.keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX) | ||
} | ||
|
||
fun decryptBlock(M: ByteArray, offset: Int) { | ||
this.doCryptBlock( | ||
M, offset, | ||
this.invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX, | ||
swap13 = true | ||
) | ||
} | ||
|
||
private fun doCryptBlock( | ||
M: IntArray, offset: Int, keySchedule: IntArray, | ||
SUB_MIX_0: IntArray, SUB_MIX_1: IntArray, SUB_MIX_2: IntArray, SUB_MIX_3: IntArray, SBOX: IntArray, | ||
swap13: Boolean = false | ||
) { | ||
doCryptBlockInternal(M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX, swap13, | ||
get = { array, o, i -> array[o + i] }, | ||
set = { array, o, i, value -> array[o + i] = value }, | ||
) | ||
} | ||
|
||
private fun doCryptBlock( | ||
M: ByteArray, offset: Int, keySchedule: IntArray, | ||
SUB_MIX_0: IntArray, SUB_MIX_1: IntArray, SUB_MIX_2: IntArray, SUB_MIX_3: IntArray, SBOX: IntArray, | ||
swap13: Boolean = false | ||
) { | ||
doCryptBlockInternal(M, offset, keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX, swap13, | ||
get = { array, o, i -> array.getInt(o + i * 4) }, | ||
set = { array, o, i, value -> array.setInt(o + i * 4, value) }, | ||
) | ||
} | ||
|
||
private inline fun <T> doCryptBlockInternal( | ||
M: T, offset: Int, keySchedule: IntArray, | ||
SUB_MIX_0: IntArray, SUB_MIX_1: IntArray, SUB_MIX_2: IntArray, SUB_MIX_3: IntArray, SBOX: IntArray, | ||
swap13: Boolean = false, | ||
get: (M: T, offset: Int, index: Int) -> Int, | ||
set: (M: T, offset: Int, index: Int, value: Int) -> Unit, | ||
) { | ||
val O1 = if (!swap13) 1 else 3 | ||
val O3 = if (!swap13) 3 else 1 | ||
var s0 = get(M, offset, 0) xor keySchedule[0] | ||
var s1 = get(M, offset, O1) xor keySchedule[1] | ||
var s2 = get(M, offset, 2) xor keySchedule[2] | ||
var s3 = get(M, offset, O3) xor keySchedule[3] | ||
var ksRow = 4 | ||
|
||
for (round in 1 until numRounds) { | ||
val t0 = SUB_MIX_0[s0.ext8(24)] xor SUB_MIX_1[s1.ext8(16)] xor SUB_MIX_2[s2.ext8(8)] xor SUB_MIX_3[s3.ext8(0)] xor keySchedule[ksRow++] | ||
val t1 = SUB_MIX_0[s1.ext8(24)] xor SUB_MIX_1[s2.ext8(16)] xor SUB_MIX_2[s3.ext8(8)] xor SUB_MIX_3[s0.ext8(0)] xor keySchedule[ksRow++] | ||
val t2 = SUB_MIX_0[s2.ext8(24)] xor SUB_MIX_1[s3.ext8(16)] xor SUB_MIX_2[s0.ext8(8)] xor SUB_MIX_3[s1.ext8(0)] xor keySchedule[ksRow++] | ||
val t3 = SUB_MIX_0[s3.ext8(24)] xor SUB_MIX_1[s0.ext8(16)] xor SUB_MIX_2[s1.ext8(8)] xor SUB_MIX_3[s2.ext8(0)] xor keySchedule[ksRow++] | ||
s0 = t0; s1 = t1; s2 = t2; s3 = t3 | ||
} | ||
|
||
val t0 = ((SBOX[s0.ext8(24)] shl 24) or (SBOX[s1.ext8(16)] shl 16) or (SBOX[s2.ext8(8)] shl 8) or SBOX[s3.ext8(0)]) xor keySchedule[ksRow++] | ||
val t1 = ((SBOX[s1.ext8(24)] shl 24) or (SBOX[s2.ext8(16)] shl 16) or (SBOX[s3.ext8(8)] shl 8) or SBOX[s0.ext8(0)]) xor keySchedule[ksRow++] | ||
val t2 = ((SBOX[s2.ext8(24)] shl 24) or (SBOX[s3.ext8(16)] shl 16) or (SBOX[s0.ext8(8)] shl 8) or SBOX[s1.ext8(0)]) xor keySchedule[ksRow++] | ||
val t3 = ((SBOX[s3.ext8(24)] shl 24) or (SBOX[s0.ext8(16)] shl 16) or (SBOX[s1.ext8(8)] shl 8) or SBOX[s2.ext8(0)]) xor keySchedule[ksRow++] | ||
|
||
set(M, offset, 0, t0) | ||
set(M, offset, O1, t1) | ||
set(M, offset, 2, t2) | ||
set(M, offset, O3, t3) | ||
} | ||
|
||
companion object { | ||
private val SBOX = IntArray(256) | ||
private val INV_SBOX = IntArray(256) | ||
private val SUB_MIX_0 = IntArray(256) | ||
private val SUB_MIX_1 = IntArray(256) | ||
private val SUB_MIX_2 = IntArray(256) | ||
private val SUB_MIX_3 = IntArray(256) | ||
private val INV_SUB_MIX_0 = IntArray(256) | ||
private val INV_SUB_MIX_1 = IntArray(256) | ||
private val INV_SUB_MIX_2 = IntArray(256) | ||
private val INV_SUB_MIX_3 = IntArray(256) | ||
private val RCON = intArrayOf(0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36) | ||
|
||
private const val BLOCK_SIZE = 16 | ||
|
||
init { | ||
val d = IntArray(256) { if (it >= 128) (it shl 1) xor 0x11b else (it shl 1) } | ||
var x = 0 | ||
var xi = 0 | ||
for (i in 0 until 256) { | ||
var sx = xi xor (xi shl 1) xor (xi shl 2) xor (xi shl 3) xor (xi shl 4) | ||
sx = (sx ushr 8) xor (sx and 0xff) xor 0x63 | ||
SBOX[x] = sx | ||
INV_SBOX[sx] = x | ||
val x2 = d[x] | ||
val x4 = d[x2] | ||
val x8 = d[x4] | ||
((d[sx] * 0x101) xor (sx * 0x1010100)).also { t -> | ||
SUB_MIX_0[x] = (t shl 24) or (t ushr 8) | ||
SUB_MIX_1[x] = (t shl 16) or (t ushr 16) | ||
SUB_MIX_2[x] = (t shl 8) or (t ushr 24) | ||
SUB_MIX_3[x] = (t shl 0) | ||
} | ||
((x8 * 0x1010101) xor (x4 * 0x10001) xor (x2 * 0x101) xor (x * 0x1010100)).also { t -> | ||
INV_SUB_MIX_0[sx] = (t shl 24) or (t ushr 8) | ||
INV_SUB_MIX_1[sx] = (t shl 16) or (t ushr 16) | ||
INV_SUB_MIX_2[sx] = (t shl 8) or (t ushr 24) | ||
INV_SUB_MIX_3[sx] = (t shl 0) | ||
} | ||
|
||
if (x == 0) { | ||
x = 1; xi = 1 | ||
} else { | ||
x = x2 xor d[d[d[x8 xor x2]]] | ||
xi = xi xor d[d[xi]] | ||
} | ||
} | ||
} | ||
|
||
fun encryptAesEcb(data: ByteArray, key: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.ECB, padding].encrypt(data) | ||
|
||
fun decryptAesEcb(data: ByteArray, key: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.ECB, padding].decrypt(data) | ||
|
||
fun encryptAesCbc(data: ByteArray, key: ByteArray, iv: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.CBC, padding, iv].encrypt(data) | ||
|
||
fun decryptAesCbc(data: ByteArray, key: ByteArray, iv: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.CBC, padding, iv].decrypt(data) | ||
|
||
fun encryptAesPcbc(data: ByteArray, key: ByteArray, iv: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.PCBC, padding, iv].encrypt(data) | ||
|
||
fun decryptAesPcbc(data: ByteArray, key: ByteArray, iv: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.PCBC, padding, iv].decrypt(data) | ||
|
||
fun encryptAesCfb(data: ByteArray, key: ByteArray, iv: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.CFB, padding, iv].encrypt(data) | ||
|
||
fun decryptAesCfb(data: ByteArray, key: ByteArray, iv: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.CFB, padding, iv].decrypt(data) | ||
|
||
fun encryptAesOfb(data: ByteArray, key: ByteArray, iv: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.OFB, padding, iv].encrypt(data) | ||
|
||
fun decryptAesOfb(data: ByteArray, key: ByteArray, iv: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.OFB, padding, iv].decrypt(data) | ||
|
||
fun encryptAesCtr(data: ByteArray, key: ByteArray, iv: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.CTR, padding, iv].encrypt(data) | ||
|
||
fun decryptAesCtr(data: ByteArray, key: ByteArray, iv: ByteArray, padding: Padding): ByteArray = | ||
AES(key)[CipherMode.CTR, padding, iv].decrypt(data) | ||
} | ||
} | ||
|
||
private fun ByteArray.getu(offset: Int): Int = (this[offset].toInt() and 0xFF) | ||
private inline fun Int.ext8(offset: Int): Int = (this ushr offset) and 0xFF | ||
private fun ByteArray.toIntArray(): IntArray = IntArray(size / 4).also { for (n in it.indices) it[n] = getInt(n * 4) } | ||
private fun ByteArray.getInt(offset: Int): Int = (getu(offset + 0) shl 24) or (getu(offset + 1) shl 16) or (getu(offset + 2) shl 8) or (getu(offset + 3) shl 0) | ||
private fun ByteArray.setInt(offset: Int, value: Int) { | ||
this[offset + 0] = ((value shr 24) and 0xFF).toByte() | ||
this[offset + 1] = ((value shr 16) and 0xFF).toByte() | ||
this[offset + 2] = ((value shr 8) and 0xFF).toByte() | ||
this[offset + 3] = ((value shr 0) and 0xFF).toByte() | ||
} |
Oops, something went wrong.