1use alloc::{format, string::String, vec::Vec};
4use zeroize::Zeroizing;
5
6use crate::{
7 error::{Error, Result},
8 keygen::{ClientKeyShare, ServerKeyShare},
9 params::Params,
10};
11
12pub trait SecretStore {
15 fn put(&self, key: &str, value: &[u8]) -> Result<()>;
16 fn get(&self, key: &str) -> Result<Option<Zeroizing<Vec<u8>>>>;
17 fn delete(&self, key: &str) -> Result<()>;
18}
19
20fn client_key(wallet_id: &str) -> String {
21 format!("tecdsa/client-share/{wallet_id}")
22}
23
24fn server_key(wallet_id: &str) -> String {
25 format!("tecdsa/server-share/{wallet_id}")
26}
27
28fn put_new(store: &dyn SecretStore, key: &str, value: &[u8]) -> Result<()> {
31 if store.get(key)?.is_some() {
32 return Err(Error::AlreadyExists);
33 }
34 store.put(key, value)
35}
36
37impl ClientKeyShare {
38 pub fn save(&self, store: &dyn SecretStore, wallet_id: &str) -> Result<()> {
41 put_new(store, &client_key(wallet_id), &self.export_secret())
42 }
43
44 pub fn replace(&self, store: &dyn SecretStore, wallet_id: &str) -> Result<()> {
46 store.put(&client_key(wallet_id), &self.export_secret())
47 }
48
49 pub fn load(store: &dyn SecretStore, wallet_id: &str, params: &Params) -> Result<Self> {
50 let bytes = store.get(&client_key(wallet_id))?.ok_or(Error::NotFound)?;
51 Self::import_secret(&bytes, params)
52 }
53}
54
55impl ServerKeyShare {
56 pub fn save(&self, store: &dyn SecretStore, wallet_id: &str) -> Result<()> {
58 put_new(store, &server_key(wallet_id), &self.export_secret())
59 }
60
61 pub fn replace(&self, store: &dyn SecretStore, wallet_id: &str) -> Result<()> {
63 store.put(&server_key(wallet_id), &self.export_secret())
64 }
65
66 pub fn load(store: &dyn SecretStore, wallet_id: &str, params: &Params) -> Result<Self> {
67 let bytes = store.get(&server_key(wallet_id))?.ok_or(Error::NotFound)?;
68 Self::import_secret(&bytes, params)
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 extern crate std;
75 use super::*;
76 use crate::testutil::{keygen, params};
77 use core::cell::RefCell;
78 use std::collections::BTreeMap;
79
80 #[derive(Default)]
81 struct MemStore(RefCell<BTreeMap<String, Vec<u8>>>);
82
83 impl SecretStore for MemStore {
84 fn put(&self, key: &str, value: &[u8]) -> Result<()> {
85 self.0.borrow_mut().insert(key.into(), value.to_vec());
86 Ok(())
87 }
88 fn get(&self, key: &str) -> Result<Option<Zeroizing<Vec<u8>>>> {
89 Ok(self.0.borrow().get(key).map(|v| Zeroizing::new(v.clone())))
90 }
91 fn delete(&self, key: &str) -> Result<()> {
92 self.0.borrow_mut().remove(key);
93 Ok(())
94 }
95 }
96
97 #[test]
98 fn shares_roundtrip_through_store() {
99 let (c, s) = keygen(41);
100 let store = MemStore::default();
101 c.save(&store, "w1").unwrap();
102 s.save(&store, "w1").unwrap();
103 let c2 = ClientKeyShare::load(&store, "w1", ¶ms()).unwrap();
104 let s2 = ServerKeyShare::load(&store, "w1", ¶ms()).unwrap();
105 assert_eq!(c2.key_id(), c.key_id());
106 assert_eq!(s2.key_id(), s.key_id());
107 assert_eq!(
108 ClientKeyShare::load(&store, "missing", ¶ms()).unwrap_err(),
109 Error::NotFound
110 );
111 store.delete(&client_key("w1")).unwrap();
112 assert!(ClientKeyShare::load(&store, "w1", ¶ms()).is_err());
113 }
114
115 #[test]
116 fn save_never_overwrites() {
117 let (c, s) = keygen(41);
118 let (c_other, s_other) = keygen(42);
119 let store = MemStore::default();
120 c.save(&store, "w1").unwrap();
121 s.save(&store, "w1").unwrap();
122 assert_eq!(
123 c_other.save(&store, "w1").unwrap_err(),
124 Error::AlreadyExists
125 );
126 assert_eq!(
127 s_other.save(&store, "w1").unwrap_err(),
128 Error::AlreadyExists
129 );
130 let kept = ClientKeyShare::load(&store, "w1", ¶ms()).unwrap();
131 assert_eq!(kept.key_id(), c.key_id());
132 c_other.replace(&store, "w1").unwrap();
133 s_other.replace(&store, "w1").unwrap();
134 let now = ClientKeyShare::load(&store, "w1", ¶ms()).unwrap();
135 assert_eq!(now.key_id(), c_other.key_id());
136 }
137}