DEV Community

Mushahid Intesum
Mushahid Intesum

Posted on

Can you run a GNN without anyone seeing the data? Journal of our experiments on privacy-preserving GNN inference on microcontrollers

I have been keenly interested in how to make Graph Neural Networks (GNN) run securely on resource-constrained devices. The question driving this work is deceptively simple: can three microcontrollers jointly compute GNN inference on traffic data such that no single device ever sees the plaintext, not the input features, not the model weights, not the intermediate activations? This post is about the findings I have found so far in this independent research endeavor.

Background on Secure GNN Inference

A standard GCN layer [1] computes: H' = σ(A · H · W + b), where A is the normalized adjacency matrix (graph topology), H is the node feature matrix, W is the learned weight matrix, and σ is an activation function. In a traffic signal coordination setting, A encodes which intersections connect to each other, H holds the per-intersection sensor readings (queue lengths, phase states, flow rates), and the output dictates signal timing decisions.

The privacy concern is real: traffic flow data reveals movement patterns, congestion bottlenecks, and potentially individual vehicle trajectories. If multiple jurisdictions or agencies need to collaboratively optimize signals at their shared borders, no party may want to expose their raw data to the others. Secure Multi-Party Computation (MPC) lets them jointly compute the model output without any single party seeing the full picture.

We use Replicated Secret Sharing (RSS) [2] in a 3-party semi-honest setting. Each value x is split into three shares (x₁, x₂, x₃) such that x₁ + x₂ + x₃ = x mod 2⁶⁴. Party i holds two of the three shares (xᵢ, xᵢ₊₁), so any single party sees only random-looking numbers. Reconstruction requires combining shares from at least two parties.

┌─────────────────────────────────────────────────────┐
│             REPLICATED SECRET SHARING               │
│                                                     │
│  Secret x split into (x₁, x₂, x₃)                 │
│  where x₁ + x₂ + x₃ = x  (mod 2⁶⁴)               │
│                                                     │
│  Party 0 holds: (x₁, x₂)                           │
│  Party 1 holds: (x₂, x₃)                           │
│  Party 2 holds: (x₃, x₁)                           │
│                                                     │
│  → No single party can recover x                    │
│  → Secure addition is FREE (no communication)       │
│  → Secure multiplication costs 1 round of comm      │
└─────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

The arithmetic properties are what make this practical. Secure addition of two shared values requires zero communication: each party just locally adds their shares. Secure multiplication is more expensive. It requires one round of communication between parties using a PRF-based resharing protocol to maintain the share structure, but the cost is bounded and predictable.

The Key Insight: Public Adjacency, Private Features

Here's the critical observation that makes GNN inference on microcontrollers feasible. In a traffic network, the graph topology is public knowledge. Everyone knows which intersections connect to which. The adjacency matrix A is not secret. Only the node features H (sensor readings) and the model weights W (the learned policy) need protection.

This means the message-passing step A · H is a public-matrix × secret-matrix multiply, which requires zero communication. Each party can locally compute it from their shares alone. Only the feature transformation step H · W (secret × secret) requires the full RSS multiplication protocol.

Compared to CryptGNN [3] which treats everything as secret, this roughly halves the communication cost. In a 2-layer GCN, we have:

Layer 1: A·X (free) → (A·X)·W₁ (1 round) → activation (1 round)
Layer 2: A·H (free) → (A·H)·W₂ (1 round)
Total:   3 communication rounds (vs 5+ if A were secret)
Enter fullscreen mode Exit fullscreen mode

Test Bed

The target hardware is ESP32-S3 microcontrollers, 240MHz dual-core Xtensa processors with WiFi and ~320KB usable SRAM. Three boards form the 3-party RSS computation, communicating over TCP/WiFi. For the model, I use a 2-layer GCN trained on a 20-node synthetic traffic grid (4×5 intersection layout):

4×5 Grid Topology:        Model Architecture:

○─○─○─○─○                 GCN(16 → 16 → 4)
│ │ │ │ │                  • 20 nodes, 16 input features
○─○─○─○─○                 • 16 hidden features, 4 output classes
│ │ │ │ │                  • Polynomial activation: 0.1x² + 0.5x + 0.1
○─○─○─○─○                 • 340 total parameters
│ │ │ │ │                  • Q20 fixed-point arithmetic
○─○─○─○─○
Enter fullscreen mode Exit fullscreen mode

The input features per node are: one-hot phase encoding (4 dims), queue lengths per direction (4 dims), flow rates (4 dims), occupancy, and time-of-day (sin/cos encoding), 16 features total. The model predicts which direction has the highest traffic demand.

An important design choice: we use a polynomial activation 0.1x² + 0.5x + 0.1 instead of ReLU. ReLU requires a comparison (x > 0), which is expensive in MPC because comparisons need bit-decomposition. A degree-2 polynomial can be evaluated using only one secure multiplication (for the term), keeping the activation cost to a single communication round. Training with the polynomial activation achieves 52.1% accuracy vs 54.0% with ReLU, a modest 1.9 percentage point drop that we consider acceptable for the massive reduction in protocol complexity.

The Pipeline

The full system goes from training through to secure inference verification:

Train GCN → Export Q20 Fixed-Point → Generate RSS Shares → Secure Inference → Verify Against Plaintext
Enter fullscreen mode Exit fullscreen mode

The Python side handles training (train_model.py), fixed-point export with RSS share generation (export_model.py), and offline analysis (analyze.py + rss_emulator.py). The C side implements the actual protocol: RSS primitives, secure matrix operations, and the 2-layer GCN inference pipeline. Both sides are designed to produce bit-identical results for a given set of shares.

Finding 1: Fixed-point quantization preserves inference fidelity

Before worrying about secret sharing, the first question is whether the float-to-fixed-point conversion introduces unacceptable error. We use Q16 fixed-point (16 fractional bits) for the offline analysis with an 8-node ring graph as a controlled test bed.

Node Float Output Q16 Output Absolute Error
0 0.293579 0.293488 0.000091
1 0.273324 0.273224 0.000100
2 0.276208 0.276123 0.000085
3 0.302083 0.302002 0.000081
4 0.328208 0.328125 0.000083
5 0.354583 0.354492 0.000091
6 0.357505 0.357422 0.000083
7 0.337120 0.337036 0.000084

The mean quantization error is 0.0000875 and the max is 0.0001002. Both output features show identical values per node (the test model uses uniform weights that produce symmetric outputs), confirming the fixed-point pipeline is numerically faithful. All errors are sub-0.01% relative to the output magnitudes, well within acceptable bounds for traffic signal decisions.

Figure 1: Output comparison across nodes showing near-identical float and Q16 fixed-point values. The bars are visually indistinguishable, confirming minimal quantization degradation.

The error is also spatially uniform across nodes. This matters: if quantization error concentrated at specific graph positions, it could systematically bias decisions at those intersections.

Figure 2: Per-node quantization error heatmap. Error values are uniformly distributed across all nodes and output features, staying within the 0.00008 to 0.0001 range.

Finding 2: RSS primitives are bit-exact

The RSS protocol correctness was verified through four escalating tests in the Python emulator:

Test Description Result
Share/Reconstruct Split 42, reconstruct from shares ✅ PASS
Secure Addition 100 + 200 = 300, zero communication ✅ PASS
Secure Multiplication 7 × 6 = 42, with PRF resharing ✅ PASS
Random Multiplication 100 random pairs, full ring arithmetic 100/100 ✅

The last test is the important one. We generate 100 random integer pairs in [0, 10000), secret-share both operands, perform secure multiplication with the full PRF-based resharing protocol, reconstruct, and verify against the plaintext product modulo 2⁶⁴. All 100 pass, confirming that the resharing logic correctly maintains the share invariant x₁ + x₂ + x₃ = x mod 2⁶⁴ through multiplication.

Finding 3: All three parties agree on the output

The C desktop emulator spawns three processes (one per party) communicating over localhost TCP sockets. Each party holds its own shares of the features and weights, runs the full GCN inference protocol (5 steps: public matmul, secure matmul, polynomial activation, public matmul, secure matmul), and then opens the output shares to reconstruct the final result.

All 3 parties produce identical reconstructed outputs, confirming protocol correctness across the inter-process communication boundary. The desktop emulation completes in approximately 0.39 ms average across the three parties (Party 0: 0.43 ms, Party 1: 0.39 ms, Party 2: 0.35 ms).

Figure 3: Inference time per party in the desktop emulator. The slight variation is expected from OS scheduling; the important thing is that all three parties complete within a tight band.

The per-party timing spread (~0.08 ms) is purely from OS scheduling jitter on the desktop. On dedicated ESP32-S3 hardware without a preemptive OS, we expect tighter synchronization.

Finding 4: The secure pipeline mirrors the C implementation

The Python RSS emulator (rss_emulator.py) replicates the exact same protocol as the C implementation:

Step 1: A * X      ← public × shared, FREE
Step 2: (A*X) * W₁ ← shared × shared, 1 communication round
Step 3: Activation  ← polynomial, 1 communication round
Step 4: A * H₁     ← public × shared, FREE
Step 5: (A*H₁) * W₂ ← shared × shared, 1 communication round
Enter fullscreen mode Exit fullscreen mode

Both implementations use identical truncation semantics (arithmetic right shift on signed 64-bit values), the same PRF-based resharing (splitmix64 hash for deterministic randomness), and the same polynomial activation coefficients (Q-format 0.1, 0.5, 0.1). This dual-stack approach, Python for rapid prototyping and verification, C for deployment, lets us catch protocol bugs early. If the Python emulator and the C binary produce different outputs for the same shares, something is wrong in the C implementation.

The Firmware Side

The ESP32-S3 firmware is written in C with ESP-IDF. Each board connects to the others over WiFi/TCP, loads its pre-generated share files (model_shares_p{0,1,2}.bin), and runs the inference protocol. The PRF uses mbedtls hardware-accelerated AES on the ESP32-S3 (replacing the OpenSSL backend used on desktop). The transport layer abstracts over POSIX sockets (desktop) vs lwIP TCP (ESP32), so the core protocol code is identical on both platforms.

This is fully built and compiles, but the actual on-device deployment and benchmarking is the next step. This will be the next part.

Future Directions

This is an ongoing experiment. This blog serves as a journal of my progress so far and will continually be updated once new findings are found. A few concrete next steps:

  1. Hardware deployment and benchmarking: flash the firmware to 3 ESP32-S3 boards, run the full protocol over WiFi, and measure end-to-end latency, per-step communication overhead, and memory usage. The key question: can the protocol complete within a traffic signal cycle (~1-5 seconds) to be practically useful? This will be the next part.
  2. Scale to the full 20-node model: the current desktop verification uses an 8-node ring graph with small weight matrices. The trained traffic model has 20 nodes with 16→16→4 dimensions, significantly larger matrices that will stress both computation and communication budgets on the ESP32.
  3. Real traffic data integration: replace the synthetic training data with real-world traffic signal datasets to evaluate whether the fixed-point quantization error affects decision quality on non-synthetic distributions.
  4. Comparison with CryptGNN: quantitatively benchmark our "public adjacency" optimization against the fully-secret approach to measure the actual communication savings in practice.

Hopefully we'll be able to get a paper through this independent endeavor.

References

[1] T. N. Kipf, M. Welling, "Semi-Supervised Classification with Graph Convolutional Networks," ICLR 2017.
[2] T. Araki, J. Furukawa, Y. Lindell, A. Nof, K. Ohara, "High-Throughput Semi-Honest Secure Three-Party Computation with an Honest Majority," CCS 2016.
[3] R. Ran, W. Wang, Q. Gang, J. Rao, "CryptGNN: Fast Privacy-Preserving Graph Neural Network Inference," 2022.
[4] ESP-IDF Programming Guide, Espressif Systems, https://docs.espressif.com/projects/esp-idf/.

Top comments (0)