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/issuer.rs

//! **The certificate-identity plan: the server is its own certificate issuer**
//! — the refusals first.
//!
//! This is an authentication boundary. A half-tested one is worse than none:
//! the failure mode is not a broken build but a server that accepts the wrong
//! client, and nothing in a suite of green tests would say so. So every
//! acceptance in this file is preceded by the refusal it is the other half of,
//! and every "the secret is not here" assertion is preceded by a probe that
//! proves the same search **can** find it when it is.
//!
//! # What is checked with what
//!
//! `rcgen` mints. **`openssl` verifies, and `openssl` writes the CSRs.** They
//! share no code, which is the whole point: a library confirming its own output
//! would agree with itself even if both halves were wrong the same way. The one
//! test that most matters is the one `bench/src/trust.rs` already establishes
//! the pattern for — an **unrelated CA must refuse** what this issuer signed,
//! because without that, "the CA verifies it" is satisfied by a chain any CA
//! would accept, which is not a trust root at all.
//!
//! # Why the storage assertions read files rather than call getters
//!
//! `put` then `get` proves a codec round-trips and proves nothing about where
//! the bytes went. The private key assertions here read `control.redb` and
//! `secrets.arrow` off the disk as bytes and search them, exactly as
//! `tests/sealed_secrets.rs` does and for the same reason.

use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::Arc;

use gunnar_core::arrow_log::ArrowLogStore;
use gunnar_core::issuer::{DEFAULT_ISSUER_DAYS, DEFAULT_LEAF_DAYS};
use gunnar_core::{ControlStore, Error, Issued, Kek, Pki, SealedSecretStore};
use gunnar_store::{SecretStore, ISSUER_PRIVATE_KEY};

/// The whole server-side world one test needs: a redb control database and a
/// sealed Arrow secret log, both **on disk** so their bytes can be read back.
struct Server {
    dir: tempfile::TempDir,
    pki: Pki,
    control: Arc<dyn ControlStore>,
    /// The backend under the seal, so a test can look at ciphertext.
    raw_secrets: Arc<dyn SecretStore>,
    /// The same store with the seal on top: what the server itself uses.
    sealed: Arc<dyn SecretStore>,
    kek: Kek,
}

impl Server {
    fn new() -> Server {
        let dir = tempfile::tempdir().expect("tempdir");
        let control = gunnar_core::open_control_store(dir.path().join("control.redb"))
            .expect("open the control store");
        let log = ArrowLogStore::open(dir.path(), gix_hash::Kind::Sha256).expect("open the log");
        let raw_secrets: Arc<dyn SecretStore> = Arc::new(log.secrets());
        let kek = Kek::generate().expect("a KEK");
        let sealed: Arc<dyn SecretStore> = Arc::new(SealedSecretStore::new(
            Arc::clone(&raw_secrets),
            kek.clone(),
        ));
        let pki = Pki::new(Arc::clone(&control), Arc::clone(&sealed)).expect("open the pki");
        Server {
            dir,
            pki,
            control,
            raw_secrets,
            sealed,
            kek,
        }
    }

    /// The issuer private key as the server itself reads it: through the seal.
    ///
    /// This is the ONE door in this file to the plaintext. Every "the key is
    /// not in that file" assertion searches for what this returns, so if the
    /// unsealing were broken the searches would be looking for the wrong bytes
    /// — which is why the tests that use it also assert it looks like a PEM
    /// private key before searching.
    fn pki_secret_key(&self) -> Option<Vec<u8>> {
        self.sealed
            .get(ISSUER_PRIVATE_KEY)
            .expect("reading the sealed secret store")
    }

    fn control_db(&self) -> PathBuf {
        self.dir.path().join("control.redb")
    }

    fn secrets_log(&self) -> PathBuf {
        self.dir.path().join("secrets.arrow")
    }
}

/// Does `path`, as it exists on disk right now, contain `needle`?
///
/// One definition, shared by the red probes and the green assertions, so the
/// detector and the thing it is trusted for cannot drift apart (one implementation). An
/// empty file is rejected outright: "not found" in an empty file is shaped
/// exactly like a clean result.
fn file_contains(path: &Path, needle: &[u8]) -> bool {
    let bytes = std::fs::read(path)
        .unwrap_or_else(|err| panic!("could not read {}: {err}", path.display()));
    assert!(
        !bytes.is_empty(),
        "{} is empty, so this detector would answer `absent` for everything",
        path.display()
    );
    assert!(!needle.is_empty(), "an empty needle is found everywhere");
    bytes.windows(needle.len()).any(|window| window == needle)
}

/// The base64 body of a PEM, in each shape a file here can store it in.
///
/// 1. joined by real newlines — a PEM written verbatim, as `secrets.arrow`
///    carries an unsealed one;
/// 2. joined by the two characters `\n` — a PEM inside a serde_json string,
///    which is how a control document carries one;
/// 3. joined by nothing — the same DER as one continuous base64 run, which is
///    how `AuthTable::register_certificate` stores a certificate.
fn pem_shapes(pem: &str) -> Vec<String> {
    let body: Vec<&str> = pem
        .lines()
        .map(str::trim)
        .filter(|line| !line.starts_with("-----") && !line.is_empty())
        .collect();
    assert!(
        !body.is_empty(),
        "this PEM has no body at all; a detector built on it would answer `absent` whatever the \
         file held"
    );
    vec![body.join("\n"), body.join("\\n"), body.concat()]
}

/// How many characters of a shape one needle covers, and how far apart the
/// needles start.
///
/// 40 base64 characters is 30 bytes: far past any chance collision, and short
/// enough that a value split across two redb pages still leaves many needles
/// wholly inside one page. The stride is what makes that true — see
/// [`secret_needles`].
const NEEDLE: usize = 40;
const STRIDE: usize = 5;

/// Every window of `text` that a search may look for.
fn windows_of(text: &str) -> Vec<Vec<u8>> {
    let bytes = text.as_bytes();
    if bytes.len() < NEEDLE {
        return vec![bytes.to_vec()];
    }
    (0..=bytes.len() - NEEDLE)
        .step_by(STRIDE)
        .map(|at| bytes[at..at + NEEDLE].to_vec())
        .collect()
}

/// **The needles that stand for "this private key is here", and nothing else.**
///
/// Two failures had to be designed out of this, and both were MEASURED on
/// 2026-08-09 against detectors that looked reasonable:
///
/// * **searching for any long LINE of the key gives a false positive.** A
///   PKCS#8 EC private key ends with the *public* key, and a certificate for
///   that key contains the same bytes; when the base64 alignment happens to
///   agree, a 56-character line from `ci_pkey.pem` is a substring of
///   `control.redb` — and it is the public key, sitting in a certificate where
///   it belongs. So the needles are drawn **only from the region before the
///   public key**, located by finding where the public point sits inside the
///   PKCS#8 DER and converting that byte offset to a base64 character offset.
///
/// * **searching for the WHOLE body gives a false negative.** redb splits a
///   value across pages: the issuer document in a real `--data-root` was
///   measured with its certificate's 8th and 9th body lines no longer adjacent
///   in the file. A single contiguous needle spanning the split is in no file
///   anywhere, so "absent" would be reported for a key that is right there. So
///   the needles are short and overlapping: a split can hide the handful that
///   straddle it and cannot hide the rest.
fn secret_needles(key_pem: &str) -> Vec<Vec<u8>> {
    use rcgen::KeyPair;

    let key = KeyPair::from_pem(key_pem).expect("the stored issuer key must parse");
    let der = key.serialize_der();
    let spki = rcgen::PublicKeyData::subject_public_key_info(&key);
    // The tail of the SubjectPublicKeyInfo is the raw public point, and that
    // same point is embedded in the PKCS#8 DER. Everything before it is private.
    let tail = &spki[spki.len().saturating_sub(16)..];
    let public_at = der
        .windows(tail.len())
        .position(|window| window == tail)
        .map(|at| at.saturating_sub(spki.len() - tail.len()))
        .unwrap_or(der.len());
    assert!(
        public_at > 16,
        "the private region of this key is {public_at} bytes, which is not a key; the needles \
         would prove nothing"
    );
    // 3 bytes of DER become 4 base64 characters, so a byte offset maps to a
    // character offset by 4/3 — rounded DOWN, so the bound never reaches into
    // the public material.
    let private_chars = public_at * 4 / 3;

    pem_shapes(key_pem)
        .iter()
        .flat_map(|shape| {
            let end = private_chars.min(shape.len());
            windows_of(&shape[..end])
        })
        .collect()
}

/// Does any needle for this private key appear in `path`?
fn file_carries_secret(path: &Path, key_pem: &str) -> bool {
    secret_needles(key_pem)
        .iter()
        .any(|needle| file_contains(path, needle))
}

/// Does this **public** PEM appear in `path`? Same window search, over the
/// whole body, so a positive control and a negative assertion are the same
/// mechanism and cannot drift apart (one implementation).
fn file_carries_pem(path: &Path, pem: &str) -> bool {
    pem_shapes(pem)
        .iter()
        .flat_map(|shape| windows_of(shape))
        .any(|needle| file_contains(path, &needle))
}

fn openssl_missing() -> bool {
    let missing = Command::new("openssl").arg("version").output().is_err();
    if missing {
        eprintln!("skipping: no openssl on PATH");
    }
    missing
}

fn write(path: &Path, contents: &str) {
    std::fs::write(path, contents)
        .unwrap_or_else(|err| panic!("writing {}: {err}", path.display()));
}

/// `openssl verify -CAfile <ca> <leaf>`: does this CA vouch for this leaf?
fn openssl_verifies(ca_pem: &str, leaf_pem: &str, dir: &Path) -> (bool, String) {
    let ca = dir.join("ca.pem");
    let leaf = dir.join("leaf.pem");
    write(&ca, ca_pem);
    write(&leaf, leaf_pem);
    let out = Command::new("openssl")
        .args(["verify", "-CAfile"])
        .arg(&ca)
        .arg(&leaf)
        .output()
        .expect("running openssl verify");
    (
        out.status.success(),
        String::from_utf8_lossy(&out.stderr).into_owned(),
    )
}

/// The subject line `openssl` reads out of a certificate, e.g. `subject=CN=alice`.
fn openssl_subject(cert_pem: &str, dir: &Path) -> String {
    let path = dir.join("subject.pem");
    write(&path, cert_pem);
    let out = Command::new("openssl")
        .args(["x509", "-noout", "-subject", "-in"])
        .arg(&path)
        .output()
        .expect("running openssl x509");
    assert!(
        out.status.success(),
        "openssl could not read the issued certificate at all:\n{}",
        String::from_utf8_lossy(&out.stderr)
    );
    // **Spaces around `=` removed, because openssl's own spelling moved.**
    //
    // OpenSSL 1.x printed `subject=CN=alice`; OpenSSL 3.x prints
    // `subject=CN = alice`. Every assertion in this file looks for `CN=alice`,
    // so on a current box they all failed against a certificate that was
    // perfectly correct — a test reporting on the version of openssl installed
    // rather than on what gunnar signed.
    //
    // Normalised here, once, rather than at each call site: matching both
    // spellings in five places is five chances to fix four of them.
    String::from_utf8_lossy(&out.stdout)
        .trim()
        .replace(" = ", "=")
}

/// A certificate signing request written by **openssl**, claiming `common_name`.
///
/// Deliberately not `rcgen::CertificateParams::serialize_request`: a CSR minted
/// by the same crate that signs it would exercise one encoder against its own
/// decoder, and the interoperability that matters is with the tooling an
/// operator actually has.
fn openssl_csr(dir: &Path, name: &str, common_name: &str) -> String {
    let key = dir.join(format!("{name}_key.pem"));
    let csr = dir.join(format!("{name}_csr.pem"));
    let out = Command::new("openssl")
        .args([
            "req",
            "-new",
            "-newkey",
            "ec",
            "-pkeyopt",
            "ec_paramgen_curve:P-256",
            "-nodes",
            "-keyout",
        ])
        .arg(&key)
        .arg("-out")
        .arg(&csr)
        .arg("-subj")
        .arg(format!("/CN={common_name}"))
        .output()
        .expect("running openssl req");
    assert!(
        out.status.success(),
        "openssl would not write a CSR:\n{}",
        String::from_utf8_lossy(&out.stderr)
    );
    std::fs::read_to_string(&csr).expect("reading the CSR")
}

/// Corrupt one base64 character deep in the body of a PEM request — the first
/// of its LAST body line, which lands in the signature rather than in the
/// armour. The tamper must actually change a byte, or the "refusal" below
/// would be testing an untampered request.
fn tampered(csr: &str) -> String {
    let mut lines: Vec<String> = csr.lines().map(str::to_owned).collect();
    let last_body = lines.len() - 2;
    let line = &mut lines[last_body];
    let flipped: String = line
        .chars()
        .enumerate()
        .map(|(i, c)| match (i, c) {
            // ALWAYS a different character: one CSR in 64 already reads `A` here.
            (0, 'A') => 'B',
            (0, _) => 'A',
            _ => c,
        })
        .collect();
    assert_ne!(&flipped, line, "the tamper must actually change a byte");
    *line = flipped;
    format!("{}\n", lines.join("\n"))
}

// ─────────────────────── refusal 1: exactly one issuer ───────────────────────

/// **REFUSED: `issuer create` twice.**
///
/// The green half is inside it and is load-bearing: the *first* create must
/// succeed, or "the second was refused" is satisfied by an issuer that never
/// works at all. And the issuer that survives must be the **first** one — a
/// refusal that still overwrote the certificate or the key would leave every
/// certificate in the field unverifiable while reporting an error, which is the
/// worst of both.
#[test]
fn a_second_issuer_create_is_refused_and_changes_nothing() {
    let server = Server::new();

    let first = server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("the FIRST create must succeed, or the refusal below proves nothing");
    let key_after_first = server
        .raw_secrets
        .get(ISSUER_PRIVATE_KEY)
        .expect("reading the stored key")
        .expect("the first create stored a key");

    let refused = server.pki.create("a second issuer", DEFAULT_ISSUER_DAYS);
    match refused {
        Err(Error::IssuerExists {
            ref subject,
            ref fingerprint,
        }) => {
            assert_eq!(
                subject, "gunnar issuer",
                "the refusal names the LIVE issuer"
            );
            assert_eq!(fingerprint, first.fingerprint.as_str());
        }
        other => panic!(
            "a second `issuer create` must be REFUSED, not silently ignored and not accepted. \
             Got: {other:?}"
        ),
    }

    let live = server
        .pki
        .issuer()
        .expect("reading back")
        .expect("still one");
    assert_eq!(
        live, first,
        "the refused create must leave the FIRST issuer in place, byte for byte"
    );
    assert_eq!(
        server
            .raw_secrets
            .get(ISSUER_PRIVATE_KEY)
            .expect("reading the stored key")
            .expect("still there"),
        key_after_first,
        "the refused create must not have touched the stored private key; if it had, every \
         certificate this issuer signed would be unverifiable"
    );
    assert_eq!(
        server.pki.trusted_cas().expect("the CA list").len(),
        1,
        "and it must not have added a second entry to the trusted-CA list"
    );
}

/// **REFUSED: `issuer import` onto a server that already has one.**
///
/// The same rule, through the other door. `create` refusing while `import`
/// quietly replaced would mean "exactly one issuer" was a property of one code
/// path rather than of the server.
#[test]
fn importing_onto_a_server_that_has_an_issuer_is_refused() {
    let server = Server::new();
    let donor = Server::new();

    let live = server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("the first issuer");
    // A real, usable issuer from a second server — so what is refused here is a
    // valid import and not a malformed one.
    donor
        .pki
        .create("somebody else", DEFAULT_ISSUER_DAYS)
        .expect("the donor's issuer");
    let donor_cert = donor.pki.issuer().unwrap().unwrap().cert_pem;
    let donor_key =
        String::from_utf8(donor.pki_secret_key().expect("the donor stored a key")).expect("utf-8");

    match server.pki.import(&donor_cert, &donor_key) {
        Err(Error::IssuerExists { .. }) => {}
        other => panic!("importing onto a server that has an issuer must be REFUSED: {other:?}"),
    }

    assert_eq!(
        server.pki.issuer().unwrap().unwrap(),
        live,
        "the refused import must leave the live issuer exactly as it was"
    );
}

/// **REFUSED: importing a certificate and a key that are not each other's.**
///
/// Accepting the pair would produce a server that stores both and only
/// discovers it can mint nothing on the first `principal add`, by which time
/// the operator has moved on. Nothing must be written.
#[test]
fn importing_a_certificate_and_a_key_that_do_not_match_is_refused() {
    let a = Server::new();
    let b = Server::new();
    a.pki
        .create("ca a", DEFAULT_ISSUER_DAYS)
        .expect("a's issuer");
    b.pki
        .create("ca b", DEFAULT_ISSUER_DAYS)
        .expect("b's issuer");

    let a_cert = a.pki.issuer().unwrap().unwrap().cert_pem;
    let b_key = String::from_utf8(b.pki_secret_key().expect("b's key")).expect("utf-8");

    let fresh = Server::new();
    match fresh.pki.import(&a_cert, &b_key) {
        Err(Error::Pki(reason)) => {
            assert!(
                reason.contains("SubjectPublicKeyInfo"),
                "the refusal must say WHICH half did not match, got: {reason}"
            );
        }
        other => panic!("a mismatched certificate and key must be REFUSED: {other:?}"),
    }
    assert!(
        fresh.pki.issuer().unwrap().is_none(),
        "a refused import must store no issuer"
    );
    assert!(
        fresh.raw_secrets.get(ISSUER_PRIVATE_KEY).unwrap().is_none(),
        "a refused import must store no private key"
    );
}

// ─────────────────── refusal 2: no issuer, nothing is issued ─────────────────

/// **REFUSED: issuing on a server with no issuer**, by both doors.
#[test]
fn issuing_without_an_issuer_is_refused() {
    if openssl_missing() {
        return;
    }
    let server = Server::new();
    let csr = openssl_csr(server.dir.path(), "alice", "alice");

    match server.pki.issue_from_csr("alice", &csr, DEFAULT_LEAF_DAYS) {
        Err(Error::NoIssuer) => {}
        other => panic!("a server with no issuer must refuse a CSR: {other:?}"),
    }
    match server.pki.issue_with_server_key("alice", DEFAULT_LEAF_DAYS) {
        Err(Error::NoIssuer) => {}
        other => panic!("a server with no issuer must refuse to mint: {other:?}"),
    }
}

/// **REFUSED: a request whose self-signature does not verify.**
///
/// Proof of possession is the only thing a CSR exists to prove. One flipped
/// byte in the signature and the request no longer demonstrates that whoever
/// sent it holds the private half of the key inside it — at which point signing
/// it would bind a name to a key its holder never asked for.
#[test]
fn a_certificate_signing_request_that_does_not_verify_is_refused() {
    if openssl_missing() {
        return;
    }
    let server = Server::new();
    server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("an issuer");

    let good = openssl_csr(server.dir.path(), "alice", "alice");
    // The green half: the untampered request IS accepted, so the refusal below
    // is about the tampering and not about the CSR shape.
    server
        .pki
        .issue_from_csr("alice", &good, DEFAULT_LEAF_DAYS)
        .expect("an untampered request must be accepted");

    let tampered = tampered(&good);

    match server
        .pki
        .issue_from_csr("alice", &tampered, DEFAULT_LEAF_DAYS)
    {
        Err(Error::Pki(_)) => {}
        other => {
            panic!("a request that does not verify against its own key must be REFUSED: {other:?}")
        }
    }
}

/// **The tamper site already reads `A`.** One CSR in 64 has an `A` as the
/// first character of its last base64 line (a 6-bit value of zero); the tamper
/// must change THAT byte too, or the refusal test asserts on an untampered
/// request and fails on the `assert_ne!` (seen ~1 in 64 runs on loki).
#[test]
fn a_csr_whose_tamper_site_already_reads_a_is_still_tampered_and_refused() {
    // Deterministic: a request body whose last line begins with `A`.
    let synthetic =
        "-----BEGIN CERTIFICATE REQUEST-----\nMIIB\nAAAA\n-----END CERTIFICATE REQUEST-----\n";
    assert_ne!(
        tampered(synthetic),
        synthetic,
        "the tamper must change the byte that already is `A`"
    );

    if openssl_missing() {
        return;
    }
    let server = Server::new();
    server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("an issuer");
    // A real one: keys until the CSR's tamper site reads `A` (1 in 64; bounded).
    let mut good = None;
    for i in 0..4096 {
        let csr = openssl_csr(server.dir.path(), &format!("alice{i}"), "alice");
        let lines: Vec<&str> = csr.lines().collect();
        if lines[lines.len() - 2].starts_with('A') {
            good = Some(csr);
            break;
        }
    }
    let good = good.expect("a CSR whose last body line starts with `A` within 4096 keys");
    server
        .pki
        .issue_from_csr("alice", &good, DEFAULT_LEAF_DAYS)
        .expect("an untampered request must be accepted");
    match server
        .pki
        .issue_from_csr("alice", &tampered(&good), DEFAULT_LEAF_DAYS)
    {
        Err(Error::Pki(_)) => {}
        other => {
            panic!("a request that does not verify against its own key must be REFUSED: {other:?}")
        }
    }
}

/// **REFUSED: removing the live issuer from the trusted-CA list.**
///
/// A server minting with a CA it does not trust issues certificates it then
/// refuses, and nothing about that failure points back here.
#[test]
fn untrusting_the_live_issuer_is_refused_but_a_second_ca_can_be_removed() {
    let server = Server::new();
    let other = Server::new();
    let live = server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("an issuer");
    other
        .pki
        .create("the old issuer", DEFAULT_ISSUER_DAYS)
        .expect("a second CA");
    let old = other.pki.issuer().unwrap().unwrap();

    // The rotation shape the certificate-identity plan requires: two CAs trusted at once.
    server
        .pki
        .trust_ca("previous", &old.cert_pem)
        .expect("trusting a second CA");
    assert_eq!(server.pki.trusted_cas().unwrap().len(), 2);

    match server.pki.untrust_ca(&live.fingerprint) {
        Err(Error::Pki(reason)) => assert!(
            reason.contains("own issuer"),
            "the refusal must say why, got: {reason}"
        ),
        other => {
            panic!("removing the live issuer from the trusted list must be REFUSED: {other:?}")
        }
    }

    // The green half, and it is what stops the refusal above from being
    // "untrust never removes anything".
    let removed = server
        .pki
        .untrust_ca(&old.fingerprint)
        .expect("removing a non-issuer CA")
        .expect("it was in the list");
    assert_eq!(removed.label, "previous");
    let left = server.pki.trusted_cas().unwrap();
    assert_eq!(left.len(), 1);
    assert_eq!(left[0].fingerprint, live.fingerprint);
    assert!(left[0].is_issuer);
}

// ───────────── refusal 3: the private key is not in the control plane ────────

/// **RED, and it must run first.** The three things the detectors below have to
/// get right, each proven here.
///
/// The two negative assertions in this file answer "the key is not in this
/// file". That answer is also what a broken search, an empty file and a store
/// that wrote nothing all produce — and, as measured on 2026-08-09, what a
/// whole-body search produces for a value redb has split across pages. So:
///
/// 1. a plaintext key in `secrets.arrow` **is** found;
/// 2. a key planted in a **large** control document — large enough that redb
///    splits it, which is what silently defeated the first version of this
///    detector — **is** found;
/// 3. a **certificate** for the same key, which is public and belongs in redb,
///    is **not** mistaken for the key, while the public-PEM search still finds
///    it. That is the other half: a detector that fires on the public key would
///    report a leak on every correct server.
#[test]
fn red_the_detectors_are_blind_in_neither_direction() {
    let server = Server::new();
    server
        .pki
        .create("probe issuer", DEFAULT_ISSUER_DAYS)
        .expect("an issuer, so the control document is committed and the database is not empty");
    let issuer_key = String::from_utf8(server.pki_secret_key().expect("the key")).expect("utf-8");

    // (1) An unsealed plaintext in the secret log must be found.
    server
        .raw_secrets
        .put("pki/probe_plaintext", issuer_key.as_bytes())
        .expect("write the probe secret");
    assert!(
        file_carries_secret(&server.secrets_log(), &issuer_key),
        "the secret-log detector cannot see a private key that IS in the file in the clear; \
         every `the key is not here` assertion below is worthless"
    );

    // (2) **Split-robustness**, proven deterministically rather than by hoping
    // redb happens to split. A value split across two pages looks, to a byte
    // search, exactly like a body with something in the middle of it — which is
    // measurably what `control.redb` does: in a real `--data-root` the issuer
    // certificate's 8th and 9th body lines are no longer adjacent in the file.
    let shape = pem_shapes(&issuer_key)[1].clone();
    let mut interrupted = shape.clone().into_bytes();
    let at = interrupted.len() / 2;
    interrupted[at] = b'#';
    let split_file = server.dir.path().join("split_probe.bin");
    std::fs::write(&split_file, &interrupted).expect("writing the split probe");
    assert!(
        !file_contains(&split_file, shape.as_bytes()),
        "the interruption did not actually break the body, so this case tests nothing"
    );
    assert!(
        file_carries_secret(&split_file, &issuer_key),
        "the detector reports ABSENT for a private key that is right there, merely because the \
         storage split it. A single contiguous needle is in no file anywhere once redb pages a \
         value, and that is how this detector was blind the first time it was written"
    );

    // And the same key planted in a real control document, through the real
    // document path, must be found too.
    let planted = Server::new();
    let donor = Server::new();
    donor.pki.create("donor", DEFAULT_ISSUER_DAYS).unwrap();
    let donor_cert = donor.pki.issuer().unwrap().unwrap().cert_pem;
    planted
        .pki
        .trust_ca(&format!("filler {issuer_key}"), &donor_cert)
        .expect("planting a key in a control document");
    assert!(
        file_carries_secret(&planted.control_db(), &issuer_key),
        "the control-database detector cannot see a private key that IS in a control document"
    );

    // (3) And the public half must not be mistaken for the private one. The
    // server-generated path puts a certificate for a key in redb; the key
    // itself is only on the caller's disk.
    let clean = Server::new();
    clean
        .pki
        .create("clean", DEFAULT_ISSUER_DAYS)
        .expect("an issuer");
    let issued = clean
        .pki
        .issue_with_server_key("ci", DEFAULT_LEAF_DAYS)
        .expect("minting for ci");
    // Registered exactly as `gunnar principal add` registers it: the DER, as
    // one continuous base64 run, which is the shape that produced the measured
    // false positive.
    let doc_key = clean
        .pki
        .trust_ca("the issued certificate", &issued.cert_pem)
        .expect("storing the issued certificate in the control database");
    assert!(
        file_carries_pem(&clean.control_db(), &doc_key.cert_pem),
        "the certificate is not in the control database, so the next assertion proves nothing"
    );
    let ci_key = issued.key_pem.as_deref().expect("the server generated one");
    assert!(
        !file_carries_secret(&clean.control_db(), ci_key),
        "the detector mistook a certificate's PUBLIC key for the private one. A PKCS#8 EC key \
         ends with its public point and a certificate for it contains the same bytes, so a \
         detector built on whole LINES reports a leak on every correct server"
    );
}

/// **The issuer private key is not retrievable from redb** — asserted against
/// the bytes of `control.redb`, not against an API that declines to return it.
///
/// The paired positive is the certificate: it **must** be findable in the same
/// file by the same search, because redb is exactly where the certificate is
/// supposed to be. Without that, this test would pass on a control database
/// that stored nothing at all.
#[test]
fn the_issuer_private_key_is_never_in_the_control_database() {
    let server = Server::new();
    let issuer = server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("an issuer");
    // Issue as well, so anything the issuance path might have written is on
    // disk before the file is read.
    server
        .pki
        .issue_with_server_key("alice", DEFAULT_LEAF_DAYS)
        .expect("issue for alice");

    let key_pem = String::from_utf8(server.pki_secret_key().expect("the key")).expect("utf-8");
    assert!(
        key_pem.contains("PRIVATE KEY"),
        "the thing being searched for must actually be a private key, got: {}",
        &key_pem[..key_pem.len().min(40)]
    );

    let db = server.control_db();

    // The positive half FIRST: the certificate is public and belongs in redb.
    assert!(
        file_carries_pem(&db, &issuer.cert_pem),
        "the issuer CERTIFICATE is not in the control database, so this file's search proves \
         nothing about the key's absence"
    );

    // And the thing that must not be there.
    assert!(
        !file_carries_secret(&db, &key_pem),
        "THE ISSUER PRIVATE KEY IS IN control.redb. A key that can mint any user must never be \
         in the control plane in the clear: an attacker holding it does not break \
         authentication, they issue themselves an administrator"
    );
    assert!(
        !file_contains(&db, b"-----BEGIN PRIVATE KEY-----"),
        "control.redb carries PEM private-key armour; something wrote a key into the control \
         plane"
    );
}

/// And in the secret store it is **sealed**, not merely elsewhere.
///
/// Moving the key out of redb into a second file in the clear would satisfy the
/// test above and be no better. The KEK is what makes the file safe to copy off
/// the machine.
#[test]
fn the_issuer_private_key_is_sealed_where_it_does_live() {
    let server = Server::new();
    server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("an issuer");

    let stored = server
        .raw_secrets
        .get(ISSUER_PRIVATE_KEY)
        .expect("reading the backend")
        .expect("something is stored");
    assert!(
        gunnar_core::is_sealed(&stored),
        "what reached the secret backend is not sealed; it carries no `{:?}` magic",
        gunnar_core::SEAL_MAGIC
    );

    let plaintext = String::from_utf8(server.pki_secret_key().expect("the key")).expect("utf-8");
    assert!(
        !file_carries_secret(&server.secrets_log(), &plaintext),
        "the issuer private key is in secrets.arrow IN THE CLEAR"
    );
}

// ─────────────── the acceptances, checked by something else ──────────────────

/// **A certificate this issuer minted verifies against this issuer — and an
/// unrelated CA refuses it.**
///
/// Both halves, and the second is the one that makes the first mean anything.
/// Without it, "the CA verifies it" is satisfied by a chain that *every* CA
/// would accept, which is a decoration and not a trust root. `openssl` is the
/// judge; it shares no code with `rcgen`.
#[test]
fn openssl_verifies_what_the_issuer_signed_and_an_unrelated_issuer_refuses_it() {
    if openssl_missing() {
        return;
    }
    let ours = Server::new();
    let theirs = Server::new();
    let our_ca = ours
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("our issuer");
    theirs
        .pki
        .create("somebody else's issuer", DEFAULT_ISSUER_DAYS)
        .expect("their issuer");
    let their_ca = theirs.pki.issuer().unwrap().unwrap();

    // Both issuance paths, because a working CSR path and a broken
    // server-generated one would be invisible if only one were checked.
    let csr = openssl_csr(ours.dir.path(), "alice", "alice");
    let from_csr = ours
        .pki
        .issue_from_csr("alice", &csr, DEFAULT_LEAF_DAYS)
        .expect("issuing from a CSR");
    let minted = ours
        .pki
        .issue_with_server_key("bob", DEFAULT_LEAF_DAYS)
        .expect("minting for bob");

    for issued in [&from_csr, &minted] {
        let (ok, why) = openssl_verifies(&our_ca.cert_pem, &issued.cert_pem, ours.dir.path());
        assert!(
            ok,
            "openssl will not verify {} against the issuer that signed it:\n{why}",
            issued.principal_id
        );

        let (ok, _) = openssl_verifies(&their_ca.cert_pem, &issued.cert_pem, theirs.dir.path());
        assert!(
            !ok,
            "an unrelated issuer verified {}, a certificate it never signed. The trust root is \
             not a root at all",
            issued.principal_id
        );
    }
}

/// **The CN that was asked for, not the one the CSR claimed.**
///
/// A CSR is a request. Taking its subject at face value would let anyone who
/// can reach an operator with a `.csr` file choose the name they are issued
/// under, and the certificate-identity plan takes the *identity* from the CN —
/// so choosing the CN is choosing who you are.
///
/// The claimed name is asserted to differ first, or the comparison at the end
/// would be between two identical strings and would pass whatever the code did.
/// The issued subject is read back with `openssl`, not with `rcgen`.
#[test]
fn a_certificate_signing_request_does_not_choose_its_own_name() {
    if openssl_missing() {
        return;
    }
    let server = Server::new();
    server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("an issuer");

    let csr = openssl_csr(server.dir.path(), "impostor", "admin");
    assert_eq!(
        gunnar_core::issuer::csr_claimed_common_name(&csr).expect("reading the request"),
        Some("admin".to_owned()),
        "the request must really claim a DIFFERENT name, or this test compares a name with \
         itself"
    );

    let issued = server
        .pki
        .issue_from_csr("alice", &csr, DEFAULT_LEAF_DAYS)
        .expect("issuing for alice");

    let subject = openssl_subject(&issued.cert_pem, server.dir.path());
    assert!(
        subject.contains("CN=alice"),
        "the issued certificate must carry the name that was ASKED FOR. openssl says: {subject}"
    );
    assert!(
        !subject.contains("admin"),
        "the issued certificate carries the name the REQUEST claimed. Anyone who can hand an \
         operator a .csr can then choose who they are. openssl says: {subject}"
    );
    assert_eq!(issued.common_name, "alice");
    assert_eq!(issued.principal_id, "alice");
}

/// A CSR must not be able to ask for the right to mint users, or for a name on
/// a host it does not own.
///
/// The request here claims `CA:TRUE` and a `dNSName` for somebody else's
/// server. Neither may survive into the certificate.
#[test]
fn a_certificate_signing_request_cannot_ask_to_become_a_certificate_authority() {
    if openssl_missing() {
        return;
    }
    let server = Server::new();
    server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("an issuer");

    let dir = server.dir.path();
    let config = dir.join("greedy.cnf");
    write(
        &config,
        "[req]\n\
         distinguished_name = dn\n\
         req_extensions = ext\n\
         prompt = no\n\
         [dn]\n\
         CN = greedy\n\
         [ext]\n\
         basicConstraints = critical, CA:TRUE\n\
         keyUsage = critical, keyCertSign, cRLSign\n\
         subjectAltName = DNS:git.example.com\n",
    );
    let key = dir.join("greedy_key.pem");
    let csr_path = dir.join("greedy_csr.pem");
    let out = Command::new("openssl")
        .args([
            "req",
            "-new",
            "-newkey",
            "ec",
            "-pkeyopt",
            "ec_paramgen_curve:P-256",
            "-nodes",
            "-keyout",
        ])
        .arg(&key)
        .arg("-out")
        .arg(&csr_path)
        .arg("-config")
        .arg(&config)
        .output()
        .expect("running openssl req");
    assert!(
        out.status.success(),
        "openssl would not write the greedy CSR:\n{}",
        String::from_utf8_lossy(&out.stderr)
    );
    let csr = std::fs::read_to_string(&csr_path).expect("reading the greedy CSR");
    let text = Command::new("openssl")
        .args(["req", "-in"])
        .arg(&csr_path)
        .args(["-noout", "-text"])
        .output()
        .expect("running openssl req -text");
    let text = String::from_utf8_lossy(&text.stdout);
    assert!(
        text.contains("CA:TRUE"),
        "the REQUEST must really ask to be a CA, or this test asserts nothing"
    );

    // The request may be refused outright, or issued with the claim stripped.
    // Both are correct; being issued WITH the claim is not.
    if let Ok(issued) = server.pki.issue_from_csr("alice", &csr, DEFAULT_LEAF_DAYS) {
        let path = server.dir.path().join("greedy_issued.pem");
        write(&path, &issued.cert_pem);
        let out = Command::new("openssl")
            .args(["x509", "-noout", "-text", "-in"])
            .arg(&path)
            .output()
            .expect("running openssl x509 -text");
        let text = String::from_utf8_lossy(&out.stdout);
        assert!(
            !text.contains("CA:TRUE"),
            "a CSR asked to become a certificate authority and gunnar granted it:\n{text}"
        );
        assert!(
            !text.contains("git.example.com"),
            "a CSR asked for a name on a host it does not own and gunnar granted it:\n{text}"
        );
    }
}

/// **The CSR path hands back no private key. The other one does, and says so.**
///
/// `Issued::key_pem` being `None` is the security property of the default form:
/// the key never existed here, so there is nothing to hand back, log or leak.
/// The `Some` on the other path is the honest cost of the convenient shape.
#[test]
fn only_the_server_generated_form_hands_back_a_private_key() {
    if openssl_missing() {
        return;
    }
    let server = Server::new();
    server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("an issuer");

    let csr = openssl_csr(server.dir.path(), "alice", "alice");
    let from_csr: Issued = server
        .pki
        .issue_from_csr("alice", &csr, DEFAULT_LEAF_DAYS)
        .expect("issuing from a CSR");
    assert!(
        from_csr.key_pem.is_none(),
        "the CSR path returned a private key. It has none to return — that is the whole point \
         of it, and a key here means one was generated on the server after all"
    );

    let minted = server
        .pki
        .issue_with_server_key("bob", DEFAULT_LEAF_DAYS)
        .expect("minting for bob");
    let key = minted
        .key_pem
        .as_deref()
        .expect("`--server-generated-key` must return the key, or nobody can use the certificate");
    assert!(
        key.contains("PRIVATE KEY"),
        "and it must be a usable PEM key"
    );
}

/// A round trip through storage: an issuer survives a restart, and what it
/// signs afterwards still verifies against the certificate the CA list hands
/// out.
///
/// This is the one that catches `signing_handle` rebuilding a *different* CA
/// from the stored PEM — the failure would be invisible until a client that
/// trusts the published CA is handed a certificate that does not chain to it.
#[test]
fn an_issuer_survives_a_restart_and_still_signs_into_the_same_chain() {
    if openssl_missing() {
        return;
    }
    let server = Server::new();
    let created = server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("an issuer");

    // **Everything the first process held is dropped**, and not just shadowed:
    // redb takes a real file lock and the Arrow log an `O_EXCL` one, so a
    // "restart" that kept the old handles alive would reopen nothing and this
    // test would prove only that a live handle can read its own writes.
    let Server {
        dir,
        pki,
        control,
        raw_secrets,
        sealed,
        kek,
    } = server;
    drop(pki);
    drop(sealed);
    drop(raw_secrets);
    drop(control);

    let control = gunnar_core::open_control_store(dir.path().join("control.redb"))
        .expect("reopen the control store");
    let log = ArrowLogStore::open(dir.path(), gix_hash::Kind::Sha256).expect("reopen the log");
    let sealed: Arc<dyn SecretStore> = Arc::new(SealedSecretStore::new(log.secrets(), kek));
    let restarted = Pki::new(control, sealed).expect("reopen the pki");

    let after = restarted.issuer().expect("read").expect("still there");
    assert_eq!(
        after, created,
        "the issuer must survive a restart unchanged"
    );

    let issued = restarted
        .issue_with_server_key("alice", DEFAULT_LEAF_DAYS)
        .expect("issuing after a restart");
    let (ok, why) = openssl_verifies(&created.cert_pem, &issued.cert_pem, dir.path());
    assert!(
        ok,
        "a certificate issued after a restart does not chain to the CA certificate this server \
         publishes:\n{why}"
    );
}

/// **A read-only PKI reads the certificate and cannot reach a key.**
///
/// The certificate-identity plan moved the control plane to redb so that a process carrying no
/// storage engine can still read it, and the issuer *certificate* is a control
/// document like any other. The private key is not: it is in the secret log
/// inside the control repository. So `Pki::read_only` must answer the first
/// question and refuse the second — and refuse it by **erroring**, never by
/// answering "no key is stored", which a caller would reasonably act on by
/// minting one.
#[test]
fn a_read_only_pki_reads_the_certificate_and_refuses_to_mint() {
    let server = Server::new();
    let live = server
        .pki
        .create("gunnar issuer", DEFAULT_ISSUER_DAYS)
        .expect("an issuer");

    let reader = Pki::read_only(Arc::clone(&server.control)).expect("a read-only pki");
    assert_eq!(
        reader.issuer().expect("reading").expect("there is one"),
        live,
        "a read-only handle must be able to answer `gunnar issuer show`"
    );
    assert_eq!(reader.trusted_cas().expect("the CA list").len(), 1);

    // Issuing needs the key, and this handle has no way to one.
    match reader.issue_with_server_key("alice", DEFAULT_LEAF_DAYS) {
        Err(Error::Store(_)) | Err(Error::Pki(_)) => {}
        other => panic!("a read-only pki must refuse to issue, not do it: {other:?}"),
    }

    // And on a server with NO issuer, `create` must fail rather than appear to
    // succeed against a store that discards writes.
    let empty = Server::new();
    let reader = Pki::read_only(Arc::clone(&empty.control)).expect("a read-only pki");
    match reader.create("nope", DEFAULT_ISSUER_DAYS) {
        Err(Error::Store(_)) | Err(Error::Pki(_)) => {}
        other => panic!("a read-only pki must refuse to create an issuer: {other:?}"),
    }
    assert!(
        empty.pki.issuer().expect("reading").is_none(),
        "and it must not have committed a certificate it can never sign with"
    );
}