Keccak256 From Scratch in 200 Lines of Kotlin (Because Web3j Was 8 MB)

Web3j is 8 MB. My APK is 4.5 MB. So I wrote the Ethereum hash function from scratch.

Why Not Just Import Web3j?

I was building a native Ethereum wallet inside an Android Capacitor app. The goal: sign transactions without MetaMask, without WalletConnect, without any external app.

Web3j handles everything — key generation, signing, ABI encoding, RLP. But it pulls in 8+ MB of dependencies. My entire APK is 4.5 MB.

So I implemented Keccak256 directly. 200 lines of Kotlin. Zero dependencies beyond what Android already ships.

What Is Keccak256?

Ethereum uses Keccak-256, a variant of SHA-3. It’s a sponge construction — data is absorbed in 136-byte blocks, then squeezed out as a 32-byte hash.

The core is Keccak-f[1600]: 24 rounds of five transformation steps on a 5×5 matrix of 64-bit words.

The Round Constants

Each round XORs a different constant into the first lane. These overflow Long.MAX_VALUE in Kotlin, so they must be expressed as signed two’s complement:

private val RC = longArrayOf(
    0x0000000000000001L,
    0x0000000000008082L,
    -0x7fffffff7fff7f76L, // 0x800000000000808A
    -0x7fffffff80008000L, // 0x8000000080008000
    // ... 20 more
)

The Five Steps

Theta: XOR parity into each lane.
Rho+Pi: Rotate and permute in one pass.
Chi: Non-linear row mixing.
Iota: XOR round constant.

All implemented with primitive LongArray operations — no objects, no allocations inside the round loop.

Results

Approach Size Impact Dependencies
Web3j +8 MB Full Web3j stack
ethers.js via WebView +800 KB JS bridge
Pure Kotlin +18 KB Zero

The entire crypto layer — Keccak256, RLP encoder, ECDSA wrapper — fits in under 50 KB. No Gradle dependency beyond BouncyCastle, which Android ships natively.

Sometimes the best library is the one you don’t import.

📂 Source: AtlasNexusTech/ai2work-android