#![forbid(unsafe_code)]
use super::{parse_hex_key, PrimaryKey};
use crate::constants::{KEYRING_SERVICE, KEYRING_USER_LEGACY, KEYRING_USER_PRIMARY};
use crate::errors::{SshCliError, SshCliResult};
use zeroize::Zeroize;
pub fn write_key_to_keyring(hex64: &str) -> SshCliResult<()> {
let _ = parse_hex_key(hex64)
.map_err(|e| SshCliError::InvalidArgument(format!("invalid key: {e}")))?;
let entry = keyring::Entry::new(KEYRING_SERVICE, KEYRING_USER_PRIMARY).map_err(|e| {
tracing::debug!(err = %e, "keyring Entry::new failed");
SshCliError::unavailable("keyring")
})?;
entry.set_password(hex64.trim()).map_err(|e| {
tracing::debug!(err = %e, "keyring set failed");
SshCliError::unavailable("keyring")
})?;
Ok(())
}
pub fn read_keyring() -> SshCliResult<Option<PrimaryKey>> {
for user in [KEYRING_USER_PRIMARY, KEYRING_USER_LEGACY] {
let entry = match keyring::Entry::new(KEYRING_SERVICE, user) {
Ok(e) => e,
Err(e) => {
if user == "secrets-master-key" {
tracing::debug!(err = %e, "keyring Entry::new failed");
return Err(SshCliError::unavailable("keyring"));
}
continue;
}
};
match entry.get_password() {
Ok(mut s) => {
let key = parse_hex_key(&s).map_err(|e| {
SshCliError::InvalidArgument(format!("invalid keyring primary-key: {e}"))
});
s.zeroize();
return Ok(Some(key?));
}
Err(keyring::Error::NoEntry) => continue,
Err(e) => {
if user == "secrets-master-key" {
tracing::debug!(err = %e, "keyring get failed");
return Err(SshCliError::unavailable("keyring"));
}
continue;
}
}
}
Ok(None)
}