Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/Skidbladnir

crates/installer/plugin-door-wire/tests/attach_installed.rs

//! **An attached estate's install notice** (5.4 ONE monetize): the enrolled owner tells the
//! central which of its boxes will call, signed with the ENROLLED key; anything else is refused
//! by name.

use plugin_door_wire::attach::{InstalledNotice, Refusal, MAX_SKEW_MS, VERSION};

fn owner(seed: u8) -> ssh_key::PrivateKey {
    ssh_key::PrivateKey::from(ssh_key::private::Ed25519Keypair::from_seed(&[seed; 32]))
}

fn notice(roles: &[&str], at: i64) -> InstalledNotice {
    InstalledNotice { v: VERSION, estate: "test2-gunnar-rs".into(), product: "gunnar".into(), roles: roles.iter().map(|r| r.to_string()).collect(), nonce: "0123456789abcdef01".into(), at_unix_ms: at, ticket_key: String::new(), fleet_keys: Default::default(), owner_sig: String::new() }
}

const NOW: i64 = 1_790_000_000_000;

#[test]
fn the_enrolled_owner_names_the_boxes_that_will_call_and_nothing_else_does() {
    let enrolled = owner(1);
    let key = enrolled.public_key().to_openssh().unwrap();
    let n = notice(&["main", "twin"], NOW).sign(&enrolled).unwrap();
    n.verify(&key, NOW).expect("the enrolled owner's notice verifies");

    let stranger = notice(&["main", "twin"], NOW).sign(&owner(2)).unwrap();
    assert!(matches!(stranger.verify(&key, NOW), Err(Refusal::Signature(_))), "another key never opens a pin window");
    let mut altered = n.clone();
    altered.roles.push("front".into());
    assert!(matches!(altered.verify(&key, NOW), Err(Refusal::Signature(_))), "the roles are signed");
    assert!(matches!(n.verify(&key, NOW + MAX_SKEW_MS + 1), Err(Refusal::Clock { .. })));
    for bad in [notice(&[], NOW), notice(&["Main"], NOW), notice(&["a", "b", "c", "d", "e", "f", "g", "h", "i"], NOW)] {
        let bad = bad.sign(&enrolled).unwrap();
        assert!(matches!(bad.verify(&key, NOW), Err(Refusal::Shape(_))), "{:?}", bad.roles);
    }
    let short = InstalledNotice { nonce: "x".into(), ..notice(&["main"], NOW) }.sign(&enrolled).unwrap();
    assert!(matches!(short.verify(&key, NOW), Err(Refusal::Shape(_))));
    assert!(String::from_utf8(n.signed_bytes()).unwrap().starts_with("monetize-attach-installed v1\n"), "never an enrolment's bytes");
}

/// An enrolment answered by a central that mints no estate bearer still parses (the field is new).
#[test]
fn a_reply_without_an_estate_bearer_still_parses() {
    let old = r#"{"cert_pem":"c","attach_ca_pem":"a","monetize_identity":"m","monetize_pubkey":"00","plugin_door":"x:1","product":"gunnar"}"#;
    let r: plugin_door_wire::attach::EnrolReply = serde_json::from_str(old).unwrap();
    assert!(r.estate_bearer.is_empty());
}

/// ★ **The site's ticket key rides the owner's notice** (STINKY ST-3, 2026-10-11): the
/// estate's elected primary's `ticket_pubkey` (64 hex) — the key its main signs its accounts'
/// actor tickets with — is carried to the ONE box by the ENROLLED owner, signed, so the box
/// learns each site's key from the estate itself and no operator hand-fills it. A notice with
/// none still signs and verifies as before (an older kit); a key that is not 64 hex is refused
/// by shape; a key swapped in transit breaks the signature.
#[test]
fn the_sites_ticket_key_rides_the_owners_notice_signed() {
    let enrolled = owner(1);
    let key = enrolled.public_key().to_openssh().unwrap();
    let plain = notice(&["main", "twin"], NOW);
    let before = String::from_utf8(plain.signed_bytes()).unwrap();
    assert!(!before.contains("ticket_key"), "a notice with no key signs the bytes it always did: {before}");
    let t = "ab".repeat(32);
    let keyed = InstalledNotice { ticket_key: t.clone(), ..notice(&["main", "twin"], NOW) }.sign(&enrolled).unwrap();
    keyed.verify(&key, NOW).expect("the owner's keyed notice verifies");
    assert!(String::from_utf8(keyed.signed_bytes()).unwrap().contains(&format!("ticket_key \"{t}\"\n")), "the key is signed");
    let mut swapped = keyed.clone();
    swapped.ticket_key = "cd".repeat(32);
    assert!(matches!(swapped.verify(&key, NOW), Err(Refusal::Signature(_))), "a key swapped in transit is refused");
    for bad in ["zz".repeat(32), "ab".repeat(31), "AB".repeat(32)] {
        let n = InstalledNotice { ticket_key: bad.clone(), ..notice(&["main"], NOW) }.sign(&enrolled).unwrap();
        assert!(matches!(n.verify(&key, NOW), Err(Refusal::Shape(_))), "{bad}");
    }
    let wire = serde_json::to_string(&notice(&["main"], NOW)).unwrap();
    assert!(!wire.contains("ticket_key"), "an empty key is not on the wire: {wire}");
    let old: InstalledNotice = serde_json::from_str(r#"{"v":1,"estate":"a","product":"gunnar","roles":["main"],"nonce":"0123456789abcdef","at_unix_ms":1}"#).unwrap();
    assert!(old.ticket_key.is_empty(), "an older kit's notice parses");
}

/// ★ **Each box's fleet key rides the owner's notice, signed** (STINKY ST-14): the owner names
/// the key every box will call with, so the central pins THAT key and no first caller inside a
/// window can take the role. A key swapped in transit breaks the signature; a key for a role
/// the notice does not name, or one that is not 64 hex, is refused by shape; a notice with
/// none signs the bytes it always did.
#[test]
fn each_boxs_fleet_key_rides_the_owners_notice_signed() {
    let enrolled = owner(1);
    let key = enrolled.public_key().to_openssh().unwrap();
    let before = String::from_utf8(notice(&["main", "twin"], NOW).signed_bytes()).unwrap();
    assert!(!before.contains("fleet"), "no keys, the old bytes: {before}");
    let keys: std::collections::BTreeMap<String, String> = [("main".to_string(), "aa".repeat(32)), ("twin".to_string(), "bb".repeat(32))].into();
    let keyed = InstalledNotice { fleet_keys: keys.clone(), ..notice(&["main", "twin"], NOW) }.sign(&enrolled).unwrap();
    keyed.verify(&key, NOW).expect("the owner's keyed notice verifies");
    let bytes = String::from_utf8(keyed.signed_bytes()).unwrap();
    assert!(bytes.contains(&format!("fleet \"main\" \"{}\"\n", "aa".repeat(32))), "signed: {bytes}");
    let mut swapped = keyed.clone();
    swapped.fleet_keys.insert("main".into(), "cc".repeat(32));
    assert!(matches!(swapped.verify(&key, NOW), Err(Refusal::Signature(_))), "a swapped key breaks the signature");
    let stray = InstalledNotice { fleet_keys: [("front".to_string(), "aa".repeat(32))].into(), ..notice(&["main"], NOW) }.sign(&enrolled).unwrap();
    assert!(matches!(stray.verify(&key, NOW), Err(Refusal::Shape(_))), "a key for a role not named");
    let bad = InstalledNotice { fleet_keys: [("main".to_string(), "XY".into())].into(), ..notice(&["main"], NOW) }.sign(&enrolled).unwrap();
    assert!(matches!(bad.verify(&key, NOW), Err(Refusal::Shape(_))), "not 64 hex");
    let wire = serde_json::to_string(&notice(&["main"], NOW)).unwrap();
    assert!(!wire.contains("fleet_keys"), "none, not on the wire: {wire}");
}