passless_rs/pin_storage/
local.rs1use crate::pin_storage::{PinStorage, SerializablePinState};
4use crate::util::create_secure_file;
5
6use soft_fido2::{PinState, StatusCode};
7
8use std::fs;
9use std::io::{Read, Write};
10use std::path::PathBuf;
11
12use log::{debug, info, warn};
13
14const PIN_STATE_FILENAME: &str = "pin_state.json";
15
16pub struct LocalPinStorage {
20 path: PathBuf,
21}
22
23impl LocalPinStorage {
24 pub fn new(storage_dir: PathBuf) -> Self {
26 let path = storage_dir.join(PIN_STATE_FILENAME);
27 debug!("Local PIN storage path: {}", path.display());
28 Self { path }
29 }
30
31 fn load_state(&self) -> Result<SerializablePinState, StatusCode> {
32 debug!("Loading PIN state from: {}", self.path.display());
33
34 if !self.path.exists() {
35 debug!("PIN state file does not exist, returning default state");
36 return Ok(SerializablePinState::default());
37 }
38
39 let mut file = fs::File::open(&self.path).map_err(|e| {
40 warn!("Failed to open PIN state file: {}", e);
41 StatusCode::Other
42 })?;
43
44 let mut contents = Vec::new();
45 file.read_to_end(&mut contents).map_err(|e| {
46 warn!("Failed to read PIN state file: {}", e);
47 StatusCode::Other
48 })?;
49
50 SerializablePinState::from_json_bytes(&contents).map_err(|e| {
51 warn!("Failed to parse PIN state: {:?}", e);
52 StatusCode::InvalidParameter
53 })
54 }
55
56 fn save_state(&self, state: &SerializablePinState) -> Result<(), StatusCode> {
57 debug!("Saving PIN state to: {}", self.path.display());
58
59 let bytes = state.to_json_bytes()?;
60
61 let mut file = create_secure_file(&self.path).map_err(|e| {
62 warn!("Failed to create PIN state file: {}", e);
63 StatusCode::Other
64 })?;
65
66 file.write_all(&bytes).map_err(|e| {
67 warn!("Failed to write PIN state file: {}", e);
68 StatusCode::Other
69 })?;
70
71 debug!("PIN state saved successfully");
72 Ok(())
73 }
74}
75
76impl PinStorage for LocalPinStorage {
77 fn load_pin_state(&self) -> Result<PinState, StatusCode> {
78 self.load_state().map(|s| s.into())
79 }
80
81 fn save_pin_state(&self, state: &PinState) -> Result<(), StatusCode> {
82 let serializable = SerializablePinState::from(state);
83
84 if state.is_pin_set() {
85 info!(
86 "Saving PIN state (PIN is set, {} retries remaining)",
87 state.retries
88 );
89 } else {
90 info!("Saving PIN state (no PIN set)");
91 }
92
93 self.save_state(&serializable)
94 }
95}