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

nordisk/gunnar

crates/gunnar-core/tests/pq_recipient_key.rs

//! A member's QUANTUM-SAFE encryption key (crypt-pq): an `age1pq1…` line
//! (age's hybrid ML-KEM-768 + X25519) registered beside their SSH keys, so
//! `Repos.RecipientKeys` can hand it to a member wrapping a quantum-safe
//! repository's key ring. It is never a login key and never attests.

mod common;

use common::Fixture;
use gunnar_core::auth::{pq_recipient_fingerprint, PQ_RECIPIENT_ALGO};
use gunnar_core::model::PrincipalKind;
use gunnar_core::Principal;

/// A valid recipient (the rage test pair, str4d/rage#632).
const PQ: &str = "age1pq1x34nzsvr0rxjsgdn8zgyhfe8j7ceq5r9rdelkjuh3y235jzxshfg87pzf5zrqtzdxz95paef6caq5aapdmwjjqpjfdyxnzr2zampc3uxy0dg4z2n2gm9su72p0pc3u0jvev55l694v78snxg3yzvcl7yda0eyytqj6a0ec477lnhcy5hzpz4zq3pxanve4cn62gqj3pjy5lqj9c6kyj4v2z8alktn8zh99970x79gjkv7522hv9kfz35zsnxhsx8wwtmu9cy3ftzjgwcp4sshn3llnylnpdsyz5jm72vefv4x5vfwytrefxg4wq3mv42wcrvkj742479zrxzpvp2p3e9fed9f0739vcu80r7ma28qfhnvlv4gfzel9q654dj3zmuvvz893azhxdvs9fxd0r7jzchzcfcs5mkyyjxhw0n2z6dvp9yn9qfdp29h0azxqyjw6v7fhyuzj7zel0uq6j9rd7wgrpz7mf5dnj43jwsgvrc8qcnhy7tu6dkdujuxzkp9xj43xe8h92ktre2a3u3s8mm5mrp9nr9pwkgtz4mdlq9hgn4fps4k57ff6wddn2fy23t47sm20r8km8sd2pcyyafnet8f0dajsrlyjeah4n3mssr6aseevuuskdvq5lzguyvpgwpta742c6698vgutzqgny8usfg0w2he7kq5vyxjd0f9hqg8xk26y9e4th0gezq92q4cpp5p2y9hf5f2cje5l0c3sa3a2qxmm38pxxvhxh99yzmfz0zk7r2s64nnwjhkfgfr3gf8xnmppcgmaykvh5sh6g7vk9790rf8ws0axmr2t7z8aae5fq2029uvcn2ghgt4fu4wgwdc0k0cz52qkvwmuzj8p8k5jgf3xzk5zmrkavjekjrpeq408xz3zxazwkc6tyfmhayrkfpjhwtz5mp8j8guqe43k2q6m2kte03vrw27y3wmqyu5etmt9dnkwcnnpmu9gz9dekfhdevf42ucshphnrk38ra6hx8w5f8q5ru0xdhrjxmwqf6cused7zc5xvq43r0zscjglpwlptpwydhqw64xz7ptjdyeyzpq2zkxtmzg29gzjpvzva4d3l0cenn9xs297wf4y4ukwrunf57xj6pm7nvrkwvtrt8hwcmgv8x7ajw7258ugf9wvkmk4052ekg87tw5vnx8nq2swyzv77v8yqlwsenvamr0zssknwts8rrhfuwj7ykysnq9jxy0uv3kuyt22djszjdtvpz6d0s0kwh8ryynddzud92emeyvvyqktd0jtj7rvvg5gch25v8smlvny3kvn5gagyz475ze2y6q466xqmz2n3hs77lddeqyta2nch5k2u5yacuk9ywnwfdzvyejnucz724hj77hrrmakm7pr3kxsrxq22ejexlud9fy2kdqmkg5yncz7jm5wv2qjk5w5kvcpqsry2yqffh2la52dxfjkjq5rzhjzeyn6dupn0qwtyv7s4lwg3xdarsdlwe2y3tujy480y7z39q259fzx6jhd2j0f5hagqpcpees7hzc2yrk5cy788uk3s7qvp5cpepx24gvws3m2g433exgwppnkjscec8qu4y9z9r7vccexjcjaen42245lmgmxmuavg9alej92322gvvyy2t6267v09ch64y0m53jff0vjj96s0ypk60hr3jw4myd6m5hpn3xjstx7tl2szhpr5qe8jj08ydjc4wy2rch2fhuy3pdfjax5awe9j99ly5hkntzz9fe5zatgjvzdd0kgtxs25njnajyf6ssekp7gelxquusn4pt25czh3scj68kq79wdn5tgm6yvm9nzavrg043x3msnygf8dweknw5jmqd0uvny6ttsn09508k0c55zfnegrm9efhxpfqdkmhh6gjtqmwze9pyyzk3tlhl53k2ykx3qheyty7saeq0d3fzv49zc0k";

#[test]
fn an_age1pq_key_is_registered_beside_ssh_keys_and_never_attests() {
    let fx = Fixture::new();
    fx.auth
        .add_principal(Principal::new("bob", "bob", PrincipalKind::User).unwrap())
        .unwrap();
    let ssh = {
        let private = gunnar_core::mint_ed25519().unwrap();
        let parsed = gunnar_core::ssh_key::PrivateKey::from_openssh(&private).unwrap();
        parsed.public_key().to_openssh().unwrap()
    };
    fx.auth.register_key("bob", &ssh, None).unwrap();
    let row = fx
        .auth
        .register_key("bob", PQ, None)
        .expect("an age1pq1 key registers");
    assert_eq!(row.algo, PQ_RECIPIENT_ALGO);
    assert_eq!(row.openssh, PQ);
    assert_eq!(
        Some(row.fingerprint_sha256.as_str()),
        pq_recipient_fingerprint(PQ).as_ref().map(|f| f.as_str())
    );

    let keys = fx.auth.keys_of("bob").unwrap();
    assert_eq!(keys.len(), 2);
    assert!(keys.iter().any(|k| k.openssh == PQ));
    let attesting = fx.auth.attesting_keys_of("bob").unwrap();
    assert_eq!(
        attesting.len(),
        1,
        "only the SSH key can sign an attestation"
    );
    assert!(attesting.iter().all(|k| k.openssh != PQ));

    // The same key twice is refused, as for an SSH key; a damaged one never registers.
    assert!(fx.auth.register_key("bob", PQ, None).is_err());
    let damaged = format!("{}x", &PQ[..PQ.len() - 1]);
    assert!(pq_recipient_fingerprint(&damaged).is_none());
    assert!(fx.auth.register_key("bob", &damaged, None).is_err());
}

/// **An expired key is never a recipient** (wiring tracer B 2026-10-08): the
/// answer `Repos.RecipientKeys` and the forge's door give is the principal's
/// keys that are valid NOW, never one its holder retired.
#[test]
fn recipient_keys_leave_out_an_expired_key() {
    let fx = Fixture::new();
    fx.auth
        .add_principal(Principal::new("bob", "bob", PrincipalKind::User).unwrap())
        .unwrap();
    let line = || {
        let private = gunnar_core::mint_ed25519().unwrap();
        let parsed = gunnar_core::ssh_key::PrivateKey::from_openssh(&private).unwrap();
        parsed.public_key().to_openssh().unwrap()
    };
    let live = fx.auth.register_key("bob", &line(), None).unwrap();
    let old = fx
        .auth
        .register_key("bob", &line(), Some(1))
        .expect("a key that expired in 1970");
    let pq = fx.auth.register_key("bob", PQ, None).unwrap();
    let mut got: Vec<String> = fx
        .auth
        .recipient_keys_of("bob")
        .unwrap()
        .into_iter()
        .map(|k| k.fingerprint_sha256)
        .collect();
    got.sort();
    let mut want = vec![live.fingerprint_sha256, pq.fingerprint_sha256];
    want.sort();
    assert_eq!(
        got, want,
        "the expired {} is a recipient",
        old.fingerprint_sha256
    );
}