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 calculatora9059cbb, 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.