diff --git a/.gitignore b/.gitignore index e046758..ccb959f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,5 @@ mina-frost-client/tests/assets/ # GraphQL schemas *.graphql -# AI generated files -CLAUDE.md +# AI generated folders +.claude/ diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index c8d5448..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,108 +0,0 @@ -# Agent guidelines for `mina-multi-sig` - -## 1. Repository overview - -- A Rust workspace consisting of two crates: - - `frost-bluepallas`: FROST implementation for Mina’s Pallas curve. - - `mina-frost-client`: CLI utilities (trusted dealer, DKG, signing sessions). -- Code is experimental and **not security audited**. See the security warnings in `README.md` and `frost-bluepallas/README.md`. - -## 2. Development workflow - -1. **Run tests** - - ```bash - cargo test - ``` - - The workspace contains extensive tests under `frost-bluepallas/tests` and `mina-frost-client/`. - -2. **Lint and formatting** - - - Format all Rust code before committing: - ```bash - cargo fmt --all - ``` - - Run clippy with strict settings (matches `.pre-commit-config.yaml`): - ```bash - cargo clippy --all-targets --all-features -- -D clippy::all -W clippy::too_many_arguments - ``` - -3. **Pre-commit hooks** - The project uses [pre-commit](https://pre-commit.com/). Running `pre-commit run --all-files` - will execute the same formatting and linting checks used in CI. - -4. **CI expectations** - GitHub Actions (see `.github/workflows/rust.yml`) run: - - - `cargo build --verbose` - - `cargo test --verbose` - - `cargo fmt --check` - -5. **Commit style** - Commit messages generally follow **Conventional Commits** - (e.g., `feat:`, `fix:`, `doc:`). Use concise, descriptive messages. - -## 3. Coding guidelines - -- Rust edition: 2021 (check `rust-toolchain.toml` for toolchain version). -- Prefer descriptive comments and doc comments. Examples and tests are heavily documented—match this style when adding new modules. -- Error handling typically uses `eyre` or `thiserror`; maintain existing patterns. -- `tokio` is used for async code in `mina-frost-client`. Keep async interfaces consistent with existing modules (`session.rs`, `coordinator/` etc.). -- For new features in `frost-bluepallas`, ensure compatibility with Mina’s signature format (see `translate` module) and update tests accordingly. - -## 4. Security notes - -- The project strives for production-quality code but has not yet undergone a formal security audit. See the README for current disclaimers. -- Avoid storing sensitive key material in version control. Example: `examples/trusted_dealer_example/generated/` is gitignored for this reason. - -## 5. File organization hints - -- Library code for the FROST implementation: `frost-bluepallas/src/` -- CLI and session logic: `mina-frost-client/src/` -- Examples: `frost-bluepallas/examples/` and `mina-frost-client/examples/` -- Tests: under each crate’s `tests/` directory - -## 6. Testing strategy - -- Aim for roughly a 50/50 split between unit and integration tests. -- Use unit tests for modules with complex behaviour that benefits from isolated coverage. -- For `mina-frost-client`, prioritize integration tests and only add unit tests for new functionality in future iterations. -- Keep tests lightweight so they run quickly. - -## 7. Agile collaboration - -- Implement features incrementally and request frequent feedback from maintainers. -- Avoid leaving "TODO" comments for essential functionality unless the implementation would cause unnecessary bloat. -- Keep pull requests small and well scoped. Document significant design decisions in the PR description for reviewers. - -## 8. Rust Programming - -You are a Rust expert specializing in safe, performant systems programming. -Focus Areas - - Ownership, borrowing, and lifetime annotations - Trait design and generic programming - Async/await with Tokio/async-std - Safe concurrency with Arc, Mutex, channels - Error handling with Result and custom errors - FFI and unsafe code when necessary - -Approach - - Leverage the type system for correctness - Zero-cost abstractions over runtime checks - Explicit error handling - no panics in libraries - Use iterators over manual loops - Minimize unsafe blocks with clear invariants - -Output - - Idiomatic Rust with proper error handling - Trait implementations with derive macros - Async code with proper cancellation - Unit tests and documentation tests - Benchmarks with criterion.rs - Cargo.toml with feature flags - -Follow clippy lints. Include examples in doc comments. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7d61593 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,181 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Experimental FROST (Flexible Round-Optimized Schnorr Threshold signatures) multi-signature tooling for the Mina Protocol. Not security audited — do not use with real funds. + +Rust workspace with three crates: +- **`frost-bluepallas`** — FROST library for Mina's Pallas curve with Poseidon hashing. Now a generic ciphersuite crate — does NOT contain Mina transaction types or `PallasMessage`. It is parameterised over a `ChallengeMessage` trait. +- **`mina-tx`** — Mina transaction types, serialisation, `PallasMessage`, `TransactionSignature`, `Sig`, `PubKeySer`, and the `bluepallas_compat` glue. Standalone library; BluePallas bridging code is gated behind the `frost-bluepallas-compat` feature. +- **`mina-frost-client`** — CLI client for distributed key generation and signing sessions. Communicates with the `frostd` server. + +## Build & Test Commands + +```bash +cargo build --verbose # Build all crates +cargo test --verbose # Run all tests +cargo fmt --all # Format code +cargo clippy --all-targets --all-features -- -D clippy::all -W clippy::too_many_arguments # Lint + +# Run specific crate tests +cargo test --package frost-bluepallas +cargo test --package mina-frost-client +cargo test --package mina-tx + +# Run examples +cargo run --example dkg +cargo run --example mina-sign-tx +cargo run --example mina-gen-pubkey +cargo run --example sign +``` + +Pre-commit hooks run fmt, clippy, and trailing whitespace checks. CI (`rust.yml`) runs build, test, and `cargo fmt --check` inside a Docker container. + +## Architecture + +### frost-bluepallas (Library) + +The `BluePallas` ciphersuite is now **generic over a `ChallengeMessage` type parameter `M`**. `M` must implement the `ChallengeMessage` trait, which provides the `challenge()` function. This allows the crate to be used without any dependency on Mina transaction types. Key modules: + +- `lib.rs` — Ciphersuite definition (`BluePallas`), `ChallengeMessage` trait, FROST round1/round2/aggregate re-exports, y-coordinate evenness enforcement for Mina compatibility +- `keys.rs` — Key generation (trusted dealer and DKG), `KeyPackage`, `PublicKeyPackage`, secret/signing shares +- `hasher.rs` — Low-level Poseidon hash utilities (`hash_to_scalar`, `hash_to_array`). `message_hash` and `PallasMessage` have been **moved to `mina-tx`** +- `negate.rs` — Y-coordinate negation logic required by Mina's signature scheme +- `signing_utilities.rs` — Signing helper utilities +- `errors.rs` — `BluePallasError` and `BluePallasResult` types + +**Note**: `pallas_message.rs` and `mina_compat.rs` have been **removed** from `frost-bluepallas` and moved to the `mina-tx` crate. + +### mina-tx (Library — new crate) + +Contains all Mina-specific transaction logic. Modules: + +- `pallas_message.rs` — `PallasMessage` struct (serialise/deserialise), `Hashable` impl, `message_hash`, `translate_pk`, `translate_sig`, `translate_minask`, and the `ChallengeMessage` impl for `PallasMessage` (gated behind `frost-bluepallas-compat` feature) +- `bluepallas_compat.rs` — Bridge code between `frost-bluepallas` types and `mina-tx` types: `TryFrom>` for `Sig`, `TryFrom>` for `PubKeySer`, `TransactionEnvelope::to_pallas_message()`, `TransactionSignature::from_frost_signature()` / `from_frost_signature_bytes()`. Only compiled with the `frost-bluepallas-compat` feature. +- `signatures.rs` — `Sig`, `PubKeySer`, `TransactionSignature`, `TransactionSignature::new_with_zkapp_injection()` +- `errors.rs` — `MinaTxError` enum and `MinaTxResult` +- `transactions/` — `TransactionEnvelope`, `TransactionKind`, legacy/zkapp transaction types +- `graphql.rs`, `base58.rs` — GraphQL output and base58 utilities + +**Feature flags for `mina-tx`:** +- `frost-bluepallas-compat` — enables `bluepallas_compat` module and `ChallengeMessage` impl for `PallasMessage`; adds `frost-bluepallas`, `frost-core`, `ark-ec` as dependencies +- `test-utils` — enables test helper utilities + +### mina-frost-client (CLI) + +Subcommand-based CLI using `clap`. Operation modes: +- **Trusted dealer** — Test-only centralized key generation +- **DKG** — Distributed key generation via `frostd` server +- **Coordinator/Participant** — Signing session roles +- **Contacts/Groups/Sessions** — Management commands +- **GraphQL** — Transaction submission support + +Each mode has its own module under `src/` with `config.rs`, `comms/`, and operation logic. Async operations use `tokio`. + +### Key Design Patterns + +- FROST protocol flow: DKG (or trusted dealer) → Round 1 (nonce commitment) → Round 2 (signature share) → Aggregation +- Mina compatibility requires y-coordinate evenness checks and Poseidon-based hashing — see `frost-bluepallas/src/negate.rs` and `mina-tx/src/pallas_message.rs` +- The canonical `BluePallasSuite` type alias throughout the codebase is `BluePallas` (from `frost-bluepallas` + `mina-tx`) + - In `mina-frost-client`: defined in `src/lib.rs` as `pub type BluePallasSuite = BluePallas` + - In `frost-bluepallas` tests: defined in `tests/helpers/types.rs` + - In `frost-bluepallas` examples: defined in `examples/_shared/types.rs` +- Serialization uses `postcard` for binary, `serde_json` for JSON, `bs58` for Mina addresses +- Snapshot testing with `insta` crate in `frost-bluepallas/tests/snapshots/` + +## Coding Conventions + +- Rust 2021 edition, toolchain 1.87.0 +- Error handling: `eyre` for applications, `thiserror` for library error types +- Conventional Commits: `feat:`, `fix:`, `doc:`, etc. +- New `frost-bluepallas` features must maintain Mina signature format compatibility — update tests accordingly +- Async code in `mina-frost-client` uses `tokio` — keep consistent with existing `session.rs`, `coordinator/` patterns +- No panics in library code; explicit error handling with `Result` +- Prefer iterators over manual loops; leverage the type system for correctness + +## ZKApp Transaction Serialization + +The `mina-tx/src/transactions/zkapp_tx/` module handles ZKApp transaction serialization for Mina. Key patterns: + +### Custom Serde Types +- **o1js JSON compatibility**: Mina's o1js library serializes numeric types as strings in JSON. Use newtype wrappers with custom `Serialize`/`Deserialize` implementations: + - `StringU32(u32)` / `StringU64(u64)` — serialize as `"123"` not `123` + - `Field`, `PublicKey`, `TokenId` — all have custom serde in `zkapp_serde.rs` +- When adding new types that appear in JSON, check o1js output format and match it + +### Module Organization +- `zkapp_tx.rs` — Core struct definitions and type aliases +- `zkapp_serde.rs` — Custom `Serialize`/`Deserialize` implementations +- `packing.rs` — `Packable` and `Emptiable` traits for hashing (converts structs to field elements) +- `zkapp_test_vectors.rs` — Test data for commitment function tests (test-only) +- `commit.rs` — Commitment/hashing logic + +### Adding New Serializable Types +1. Define the type in `zkapp_tx.rs` +2. Add serde impl in `zkapp_serde.rs` if custom serialization needed +3. Implement `Packable` in `packing.rs` if the type participates in transaction hashing +4. Implement `Emptiable` if the type appears in `Option` fields that need packing +5. Update test vectors in `zkapp_test_vectors.rs` if affected + +### Test Data +- Real transaction JSON files in `mina-tx/tests/data/` +- Use `include_str!` to load test fixtures +- Round-trip tests (`serialize` → `deserialize` → compare) catch serde issues + +## Key Types and Their Locations + +### Signature Output (`TransactionSignature`) +- Defined in `mina-tx/src/signatures.rs` +- Structure: `{ publicKey: PubKeySer, signature: Sig, payload: TransactionEnvelope }` +- `Sig` has fields `field: BigInt<4>` and `scalar: BigInt<4>` with custom serde (decimal strings + base58) +- The `payload` field contains the full `TransactionEnvelope` — for ZkApp transactions, this includes injected signatures +- Accessible via `mina_tx::TransactionSignature` (re-exported from `mina_tx::signatures`) +- FROST-specific constructors (`from_frost_signature`, `from_frost_signature_bytes`) are in `mina-tx/src/bluepallas_compat.rs` and require the `frost-bluepallas-compat` feature + +### ZkApp Signature Injection +- `ZKAppCommand::inject_signature()` in `mina-tx/src/transactions/zkapp_tx/signature_injection.rs` +- Injects into `fee_payer.authorization` (a `String` field, base58-encoded) +- Injects into `account_updates[i].authorization.signature` (`Option`) for updates where: + - `authorization_kind.is_signed == true` + - `public_key` matches the signer + - `use_full_commitment == true` +- After injection, the full signed transaction is included in the `TransactionSignature.payload` + +### Transaction Types +- `TransactionEnvelope` wraps `TransactionKind` (enum: `ZkApp(ZKAppCommand)` | `Legacy(LegacyTransaction)`) + `NetworkIdEnvelope` +- `TransactionEnvelope::inner()` returns `&TransactionKind`, `inner_mut()` returns `&mut TransactionKind` +- `TransactionEnvelope::from_str_network()` auto-detects transaction type (tries ZkApp first, then Legacy) +- `TransactionEnvelope::to_pallas_message()` and `From<&TransactionEnvelope> for PallasMessage` are in `mina-tx/src/bluepallas_compat.rs` + +### ZkApp Structs (in `mina-tx/src/transactions/zkapp_tx.rs`) +- `ZKAppCommand { fee_payer: FeePayer, account_updates: Vec, memo: [u8; MEMO_BYTES] }` +- `FeePayer { body: FeePayerBody, authorization: String }` — authorization is a plain String (base58 signature) +- `AccountUpdate { body: AccountUpdateBody, authorization: Authorization }` +- `Authorization { proof: Option, signature: Option }` +- `AuthorizationKind { is_signed: Bool, is_proved: Bool, verification_key_hash: Field }` — `Bool` is a type alias for `bool` + +### Error Types +- `mina_tx::errors::MinaTxError` — error type for all `mina-tx` operations (serialization, deserialization, invalid signatures/public keys, memo errors, zkapp errors, unknown tx type) +- `frost_bluepallas::errors::BluePallasError` — error type for `frost-bluepallas` operations (serialization, no message provided, save signature errors) + +## File Structure Gotchas + +- `mina-tx/src/transactions/zkapp_tx.rs` is a **file** (not a directory with `mod.rs`). The submodules live in `mina-tx/src/transactions/zkapp_tx/` directory alongside it (Rust 2021 module system). +- `mina-tx/src/base58.rs` module is **public** (`pub mod base58`) — can be imported from outside +- `mina-tx/src/bluepallas_compat.rs` is only compiled when `frost-bluepallas-compat` feature is enabled +- `frost-bluepallas` no longer has a `pallas_message` module — it's in `mina-tx` +- `frost-bluepallas` no longer has a `mina_compat` module — it's in `mina-tx/src/bluepallas_compat.rs` +- `frost-bluepallas` no longer has a `transactions/` directory — it's in `mina-tx/src/transactions/` + +## Integration Test (`mina-frost-client/tests/integration-tests.rs`) + +- End-to-end test: DKG → signing → cross-package verification with `mina-signer` +- Uses release binary (`cargo build --release`) spawned as subprocesses +- Requires `frostd` server running + `mkcert` TLS certificates +- `parse_and_verify()` deserializes `TransactionSignature` from the signature output file, then: + - For ZkApp: verifies signature injection into authorization fields, then verifies with `mina_signer::create_kimchi` + - For Legacy: verifies with `mina_signer::create_legacy` +- Test is slow (spawns multiple processes, network communication) — run manually rather than in quick iteration loops +- `BigInt<4>` from `Sig` converts to mina-signer types via `.into()` diff --git a/Cargo.lock b/Cargo.lock index c70a44a..21ecd5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1080,7 +1080,7 @@ dependencies = [ [[package]] name = "frost-bluepallas" -version = "0.2.0" +version = "1.0.0-alpha.0" dependencies = [ "ark-ec", "ark-ff", @@ -1768,7 +1768,7 @@ dependencies = [ [[package]] name = "mina-frost-client" -version = "0.2.0" +version = "1.0.0-alpha.0" dependencies = [ "ark-ff", "async-trait", @@ -1857,7 +1857,7 @@ dependencies = [ [[package]] name = "mina-tx" -version = "0.1.0" +version = "1.0.0-alpha.0" dependencies = [ "ark-ec", "ark-ff", diff --git a/README.md b/README.md index ff68437..2807191 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ # Mina Multi-Sig +[![Rust](https://github.com/raspberry-devs/mina-multi-sig/actions/workflows/rust.yml/badge.svg)](https://github.com/raspberry-devs/mina-multi-sig/actions/workflows/rust.yml) +[![Documentation](https://github.com/raspberry-devs/mina-multi-sig/actions/workflows/docs.yml/badge.svg)](https://github.com/raspberry-devs/mina-multi-sig/actions/workflows/docs.yml) + This repository provides an experimental implementation of multi-signature tooling for the [Mina Protocol](https://minaprotocol.com/). The project is built around [FROST](https://github.com/cfrg/draft-irtf-cfrg-frost) (Flexible Round-Optimized Schnorr Threshold signatures) and contains both a reusable library and a command line client. ## ⚠️ Security Warning diff --git a/audits/mina-multi-sig-audit-rfp.md b/audits/mina-multi-sig-audit-rfp.md new file mode 100755 index 0000000..fc2cb9b --- /dev/null +++ b/audits/mina-multi-sig-audit-rfp.md @@ -0,0 +1,315 @@ +--- +title: "Security Audit RFP -- frost-bluepallas Crate" +date: 2026-02-23 +version: "2.0" +--- + +# Request for Proposal (RFP) +## Security Audit -- `frost-bluepallas` (and Security-Relevant Bridging Code) + +--- + +### 1. Introduction + +`mina-multi-sig` is a Rust project that implements **off-chain threshold multisig wallets for the Mina Protocol** using the **FROST (Flexible Round-Optimized Schnorr Threshold Signatures) scheme**. A group of signers collaboratively produce a single Mina-compatible Schnorr signature without placing multisig logic on-chain. + +The repository is a Cargo workspace with three crates: + +| Crate | Role | +|---|---| +| `frost-bluepallas` | Core cryptography library. Implements the FROST ciphersuite over Mina's Pallas curve with Poseidon hashing. Generic over a `ChallengeMessage` trait parameter. | +| `mina-tx` | Mina transaction types, serialisation, and a `PallasMessage` type that implements `ChallengeMessage`. Contains bridging code between FROST types and Mina types. | +| `mina-frost-client` | CLI client for distributed key generation (DKG) and signing sessions. Communicates with a `frostd` server. | + +**The primary audit scope is the `frost-bluepallas` crate.** Two files in `mina-tx` that implement the `ChallengeMessage` trait and FROST-to-Mina type conversions are also in scope because they directly affect cryptographic correctness. + +**Status**: Experimental. Not security audited. Not suitable for use with real funds. + +**Rust toolchain**: 1.92.0 (stable), Rust 2021 edition. + +**`#![no_std]`**: The `frost-bluepallas` crate is `no_std` (uses `alloc`). + +--- + +### 2. Architecture Overview + +#### 2.1 Cryptographic Stack + +``` +frost-core (v3.0.0-rc.0) -- Generic FROST protocol engine + | + +-- frost-bluepallas -- Pallas/Poseidon ciphersuite impl + | | + | +-- lib.rs -- Ciphersuite definition (BluePallas) + | +-- hasher.rs -- Poseidon hash utilities + | +-- keys.rs -- Key generation/DKG wrappers + | +-- negate.rs -- Y-coordinate negation logic + | +-- signing_utilities.rs -- Signing helper flows + | +-- errors.rs -- Error types + | + +-- mina-tx (bridging code only) + +-- pallas_message.rs -- ChallengeMessage impl, message_hash() + +-- bluepallas_compat.rs -- FROST-to-Mina type conversions +``` + +#### 2.2 FROST Protocol Flow + +1. **Key Generation** -- Either trusted dealer (`keys::generate_with_dealer`) or DKG (`keys::dkg::part1` / `part2` / `part3`), both delegating to `frost-core`. +2. **Round 1** -- Each signer generates nonces and commitments (`round1::commit`). +3. **Round 2** -- Each signer generates a signature share (`round2::sign`). Before signing, the `pre_commitment_sign` hook checks the y-coordinate parity of the group commitment and negates nonces/commitments if the y-coordinate is odd (Mina compatibility requirement). +4. **Aggregation** -- Coordinator aggregates shares (`aggregate`). The `pre_commitment_aggregate` hook performs the same parity check from the coordinator's perspective, negating commitments if needed. +5. **Challenge Computation** -- The `BluePallas::challenge()` method delegates to `M::challenge()` (the `ChallengeMessage` trait). For Mina transactions, this is implemented by `PallasMessage` in `mina-tx`, which deserializes the message, hashes it with the public key and nonce commitment x-coordinate using Poseidon, and returns the challenge scalar. + +#### 2.3 Mina Compatibility Requirements + +- **Y-coordinate evenness**: Mina's Schnorr signature scheme requires the group commitment's y-coordinate to be even. The `pre_commitment_sign` and `pre_commitment_aggregate` hooks enforce this by conditionally negating nonces and commitments. This is so that generated signatures are inline with the [specification](https://github.com/MinaProtocol/mina/blob/develop/docs/specs/signatures/description.md). +- **Poseidon hashing**: All FROST hash functions (H1, H3, H4, H5, HDKG, HID) use Poseidon via `mina-hasher::create_legacy`. +- **Challenge computation**: Uses `message_hash()` which constructs a Poseidon hash over `(transaction_input || pub_key_x || pub_key_y || rx)` with a Mina network-specific domain string. +- **H2 is intentionally unimplemented**: The standard FROST H2 (used for challenge computation) is replaced by the Mina-specific challenge path. Calling H2 will panic. + +#### 2.4 Key External Dependencies + +| Dependency | Version | Role | Security Relevance | +|---|---|---|---| +| `frost-core` | 3.0.0-rc.0 | Generic FROST protocol engine | **Critical** -- all protocol logic lives here. RC status is a risk factor. | +| `mina-curves` | 0.3.0 (git) | Pallas curve definition | **Critical** -- curve arithmetic correctness | +| `mina-hasher` | 0.3.0 (git) | Poseidon hash implementation | **Critical** -- hash function correctness | +| `mina-poseidon` | 0.3.0 (git) | Poseidon constants/parameters | **Critical** -- correct Poseidon parameterisation | +| `mina-signer` | 0.3.0 (git) | Reference signer (used for interop verification in tests) | Medium -- not used in production signing path | +| `ark-ec` | 0.5.0 | Elliptic curve traits and operations | **High** -- curve operations | +| `ark-ff` | 0.5.0 | Finite field arithmetic | **High** -- field operations | +| `ark-serialize` | 0.5.0 | Canonical serialization for curve/field elements | **High** -- serialization correctness | +| `rand_core` | 0.6.4 | Cryptographic RNG trait | **High** -- randomness for nonces | +| `bs58` | 0.5.1 | Base58 encoding | Medium -- address encoding | +| `serde` / `serde_json` | 1.0 | Serialization framework | Medium -- data interchange | +| `sha2` | 0.10 | SHA-256 (used in bs58 checksum, not core signing) | Low | + +**Note on `frost-core` version**: The project uses `frost-core` 3.0.0-rc.0, a release candidate. Several integration tests are marked `#[ignore]` due to an upstream bug ([frost-core issue #1015](https://github.com/ZcashFoundation/frost/issues/1015): "signature share verification bug"). This affects `check_sign_with_dkg`, `check_sign_with_dealer`, `check_refresh_shares_with_dealer`, and `check_refresh_shares_with_dkg`. We intend on upgrading to 3.0.0 as soon as it is released and will not release the tool into production until we do. + +**Note on `mina-*` dependencies**: These are sourced from `o1-labs/proof-systems` via git tag `0.3.0`. They are not published to crates.io. The audit team may wish to review the specific commit referenced in `Cargo.lock` for known issues. + +--- + +### 3. Audit Scope (In Scope) + +#### 3.1 Primary Scope: `frost-bluepallas` Crate (~1,024 lines) + +All source files in `frost-bluepallas/src/`: + +| File | Lines | Priority | Description | +|---|---|---|---| +| `frost-bluepallas/src/lib.rs` | 354 | **Critical** | Ciphersuite definition (`BluePallas`), `ChallengeMessage` trait, `PallasScalarField` (FROST `Field` impl), `PallasGroup` (FROST `Group` impl), hash function bindings (H1, H3, H4, H5, HDKG, HID), `pre_commitment_sign` and `pre_commitment_aggregate` hooks (y-coordinate parity enforcement), FROST round1/round2/aggregate re-exports. | +| `frost-bluepallas/src/hasher.rs` | 79 | **Critical** | `hash_to_scalar()` and `hash_to_array()` -- Poseidon hash utilities used by all FROST hash functions. `PallasHashElement` wrapper for the `Hashable` trait. Base-to-scalar field conversion. | +| `frost-bluepallas/src/keys.rs` | 192 | **High** | Key generation wrappers: `generate_with_dealer()`, `split()`, and DKG (`dkg::part1`, `part2`, `part3`). All delegate to `frost-core`. Type aliases for `SecretShare`, `SigningShare`, `KeyPackage`, `PublicKeyPackage`, etc. | +| `frost-bluepallas/src/negate.rs` | 191 | **Critical** | `NegateY` trait and implementations for `SigningNonces`, `SigningCommitments`, and `SigningPackage`. Nonce scalar negation and commitment point negation. This module enforces the Mina y-coordinate evenness invariant. | +| `frost-bluepallas/src/signing_utilities.rs` | 144 | **High** | Helper functions `sign_from_packages()`, `generate_signature_random()`, `generate_signature_from_sk()` -- end-to-end signing flows used in testing and potentially by consumers. | +| `frost-bluepallas/src/errors.rs` | 64 | **Low** | `BluePallasError` enum and convenience constructors. No cryptographic logic. | + +#### 3.2 Extended Scope: Bridging Code in `mina-tx` (~346 lines) + +These files are in scope because they implement the `ChallengeMessage` trait and perform security-critical type conversions between FROST and Mina types. A bug here directly compromises signature correctness. + +| File | Lines | Priority | Description | +|---|---|---|---| +| `mina-tx/src/pallas_message.rs` | 262 | **Critical** | `PallasMessage` struct, its `ChallengeMessage` impl (gated behind `frost-bluepallas-compat` feature), `message_hash()` function (Poseidon-based challenge computation), `translate_pk()`, `translate_sig()`, `translate_minask()`, serialization/deserialization. | +| `mina-tx/src/bluepallas_compat.rs` | 84 | **High** | `TryFrom>` for `Sig`, `TryFrom>` for `PubKeySer`, `TransactionEnvelope::to_pallas_message()`, `TransactionSignature::from_frost_signature()`. | + +#### 3.3 Security-Relevant Tests + +These test files validate cryptographic correctness and should be reviewed for coverage adequacy: + +| File | Lines | Purpose | +|---|---|---| +| `frost-bluepallas/tests/integration_tests.rs` | 289 | Runs `frost-core` generic ciphersuite tests against `BluePallas`. Includes DKG, share generation, test vector verification, error culprit checks. **Note**: 4 tests are `#[ignore]`d due to upstream frost-core bug. | +| `frost-bluepallas/tests/interoperability_tests.rs` | 17 | Verifies that FROST-generated signatures pass `mina-signer` verification. Runs 256 iterations with different seeds. | +| `frost-bluepallas/tests/recreation_tests.rs` | 178 | Verifies that all FROST types can be decomposed and recreated from their components. | +| `frost-bluepallas/tests/serialization_tests.rs` | 127 | Postcard binary serialization round-trip tests with snapshot assertions. | +| `frost-bluepallas/tests/serde_tests.rs` | 642 | JSON serialization/deserialization tests including invalid input rejection. | +| `frost-bluepallas/tests/common_traits_tests.rs` | 75 | Verifies `Clone`, `Eq`, `Debug` traits on all key types. | +| `frost-bluepallas/tests/deserialize.rs` | 140 | Regression tests for scalar/group element serialization, endianness, identity element rejection. | +| `frost-bluepallas/tests/serialization_pbt.rs` | 50 | Property-based tests (proptest) for scalar and group element round-trip serialization. | +| `frost-bluepallas/tests/helpers/mod.rs` | 30 | Test helper: `verify_signature()` -- converts FROST sig/pk to Mina types and verifies with `mina-signer`. | +| `frost-bluepallas/tests/helpers/samples.rs` | 169 | Generates fixed sample instances of all FROST types for deterministic testing. | +| `frost-bluepallas/tests/helpers/types.rs` | 13 | Type aliases for `BluePallas` suite. | + +**Test vector files**: +- `frost-bluepallas/tests/helpers/vectors.json` +- `frost-bluepallas/tests/helpers/vectors-big-identifier.json` + +**Snapshot files** (11 files in `frost-bluepallas/tests/snapshots/`): Insta snapshot files for postcard serialization regression tests. + +--- + +### 4. Out of Scope + +| Component | Reason for Exclusion | Conditions for Bringing In Scope | +|---|---|---| +| `mina-frost-client` crate | CLI/networking code. Does not implement cryptographic primitives. | If the audit budget allows or if the client is found to modify cryptographic data in transit. | +| `mina-tx` crate (excluding the two bridging files listed above) | Transaction types, ZkApp serialization, GraphQL formatting. Not part of the FROST cryptographic path. | If auditors discover that transaction serialization feeds into signing in unexpected ways. | +| `frost-core` (upstream dependency) | Separate open-source project maintained by the Zcash Foundation. Has its own audit history. | If the RC status or the known bug (issue #1015) raises concerns about protocol correctness. The audit team should note that this is an RC version. | +| `mina-curves`, `mina-hasher`, `mina-poseidon` (upstream dependencies) | Maintained by o1-labs. Curve and hash implementations. | If the audit team suspects bugs in Poseidon parameterisation or Pallas curve arithmetic. | +| CI/CD, build scripts, Docker configuration | Infrastructure with no cryptographic impact. | N/A | +| Examples (`frost-bluepallas/examples/`) | Demonstration code, not production. | N/A | +| Dead code / documentation-only files | No runtime impact. | N/A | + +--- + +### 5. Audit Objectives + +#### 5.1 Critical Security Goals + +1. **Signature unforgeability** -- A threshold signature produced by the FROST protocol must be a valid Mina Schnorr signature that cannot be forged without control of the threshold number of signing shares. +2. **Challenge computation correctness** -- The `ChallengeMessage::challenge()` implementation in `PallasMessage` must produce the same challenge scalar as `mina-signer` for identical inputs. Any deviation means signatures will either fail verification or, worse, pass verification with incorrect semantics. +3. **Y-coordinate parity enforcement** -- The `pre_commitment_sign` and `pre_commitment_aggregate` hooks must correctly enforce even y-coordinates on the group commitment. Incorrect parity handling produces signatures that are invalid on Mina. +4. **Nonce/commitment negation correctness** -- When the group commitment has an odd y-coordinate, all nonces and commitments must be negated consistently across all signers and the coordinator. Inconsistent negation is catastrophic. +5. **Serialization/deserialization correctness** -- Scalars, group elements, and signatures must be serialized in the correct byte order (little-endian for Pallas scalars) and format. Incorrect serialization silently produces invalid cryptographic objects. +6. **Key generation and DKG safety** -- Key splitting, share distribution, and DKG rounds must produce valid Shamir secret shares with correct verification commitments. +7. **Identity element rejection** -- The identity (zero) group element must never be accepted as a valid public key, commitment, or signature component. The `PallasGroup::serialize` and `PallasGroup::deserialize` functions explicitly check for this. +8. **No secret key leakage** -- Error messages, serialization paths, and debug output must not inadvertently expose secret key material. + +#### 5.2 Implementation Correctness Checks + +1. **Input validation** -- All points, scalars, nonces, commitments, and identifiers must be validated on input. Malformed inputs must be rejected with appropriate errors. +2. **Endianness consistency** -- Scalar serialization uses `ark-serialize`'s compressed format (little-endian). Verify this is consistent throughout the codebase and matches what `mina-signer` expects. +3. **Panic-freedom in library code** -- The `frost-bluepallas` crate is `#![no_std]` library code. There is one `expect()` call in `PallasScalarField::serialize()` at `lib.rs:86` (`"Serialization should not fail for valid scalars"`). Auditors should evaluate whether this invariant truly holds for all inputs or if a panic can be triggered. The `H2` function intentionally panics via `unimplemented!()` -- auditors should verify it cannot be called through any reachable code path. +4. **Hash domain separation** -- The FROST hash functions (H1, H3, H4, H5, HDKG, HID) use domain-separated Poseidon hashing with the prefix `"bluepallas"` concatenated with a purpose tag (`"rho"`, `"nonce"`, `"msg"`, `"com"`, `"dkg"`, `"id"`). Verify that these domain separators are collision-resistant and correctly bound to their context. +5. **Base-to-scalar field conversion** -- `hash_to_scalar()` in `hasher.rs` converts a Poseidon hash output (base field element) to a scalar field element via `Fq::from(hasher.hash(&wrap).into_bigint())`. This cross-field conversion must be verified for correctness (Pallas base field and scalar field have different moduli). +6. **`PallasMessage` deserialization fallback** -- In `pallas_message.rs:234`, the `ChallengeMessage::challenge()` impl uses `unwrap_or_else(|_| Self::from_raw_bytes_default(message))` when deserialization fails. This fallback creates a `PallasMessage` with `NetworkId::TESTNET` and `is_legacy: true`. Auditors should evaluate whether this fallback can be exploited to produce a different challenge hash for the same transaction, enabling signature malleability or domain confusion attacks. +7. **`translate_pk` safety** -- `PubKey::from_point_unsafe()` is used in `pallas_message.rs:141`. The "unsafe" in the name suggests reduced validation. Auditors should verify what checks are skipped. + +--- + +### 6. Specific Areas of Concern + +The following items deserve focused auditor attention: + +#### 6.1 Y-Coordinate Parity Logic (Critical) + +**Files**: `frost-bluepallas/src/lib.rs` (lines 216-268), `frost-bluepallas/src/negate.rs` + +The `pre_commitment_sign` and `pre_commitment_aggregate` hooks both: +1. Compute the group commitment via `compute_group_commitment()` +2. Convert to affine coordinates and check `y.into_bigint().is_even()` +3. If odd, negate all nonces/commitments + +**Key concerns**: +- Parity check (`is_even()`) is applied to the correct representation of the y-coordinate. +- Hooks are consistent for the same signing package. +- Negation in `NegateY` for `SigningNonces` (scalar negation) is consistent with the negation in `NegateY` for `SigningCommitments` (point negation). + +#### 6.2 Challenge Computation (Critical) + +**Files**: `mina-tx/src/pallas_message.rs` (lines 222-241) + +The `ChallengeMessage::challenge()` implementation: +1. Translates the FROST verifying key to a Mina `PubKey` +2. Extracts the x-coordinate of the nonce commitment `r` +3. Deserializes the message bytes back into a `PallasMessage` (with fallback) +4. Calls `message_hash()` which constructs a Poseidon hash over `(input || pub_key_x || pub_key_y || rx)` + +**Key concerns**: +- Does the field element ordering in `message_hash()` match the ordering used by `mina-signer`? +- Is domain string selection (legacy vs kimchi, mainnet vs testnet) correct and consistent? +- Can the `unwrap_or_else` fallback on deserialization failure lead to a challenge mismatch between signers? + +#### 6.3 Serialization Correctness (High) + +**Files**: `frost-bluepallas/src/lib.rs` (PallasScalarField, PallasGroup impls) + +- `PallasScalarField::serialize` uses `serialize_compressed` which produces little-endian output. Auditors should verify that (de)serialization is performed correctly with little-endian. +- `PallasGroup::Serialization` is `[u8; 96]` (3 base field elements for projective coordinates) but uses `serialize_compressed`. Auditors should verify that compressed Pallas points always fit in 96 bytes and that no truncation occurs. + +#### 6.4 `H2` Unimplemented Panic (Medium) + +`H2` in `lib.rs:184` panics with `unimplemented!()`. The comment says "H2 is not implemented on purpose, please see the `challenge` function." Auditors should verify that no code path in `frost-core` can reach `H2` when the `challenge()` method is overridden via the `Ciphersuite` trait. + +--- + +### 7. Deliverables + +We expect the following deliverables: + +1. **Detailed security report**, including: + - Executive summary + - Findings categorized by severity (Critical / High / Medium / Low / Informational) + - Proof-of-concept exploits or demonstrations where applicable + - Recommended remediations for each finding +2. **Code review comments** (inline annotations or GitHub PR comments) +3. **Verification of interoperability** -- confirmation that the FROST challenge computation matches `mina-signer` for all transaction types (legacy and kimchi/zkApp) + +--- + +### 8. Preferred Auditor Profile + +- Strong Rust cryptography audit experience +- Familiarity with the **FROST threshold Schnorr signature protocol** (RFC 9591 or the ZcashFoundation/frost implementation) +- Understanding of **Pallas/Pasta curves** (curve arithmetic, coordinate conventions, cofactor-1 behavior) +- Experience with **Poseidon hash functions** (sponge construction, domain separation) +- Familiarity with **Mina Protocol signature conventions** (y-coordinate evenness, legacy vs kimchi hashing modes, network ID domain strings) +- Experience auditing `no_std` Rust crates and `ark-*` ecosystem libraries + +--- + +### 9. Appendix: File Inventory + +#### Complete list of in-scope source files + +``` +frost-bluepallas/src/lib.rs (354 lines) -- Ciphersuite definition +frost-bluepallas/src/hasher.rs (79 lines) -- Poseidon hash utilities +frost-bluepallas/src/keys.rs (192 lines) -- Key generation / DKG wrappers +frost-bluepallas/src/negate.rs (191 lines) -- Y-coordinate negation +frost-bluepallas/src/signing_utilities.rs (144 lines) -- Signing helpers +frost-bluepallas/src/errors.rs (64 lines) -- Error types +mina-tx/src/pallas_message.rs (262 lines) -- ChallengeMessage impl +mina-tx/src/bluepallas_compat.rs (84 lines) -- FROST-to-Mina conversions +------------------------------------------------------------------------ +Total in-scope source: ~1,370 lines +``` + +#### In-scope test files + +``` +frost-bluepallas/tests/integration_tests.rs (289 lines) +frost-bluepallas/tests/interoperability_tests.rs (17 lines) +frost-bluepallas/tests/recreation_tests.rs (178 lines) +frost-bluepallas/tests/serialization_tests.rs (127 lines) +frost-bluepallas/tests/serde_tests.rs (642 lines) +frost-bluepallas/tests/common_traits_tests.rs (75 lines) +frost-bluepallas/tests/deserialize.rs (140 lines) +frost-bluepallas/tests/serialization_pbt.rs (50 lines) +frost-bluepallas/tests/helpers/mod.rs (30 lines) +frost-bluepallas/tests/helpers/samples.rs (169 lines) +frost-bluepallas/tests/helpers/types.rs (13 lines) +------------------------------------------------------------------------ +Total test code: ~1,730 lines +``` + +#### Test vector / snapshot files + +``` +frost-bluepallas/tests/helpers/vectors.json +frost-bluepallas/tests/helpers/vectors-big-identifier.json +frost-bluepallas/tests/snapshots/ (11 .snap files) +``` + +--- + +### 10. Suggested Audit Focus Order + +Ranked by risk: + +1. **Y-coordinate parity enforcement** (`lib.rs` pre_commitment hooks + `negate.rs`) +2. **Challenge computation** (`pallas_message.rs` ChallengeMessage impl + `message_hash()`) +3. **Field/group serialization** (`lib.rs` PallasScalarField/PallasGroup impls, `deserialize.rs` tests) +4. **FROST-to-Mina type conversions** (`bluepallas_compat.rs`, `translate_pk`, `translate_sig`) +5. **Hash domain separation** (`hasher.rs` + H1/H3/H4/H5/HDKG/HID in `lib.rs`) +6. **Signing utilities** (`signing_utilities.rs`) -- helper flows, verify they compose correctly +7. **Key generation and DKG wrappers** (`keys.rs`) + +### 11. Acknowledgements +This file was created with the help of Claude Opus 4.6 but has been manually reviewed and edited. + +Thank you to o1Labs et al. for all the help and feedback. + +❤️ Raspberry Devs diff --git a/frost-bluepallas/Cargo.toml b/frost-bluepallas/Cargo.toml index 47ee051..5b96e4f 100644 --- a/frost-bluepallas/Cargo.toml +++ b/frost-bluepallas/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "frost-bluepallas" -version = "0.2.0" +version = "1.0.0-alpha.0" edition = "2021" rust-version = "1.92" diff --git a/frost-bluepallas/src/lib.rs b/frost-bluepallas/src/lib.rs index 9c67ca0..c7b3cd8 100644 --- a/frost-bluepallas/src/lib.rs +++ b/frost-bluepallas/src/lib.rs @@ -1,18 +1,6 @@ -//! Information sharing: -//! defines tools for interfacing with the mina blockchain -//! (pretty sure that) the actual internals of the mina blockchain such as signature verification for -//! contracts with the `signature` permission happens through the OCaml implementation. -//! -//! There are 3 relevant crates in the proof-systems, `signer` which uses `hasher` and `curves` -//! Do not use the `pasta-curves` from crates.io. That's different implementation of pasta by the -//! ZCash Foundation (the won't match up nicely). The above 3 crates are not on crates.io and are -//! used directly from github. -//! -//! The goal is to replace the functionality of `signer` with the implementation of `frost-core` -//! found in this file! So the tests will generate a signature with our implementation and try to -//! verify it with the `signer`'s verify method. We do not use `signer` at all in our -//! implementation. We do use `hasher` which provides the hash functions used by `signer` and our -//! implementation of `frost-core`. +//! FROST BluePallas ciphersuite implementation for the Mina protocol. +//! This library uses the mina_hasher crate for the Poseidon hash function and mina_curves for the Pallas curve implementation, +//! and implements the FROST signature scheme as specified in the FROST paper and the Mina protocol specifications. #![warn(rustdoc::broken_intra_doc_links)] #![warn(rustdoc::bare_urls)] #![no_std] @@ -95,7 +83,7 @@ impl Field for PallasScalarField { Self::serialize(scalar) } - // Parse the canonical 32-byte big-endian form back into a field element, + // Parse the scalar from compressed form fn deserialize(buf: &Self::Serialization) -> Result { let scalar = ::deserialize_compressed(&buf[..]) .map_err(|_| FieldError::MalformedScalar)?; diff --git a/mina-frost-client/Cargo.toml b/mina-frost-client/Cargo.toml index 20b4b2e..da4641d 100644 --- a/mina-frost-client/Cargo.toml +++ b/mina-frost-client/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mina-frost-client" -version = "0.2.0" +version = "1.0.0-alpha.0" edition = "2021" rust-version = "1.92" default-run = "mina-frost-client" diff --git a/mina-tx/Cargo.toml b/mina-tx/Cargo.toml index b7b4929..011da4a 100644 --- a/mina-tx/Cargo.toml +++ b/mina-tx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mina-tx" -version = "0.1.0" +version = "1.0.0-alpha.0" edition = "2021" rust-version = "1.92"