1#![allow(clippy::doc_markdown)]
7
8use crate::locked;
9use crate::prelude::Error as BwxError;
10
11const FILENAME: &str = "touchid.json";
12
13#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
14pub struct Blob {
15 pub keychain_label: String,
17 pub wrapped_priv_key: String,
20 pub wrapped_org_keys: std::collections::BTreeMap<String, String>,
22}
23
24impl Blob {
25 pub fn path() -> std::path::PathBuf {
26 crate::dirs::make_all().ok();
27 data_dir_for_blob().join(FILENAME)
28 }
29
30 pub fn exists() -> bool {
31 Self::path().exists()
32 }
33
34 pub fn load() -> Result<Self, BwxError> {
35 let path = Self::path();
36 let json = std::fs::read_to_string(&path).map_err(|source| {
37 BwxError::LoadConfig {
38 source,
39 file: path.clone(),
40 }
41 })?;
42 serde_json::from_str(&json)
43 .map_err(|source| BwxError::Json { source })
44 }
45
46 pub fn save(&self) -> Result<(), BwxError> {
47 use std::io::Write as _;
48 use std::os::unix::fs::{OpenOptionsExt as _, PermissionsExt as _};
49 let path = Self::path();
50 if let Some(parent) = path.parent() {
51 std::fs::create_dir_all(parent).map_err(|source| {
52 BwxError::SaveConfig {
53 source,
54 file: path.clone(),
55 }
56 })?;
57 }
58 let json = serde_json::to_string(self)
59 .map_err(|source| BwxError::Json { source })?;
60 let mut fh = std::fs::OpenOptions::new()
61 .write(true)
62 .create(true)
63 .truncate(true)
64 .mode(0o600)
65 .open(&path)
66 .map_err(|source| BwxError::SaveConfig {
67 source,
68 file: path.clone(),
69 })?;
70 fh.set_permissions(std::fs::Permissions::from_mode(0o600))
71 .map_err(|source| BwxError::SaveConfig {
72 source,
73 file: path.clone(),
74 })?;
75 fh.write_all(json.as_bytes())
76 .map_err(|source| BwxError::SaveConfig { source, file: path })?;
77 Ok(())
78 }
79
80 pub fn remove() -> Result<(), BwxError> {
81 let path = Self::path();
82 match std::fs::remove_file(&path) {
83 Ok(()) => Ok(()),
84 Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(()),
85 Err(source) => Err(BwxError::SaveConfig { source, file: path }),
86 }
87 }
88}
89
90fn data_dir_for_blob() -> std::path::PathBuf {
91 let p = crate::dirs::agent_stdout_file();
93 p.parent().map_or_else(
94 || std::path::PathBuf::from("."),
95 std::path::Path::to_path_buf,
96 )
97}
98
99pub fn keys_from_wrapper_seed(seed: &[u8]) -> locked::Keys {
103 assert_eq!(seed.len(), 64, "wrapper seed must be 64 bytes");
104 let mut buf = locked::Vec::new();
105 buf.extend(seed.iter().copied());
106 locked::Keys::new(buf)
107}