kassandra_client/
config.rs1use std::collections::BTreeMap;
6use std::collections::btree_map::Entry;
7use std::io::ErrorKind;
8use std::path::Path;
9
10use fmd::KeyExpansion;
11use fmd::fmd2_compact::{CompactSecretKey, MultiFmd2CompactScheme};
12use serde::{Deserialize, Serialize};
13use sha2::Digest;
14use shared::db::EncKey;
15
16use crate::error::{self, Error};
17
18pub const CLIENT_FILE_NAME: &str = "kassandra-client.toml";
20
21#[derive(Debug, Default, Clone, Serialize, Deserialize)]
22pub struct Config {
23 pub services: BTreeMap<String, Vec<Service>>,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct Service {
31 pub url: String,
33 pub index: usize,
35 pub enc_key: EncKey,
37}
38
39impl Config {
40 pub fn load(path: impl AsRef<Path>) -> error::Result<Self> {
42 toml::from_str(&std::fs::read_to_string(path).map_err(Error::Io)?).map_err(|e| {
43 Error::Io(std::io::Error::new(
44 ErrorKind::InvalidData,
45 format!("Could not parse client config file: {e}"),
46 ))
47 })
48 }
49
50 pub fn load_or_new(path: impl AsRef<Path>) -> error::Result<Self> {
52 let path = path.as_ref().join(CLIENT_FILE_NAME);
53 if path.exists() {
54 Self::load(path)
55 } else {
56 Ok(Self::default())
57 }
58 }
59
60 pub fn save(&mut self, path: impl AsRef<Path>) -> error::Result<()> {
62 for (_, services) in self.services.iter_mut() {
63 services.sort_by_key(|s| s.index);
64 services.dedup_by_key(|s| s.index);
65 }
66 let dest = path.as_ref().join(CLIENT_FILE_NAME);
67 std::fs::write(
68 dest,
69 toml::to_string(&self).expect("This operation should not fail"),
70 )
71 .map_err(Error::Io)
72 }
73
74 pub fn add_service(&mut self, key: String, url: &str, enc_key: EncKey) {
76 match self.services.entry(key) {
77 Entry::Vacant(e) => {
78 e.insert(vec![Service {
79 url: url.to_string(),
80 index: 1,
81 enc_key,
82 }]);
83 }
84 Entry::Occupied(mut o) => {
85 let ix = o.get().iter().map(|s| s.index).max().unwrap_or_default();
86 o.get_mut().push(Service {
87 url: url.to_string(),
88 index: ix + 1,
89 enc_key,
90 });
91 }
92 }
93 }
94
95 pub fn get_services(&self, key: &String) -> Vec<Service> {
97 self.services.get(key).cloned().unwrap_or_default()
98 }
99}
100
101pub fn hash_key(csk_key: &CompactSecretKey, gamma: usize) -> String {
103 let mut hasher = sha2::Sha256::new();
104
105 let cpk_key = csk_key.master_public_key();
106 let mut scheme = MultiFmd2CompactScheme::new(gamma, 1);
107 let (fmd_key, _) = scheme.expand_keypair(csk_key, &cpk_key);
108
109 hasher.update(serde_json::to_string(&fmd_key).unwrap().as_bytes());
110 let bytes: [u8; 32] = hasher.finalize().into();
111 hex::encode(bytes)
112}