mod ctor_migrate_util;
mod key_util;
use crate::hsc::ctor_migrate_util::{
CTOR_KEYSTORE_ID, CTOR_KEYSTORE1_PATH, CTOR_KEYSTORE2_PATH, CTOR_KEYSTORE3_PATH,
CTOR_KEYSTORE4_PATH, CTOR_KEYSTORE5_PATH, CTorMigrateCmd, ONION_ADDR_SERVICE_1,
ONION_ADDR_SERVICE_2,
};
use crate::hsc::key_util::{ADDR_LEN, ArtiHscKeyCmd, KeyCmdBuilder, ONION_ADDR};
const ERR_MSG: &str = "Service discovery key not found. Rerun with --generate=if-needed to generate a new service discovery keypair";
#[test]
fn gen_key() {
let state_dir = tempfile::TempDir::new().unwrap();
let state_dir = state_dir.path();
let cmd = KeyCmdBuilder::default()
.subcommand(ArtiHscKeyCmd::Get { generate: true })
.state_dir(state_dir.to_path_buf())
.build()
.unwrap();
let output = cmd.run().unwrap();
assert!(output.status.success());
assert!(
String::from_utf8(output.stdout)
.unwrap()
.contains("descriptor:x25519:")
);
assert!(cmd.keystore_contains_priv_key(ONION_ADDR));
}
#[test]
fn get_fails_if_no_gen_and_no_key() {
let state_dir = tempfile::TempDir::new().unwrap();
let state_dir = state_dir.path();
let cmd = KeyCmdBuilder::default()
.subcommand(ArtiHscKeyCmd::Get { generate: false })
.state_dir(state_dir.to_path_buf())
.build()
.unwrap();
let output = cmd.run().unwrap();
assert!(!output.status.success());
assert!(String::from_utf8(output.stderr).unwrap().contains(ERR_MSG));
}
#[test]
fn generate_then_rotate() {
let state_dir = tempfile::TempDir::new().unwrap();
let state_dir = state_dir.path();
let cmd = KeyCmdBuilder::default()
.subcommand(ArtiHscKeyCmd::Get { generate: true })
.state_dir(state_dir.to_path_buf())
.build()
.unwrap();
let output = cmd.run().unwrap();
assert!(output.status.success());
let descriptor = String::from_utf8(output.stdout).unwrap();
assert!(descriptor.contains("descriptor:x25519:"));
let cmd = KeyCmdBuilder::default()
.subcommand(ArtiHscKeyCmd::Rotate)
.state_dir(state_dir.to_path_buf())
.build()
.unwrap();
let output = cmd.run().unwrap();
assert!(output.status.success());
let rotated_descriptor = String::from_utf8(output.stdout).unwrap();
assert!(rotated_descriptor.contains("descriptor:x25519:"));
assert_ne!(descriptor, rotated_descriptor);
assert!(cmd.keystore_contains_priv_key(ONION_ADDR));
}
#[test]
fn generate_then_remove() {
let state_dir = tempfile::TempDir::new().unwrap();
let state_dir = state_dir.path();
let cmd = KeyCmdBuilder::default()
.subcommand(ArtiHscKeyCmd::Get { generate: true })
.state_dir(state_dir.to_path_buf())
.build()
.unwrap();
let output = cmd.run().unwrap();
assert!(output.status.success());
assert!(
String::from_utf8(output.stdout)
.unwrap()
.contains("descriptor:x25519:")
);
let cmd = KeyCmdBuilder::default()
.subcommand(ArtiHscKeyCmd::Remove)
.state_dir(state_dir.to_path_buf())
.build()
.unwrap();
let output = cmd.run().unwrap();
assert!(output.status.success());
let keystore_path = state_dir
.join("keystore/client")
.join(&ONION_ADDR[..ADDR_LEN]);
let entries = keystore_path.read_dir().unwrap().flatten();
assert_eq!(entries.count(), 0);
}
#[test]
fn simple_ctor_migration() {
let migrate_cmd = CTorMigrateCmd::new();
let assert_key_is_missing = |svc: &str| {
let err = migrate_cmd.keystore_contains_client_key(svc).unwrap_err();
assert!(err.to_string().contains(ERR_MSG));
};
assert_key_is_missing(ONION_ADDR_SERVICE_1);
assert_key_is_missing(ONION_ADDR_SERVICE_2);
let output = migrate_cmd.output(CTOR_KEYSTORE1_PATH).unwrap();
assert!(output.status.success());
assert!(
migrate_cmd
.keystore_contains_client_key(ONION_ADDR_SERVICE_1)
.is_ok()
);
assert!(
migrate_cmd
.keystore_contains_client_key(ONION_ADDR_SERVICE_2)
.is_ok()
);
}
#[test]
fn migrate_duplicate_ctor_entries() {
let migrate_cmd = CTorMigrateCmd::new();
let output = migrate_cmd.output(CTOR_KEYSTORE2_PATH).unwrap();
assert!(!output.status.success());
assert!(
String::from_utf8(output.stderr)
.unwrap()
.contains("Invalid C Tor keystore (multiple keys exist for service")
);
}
#[test]
fn forced_migration_overwrites_arti_keys() {
let migrate_cmd = CTorMigrateCmd::new();
migrate_cmd.populate_state_dir();
let output_service_1_prev = migrate_cmd
.keystore_contains_client_key(ONION_ADDR_SERVICE_1)
.unwrap();
let output_service_2_prev = migrate_cmd
.keystore_contains_client_key(ONION_ADDR_SERVICE_2)
.unwrap();
assert!(
migrate_cmd
.output(CTOR_KEYSTORE1_PATH)
.unwrap()
.status
.success()
);
let output_service_1_current = migrate_cmd
.keystore_contains_client_key(ONION_ADDR_SERVICE_1)
.unwrap();
let output_service_2_current = migrate_cmd
.keystore_contains_client_key(ONION_ADDR_SERVICE_2)
.unwrap();
assert_ne!(output_service_1_prev, output_service_1_current);
assert_ne!(output_service_2_prev, output_service_2_current);
}
#[test]
fn migrate_invalid_ctor_keystore() {
let assert_cmd_fails = |path: &str| {
let migrate_cmd = CTorMigrateCmd::new();
let output = migrate_cmd.output(path).unwrap();
assert!(!output.status.success());
let error = String::from_utf8(output.stderr).unwrap();
assert!(error.contains("No CTor client keys found in keystore"));
assert!(error.contains(CTOR_KEYSTORE_ID));
};
assert_cmd_fails(CTOR_KEYSTORE3_PATH);
assert_cmd_fails(CTOR_KEYSTORE4_PATH);
}
#[test]
fn migrate_skips_invalid_ctor_entries() {
let migrate_cmd = CTorMigrateCmd::new();
let output = migrate_cmd.output(CTOR_KEYSTORE5_PATH).unwrap();
assert!(output.status.success());
assert!(
migrate_cmd
.keystore_contains_client_key(ONION_ADDR_SERVICE_1)
.is_ok()
);
}