bitbox_api/
noise.rs

1use crate::util::Threading;
2use thiserror::Error;
3
4#[derive(Error, Debug)]
5#[error("{0}")]
6pub struct ConfigError(pub String);
7
8#[derive(Default, Clone, Debug, serde::Serialize, serde::Deserialize)]
9pub struct NoiseConfigData {
10    pub app_static_privkey: Option<[u8; 32]>,
11    pub device_static_pubkeys: Vec<Vec<u8>>,
12}
13
14impl NoiseConfigData {
15    pub(crate) fn contains_device_static_pubkey(&self, pubkey: &[u8]) -> bool {
16        self.device_static_pubkeys
17            .iter()
18            .any(|config_pubkey| config_pubkey.as_slice() == pubkey)
19    }
20
21    pub(crate) fn add_device_static_pubkey(&mut self, pubkey: &[u8]) {
22        if !self.contains_device_static_pubkey(pubkey) {
23            self.device_static_pubkeys.push(pubkey.to_vec());
24        }
25    }
26
27    pub(crate) fn get_app_static_privkey(&self) -> Option<zeroize::Zeroizing<[u8; 32]>> {
28        // This zeroize is just to make the types work. Ideally we'd zerioze the struct field too,
29        // but that is not compatible with serde.
30        self.app_static_privkey.map(zeroize::Zeroizing::new)
31    }
32
33    pub(crate) fn set_app_static_privkey(&mut self, privkey: &[u8]) -> Result<(), ConfigError> {
34        self.app_static_privkey = Some(
35            privkey
36                .try_into()
37                .map_err(|e: std::array::TryFromSliceError| ConfigError(e.to_string()))?,
38        );
39        Ok(())
40    }
41}
42
43pub trait NoiseConfig: Threading {
44    fn read_config(&self) -> Result<NoiseConfigData, ConfigError> {
45        Ok(NoiseConfigData::default())
46    }
47    fn store_config(&self, _conf: &NoiseConfigData) -> Result<(), ConfigError> {
48        Ok(())
49    }
50}
51
52pub struct NoiseConfigNoCache;
53impl NoiseConfig for NoiseConfigNoCache {}
54impl Threading for NoiseConfigNoCache {}
55
56pub struct PersistedNoiseConfig {
57    config_dir: String,
58}
59
60impl Threading for PersistedNoiseConfig {}
61
62impl PersistedNoiseConfig {
63    /// Creates a new persisting noise config, which stores the pairing information in "bitbox.json"
64    /// in the provided directory.
65    pub fn new(config_dir: &str) -> PersistedNoiseConfig {
66        PersistedNoiseConfig {
67            config_dir: config_dir.into(),
68        }
69    }
70}
71
72impl NoiseConfig for PersistedNoiseConfig {
73    fn read_config(&self) -> Result<NoiseConfigData, ConfigError> {
74        use std::io::Read;
75
76        let config_path = std::path::Path::new(&self.config_dir).join("bitbox.json");
77
78        if !config_path.exists() {
79            return Ok(NoiseConfigData::default());
80        }
81
82        let mut file = std::fs::File::open(config_path).map_err(|e| ConfigError(e.to_string()))?;
83
84        let mut contents = String::new();
85        file.read_to_string(&mut contents)
86            .map_err(|e| ConfigError(e.to_string()))?;
87
88        serde_json::from_str::<NoiseConfigData>(&contents).map_err(|e| ConfigError(e.to_string()))
89    }
90
91    fn store_config(&self, conf: &NoiseConfigData) -> Result<(), ConfigError> {
92        use std::io::Write;
93
94        let config_path = std::path::Path::new(&self.config_dir).join("bitbox.json");
95
96        let mut file =
97            std::fs::File::create(config_path).map_err(|e| ConfigError(e.to_string()))?;
98
99        let data = serde_json::to_string(conf).map_err(|e| ConfigError(e.to_string()))?;
100
101        file.write_all(data.as_bytes())
102            .map_err(|e| ConfigError(e.to_string()))
103    }
104}