From 0c61d672ceee4b878e87a0209ce8022454021636 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 22 Aug 2025 11:05:51 +0200 Subject: [PATCH 1/3] WIP: Bump `vss-client` dependency .. we bump the `vss-client` dependency. --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 96a9eea53..b071fd6a0 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -90,7 +90,8 @@ serde = { version = "1.0.210", default-features = false, features = ["std", "der serde_json = { version = "1.0.128", default-features = false, features = ["std"] } log = { version = "0.4.22", default-features = false, features = ["std"]} -vss-client = "0.3" +#vss-client = "0.3" +vss-client = { git = "https://github.com/tnull/vss-rust-client", rev = "03ca9d99f70387aabec225020e46434cda8d18ff" } prost = { version = "0.11.6", default-features = false} [target.'cfg(windows)'.dependencies] From 59651e8c4e84cf43515f08cd102b3345c334637c Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 22 Aug 2025 11:06:53 +0200 Subject: [PATCH 2/3] Supply `data_decryption_key` and `aad` for `StorableBuilder` Previously, we the `vss-client` didn't allow to set `ChaCha20Poly1305RFC`'s `aad` field, which had the `tag` not commit to any particular key. This would allow a malicious VSS provider to substitute blobs stored under a different key without the client noticing. Here, we now set the `aad` field to the key under which the `Storable` will be stored, ensuring that the retrieved data was originally stored under the key we expected. We also account for `StorableBuilder` now taking `data_decryption_key` by reference on `build`/`deconstruct`. --- src/io/vss_store.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/io/vss_store.rs b/src/io/vss_store.rs index e2cfc3c7b..33e693435 100644 --- a/src/io/vss_store.rs +++ b/src/io/vss_store.rs @@ -43,7 +43,7 @@ pub struct VssStore { client: VssClient, store_id: String, runtime: Arc, - storable_builder: StorableBuilder, + data_encryption_key: [u8; 32], key_obfuscator: KeyObfuscator, } @@ -55,7 +55,6 @@ impl VssStore { let (data_encryption_key, obfuscation_master_key) = derive_data_encryption_and_obfuscation_keys(&vss_seed); let key_obfuscator = KeyObfuscator::new(obfuscation_master_key); - let storable_builder = StorableBuilder::new(data_encryption_key, RandEntropySource); let retry_policy = ExponentialBackoffRetryPolicy::new(Duration::from_millis(10)) .with_max_attempts(10) .with_max_total_delay(Duration::from_secs(15)) @@ -70,7 +69,7 @@ impl VssStore { }) as _); let client = VssClient::new_with_headers(base_url, retry_policy, header_provider); - Self { client, store_id, runtime, storable_builder, key_obfuscator } + Self { client, store_id, runtime, data_encryption_key, key_obfuscator } } fn build_key( @@ -132,10 +131,9 @@ impl KVStore for VssStore { &self, primary_namespace: &str, secondary_namespace: &str, key: &str, ) -> io::Result> { check_namespace_key_validity(primary_namespace, secondary_namespace, Some(key), "read")?; - let request = GetObjectRequest { - store_id: self.store_id.clone(), - key: self.build_key(primary_namespace, secondary_namespace, key)?, - }; + + let store_key = self.build_key(primary_namespace, secondary_namespace, key)?; + let request = GetObjectRequest { store_id: self.store_id.clone(), key: store_key.clone() }; let resp = self.runtime.block_on(self.client.get_object(&request)).map_err(|e| { let msg = format!( "Failed to read from key {}/{}/{}: {}", @@ -156,20 +154,31 @@ impl KVStore for VssStore { Error::new(ErrorKind::Other, msg) })?; - Ok(self.storable_builder.deconstruct(storable)?.0) + let storable_builder = StorableBuilder::new(RandEntropySource); + let decrypted = storable_builder + .deconstruct(storable, &self.data_encryption_key, store_key.as_bytes())? + .0; + Ok(decrypted) } 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 store_key = self.build_key(primary_namespace, secondary_namespace, key)?; let version = -1; - let storable = self.storable_builder.build(buf.to_vec(), version); + let storable_builder = StorableBuilder::new(RandEntropySource); + let storable = storable_builder.build( + buf.to_vec(), + version, + &&self.data_encryption_key, + store_key.as_bytes(), + ); 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)?, + key: store_key, version, value: storable.encode_to_vec(), }], From 3490f2a32dd5a3c73ec6c333a318664e97ab4b98 Mon Sep 17 00:00:00 2001 From: Elias Rohrer Date: Fri, 22 Aug 2025 11:52:55 +0200 Subject: [PATCH 3/3] Zeroize `VssStore`'s `data_encryption_key` on `Drop` .. to make sure it doesn't linger in memory. --- src/io/vss_store.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/io/vss_store.rs b/src/io/vss_store.rs index 33e693435..e93aa9c8a 100644 --- a/src/io/vss_store.rs +++ b/src/io/vss_store.rs @@ -38,12 +38,14 @@ type CustomRetryPolicy = FilteredRetryPolicy< Box bool + 'static + Send + Sync>, >; +const KEY_LENGTH: usize = 32; + /// 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: Arc, - data_encryption_key: [u8; 32], + data_encryption_key: [u8; KEY_LENGTH], key_obfuscator: KeyObfuscator, } @@ -237,6 +239,12 @@ impl KVStore for VssStore { } } +impl Drop for VssStore { + fn drop(&mut self) { + self.data_encryption_key.copy_from_slice(&[0u8; KEY_LENGTH]); + } +} + fn derive_data_encryption_and_obfuscation_keys(vss_seed: &[u8; 32]) -> ([u8; 32], [u8; 32]) { let hkdf = |initial_key_material: &[u8], salt: &[u8]| -> [u8; 32] { let mut engine = HmacEngine::::new(salt);