identity_storage.rs (2823B)
1 use std::path::{Path, PathBuf}; 2 3 use anyhow::Result; 4 use radroots_identity::{IdentityError, RadrootsIdentity, RadrootsIdentityFile}; 5 6 const RHI_IDENTITY_KEY_SLOT: &str = "rhi_identity"; 7 8 #[cfg(test)] 9 pub fn encrypted_identity_key_path(path: impl AsRef<Path>) -> PathBuf { 10 radroots_runtime::local_wrapping_key_path(path) 11 } 12 13 pub fn load_service_identity( 14 path: Option<&Path>, 15 allow_generate: bool, 16 ) -> Result<RadrootsIdentity> { 17 let path = resolved_identity_path(path); 18 if path.exists() { 19 return load_encrypted_identity(&path); 20 } 21 if !allow_generate { 22 return Err(IdentityError::GenerationNotAllowed(path).into()); 23 } 24 25 let identity = RadrootsIdentity::generate(); 26 store_encrypted_identity(&path, &identity)?; 27 Ok(identity) 28 } 29 30 pub fn store_encrypted_identity(path: impl AsRef<Path>, identity: &RadrootsIdentity) -> Result<()> { 31 let payload = serde_json::to_vec(&identity.to_file())?; 32 radroots_runtime::seal_local_secret_file(path, RHI_IDENTITY_KEY_SLOT, &payload)?; 33 Ok(()) 34 } 35 36 pub fn load_encrypted_identity(path: impl AsRef<Path>) -> Result<RadrootsIdentity> { 37 let payload = radroots_runtime::open_local_secret_file(path, RHI_IDENTITY_KEY_SLOT)?; 38 let file: RadrootsIdentityFile = serde_json::from_slice(&payload)?; 39 Ok(RadrootsIdentity::try_from(file)?) 40 } 41 42 fn resolved_identity_path(path: Option<&Path>) -> PathBuf { 43 path.map(Path::to_path_buf).unwrap_or_else(|| { 44 crate::paths::default_identity_path_for_process() 45 .expect("resolve canonical rhi identity path") 46 }) 47 } 48 49 #[cfg(test)] 50 mod tests { 51 use super::{encrypted_identity_key_path, load_service_identity}; 52 53 #[test] 54 fn load_service_identity_generates_encrypted_identity_artifacts() { 55 let temp = tempfile::tempdir().expect("tempdir"); 56 let path = temp.path().join("rhi-identity.secret.json"); 57 58 let generated = 59 load_service_identity(Some(&path), true).expect("generate encrypted identity"); 60 let loaded = load_service_identity(Some(&path), false).expect("load encrypted identity"); 61 62 assert_eq!(generated.id(), loaded.id()); 63 assert!(path.is_file()); 64 assert!(encrypted_identity_key_path(&path).is_file()); 65 } 66 67 #[test] 68 fn load_service_identity_fails_when_wrapping_key_is_missing() { 69 let temp = tempfile::tempdir().expect("tempdir"); 70 let path = temp.path().join("rhi-identity.secret.json"); 71 let _ = load_service_identity(Some(&path), true).expect("generate encrypted identity"); 72 std::fs::remove_file(encrypted_identity_key_path(&path)).expect("remove wrapping key"); 73 74 let err = load_service_identity(Some(&path), false) 75 .expect_err("missing wrapping key should fail"); 76 assert!(err.to_string().contains("identity")); 77 } 78 }