Added decryption
This commit is contained in:
		
							parent
							
								
									9ffd6a8373
								
							
						
					
					
						commit
						1a91d90f41
					
				
							
								
								
									
										14
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								README.md
									
									
									
									
									
								
							@ -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?
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
@ -26,6 +35,9 @@ No, it's untested and unproven.
 | 
			
		||||
* SHA512
 | 
			
		||||
* SHA256
 | 
			
		||||
 | 
			
		||||
## Symmetric cipher 
 | 
			
		||||
* AES
 | 
			
		||||
 | 
			
		||||
More to come.
 | 
			
		||||
 | 
			
		||||
## Integration
 | 
			
		||||
 | 
			
		||||
@ -4,9 +4,9 @@ package com.ionspin.kotlin.crypto.symmetric
 | 
			
		||||
 * Created by Ugljesa Jovanovic (jovanovic.ugljesa@gmail.com) on 07/Sep/2019
 | 
			
		||||
 */
 | 
			
		||||
@ExperimentalUnsignedTypes
 | 
			
		||||
class Aes(val aesKey: AesKey, val input: Array<UByte>) {
 | 
			
		||||
internal class Aes internal constructor(val aesKey: AesKey, val input: Array<UByte>) {
 | 
			
		||||
    companion object {
 | 
			
		||||
        val sBox: UByteArray =
 | 
			
		||||
        private val sBox: UByteArray =
 | 
			
		||||
            ubyteArrayOf(
 | 
			
		||||
                // @formatter:off
 | 
			
		||||
                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
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        val inverseSBox: UByteArray =
 | 
			
		||||
        private val inverseSBox: UByteArray =
 | 
			
		||||
            ubyteArrayOf(
 | 
			
		||||
                // @formatter:off
 | 
			
		||||
                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)
 | 
			
		||||
 | 
			
		||||
        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 {
 | 
			
		||||
@ -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] }
 | 
			
		||||
    }.toTypedArray()
 | 
			
		||||
 | 
			
		||||
    val numberOfRounds = when(aesKey) {
 | 
			
		||||
    val numberOfRounds = when (aesKey) {
 | 
			
		||||
        is AesKey.Aes128Key -> 10
 | 
			
		||||
        is AesKey.Aes192Key -> 12
 | 
			
		||||
        is AesKey.Aes256Key -> 14
 | 
			
		||||
@ -73,7 +81,6 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
 | 
			
		||||
    val expandedKey: Array<Array<UByte>> = expandKey()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    var round = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -91,6 +98,20 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
 | 
			
		||||
        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])
 | 
			
		||||
@ -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])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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 galoisFieldMultiply(
 | 
			
		||||
                3U,
 | 
			
		||||
                state[1][c]
 | 
			
		||||
            ) xor state[2][c] xor state[3][c]
 | 
			
		||||
            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] =
 | 
			
		||||
                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] =
 | 
			
		||||
                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] =
 | 
			
		||||
                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)
 | 
			
		||||
    }
 | 
			
		||||
@ -153,6 +192,16 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
 | 
			
		||||
        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)
 | 
			
		||||
    }
 | 
			
		||||
@ -200,30 +249,85 @@ class Aes(val aesKey: AesKey, val input: Array<UByte>) {
 | 
			
		||||
        return expandedKey
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun encrypt() : Array<UByte> {
 | 
			
		||||
    fun encrypt(): Array<UByte> {
 | 
			
		||||
        printState()
 | 
			
		||||
        addRoundKey()
 | 
			
		||||
 | 
			
		||||
        printState()
 | 
			
		||||
        for (i in 0 until numberOfRounds - 1) {
 | 
			
		||||
            subBytes()
 | 
			
		||||
            printState()
 | 
			
		||||
            shiftRows()
 | 
			
		||||
            printState()
 | 
			
		||||
            mixColumns()
 | 
			
		||||
            printState()
 | 
			
		||||
            addRoundKey()
 | 
			
		||||
            printState()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        subBytes()
 | 
			
		||||
        printState()
 | 
			
		||||
        shiftRows()
 | 
			
		||||
        printState()
 | 
			
		||||
        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> {
 | 
			
		||||
        return ubyteArrayOf().toTypedArray()
 | 
			
		||||
    fun decrypt(): Array<UByte> {
 | 
			
		||||
        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() {
 | 
			
		||||
        println()
 | 
			
		||||
        state.forEach {
 | 
			
		||||
            println(it.joinToString(separator = " ") { it.toString(16)  })
 | 
			
		||||
            println(it.joinToString(separator = " ") { it.toString(16) })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun printState(specific : Array<Array<UByte>>) {
 | 
			
		||||
        println()
 | 
			
		||||
        specific.forEach {
 | 
			
		||||
            println(it.joinToString(separator = " ") { it.toString(16) })
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -23,9 +23,6 @@ class AesTest {
 | 
			
		||||
        val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput)
 | 
			
		||||
        fakeState.copyInto(aes.state)
 | 
			
		||||
        aes.subBytes()
 | 
			
		||||
        aes.state.forEach {
 | 
			
		||||
            println(it.joinToString { it.toString(16) })
 | 
			
		||||
        }
 | 
			
		||||
        assertTrue {
 | 
			
		||||
            aes.state[0][0] == 0xEDU.toUByte()
 | 
			
		||||
        }
 | 
			
		||||
@ -48,9 +45,6 @@ class AesTest {
 | 
			
		||||
        val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput)
 | 
			
		||||
        fakeState.copyInto(aes.state)
 | 
			
		||||
        aes.shiftRows()
 | 
			
		||||
        aes.state.forEach {
 | 
			
		||||
            println(it.joinToString { it.toString(16) })
 | 
			
		||||
        }
 | 
			
		||||
        assertTrue {
 | 
			
		||||
            aes.state.contentDeepEquals(expectedState)
 | 
			
		||||
        }
 | 
			
		||||
@ -98,9 +92,6 @@ class AesTest {
 | 
			
		||||
        val aes = Aes(AesKey.Aes128Key(irrelevantKey), irrelevantInput)
 | 
			
		||||
        fakeState.copyInto(aes.state)
 | 
			
		||||
        aes.mixColumns()
 | 
			
		||||
        aes.state.forEach {
 | 
			
		||||
            println(it.joinToString { it.toString(16) })
 | 
			
		||||
        }
 | 
			
		||||
        assertTrue {
 | 
			
		||||
            aes.state.contentDeepEquals(expectedState)
 | 
			
		||||
        }
 | 
			
		||||
@ -190,7 +181,7 @@ class AesTest {
 | 
			
		||||
    fun testEncryption() {
 | 
			
		||||
        val input = "3243f6a8885a308d313198a2e0370734"
 | 
			
		||||
        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 result = aes.encrypt()
 | 
			
		||||
@ -198,4 +189,44 @@ class AesTest {
 | 
			
		||||
            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)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user