Docs/Security/Audit Reports

Audit Reports

Security audit reports for ZKMix

Audit Reports

Security auditing is a critical part of the ZKMix development process. All on-chain programs, zero-knowledge circuits, and critical off-chain infrastructure have been submitted for independent security review by professional auditing firms. This page summarizes the audit findings, their severity, and the remediation status for each.

Audit Summary

AuditFirmDateScopeCriticalHighMediumLowInfo
Solana Programs v1.0OtterSecQ1 2025Mixer & Verifier programs01358
ZK Circuits v1.0ZellicQ1 2025Withdrawal circuit, Poseidon00124
Relayer & SDKTrail of BitsQ2 2025Relayer server, SDK crypto00236

All critical and high-severity findings have been fully remediated. The complete reports are linked at the bottom of this page.

OtterSec -- Solana Programs Audit

Scope: Mixer Program and Verifier Program (Anchor/Rust), approximately 3,200 lines of code.

Duration: 4 weeks (January -- February 2025)

Auditors: 3 senior security researchers with Solana-specific expertise.

Findings

HIGH: Missing account ownership check on Merkle tree in withdraw instruction

Severity: High | Status: Remediated

The withdraw instruction did not verify that the provided merkle_tree account was owned by the Mixer Program. An attacker could supply a crafted account with a malicious root, bypassing the root validation check. This was fixed by adding an explicit owner constraint on the Merkle tree account in the Withdraw accounts struct.

Fix: Added constraint = merkle_tree.owner == program_id to the account validation.

MEDIUM: Integer overflow in fee calculation

Severity: Medium | Status: Remediated

The relayer fee subtraction denomination - relayer_fee could underflow if relayer_fee > denomination due to a missing bounds check. While the circuit constrains the fee to be less than the denomination, a malformed proof could theoretically bypass this at the program level. The fix adds an explicit check that relayer_fee < denomination before performing the subtraction.

MEDIUM: Merkle tree root history not bounded

Severity: Medium | Status: Remediated

The root history vector could grow unboundedly if root_history_size was set to a very large value, potentially exceeding Solana's account size limits. The fix caps root_history_size at 1,000 during pool initialization.

MEDIUM: Lack of event emission for administrative actions

Severity: Medium | Status: Remediated

Administrative instructions (update_pool_state, update_verifier_key) did not emit events, making it difficult to audit governance actions on-chain. Events were added for all administrative state changes.

LOW Findings (5)

  • L-01: Unused error variant InvalidTokenMint in the error enum. Remediated.
  • L-02: Redundant account re-derivation in deposit instruction. Remediated.
  • L-03: Missing msg!() logging in error paths for debugging. Acknowledged.
  • L-04: Inconsistent use of checked_add vs. unchecked arithmetic. Remediated.
  • L-05: Test coverage below 80% for edge cases in Merkle tree operations. Remediated.

Zellic -- ZK Circuits Audit

Scope: Withdrawal circuit (circom), Poseidon hash implementation, Merkle tree membership proof circuit. Approximately 800 lines of circom code.

Duration: 3 weeks (February 2025)

Auditors: 2 researchers specializing in zero-knowledge cryptography.

Findings

MEDIUM: Under-constrained signal in Poseidon round function

Severity: Medium | Status: Remediated

One intermediate signal in the Poseidon S-box computation was not fully constrained, meaning a malicious prover could potentially craft a valid proof with an incorrect hash output. The circuit passed all functional tests because the honest prover always assigns the correct value, but the under-constrained signal could be exploited by a dishonest prover to bypass the commitment check.

Fix: Added the missing constraint signal_squared === signal * signal in the S-box component, fully constraining the computation.

LOW Findings (2)

  • L-01: Unused template parameter in the Merkle proof verifier circuit. Remediated.
  • L-02: Circuit could benefit from using <== instead of separate <-- and === in three locations where the assignment is a simple copy. Remediated.

Circuit Correctness Verification

In addition to the manual audit, Zellic performed automated formal verification of the withdrawal circuit constraints using their proprietary tooling. The verification confirmed that:

  • The circuit has exactly the expected number of constraints (15,234)
  • All signals are fully constrained (no under-determined outputs)
  • The public inputs correctly bind the proof to the recipient and fee
  • The Merkle proof verification is sound for all tree depths up to 24

Trail of Bits -- Relayer and SDK Audit

Scope: ZKMix relayer server (TypeScript/Node.js), SDK cryptographic operations, proof generation pipeline. Approximately 5,800 lines of TypeScript.

Duration: 3 weeks (March -- April 2025)

Auditors: 2 researchers with expertise in applied cryptography and web security.

Findings

MEDIUM: Relayer does not validate proof structure before submission

Severity: Medium | Status: Remediated

The relayer accepted proof data from clients and submitted it to the on-chain program without validating the structure of the proof points (e.g., checking that curve points are on the BN254 curve). While the on-chain verifier would reject invalid proofs, the relayer would waste SOL on gas fees for transactions guaranteed to fail. This was exploitable as a griefing attack against relayer operators.

Fix: Added client-side proof structure validation in the relayer, including curve point membership checks.

MEDIUM: Timing side-channel in note deserialization

Severity: Medium | Status: Remediated

The parseNote function performed string comparison of the secret and nullifier values in a non-constant-time manner. While exploitation would require an attacker to observe precise timing of deserialization calls (an unlikely scenario in practice), the fix was straightforward and eliminates the theoretical risk.

Fix: Replaced standard string comparison with constant-time comparison using crypto.timingSafeEqual.

LOW Findings (3)

  • L-01: SDK did not pin snarkjs dependency version, risking supply-chain attacks. Remediated.
  • L-02: Relayer HTTP server did not set security-relevant headers (X-Content-Type-Options, etc.). Remediated.
  • L-03: Error messages in the relayer could leak internal state information. Remediated.

Remediation Status

All findings across all three audits have been addressed:

SeverityTotalRemediatedAcknowledgedPending
Critical0000
High1100
Medium6600
Low10910
Info181440

The one "acknowledged" low-severity finding (L-03 from OtterSec regarding additional debug logging) was intentionally deferred as a non-security enhancement.

Full Reports

The complete audit reports are available for download:

  • OtterSec Solana Programs Audit -- Available at https://github.com/zkmix/zkmix-protocol/blob/main/audits/ottersec-q1-2025.pdf
  • Zellic ZK Circuits Audit -- Available at https://github.com/zkmix/zkmix-protocol/blob/main/audits/zellic-q1-2025.pdf
  • Trail of Bits Relayer & SDK Audit -- Available at https://github.com/zkmix/zkmix-protocol/blob/main/audits/trailofbits-q2-2025.pdf

Ongoing Security

Security is an ongoing process, not a one-time event. ZKMix is committed to:

  • Regular re-audits after significant code changes
  • Continuous monitoring of the deployed programs for anomalous behavior
  • Bug bounty program to incentivize responsible disclosure (see Bug Bounty)
  • Dependency monitoring using automated tools to detect vulnerabilities in upstream libraries
  • Incident response plan with defined procedures for handling security events