OpenZeppelin Blog

A Developer’s Guide to Building Safe Noir Circuits

Written by Felix Wegener | August 26, 2025

Table of Contents



Introduction

In the world of zero-knowledge proofs (ZKPs), Noir has emerged as a powerful, developer-friendly language for building privacy-preserving applications. Aztec has designed Noir as a domain-specific language (DSL) rooted in Rust’s principles and it simplifies the creation of complex arithmetic circuits—programs that define computations to be proven without revealing sensitive information. Noir’s design abstracts much of the cryptographic complexity, but secure circuits still depend on careful implementation, comprehensive testing, and independent review.


Noir and the Core Principles of Zero-Knowledge Systems

Noir abstracts low-level cryptographic complexities, allowing developers to focus on logic rather than circuit optimization. Unlike earlier frameworks like Circom or ZoKrates, Noir leverages Rust-like syntax and tooling to reduce boilerplate code and human error. However, this abstraction does not fully eliminate risk: The Noir compiler automates the transformation of Noir circuits into an Abstract Circuit Intermediate Representation (ACIR), which is then processed by proving systems such as Barretenberg, Groth16, and PLONK to generate and verify proofs. This abstraction removes the burden of handling complex mathematical details. However, developers remain responsible for ensuring the correctness of the circuit logic itself.

The security of these circuits rests on three foundational properties:

  1. Soundness: A malicious prover cannot forge proofs for false statements.
  2. Completeness: Honest provers can always generate valid proofs for true statements.
  3. Zero-Knowledge: Proofs reveal nothing about private inputs beyond explicitly declared public outputs.

A single flaw in a circuit can undermine one or more of these pillars, leading to insecure or incorrect proofs.

 


Common Security Vulnerabilities in Noir Circuits

Below, we explore vulnerabilities that threaten the soundness, completeness, or zero-knowledge property of Noir-based systems:

1. Logical Errors in Constraints

Logical errors directly jeopardize soundness. For example, a circuit designed to prove “knowledge of a secret value greater than 100” might omit a constraint enforcing this inequality. Attackers could then submit values like 50 and “prove” compliance, tricking verifiers. Such gaps often stem from incomplete translations of requirements into circuit logic.

Audit Focus:

  • Are all business rules codified as constraints?
  • Do edge cases (e.g., minimum/maximum values) have explicit safeguards?

Example:

Consider the following circuit in which we want to constrain a user's age to be at least 18:

Since Noir feels like writing “normal” Rust-style high-level code, the condition `if is_adult` is evaluated during proving, but is not constrained. To enforce that `user.age >= 18`, the circuit implementer must explicitly assert it. The correct implementation would be:

2. Arithmetic Pitfalls in Finite Fields

Noir operates over finite fields, where arithmetics over the native Field type wraps modulo a prime number. This introduces subtle risks for the soundness of the system:

  • Overflow/Underflow: A check like x+y==z might pass erroneously if x+y exceeds the field modulus.
  • Missing Range Checks: Values intended to mimic “real-world” integers (e.g., 8-bit unsigned) require explicit bounds constraints.

Audit Focus:

  • Are operations resilient to wrap-around behavior?
  • Do variables either have manually-enforceable bounds e.g. assert(x.lt(256))  or are cast into types with automatic boundary checks such as integers

Example:

Consider the following example in which the sum of two private `Field` inputs $x,y$ is constrained to be equal to a public `Field` input $z$: The above implementation is correct if the intention is to perform the check modulo the prime $p$ of the field: $x + y = z \mod p$. However, if the intention is to check the sum $x+y$ without reducing $\mod p$ (which will be performed implicitly), then the above may cause the wrong result. E.g. for $p=7$ and $x=5,y=4$ we may want to check $5+4=9$ and not $5+4=2\mod 7$. To avoid such ambiguities one needs to ensure that the field is of sufficiently large size.

As a side comment, related to constraining values to be in an expected range, we note that Noir adds implicit `assert`-s for fixed-sized inputs such as `Field` and array. Therefore, explicit `assert`-s are necessary only for dynamic objects such as vectors, slices, etc.

3. Intent vs. Implementation Mismatches

Even well-documented circuits can harbor discrepancies between the developer’s intent and the code. For instance, a circuit meant to enforce “user X owns NFT Y” might inadvertently validate proofs although X has already sold Y. These mismatches often arise from ambiguous specifications or misunderstandings of cryptographic primitives.

Audit Focus:

  • Does the code align with protocol specifications?
  • Are cryptographic assumptions (e.g., hash functions) explicitly validated?

Example:

Consider an authorization scenario in which a user is granted access to some resource, based on knowledge of a secret. E.g., this can be part of a larger ZK-based login system, where authorization is granted without revealing the user credentials. 

A somewhat simple, but illustrative vulnerability, that could arise from insufficiently detailed specification or from a misunderstanding of cryptographic assumptions, is introduced with the following implementation:While the circuit proves knowledge of the secret by means of constraining `poseidon([sk]) == auth_hash`, it fails to enforce any binding between the user id and the secret. Therefore anyone who knows the secret can produce a valid proof for any user id. A fix would be to derive the user id from the secret using some domain separation constant as in:Note that the illustrated vulnerability is not Noir-specific per se and is potentially applicable to other circuit DSLs e.g., Circom. However the high-level Rust-like syntax of Noir - which is also one of its undisputed advantages - may make similar mistakes harder to spot.

4. Privacy Leaks

Privacy failures jeopardize the zero-knowledge property and expose sensitive data through two primary vectors:

  • Accidental Public Inputs: A value unintentionally marked pub accidentally becomes visible to verifiers.
  • Implicit Leaks: Public outputs may correlate with private inputs.
    •  For example, a voting system’s public “total votes” could leak individual preferences if combined with auxiliary data. 
    • Another example is the (mis)-usage of the hashed sender address as a nullifier. It prevents replay attacks, but implicitly leaks the sender identity.

Audit Focus:

  • Are inputs/outputs correctly labeled public or private?
  • Do public outputs reveal indirect information about private inputs?

Example:

A naive example of fully leaking a supposedly private input (age) is the following:A fix is to omit returning the private input (age): Also note that when a circuit exposes a public hash of a secret input from a small domain, an adversary can brute force that value. For instance, consider the example from above, where additionally the circuit returns the public hash over the age. Despite the hash being cryptographically secure in isolation, the limited range of possible ages (only about 82 = 100-18 potential values, assuming maximum age 100) allows an attacker to quickly iterate through every candidate, compute its hash, and match it against the public hash. In doing so, the attacker can easily recover the actual age, thereby compromising the privacy of the input.


Conclusion

Zero-knowledge circuits succeed or fail on rigorous constraint design. By translating every business rule into explicit assertions, guarding against field-arithmetic surprises, and treating each public interface as a potential side-channel, teams preserve both soundness and privacy. As the Noir ecosystem matures, continuous peer review and thoughtful audits - whether internal or external - remain the surest path to trustworthy ZK applications.

If you have any questions about auditing Noir circuits, reach out to our ZK team.