Documentation Index
Fetch the complete documentation index at: https://mintlify.com/noir-lang/noir/llms.txt
Use this file to discover all available pages before exploring further.
The Noir standard library ships a set of cryptographic primitives that are compiled down to optimised backend constraints. Many of these functions are black box functions — the backend implements them natively rather than expressing them as plain Noir arithmetic.
Some functions are only available when using the BN254 scalar field (i.e., the Barretenberg backend). These are noted where relevant.
Hashes
Many hash functions have been extracted into standalone libraries published in the Hashes section of awesome-noir. The functions that remain in the standard library are those that are implemented as black box constraints or are fundamental to internal stdlib operations.
sha256_compression
Performs a single SHA-256 compression round on a 16-word input block with an 8-word initial state. This is a lower-level primitive, not a full SHA-256 hash.
This is not the same as computing a SHA-256 digest. For full SHA-256 hashing use the sha256 library.
pub fn sha256_compression(input: [u32; 16], state: [u32; 8]) -> [u32; 8]
| Parameter | Type | Description |
|---|
input | [u32; 16] | 16-word message block |
state | [u32; 8] | 8-word initial hash state |
Returns [u32; 8] — the resulting compression state.
fn main() {
let block: [u32; 16] = [0; 16];
let initial_state: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
];
let next_state = std::hash::sha256_compression(block, initial_state);
}
blake2s
Hashes an arbitrary byte array using the Blake2s algorithm, returning a 32-byte digest.
pub fn blake2s<let N: u32>(input: [u8; N]) -> [u8; 32]
| Parameter | Type | Description |
|---|
input | [u8; N] | Input bytes of any compile-time-known length |
Returns [u8; 32] — the Blake2s digest.
fn main() {
let x = [163, 117, 178, 149]; // arbitrary bytes
let hash = std::hash::blake2s(x);
}
blake3
Hashes an arbitrary byte array using the Blake3 algorithm, returning a 32-byte digest.
When running under Barretenberg, the input is limited to 1024 bytes.
pub fn blake3<let N: u32>(input: [u8; N]) -> [u8; 32]
| Parameter | Type | Description |
|---|
input | [u8; N] | Input bytes (≤ 1024 bytes under Barretenberg) |
Returns [u8; 32] — the Blake3 digest.
fn main() {
let x = [163, 117, 178, 149];
let hash = std::hash::blake3(x);
}
pedersen_hash
Hashes an array of Field elements using Pedersen, returning a single Field.
pub fn pedersen_hash<let N: u32>(input: [Field; N]) -> Field
| Parameter | Type | Description |
|---|
input | [Field; N] | Field elements to hash |
Returns Field — the Pedersen hash.
fn main(x: [Field; 2]) -> pub Field {
std::hash::pedersen_hash(x)
}
A separator variant is also available:
pub fn pedersen_hash_with_separator<let N: u32>(input: [Field; N], separator: u32) -> Field
pedersen_commitment
Produces a Pedersen commitment as an EmbeddedCurvePoint rather than collapsing to a single field element.
pub fn pedersen_commitment<let N: u32>(input: [Field; N]) -> EmbeddedCurvePoint
| Parameter | Type | Description |
|---|
input | [Field; N] | Field elements to commit to |
Returns EmbeddedCurvePoint with .x, .y, and .is_infinite fields.
fn main(x: [Field; 2]) {
let commit = std::hash::pedersen_commitment(x);
println(f"x: {commit.x}, y: {commit.y}");
}
keccakf1600
Applies a single Keccak-f[1600] permutation to a 25-word state (each word is 64 bits).
pub fn keccakf1600(input: [u64; 25]) -> [u64; 25]
| Parameter | Type | Description |
|---|
input | [u64; 25] | 25-word Keccak sponge state |
Returns [u64; 25] — the permuted state.
fn main() {
let state: [u64; 25] = [0; 25];
let next_state = std::hash::keccakf1600(state);
}
Use this primitive to build full Keccak-256 or SHA-3 by implementing the sponge construction on top.
poseidon2_permutation
Applies the Poseidon2 permutation to a field array. The required state size depends on backend configuration.
pub fn poseidon2_permutation<let N: u32>(input: [Field; N], state_len: u32) -> [Field; N]
| Parameter | Type | Description |
|---|
input | [Field; N] | Sponge state (length must equal the backend’s POSEIDON2_CONFIG_STATE_SIZE) |
state_len | u32 | Must equal input.len() |
Returns [Field; N] — the permuted state.
For most use cases, prefer the Poseidon2Hasher from std::hash::poseidon2, which wraps the sponge construction:
use std::hash::poseidon2::Poseidon2Hasher;
use std::hash::{Hasher, BuildHasherDefault};
fn hash_fields(inputs: [Field; 3]) -> Field {
let mut hasher = Poseidon2Hasher::default();
for input in inputs {
hasher.write(input);
}
hasher.finish()
}
ECDSA signature verification
Noir supports ECDSA signature verification over two curves. Both functions take the public key as separate x and y coordinate byte arrays, the 64-byte signature (r, s), and a 32-byte message hash.
s must be normalised (i.e., s ≤ order / 2) to prevent signature malleability. See BIP 0062 for context.
secp256k1
// std::ecdsa_secp256k1
pub fn verify_signature(
public_key_x: [u8; 32],
public_key_y: [u8; 32],
signature: [u8; 64],
message_hash: [u8; 32],
) -> bool
| Parameter | Type | Description |
|---|
public_key_x | [u8; 32] | X coordinate of the public key |
public_key_y | [u8; 32] | Y coordinate of the public key |
signature | [u8; 64] | 64-byte (r, s) signature, big-endian |
message_hash | [u8; 32] | 32-byte hash of the signed message |
Returns bool — true if the signature is valid.fn main(
hashed_message: [u8; 32],
pub_key_x: [u8; 32],
pub_key_y: [u8; 32],
signature: [u8; 64],
) {
let valid = std::ecdsa_secp256k1::verify_signature(
pub_key_x,
pub_key_y,
signature,
hashed_message,
);
assert(valid);
}
secp256r1
// std::ecdsa_secp256r1
pub fn verify_signature(
public_key_x: [u8; 32],
public_key_y: [u8; 32],
signature: [u8; 64],
message_hash: [u8; 32],
) -> bool
The signature has the same shape as the secp256k1 variant; only the underlying curve order differs.
fn main(
hashed_message: [u8; 32],
pub_key_x: [u8; 32],
pub_key_y: [u8; 32],
signature: [u8; 64],
) {
let valid = std::ecdsa_secp256r1::verify_signature(
pub_key_x,
pub_key_y,
signature,
hashed_message,
);
assert(valid);
}
Embedded curve operations (Grumpkin)
The embedded curve is the curve whose base field equals the scalar field of the host curve used by the proof system. For BN254 (Barretenberg), this is the Grumpkin curve (y² = x³ - 17). For BLS12-381 it is Bandersnatch.
Two types describe points and scalars on this curve:
pub struct EmbeddedCurvePoint {
pub x: Field,
pub y: Field,
pub is_infinite: bool,
}
pub struct EmbeddedCurveScalar {
pub lo: Field, // low 128-bit limb
pub hi: Field, // high 128-bit limb
}
Scalars on the embedded curve may not fit in a single Field element, which is why they are split into lo and hi 128-bit limbs.
multi_scalar_mul
Computes a multi-scalar multiplication (MSM): given N point–scalar pairs, returns the sum Σ scalars[i] * points[i].
pub fn multi_scalar_mul<let N: u32>(
points: [EmbeddedCurvePoint; N],
scalars: [EmbeddedCurveScalar; N],
) -> EmbeddedCurvePoint
Prefer multi_scalar_mul over repeated calls to embedded_curve_add when adding more than two points. MSM is significantly more constraint-efficient.
use std::embedded_curve_ops::{EmbeddedCurvePoint, EmbeddedCurveScalar, multi_scalar_mul};
fn main(point_x: Field, point_y: Field, scalar_low: Field, scalar_high: Field) {
let point = EmbeddedCurvePoint { x: point_x, y: point_y, is_infinite: false };
let scalar = EmbeddedCurveScalar { lo: scalar_low, hi: scalar_high };
let result = multi_scalar_mul([point], [scalar]);
println(result);
}
fixed_base_scalar_mul
Multiplies a scalar by the curve’s generator point. Equivalent to multi_scalar_mul([generator], [scalar]).
pub fn fixed_base_scalar_mul(scalar: EmbeddedCurveScalar) -> EmbeddedCurvePoint
use std::embedded_curve_ops::{EmbeddedCurveScalar, fixed_base_scalar_mul};
fn main(scalar_low: Field, scalar_high: Field) {
let scalar = EmbeddedCurveScalar { lo: scalar_low, hi: scalar_high };
let point = fixed_base_scalar_mul(scalar);
println(f"({point.x}, {point.y})");
}
embedded_curve_add
Adds two points on the embedded curve. Handles all corner cases including the point at infinity and point doubling.
pub fn embedded_curve_add(
point1: EmbeddedCurvePoint,
point2: EmbeddedCurvePoint,
) -> EmbeddedCurvePoint
| Parameter | Type | Description |
|---|
point1 | EmbeddedCurvePoint | First point |
point2 | EmbeddedCurvePoint | Second point |
Returns EmbeddedCurvePoint — the sum point1 + point2.
use std::embedded_curve_ops::{EmbeddedCurvePoint, embedded_curve_add};
fn main() {
let p1 = EmbeddedCurvePoint { x: 1, y: 2, is_infinite: false };
let p2 = EmbeddedCurvePoint { x: 3, y: 4, is_infinite: false };
let sum = embedded_curve_add(p1, p2);
println(f"({sum.x}, {sum.y})");
}
EmbeddedCurvePoint also implements the +, -, and unary - operators via the standard Add, Sub, and Neg traits, so you can write p1 + p2 directly.
Ciphers
aes128_encrypt
Encrypts a byte array using AES-128 in CBC mode. Input is automatically padded with PKCS#7, so the output length is always N + (16 - N % 16).
pub fn aes128_encrypt<let N: u32>(
input: [u8; N],
iv: [u8; 16],
key: [u8; 16],
) -> [u8; N + 16 - N % 16]
| Parameter | Type | Description |
|---|
input | [u8; N] | Plaintext bytes |
iv | [u8; 16] | 16-byte initialisation vector |
key | [u8; 16] | 16-byte AES-128 key |
Returns [u8; N + 16 - N % 16] — the ciphertext including PKCS#7 padding.
fn main() {
let input: [u8; 4] = [0, 12, 3, 15];
let iv: [u8; 16] = [0; 16];
let key: [u8; 16] = [0; 16];
// Output length is 4 + (16 - 4 % 16) = 16 bytes
let ciphertext = std::aes128::aes128_encrypt(input, iv, key);
}
AES-128 encryption is a black box function. The backend implements it natively. There is no decryption primitive in the stdlib — decryption is generally not needed inside a ZK circuit.