use cryptoki::context::{CInitializeArgs, CInitializeFlags, Pkcs11};
use cryptoki::session::UserType;
use cryptoki::slot::Slot;
use cryptoki::types::AuthPin;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
pub fn get_module() -> String {
env::var("TEST_PKCS11_MODULE")
.unwrap_or_else(|_| "../target/debug/libkryoptic_pkcs11.so".to_string())
}
pub fn get_tmpdir() -> PathBuf {
PathBuf::from(
env::var("CARGO_TARGET_TMPDIR")
.unwrap_or_else(|_| "target/tmp".to_string()),
)
}
fn setup_common() -> (Pkcs11, Slot) {
let module = get_module();
let pkcs11 = Pkcs11::new(&module).expect("Failed to load PKCS#11 module");
pkcs11
.initialize(CInitializeArgs::new(CInitializeFlags::OS_LOCKING_OK))
.expect("Failed to initialize PKCS#11");
let slots = pkcs11.get_slots_with_token().expect("Failed to get slots");
let slot = slots.first().expect("No slots with tokens found").clone();
let so_pin = AuthPin::new("87654321".into());
pkcs11
.init_token(slot, &so_pin, "Test Kryoptic Token")
.expect("Failed to init token");
let user_pin = AuthPin::new("12345678".into());
let session = pkcs11
.open_rw_session(slot)
.expect("Failed to open RW session");
session
.login(UserType::So, Some(&so_pin))
.expect("SO login failed");
session.init_pin(&user_pin).expect("init_pin failed");
session.logout().expect("SO logout failed");
(pkcs11, slot)
}
fn reload_common((pkcs11, _slot): (Pkcs11, Slot)) -> (Pkcs11, Slot) {
pkcs11
.finalize()
.expect("Failed to finalize previous Pkcs11 context");
let module = get_module();
let pkcs11 = Pkcs11::new(&module).expect("Failed to load PKCS#11 module");
pkcs11
.initialize(CInitializeArgs::new(CInitializeFlags::OS_LOCKING_OK))
.expect("Failed to initialize PKCS#11");
let slots = pkcs11.get_slots_with_token().expect("Failed to get slots");
let slot = slots.first().expect("No slots with tokens found").clone();
(pkcs11, slot)
}
fn setup_test_dir(name: &str) -> PathBuf {
let tmpdir = get_tmpdir();
let testdir = tmpdir.join(name);
if testdir.exists() {
fs::remove_dir_all(&testdir).unwrap();
}
fs::create_dir_all(&testdir).unwrap();
testdir
}
fn generate_config(
name: &str,
testdir: &Path,
common_config_lines: &[&str],
) -> PathBuf {
let confname = testdir.join(format!("{}.conf", name));
let mut config_content = String::new();
let mut dbtype = "sqlite".to_string();
let mut final_config_lines = Vec::new();
for line in common_config_lines {
let line_str = *line;
if line_str.starts_with("dbtype=") {
dbtype = line_str.splitn(2, '=').nth(1).unwrap().trim().to_string();
} else {
final_config_lines.push(line_str);
}
}
match dbtype.as_str() {
"sqlite" | "nssdb" | "memory" => {}
_ => panic!("Unsupported dbtype: {}", dbtype),
}
for line in final_config_lines {
config_content.push_str(line);
config_content.push('\n');
}
let dbargs = match dbtype.as_str() {
"sqlite" => {
let sql_path = testdir.join(format!("{}.sql", name));
sql_path.to_str().unwrap().replace('\\', "\\\\")
}
"nssdb" => format!(
"configDir={}",
testdir.to_str().unwrap().replace('\\', "\\\\")
),
"memory" => "flags=encrypt".to_string(),
_ => unreachable!(),
};
let slot_config = format!(
r#"
[[slots]]
slot = 0
dbtype = "{}"
dbargs = "{}"
"#,
dbtype, dbargs
);
config_content.push_str(&slot_config);
fs::write(&confname, config_content)
.expect("Failed to write kryoptic.conf");
confname
}
pub fn setup_token(name: &str, common_config_lines: &[&str]) -> (Pkcs11, Slot) {
let testdir = setup_test_dir(name);
let confname = generate_config(name, &testdir, common_config_lines);
unsafe {
env::set_var("KRYOPTIC_CONF", confname);
}
setup_common()
}
#[allow(dead_code)]
pub fn modify_setup(
name: &str,
common_config_lines: &[&str],
prev: (Pkcs11, Slot),
) -> (Pkcs11, Slot) {
let testdir = get_tmpdir().join(name);
let confname = generate_config(name, &testdir, common_config_lines);
unsafe {
env::set_var("KRYOPTIC_CONF", confname);
}
reload_common(prev)
}