From 355ce1cfde9c1ea82ca829e8668d576edeb582c9 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Mon, 2 Jan 2023 12:26:10 +0000 Subject: [PATCH] Add integration tests that require a live YubiKey slot --- tests/integration.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/tests/integration.rs b/tests/integration.rs index e147a5b..284cc6c 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -5,6 +5,36 @@ use std::process::{Command, Stdio}; const PLUGIN_BIN: &str = env!("CARGO_BIN_EXE_age-plugin-yubikey"); +#[test_with::env(YUBIKEY_SERIAL, YUBIKEY_SLOT)] +#[cfg_attr(all(unix, not(target_os = "macos")), test_with::executable(pcscd))] +#[test] +fn recipient_and_identity_match() { + let recipient = Command::new(PLUGIN_BIN) + .arg("--list") + .arg("--serial") + .arg(env::var("YUBIKEY_SERIAL").unwrap()) + .arg("--slot") + .arg(env::var("YUBIKEY_SLOT").unwrap()) + .output() + .unwrap(); + assert_eq!(recipient.status.code(), Some(0)); + + let identity = Command::new(PLUGIN_BIN) + .arg("--identity") + .arg("--serial") + .arg(env::var("YUBIKEY_SERIAL").unwrap()) + .arg("--slot") + .arg(env::var("YUBIKEY_SLOT").unwrap()) + .output() + .unwrap(); + assert_eq!(identity.status.code(), Some(0)); + + let recipient_file = String::from_utf8_lossy(&recipient.stdout); + let recipient = recipient_file.lines().last().unwrap(); + let identity = String::from_utf8_lossy(&identity.stdout); + assert!(identity.contains(recipient)); +} + #[test_with::executable(rage)] #[test] fn plugin_encrypt() { @@ -30,3 +60,66 @@ fn plugin_encrypt() { let status = process.wait().unwrap(); assert_eq!(status.code(), Some(0)); } + +#[test_with::env(YUBIKEY_SERIAL, YUBIKEY_SLOT)] +#[test_with::executable(rage)] +#[cfg_attr(all(unix, not(target_os = "macos")), test_with::executable(pcscd))] +#[test] +fn plugin_decrypt() { + let mut identity_file = tempfile::NamedTempFile::new_in(env!("CARGO_TARGET_TMPDIR")).unwrap(); + let enc_file = tempfile::NamedTempFile::new_in(env!("CARGO_TARGET_TMPDIR")).unwrap(); + let plaintext = "Testing YubiKey encryption"; + + // Write an identity file corresponding to this YubiKey slot. + let identity = Command::new(PLUGIN_BIN) + .arg("--identity") + .arg("--serial") + .arg(env::var("YUBIKEY_SERIAL").unwrap()) + .arg("--slot") + .arg(env::var("YUBIKEY_SLOT").unwrap()) + .output() + .unwrap(); + assert_eq!(identity.status.code(), Some(0)); + identity_file.write_all(&identity.stdout).unwrap(); + identity_file.flush().unwrap(); + + // Encrypt to the YubiKey slot. + let mut enc_process = Command::new(which::which("rage").unwrap()) + .arg("-e") + .arg("-i") + .arg(identity_file.path()) + .arg("-o") + .arg(enc_file.path()) + .stdin(Stdio::piped()) + .env("PATH", Path::new(PLUGIN_BIN).parent().unwrap()) + .spawn() + .unwrap(); + + // Scope to ensure stdin is closed. + { + let mut stdin = enc_process.stdin.take().unwrap(); + stdin.write_all(plaintext.as_bytes()).unwrap(); + stdin.flush().unwrap(); + } + + let enc_status = enc_process.wait().unwrap(); + assert_eq!(enc_status.code(), Some(0)); + + // Decrypt with the YubiKey. + let dec_process = Command::new(which::which("rage").unwrap()) + .arg("-d") + .arg("-i") + .arg(identity_file.path()) + .arg(enc_file.path()) + .stdin(Stdio::piped()) + .env("PATH", Path::new(PLUGIN_BIN).parent().unwrap()) + .output() + .unwrap(); + + let stderr = String::from_utf8_lossy(&dec_process.stderr); + if !stderr.is_empty() { + assert!(stderr.contains("age-plugin-yubikey")); + assert!(stderr.ends_with("...\n")); + } + assert_eq!(String::from_utf8_lossy(&dec_process.stdout), plaintext); +}