// inside head tag

Our First Deep Dive into Aztec’s Noir Language, What ZK Auditors Learned

Security

July 10, 2025

Nethermind Security's ZK research team shares insights from their first major Noir audit with Oxorio

Our team of ZK auditors analysed and tested Noir circuits to ensure correct execution, as well as completeness, soundness and zero knowledge were preserved correctly. When we first approached Noir, we brought experience from across the ZK landscape: Circom, o1js (implemented zk-email), SP1, Risc-0, and Cairo.

Our initial expectation was that this higher level zkDSL (compared to Circom) would allow for easier readability and auditing capabilities.

Language Design: Cleaner Than the Competition

Noir stands out for its Rust-inspired syntax, providing a high-level, statically typed, and user-friendly environment that abstracts away much of the complexity of constraint writing, that we had to deal with circom.

In comparison, o1js, built in JavaScript and TypeScript, takes a different route by enabling developers to write ZK circuits using familiar JS constructs. However, o1js has a lot of more complexity of what its actually considered a proof and what is normal code, and has a more stern learning curve, its dynamic typing and JavaScript runtime may introduce challenges in debugging and performance. Overall, Noir offers the cleanest and most modern language design.

Accessibility for Mainstream Developers

Noir is particularly accessible to developers from mainstream programming backgrounds due to its familiar, Rust-like syntax and high-level abstractions. It allows you to write logic using standard constructs like functions, conditionals, and structs, without needing to manually manage low-level details like constraint wiring. The language also handles constraint generation implicitly, so developers can focus on writing clear, intuitive code rather than grappling with the underlying cryptographic complexity.

Privacy and Security Features

During our audit, Noir's expressive yet intuitive syntax significantly simplified complex zero-knowledge constructs compared to Circom, particularly in handling Merkle tree proofs. For instance, while a Merkle inclusion proof in Circom often required developers to manually manage index arithmetic, hash computation, and explicit wire definitions, Noir abstracted these operations through concise built-in functions.

Circom code here.

pragma circom 2.1.5;

include "node_modules/circomlib/circuits/poseidon.circom";

template Merkleverifier(N) {
    signal input leaf;
    signal input root;
    signal input path[N];
    signal input pathIndex[N];

    component hasher[N];
    component mux[N];

    for (var i = 0; i < N; i++) {
        mux[i] = DualMux();
        mux[i].in[0] <== i == 0 ? leaf : hasher[i - 1].out;
        mux[i].in[1] <== path[i];
        mux[i].s <== pathIndex[i];

        hasher[i] = hash_lr();
        hasher[i].left <== mux[i].out[0];
        hasher[i].right <== mux[i].out[1];
    }
    root === hasher[N - 1].out;
}

template DualMux() {
    signal input in[2];
    signal input s;
    signal output out[2];

    s * (1 - s) === 0;
    out[0] <== (in[1] - in[0])*s + in[0];
    out[1] <== (in[0] - in[1])*s + in[1];
}

template hash_lr() {
    signal input left;
    signal input right;
    signal output out;
    component poseidon = Poseidon(2);
    poseidon.inputs[0] <== left;
    poseidon.inputs[1] <== right;
    out <== poseidon.out; 
}

component main = Merkleverifier(2);

Equivalent Noir code here.

use dep::std;

// Returns the merkle root of the tree from the provided leaf, path indices, siblings with poseidon hash.
fn compute_merkle_root(root: Field, leaf: Field, path_indices: [Field], siblings: [Field]) {
    let n = siblings.len();
    let mut current = leaf;
    for i in 0..n {
        let is_right = (path_indices[i] == 1) as bool;
        let (hash_left, hash_right) = if is_right {
            (siblings[i], current)
         } else {
            (current, siblings[i])
         };
      current = std::hash::poseidon::bn254::hash_2([hash_left, hash_right]);
    };
    assert(current == root)
}

Learning Curve

Noir borrows syntax and structural elements from Rust, so developers and auditors with Rust background may find it easier to learn. Despite dealing with complex concepts, Noir aims to be simple and easy to read. It abstracts much of the underlying cryptographic complexity, compared to Circom as mentioned in the previous question.

Security Approach

Noir is designed so developers do not need deep knowledge of the underlying mathematics or cryptography to write secure ZK programs. By abstracting away these details, it reduces the risk of developer-introduced cryptographic errors.

In terms of security guarantees, abstraction of cryptography reduces the risk of cryptographic misuse. By being backend-agnostic developers can chose the security guarantees they want and helps when an issue arises in one backend or a newer, better backend is released as they can easily switch.

For pattern enforcement, Noir enforces type checking similar to rust. It allows for the concept of "constrained/unconstrained" code whereby unconstrained code is not proven and can thus catch unconstrained data from leaking into the proof.

Developer Experience

The aspects of the Noir developer experience that most impressed our team: Rust-like syntax was easy to migrate as a Rust developer. Cryptography abstraction meant little cryptography is required to implement protocols.

Our ratings for Noir's current state:

  • Tooling: 8/10 - the noir LSP needs improvement as it is quite slow.
  • Documentation: 6/10 - should have more examples and clearer explanations of basic functionality usage, breaking changes should be clearly described and not yet implemented functionality must be outlined.
  • Error messages: 7/10 - generally good but in certain more niche cases the error messages are not descriptive and incorrect.

The "Aha Moment"

With the simplicity of the syntax and clear documentation of basic functionality it was quick to onboard and start writing basic circuits.

Looking Forward

With regard to audits, with the increased adoption of ZK in protocols and the improvement of Noir more audits related to Noir are foreseeable.

Latest articles