Skip to content

Instantly share code, notes, and snippets.

@igor-ramazanov
Last active October 5, 2025 23:01
Show Gist options
  • Select an option

  • Save igor-ramazanov/f630b9474ec895e8f3289ddee502bc24 to your computer and use it in GitHub Desktop.

Select an option

Save igor-ramazanov/f630b9474ec895e8f3289ddee502bc24 to your computer and use it in GitHub Desktop.
Pure Scala Keccak-256 hashing function implementation
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