98 lines
3.7 KiB
Rust
98 lines
3.7 KiB
Rust
//! Acceptance tests for ADR-120 §2.3 ↔ ADR-118 §2.1 wiring — `SignatureHasher`
|
|
//! derives `rf_signature_hash` end-to-end through `BfldEmitter`.
|
|
|
|
#![cfg(feature = "std")]
|
|
|
|
use wifi_densepose_bfld::{
|
|
BfldEmitter, IdentityEmbedding, SensingInputs, SignatureHasher, EMBEDDING_DIM, SITE_SALT_LEN,
|
|
};
|
|
|
|
fn salt(seed: u8) -> [u8; SITE_SALT_LEN] {
|
|
let mut s = [0u8; SITE_SALT_LEN];
|
|
for (i, b) in s.iter_mut().enumerate() {
|
|
*b = seed.wrapping_add(i as u8);
|
|
}
|
|
s
|
|
}
|
|
|
|
fn embedding(seed: u8) -> IdentityEmbedding {
|
|
let mut a = [0.0f32; EMBEDDING_DIM];
|
|
for (i, v) in a.iter_mut().enumerate() {
|
|
*v = (i as f32 + seed as f32) * 0.001;
|
|
}
|
|
IdentityEmbedding::from_raw(a)
|
|
}
|
|
|
|
fn inputs(seed: u8) -> SensingInputs {
|
|
SensingInputs {
|
|
timestamp_ns: 1_700_000_000_000_000_000 + (seed as u64) * 1_000_000_000,
|
|
presence: true,
|
|
motion: 0.5,
|
|
person_count: 1,
|
|
sensing_confidence: 0.9,
|
|
sep: 0.2,
|
|
stab: 0.2,
|
|
consist: 0.2,
|
|
risk_conf: 0.2,
|
|
rf_signature_hash: Some([0xFF; 32]), // caller-supplied "wrong" hash
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn no_hasher_passes_caller_supplied_hash_through() {
|
|
let mut e = BfldEmitter::new("seed-01");
|
|
let out = e.emit(inputs(0), Some(embedding(0))).unwrap();
|
|
assert_eq!(out.rf_signature_hash, Some([0xFF; 32]));
|
|
}
|
|
|
|
#[test]
|
|
fn installed_hasher_overrides_caller_supplied_hash() {
|
|
let mut e = BfldEmitter::new("seed-01").with_signature_hasher(SignatureHasher::new(salt(7)));
|
|
let out = e.emit(inputs(0), Some(embedding(0))).unwrap();
|
|
let hash = out.rf_signature_hash.unwrap();
|
|
assert_ne!(hash, [0xFF; 32], "derived hash must override caller-supplied");
|
|
assert_ne!(hash, [0x00; 32], "derived hash must be non-trivial");
|
|
}
|
|
|
|
#[test]
|
|
fn same_emitter_same_inputs_produce_same_hash() {
|
|
let mut e_a = BfldEmitter::new("seed-01").with_signature_hasher(SignatureHasher::new(salt(7)));
|
|
let mut e_b = BfldEmitter::new("seed-01").with_signature_hasher(SignatureHasher::new(salt(7)));
|
|
let a = e_a.emit(inputs(0), Some(embedding(0))).unwrap();
|
|
let b = e_b.emit(inputs(0), Some(embedding(0))).unwrap();
|
|
assert_eq!(a.rf_signature_hash, b.rf_signature_hash);
|
|
}
|
|
|
|
#[test]
|
|
fn different_site_salts_produce_different_hashes_end_to_end() {
|
|
let mut e_a = BfldEmitter::new("seed-01").with_signature_hasher(SignatureHasher::new(salt(1)));
|
|
let mut e_b = BfldEmitter::new("seed-02").with_signature_hasher(SignatureHasher::new(salt(2)));
|
|
// Same embedding, same inputs → different sites must produce different hashes.
|
|
let a = e_a.emit(inputs(0), Some(embedding(0))).unwrap();
|
|
let b = e_b.emit(inputs(0), Some(embedding(0))).unwrap();
|
|
assert_ne!(
|
|
a.rf_signature_hash, b.rf_signature_hash,
|
|
"cross-site emit must produce uncorrelated hashes",
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn no_embedding_falls_back_to_risk_factor_bytes() {
|
|
let mut e = BfldEmitter::new("seed-01").with_signature_hasher(SignatureHasher::new(salt(5)));
|
|
let out = e.emit(inputs(0), None).unwrap();
|
|
let hash = out.rf_signature_hash.unwrap();
|
|
assert_ne!(hash, [0xFF; 32]); // still derived (fallback path), not caller-supplied
|
|
}
|
|
|
|
#[test]
|
|
fn fallback_hash_differs_from_embedding_hash() {
|
|
let mut e_with = BfldEmitter::new("seed-01").with_signature_hasher(SignatureHasher::new(salt(9)));
|
|
let mut e_without = BfldEmitter::new("seed-01").with_signature_hasher(SignatureHasher::new(salt(9)));
|
|
let with_emb = e_with.emit(inputs(0), Some(embedding(0))).unwrap();
|
|
let no_emb = e_without.emit(inputs(0), None).unwrap();
|
|
assert_ne!(
|
|
with_emb.rf_signature_hash, no_emb.rf_signature_hash,
|
|
"embedding bytes and risk-factor bytes should hash to different values",
|
|
);
|
|
}
|