nym_client_core/client/key_manager/
persistence.rs

1// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::client::key_manager::ClientKeys;
5use async_trait::async_trait;
6use rand::{CryptoRng, RngCore};
7use std::error::Error;
8use std::sync::Arc;
9use tokio::sync::Mutex;
10
11#[cfg(not(target_arch = "wasm32"))]
12use crate::config::disk_persistence::ClientKeysPaths;
13#[cfg(not(target_arch = "wasm32"))]
14use nym_crypto::asymmetric::{ed25519, x25519};
15#[cfg(not(target_arch = "wasm32"))]
16use nym_pemstore::traits::{PemStorableKey, PemStorableKeyPair};
17#[cfg(not(target_arch = "wasm32"))]
18use nym_pemstore::KeyPairPath;
19#[cfg(not(target_arch = "wasm32"))]
20use nym_sphinx::acknowledgements::AckKey;
21
22// we have to define it as an async trait since wasm storage is async
23#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
24#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
25pub trait KeyStore {
26    type StorageError: Error;
27
28    async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError>;
29
30    async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError>;
31}
32
33#[cfg(not(target_arch = "wasm32"))]
34#[derive(Debug, thiserror::Error)]
35pub enum OnDiskKeysError {
36    #[error("failed to load {keys} keys from {:?} (private key) and {:?} (public key): {err}", .paths.private_key_path, .paths.public_key_path)]
37    KeyPairLoadFailure {
38        keys: String,
39        paths: nym_pemstore::KeyPairPath,
40        #[source]
41        err: std::io::Error,
42    },
43
44    #[error("failed to store {keys} keys to {:?} (private key) and {:?} (public key): {err}", .paths.private_key_path, .paths.public_key_path)]
45    KeyPairStoreFailure {
46        keys: String,
47        paths: nym_pemstore::KeyPairPath,
48        #[source]
49        err: std::io::Error,
50    },
51
52    #[error("failed to load {key} key from {path}: {err}")]
53    KeyLoadFailure {
54        key: String,
55        path: String,
56        #[source]
57        err: std::io::Error,
58    },
59
60    #[error("failed to store {key} key to {path}: {err}")]
61    KeyStoreFailure {
62        key: String,
63        path: String,
64        #[source]
65        err: std::io::Error,
66    },
67}
68
69#[derive(Clone)]
70#[cfg(not(target_arch = "wasm32"))]
71pub struct OnDiskKeys {
72    paths: ClientKeysPaths,
73}
74
75#[cfg(not(target_arch = "wasm32"))]
76impl From<ClientKeysPaths> for OnDiskKeys {
77    fn from(paths: ClientKeysPaths) -> Self {
78        OnDiskKeys { paths }
79    }
80}
81
82#[cfg(not(target_arch = "wasm32"))]
83impl OnDiskKeys {
84    pub fn new(paths: ClientKeysPaths) -> Self {
85        OnDiskKeys { paths }
86    }
87
88    #[doc(hidden)]
89    pub fn load_encryption_keypair(&self) -> Result<x25519::KeyPair, OnDiskKeysError> {
90        let encryption_paths = self.paths.encryption_key_pair_path();
91        self.load_keypair(encryption_paths, "encryption")
92    }
93
94    #[doc(hidden)]
95    pub fn load_identity_keypair(&self) -> Result<ed25519::KeyPair, OnDiskKeysError> {
96        let identity_paths = self.paths.identity_key_pair_path();
97        self.load_keypair(identity_paths, "identity")
98    }
99
100    fn load_key<T: PemStorableKey>(
101        &self,
102        path: &std::path::Path,
103        name: impl Into<String>,
104    ) -> Result<T, OnDiskKeysError> {
105        nym_pemstore::load_key(path).map_err(|err| OnDiskKeysError::KeyLoadFailure {
106            key: name.into(),
107            path: path.to_str().map(|s| s.to_owned()).unwrap_or_default(),
108            err,
109        })
110    }
111
112    fn load_keypair<T: PemStorableKeyPair>(
113        &self,
114        paths: KeyPairPath,
115        name: impl Into<String>,
116    ) -> Result<T, OnDiskKeysError> {
117        nym_pemstore::load_keypair(&paths).map_err(|err| OnDiskKeysError::KeyPairLoadFailure {
118            keys: name.into(),
119            paths,
120            err,
121        })
122    }
123
124    fn store_key<T: PemStorableKey>(
125        &self,
126        key: &T,
127        path: &std::path::Path,
128        name: impl Into<String>,
129    ) -> Result<(), OnDiskKeysError> {
130        nym_pemstore::store_key(key, path).map_err(|err| OnDiskKeysError::KeyStoreFailure {
131            key: name.into(),
132            path: path.to_str().map(|s| s.to_owned()).unwrap_or_default(),
133            err,
134        })
135    }
136
137    fn store_keypair<T: PemStorableKeyPair>(
138        &self,
139        keys: &T,
140        paths: KeyPairPath,
141        name: impl Into<String>,
142    ) -> Result<(), OnDiskKeysError> {
143        nym_pemstore::store_keypair(keys, &paths).map_err(|err| {
144            OnDiskKeysError::KeyPairStoreFailure {
145                keys: name.into(),
146                paths,
147                err,
148            }
149        })
150    }
151
152    fn load_keys(&self) -> Result<ClientKeys, OnDiskKeysError> {
153        let identity_keypair = self.load_identity_keypair()?;
154        let encryption_keypair = self.load_encryption_keypair()?;
155        let ack_key: AckKey = self.load_key(self.paths.ack_key(), "ack key")?;
156
157        Ok(ClientKeys::from_keys(
158            identity_keypair,
159            encryption_keypair,
160            ack_key,
161        ))
162    }
163
164    fn store_keys(&self, keys: &ClientKeys) -> Result<(), OnDiskKeysError> {
165        let identity_paths = self.paths.identity_key_pair_path();
166        let encryption_paths = self.paths.encryption_key_pair_path();
167
168        self.store_keypair(
169            keys.identity_keypair.as_ref(),
170            identity_paths,
171            "identity keys",
172        )?;
173        self.store_keypair(
174            keys.encryption_keypair.as_ref(),
175            encryption_paths,
176            "encryption keys",
177        )?;
178
179        self.store_key(keys.ack_key.as_ref(), self.paths.ack_key(), "ack key")?;
180
181        Ok(())
182    }
183}
184
185#[cfg(not(target_arch = "wasm32"))]
186#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
187impl KeyStore for OnDiskKeys {
188    type StorageError = OnDiskKeysError;
189
190    async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
191        self.load_keys()
192    }
193
194    async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
195        self.store_keys(keys)
196    }
197}
198
199#[derive(Clone)]
200pub struct InMemEphemeralKeys {
201    keys: Arc<Mutex<ClientKeys>>,
202}
203
204impl InMemEphemeralKeys {
205    pub fn new<R>(rng: &mut R) -> Self
206    where
207        R: RngCore + CryptoRng,
208    {
209        InMemEphemeralKeys {
210            keys: Arc::new(Mutex::new(ClientKeys::generate_new(rng))),
211        }
212    }
213}
214
215#[derive(Debug, thiserror::Error)]
216#[error("old ephemeral keys can't be loaded from storage")]
217pub struct EphemeralKeysError;
218
219#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
220#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
221impl KeyStore for InMemEphemeralKeys {
222    type StorageError = EphemeralKeysError;
223
224    async fn load_keys(&self) -> Result<ClientKeys, Self::StorageError> {
225        Ok(self.keys.lock().await.clone())
226    }
227
228    async fn store_keys(&self, keys: &ClientKeys) -> Result<(), Self::StorageError> {
229        *self.keys.lock().await = keys.clone();
230        Ok(())
231    }
232}