// inside head tag
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.
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.
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.
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)
}
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.
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.
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:
With the simplicity of the syntax and clear documentation of basic functionality it was quick to onboard and start writing basic circuits.
With regard to audits, with the increased adoption of ZK in protocols and the improvement of Noir more audits related to Noir are foreseeable.
Nick Dimitriou - MSc in Information Security at UCL with a Cryptography and Blockchain technology direction. Nick has worked on various engineering problems, from mobile application development to blockchain applications using zk DSLs and zk VMs (Mina, Noir, Circom, Risc-0, etc.). Currently working as a Cryptography Engineer at Nethermind, where he focuses on engineering zk solutions, contributing to the implementation of state-of-the-art research papers, and participating in zk-audits.
Michael Belegris - MSc in Information Security with a focus in cryptography and blockchain applications. Michael has worked on engineering research by implementing newly published zk-friendly hash functions for use on-chain, off-chain and in circuits as well as implementing attacks on mix-networks proving their insecurity. Additionally, Michael has helped in the teaching of block ciphers by building a learning tool for the AES and DES ciphers.
Isaac Villalobos Gutiérrez - BSc in Physics and developer experienced in C++ and Rust. Contributed to the development of Vampire zkSNARK, Latticefold post-quantum folding scheme, Starkpack implementation over Risc0 zkVM. Additionally, a consultant for technical due diligence requests for Ethereum ecosystem projects at Nethermind, delivering these services to external clients of the company.
Krzysztof Szubiczuk - Security researcher at Nethermind for three years, focusing on Ethereum and Starknet platforms. Has secured numerous protocols including DEXs, overcollateralized lending protocols, cross-chain bridges, staking solutions, wallets, and zero-knowledge applications. Member of the OpenAI Red Teaming Network and contributed to the "OpenAI o1 System Card." Has over 3 years of experience in Smart Contract auditing across various types of protocols.
Luciana Silva - Ph.D. in Computer Science with over 10 years in applied AI research. Published 29 papers in top IEEE, ACM, and Elsevier journals. Has been working in Smart Contracts Security for three years across various ecosystems with expertise in Solidity, Cairo, Rust, and zkDSLs (Noir and Circom). Works on AMMs, order books, lending protocols, cross-chain bridges, staking, and Zero Knowledge applications. Has presented talks on Smart Contract security at Aleph Crecimiento 2024 and AI Agents Day during ETH Denver 2025.