Last active
October 5, 2025 23:01
-
-
Save igor-ramazanov/f630b9474ec895e8f3289ddee502bc24 to your computer and use it in GitHub Desktop.
Pure Scala Keccak-256 hashing function implementation
This file contains hidden or 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
| object Keccak256: | |
| def hash(data: Array[Byte]): Array[Byte] = new Keccak256().hash(data) | |
| private class Keccak256 private ( | |
| private val state: Array[Long] = Array.fill[Long](25)(0L), | |
| private val dataQueue: Array[Byte] = Array.fill[Byte](192)(0), | |
| private var bitsInQueue: Int = 0, | |
| ): | |
| private def hash(data: Array[Byte]): Array[Byte] = | |
| if data.length < 136 then | |
| Array.copy(data, 0, dataQueue, 0, data.length) | |
| bitsInQueue += data.length << 3 | |
| else | |
| var count: Int = 0 | |
| var remaining: Int = data.length - count | |
| while remaining >= 136 do | |
| KeccakAbsorb(data, count) | |
| count += 136 | |
| remaining = data.length - count | |
| Array.copy(data, count, dataQueue, 0, remaining) | |
| bitsInQueue = remaining << 3 | |
| end if | |
| val output = Array.fill[Byte](32)(0) | |
| dataQueue(bitsInQueue >>> 3) = (dataQueue(bitsInQueue >>> 3) | (1 << (bitsInQueue & 7)).toByte).toByte | |
| bitsInQueue += 1 | |
| if bitsInQueue == 1088 then KeccakAbsorb(dataQueue, 0) | |
| else | |
| val full = bitsInQueue >>> 6 | |
| val partial = bitsInQueue & 63 | |
| var off = 0 | |
| var i = 0 | |
| while i < full do | |
| state(i) ^= littleEndianToLong(dataQueue, off) | |
| off += 8 | |
| i += 1 | |
| if partial > 0 then | |
| val mask = (1L << partial) - 1L | |
| state(full) ^= littleEndianToLong(dataQueue, off) & mask | |
| end if | |
| state(16) ^= 1L << 63 | |
| bitsInQueue = 0 | |
| var i: Long = 0L | |
| while i < 256 do | |
| if bitsInQueue == 0 then KeccakExtract() | |
| val partialBlock = math.min(bitsInQueue.toLong, 256).toInt | |
| Array.copy(dataQueue, (1088 - bitsInQueue) / 8, output, i.toInt / 8, partialBlock / 8) | |
| bitsInQueue -= partialBlock | |
| i += partialBlock | |
| end while | |
| output | |
| end hash | |
| private def KeccakAbsorb(data: Array[Byte], off: Int): Unit = | |
| val count = 17 | |
| var offf = off | |
| var i = 0 | |
| while i < count do | |
| state(i) ^= littleEndianToLong(data, offf) | |
| offf += 8 | |
| i += 1 | |
| end KeccakAbsorb | |
| private def KeccakPermutation(): Unit = | |
| val KeccakRoundConstants: Array[Long] = Array( | |
| 0x0000000000000001L, 0x0000000000008082L, 0x800000000000808aL, 0x8000000080008000L, 0x000000000000808bL, | |
| 0x0000000080000001L, 0x8000000080008081L, 0x8000000000008009L, 0x000000000000008aL, 0x0000000000000088L, | |
| 0x0000000080008009L, 0x000000008000000aL, 0x000000008000808bL, 0x800000000000008bL, 0x8000000000008089L, | |
| 0x8000000000008003L, 0x8000000000008002L, 0x8000000000000080L, 0x000000000000800aL, 0x800000008000000aL, | |
| 0x8000000080008081L, 0x8000000000008080L, 0x0000000080000001L, 0x8000000080008008L, | |
| ) | |
| var a00 = state(0) | |
| var a01 = state(1) | |
| var a02 = state(2) | |
| var a03 = state(3) | |
| var a04 = state(4) | |
| var a05 = state(5) | |
| var a06 = state(6) | |
| var a07 = state(7) | |
| var a08 = state(8) | |
| var a09 = state(9) | |
| var a10 = state(10) | |
| var a11 = state(11) | |
| var a12 = state(12) | |
| var a13 = state(13) | |
| var a14 = state(14) | |
| var a15 = state(15) | |
| var a16 = state(16) | |
| var a17 = state(17) | |
| var a18 = state(18) | |
| var a19 = state(19) | |
| var a20 = state(20) | |
| var a21 = state(21) | |
| var a22 = state(22) | |
| var a23 = state(23) | |
| var a24 = state(24) | |
| var i = 0 | |
| while i < 24 do | |
| var c0 = a00 ^ a05 ^ a10 ^ a15 ^ a20 | |
| var c1 = a01 ^ a06 ^ a11 ^ a16 ^ a21 | |
| val c2 = a02 ^ a07 ^ a12 ^ a17 ^ a22 | |
| val c3 = a03 ^ a08 ^ a13 ^ a18 ^ a23 | |
| val c4 = a04 ^ a09 ^ a14 ^ a19 ^ a24 | |
| val d1 = (c1 << 1 | c1 >>> -1) ^ c4 | |
| val d2 = (c2 << 1 | c2 >>> -1) ^ c0 | |
| val d3 = (c3 << 1 | c3 >>> -1) ^ c1 | |
| val d4 = (c4 << 1 | c4 >>> -1) ^ c2 | |
| val d0 = (c0 << 1 | c0 >>> -1) ^ c3 | |
| a00 ^= d1 | |
| a05 ^= d1 | |
| a10 ^= d1 | |
| a15 ^= d1 | |
| a20 ^= d1 | |
| a01 ^= d2 | |
| a06 ^= d2 | |
| a11 ^= d2 | |
| a16 ^= d2 | |
| a21 ^= d2 | |
| a02 ^= d3 | |
| a07 ^= d3 | |
| a12 ^= d3 | |
| a17 ^= d3 | |
| a22 ^= d3 | |
| a03 ^= d4 | |
| a08 ^= d4 | |
| a13 ^= d4 | |
| a18 ^= d4 | |
| a23 ^= d4 | |
| a04 ^= d0 | |
| a09 ^= d0 | |
| a14 ^= d0 | |
| a19 ^= d0 | |
| a24 ^= d0 | |
| c1 = a01 << 1 | a01 >>> 63 | |
| a01 = a06 << 44 | a06 >>> 20 | |
| a06 = a09 << 20 | a09 >>> 44 | |
| a09 = a22 << 61 | a22 >>> 3 | |
| a22 = a14 << 39 | a14 >>> 25 | |
| a14 = a20 << 18 | a20 >>> 46 | |
| a20 = a02 << 62 | a02 >>> 2 | |
| a02 = a12 << 43 | a12 >>> 21 | |
| a12 = a13 << 25 | a13 >>> 39 | |
| a13 = a19 << 8 | a19 >>> 56 | |
| a19 = a23 << 56 | a23 >>> 8 | |
| a23 = a15 << 41 | a15 >>> 23 | |
| a15 = a04 << 27 | a04 >>> 37 | |
| a04 = a24 << 14 | a24 >>> 50 | |
| a24 = a21 << 2 | a21 >>> 62 | |
| a21 = a08 << 55 | a08 >>> 9 | |
| a08 = a16 << 45 | a16 >>> 19 | |
| a16 = a05 << 36 | a05 >>> 28 | |
| a05 = a03 << 28 | a03 >>> 36 | |
| a03 = a18 << 21 | a18 >>> 43 | |
| a18 = a17 << 15 | a17 >>> 49 | |
| a17 = a11 << 10 | a11 >>> 54 | |
| a11 = a07 << 6 | a07 >>> 58 | |
| a07 = a10 << 3 | a10 >>> 61 | |
| a10 = c1 | |
| c0 = a00 ^ ~a01 & a02 | |
| c1 = a01 ^ ~a02 & a03 | |
| a02 ^= ~a03 & a04 | |
| a03 ^= ~a04 & a00 | |
| a04 ^= ~a00 & a01 | |
| a00 = c0 | |
| a01 = c1 | |
| c0 = a05 ^ ~a06 & a07 | |
| c1 = a06 ^ ~a07 & a08 | |
| a07 ^= ~a08 & a09 | |
| a08 ^= ~a09 & a05 | |
| a09 ^= ~a05 & a06 | |
| a05 = c0 | |
| a06 = c1 | |
| c0 = a10 ^ ~a11 & a12 | |
| c1 = a11 ^ ~a12 & a13 | |
| a12 ^= ~a13 & a14 | |
| a13 ^= ~a14 & a10 | |
| a14 ^= ~a10 & a11 | |
| a10 = c0 | |
| a11 = c1 | |
| c0 = a15 ^ ~a16 & a17 | |
| c1 = a16 ^ ~a17 & a18 | |
| a17 ^= ~a18 & a19 | |
| a18 ^= ~a19 & a15 | |
| a19 ^= ~a15 & a16 | |
| a15 = c0 | |
| a16 = c1 | |
| c0 = a20 ^ ~a21 & a22 | |
| c1 = a21 ^ ~a22 & a23 | |
| a22 ^= ~a23 & a24 | |
| a23 ^= ~a24 & a20 | |
| a24 ^= ~a20 & a21 | |
| a20 = c0 | |
| a21 = c1 | |
| a00 ^= KeccakRoundConstants(i) | |
| i = i + 1 | |
| end while | |
| state(0) = a00 | |
| state(1) = a01 | |
| state(2) = a02 | |
| state(3) = a03 | |
| state(4) = a04 | |
| state(5) = a05 | |
| state(6) = a06 | |
| state(7) = a07 | |
| state(8) = a08 | |
| state(9) = a09 | |
| state(10) = a10 | |
| state(11) = a11 | |
| state(12) = a12 | |
| state(13) = a13 | |
| state(14) = a14 | |
| state(15) = a15 | |
| state(16) = a16 | |
| state(17) = a17 | |
| state(18) = a18 | |
| state(19) = a19 | |
| state(20) = a20 | |
| state(21) = a21 | |
| state(22) = a22 | |
| state(23) = a23 | |
| state(24) = a24 | |
| end KeccakPermutation | |
| private def KeccakExtract(): Unit = | |
| KeccakPermutation() | |
| longToLittleEndian(state, 0, 17, dataQueue, 0) | |
| bitsInQueue = 1088 | |
| private def littleEndianToInt(bs: Array[Byte], off: Int): Int = | |
| var o = off | |
| var n = bs(o) & 0xff | |
| n |= (bs { o += 1; o } & 0xff) << 8 | |
| n |= (bs { o += 1; o } & 0xff) << 16 | |
| n |= bs { o += 1; o } << 24 | |
| n | |
| end littleEndianToInt | |
| private def intToLittleEndian(n: Int, bs: Array[Byte], off: Int): Unit = | |
| var o = off | |
| bs(o) = n.toByte | |
| bs { o += 1; o } = (n >>> 8).toByte | |
| bs { o += 1; o } = (n >>> 16).toByte | |
| bs { o += 1; o } = (n >>> 24).toByte | |
| end intToLittleEndian | |
| private def littleEndianToLong(bs: Array[Byte], off: Int): Long = | |
| val lo = littleEndianToInt(bs, off) & 0xffffffffL | |
| val hi = littleEndianToInt(bs, off + 4) & 0xffffffffL | |
| hi << 32 | lo | |
| private def longToLittleEndian(n: Long, bs: Array[Byte], off: Int): Unit = | |
| intToLittleEndian((n & 0xffffffffL).toInt, bs, off) | |
| intToLittleEndian((n >>> 32).toInt, bs, off + 4) | |
| private def longToLittleEndian(ns: Array[Long], nsOff: Int, nsLen: Int, bs: Array[Byte], bsOff: Int): Unit = | |
| var o = bsOff | |
| var i = 0 | |
| while i < nsLen do | |
| longToLittleEndian(ns(nsOff + i), bs, o); o += 8; i += 1 | |
| end longToLittleEndian | |
| end Keccak256 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment