Skip to content

Commit

Permalink
feat(nodes): encrypt all records before disk, decrypt on get
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuef committed Jan 25, 2024
1 parent c051d31 commit 94fc5f9
Showing 1 changed file with 64 additions and 26 deletions.
90 changes: 64 additions & 26 deletions sn_networking/src/record_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ use sn_protocol::{
storage::{RecordHeader, RecordKind, RecordType},
NetworkAddress, PrettyPrintRecordKey,
};
use sn_transfers::bls::SecretKey;
use sn_transfers::bls::{Ciphertext, PublicKey};
use sn_transfers::NanoTokens;
use std::{
borrow::Cow,
Expand Down Expand Up @@ -56,6 +58,8 @@ pub struct NodeRecordStore {
record_count_metric: Option<Gauge>,
/// Counting how many times got paid
received_payment_count: usize,
/// Encyption key for the records, randomly generated at node startup
encryption_key: SecretKey,
}

/// Configuration for a `DiskBackedRecordStore`.
Expand Down Expand Up @@ -97,6 +101,7 @@ impl NodeRecordStore {
#[cfg(feature = "open-metrics")]
record_count_metric: None,
received_payment_count: 0,
encryption_key: SecretKey::random(),
}
}

Expand All @@ -117,7 +122,11 @@ impl NodeRecordStore {
hex_string
}

fn read_from_disk<'a>(key: &Key, storage_dir: &Path) -> Option<Cow<'a, Record>> {
fn read_from_disk<'a>(
decryption_key: &SecretKey,
key: &Key,
storage_dir: &Path,
) -> Option<Cow<'a, Record>> {
let start = Instant::now();
let filename = Self::key_to_hex(key);
let file_path = storage_dir.join(&filename);
Expand All @@ -130,13 +139,19 @@ impl NodeRecordStore {
"Retrieved record from disk! filename: {filename} after {:?}",
start.elapsed()
);
let record = Record {
key: key.clone(),
value,
publisher: None,
expires: None,
};
Some(Cow::Owned(record))

if let Some(value) = Self::decrypt_record_value(decryption_key, value) {
let record = Record {
key: key.clone(),
value,
publisher: None,
expires: None,
};
return Some(Cow::Owned(record));
} else {
error!("Error while decrypting record. filename: {filename}");
None
}
}
Err(err) => {
error!("Error while reading file. filename: {filename}, error: {err:?}");
Expand Down Expand Up @@ -233,6 +248,19 @@ impl NodeRecordStore {
);
}

/// Takes a record value and encryptes the value with the record store encryption key
fn encrypt_record_value(pk: PublicKey, value: Vec<u8>) -> Result<Vec<u8>> {
Ok(pk.encrypt(value).to_bytes())
}

/// decryptes a Record value with the record store encryption key
fn decrypt_record_value(decrypt_key: &SecretKey, value: Vec<u8>) -> Option<Vec<u8>> {
let ciphertext = Ciphertext::from_bytes(&value).ok()?;
let decrypted_value = decrypt_key.decrypt(&ciphertext)?;

Some(decrypted_value)
}

/// Warning: Write's a `Record` to disk without validation
/// Should be used in context where the `Record` is trusted
///
Expand All @@ -252,27 +280,33 @@ impl NodeRecordStore {
let _ = metric.set(self.records.len() as i64);
}

let pk = self.encryption_key.public_key();

let cloned_cmd_sender = self.swarm_cmd_sender.clone();
spawn(async move {
let cmd = match fs::write(&file_path, r.value) {
Ok(_) => {
// vdash metric (if modified please notify at https://github.com/happybeing/vdash/issues):
info!("Wrote record {record_key:?} to disk! filename: {filename}");

SwarmCmd::AddLocalRecordAsStored {
key: r.key,
record_type,
if let Ok(value) = Self::encrypt_record_value(pk, r.value) {
let cmd = match fs::write(&file_path, value) {
Ok(_) => {
// vdash metric (if modified please notify at https://github.com/happybeing/vdash/issues):
info!("Wrote record {record_key:?} to disk! filename: {filename}");

SwarmCmd::AddLocalRecordAsStored {
key: r.key,
record_type,
}
}
}
Err(err) => {
error!(
Err(err) => {
error!(
"Error writing record {record_key:?} filename: {filename}, error: {err:?}"
);
SwarmCmd::RemoveFailedLocalRecord { key: r.key }
}
};
SwarmCmd::RemoveFailedLocalRecord { key: r.key }
}
};

send_swarm_cmd(cloned_cmd_sender, cmd);
send_swarm_cmd(cloned_cmd_sender, cmd);
} else {
warn!("Failed to encrypt record {record_key:?} filename: {filename}");
}
});

Ok(())
Expand Down Expand Up @@ -341,7 +375,7 @@ impl RecordStore for NodeRecordStore {

debug!("GET request for Record key: {key}");

Self::read_from_disk(k, &self.config.storage_dir)
Self::read_from_disk(&self.encryption_key, k, &self.config.storage_dir)
}

fn put(&mut self, record: Record) -> Result<()> {
Expand Down Expand Up @@ -732,8 +766,12 @@ mod tests {
// Confirm the pruned_key got removed, looping to allow async disk ops to complete.
let mut iteration = 0;
while iteration < max_iterations {
if NodeRecordStore::read_from_disk(&pruned_key, &store_config.storage_dir)
.is_none()
if NodeRecordStore::read_from_disk(
&store.encryption_key,
&pruned_key,
&store_config.storage_dir,
)
.is_none()
{
break;
}
Expand Down

0 comments on commit 94fc5f9

Please sign in to comment.