miden_client/keystore/
fs_keystore.rs1use alloc::string::String;
2use std::{
3 fs::OpenOptions,
4 io::{BufRead, BufReader, BufWriter, Write},
5 path::PathBuf,
6 string::ToString,
7 sync::Arc,
8 vec::Vec,
9};
10
11use miden_objects::{
12 Digest, Felt, Word,
13 account::{AccountDelta, AuthSecretKey},
14};
15use miden_tx::{
16 AuthenticationError,
17 auth::TransactionAuthenticator,
18 utils::{Deserializable, Serializable, sync::RwLock},
19};
20use rand::{Rng, SeedableRng};
21
22use super::KeyStoreError;
23
24#[derive(Debug, Clone)]
31pub struct FilesystemKeyStore<R: Rng> {
32 rng: Arc<RwLock<R>>,
34 keys_directory: PathBuf,
36}
37
38impl<R: Rng> FilesystemKeyStore<R> {
39 pub fn with_rng(keys_directory: PathBuf, rng: R) -> Result<Self, KeyStoreError> {
40 if !keys_directory.exists() {
41 std::fs::create_dir_all(&keys_directory).map_err(|err| {
42 KeyStoreError::StorageError(format!("error creating keys directory: {err:?}"))
43 })?;
44 }
45
46 Ok(FilesystemKeyStore {
47 keys_directory,
48 rng: Arc::new(RwLock::new(rng)),
49 })
50 }
51
52 pub fn add_key(&self, key: &AuthSecretKey) -> Result<(), KeyStoreError> {
54 let pub_key = match key {
55 AuthSecretKey::RpoFalcon512(k) => Digest::from(Word::from(k.public_key())).to_hex(),
56 };
57
58 let file_path = self.keys_directory.join(pub_key);
59 let file = OpenOptions::new()
60 .write(true)
61 .create(true)
62 .truncate(true)
63 .open(file_path)
64 .map_err(|err| {
65 KeyStoreError::StorageError(format!("error opening secret key file: {err:?}"))
66 })?;
67
68 let mut writer = BufWriter::new(file);
69 let key_pair_hex = hex::encode(key.to_bytes());
70 writer.write_all(key_pair_hex.as_bytes()).map_err(|err| {
71 KeyStoreError::StorageError(format!("error writing secret key file: {err:?}"))
72 })?;
73
74 Ok(())
75 }
76
77 pub fn get_key(&self, pub_key: Word) -> Result<Option<AuthSecretKey>, KeyStoreError> {
79 let pub_key_str = Digest::from(pub_key).to_hex();
80
81 let file_path = self.keys_directory.join(pub_key_str);
82 if !file_path.exists() {
83 return Ok(None);
84 }
85
86 let file = OpenOptions::new().read(true).open(file_path).map_err(|err| {
87 KeyStoreError::StorageError(format!("error opening secret key file: {err:?}"))
88 })?;
89 let mut reader = BufReader::new(file);
90 let mut key_pair_hex = String::new();
91 reader.read_line(&mut key_pair_hex).map_err(|err| {
92 KeyStoreError::StorageError(format!("error reading secret key file: {err:?}"))
93 })?;
94
95 let secret_key_bytes = hex::decode(key_pair_hex.trim()).map_err(|err| {
96 KeyStoreError::DecodingError(format!("error decoding secret key hex: {err:?}"))
97 })?;
98 let secret_key =
99 AuthSecretKey::read_from_bytes(secret_key_bytes.as_slice()).map_err(|err| {
100 KeyStoreError::DecodingError(format!(
101 "error reading secret key from bytes: {err:?}"
102 ))
103 })?;
104
105 Ok(Some(secret_key))
106 }
107}
108
109impl FilesystemKeyStore<rand::rngs::StdRng> {
112 pub fn new(keys_directory: PathBuf) -> Result<Self, KeyStoreError> {
114 use rand::rngs::StdRng;
115 let rng = StdRng::from_os_rng();
116
117 FilesystemKeyStore::with_rng(keys_directory, rng)
118 }
119}
120
121impl<R: Rng> TransactionAuthenticator for FilesystemKeyStore<R> {
122 fn get_signature(
130 &self,
131 pub_key: Word,
132 message: Word,
133 _account_delta: &AccountDelta,
134 ) -> Result<Vec<Felt>, AuthenticationError> {
135 let mut rng = self.rng.write();
136
137 let secret_key = self
138 .get_key(pub_key)
139 .map_err(|err| AuthenticationError::other(err.to_string()))?;
140
141 let AuthSecretKey::RpoFalcon512(k) = secret_key
142 .ok_or(AuthenticationError::UnknownPublicKey(Digest::from(pub_key).into()))?;
143
144 miden_tx::auth::signatures::get_falcon_signature(&k, message, &mut *rng)
145 }
146}