Metadata-Version: 2.5
Name: secure-fl
Version: 26.8.105
Summary: Dual-Verifiable Framework for Federated Learning using Zero-Knowledge Proofs
Project-URL: Homepage, https://github.com/krishantt/secure-fl
Project-URL: Bug Reports, https://github.com/krishantt/secure-fl/issues
Project-URL: Source, https://github.com/krishantt/secure-fl
Project-URL: Documentation, https://github.com/krishantt/secure-fl/blob/main/README.md
Author-email: Krishant Timilsina <krishtimil@gmail.com>, Bindu Paudel <binduupaudel565@gmail.com>
Maintainer-email: Krishant Timilsina <krishtimil@gmail.com>, Bindu Paudel <binduupaudel565@gmail.com>
License: MIT
License-File: LICENSE
Keywords: cryptography,federated-learning,machine-learning,privacy,zero-knowledge-proofs,zk-snarks,zk-starks
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Security :: Cryptography
Requires-Python: >=3.12
Requires-Dist: click>=8.0.0
Requires-Dist: flwr>=1.5.0
Requires-Dist: numpy>=1.24.0
Requires-Dist: pysnark
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.0.0
Requires-Dist: textual>=6.2.1
Requires-Dist: torch>=2.8.0
Requires-Dist: torchvision>=0.23.0
Provides-Extra: benchmark
Requires-Dist: memory-profiler>=0.61.0; extra == 'benchmark'
Requires-Dist: pytest-benchmark>=5.2.3; extra == 'benchmark'
Provides-Extra: dev
Requires-Dist: mypy>=1.19.0; extra == 'dev'
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
Requires-Dist: psutil>=5.9.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest-xdist>=3.3.0; extra == 'dev'
Requires-Dist: pytest>=7.4.0; extra == 'dev'
Requires-Dist: ruff>=0.14.8; extra == 'dev'
Requires-Dist: types-psutil; extra == 'dev'
Requires-Dist: types-pyyaml; extra == 'dev'
Provides-Extra: medical
Requires-Dist: medmnist>=2.2.0; extra == 'medical'
Provides-Extra: viz
Requires-Dist: matplotlib>=3.7.0; extra == 'viz'
Description-Content-Type: text/markdown

# Secure FL: Zero-Knowledge Federated Learning

A dual-verifiable framework for federated learning using zero-knowledge proofs to ensure training integrity and aggregation correctness.

## Core Features

- **Dual ZKP Verification**: Client-side Groth16 zk-SNARK (Circom, transferable) + PySNARK local audit + Server-side Groth16 zk-SNARK (Circom/SnarkJS/rapidsnark) for aggregation
- **FedJSCM Aggregation**: Momentum-based federated optimization with auto-reinit on architecture changes
- **Dynamic Proof Rigor**: `StabilityMonitor` adapts proof complexity (`low`/`medium`/`high`/`adaptive`) based on training stability
- **Deterministic Hashing**: SHA-256 over the full parameter state dict — safe across Python/numpy versions
- **Attack Detection**: Norm-bound enforcement, hash verification, and ZKP circuit checks block poisoned updates

## Architecture

```
Client: Local Training → Circom Groth16 delta-bound proof (transferable) + PySNARK (local audit)
      → Send full state_dict + proof
Server: Receive updates → verify (hash + norm + Groth16) → FedJSCM aggregation
      → Groth16 SNARK proof → Distribute model
```

**Dual verification:**
1. **Clients** generate a transferable Groth16 proof of correct local training (delta norm bound), independently server-verifiable, plus a SHA-256 hash commitment over the full parameter state
2. **Server** generates a Groth16 proof of correct FedJSCM aggregation

## Quick Start

### Installation

```bash
# From source with uv (recommended)
git clone https://github.com/krishantt/secure-fl
cd secure-fl
uv sync --all-extras
```

### ZKP Prerequisites

```bash
# Automated setup (installs Rust, Circom, SnarkJS)
make setup-zkp

# Verify
uv run secure-fl setup --action check
```

### Run the attack defense demo

```bash
# Synthetic data — fastest, no download needed
uv run python demo/simulate_attack.py --dataset synthetic_small --num-rounds 6

# MNIST
uv run python demo/simulate_attack.py --dataset mnist

# CIFAR-10 with more malicious clients
uv run python demo/simulate_attack.py --dataset cifar10 --malicious 2 --proof-rigor low

# MedMNIST
uv run python demo/simulate_attack.py --dataset medmnist --malicious 2 --proof-rigor low
```

The TUI shows real-time ZKP verification, attack detection, and accuracy comparison (with ZKP vs projected without). Attackers appear in ~65% of rounds; the rest are all-honest.

### Start a federated learning session

```bash
# Server
uv run secure-fl-server --config experiments/config.yaml

# Client (in separate terminals)
uv run secure-fl-client --server localhost:8080 --dataset mnist --client-id client_1
uv run secure-fl-client --server localhost:8080 --dataset mnist --client-id client_2

# Or use Docker
docker compose up -d
```

## Python API

```python
from secure_fl.federation.client_runtime import create_client
from secure_fl.models.factory import model_fn

# Create a secure FL client
factory = model_fn("mnist")   # returns MNISTCNN factory
client = create_client(
    client_id="client_1",
    model_fn=factory,
    train_data=train_dataset,
    enable_zkp=True,
    proof_rigor="low",    # "low" | "medium" | "high"
    local_epochs=1,
    learning_rate=0.01,
)

# client.fit(parameters, config) → (updated_params, num_examples, metrics)
# metrics["zkp_proof"] contains the JSON proof string when enable_zkp=True
```

```python
from secure_fl.federation.aggregation import FedJSCMAggregator
from secure_fl.zkp import ServerProofManager

aggregator = FedJSCMAggregator(momentum=0.9, learning_rate=1.0)
server_pm  = ServerProofManager()

# Verify a client proof
ok = server_pm.verify_client_proof(proof_json, updated_params, global_params)

# Aggregate verified updates
global_params = aggregator.aggregate(
    client_updates=verified_updates,
    client_weights=normalized_weights,
    server_round=rnd,
    global_params=global_params,
)
```

## Configuration

```yaml
# experiments/config.yaml
server:
  host: "localhost"
  port: 8080
  num_rounds: 10

strategy:
  min_fit_clients: 2
  fraction_fit: 1.0
  momentum: 0.9

zkp:
  enable_zkp: true
  proof_rigor: "high"   # "low" | "medium" | "high" | "adaptive"
  quantize_weights: true
  quantization_bits: 8
```

## Models and Datasets

| Dataset | Model | Notes |
|---|---|---|
| `mnist` | `MNISTCNN` | 2-layer CNN, fast |
| `cifar10` | `ResNet18Model` | Full ResNet18 |
| `medmnist` | `ResNet18Model` | Medical imaging |
| `synthetic_small` | `SimpleModel` | MLP, no download needed |

```python
from secure_fl.models.factory import model_fn
factory = model_fn("cifar10")   # returns callable → ResNet18Model instance
model = factory()
```

```python
from secure_fl.data.benchmark_data import load_dataset, partition_dataset, subsample_dataset

train, test = load_dataset("mnist", seed=42)
train = subsample_dataset(train, max_samples=2000, seed=42)
client_subsets = partition_dataset(train, n=6, iid=False, seed=42)  # Dirichlet non-IID
```

## Technical Details

### FedJSCM Aggregation

```
m^{t+1} = γ·m^t + (1−γ)·Δ
w^{t+1} = w^t + η·m^{t+1}
```
where `Δ = weighted_avg(client_updates) − global_params`. Momentum is reinitialized automatically if the model architecture changes between rounds. Integer-dtype buffers (e.g. BatchNorm `num_batches_tracked`) are preserved through aggregation.

### ZKP Proof Schema

Each client proof is a JSON object containing:
- `initial_hash`, `updated_hash`, `delta_hash` — SHA-256 commitments over the **full parameter state dict** (weights, biases, and BatchNorm running stats) — catches any silent parameter substitution, including in buffers not covered by the norm check
- `delta_norm_l2`, `max_delta_norm_l2` — L2 norm bound enforcement over **learnable parameters only** (BN buffers excluded — their early-round deltas are large and not adversarially controlled)
- `pysnark` — PySNARK circuit result (`delta_bound_proof`), a secondary in-process local audit
- `circom_proof` — transferable Groth16 proof (`{"proof": ..., "public": ...}`) when Circom/SnarkJS tools are available client-side; independently verifiable by the server via the same mechanism, without re-running any client code

Server verification order: proof present → valid JSON → hash matches (full state dict) → delta hash matches (full state dict) → norm within bound (learnable-only) → PySNARK circuit ok → `circom_proof` verified (if present).

### Parameter Representations

Two lists exist and must not be mixed:

| | Source | Contents | Used for |
|---|---|---|---|
| Full state dict | `model.state_dict().values()` | weights + biases + BN buffers | hash commitment, aggregation, eval |
| Learnable only | `model.parameters()` | weights + biases only | norm-bound enforcement, ZKP circuits |

`fit()` returns full state dict. Mixing the two representations with `zip` causes shape-mismatch errors on BN models (ResNet18, MNISTCNN) — see `CLAUDE.md`'s "Parameter list conventions" section for the full detail.

### Dynamic Proof Rigor

`StabilityMonitor` tracks gradient variance and convergence across rounds and adapts `ProofRigor`:
- High variance / early rounds → `high` (more proof coverage)
- Stable training → `low` (faster proofs)
- `adaptive` mode enables automatic adjustment each round

## Experiments & Benchmarks

`experiments/full_benchmark.py` is the single source of truth for paper-quality
results — k-fold accuracy, overhead, and attack resilience in one run.

```bash
# Fast smoke run
uv run python experiments/full_benchmark.py --quick

# Paper-quality run (real ZKP proofs enforced, resumable — skips
# already-completed config JSONs unless --force)
uv run python experiments/full_benchmark.py \
  --datasets mnist --k-folds 5 --num-clients 20 --clients-per-round 5 \
  --num-rounds 100 --require-real-proofs --force \
  --output-dir results/full_benchmark
```

Outputs per-dataset, per-config k-fold JSONs under `--output-dir`
(`aggregate` summary stats + `folds[i].rounds[j]` per-round detail). See
`CLAUDE.md`'s "Benchmark results status" section for which result set is
currently canonical and why.

## Repository Structure

```
secure-fl/
├── src/secure_fl/
│   ├── core/            # Types, config (thread-safe singleton), exceptions
│   ├── federation/      # SecureFlowerClient, SecureFlowerServer, FedJSCMAggregator,
│   │                    #   StabilityMonitor, client_utils, helpers
│   ├── zkp/             # ClientProofManager, ServerProofManager, quantization
│   ├── models/          # SimpleModel, MNISTCNN, ResNet18Model, factory
│   ├── data/            # benchmark_data: load/partition/subsample datasets
│   ├── cli/             # Click CLI: secure-fl, secure-fl-server, secure-fl-client, secure-fl-setup
│   ├── proofs/
│   │   ├── client_circuits/  # PySNARK delta_bound_proof circuit
│   │   └── server/           # Circom aggregation SNARK circuits
│   └── utils/           # helpers (compute_hash, compute_parameter_norm), logging
├── demo/
│   └── simulate_attack.py    # Textual TUI: real ZKP attack detection demo
├── experiments/
│   ├── full_benchmark.py     # Canonical paper benchmark (accuracy + overhead + attacks)
│   ├── demo.py                 # CLI demo entry point
│   └── config.yaml
└── tests/
    ├── unit/            # No external dependencies (fast)
    └── integration/     # Requires Circom + SnarkJS
```

## Development

```bash
make dev          # Full setup (uv sync + ZKP tools)
make lint         # Ruff lint
make format       # Ruff format  ← run before every commit
make type-check   # mypy (strict)
make test         # Full suite
make test-quick   # Fast, --exitfirst
make test-cov     # With coverage (threshold: 30%)
```

## Known Limitations

- Server ZKP (Circom/SnarkJS) requires `make setup-zkp`; subprocess calls time out after 300 s
- Client-side transferable Groth16 proof falls back gracefully to PySNARK-only when Circom/SnarkJS tools are unavailable client-side
- Coverage threshold is 30% — being raised incrementally
- Norm-bound enforcement does not defend against within-bound poisoning (adversarial updates crafted to satisfy the norm constraint) — see `docs/paper/main.tex` §Limitations

## License

MIT License — see [LICENSE](LICENSE) for details.

## Citation

```bibtex
@misc{timilsina2026securefl,
  title={Secure-FL: Zero-Knowledge Proofs for Dual-Verifiable Federated Learning},
  author={Timilsina, Krishant and Paudel, Bindu and Timilsina, Arun Kumar},
  year={2026},
  url={https://github.com/krishantt/secure-fl},
  note={Preprint}
}
```

## Acknowledgments

- [Flower](https://flower.dev) — federated learning infrastructure
- [Circom](https://github.com/iden3/circom) / [SnarkJS](https://github.com/iden3/snarkjs) — zk-SNARK toolchain
- [PySNARK](https://github.com/meilof/pysnark) — Python zk-SNARK (Groth16) library
