mirror of
https://github.com/ruvnet/RuView
synced 2026-08-02 19:11:46 +00:00
0ca8a38cbc
Iter 15. Lands ADR-120 §2.3 — the cryptographic foundation of invariant
I3 ("cross-site identity correlation is impossible"). rf_signature_hash
is now derived from a per-site secret and a daily epoch, so two nodes
observing the same physical person produce uncorrelated 256-bit digests.
Added (no_std-compatible):
- blake3 = "1.5", default-features = false (no_std, no SIMD by default)
- src/signature_hasher.rs:
* Constants SECONDS_PER_DAY (86_400), SITE_SALT_LEN (32), RF_SIGNATURE_LEN (32)
* SignatureHasher { site_salt: [u8; 32] } with new(salt) const ctor
* compute(day_epoch, &features) -> [u8; 32] (BLAKE3 keyed mode)
* compute_at(unix_secs, &features) -> [u8; 32] convenience
* day_epoch_from_unix_secs(unix_secs) -> u32 helper (floor(t / 86400))
- pub use SignatureHasher, RF_SIGNATURE_LEN, SITE_SALT_LEN from lib.rs
tests/signature_hasher.rs (8 named tests, all green):
deterministic_under_identical_inputs
different_site_salts_produce_different_hashes
different_day_epochs_rotate_the_hash
different_features_produce_different_hashes
output_length_is_32_bytes
day_epoch_from_unix_secs_matches_floor_division
(covers 0, 86_399, 86_400, and the 1.7e9 modern timestamp)
compute_at_matches_compute_with_derived_day
cross_site_hamming_distance_is_statistically_high
*** ADR-120 §2.7 AC2 acceptance test ***
Runs 100 trials with distinct (salt_a, salt_b) pairs observing
identical features, computes per-trial Hamming distance, asserts
mean >= 120 bits and min >= 80 bits. Empirically lands at ~128 bits
mean (the expected value for two independent 256-bit hashes), with
no trial below 80 bits — i.e., zero suspicious near-collisions.
ACs progressed:
- ADR-120 §2.7 AC2 — structurally enforced cross-site isolation, now
proven empirically by the Hamming-distance test. This is the
cryptographic half of invariant I3 in code, not just docs.
- ADR-118 invariant I3 — first runtime witness that two sites with
independent site_salts cannot correlate the same person's signature.
Test config:
- cargo test --no-default-features → 72 passed (64 + 8; signature_hasher is no_std)
- cargo test → 117 passed (109 + 8)
Out of scope (next iter target):
- Wire SignatureHasher into BfldEmitter: replace caller-supplied
rf_signature_hash with hasher.compute_at(ts, &features) so the
pipeline produces correct hashes end-to-end.
- IdentityFeatures canonical-bytes encoder so callers don't need to
hand-serialize per-feature representations.
Co-Authored-By: claude-flow <ruv@ruv.net>
46 lines
1.3 KiB
TOML
46 lines
1.3 KiB
TOML
[package]
|
|
name = "wifi-densepose-bfld"
|
|
description = "BFLD — Beamforming Feedback Layer for Detection. Privacy-gated WiFi BFI sensing primitives. See ADR-118."
|
|
version.workspace = true
|
|
edition.workspace = true
|
|
authors.workspace = true
|
|
license.workspace = true
|
|
repository.workspace = true
|
|
documentation.workspace = true
|
|
keywords.workspace = true
|
|
categories.workspace = true
|
|
|
|
[features]
|
|
default = ["std", "serde-json"]
|
|
std = []
|
|
# JSON serialization for BfldEvent (ADR-121 §2.1, ADR-122 §2.1). Pulls in
|
|
# serde + serde_json; tied to `std` because serde_json is std-only.
|
|
serde-json = ["std", "dep:serde", "dep:serde_json"]
|
|
# Soul Signature integration (ADR-118 §1.4, ADR-120 §2.7, ADR-121 §2.6) —
|
|
# enables privacy_class = 1 (derived) mode and the SoulMatchOracle gate
|
|
# exemption. Disabled by default per the structural class-2 default.
|
|
soul-signature = []
|
|
|
|
[dependencies]
|
|
thiserror.workspace = true
|
|
static_assertions = "1.1"
|
|
crc = "3"
|
|
blake3 = { version = "1.5", default-features = false }
|
|
serde = { workspace = true, features = ["derive"], optional = true }
|
|
serde_json = { workspace = true, optional = true }
|
|
|
|
[dev-dependencies]
|
|
proptest.workspace = true
|
|
|
|
[lints.rust]
|
|
unsafe_code = "forbid"
|
|
missing_docs = "warn"
|
|
|
|
[lints.clippy]
|
|
all = "warn"
|
|
pedantic = "warn"
|
|
nursery = "warn"
|
|
module_name_repetitions = "allow"
|
|
missing_const_for_fn = "allow"
|
|
missing_panics_doc = "allow"
|