Skip to main content

passless_rs/pin_storage/
local.rs

1//! Local file system PIN storage
2
3use 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
16/// Local file system PIN storage
17///
18/// Stores PIN state as a JSON file in the storage directory.
19pub struct LocalPinStorage {
20    path: PathBuf,
21}
22
23impl LocalPinStorage {
24    /// Create a new local PIN storage
25    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}