Secure Vertical Federated Learning: ADP + Selective HE + ZKP, with a pluggable threat-detection / audit engine.
Project description
U-VFL — runnable reference implementation
A clean, reproducible implementation of the U-VFL framework from the paper "U-VFL: A Unified Framework for Secure Vertical Federated Learning with Adaptive Privacy and Verifiable Computation" (Ahmed et al., ICONIP 2026), so it can be installed and tested on any machine against the real UCI Heart Disease dataset — or any clinical CSV you supply.
It implements the three security modules over a two-party Vertical FL (SplitNN):
- ADP — Adaptive Differential Privacy (decaying privacy budget)
- SHE — Sensitivity-driven Selective Homomorphic Encryption (real CKKS via TenSEAL)
- ZKP — a verifiable-integrity check that rejects tampered gradients
Install
pip install uvfl # core (numpy-only knobs)
pip install "uvfl[all]" # + real CKKS HE + the high-level helpers
60-second usage
Low-level — drop the three knobs into your own training loop:
import uvfl, numpy as np
tri = uvfl.Trilemma(uvfl.ADP(epsilon_max=5), uvfl.SHE(high_risk_fraction=0.3), uvfl.ZKP())
payload = tri.protect(gradient, round_t=t, total_rounds=T) # privacy+encrypt+commit
gradient, integrity_ok = tri.open(payload) # verify + recover
High-level — fully-secured VFL in one call:
model = uvfl.fit("data.csv", target="diagnosis") # ADP+SHE+ZKP applied automatically
print(model.report())
best = uvfl.autotune("data.csv", target="diagnosis") # search configs, keep best by F1
Distributed (2 medical sites + 1 master), data never leaves a site:
# site 1 VM: uvfl.serve_party("siteA.csv", port=8001)
# site 2 VM (labels): uvfl.serve_party("siteB.csv", port=8001, target="diagnosis")
# master VM:
model = uvfl.train_federated(["http://SITE1_IP:8001", "http://SITE2_IP:8001"])
Empirical proof suite: uvfl.validate("data.csv", target="diagnosis"). See
VALIDATION.md, DISTRIBUTED.md, ROADMAP.md.
⚠️ Please read first — what this is and isn't
This is an independent, clean-room re-implementation built from the paper's equations — it is not the authors' original experiment code. Consequently:
- Absolute numbers will differ from the paper (different seed, data curation, optimiser, and the fact that the math describes a SplitNN while the paper also mentions XGBoost). The code reproduces the mechanisms and the qualitative story, not the exact 0.85/0.83 figures.
- HE is real. Encryption uses CKKS (Microsoft SEAL via TenSEAL) when installed. The selective-vs-full speedup is measured, not assumed.
- ZKP is an honest stand-in, not a zk-SNARK. It uses a hash commitment +
Fiat–Shamir challenge to demonstrate the property (a tampered/malicious
gradient is rejected, soundness ≈
1 − 2^−κ). A deployment would use Groth16/PLONK over the gradient circuit. The paper itself notes "ZKP lacks concrete implementation benchmarks", so this matches its scope. - Two honest discrepancies we found vs. the paper, worth discussing:
- With CKKS slot-packing, a small gradient fits in one ciphertext whether
full or selective, giving no speedup. Selective HE only helps when the
parameter count exceeds slot capacity (per-component encryption). We
therefore encrypt per-component, where the measured speedup is
≈ 1 / high_risk_fraction(≈ 3.3× at 30%). This is internally more coherent than the paper's headline (which states both 8.5× and 20–34×). - The methodology describes SplitNN gradient exchange; "XGBoost" is mentioned once in limitations. We implement the SplitNN that the equations describe.
- With CKKS slot-packing, a small gradient fits in one ciphertext whether
full or selective, giving no speedup. Selective HE only helps when the
parameter count exceeds slot capacity (per-component encryption). We
therefore encrypt per-component, where the measured speedup is
Nothing here is tuned to force the paper's numbers — results are whatever the seeded run produces.
1. Setup (pick one)
Requires Python 3.9–3.12. CPU only — no GPU, no PyTorch.
Option A — venv + pip (works everywhere)
cd uvfl
python3 -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
pip install -r requirements-he.txt # optional: real CKKS HE (recommended)
Option B — conda
cd uvfl
conda env create -f environment.yml
conda activate uvfl
pip install -r requirements-he.txt # optional: real CKKS HE
Option C — make
cd uvfl
make setup # core deps
make setup-he # + real HE (optional)
If TenSEAL will not install on your platform, skip
requirements-he.txt— the SHE module falls back to a clearly-labelled cost simulation and everything still runs.
2. Run it
# Real UCI Heart Disease (auto-downloads via ucimlrepo -> UCI URL -> mirror)
python run.py
# YOUR OWN real-life dataset (a CSV with a label column)
python run.py --data /path/to/your_clinical_data.csv --target outcome
# Offline smoke test (synthetic data, no internet)
python run.py --synthetic
# Faster / narrower sweeps
python run.py --epochs 40 --scenarios S1 S5 --noise 0.5 1.0
make run # == python run.py
make run-synthetic # == python run.py --synthetic
A full run takes well under a minute on a laptop.
Outputs (in outputs/)
results.csv— every scenario × noise level: accuracy, precision, recall, F1, F2, timingsresults.json— same, plus the efficiency table and ZKP soundness demofig_accuracy_f1_vs_noise.png,fig_pareto.png,fig_comparative_bar.png,fig_confusion_matrices.png
2b. Distributed deployment (the real 3-VM setup) + proving it works
The single-process run above validates the algorithm. There is also a real distributed implementation — two data parties + a master/aggregation server, talking over HTTP — that maps onto a 3-VM deployment (two feature VMs + one master VM). No extra dependencies (stdlib transport).
# simulate all 3 nodes on one machine
python -m uvfl.distributed.run_local --data heart.csv # clean run
python -m uvfl.distributed.run_local --data heart.csv --byzantine-mitm 0.5 # integrity test
# run the full PROOF suite (writes outputs/VALIDATION_REPORT.md)
python -m uvfl.distributed.validate --data heart.csv
The validation suite empirically proves all five claims (correctness, ADP
privacy, SHE confidentiality, ZKP integrity, efficiency). See VALIDATION.md
for the methodology, DISTRIBUTED.md to deploy on the actual VMs, and
ROADMAP.md for next steps. In the distributed system, raw features never
leave a party VM, labels never leave the master, and high-risk gradient
components are CKKS-encrypted with each party's public key (only that party can
decrypt).
3. Testing on a real-life dataset
Point --data at any CSV and name the label with --target:
python run.py --data hospital_data.csv --target disease
- If the CSV has the standard UCI heart columns, it uses the paper's clinical vertical split (Client A = primary-care features, Client B = cardiac-centre features + label).
- Otherwise it auto-splits features by index into the two parties and warns.
- Set the exact split, missing-value handling, target binarisation, etc. in
config.yamlunderdata:.
4. Scenarios (paper's S1/S2/S3/S5)
| ID | Name | Security on the exchanged gradient |
|---|---|---|
| S1 | No Security | plaintext (baseline utility) |
| S2 | Full HE | encrypt all ∇W_A components (overhead ref) |
| S3 | Fixed DP | constant high noise, no schedule |
| S5 | U-VFL (ours) | Adaptive DP + Selective HE + ZKP |
noise_level ∈ [0,1] scales injected DP noise (the paper's 10%–100% grid);
1.0 = the full configured schedule. S1/S2 do not use DP, so they are
noise-independent (trained once, drawn flat across the grid).
5. Paper → code map
| Paper | File | Notes |
|---|---|---|
| Two-party SplitNN, gradient exchange | uvfl/model.py |
pure NumPy + Adam |
ADP eqs (1)–(5): ε_t, σ_t, clipping |
uvfl/adp.py |
AdaptiveDP |
| Fixed DP baseline | uvfl/adp.py |
FixedDP |
| SHE eqs (6)–(11): sensitivity, selection | uvfl/she.py |
real CKKS; per-component |
| ZKP prove/verify, soundness | uvfl/zkp.py |
commitment + Fiat–Shamir stand-in |
| Scenarios, security stack | uvfl/scenarios.py |
S1/S2/S3/S5 |
| Metrics, confusion matrices | uvfl/metrics.py |
sklearn |
| Experiment loop, efficiency table | uvfl/experiment.py |
one-time HE benchmark |
| Figures | uvfl/plots.py |
matplotlib |
| Distributed party node (per VM) | uvfl/distributed/party.py |
bottom model + HE secret key |
| Master / aggregation node | uvfl/distributed/master.py |
top model + security enforcement |
| 3-VM simulator (localhost) | uvfl/distributed/run_local.py |
start all nodes locally |
| Proof / validation suite | uvfl/distributed/validate.py |
the five empirical proofs |
| Proof methodology / VM deploy / roadmap | VALIDATION.md, DISTRIBUTED.md, ROADMAP.md |
6. Reproducibility
- Single global
seed(default 42) inconfig.yamlcontrols data split, weight init, DP noise, and the ZKP demo. Re-running gives identical numbers. - HE cost is hardware-dependent; the speedup ratio (full / selective) is the stable, portable quantity.
7. Example run (UCI heart.csv mirror, seed 42 — yours will differ)
S1 acc~0.738 f1~0.765 (no security)
S2 acc~0.738 f1~0.765 (Full HE, exact -> same utility)
S3 acc~0.770 f1~0.794 (Fixed DP)
S5 acc~0.754 f1~0.783 (U-VFL: ADP+SHE+ZKP)
HE/round: full=633ms selective=190ms -> 3.33x speedup (real CKKS)
ZKP soundness: 200/200 honest accepted, 200/200 tampered rejected
Takeaway demonstrated on real data: full cryptographic + privacy protection keeps accuracy within ~2% of the unsecured baseline, selective HE is ~3.3× cheaper than full HE, and the integrity check rejects 100% of tampered gradients.
8. Data & license
UCI Heart Disease (Cleveland) — Janosi, Steinbrunn, Pfisterer, Detrano (1988), UCI ML Repository (id 45). The dataset is downloaded at runtime and not redistributed here. Code is provided for academic reproducibility.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file uvfl-0.2.0.tar.gz.
File metadata
- Download URL: uvfl-0.2.0.tar.gz
- Upload date:
- Size: 53.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92d42a716c6561d664c8d3f6cadf53acb16084c42b1500153b64209818d0fd70
|
|
| MD5 |
90c1e2f47d4c30f0bd406aaad2a67cf8
|
|
| BLAKE2b-256 |
0196d19ea91dc1cd5513b0963ececb70c85a2e1996948567520eba14a654f7ae
|
File details
Details for the file uvfl-0.2.0-py3-none-any.whl.
File metadata
- Download URL: uvfl-0.2.0-py3-none-any.whl
- Upload date:
- Size: 55.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
054160ac30736cd4e8e1ce38a8d23d07cc8a8f2e7560d27a8d52cf6d996fb9b2
|
|
| MD5 |
24b0f99e49f93fad1165523b90b4b4ae
|
|
| BLAKE2b-256 |
0785ec8c3a724f49f32386b2dee133884012974a372cc2186978081c00248d1a
|