Added decryption

This commit is contained in:
Ugljesa Jovanovic 2019-09-19 23:53:23 +02:00 committed by Ugljesa Jovanovic
parent 9ffd6a8373
commit 1a91d90f41
No known key found for this signature in database
GPG Key ID: 33A5F353387711A5
3 changed files with 177 additions and 30 deletions

View File

@ -17,7 +17,16 @@ 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.
## Supported ## Supported
@ -26,6 +35,9 @@ No, it's untested and unproven.
* SHA512 * SHA512
* SHA256 * SHA256
## Symmetric cipher
* AES
More to come. More to come.
## Integration ## Integration

View File

@ -4,9 +4,9 @@ package com.ionspin.kotlin.crypto.symmetric
* Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 07/Sep/2019 * Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 07/Sep/2019
*/ */
@ExperimentalUnsignedTypes @ExperimentalUnsignedTypes
class Aes(val aesKey: AesKey, val input: Array<UByte>) { internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UByte>) {
companion object { companion object {
val sBox: UByteArray = private val sBox: UByteArray =
ubyteArrayOf( ubyteArrayOf(
// @formatter:off // @formatter:off
0x63U, 0x7cU, 0x77U, 0x7bU, 0xf2U, 0x6bU, 0x6fU, 0xc5U, 0x30U, 0x01U, 0x67U, 0x2bU, 0xfeU, 0xd7U, 0xabU, 0x76U, 0x63U, 0x7cU, 0x77U, 0x7bU, 0xf2U, 0x6bU, 0x6fU, 0xc5U, 0x30U, 0x01U, 0x67U, 0x2bU, 0xfeU, 0xd7U, 0xabU, 0x76U,
@ -28,7 +28,7 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
// @formatter:on // @formatter:on
) )
val inverseSBox: UByteArray = private val inverseSBox: UByteArray =
ubyteArrayOf( ubyteArrayOf(
// @formatter:off // @formatter:off
0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,
@ -52,6 +52,14 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
val rcon: UByteArray = ubyteArrayOf(0x8DU, 0x01U, 0x02U, 0x04U, 0x08U, 0x10U, 0x20U, 0x40U, 0x80U, 0x1BU, 0x36U) 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()
}
} }
init { init {
@ -60,11 +68,11 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
} }
} }
val state: Array<Array<UByte>> = (0 until 4).map{ outerCounter -> val state: Array<Array<UByte>> = (0 until 4).map { outerCounter ->
Array<UByte>(4) { innerCounter -> input[innerCounter * 4 + outerCounter] } Array<UByte>(4) { innerCounter -> input[innerCounter * 4 + outerCounter] }
}.toTypedArray() }.toTypedArray()
val numberOfRounds = when(aesKey) { val numberOfRounds = when (aesKey) {
is AesKey.Aes128Key -> 10 is AesKey.Aes128Key -> 10
is AesKey.Aes192Key -> 12 is AesKey.Aes192Key -> 12
is AesKey.Aes256Key -> 14 is AesKey.Aes256Key -> 14
@ -73,7 +81,6 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
val expandedKey: Array<Array<UByte>> = expandKey() val expandedKey: Array<Array<UByte>> = expandKey()
var round = 0 var round = 0
@ -91,6 +98,20 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
return sBox[firstDigit * 16 + secondDigit] 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() { fun shiftRows() {
state[0] = arrayOf(state[0][0], state[0][1], state[0][2], state[0][3]) 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[1] = arrayOf(state[1][1], state[1][2], state[1][3], state[1][0])
@ -98,22 +119,40 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
state[3] = arrayOf(state[3][3], state[3][0], state[3][1], state[3][2]) 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() { fun mixColumns() {
val stateMixed: Array<Array<UByte>> = (0 until 4).map { val stateMixed: Array<Array<UByte>> = (0 until 4).map {
Array<UByte>(4) { 0U } Array<UByte>(4) { 0U }
}.toTypedArray() }.toTypedArray()
for (c in 0..3) { for (c in 0..3) {
stateMixed[0][c] = (2U gfm state[0][c]) xor galoisFieldMultiply( stateMixed[0][c] = (2U gfm state[0][c]) xor (3U gfm state[1][c]) xor state[2][c] xor state[3][c]
3U, stateMixed[1][c] = state[0][c] xor (2U gfm state[1][c]) xor (3U gfm state[2][c]) xor state[3][c]
state[1][c] stateMixed[2][c] = state[0][c] xor state[1][c] xor (2U gfm state[2][c]) xor (3U gfm state[3][c])
) xor state[2][c] xor 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] = stateMixed[1][c] =
state[0][c] xor (2U gfm state[1][c]) xor (3U gfm state[2][c]) xor state[3][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] = stateMixed[2][c] =
state[0][c] xor state[1][c] xor (2U gfm state[2][c]) xor (3U gfm state[3][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] = stateMixed[3][c] =
3U gfm state[0][c] xor state[1][c] xor state[2][c] xor (2U gfm state[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) stateMixed.copyInto(state)
} }
@ -153,6 +192,16 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
round++ 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 { infix fun UInt.gfm(second: UByte): UByte {
return galoisFieldMultiply(this.toUByte(), second) return galoisFieldMultiply(this.toUByte(), second)
} }
@ -200,24 +249,72 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
return expandedKey return expandedKey
} }
fun encrypt() : Array<UByte> { fun encrypt(): Array<UByte> {
printState()
addRoundKey() addRoundKey()
printState()
for (i in 0 until numberOfRounds - 1) { for (i in 0 until numberOfRounds - 1) {
subBytes() subBytes()
printState()
shiftRows() shiftRows()
printState()
mixColumns() mixColumns()
printState()
addRoundKey() addRoundKey()
printState()
} }
subBytes() subBytes()
printState()
shiftRows() shiftRows()
printState()
addRoundKey() addRoundKey()
return state.flatten().toTypedArray() 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)
return transposedMatrix.flatten().toTypedArray()
} }
fun decrypt() : Array<UByte> { fun decrypt(): Array<UByte> {
return ubyteArrayOf().toTypedArray() 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)
return transposedMatrix.flatten().toTypedArray()
} }
fun printState() { fun printState() {
@ -227,6 +324,13 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
} }
} }
fun printState(specific : Array<Array<UByte>>) {
println()
specific.forEach {
println(it.joinToString(separator = " ") { it.toString(16) })
}
}
} }

View File

@ -23,9 +23,6 @@ class AesTest {
val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput) val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput)
fakeState.copyInto(aes.state) fakeState.copyInto(aes.state)
aes.subBytes() aes.subBytes()
aes.state.forEach {
println(it.joinToString { it.toString(16) })
}
assertTrue { assertTrue {
aes.state[0][0] == 0xEDU.toUByte() aes.state[0][0] == 0xEDU.toUByte()
} }
@ -48,9 +45,6 @@ class AesTest {
val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput) val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput)
fakeState.copyInto(aes.state) fakeState.copyInto(aes.state)
aes.shiftRows() aes.shiftRows()
aes.state.forEach {
println(it.joinToString { it.toString(16) })
}
assertTrue { assertTrue {
aes.state.contentDeepEquals(expectedState) aes.state.contentDeepEquals(expectedState)
} }
@ -98,9 +92,6 @@ class AesTest {
val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput) val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput)
fakeState.copyInto(aes.state) fakeState.copyInto(aes.state)
aes.mixColumns() aes.mixColumns()
aes.state.forEach {
println(it.joinToString { it.toString(16) })
}
assertTrue { assertTrue {
aes.state.contentDeepEquals(expectedState) aes.state.contentDeepEquals(expectedState)
} }
@ -190,7 +181,7 @@ class AesTest {
fun testEncryption() { fun testEncryption() {
val input = "3243f6a8885a308d313198a2e0370734" val input = "3243f6a8885a308d313198a2e0370734"
val key = "2b7e151628aed2a6abf7158809cf4f3c" val key = "2b7e151628aed2a6abf7158809cf4f3c"
val expectedResult = "3902dc1925dc116a8409850b1dfb9732" val expectedResult = "3925841d02dc09fbdc118597196a0b32"
val aes = Aes(AesKey.Aes128Key(key), input.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray()) val aes = Aes(AesKey.Aes128Key(key), input.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray())
val result = aes.encrypt() val result = aes.encrypt()
@ -198,4 +189,44 @@ class AesTest {
result.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray()) result.contentEquals(expectedResult.chunked(2).map { it.toInt(16).toUByte() }.toTypedArray())
} }
} }
@Test
fun testDecryption() {
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)
assertTrue {
decrypted.contentEquals(original)
}
}
@Test
fun testDecryption2() {
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)
}
assertTrue {
decrypted.contentDeepEquals(original)
}
}
} }