Skip to main content

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]
ParameterTypeDescription
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]
ParameterTypeDescription
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]
ParameterTypeDescription
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
ParameterTypeDescription
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
ParameterTypeDescription
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]
ParameterTypeDescription
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]
ParameterTypeDescription
input[Field; N]Sponge state (length must equal the backend’s POSEIDON2_CONFIG_STATE_SIZE)
state_lenu32Must 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
ParameterTypeDescription
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 booltrue if the signature is 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
ParameterTypeDescription
point1EmbeddedCurvePointFirst point
point2EmbeddedCurvePointSecond 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]
ParameterTypeDescription
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.

Build docs developers (and LLMs) love