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

nordisk/gunnar

crates/gunnar-grpc/tests/key_enrolling.rs

//! **A key the installer FED a primary, and its pairing loop has not enrolled
//! yet, is answered `Unavailable [key-enrolling]` — ask again — never
//! `Unauthenticated`** (cov-E r3 2026-10-06, run 3).
//!
//! MEASURED: the front's twin-certification loop called the primary with the
//! front's certify key from 14:39:18 on, and every answer for five minutes was
//! "Unauthenticated: the credential did not verify against any registered
//! key" — while the primary was enrolling that very key (its fed principals
//! took 4 m 47 s on a starved box, the certify key sixth in line). The log
//! gate failed the install on the retry loop. An enrolment that is not yet
//! possible is a "not yet", not a "who are you".
//!
//! Only the HOLDER of the fed key is told so: the signature is checked against
//! the fed key first, so a stranger presenting its fingerprint learns nothing
//! and is `NotVerified` as before.

use std::sync::Arc;

use gunnar_grpc::auth::{AuthFailure, AuthGate};
use gunnar_grpc::{
    sign_credential_for, AuthMode, ControlPlane, MemoryControlPlane, CREDENTIAL_METADATA_KEY,
    KEY_ENROLLING,
};
use gunnar_proto::PrincipalKind;
use tonic::Code;

fn mint() -> ssh_key::PrivateKey {
    ssh_key::PrivateKey::random(
        &mut rand::rand_core::UnwrapErr(rand::rngs::SysRng),
        ssh_key::Algorithm::Ed25519,
    )
    .expect("mint an ed25519 key")
}

fn openssh_of(key: &ssh_key::PrivateKey) -> String {
    key.public_key()
        .to_openssh()
        .expect("render the public key")
}

fn fingerprint_of(key: &ssh_key::PrivateKey) -> String {
    key.public_key()
        .fingerprint(ssh_key::HashAlg::Sha256)
        .to_string()
}

fn now_ms() -> i64 {
    std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .expect("after 1970")
        .as_millis() as i64
}

fn presented(credential: &gunnar_grpc::Credential) -> tonic::metadata::MetadataMap {
    let mut metadata = tonic::metadata::MetadataMap::new();
    metadata.insert(
        CREDENTIAL_METADATA_KEY,
        credential.to_header_value().parse().expect("ascii"),
    );
    metadata
}

/// A primary whose installer fed it `fed` (the front's certify key) and whose
/// pairing loop has not enrolled it yet.
fn primary_awaiting(fed: &ssh_key::PrivateKey) -> (Arc<MemoryControlPlane>, AuthGate) {
    let plane = Arc::new(MemoryControlPlane::new());
    plane.await_enrolment(&fingerprint_of(fed), &openssh_of(fed));
    let control: Arc<dyn ControlPlane> = plane.clone();
    (plane, AuthGate::new(control, AuthMode::Required))
}

#[test]
fn the_holder_of_a_fed_key_not_enrolled_yet_is_told_to_ask_again_not_that_it_is_unknown() {
    let certify = mint();
    let (_, gate) = primary_awaiting(&certify);
    let credential = sign_credential_for(&certify, now_ms(), "nonce-enrolling-01", "Admin.Health")
        .expect("sign");
    let refused = gate.authenticate_bound(&presented(&credential), now_ms(), Some("Admin.Health"));
    assert_eq!(
        refused,
        Err(AuthFailure::Enrolling),
        "the front's certify key, fed to this primary and not enrolled yet, was answered as a stranger's"
    );
    let status = AuthFailure::Enrolling.status();
    assert_eq!(
        status.code(),
        Code::Unavailable,
        "a key being enrolled is a `not yet`: Unavailable, never Unauthenticated"
    );
    assert!(
        status.message().contains(KEY_ENROLLING),
        "the answer carries its tag, so the caller and the request log can key on it: {}",
        status.message()
    );
    assert_eq!(AuthFailure::Enrolling.reason(), "enrolling");
}

#[test]
fn a_stranger_naming_a_fed_keys_fingerprint_is_not_verified_as_before() {
    let certify = mint();
    let stranger = mint();
    let (_, gate) = primary_awaiting(&certify);
    // The fed key's fingerprint on the header, the stranger's signature under it.
    let mut credential =
        sign_credential_for(&stranger, now_ms(), "nonce-enrolling-02", "Admin.Health")
            .expect("sign");
    credential.fingerprint = fingerprint_of(&certify);
    assert_eq!(
        gate.authenticate_bound(&presented(&credential), now_ms(), Some("Admin.Health")),
        Err(AuthFailure::NotVerified),
        "a stranger naming a fed key's fingerprint must learn nothing: NotVerified, as for any unknown key"
    );
    // And a key nobody fed is unknown, as ever.
    let unknown = sign_credential_for(&stranger, now_ms(), "nonce-enrolling-03", "Admin.Health")
        .expect("sign");
    assert_eq!(
        gate.authenticate_bound(&presented(&unknown), now_ms(), Some("Admin.Health")),
        Err(AuthFailure::NotVerified)
    );
}

#[test]
fn once_the_pairing_loop_enrolled_it_the_fed_key_is_a_caller() {
    let certify = mint();
    let (plane, gate) = primary_awaiting(&certify);
    let principal = plane
        .add_principal("front-certify", "front-certify", PrincipalKind::Machine)
        .expect("principal");
    plane
        .add_key(
            &principal.id,
            &openssh_of(&certify),
            "fed by the installer",
            None,
            "",
            None,
        )
        .expect("key registered");
    let credential = sign_credential_for(&certify, now_ms(), "nonce-enrolling-04", "Admin.Health")
        .expect("sign");
    let caller = gate
        .authenticate_bound(&presented(&credential), now_ms(), Some("Admin.Health"))
        .expect("the enrolled key verifies");
    assert_eq!(caller.principal_id, "front-certify");
}