Skip to main content

2026-09-16

9 min

By Formatho Editorial

Keccak-256 vs SHA-256: When to Use Which (With Examples)

CryptographyEthereumSecurityWeb3Hashing

Two hashes, one family tree, one byte of difference

Keccak-256 and SHA-256 answer the same question — give me a fixed-size fingerprint of this data — and both are cryptographically secure. Yet they are not interchangeable, and confusing them is one of the most common bugs in Web3 engineering. Sign a message with the wrong one and your signature verification fails silently. Build a Merkle proof with SHA-256 when the contract expects Keccak-256 and every proof reverts.

The short version: SHA-256 is the general-purpose integrity hash of the internet — TLS certificates, Git objects, Bitcoin, file checksums. Keccak-256 is the hash of the Ethereum Virtual Machine — function selectors, address derivation, typed-data signatures, on-chain Merkle trees. And SHA3-256 is a third thing that is frequently mistaken for Keccak-256.

The gotcha that costs hours: Keccak-256 ≠ SHA3-256

The Keccak team won NIST's SHA-3 competition in 2012. When NIST standardized the design in 2015 (FIPS 202), it changed the padding byte from 0x01 to 0x06 — a deliberate domain separation tweak. The result: the same input produces different digests under original Keccak and under standardized SHA3-256.

Ethereum launched in 2015 with the pre-standard Keccak-256 already baked into the protocol — and never switched. So when an Ethereum tool says "keccak256", it means the original padding (0x01), and when your system library says "SHA3-256" (Node's crypto, Python's hashlib.sha3_256, Go's golang.org/x/crypto/sha3), it means the standardized padding (0x06). Same sponge construction, same capacity, incompatible outputs.

# same input, three different answers
keccak256("transfer(address,uint256)")
  → 0xa9059cbb2ab09eb219583f4a59a5d0623ade34bc954990bef74b0f6c6dece5be   (Ethereum selector)

sha3_256("transfer(address,uint256)")
  → 0x5c1c3be2...  (standardized SHA3 — NOT what the EVM uses)

sha256("transfer(address,uint256)")
  → 0x6a4ffbd9...  (SHA-2 family, entirely different design)

The first four bytes of the Keccak digest — a9059cbb — are the function selector every EVM wallet and contract recognizes for ERC-20 transfer. Compute that selector with SHA3-256 and you get a selector for a function that does not exist.

When to use Keccak-256

Reach for Keccak-256 whenever the EVM is the verifier:

  • Function selectors. The first 4 bytes of keccak256(signature) route every contract call. Computing selectors, encoding calldata, or decoding transactions all need Keccak — the exact algorithm, original padding.
  • Address derivation. An Ethereum address is the last 20 bytes of keccak256(uncompressed_public_key). Not the first 20, not SHA-256 — the mismatched variants produce valid-looking addresses that own nothing.
  • Solidity hashing. The keccak256() builtin is the only hash most contracts use for commitments, digests, and comparisons, because it is cheap and native.
  • EIP-712 typed data. The signature scheme every modern wallet uses for human-readable signing hashes the typed structure with Keccak-256 at every level.
  • On-chain Merkle proofs. Airdrops and whitelist trees are built with Keccak because the contract verifier is Solidity's keccak256. Your off-chain tree builder must match, including the double-hash of leaves and the abi.encodePacked concatenation quirks.
  • CREATE2 addresses. The deterministic address formula keccaks the deployer, salt, and init code together.
// JavaScript — ethers v6
import { keccak256, toUtf8Bytes } from 'ethers'
keccak256(toUtf8Bytes('transfer(address,uint256)'))
// '0xa9059cbb2ab09eb219583f4a59a5d0623ade34bc954990bef74b0f6c6dece5be'

// Solidity — the same hash on-chain
bytes32 digest = keccak256(abi.encodePacked("transfer(address,uint256)"));

When to use SHA-256

Reach for SHA-256 everywhere the ecosystem predates or ignores the EVM:

  • Integrity and checksums. File verification, software signatures, sha256sum — SHA-2 is the default everywhere precisely because it predates SHA-3 and has decades of unbroken deployment.
  • Bitcoin. Double SHA-256 (SHA256(SHA256(x))) secures block headers and addresses in that order — mixing in Keccak breaks at the first verification.
  • TLS and certificates. Signature algorithms in the WebPKI use SHA-256 (or SHA-384) with RSA/ECDSA.
  • HMAC. HMAC-SHA256 is the workhorse of API request signing and JWTs (HS256). HMAC's construction already neutralizes length-extension, which is the main theoretical weakness of raw SHA-256 — you get a keyed MAC with no caveats.
  • Subresource Integrity and content addressing. SRI hashes in CSP headers, Docker digests, and IPFS multihashes use SHA-2 family hashes.
// Node — SHA-256 and HMAC, no extra dependencies
import { createHash, createHmac } from 'node:crypto'

createHash('sha256').update('payload').digest('hex')

createHmac('sha256', secret)
  .update('POST /api/transfer
' + timestamp + '.' + body)
  .digest('base64url')   // the API-signature pattern

Are they equally secure?

Yes, in every way that matters today. Both offer 128-bit collision resistance at 256-bit output; neither has a practical collision or preimage attack. Keccak's sponge construction has a wider security margin in some theoretical senses; SHA-256 has more cryptanalytic mileage. The choice is compatibility, not strength — pick the hash your verifier expects, and if you are the verifier, pick the one your ecosystem's tooling speaks natively.

One real caveat they share: both are unkeyed hashes, so both are length-extension vulnerable when used as hash(secret ‖ message). Never authenticate that way — use HMAC with either hash.

The cross-language interop table

LanguageKeccak-256 (Ethereum)SHA-256
JavaScriptethers.keccak256(), js-sha3 keccak256crypto.createHash('sha256'), Web Crypto
Pythoneth_hash, SafeKeccak, sha3.keccak_256 (not hashlib.sha3_256!)hashlib.sha256()
Gogolang.org/x/crypto/sha3 with NewLegacyKeccak256()crypto/sha256
Rusttiny-keccak keccak256sha2::Sha256
Soliditykeccak256() builtinprecompile 0x02

Python's pysha3 is the classic trap: it exposed both keccak_256 and sha3_256, was deprecated, and replacements shuffle the names again. If a digest starts looking right but a contract still rejects it, print the first byte of the domain separator digest — a mismatched padding shows up immediately.

Decision table

You are…Use
Computing a function selector or calldataKeccak-256
Deriving an address from a public keyKeccak-256
Building Merkle proofs verified by a Solidity contractKeccak-256 (match the contract's exact encodePacked layout)
Signing EIP-712 typed dataKeccak-256
Verifying file downloads, writing SRI, content addressingSHA-256
Anything BitcoinDouble SHA-256
Signing API requests or JWTsHMAC-SHA256
General-purpose integrity outside Web3SHA-256

Try both, right now, in your browser

The fastest way to internalize the difference is to hash the same string with each. Our Keccak-256 hasher and SHA-256 hash tool run side by side — paste transfer(address,uint256) into both and watch the digests diverge from the first byte. Then take the Keccak digest's first four bytes to the function selector calculator — a9059cbb, the selector you have seen in every Etherscan transaction. Everything runs client-side, so test signatures and candidate digests never leave your machine.

Formatho Editorial — written and maintained by the team behind formatho.com, a library of free, privacy-first developer tools that run entirely in your browser. Every guide is tested against the tools it describes. Corrections and suggestions: github.com/formatho.