mirror of
https://github.com/ruvnet/RuView
synced 2026-08-11 20:41:44 +00:00
b827dc40b1
Take VEIL from the synthetic Rust reference model toward real WiFi silicon
across multiple hardware providers, around one shared, host-validated core.
Answers the questions "can OpenWRT / open WiFi software implement this?" and
"can ESP32 help scramble signals?" with an honest per-platform feasibility map.
Portable C shield core (firmware/privshield/core/) — VALIDATED (host test):
- veil_shield.{h,c}: keyed Givens-rotation obfuscation of the identity-bearing
"fine" subspace, C99, no malloc / no libc I/O, only <math.h>. SplitMix64 key
schedule byte-identical to the Rust crate, so on-air behavior is consistent
everywhere and every adapter links the same math.
- make test passes: energy conservation (orthogonal => "not jamming"),
reversibility (recover inverts apply), wrong-key-fails, and PRNG stream parity
with the Rust crate. This is build/host evidence, NOT silicon.
Per-provider adapters (all SYNTHETIC / L0, build-only, TODO(hw) markers):
- openwifi/ grade B (ceiling A, effort D): only open PHY/MAC (FPGA) that can
host the full keyed rotation + inverse; needs new HDL + 2nd TX chain. Carries
the P5 measurement protocol (MEASUREMENT.md) for the first MEASURED result.
- openwrt/ grade C: per-packet keyed unitary is blob-blocked on commodity APs;
coarse compliant knobs (TX antenna map, sounding-cadence jitter) reachable
from userspace/hostapd; ath9k is the one credible driver-patch route.
- nexmon/ grade C: reading the compressed-BF angles is solved (nexmon_csi /
Wi-BFI); shaping the transmitted report is research-grade (D11 ucode-adjacent).
- esp32/ grade F (self) / B (supporting): cannot shape its own BF feedback
(closed esp-phy-lib blob); legitimate as a sensing detector and external-RIS
controller — the honest way ESP32 "helps scramble", via an external surface.
Docs:
- firmware/privshield/README.md: architecture, layout, and the feasibility matrix.
- ADR-290: the E2E hardware program, PROOF discipline, and per-provider decision;
added to docs/adr/README.md index.
Compliant waveform controls only, never jamming. No adapter has run on silicon;
no MEASURED claim is made (that is roadmap P5, gated on a captured log).
Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01WEXNqzs7UsfNFBcP5yW21p
121 lines
4.2 KiB
C
121 lines
4.2 KiB
C
/* SPDX-License-Identifier: MIT OR Apache-2.0
|
|
* veil_shield core — see veil_shield.h. Pure computation; no radio, no I/O. */
|
|
#include "veil_shield.h"
|
|
#include <math.h>
|
|
|
|
/* Two-pi constant matching Rust core::f32::consts::TAU. */
|
|
#define VEIL_TAU 6.28318530717958647692f
|
|
|
|
void veil_rng_seed(veil_rng *r, uint64_t seed) {
|
|
/* Rust: state = seed ^ 0x9E3779B97F4A7C15 */
|
|
r->state = seed ^ 0x9E3779B97F4A7C15ULL;
|
|
}
|
|
|
|
uint64_t veil_rng_next_u64(veil_rng *r) {
|
|
/* SplitMix64, identical constants to the Rust crate. */
|
|
r->state += 0x9E3779B97F4A7C15ULL;
|
|
uint64_t z = r->state;
|
|
z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ULL;
|
|
z = (z ^ (z >> 27)) * 0x94D049BB133111EBULL;
|
|
return z ^ (z >> 31);
|
|
}
|
|
|
|
float veil_rng_next_f32(veil_rng *r) {
|
|
/* (next_u64 >> 40) / 2^24 — 24 mantissa bits, matches Rust `next_f32`. */
|
|
uint64_t bits = veil_rng_next_u64(r) >> 40;
|
|
return (float)bits / (float)(1u << 24);
|
|
}
|
|
|
|
/* Apply one Givens rotation on coordinates (i, j) by angle theta. Orthogonal. */
|
|
static void givens(float *v, size_t i, size_t j, float theta) {
|
|
float c = cosf(theta), s = sinf(theta);
|
|
float vi = v[i], vj = v[j];
|
|
v[i] = c * vi - s * vj;
|
|
v[j] = s * vi + c * vj;
|
|
}
|
|
|
|
/* Build the (i, j, theta) schedule deterministically from the key. The order
|
|
* and draws mirror `protector.rs::session_rotation`. */
|
|
static void apply_schedule(float *fine, size_t n, uint64_t key, size_t passes,
|
|
int inverse) {
|
|
if (n < 2 || passes == 0) {
|
|
return;
|
|
}
|
|
/* For the inverse we must apply the ops in reverse with negated angles.
|
|
* Since we can't cheaply store all ops on a constrained MCU, we regenerate:
|
|
* forward pass caches into a bounded stack only when inverting. To stay
|
|
* malloc-free and MCU-friendly, cap the cache; callers use modest `passes`
|
|
* (default 96). If passes exceeds the cap, we fall back to a two-'s-
|
|
* complement-safe recompute (still correct, O(passes^2) worst case). */
|
|
enum { CACHE = 256 };
|
|
if (!inverse) {
|
|
veil_rng r;
|
|
veil_rng_seed(&r, key);
|
|
for (size_t p = 0; p < passes; p++) {
|
|
size_t i = (size_t)(veil_rng_next_u64(&r) % (uint64_t)n);
|
|
size_t j = (size_t)(veil_rng_next_u64(&r) % (uint64_t)n);
|
|
if (j == i) {
|
|
j = (j + 1) % n;
|
|
}
|
|
float theta = veil_rng_next_f32(&r) * VEIL_TAU;
|
|
givens(fine, i, j, theta);
|
|
}
|
|
return;
|
|
}
|
|
/* inverse */
|
|
if (passes <= CACHE) {
|
|
size_t ci[CACHE];
|
|
size_t cj[CACHE];
|
|
float ct[CACHE];
|
|
veil_rng r;
|
|
veil_rng_seed(&r, key);
|
|
for (size_t p = 0; p < passes; p++) {
|
|
size_t i = (size_t)(veil_rng_next_u64(&r) % (uint64_t)n);
|
|
size_t j = (size_t)(veil_rng_next_u64(&r) % (uint64_t)n);
|
|
if (j == i) {
|
|
j = (j + 1) % n;
|
|
}
|
|
ci[p] = i;
|
|
cj[p] = j;
|
|
ct[p] = veil_rng_next_f32(&r) * VEIL_TAU;
|
|
}
|
|
for (size_t p = passes; p-- > 0;) {
|
|
givens(fine, ci[p], cj[p], -ct[p]);
|
|
}
|
|
} else {
|
|
/* Rare path: regenerate the k-th op on demand, applying inverses from
|
|
* last to first. O(passes^2) but malloc-free and correct. */
|
|
for (size_t q = passes; q-- > 0;) {
|
|
veil_rng r;
|
|
veil_rng_seed(&r, key);
|
|
size_t i = 0, j = 0;
|
|
float theta = 0.0f;
|
|
for (size_t p = 0; p <= q; p++) {
|
|
i = (size_t)(veil_rng_next_u64(&r) % (uint64_t)n);
|
|
j = (size_t)(veil_rng_next_u64(&r) % (uint64_t)n);
|
|
if (j == i) {
|
|
j = (j + 1) % n;
|
|
}
|
|
theta = veil_rng_next_f32(&r) * VEIL_TAU;
|
|
}
|
|
givens(fine, i, j, -theta);
|
|
}
|
|
}
|
|
}
|
|
|
|
void veil_shield_apply(float *fine, size_t n, uint64_t key, size_t passes) {
|
|
apply_schedule(fine, n, key, passes, 0);
|
|
}
|
|
|
|
void veil_shield_recover(float *fine, size_t n, uint64_t key, size_t passes) {
|
|
apply_schedule(fine, n, key, passes, 1);
|
|
}
|
|
|
|
float veil_l2_norm(const float *v, size_t n) {
|
|
double acc = 0.0;
|
|
for (size_t i = 0; i < n; i++) {
|
|
acc += (double)v[i] * (double)v[i];
|
|
}
|
|
return (float)sqrt(acc);
|
|
}
|