use super::super::*;
use super::{deps, enrolled, provisioned, request};
use crate::registry::Grade;
use crate::wire::rendezvous::material::{PUBLIC, SALT};
use serde_json::{Value, json};
use tempfile::tempdir;
const CAPACITY: [(&str, usize); 4] = [("L", 2953), ("M", 2331), ("Q", 1663), ("H", 1273)];
fn envelope(enrolled: &Enrolled) -> String {
let mut value: Value = json!({
"yog-enroll": 1,
"grade": enrolled.grade.word(),
"name": enrolled.name,
"address": enrolled.address,
"ca": enrolled.ca,
"cert": enrolled.cert,
"key": enrolled.key,
});
if let (Some(object), Some(handoff)) = (value.as_object_mut(), &enrolled.rendezvous) {
object.insert("rendezvous_pub".into(), json!(handoff.public));
object.insert("pairing_salt".into(), json!(handoff.salt));
}
value.to_string()
}
#[test]
fn the_envelope_fits_a_version_40_qr_code() {
let tmp = tempdir().expect("tmp");
let (deps, _) = provisioned(&tmp);
let answer = enrolled(enroll(&deps, "7", &request("phone-1", Grade::Foot)).expect("act"));
let size = envelope(&answer).len();
assert!(
(1600..1850).contains(&size),
"the envelope measured {size} bytes; REMOTE §8.4 records ~1.7 kB, so the encoding \
ruling needs re-taking"
);
for (level, capacity) in CAPACITY {
assert_eq!(
size <= capacity,
matches!(level, "L" | "M"),
"level {level} carries {capacity} bytes and the envelope is {size}"
);
}
}
#[test]
fn the_envelope_carries_exactly_the_six_facts_and_a_version() {
let tmp = tempdir().expect("tmp");
let (deps, dir) = provisioned(&tmp);
let answer = enrolled(enroll(&deps, "7", &request("phone-1", Grade::Operator)).expect("act"));
let value: Value = serde_json::from_str(&envelope(&answer)).expect("compact JSON");
let object = value.as_object().expect("an object");
assert_eq!(object.get("yog-enroll"), Some(&json!(1)), "the marker");
let mut keys: Vec<&str> = object.keys().map(String::as_str).collect();
keys.sort_unstable();
assert_eq!(
keys,
[
"address",
"ca",
"cert",
"grade",
"key",
"name",
"pairing_salt",
"rendezvous_pub",
"yog-enroll"
]
);
let file = |name: &str| read(&dir.join(name)).expect("minted").trim().to_owned();
assert_eq!(object.get("rendezvous_pub"), Some(&json!(file(PUBLIC))));
assert_eq!(object.get("pairing_salt"), Some(&json!(file(SALT))));
assert!(answer.ca.contains("-----BEGIN CERTIFICATE-----\n"));
assert!(envelope(&answer).contains("-----BEGIN CERTIFICATE-----\\n"));
}
#[test]
fn a_loopback_box_omits_the_rendezvous_pair() {
let tmp = tempdir().expect("tmp");
let world = crate::test_support::world_under(tmp.path());
let dir = material::dir(&world);
provision::mint(&dir, "127.0.0.1:7737", &[], false).expect("mint");
assert!(!dir.join(PUBLIC).exists() && !dir.join(SALT).exists());
let deps = deps(&world, &tmp.path().join("state-root"));
let answer = enrolled(enroll(&deps, "7", &request("phone-1", Grade::Foot)).expect("act"));
assert_eq!(answer.rendezvous, None);
let text = envelope(&answer);
assert!(!text.contains("rendezvous_pub") && !text.contains("pairing_salt"));
assert!(text.len() <= CAPACITY[2].1, "{} bytes fit Q", text.len());
}
#[test]
fn half_the_pair_refuses_on_the_wire_and_on_disk() {
let mut reply = serde_json::from_str::<Value>(
r#"{"ok":true,"kind":"enrolled","grade":"foot","name":"n","address":"h:1","ca":"","cert":"","key":"","rendezvous_pub":"00"}"#,
)
.expect("json");
let refusal = crate::boundary::reply::decode(&reply).expect_err("half");
assert!(refusal.contains("travel together"), "{refusal}");
reply["pairing_salt"] = json!("11");
assert!(crate::boundary::reply::decode(&reply).is_ok());
let tmp = tempdir().expect("tmp");
let (deps, dir) = provisioned(&tmp);
std::fs::remove_file(dir.join(SALT)).expect("rm");
let refusal = enroll(&deps, "7", &request("phone-1", Grade::Foot)).expect_err("half");
assert!(refusal.contains(SALT), "{refusal}");
assert!(!dir.join("phone-1.pem").exists(), "nothing minted");
}