Skip to main content
info

Please note that zkApp programmability is not yet available on Mina Mainnet, but zkApps can now be deployed to Berkeley Testnet.

Keccak (SHA3)

What is Keccak?

Keccak was developed by a team of cryptographers from Belgium and it was one of the submissions for the National Institute of Standards and Technology (NIST) SHA-3 competition. Keccak was eventually chosen as the winner of the competition and standardized as SHA-3. Today, Keccak is used in many different applications, including Ethereum, and many others.

Specifically, Keccak is used in Ethereum to hash addresses, transactions, and blocks. It is also used to hash the state trie, which is a data structure that stores the state of the Ethereum blockchain. Because of the common usage of Keccak in Ethereum, it has become a key component of o1js and enables developers to verify Ethereum transactions and blocks in o1js.

Keccak and Poseidon

Developers familar with o1js are likely familar with Poseidon. It is important to briefly explain the differences between Poseidon, a zero-knowledge native hash function, and Keccak, a hash function that requires binary arethmetic. More specifically, Poseidon operates over the native Pallas base field and uses parameters generated specifically for Mina. Because of that, Poseidon is by far the most efficient hash function currently available in o1js.

Keccak, on the other hand, is a hash function that operates over binary data and is not native to most zero-knowledge proofs. Because of that, Keccak is not as efficient as Poseidon, but it is still very useful for verifying Ethereum transactions and blocks. So, when choosing what hash function to use, it is important to consider the use case and the data that needs to be hashed.

Basic usage

This section briefly explains how to use Keccak in o1js.

Keccak is available in different configurations, which are available under the Hash namespace. The following configurations are available:

  • Hash.SHA3_256: NIST SHA3 hash function with output size of 256 bits.
  • Hash.SHA3_385: NIST SHA3 hash function with output size of 384 bits.
  • Hash.SHA3_512: NIST SHA3 hash function with output size of 512 bits.
  • Hash.Keccak256: pre-NIST Keccak hash function with output size of 256 bits. This is the configuration used to hash blocks, transactions and more in Ethereum.
  • Hash.Keccak384: pre-NIST Keccak hash function with output size of 384 bits.
  • Hash.Keccak512: pre-NIST Keccak hash function with output size of 512 bits.

As previously mentioned, Keccak operates over binary data instead of native Field elements like Poseidon. For that reason, we introduced a new type to o1js - Bytes. Bytes is a fixed-length array of bytes that can be used to represent binary data. Under the hood, Bytes is represented as an array of UInt8 elements.

In order to use Bytes, you need to extend the Bytes class and specify the length of bytes.

// This creates a Bytes class that represents 16 bytes
class Bytes16 extends Bytes(16) {}

To initiate your Bytes16 class with a value, you can use from, fromHex or fromString.

// `.from` accepts an  array of `number`, `bigint`, `UInt8` elements or a `Uint8Array` or `Bytes`
let bytes = Bytes16.from(new Array(16).fill(0));

// converts a hex string to bytes
bytes = Bytes16.fromHex('646f67');

// converts a string to bytes
bytes = Bytes16.fromString('dog');

// print the contents of `bytes` to the console
bytes.bytes.forEach((b) => console.log(b.toBigInt()));
// [100n, 111n, 103n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n]

Note: If the input array is smaller than the length of the Bytes class, it will be padded with zeros.

You can also convert a Bytes object back to a hex string using toHex.

class Bytes3 extends Bytes(3) {}

let bytes = Bytes3.fromHex('646f67');
let hex = bytes.toHex();
console.log('hex', hex);
// 646f67

Now that we know how to use Bytes as input, we can use it to hash data using Keccak.

// define a preimage
let preimage = 'The quick brown fox jumps over the lazy dog';

// create a Bytes class that represents 43 bytes
class Bytes43 extends Bytes(43) {}

// convert the preimage to bytes
let preimageBytes = Bytes43.fromString(preimage);

// hash the preimage
let hash = Hash.SHA3_256.hash(preimageBytes);

console.log(hash.toHex());
//69070dda01975c8c120c3aada1b282394e7f032fa9cf32f4cb2259a0897dfc04

Bytes - API reference

// creates a Bytes class that represents n bytes (n is 32 in this example)
let n = 32;
class BytesN extends Bytes(n) {}

// initiate an instance from a hex string
let bytes = BytesN.fromHex('646f67');

// initiate an instance from a string
bytes = BytesN.fromString('dog');

// initiate an instance from an array of numbers
bytes = BytesN.from([100, 111, 103]);

// initiate an instance from an array of bigints
bytes = BytesN.from([100n, 111n, 103n]);

// initiate an instance from an array of UInt8 elements
bytes = BytesN.from([UInt8(100), UInt8(111), UInt8(103)]);

// initiate an instance from a Uint8Array
bytes = BytesN.from(new Uint8Array([100, 111, 103]));

// initiate an instance from another Bytes instance
bytes = BytesN.from(BytesN.fromHex('646f67'));

// convert the bytes to a hex string
const hex = bytes.toHex();

Keccak - API reference

// https://keccak.team/keccak.html hash function, pre-NIST specification
// hash bytes using Keccak256 with output size of 256 bits, mainly used in Ethereum
Hash.Keccak256.hash(bytes);

// hash bytes using Keccak384 with output size of 384 bits
Hash.Keccak384.hash(bytes);

// hash bytes using Keccak512 with output size of 512 bits
Keccak.Keccak512.hash(bytes);

// https://csrc.nist.gov/pubs/fips/202/final hash function, official NIST specification
// hash bytes using SHA3_256 with output size of 256 bits
Hash.SHA3_256.hash(bytes);

// hash bytes using SHA3_384 with output size of 384 bits
Hash.SHA3_384.hash(bytes);

// hash bytes using SHA3_512 with output size of 512 bits
Hash.SHA3_512.hash(bytes);