breez_sdk_liquid/plugin/
storage.rs1use aes::cipher::generic_array::GenericArray;
2use aes_gcm::{
3 aead::{Aead, OsRng},
4 AeadCore as _, Aes256Gcm, KeyInit as _, Nonce,
5};
6use anyhow::{bail, Result};
7
8use std::sync::{Arc, Weak};
9
10use crate::persist::Persister;
11
12#[derive(Clone)]
13pub struct PluginStorage {
14 plugin_id: String,
15 persister: Weak<Persister>,
16 cipher: Aes256Gcm,
17}
18
19#[derive(Debug, thiserror::Error)]
20pub enum PluginStorageError {
21 #[error("Could not write to storage: value has changed since last read.")]
22 DataTooOld,
23
24 #[error("Could not encrypt storage data: {err}")]
25 Encryption { err: String },
26
27 #[error("Plugin storage operation failed: {err}")]
28 Generic { err: String },
29}
30
31impl From<aes_gcm::Error> for PluginStorageError {
32 fn from(value: aes_gcm::Error) -> Self {
33 Self::Encryption {
34 err: value.to_string(),
35 }
36 }
37}
38
39impl From<rusqlite::Error> for PluginStorageError {
40 fn from(value: rusqlite::Error) -> Self {
41 Self::Generic {
42 err: value.to_string(),
43 }
44 }
45}
46
47impl From<anyhow::Error> for PluginStorageError {
48 fn from(value: anyhow::Error) -> Self {
49 Self::Generic {
50 err: value.to_string(),
51 }
52 }
53}
54
55impl PluginStorage {
56 pub(crate) fn new(
57 persister: Weak<Persister>,
58 passphrase: &[u8],
59 plugin_id: String,
60 ) -> Result<Self> {
61 if plugin_id.is_empty() {
62 log::error!("Plugin ID cannot be an empty string!");
63 bail!("Plugin ID cannot be an empty string!");
64 }
65 let passphrase = GenericArray::clone_from_slice(passphrase);
66 let cipher = Aes256Gcm::new(&passphrase);
67
68 Ok(Self {
69 cipher,
70 persister,
71 plugin_id,
72 })
73 }
74
75 fn get_persister(&self) -> Result<Arc<Persister>, PluginStorageError> {
76 self.persister.upgrade().ok_or(PluginStorageError::Generic {
77 err: "SDK is not running.".to_string(),
78 })
79 }
80
81 fn encrypt(&self, data: String) -> Result<String, PluginStorageError> {
82 let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
83 let encrypted = self.cipher.encrypt(&nonce, data.as_bytes())?;
84 let mut payload = nonce.to_vec();
85 payload.extend_from_slice(&encrypted);
86 Ok(hex::encode(payload))
87 }
88
89 fn decrypt(&self, data: String) -> Result<String, PluginStorageError> {
90 let decoded = hex::decode(data).map_err(|err| PluginStorageError::Encryption {
91 err: err.to_string(),
92 })?;
93 let (nonce, data) = decoded.split_at(12);
94 let nonce = Nonce::from_slice(nonce);
95 let decrypted = self.cipher.decrypt(nonce, data)?;
96 let result =
97 String::from_utf8(decrypted).map_err(|err| PluginStorageError::Encryption {
98 err: err.to_string(),
99 })?;
100 Ok(result)
101 }
102
103 pub(crate) fn scoped_key(&self, key: &str) -> String {
104 format!("{}-{}", self.plugin_id, key)
105 }
106
107 pub fn set_item(
116 &self,
117 key: &str,
118 value: String,
119 old_value: Option<String>,
120 ) -> Result<(), PluginStorageError> {
121 let scoped_key = self.scoped_key(key);
122 let persister = self.get_persister()?;
123 let mut con = persister.get_connection()?;
124 let tx = con.transaction_with_behavior(rusqlite::TransactionBehavior::Immediate)?;
125 if let Some(old_value) = old_value {
126 if let Some(current_value) = Persister::get_cached_item_inner(&tx, &scoped_key)? {
127 let current_value = self.decrypt(current_value)?;
128 if old_value != current_value {
129 return Err(PluginStorageError::DataTooOld);
130 }
131 }
132 }
133 Persister::update_cached_item_inner(&tx, &scoped_key, self.encrypt(value)?)?;
134 tx.commit()?;
135 Ok(())
136 }
137
138 pub fn get_item(&self, key: &str) -> Result<Option<String>, PluginStorageError> {
139 let scoped_key = self.scoped_key(key);
140 let value = self
141 .get_persister()?
142 .get_cached_item(&scoped_key)
143 .map_err(Into::<PluginStorageError>::into)?;
144 if let Some(value) = value {
145 return Ok(Some(self.decrypt(value)?));
146 }
147 Ok(None)
148 }
149
150 pub fn remove_item(&self, key: &str) -> Result<(), PluginStorageError> {
151 let scoped_key = self.scoped_key(key);
152 self.get_persister()?
153 .delete_cached_item(&scoped_key)
154 .map_err(Into::into)
155 }
156}