All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.dev.icerock.moko.web3.crypto.Keccak.kt Maven / Gradle / Ivy

There is a newer version: 0.18.4-ktor2_ionspinbignum
Show newest version
/*
 * Copyright (c) 2017 ligi. Use of this source code is governed by the MIT License.
 * Original: https://github.com/komputing/KHash/blob/master/keccak/src/main/kotlin/org/komputing/khash/keccak/Keccak.kt
 */

package dev.icerock.moko.web3.crypto

import com.ionspin.kotlin.bignum.integer.BigInteger
import com.ionspin.kotlin.bignum.integer.toBigInteger
import kotlin.math.min

object Keccak {
    private val BIT_65 = BigInteger(1).shl(64)
    private val MAX_64_BITS = BIT_65.minus(BigInteger(1))

    fun digest(value: ByteArray, parameter: KeccakParameter): ByteArray {
        val uState = IntArray(200)
        val uMessage = convertToUInt(value)

        var blockSize = 0
        var inputOffset = 0

        // Absorbing phase
        while (inputOffset < uMessage.size) {
            blockSize = min(uMessage.size - inputOffset, parameter.rateInBytes)
            for (i in 0 until blockSize) {
                uState[i] = uState[i] xor uMessage[i + inputOffset]
            }

            inputOffset += blockSize

            if (blockSize == parameter.rateInBytes) {
                doF(uState)
                blockSize = 0
            }
        }

        // Padding phase
        uState[blockSize] = uState[blockSize] xor parameter.d
        if (parameter.d and 0x80 != 0 && blockSize == parameter.rateInBytes - 1) {
            doF(uState)
        }

        uState[parameter.rateInBytes - 1] = uState[parameter.rateInBytes - 1] xor 0x80
        doF(uState)

        // Squeezing phase
        val byteResults = mutableListOf()
        var tOutputLen = parameter.outputLengthInBytes
        while (tOutputLen > 0) {
            blockSize = min(tOutputLen, parameter.rateInBytes)
            for (i in 0 until blockSize) {
                byteResults.add(uState[i].toByte().toInt().toByte())
            }

            tOutputLen -= blockSize
            if (tOutputLen > 0) {
                doF(uState)
            }
        }

        return byteResults.toByteArray()
    }

    private fun doF(uState: IntArray) {
        val lState = Array(5) { Array(5) { BigInteger(0) } }

        for (i in 0..4) {
            for (j in 0..4) {
                val data = IntArray(8)
                val index = 8 * (i + 5 * j)
                uState.copyInto(data, 0, index, index + data.size)
                lState[i][j] = convertFromLittleEndianTo64(data)
            }
        }
        roundB(lState)

        uState.fillWith(0)
        for (i in 0..4) {
            for (j in 0..4) {
                val data = convertFrom64ToLittleEndian(lState[i][j])
                data.copyInto(uState, 8 * (i + 5 * j))
            }
        }
    }

    /**
     * Permutation on the given state.
     */
    private fun roundB(state: Array>) {
        var lfsrState = 1
        for (round in 0..23) {
            val c = arrayOfNulls(5)
            val d = arrayOfNulls(5)

            // θ step
            for (i in 0..4) {
                c[i] = state[i][0].xor(state[i][1]).xor(state[i][2]).xor(state[i][3]).xor(state[i][4])
            }

            for (i in 0..4) {
                d[i] = c[(i + 4) % 5]!!.xor(c[(i + 1) % 5]!!.leftRotate64(1))
            }

            for (i in 0..4) {
                for (j in 0..4) {
                    state[i][j] = state[i][j].xor(d[i]!!)
                }
            }

            // ρ and π steps
            var x = 1
            var y = 0
            var current = state[x][y]
            for (i in 0..23) {
                val tX = x
                x = y
                y = (2 * tX + 3 * y) % 5

                val shiftValue = current
                current = state[x][y]

                state[x][y] = shiftValue.leftRotate64Safely((i + 1) * (i + 2) / 2)
            }

            // χ step
            for (j in 0..4) {
                val t = arrayOfNulls(5)
                for (i in 0..4) {
                    t[i] = state[i][j]
                }

                for (i in 0..4) {
                    // ~t[(i + 1) % 5]
                    val invertVal = t[(i + 1) % 5]!!.xor(MAX_64_BITS)
                    // t[i] ^ ((~t[(i + 1) % 5]) & t[(i + 2) % 5])
                    state[i][j] = t[i]!!.xor(invertVal.and(t[(i + 2) % 5]!!))
                }
            }

            // ι step
            for (i in 0..6) {
                lfsrState = (lfsrState shl 1 xor (lfsrState shr 7) * 0x71) % 256
                // pow(2, i) - 1
                val bitPosition = (1 shl i) - 1
                if (lfsrState and 2 != 0) {
                    state[0][0] = state[0][0].xor(BigInteger(1).shl(bitPosition))
                }
            }
        }
    }

    /**
     * Converts the given [data] array to an [IntArray] containing UInt values.
     */
    private fun convertToUInt(data: ByteArray) = IntArray(data.size) {
        data[it].toInt() and 0xFF
    }

    /**
     * Converts the given [data] array containing the little endian representation of a number to a [BigInteger].
     */
    private fun convertFromLittleEndianTo64(data: IntArray): BigInteger {
        val value = data.map { it.toString(16) }
            .map { if (it.length == 2) it else "0$it" }
            .reversed()
            .joinToString("")
        return value.toBigInteger(16)
    }

    /**
     * Converts the given [BigInteger] to a little endian representation as an [IntArray].
     */
    private fun convertFrom64ToLittleEndian(uLong: BigInteger): IntArray {
        val asHex = uLong.toString(16)
        val asHexPadded = "0".repeat((8 * 2) - asHex.length) + asHex
        return IntArray(8) {
            ((7 - it) * 2).let { pos ->
                asHexPadded.substring(pos, pos + 2).toInt(16)
            }
        }
    }

    private fun BigInteger.leftRotate64Safely(rotate: Int) = leftRotate64(rotate % 64)

    private fun BigInteger.leftRotate64(rotate: Int) = shr(64 - rotate).plus(shl(rotate)).rem(BIT_65)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy