diff --git a/AGENTS.md b/AGENTS.md index 37547d8..1fe6c60 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -150,8 +150,10 @@ Roadmap items should be actionable and checkable: - SSH revocations can be exported as JSONL, OpenSSH KRL specification text, or binary OpenSSH KRL files generated through `ssh-keygen`. JSONL and OpenSSH KRL specification imports are supported; binary KRL import is unsupported because - OpenSSH KRL files are not enumerable through OpenSSH tooling. + OpenSSH KRL files are not enumerable through OpenSSH tooling. Tests cover + public-key and certificate binary KRL revocations when `ssh-keygen` is + available. - Signed peer-card LAN discovery payloads, peer auth over Iroh, cr-sqlite, - iroh-docs, iroh-blobs, Automerge sync, broader auth enforcement, certificate - revocation KRL tests, and Keyhive/BeeKEM-style authorization are future - roadmap items unless implemented later. + iroh-docs, iroh-blobs, Automerge sync, broader auth enforcement, and + Keyhive/BeeKEM-style authorization are future roadmap items unless + implemented later. diff --git a/crates/geth-ssh-identity/src/lib.rs b/crates/geth-ssh-identity/src/lib.rs index b46dec8..ac7a6e5 100644 --- a/crates/geth-ssh-identity/src/lib.rs +++ b/crates/geth-ssh-identity/src/lib.rs @@ -603,4 +603,84 @@ mod tests { .contains("revoked") ); } + + #[test] + fn openssh_krl_binary_export_revokes_certificate_when_ssh_keygen_available() { + if ensure_ssh_keygen_available().is_err() { + return; + } + + let dir = tempfile::tempdir().expect("tempdir"); + let ca_key_path = dir.path().join("ca"); + let user_key_path = dir.path().join("user"); + let ca_status = Command::new("ssh-keygen") + .arg("-q") + .arg("-t") + .arg("ed25519") + .arg("-N") + .arg("") + .arg("-f") + .arg(&ca_key_path) + .status() + .expect("generate ca key"); + assert!(ca_status.success()); + + let user_status = Command::new("ssh-keygen") + .arg("-q") + .arg("-t") + .arg("ed25519") + .arg("-N") + .arg("") + .arg("-f") + .arg(&user_key_path) + .status() + .expect("generate user key"); + assert!(user_status.success()); + + let user_public_key_path = user_key_path.with_extension("pub"); + let sign_status = Command::new("ssh-keygen") + .arg("-q") + .arg("-s") + .arg(&ca_key_path) + .arg("-I") + .arg("geth-test-cert") + .arg("-n") + .arg("eric") + .arg("-V") + .arg("+1d") + .arg("-z") + .arg("100") + .arg(&user_public_key_path) + .status() + .expect("sign user certificate"); + assert!(sign_status.success()); + + let cert_path = dir.path().join("user-cert.pub"); + let certificate = std::fs::read_to_string(&cert_path).expect("read certificate"); + let entry = SshRevocationEntry { + id: "ssh-revocation:cert".into(), + kind: SshRevocationKind::Certificate, + target: certificate, + reason: Some("test certificate revocation".to_owned()), + created_at: UnixMillis(1), + published: true, + }; + let krl_path = dir.path().join("revoked-certs.krl"); + + write_openssh_krl(&[entry], &krl_path, None).expect("write certificate krl"); + + let output = Command::new("ssh-keygen") + .arg("-Q") + .arg("-f") + .arg(&krl_path) + .arg(&cert_path) + .output() + .expect("query certificate krl"); + assert!(!output.status.success()); + assert!( + String::from_utf8_lossy(&output.stdout) + .to_ascii_lowercase() + .contains("revoked") + ); + } } diff --git a/docs/roadmap.md b/docs/roadmap.md index f9b414c..93fe307 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -291,7 +291,7 @@ Goal: add authorized stream-oriented management workflows over Iroh. - Consumers can list current certs/revocations from local state while offline. - Conflicting or unsigned records are rejected or quarantined. -- `[~]` OpenSSH KRL import/export. +- `[x]` OpenSSH KRL import/export. Acceptance criteria: - `[x]` Revocation records can produce an OpenSSH KRL specification file. - `[x]` Tests cover serial, key ID, and public key revocation spec lines. @@ -303,7 +303,7 @@ Goal: add authorized stream-oriented management workflows over Iroh. - `[x]` Binary OpenSSH KRL import returns a clear unsupported message because KRL files are not enumerable through OpenSSH tooling. - `[x]` Tests cover JSONL and KRL-spec import. - - `[ ]` Tests cover certificate revocations. + - `[x]` Tests cover certificate revocations. ## Phase 5: DB And Documents