// SPDX-FileCopyrightText: 2023 David Runge // SPDX-License-Identifier: Apache-2.0 OR MIT use std::fs::create_dir_all; use std::fs::read_to_string; use std::path::PathBuf; use std::str::FromStr; use std::time::SystemTime; use assert_cmd::assert::AssertError; use assert_cmd::Command; use fqdn::FQDN; use rstest::fixture; use sequoia_openpgp::cert::CertBuilder; use sequoia_openpgp::packet::key::PublicParts; use sequoia_openpgp::packet::key::SubordinateRole; use sequoia_openpgp::packet::Key; use sequoia_openpgp::packet::UserID; use sequoia_openpgp::Cert; use ssh_key::known_hosts::Entry; use ssh_key::PublicKey; use sshd_openpgp_auth::attach_subkeys_to_cert; use sshd_openpgp_auth::create_openpgp_subkey_from_ssh_public_key_file; use sshd_openpgp_auth::create_trust_anchor; use sshd_openpgp_auth::get_public_ssh_host_keys; use testdir::testdir; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("OpenPGP Error: {0}")] OpenPGP(#[from] anyhow::Error), #[error("Running a command failed: {0}")] Command(#[from] AssertError), #[error("I/O error occurred: {0}")] IO(#[from] std::io::Error), #[error("FQDN parsing issue: {0}")] FQDN(#[from] fqdn::Error), #[error("ToolError: {0}")] Tool(#[from] sshd_openpgp_auth::Error), } #[fixture] pub fn ssh_config_dir() -> Result { let mut ssh_dir = PathBuf::new(); ssh_dir.push(testdir!()); ssh_dir.push("etc/ssh"); create_dir_all(&ssh_dir)?; Command::new("ssh-keygen") .arg("-A") .arg("-f") .arg(format!("{}", testdir!().display())) .assert() .try_success()?; Ok(ssh_dir) } #[fixture] pub fn ssh_public_host_keys(ssh_config_dir: Result) -> Result, Error> { Ok(get_public_ssh_host_keys(Some(ssh_config_dir?.as_path()))?) } #[fixture] pub fn default_host() -> Result { Ok(FQDN::from_str("example.com")?) } #[fixture] pub fn wrong_host() -> Result { Ok(FQDN::from_str("wrong.com")?) } #[fixture] pub fn trust_anchor(default_host: Result) -> Result { Ok(create_trust_anchor(&default_host?, None, None)?) } #[fixture] pub fn subkeys( ssh_config_dir: Result, ) -> Result>, Error> { let paths = get_public_ssh_host_keys(Some(ssh_config_dir?.as_path()))?; Ok(paths .iter() .filter_map(|x| create_openpgp_subkey_from_ssh_public_key_file(x.as_path(), None).ok()) .collect()) } #[fixture] pub fn trust_anchor_with_subkeys( trust_anchor: Result, subkeys: Result>, Error>, ) -> Result { Ok(attach_subkeys_to_cert(trust_anchor?, subkeys?)?) } #[fixture] pub fn ssh_pubkeys_as_entries( default_host: Result, ssh_public_host_keys: Result, Error>, ) -> Result, Error> { let host = format!("{}", default_host?); Ok(ssh_public_host_keys? .iter() .filter_map(|x| PublicKey::from_openssh(&read_to_string(x).unwrap_or("".to_string())).ok()) .map(|x| format!("{} {}", host, x.to_openssh().unwrap_or_default())) .filter_map(|x| Entry::from_str(&x).ok()) .collect()) } #[fixture] pub fn ssh_known_hosts(ssh_pubkeys_as_entries: Result, Error>) -> Result { let mut output = String::new(); for entry in ssh_pubkeys_as_entries?.iter() { output.push_str(&format!("{}\n", entry.to_string())); } Ok(output) } #[fixture] pub fn trust_anchor_ca(default_host: Result) -> Result { let cert = CertBuilder::new() .set_creation_time(Some(SystemTime::now())) .set_validity_period(None) .add_userid(UserID::from(format!("", &default_host?))) .generate()? .0; Ok(cert) }