etopay_sdk/wallet/
kdbx.rs1use etopay_wallet::bip39::Mnemonic;
2use kdbx_rs::database::Entry;
3use kdbx_rs::errors::FailedUnlock;
4use kdbx_rs::{CompositeKey, Database, Kdbx};
5use log::info;
6use secrecy::{ExposeSecret, SecretString};
7
8pub fn load_mnemonic(backup: &[u8], password: &SecretString) -> Result<Mnemonic, KdbxStorageError> {
10 info!("Loading kdbx file from bytes");
11 let kdbx = kdbx_rs::from_reader(backup)?;
12 let key = CompositeKey::from_password(password.expose_secret());
13 let unlocked = kdbx.unlock(&key)?;
14
15 let Some(entry) = unlocked.find_entry(|entry| entry.title() == Some("mnemonic")) else {
16 return Err(KdbxStorageError::NotFound("Entry not found".to_string()));
17 };
18
19 let Some(mnemonic) = entry.password() else {
20 return Err(KdbxStorageError::NotFound("Mnemonic not found".to_string()));
21 };
22
23 let mnemonic = Mnemonic::from_phrase(mnemonic, etopay_wallet::bip39::Language::English)?;
24
25 Ok(mnemonic)
26}
27
28pub fn store_mnemonic(mnemonic: &Mnemonic, password: &SecretString) -> Result<Vec<u8>, KdbxStorageError> {
30 info!("Creating kdbx file from mnemonic");
31
32 let mut database = Database::default();
33 database.set_name("etopay");
34
35 let mut entry = Entry::default();
36 entry.set_title("mnemonic");
37 entry.set_password(mnemonic.phrase());
38 database.add_entry(entry);
39
40 let mut kdbx = Kdbx::from_database(database);
41 kdbx.set_key(CompositeKey::from_password(password.expose_secret()))?;
42
43 let mut buffer = Vec::new();
44
45 kdbx.write(&mut buffer)?;
46
47 Ok(buffer)
48}
49
50#[derive(Debug, thiserror::Error)]
52pub enum KdbxStorageError {
53 #[error("KdbxError: {0}")]
55 KdbxError(#[from] kdbx_rs::Error),
56 #[error("OpenError: {0}")]
58 OpenError(#[from] kdbx_rs::errors::OpenError),
59 #[error("WriteError: {0}")]
61 WriteError(#[from] kdbx_rs::errors::WriteError),
62 #[error("UnlockError: {0}")]
64 UnlockError(#[from] kdbx_rs::errors::UnlockError),
65 #[error("KeyGenerationError: {0}")]
67 KeyGenerationError(#[from] kdbx_rs::errors::KeyGenerationError),
68 #[error("Not found: {0}")]
70 NotFound(String),
71
72 #[error("Bip39 error: {0:?}")]
74 Bip39(#[from] etopay_wallet::bip39::ErrorKind),
75}
76
77impl From<FailedUnlock> for KdbxStorageError {
78 fn from(funlock: FailedUnlock) -> KdbxStorageError {
79 KdbxStorageError::UnlockError(funlock.1)
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use secrecy::SecretString;
87
88 #[test]
89 fn test_store_and_load_mnemonic() {
90 let mnemonic = Mnemonic::new(
92 etopay_wallet::bip39::MnemonicType::Words24,
93 etopay_wallet::bip39::Language::English,
94 );
95
96 let password = SecretString::new("password".into());
97
98 let kdbx = store_mnemonic(&mnemonic, &password).unwrap();
100 let mnemonic_recovered = load_mnemonic(&kdbx, &password).unwrap();
101
102 assert_eq!(mnemonic_recovered.phrase(), mnemonic.phrase());
104 }
105}