secure-fl 26.8.85__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- secure_fl-26.8.85/.gitignore +216 -0
- secure_fl-26.8.85/LICENSE +21 -0
- secure_fl-26.8.85/PKG-INFO +344 -0
- secure_fl-26.8.85/README.md +291 -0
- secure_fl-26.8.85/docs/paper/README.md +13 -0
- secure_fl-26.8.85/experiments/README.md +64 -0
- secure_fl-26.8.85/pyproject.toml +252 -0
- secure_fl-26.8.85/src/secure_fl/__init__.py +452 -0
- secure_fl-26.8.85/src/secure_fl/__main__.py +11 -0
- secure_fl-26.8.85/src/secure_fl/benchmarks/__init__.py +42 -0
- secure_fl-26.8.85/src/secure_fl/benchmarks/attacks.py +444 -0
- secure_fl-26.8.85/src/secure_fl/benchmarks/defaults.py +15 -0
- secure_fl-26.8.85/src/secure_fl/benchmarks/execution.py +573 -0
- secure_fl-26.8.85/src/secure_fl/benchmarks/pipeline.py +104 -0
- secure_fl-26.8.85/src/secure_fl/benchmarks/reporting.py +185 -0
- secure_fl-26.8.85/src/secure_fl/cli/__init__.py +70 -0
- secure_fl-26.8.85/src/secure_fl/cli/__main__.py +6 -0
- secure_fl-26.8.85/src/secure_fl/cli/handlers.py +240 -0
- secure_fl-26.8.85/src/secure_fl/cli/main.py +253 -0
- secure_fl-26.8.85/src/secure_fl/cli/setup.py +143 -0
- secure_fl-26.8.85/src/secure_fl/cli/zkp_check.py +93 -0
- secure_fl-26.8.85/src/secure_fl/core/__init__.py +137 -0
- secure_fl-26.8.85/src/secure_fl/core/_version.py +44 -0
- secure_fl-26.8.85/src/secure_fl/core/config.py +366 -0
- secure_fl-26.8.85/src/secure_fl/core/exceptions.py +509 -0
- secure_fl-26.8.85/src/secure_fl/core/types.py +634 -0
- secure_fl-26.8.85/src/secure_fl/data/__init__.py +23 -0
- secure_fl-26.8.85/src/secure_fl/data/benchmark_data.py +300 -0
- secure_fl-26.8.85/src/secure_fl/federation/__init__.py +33 -0
- secure_fl-26.8.85/src/secure_fl/federation/aggregation.py +466 -0
- secure_fl-26.8.85/src/secure_fl/federation/client.py +638 -0
- secure_fl-26.8.85/src/secure_fl/federation/client_runtime.py +113 -0
- secure_fl-26.8.85/src/secure_fl/federation/client_utils.py +138 -0
- secure_fl-26.8.85/src/secure_fl/federation/server.py +470 -0
- secure_fl-26.8.85/src/secure_fl/federation/server_runtime.py +103 -0
- secure_fl-26.8.85/src/secure_fl/federation/stability_monitor.py +484 -0
- secure_fl-26.8.85/src/secure_fl/models/__init__.py +85 -0
- secure_fl-26.8.85/src/secure_fl/models/factory.py +55 -0
- secure_fl-26.8.85/src/secure_fl/models/mnist_cnn.py +30 -0
- secure_fl-26.8.85/src/secure_fl/models/simple.py +94 -0
- secure_fl-26.8.85/src/secure_fl/proofs/__init__.py +0 -0
- secure_fl-26.8.85/src/secure_fl/proofs/client/__init__.py +0 -0
- secure_fl-26.8.85/src/secure_fl/proofs/client/delta_bound.circom +56 -0
- secure_fl-26.8.85/src/secure_fl/proofs/client_circuits/__init__.py +0 -0
- secure_fl-26.8.85/src/secure_fl/proofs/client_circuits/delta_bound.py +36 -0
- secure_fl-26.8.85/src/secure_fl/proofs/server/aggregation.circom +55 -0
- secure_fl-26.8.85/src/secure_fl/proofs/server/pot12_final.ptau +0 -0
- secure_fl-26.8.85/src/secure_fl/proofs/server/pot13_final.ptau +0 -0
- secure_fl-26.8.85/src/secure_fl/proofs/server/test.txt +3 -0
- secure_fl-26.8.85/src/secure_fl/py.typed +0 -0
- secure_fl-26.8.85/src/secure_fl/utils/__init__.py +59 -0
- secure_fl-26.8.85/src/secure_fl/utils/helpers.py +286 -0
- secure_fl-26.8.85/src/secure_fl/utils/logging_config.py +433 -0
- secure_fl-26.8.85/src/secure_fl/zkp/__init__.py +39 -0
- secure_fl-26.8.85/src/secure_fl/zkp/base.py +33 -0
- secure_fl-26.8.85/src/secure_fl/zkp/client_proof_manager.py +504 -0
- secure_fl-26.8.85/src/secure_fl/zkp/client_setup.py +205 -0
- secure_fl-26.8.85/src/secure_fl/zkp/client_snark_io.py +164 -0
- secure_fl-26.8.85/src/secure_fl/zkp/proof_manager.py +11 -0
- secure_fl-26.8.85/src/secure_fl/zkp/proof_schema.py +69 -0
- secure_fl-26.8.85/src/secure_fl/zkp/quantization.py +703 -0
- secure_fl-26.8.85/src/secure_fl/zkp/server_proof_manager.py +627 -0
- secure_fl-26.8.85/src/secure_fl/zkp/server_setup.py +127 -0
- secure_fl-26.8.85/src/secure_fl/zkp/server_snark_io.py +142 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# ===========================
|
|
2
|
+
# Python
|
|
3
|
+
# ===========================
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.py[cod]
|
|
6
|
+
*$py.class
|
|
7
|
+
*.so
|
|
8
|
+
.Python
|
|
9
|
+
build/
|
|
10
|
+
develop-eggs/
|
|
11
|
+
dist/
|
|
12
|
+
downloads/
|
|
13
|
+
eggs/
|
|
14
|
+
.eggs/
|
|
15
|
+
lib/
|
|
16
|
+
lib64/
|
|
17
|
+
parts/
|
|
18
|
+
sdist/
|
|
19
|
+
var/
|
|
20
|
+
wheels/
|
|
21
|
+
pip-wheel-metadata/
|
|
22
|
+
share/python-wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
MANIFEST
|
|
27
|
+
|
|
28
|
+
# PyInstaller
|
|
29
|
+
*.manifest
|
|
30
|
+
*.spec
|
|
31
|
+
|
|
32
|
+
# Unit test / coverage reports
|
|
33
|
+
htmlcov/
|
|
34
|
+
.tox/
|
|
35
|
+
.nox/
|
|
36
|
+
.coverage
|
|
37
|
+
.coverage.*
|
|
38
|
+
.cache
|
|
39
|
+
nosetests.xml
|
|
40
|
+
coverage.xml
|
|
41
|
+
*.cover
|
|
42
|
+
*.py,cover
|
|
43
|
+
.hypothesis/
|
|
44
|
+
.pytest_cache/
|
|
45
|
+
|
|
46
|
+
# Virtual environments
|
|
47
|
+
.env
|
|
48
|
+
.venv
|
|
49
|
+
env/
|
|
50
|
+
venv/
|
|
51
|
+
ENV/
|
|
52
|
+
env.bak/
|
|
53
|
+
venv.bak/
|
|
54
|
+
|
|
55
|
+
# IDEs and editors
|
|
56
|
+
.vscode/
|
|
57
|
+
.idea/
|
|
58
|
+
*.swp
|
|
59
|
+
*.swo
|
|
60
|
+
*.swn
|
|
61
|
+
*.vim
|
|
62
|
+
.spyderproject
|
|
63
|
+
.spyproject
|
|
64
|
+
.ropeproject
|
|
65
|
+
|
|
66
|
+
# Jupyter Notebook
|
|
67
|
+
.ipynb_checkpoints
|
|
68
|
+
|
|
69
|
+
# pyenv
|
|
70
|
+
.python-version
|
|
71
|
+
|
|
72
|
+
# pipenv
|
|
73
|
+
Pipfile.lock
|
|
74
|
+
|
|
75
|
+
# PEP 582
|
|
76
|
+
__pypackages__/
|
|
77
|
+
|
|
78
|
+
# Celery stuff
|
|
79
|
+
celerybeat-schedule
|
|
80
|
+
celerybeat.pid
|
|
81
|
+
|
|
82
|
+
# SageMath parsed files
|
|
83
|
+
*.sage.py
|
|
84
|
+
|
|
85
|
+
# Spyder project settings
|
|
86
|
+
.spyderproject
|
|
87
|
+
.spyproject
|
|
88
|
+
|
|
89
|
+
# Rope project settings
|
|
90
|
+
.ropeproject
|
|
91
|
+
|
|
92
|
+
# mypy
|
|
93
|
+
.mypy_cache/
|
|
94
|
+
.dmypy.json
|
|
95
|
+
dmypy.json
|
|
96
|
+
|
|
97
|
+
# Pyre type checker
|
|
98
|
+
.pyre/
|
|
99
|
+
|
|
100
|
+
# ===========================
|
|
101
|
+
# Secure FL Specific
|
|
102
|
+
# ===========================
|
|
103
|
+
# Runtime logs and results
|
|
104
|
+
logs/
|
|
105
|
+
results/*
|
|
106
|
+
!results/full_benchmark/
|
|
107
|
+
!results/full_benchmark_50/
|
|
108
|
+
!results/full_benchmark_v2/
|
|
109
|
+
results_bak/
|
|
110
|
+
*.log
|
|
111
|
+
|
|
112
|
+
# Stray local artifacts (empty dirs from mock leaks / tool runs, not code-created)
|
|
113
|
+
/MagicMock/
|
|
114
|
+
/.benchmarks/
|
|
115
|
+
|
|
116
|
+
# Data directories
|
|
117
|
+
datasets/
|
|
118
|
+
|
|
119
|
+
# ZKP artifacts
|
|
120
|
+
*.r1cs
|
|
121
|
+
*.wtns
|
|
122
|
+
# *.circom
|
|
123
|
+
output/
|
|
124
|
+
graphify-out/
|
|
125
|
+
witness/
|
|
126
|
+
circuit.wasm
|
|
127
|
+
circuit_js/
|
|
128
|
+
proofs/compiled/
|
|
129
|
+
*.proof
|
|
130
|
+
*.vkey
|
|
131
|
+
*.zkey
|
|
132
|
+
|
|
133
|
+
# pysnark debug/trace output files
|
|
134
|
+
pysnark_eqs
|
|
135
|
+
pysnark_eqs_main
|
|
136
|
+
pysnark_schedule
|
|
137
|
+
pysnark_values
|
|
138
|
+
pysnark_wires
|
|
139
|
+
|
|
140
|
+
# Temporary files
|
|
141
|
+
temp/
|
|
142
|
+
tmp/
|
|
143
|
+
.temp/
|
|
144
|
+
.tmp/
|
|
145
|
+
|
|
146
|
+
# Build artifacts
|
|
147
|
+
*.compiled.json
|
|
148
|
+
|
|
149
|
+
# Package management
|
|
150
|
+
.pdm-python
|
|
151
|
+
.pdm-build/
|
|
152
|
+
#uv.lock
|
|
153
|
+
|
|
154
|
+
# ===========================
|
|
155
|
+
# System Files
|
|
156
|
+
# ===========================
|
|
157
|
+
.DS_Store
|
|
158
|
+
.DS_Store?
|
|
159
|
+
._*
|
|
160
|
+
.Spotlight-V100
|
|
161
|
+
.Trashes
|
|
162
|
+
ehthumbs.db
|
|
163
|
+
Thumbs.db
|
|
164
|
+
|
|
165
|
+
# ===========================
|
|
166
|
+
# LaTeX (for documentation)
|
|
167
|
+
# ===========================
|
|
168
|
+
*.aux
|
|
169
|
+
*.bbl
|
|
170
|
+
*.blg
|
|
171
|
+
*.fls
|
|
172
|
+
*.fdb_latexmk
|
|
173
|
+
*.synctex.gz
|
|
174
|
+
*.out
|
|
175
|
+
*.toc
|
|
176
|
+
*.lof
|
|
177
|
+
*.lot
|
|
178
|
+
*.log
|
|
179
|
+
*.pdf
|
|
180
|
+
*.dvi
|
|
181
|
+
*.ps
|
|
182
|
+
# Allow committed figure PDFs in the report
|
|
183
|
+
!docs/final-defense/report/images/figures/*.pdf
|
|
184
|
+
!docs/final-defense/report/diagrams/*.pdf
|
|
185
|
+
!docs/research\ paper/figures/*.pdf
|
|
186
|
+
!docs/paper/figures/*.pdf
|
|
187
|
+
|
|
188
|
+
# ===========================
|
|
189
|
+
# Development Tools
|
|
190
|
+
# ===========================
|
|
191
|
+
# Ruff cache
|
|
192
|
+
.ruff_cache/
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
# Docker
|
|
196
|
+
.dockerignore
|
|
197
|
+
|
|
198
|
+
# GitHub Actions local testing
|
|
199
|
+
.secrets
|
|
200
|
+
/tmp/artifacts/
|
|
201
|
+
*-event.json
|
|
202
|
+
.act/
|
|
203
|
+
|
|
204
|
+
# Node.js (for ZKP tools)
|
|
205
|
+
node_modules/
|
|
206
|
+
npm-debug.log*
|
|
207
|
+
yarn-debug.log*
|
|
208
|
+
yarn-error.log*
|
|
209
|
+
|
|
210
|
+
# Environment variables
|
|
211
|
+
.env*
|
|
212
|
+
!.env.example
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
experiments/data/
|
|
216
|
+
*.pptx
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Krishant Timilsina, Bindu Paudel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: secure-fl
|
|
3
|
+
Version: 26.8.85
|
|
4
|
+
Summary: Dual-Verifiable Framework for Federated Learning using Zero-Knowledge Proofs
|
|
5
|
+
Project-URL: Homepage, https://github.com/krishantt/secure-fl
|
|
6
|
+
Project-URL: Bug Reports, https://github.com/krishantt/secure-fl/issues
|
|
7
|
+
Project-URL: Source, https://github.com/krishantt/secure-fl
|
|
8
|
+
Project-URL: Documentation, https://github.com/krishantt/secure-fl/blob/main/README.md
|
|
9
|
+
Author-email: Krishant Timilsina <krishtimil@gmail.com>, Bindu Paudel <binduupaudel565@gmail.com>
|
|
10
|
+
Maintainer-email: Krishant Timilsina <krishtimil@gmail.com>, Bindu Paudel <binduupaudel565@gmail.com>
|
|
11
|
+
License: MIT
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: cryptography,federated-learning,machine-learning,privacy,zero-knowledge-proofs,zk-snarks,zk-starks
|
|
14
|
+
Classifier: Development Status :: 3 - Alpha
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: Science/Research
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Security :: Cryptography
|
|
23
|
+
Requires-Python: >=3.12
|
|
24
|
+
Requires-Dist: click>=8.0.0
|
|
25
|
+
Requires-Dist: flwr>=1.5.0
|
|
26
|
+
Requires-Dist: numpy>=1.24.0
|
|
27
|
+
Requires-Dist: pysnark
|
|
28
|
+
Requires-Dist: pyyaml>=6.0
|
|
29
|
+
Requires-Dist: rich>=13.0.0
|
|
30
|
+
Requires-Dist: textual>=6.2.1
|
|
31
|
+
Requires-Dist: torch>=2.8.0
|
|
32
|
+
Requires-Dist: torchvision>=0.23.0
|
|
33
|
+
Provides-Extra: benchmark
|
|
34
|
+
Requires-Dist: memory-profiler>=0.61.0; extra == 'benchmark'
|
|
35
|
+
Requires-Dist: pytest-benchmark>=5.2.3; extra == 'benchmark'
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: mypy>=1.19.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: psutil>=5.9.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: pytest-xdist>=3.3.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: pytest>=7.4.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: ruff>=0.14.8; extra == 'dev'
|
|
44
|
+
Requires-Dist: types-psutil; extra == 'dev'
|
|
45
|
+
Requires-Dist: types-pyyaml; extra == 'dev'
|
|
46
|
+
Provides-Extra: medical
|
|
47
|
+
Requires-Dist: medmnist>=2.2.0; extra == 'medical'
|
|
48
|
+
Provides-Extra: viz
|
|
49
|
+
Requires-Dist: matplotlib>=3.7.0; extra == 'viz'
|
|
50
|
+
Requires-Dist: plotly>=5.0.0; extra == 'viz'
|
|
51
|
+
Requires-Dist: seaborn>=0.12.0; extra == 'viz'
|
|
52
|
+
Description-Content-Type: text/markdown
|
|
53
|
+
|
|
54
|
+
# Secure FL: Zero-Knowledge Federated Learning
|
|
55
|
+
|
|
56
|
+
A dual-verifiable framework for federated learning using zero-knowledge proofs to ensure training integrity and aggregation correctness.
|
|
57
|
+
|
|
58
|
+
## Core Features
|
|
59
|
+
|
|
60
|
+
- **Dual ZKP Verification**: Client-side Groth16 zk-SNARK (Circom, transferable) + PySNARK local audit + Server-side Groth16 zk-SNARK (Circom/SnarkJS/rapidsnark) for aggregation
|
|
61
|
+
- **FedJSCM Aggregation**: Momentum-based federated optimization with auto-reinit on architecture changes
|
|
62
|
+
- **Dynamic Proof Rigor**: `StabilityMonitor` adapts proof complexity (`low`/`medium`/`high`/`adaptive`) based on training stability
|
|
63
|
+
- **Deterministic Hashing**: SHA-256 over the full parameter state dict — safe across Python/numpy versions
|
|
64
|
+
- **Attack Detection**: Norm-bound enforcement, hash verification, and ZKP circuit checks block poisoned updates
|
|
65
|
+
|
|
66
|
+
## Architecture
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
Client: Local Training → Circom Groth16 delta-bound proof (transferable) + PySNARK (local audit)
|
|
70
|
+
→ Send full state_dict + proof
|
|
71
|
+
Server: Receive updates → verify (hash + norm + Groth16) → FedJSCM aggregation
|
|
72
|
+
→ Groth16 SNARK proof → Distribute model
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Dual verification:**
|
|
76
|
+
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
|
|
77
|
+
2. **Server** generates a Groth16 proof of correct FedJSCM aggregation
|
|
78
|
+
|
|
79
|
+
## Quick Start
|
|
80
|
+
|
|
81
|
+
### Installation
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
# From source with uv (recommended)
|
|
85
|
+
git clone https://github.com/krishantt/secure-fl
|
|
86
|
+
cd secure-fl
|
|
87
|
+
uv sync --all-extras
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### ZKP Prerequisites
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Automated setup (installs Rust, Circom, SnarkJS)
|
|
94
|
+
make setup-zkp
|
|
95
|
+
|
|
96
|
+
# Verify
|
|
97
|
+
uv run secure-fl setup --action check
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Run the attack defense demo
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
# Synthetic data — fastest, no download needed
|
|
104
|
+
uv run python demo/simulate_attack.py --dataset synthetic_small --num-rounds 6
|
|
105
|
+
|
|
106
|
+
# MNIST
|
|
107
|
+
uv run python demo/simulate_attack.py --dataset mnist
|
|
108
|
+
|
|
109
|
+
# CIFAR-10 with more malicious clients
|
|
110
|
+
uv run python demo/simulate_attack.py --dataset cifar10 --malicious 2 --proof-rigor low
|
|
111
|
+
|
|
112
|
+
# MedMNIST
|
|
113
|
+
uv run python demo/simulate_attack.py --dataset medmnist --malicious 2 --proof-rigor low
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
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.
|
|
117
|
+
|
|
118
|
+
### Start a federated learning session
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# Server
|
|
122
|
+
uv run secure-fl-server --config experiments/config.yaml
|
|
123
|
+
|
|
124
|
+
# Client (in separate terminals)
|
|
125
|
+
uv run secure-fl-client --server localhost:8080 --dataset mnist --client-id client_1
|
|
126
|
+
uv run secure-fl-client --server localhost:8080 --dataset mnist --client-id client_2
|
|
127
|
+
|
|
128
|
+
# Or use Docker
|
|
129
|
+
docker compose up -d
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Python API
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
from secure_fl.federation.client_runtime import create_client
|
|
136
|
+
from secure_fl.models.factory import model_fn
|
|
137
|
+
|
|
138
|
+
# Create a secure FL client
|
|
139
|
+
factory = model_fn("mnist") # returns MNISTCNN factory
|
|
140
|
+
client = create_client(
|
|
141
|
+
client_id="client_1",
|
|
142
|
+
model_fn=factory,
|
|
143
|
+
train_data=train_dataset,
|
|
144
|
+
enable_zkp=True,
|
|
145
|
+
proof_rigor="low", # "low" | "medium" | "high"
|
|
146
|
+
local_epochs=1,
|
|
147
|
+
learning_rate=0.01,
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
# client.fit(parameters, config) → (updated_params, num_examples, metrics)
|
|
151
|
+
# metrics["zkp_proof"] contains the JSON proof string when enable_zkp=True
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from secure_fl.federation.aggregation import FedJSCMAggregator
|
|
156
|
+
from secure_fl.zkp.proof_manager import ServerProofManager
|
|
157
|
+
|
|
158
|
+
aggregator = FedJSCMAggregator(momentum=0.9, learning_rate=1.0)
|
|
159
|
+
server_pm = ServerProofManager()
|
|
160
|
+
|
|
161
|
+
# Verify a client proof
|
|
162
|
+
ok = server_pm.verify_client_proof(proof_json, updated_params, global_params)
|
|
163
|
+
|
|
164
|
+
# Aggregate verified updates
|
|
165
|
+
global_params = aggregator.aggregate(
|
|
166
|
+
client_updates=verified_updates,
|
|
167
|
+
client_weights=normalized_weights,
|
|
168
|
+
server_round=rnd,
|
|
169
|
+
global_params=global_params,
|
|
170
|
+
)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Configuration
|
|
174
|
+
|
|
175
|
+
```yaml
|
|
176
|
+
# experiments/config.yaml
|
|
177
|
+
server:
|
|
178
|
+
host: "localhost"
|
|
179
|
+
port: 8080
|
|
180
|
+
num_rounds: 10
|
|
181
|
+
|
|
182
|
+
strategy:
|
|
183
|
+
min_fit_clients: 2
|
|
184
|
+
fraction_fit: 1.0
|
|
185
|
+
momentum: 0.9
|
|
186
|
+
|
|
187
|
+
zkp:
|
|
188
|
+
enable_zkp: true
|
|
189
|
+
proof_rigor: "high" # "low" | "medium" | "high" | "adaptive"
|
|
190
|
+
quantize_weights: true
|
|
191
|
+
quantization_bits: 8
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Models and Datasets
|
|
195
|
+
|
|
196
|
+
| Dataset | Model | Notes |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| `mnist` | `MNISTCNN` | 2-layer CNN, fast |
|
|
199
|
+
| `cifar10` | `ResNet18Model` | Full ResNet18 |
|
|
200
|
+
| `medmnist` | `ResNet18Model` | Medical imaging |
|
|
201
|
+
| `synthetic_small` | `SimpleModel` | MLP, no download needed |
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
from secure_fl.models.factory import model_fn
|
|
205
|
+
factory = model_fn("cifar10") # returns callable → ResNet18Model instance
|
|
206
|
+
model = factory()
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
```python
|
|
210
|
+
from secure_fl.data.benchmark_data import load_dataset, partition_dataset, subsample_dataset
|
|
211
|
+
|
|
212
|
+
train, test = load_dataset("mnist", seed=42)
|
|
213
|
+
train = subsample_dataset(train, max_samples=2000, seed=42)
|
|
214
|
+
client_subsets = partition_dataset(train, n=6, iid=False, seed=42) # Dirichlet non-IID
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Technical Details
|
|
218
|
+
|
|
219
|
+
### FedJSCM Aggregation
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
m^{t+1} = γ·m^t + (1−γ)·Δ
|
|
223
|
+
w^{t+1} = w^t + η·m^{t+1}
|
|
224
|
+
```
|
|
225
|
+
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.
|
|
226
|
+
|
|
227
|
+
### ZKP Proof Schema
|
|
228
|
+
|
|
229
|
+
Each client proof is a JSON object containing:
|
|
230
|
+
- `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
|
|
231
|
+
- `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)
|
|
232
|
+
- `pysnark` — PySNARK circuit result (`delta_bound_proof`), a secondary in-process local audit
|
|
233
|
+
- `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
|
|
234
|
+
|
|
235
|
+
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).
|
|
236
|
+
|
|
237
|
+
### Parameter Representations
|
|
238
|
+
|
|
239
|
+
Two lists exist and must not be mixed:
|
|
240
|
+
|
|
241
|
+
| | Source | Contents | Used for |
|
|
242
|
+
|---|---|---|---|
|
|
243
|
+
| Full state dict | `model.state_dict().values()` | weights + biases + BN buffers | hash commitment, aggregation, eval |
|
|
244
|
+
| Learnable only | `model.parameters()` | weights + biases only | norm-bound enforcement, ZKP circuits |
|
|
245
|
+
|
|
246
|
+
`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.
|
|
247
|
+
|
|
248
|
+
### Dynamic Proof Rigor
|
|
249
|
+
|
|
250
|
+
`StabilityMonitor` tracks gradient variance and convergence across rounds and adapts `ProofRigor`:
|
|
251
|
+
- High variance / early rounds → `high` (more proof coverage)
|
|
252
|
+
- Stable training → `low` (faster proofs)
|
|
253
|
+
- `adaptive` mode enables automatic adjustment each round
|
|
254
|
+
|
|
255
|
+
## Experiments & Benchmarks
|
|
256
|
+
|
|
257
|
+
`experiments/full_benchmark.py` is the single source of truth for paper-quality
|
|
258
|
+
results — k-fold accuracy, overhead, and attack resilience in one run.
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
# Fast smoke run
|
|
262
|
+
uv run python experiments/full_benchmark.py --quick
|
|
263
|
+
|
|
264
|
+
# Paper-quality run (real ZKP proofs enforced, resumable — skips
|
|
265
|
+
# already-completed config JSONs unless --force)
|
|
266
|
+
uv run python experiments/full_benchmark.py \
|
|
267
|
+
--datasets mnist --k-folds 5 --num-clients 20 --clients-per-round 5 \
|
|
268
|
+
--num-rounds 100 --require-real-proofs --force \
|
|
269
|
+
--output-dir results/full_benchmark
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Outputs per-dataset, per-config k-fold JSONs under `--output-dir`
|
|
273
|
+
(`aggregate` summary stats + `folds[i].rounds[j]` per-round detail). See
|
|
274
|
+
`CLAUDE.md`'s "Benchmark results status" section for which result set is
|
|
275
|
+
currently canonical and why.
|
|
276
|
+
|
|
277
|
+
## Repository Structure
|
|
278
|
+
|
|
279
|
+
```
|
|
280
|
+
secure-fl/
|
|
281
|
+
├── src/secure_fl/
|
|
282
|
+
│ ├── core/ # Types, config (thread-safe singleton), exceptions
|
|
283
|
+
│ ├── federation/ # SecureFlowerClient, SecureFlowerServer, FedJSCMAggregator,
|
|
284
|
+
│ │ # StabilityMonitor, client_utils, helpers
|
|
285
|
+
│ ├── zkp/ # ClientProofManager, ServerProofManager, quantization
|
|
286
|
+
│ ├── models/ # SimpleModel, MNISTCNN, ResNet18Model, factory
|
|
287
|
+
│ ├── data/ # benchmark_data: load/partition/subsample datasets
|
|
288
|
+
│ ├── cli/ # Click CLI: secure-fl, secure-fl-server, secure-fl-client, secure-fl-setup
|
|
289
|
+
│ ├── proofs/
|
|
290
|
+
│ │ ├── client_circuits/ # PySNARK delta_bound_proof circuit
|
|
291
|
+
│ │ └── server/ # Circom aggregation SNARK circuits
|
|
292
|
+
│ └── utils/ # helpers (compute_hash, compute_parameter_norm), logging
|
|
293
|
+
├── demo/
|
|
294
|
+
│ └── simulate_attack.py # Textual TUI: real ZKP attack detection demo
|
|
295
|
+
├── experiments/
|
|
296
|
+
│ ├── full_benchmark.py # Canonical paper benchmark (accuracy + overhead + attacks)
|
|
297
|
+
│ ├── train.py # Single training run
|
|
298
|
+
│ ├── demo.py # CLI demo entry point
|
|
299
|
+
│ └── config.yaml
|
|
300
|
+
└── tests/
|
|
301
|
+
├── unit/ # No external dependencies (fast)
|
|
302
|
+
└── integration/ # Requires Circom + SnarkJS
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
## Development
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
make dev # Full setup (uv sync + ZKP tools)
|
|
309
|
+
make lint # Ruff lint
|
|
310
|
+
make format # Ruff format ← run before every commit
|
|
311
|
+
make type-check # mypy (strict)
|
|
312
|
+
make test # Full suite
|
|
313
|
+
make test-quick # Fast, --exitfirst
|
|
314
|
+
make test-cov # With coverage (threshold: 30%)
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## Known Limitations
|
|
318
|
+
|
|
319
|
+
- Server ZKP (Circom/SnarkJS) requires `make setup-zkp`; subprocess calls time out after 300 s
|
|
320
|
+
- Client-side transferable Groth16 proof falls back gracefully to PySNARK-only when Circom/SnarkJS tools are unavailable client-side
|
|
321
|
+
- Coverage threshold is 30% — being raised incrementally
|
|
322
|
+
- Norm-bound enforcement does not defend against within-bound poisoning (adversarial updates crafted to satisfy the norm constraint) — see `docs/paper/main.tex` §Limitations
|
|
323
|
+
|
|
324
|
+
## License
|
|
325
|
+
|
|
326
|
+
MIT License — see [LICENSE](LICENSE) for details.
|
|
327
|
+
|
|
328
|
+
## Citation
|
|
329
|
+
|
|
330
|
+
```bibtex
|
|
331
|
+
@misc{timilsina2026securefl,
|
|
332
|
+
title={Secure-FL: Zero-Knowledge Proofs for Dual-Verifiable Federated Learning},
|
|
333
|
+
author={Timilsina, Krishant and Paudel, Bindu and Timilsina, Arun Kumar},
|
|
334
|
+
year={2026},
|
|
335
|
+
url={https://github.com/krishantt/secure-fl},
|
|
336
|
+
note={Preprint}
|
|
337
|
+
}
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Acknowledgments
|
|
341
|
+
|
|
342
|
+
- [Flower](https://flower.dev) — federated learning infrastructure
|
|
343
|
+
- [Circom](https://github.com/iden3/circom) / [SnarkJS](https://github.com/iden3/snarkjs) — zk-SNARK toolchain
|
|
344
|
+
- [PySNARK](https://github.com/meilof/pysnark) — Python zk-SNARK (Groth16) library
|