Skip to content

Commit

Permalink
Merge pull request #225 from G8XSU/vss-cse
Browse files Browse the repository at this point in the history
Add Vss client side encryption using StorableBuilder
  • Loading branch information
tnull committed Jan 8, 2024
2 parents 5b20403 + 6c168df commit 860a1d5
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 13 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ uniffi = { version = "0.25.1", features = ["build"], optional = true }

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

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase"] }
Expand Down
39 changes: 36 additions & 3 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ use bip39::Mnemonic;

use bitcoin::BlockHash;

#[cfg(any(vss, vss_test))]
use bitcoin::bip32::ChildNumber;
use std::convert::TryInto;
use std::default::Default;
use std::fmt;
Expand Down Expand Up @@ -285,10 +287,41 @@ impl NodeBuilder {
/// previously configured.
#[cfg(any(vss, vss_test))]
pub fn build_with_vss_store(
&self, url: &str, store_id: String,
&self, url: String, store_id: String,
) -> Result<Node<VssStore>, BuildError> {
let vss = Arc::new(VssStore::new(url, store_id));
self.build_with_store(vss)
let logger = setup_logger(&self.config)?;

let seed_bytes = seed_bytes_from_config(
&self.config,
self.entropy_source_config.as_ref(),
Arc::clone(&logger),
)?;
let config = Arc::new(self.config.clone());

let xprv = bitcoin::bip32::ExtendedPrivKey::new_master(config.network.into(), &seed_bytes)
.map_err(|e| {
log_error!(logger, "Failed to derive master secret: {}", e);
BuildError::InvalidSeedBytes
})?;

let vss_xprv = xprv
.ckd_priv(&Secp256k1::new(), ChildNumber::Hardened { index: 877 })
.map_err(|e| {
log_error!(logger, "Failed to derive VSS secret: {}", e);
BuildError::KVStoreSetupFailed
})?;

let vss_seed_bytes: [u8; 32] = vss_xprv.private_key.secret_bytes();

let vss_store = Arc::new(VssStore::new(url, store_id, vss_seed_bytes));
build_with_store_internal(
config,
self.chain_data_source_config.as_ref(),
self.gossip_source_config.as_ref(),
seed_bytes,
logger,
vss_store,
)
}

/// Builds a [`Node`] instance according to the options previously configured.
Expand Down
38 changes: 30 additions & 8 deletions src/io/vss_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,31 @@ use std::panic::RefUnwindSafe;

use crate::io::utils::check_namespace_key_validity;
use lightning::util::persist::KVStore;
use prost::Message;
use rand::RngCore;
use tokio::runtime::Runtime;
use vss_client::client::VssClient;
use vss_client::error::VssError;
use vss_client::types::{
DeleteObjectRequest, GetObjectRequest, KeyValue, ListKeyVersionsRequest, PutObjectRequest,
Storable,
};
use vss_client::util::storable_builder::{EntropySource, StorableBuilder};

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

impl VssStore {
pub(crate) fn new(base_url: &str, store_id: String) -> Self {
let client = VssClient::new(base_url);
pub(crate) fn new(base_url: String, store_id: String, data_encryption_key: [u8; 32]) -> Self {
let client = VssClient::new(base_url.as_str());
let runtime = tokio::runtime::Builder::new_multi_thread().enable_all().build().unwrap();
Self { client, store_id, runtime }
let storable_builder = StorableBuilder::new(data_encryption_key, RandEntropySource);
Self { client, store_id, runtime, storable_builder }
}

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

fn write(
&self, primary_namespace: &str, secondary_namespace: &str, key: &str, buf: &[u8],
) -> io::Result<()> {
check_namespace_key_validity(primary_namespace, secondary_namespace, Some(key), "write")?;
let version = -1;
let storable = self.storable_builder.build(buf.to_vec(), version);
let request = PutObjectRequest {
store_id: self.store_id.clone(),
global_version: None,
transaction_items: vec![KeyValue {
key: self.build_key(primary_namespace, secondary_namespace, key)?,
version: -1,
value: buf.to_vec(),
version,
value: storable.encode_to_vec(),
}],
delete_items: vec![],
};
Expand Down Expand Up @@ -171,6 +182,15 @@ impl KVStore for VssStore {
}
}

/// A source for generating entropy/randomness using [`rand`].
pub(crate) struct RandEntropySource;

impl EntropySource for RandEntropySource {
fn fill_bytes(&self, buffer: &mut [u8]) {
rand::thread_rng().fill_bytes(buffer);
}
}

#[cfg(test)]
impl RefUnwindSafe for VssStore {}

Expand All @@ -180,14 +200,16 @@ mod tests {
use super::*;
use crate::io::test_utils::do_read_write_remove_list_persist;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use rand::{thread_rng, Rng, RngCore};

#[test]
fn read_write_remove_list_persist() {
let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap();
let mut rng = thread_rng();
let rand_store_id: String = (0..7).map(|_| rng.sample(Alphanumeric) as char).collect();
let vss_store = VssStore::new(&vss_base_url, rand_store_id);
let mut data_encryption_key = [0u8; 32];
rng.fill_bytes(&mut data_encryption_key);
let vss_store = VssStore::new(vss_base_url, rand_store_id, data_encryption_key);

do_read_write_remove_list_persist(&vss_store);
}
Expand Down
5 changes: 3 additions & 2 deletions tests/integration_tests_vss.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ fn channel_full_cycle_with_vss_store() {
let mut builder_a = Builder::from_config(config_a);
builder_a.set_esplora_server(esplora_url.clone());
let vss_base_url = std::env::var("TEST_VSS_BASE_URL").unwrap();
let node_a = builder_a.build_with_vss_store(&vss_base_url, "node_1_store".to_string()).unwrap();
let node_a =
builder_a.build_with_vss_store(vss_base_url.clone(), "node_1_store".to_string()).unwrap();
node_a.start().unwrap();

println!("\n== Node B ==");
let config_b = common::random_config();
let mut builder_b = Builder::from_config(config_b);
builder_b.set_esplora_server(esplora_url);
let node_b = builder_b.build_with_vss_store(&vss_base_url, "node_2_store".to_string()).unwrap();
let node_b = builder_b.build_with_vss_store(vss_base_url, "node_2_store".to_string()).unwrap();
node_b.start().unwrap();

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

0 comments on commit 860a1d5

Please sign in to comment.