Keccak vs SHA3: Why Results Differ and How to Verify Encoding and Hashes

 / 
2

Keccak-256 and SHA3-256 give different results for one simple reason: the padding bytes are not the same. Keccak uses 0x01, while the NIST-standardized SHA3 changed it to 0x06. With the same input, the different padding at the end completely changes the hash value. Ethereum uses the original Keccak-256, not SHA3-256. If you call sha3-256 in your code to compute an Ethereum function selector or address, the result will not match, and most libraries will not warn you.

OKX Exchange
A leading global cryptocurrency platform,suitable for both beginners and experienced traders.
New user benefit: 20% off trading fees upon registration!!

Why There Are Two Versions

Keccak was the winning algorithm of the NIST SHA-3 competition in 2012. However, when NIST officially published the FIPS 202 standard in 2015, it modified the padding scheme, changing the original 0x01 from the Keccak submission to 0x06. The change itself is small, but the output is completely different. Ethereum adopted Keccak-256 before NIST standardization, so it has kept using the original version. Monero is in the same situation: it launched in 2014, before the NIST standard was released.

A common source of confusion is the claim that "Keccak-256 was standardized as SHA-3." The Chinese entry in ETHGlossary says something similar. Strictly speaking, the Keccak algorithm family was standardized, but the standard version changed the padding. So what is called SHA3-256 is not the same thing as the Keccak-256 used by Ethereum. Do not let this kind of simplified wording mislead you when writing code.

How to Verify: Check Against Known Vectors

The most direct way to verify is to compare against a known input-output pair. The Ethereum function selector is a good test point: the first 4 bytes of the Keccak-256 hash of transfer(address,uint256) should be 0xa9059cbb. If a library gives you something different, you are not using Keccak-256.

Another useful check is the hash of an empty string. Keccak-256 on empty input produces c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470. SHA3-256 on empty input produces a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a. The two are different, so you can use this to quickly determine which algorithm a tool or library is actually returning.

If you need a quick verification tool in the browser, some online tools explicitly state that they support the Keccak-256 variant and that the output matches Solidity's keccak256(). When using one, check whether it lists Keccak-256 and SHA3-256 separately. If it only says "SHA3" without "Keccak," it will most likely not give you the result you need.

Encoding: The Pitfall Before Hashing

The word "encoding" in the title points to a problem that has nothing to do with the hash itself but can still cause mismatched results. A hash function processes byte sequences, not strings. Whatever you pass to the hash function in your code must have a clearly defined encoding first.

The most common mistake is confusing hex strings with raw bytes. Suppose you need to hash an address like 0xAbC.... If you feed the UTF-8 bytes of the string "0xAbC..." directly into Keccak, the result will be completely different from first parsing 0xAbC... into 20 bytes and then hashing it. Solidity's keccak256(address) does the latter. In JavaScript, when using ethers.keccak256 or viem's keccak256, you pass them a BytesLike value, which can be a hex string, but the library will decode the hex first and then hash it. If you manually convert the string to a byte array and pass it to a low-level Keccak implementation, it is easy to forget the decoding step.

The same applies to text encoding. Hashing a UTF-8 string and hashing a UTF-16 string produces different byte sequences and therefore different hashes. When verifying across languages or platforms, first make sure both sides use the same byte representation. Hex output should also use consistent capitalization and decide whether to include the 0x prefix. Otherwise, you may wrongly conclude that the hashes do not match because of formatting differences.

What You Should Do

If you write code in the Ethereum ecosystem, never use the sha3-256 built into your language's standard library as a substitute for Keccak-256. Node.js's crypto.createHash('sha3-256') is the NIST version. Using it here will silently produce wrong function selectors, event topics, and addresses. Use ethers, viem, web3.js, or an implementation that clearly says Keccak.

If you are verifying whether a hash is correct, first confirm the algorithm name, then confirm the encoding. If either one is wrong, the result will not match. Use the transfer(address,uint256) selector or the empty string hash for a quick check, and you will rule out most problems.

If you are just curious about the difference between the two, remember one thing: for the same input, Keccak-256 and SHA3-256 produce different 32-byte results because the padding bytes at the end are different. Ethereum chose the former only for historical timing reasons, not because one is technically better than the other.

OKX Exchange
A leading global cryptocurrency platform,suitable for both beginners and experienced traders.
New user benefit: 20% off trading fees upon registration!!

References

  1. Dart packages·dvKeccak256 function, no update date listed; accessed: 2026-10-21.
  2. Dart packages·Keccak256 class, no update date listed; accessed: 2026-10-21.
  3. GitHub·Keccak-256 Hash Function (Monero docs), no update date listed; accessed: 2026-10-21.
  4. Zerethon·Keccak-256 Hash Generator, published or updated: 2026-05-14; accessed: 2026-10-21.
  5. ETHGlossary·keccak-256 Chinese entry, no update date listed; accessed: 2026-10-21.
  6. Zerethon Tools·Hash Generator — All Algorithms Supported, published or updated: 2026-05-16; accessed: 2026-10-21.
  7. GitHub·nodejs-keccak256 SKILL.md, published or updated: 2026-06-20; accessed: 2026-10-21.