Mina Multisig is an open-source implementation of FROST threshold signing for Mina, built by community developers under a grant and now moving under o1Labs’ stewardship. By convention, a Mina smart contract has an associated private key authorized to update its verification key, necessary whenever the chain hard-forks or changes proof systems. That convention places upgrade authority in a single private key, creating a point of trust that some applications may want to distribute. Distributing it with a conventional multisig contract does not work: the contract would brick outright across the same hard fork it exists to survive. FROST distributes it without that failure. A threshold of signers can jointly produce one ordinary signature, matching what Mina expects, with the private key never reconstructed by any single party. Nori, a trust-minimized Ethereum-to-Mina bridge, will be the first to run it in production.
The challenge: a signature scheme meeting a chain that signs differently
FROST’s security has been formally proven under standard cryptographic hardness assumptions, provided an implementation correctly handles the edge cases those proofs assume away, like rejecting the identity element and enforcing unique nonces and commitments. The Zcash Foundation’s frost-core is a mature, production-used implementation of those requirements. None of that transfers automatically to Mina, which has its own signing conventions: the Pallas curve, Poseidon-based hashing, a specific challenge construction, and a rule under which a signature stores only the x-coordinate of the Schnorr commitment. Matching those conventions means overriding parts of frost-core rather than calling it as shipped.
Mina Multisig does this across two crates. frost-bluepallas is the Mina-specific ciphersuite: it defines the Pallas curve and Poseidon hashing setup, the field and group serialization rules, and the parity normalization FROST needs to behave the way Mina expects, while leaving the distributed-signing mechanics to frost-core itself. mina-tx sits on top of it, building the Mina-specific challenge payload and hashing it exactly the way Mina does, then converting the resulting FROST keys and signatures into the transaction structures Mina expects downstream.
Those overrides are where risk concentrates. A library’s guarantees are properties of the whole construction, not just the obviously cryptographic parts. Enforcing a given guarantee can depend on steps elsewhere in the library that look like plumbing. Replace one of those steps with your own and you can lose the invariant it was quietly enforcing, and nothing tells you. The code compiles, the tests pass, and signatures verify, because the conditions that expose the missing check are ones no honest signer produces. This audit covered the signature layer alone, not its downstream use, but that layer is what a Mina contract will trust to authorize its own upgrades.
The solution: expert review, tracked end to end
We reviewed this codebase line by line. It is Rust, not Solidity or a ZK circuit, so it falls outside what our automated tools cover. The bugs that matter here would likely escape a scanner regardless: catching them means knowing how frost-core structures its guarantees, knowing what Mina’s conventions demand, and noticing where the two disagree.
Two analysts spent two person-weeks on commit 1457cdd, working from the project’s documentation and the underlying frost-core design. Two of the report’s recommendations concern intent rather than code. One flags that signing_utilities.rs pools all the secret material for a signing session in one place, fine for tests but risky if it reached a consumer without the network and participant authentication a real deployment needs. The other flags that it is unclear whether the project is meant to be used standalone or imported as a library, which changes what should be publicly exposed. Both surfaced through conversation with the developers, tracked through AuditHub alongside the findings so the review, the discussion, and the fixes stayed in one place.
Review highlights: assumptions that were never checked
The most severe finding was a missing point-at-infinity check.
In frost-core‘s normal flow, identity-element points are rejected during group-element serialization, and the default challenge computation routes through that serialization before hashing. Mina Multisig overrides that computation, as it must, to match Mina’s hashing, and in doing so reads affine coordinates directly:
|
1 2 3 |
point.into_affine().x point.into_affine().y |
Those two lines are the vulnerability. Cryptographic engineering is largely about recognizing that code this innocent-looking can still break a security property. In the arkworks short-Weierstrass affine representation, a point carries a separate infinity flag alongside its x and y coordinates, and that flag, not the coordinate values, is the authoritative answer to whether a point is the point at infinity. For that point specifically, x and y both happen to be stored as zero, so reading .x directly returns zero without ever consulting the flag. The identity rejection that serialization would have performed never happens, and zeros flow onward as though they were real coordinates. Our analysts found the pattern in five places across both crates.
The same root cause surfaced in the group commitment itself, which was never checked to be nonzero, so the protocol could derive challenges from that same identity point. The developers fixed both together, adding a group_commitment_affine wrapper that refuses a zero value before returning it.
A third finding took the same shape at the conversion boundary. Mina’s signature format stores only the x-coordinate of the commitment R. That works because Mina fixes R‘s y-coordinate to a single parity by convention: given x, only one of the two possible y-values is valid, so nothing is lost by dropping it, provided the signer actually produced a y with the right parity in the first place. The conversion helpers discarded R.y without checking that it did, so a signature with an odd y-coordinate could convert into one that later failed to verify. The fix checks parity and either negates or returns an error.
The remaining three were data-validation and logic issues in the same compatibility layer: an off-by-one in custom-network parsing that let a malformed message panic a client mid-signing, a deserialization fallback that reinterpreted malformed input as a raw testnet message instead of rejecting it, and a SignatureSerialization size that was wrong in the ciphersuite contract, a hazard for anything downstream that trusted it.
All six were found by manual review, and all six were fixed and confirmed before the report was finalized. The complete scope, methodology, and findings are in the full audit report.
Impact
The engagement closed with every finding resolved and each fix confirmed against a linked pull request.
- Fixes without redesign: none of the six required an architectural change. The high-severity finding was resolved by adding the checks the override had skipped.
- A reviewed base for the ecosystem: Mina Multisig is open source and passing to o1Labs, giving other Mina teams building wallet or self-custody tooling access to a reviewed foundation they can build on.
- Practice changes beyond the code: the report prompted work Nori had not scoped before it, including a more transparent signing interface and the removal of test-only helpers from the production surface.
- Re-runnable, not automatic: this was a point-in-time review. The findings, discussion, and fix history remain in AuditHub, so a later pass starts with that context already in place. "It gave us a deeper understanding of our own protocol. Developers get closed-minded about their own code after a while. Having auditors go over it, spotting what could get misdiagnosed during development, was critical."
When you override a library, check what you dropped
Half the findings here point in the same direction. Two trace to a single missing check, and a third to the same habit of trusting a convention without verifying it. The pattern generalizes beyond FROST and beyond Mina. Cryptographic libraries bundle safety properties into their default paths, often in steps that look like plumbing, and nothing marks them as security controls because inside the library’s own construction they do not need to be. Any team adapting such a library to a chain with its own conventions will override some of those steps. The question worth asking at review time is which guarantees the original step was carrying, and whether the replacement carries them too. Reading can answer that. Testing largely cannot.
Mina Multisig had that review before its first production deployment, not after.
