Skip to content

Commit 6c168df

Browse files
committed
Add Vss client side encryption using StorableBuilder
1 parent 5b20403 commit 6c168df

File tree

4 files changed

+70
-13
lines changed

4 files changed

+70
-13
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ uniffi = { version = "0.25.1", features = ["build"], optional = true }
6969

7070
[target.'cfg(vss)'.dependencies]
7171
vss-client = "0.1"
72+
prost = { version = "0.11.6", default-features = false}
7273

7374
[target.'cfg(windows)'.dependencies]
7475
winapi = { version = "0.3", features = ["winbase"] }

src/builder.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ use bip39::Mnemonic;
5151

5252
use bitcoin::BlockHash;
5353

54+
#[cfg(any(vss, vss_test))]
55+
use bitcoin::bip32::ChildNumber;
5456
use std::convert::TryInto;
5557
use std::default::Default;
5658
use std::fmt;
@@ -285,10 +287,41 @@ impl NodeBuilder {
285287
/// previously configured.
286288
#[cfg(any(vss, vss_test))]
287289
pub fn build_with_vss_store(
288-
&self, url: &str, store_id: String,
290+
&self, url: String, store_id: String,
289291
) -> Result<Node<VssStore>, BuildError> {
290-
let vss = Arc::new(VssStore::new(url, store_id));
291-
self.build_with_store(vss)
292+
let logger = setup_logger(&self.config)?;
293+
294+
let seed_bytes = seed_bytes_from_config(
295+
&self.config,
296+
self.entropy_source_config.as_ref(),
297+
Arc::clone(&logger),
298+
)?;
299+
let config = Arc::new(self.config.clone());
300+
301+
let xprv = bitcoin::bip32::ExtendedPrivKey::new_master(config.network.into(), &seed_bytes)
302+
.map_err(|e| {
303+
log_error!(logger, "Failed to derive master secret: {}", e);
304+
BuildError::InvalidSeedBytes
305+
})?;
306+
307+
let vss_xprv = xprv
308+
.ckd_priv(&Secp256k1::new(), ChildNumber::Hardened { index: 877 })
309+
.map_err(|e| {
310+
log_error!(logger, "Failed to derive VSS secret: {}", e);
311+
BuildError::KVStoreSetupFailed
312+
})?;
313+
314+
let vss_seed_bytes: [u8; 32] = vss_xprv.private_key.secret_bytes();
315+
316+
let vss_store = Arc::new(VssStore::new(url, store_id, vss_seed_bytes));
317+
build_with_store_internal(
318+
config,
319+
self.chain_data_source_config.as_ref(),
320+
self.gossip_source_config.as_ref(),
321+
seed_bytes,
322+
logger,
323+
vss_store,
324+
)
292325
}
293326

294327
/// Builds a [`Node`] instance according to the options previously configured.

src/io/vss_store.rs

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,31 @@ use std::panic::RefUnwindSafe;
66

77
use crate::io::utils::check_namespace_key_validity;
88
use lightning::util::persist::KVStore;
9+
use prost::Message;
10+
use rand::RngCore;
911
use tokio::runtime::Runtime;
1012
use vss_client::client::VssClient;
1113
use vss_client::error::VssError;
1214
use vss_client::types::{
1315
DeleteObjectRequest, GetObjectRequest, KeyValue, ListKeyVersionsRequest, PutObjectRequest,
16+
Storable,
1417
};
18+
use vss_client::util::storable_builder::{EntropySource, StorableBuilder};
1519

1620
/// A [`KVStore`] implementation that writes to and reads from a [VSS](https://github.com/lightningdevkit/vss-server/blob/main/README.md) backend.
1721
pub struct VssStore {
1822
client: VssClient,
1923
store_id: String,
2024
runtime: Runtime,
25+
storable_builder: StorableBuilder<RandEntropySource>,
2126
}
2227

2328
impl VssStore {
24-
pub(crate) fn new(base_url: &str, store_id: String) -> Self {
25-
let client = VssClient::new(base_url);
29+
pub(crate) fn new(base_url: String, store_id: String, data_encryption_key: [u8; 32]) -> Self {
30+
let client = VssClient::new(base_url.as_str());
2631
let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap();
27-
Self { client, store_id, runtime }
32+
let storable_builder = StorableBuilder::new(data_encryption_key, RandEntropySource);
33+
Self { client, store_id, runtime, storable_builder }
2834
}
2935

3036
fn build_key(
@@ -99,20 +105,25 @@ impl KVStore for VssStore {
99105
_ => Error::new(ErrorKind::Other, msg),
100106
}
101107
})?;
102-
Ok(resp.value.unwrap().value)
108+
// unwrap safety: resp.value must be always present for a non-erroneous VSS response, otherwise
109+
// it is an API-violation which is converted to [`VssError::InternalServerError`] in [`VssClient`]
110+
let storable = Storable::decode(&resp.value.unwrap().value[..])?;
111+
Ok(self.storable_builder.deconstruct(storable)?.0)
103112
}
104113

105114
fn write(
106115
&self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: &[u8],
107116
) -> io::Result<()> {
108117
check_namespace_key_validity(primary_namespace, secondary_namespace, Some(key), "write")?;
118+
let version = -1;
119+
let storable = self.storable_builder.build(buf.to_vec(), version);
109120
let request = PutObjectRequest {
110121
store_id: self.store_id.clone(),
111122
global_version: None,
112123
transaction_items: vec![KeyValue {
113124
key: self.build_key(primary_namespace, secondary_namespace, key)?,
114-
version: -1,
115-
value: buf.to_vec(),
125+
version,
126+
value: storable.encode_to_vec(),
116127
}],
117128
delete_items: vec![],
118129
};
@@ -171,6 +182,15 @@ impl KVStore for VssStore {
171182
}
172183
}
173184

185+
/// A source for generating entropy/randomness using [`rand`].
186+
pub(crate) struct RandEntropySource;
187+
188+
impl EntropySource for RandEntropySource {
189+
fn fill_bytes(&self, buffer: &mut [u8]) {
190+
rand::thread_rng().fill_bytes(buffer);
191+
}
192+
}
193+
174194
#[cfg(test)]
175195
impl RefUnwindSafe for VssStore {}
176196

@@ -180,14 +200,16 @@ mod tests {
180200
use super::*;
181201
use crate::io::test_utils::do_read_write_remove_list_persist;
182202
use rand::distributions::Alphanumeric;
183-
use rand::{thread_rng, Rng};
203+
use rand::{thread_rng, Rng, RngCore};
184204

185205
#[test]
186206
fn read_write_remove_list_persist() {
187207
let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap();
188208
let mut rng = thread_rng();
189209
let rand_store_id: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect();
190-
let vss_store = VssStore::new(&vss_base_url, rand_store_id);
210+
let mut data_encryption_key = [0u8; 32];
211+
rng.fill_bytes(&mut data_encryption_key);
212+
let vss_store = VssStore::new(vss_base_url, rand_store_id, data_encryption_key);
191213

192214
do_read_write_remove_list_persist(&vss_store);
193215
}

tests/integration_tests_vss.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ fn channel_full_cycle_with_vss_store() {
1313
let mut builder_a = Builder::from_config(config_a);
1414
builder_a.set_esplora_server(esplora_url.clone());
1515
let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap();
16-
let node_a = builder_a.build_with_vss_store(&vss_base_url, "node_1_store".to_string()).unwrap();
16+
let node_a =
17+
builder_a.build_with_vss_store(vss_base_url.clone(), "node_1_store".to_string()).unwrap();
1718
node_a.start().unwrap();
1819

1920
println!("\n== Node B ==");
2021
let config_b = common::random_config();
2122
let mut builder_b = Builder::from_config(config_b);
2223
builder_b.set_esplora_server(esplora_url);
23-
let node_b = builder_b.build_with_vss_store(&vss_base_url, "node_2_store".to_string()).unwrap();
24+
let node_b = builder_b.build_with_vss_store(vss_base_url, "node_2_store".to_string()).unwrap();
2425
node_b.start().unwrap();
2526

2627
common::do_channel_full_cycle(node_a, node_b, &bitcoind.client, &electrsd.client, false);

0 commit comments

Comments
 (0)