1use std::collections::HashMap;
7use std::fs;
8use std::io;
9use std::path::{Path, PathBuf};
10
11use serde::{Deserialize, Serialize};
12
13#[derive(Debug, thiserror::Error)]
15pub enum Error {
16 #[error("keystore I/O error: {0}")]
18 Io(#[from] io::Error),
19
20 #[error("keystore JSON error: {0}")]
22 Json(#[from] serde_json::Error),
23
24 #[error("key not found: {0}")]
26 NotFound(String),
27
28 #[error("key already exists: {0}")]
30 AlreadyExists(String),
31
32 #[error("unknown algorithm: {0}")]
34 UnknownAlgorithm(String),
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct StoredKey {
40 pub alg: String,
42
43 #[serde(with = "base64url_bytes")]
45 pub pub_key: Vec<u8>,
46
47 #[serde(with = "base64url_bytes")]
49 pub prv_key: Vec<u8>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub tag: Option<String>,
54}
55
56pub trait KeyStore {
60 fn store(&mut self, tmb: &str, key: StoredKey) -> Result<(), Error>;
62
63 fn get(&self, tmb: &str) -> Result<&StoredKey, Error>;
65
66 fn list(&self) -> Vec<&str>;
68
69 fn save(&self) -> Result<(), Error>;
71}
72
73pub struct JsonKeyStore {
78 path: PathBuf,
79 keys: HashMap<String, StoredKey>,
80}
81
82impl JsonKeyStore {
83 pub fn open(path: impl AsRef<Path>) -> Result<Self, Error> {
85 let path = path.as_ref().to_path_buf();
86
87 let keys = if path.exists() {
88 let content = fs::read_to_string(&path)?;
89 serde_json::from_str(&content)?
90 } else {
91 HashMap::new()
92 };
93
94 Ok(Self { path, keys })
95 }
96
97 pub fn path(&self) -> &Path {
99 &self.path
100 }
101}
102
103impl KeyStore for JsonKeyStore {
104 fn store(&mut self, tmb: &str, key: StoredKey) -> Result<(), Error> {
105 if self.keys.contains_key(tmb) {
106 return Err(Error::AlreadyExists(tmb.to_string()));
107 }
108 self.keys.insert(tmb.to_string(), key);
109 Ok(())
110 }
111
112 fn get(&self, tmb: &str) -> Result<&StoredKey, Error> {
113 self.keys
114 .get(tmb)
115 .ok_or_else(|| Error::NotFound(tmb.to_string()))
116 }
117
118 fn list(&self) -> Vec<&str> {
119 self.keys.keys().map(String::as_str).collect()
120 }
121
122 fn save(&self) -> Result<(), Error> {
123 let content = serde_json::to_string_pretty(&self.keys)?;
124 fs::write(&self.path, content)?;
125 Ok(())
126 }
127}
128
129mod base64url_bytes {
131 use base64ct::{Base64UrlUnpadded, Encoding};
132 use serde::{Deserialize, Deserializer, Serializer, de};
133
134 pub fn serialize<S>(bytes: &[u8], serializer: S) -> Result<S::Ok, S::Error>
135 where
136 S: Serializer,
137 {
138 let encoded = Base64UrlUnpadded::encode_string(bytes);
139 serializer.serialize_str(&encoded)
140 }
141
142 pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<u8>, D::Error>
143 where
144 D: Deserializer<'de>,
145 {
146 let s = String::deserialize(deserializer)?;
147 Base64UrlUnpadded::decode_vec(&s).map_err(de::Error::custom)
148 }
149}