bitbox_api/
noise.rs

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