DEV Community

anabdellali
anabdellali

Posted on Originally published at sha3.si

SHA3-256 vs Keccak-256: the one-byte difference that breaks Ethereum hashes

If you've ever hashed something in Python with hashlib.sha3_256 and got a different result from Solidity's keccak256, nothing is wrong with your code. You're using two different functions that share almost everything.

Same engine, different padding

Keccak won NIST's hash competition in 2012. When NIST published the final standard, FIPS 202, in 2015, it changed the padding: SHA-3 appends the bits 01 before the usual 10*1 padding (so the first padding byte is 0x06), while the original Keccak used 0x01. Ethereum had already adopted Keccak, so it kept the original.

One byte, completely different outputs:

Input Keccak-256 (Ethereum) SHA3-256 (NIST)
"" c5d24601...5d85a470 a7ffc6f8...80f8434a
"abc" 4e03657a...a12d6c45 3a985da7...11431532

Quick check: if your empty-string hash starts with c5d24601, you have Keccak-256. If it starts with a7ffc6f8, you have SHA3-256.

Getting the one you need

import hashlib
hashlib.sha3_256(b"abc").hexdigest()      # NIST SHA3-256

from Crypto.Hash import keccak            # pip install pycryptodome
k = keccak.new(digest_bits=256); k.update(b"abc"); k.hexdigest()   # Ethereum
Enter fullscreen mode Exit fullscreen mode
import { createHash } from "node:crypto";
createHash("sha3-256").update("abc").digest("hex");   // NIST SHA3-256

import { keccak256, toUtf8Bytes } from "ethers";
keccak256(toUtf8Bytes("abc"));                         // Ethereum
Enter fullscreen mode Exit fullscreen mode

Watch out for library names: Solidity's old sha3() was an alias of keccak256(), and some packages still say "sha3" when they mean Keccak.

Try it

I put every variant side by side in a free in-browser tool: sha3.si. Type transfer(address,uint256) and the first 8 hex characters of Keccak-256 give you the ERC-20 selector, a9059cbb. From the terminal: npx sha3-check -s "transfer(address,uint256)" -a keccak-256.

Top comments (0)