From 161a07733ce4923dae31310f29977c711ce0ff2e Mon Sep 17 00:00:00 2001 From: Jonas Schneider-Bensch Date: Tue, 5 Sep 2023 22:51:46 +0200 Subject: [PATCH 01/24] WIP WASM demo --- hacspec-scrambledb/Cargo.toml | 1 + .../oprf/src/coprf/coprf_setup.rs | 8 +- hacspec-scrambledb/oprf/src/lib.rs | 7 + hacspec-scrambledb/scrambledb/Cargo.toml | 22 +++ hacspec-scrambledb/scrambledb/src/error.rs | 1 + hacspec-scrambledb/scrambledb/src/finalize.rs | 2 +- hacspec-scrambledb/scrambledb/src/join.rs | 4 +- hacspec-scrambledb/scrambledb/src/lib.rs | 1 + hacspec-scrambledb/scrambledb/src/setup.rs | 16 +- hacspec-scrambledb/scrambledb/src/split.rs | 2 +- hacspec-scrambledb/scrambledb/src/table.rs | 73 +++++++++ .../scrambledb/wasm_demo/index.html | 152 ++++++++++++++++++ 12 files changed, 275 insertions(+), 14 deletions(-) create mode 100644 hacspec-scrambledb/scrambledb/wasm_demo/index.html diff --git a/hacspec-scrambledb/Cargo.toml b/hacspec-scrambledb/Cargo.toml index 53a9838..5b44106 100644 --- a/hacspec-scrambledb/Cargo.toml +++ b/hacspec-scrambledb/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "scrambledb", + "scrambledb-demo", "elgamal", "hash-to-curve", "p256", diff --git a/hacspec-scrambledb/oprf/src/coprf/coprf_setup.rs b/hacspec-scrambledb/oprf/src/coprf/coprf_setup.rs index 07f6f5a..e53eefe 100644 --- a/hacspec-scrambledb/oprf/src/coprf/coprf_setup.rs +++ b/hacspec-scrambledb/oprf/src/coprf/coprf_setup.rs @@ -26,7 +26,8 @@ pub type BlindingPrivateKey = elgamal::DecryptionKey; /// `Nsk` bytes of entropy is provided to the key derivation /// algorithm, where `Nsk` is the number of bytes to represent a valid /// private key, i.e. a P256 scalar in our case. -pub type CoPRFMasterSecret = [u8; 32]; +pub type CoPRFMasterSecret = Vec; +const COPRF_MSK_BYTES: usize = 32; /// A coPRF evaluation key is identified by a bytestring of arbitrary /// length. @@ -78,8 +79,9 @@ impl CoPRFEvaluatorContext { /// ### E.1.2. Evaluator Setup /// The coPRF evaluator holds the master secret as well as any PRF /// evaluation keys derived from it. - pub fn new(msk: CoPRFMasterSecret) -> Self { - CoPRFEvaluatorContext { msk } + pub fn new(randomness: &mut Randomness) -> Result { + let msk = randomness.bytes(COPRF_MSK_BYTES)?.to_vec(); + Ok(CoPRFEvaluatorContext { msk }) } } diff --git a/hacspec-scrambledb/oprf/src/lib.rs b/hacspec-scrambledb/oprf/src/lib.rs index bcf42ce..314c5a2 100644 --- a/hacspec-scrambledb/oprf/src/lib.rs +++ b/hacspec-scrambledb/oprf/src/lib.rs @@ -7,6 +7,7 @@ pub enum Error { CurveError, HashToCurveError, ElgamalError, + RandomnessError, } impl From for Error { @@ -27,6 +28,12 @@ impl From for Error { } } +impl From for Error { + fn from(_value: hacspec_lib::Error) -> Self { + Self::RandomnessError + } +} + // 3. Protocol pub mod protocol; diff --git a/hacspec-scrambledb/scrambledb/Cargo.toml b/hacspec-scrambledb/scrambledb/Cargo.toml index 285299c..882e964 100644 --- a/hacspec-scrambledb/scrambledb/Cargo.toml +++ b/hacspec-scrambledb/scrambledb/Cargo.toml @@ -5,9 +5,31 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +crate-type = ["rlib", "cdylib"] + [dependencies] oprf.workspace = true elgamal.workspace = true prp.workspace = true p256.workspace = true hacspec_lib.workspace = true +wasm-bindgen = { version = "0.2.87", optional = true } +rand = "0.8.5" +hash-to-curve.workspace = true +getrandom = { version = "0.2.10", features = ["js"] } +hex = "0.4.3" +[dependencies.web-sys] +version = "0.3.4" +features = [ + 'Document', + 'Element', + 'HtmlElement', + 'Node', + 'Window', + 'console', + 'HtmlTableElement', + 'HtmlTableRowElement', +] +[features] +wasm = ["wasm-bindgen"] diff --git a/hacspec-scrambledb/scrambledb/src/error.rs b/hacspec-scrambledb/scrambledb/src/error.rs index 8179482..60fda95 100644 --- a/hacspec-scrambledb/scrambledb/src/error.rs +++ b/hacspec-scrambledb/scrambledb/src/error.rs @@ -12,6 +12,7 @@ impl From for Error { oprf::Error::CurveError => Self::CorruptedData, oprf::Error::HashToCurveError => Self::CorruptedData, oprf::Error::ElgamalError => Self::RandomnessError, + oprf::Error::RandomnessError => Self::RandomnessError, } } } diff --git a/hacspec-scrambledb/scrambledb/src/finalize.rs b/hacspec-scrambledb/scrambledb/src/finalize.rs index 79d4f60..2f1cd8c 100644 --- a/hacspec-scrambledb/scrambledb/src/finalize.rs +++ b/hacspec-scrambledb/scrambledb/src/finalize.rs @@ -14,7 +14,7 @@ use crate::{ /// In addition the encrypted values need to be decrypted to be available /// for future conversions towards other data stores. pub fn finalize_conversion( - store_context: StoreContext, + store_context: &StoreContext, converted_tables: Vec, ) -> Result, Error> { let mut pseudonymized_tables = Vec::new(); diff --git a/hacspec-scrambledb/scrambledb/src/join.rs b/hacspec-scrambledb/scrambledb/src/join.rs index 578c8bd..6021047 100644 --- a/hacspec-scrambledb/scrambledb/src/join.rs +++ b/hacspec-scrambledb/scrambledb/src/join.rs @@ -60,7 +60,7 @@ use crate::{ /// return blind_tables /// ``` pub fn prepare_join_conversion( - origin_context: StoreContext, + origin_context: &StoreContext, bpk_target: BlindingPublicKey, ek_target: EncryptionKey, pseudonymized_tables: Vec, @@ -102,7 +102,7 @@ pub fn join_identifier(identifier: String, attribute: String) -> String { /// evaluation key. /// pub fn join_conversion( - converter_context: ConverterContext, + converter_context: &ConverterContext, bpk_target: BlindingPublicKey, ek_target: EncryptionKey, tables: Vec, diff --git a/hacspec-scrambledb/scrambledb/src/lib.rs b/hacspec-scrambledb/scrambledb/src/lib.rs index 6c581fd..034d18e 100644 --- a/hacspec-scrambledb/scrambledb/src/lib.rs +++ b/hacspec-scrambledb/scrambledb/src/lib.rs @@ -207,3 +207,4 @@ pub mod join; pub mod finalize; pub mod error; +pub mod wasm_demo; \ No newline at end of file diff --git a/hacspec-scrambledb/scrambledb/src/setup.rs b/hacspec-scrambledb/scrambledb/src/setup.rs index eb000b2..4ad02e9 100644 --- a/hacspec-scrambledb/scrambledb/src/setup.rs +++ b/hacspec-scrambledb/scrambledb/src/setup.rs @@ -41,10 +41,10 @@ impl ConverterContext { /// fn setup_converter_context(msk) -> ConverterContext: /// return coPRFEvaluatorContext::new(msk) /// ``` - pub fn setup(msk: [u8; 32]) -> Self { - ConverterContext { - coprf_context: CoPRFEvaluatorContext::new(msk), - } + pub fn setup(randomness: &mut Randomness) -> Result { + Ok(ConverterContext { + coprf_context: CoPRFEvaluatorContext::new(randomness)?, + }) } } @@ -76,10 +76,10 @@ impl StoreContext { /// k_prp /// } /// ``` - pub fn setup(mut randomness: Randomness) -> Result { - let receiver_context = CoPRFReceiverContext::new(&mut randomness); + pub fn setup(randomness: &mut Randomness) -> Result { + let receiver_context = CoPRFReceiverContext::new(randomness); - let (dk, ek) = generate_keys(&mut randomness)?; + let (dk, ek) = generate_keys(randomness)?; let k_prp = randomness.bytes(32)?.try_into()?; @@ -171,3 +171,5 @@ impl StoreContext { elgamal::decrypt(self.dk, encrypted_value).map_err(|e| e.into()) } } + + diff --git a/hacspec-scrambledb/scrambledb/src/split.rs b/hacspec-scrambledb/scrambledb/src/split.rs index b3e7620..a8b4246 100644 --- a/hacspec-scrambledb/scrambledb/src/split.rs +++ b/hacspec-scrambledb/scrambledb/src/split.rs @@ -74,7 +74,7 @@ pub fn prepare_split_conversion( /// have been shuffled to prevent correlation of the incoming with the /// outgoing table data. pub fn split_conversion( - converter_context: ConverterContext, + converter_context: &ConverterContext, bpk_store: BlindingPublicKey, ek_store: EncryptionKey, table: BlindTable, diff --git a/hacspec-scrambledb/scrambledb/src/table.rs b/hacspec-scrambledb/scrambledb/src/table.rs index 3ab370f..773aa01 100644 --- a/hacspec-scrambledb/scrambledb/src/table.rs +++ b/hacspec-scrambledb/scrambledb/src/table.rs @@ -26,15 +26,51 @@ pub struct Column { data: Vec<(K, V)>, } +// impl PlainTable { +// pub fn rows(&self) -> Vec<(PlainIdentifier, Vec)> { +// let keys = self.columns[0].keys(); +// let mut out = Vec::new(); +// for key in keys { +// let mut key_values = Vec::new(); +// for column in self.columns() { +// key_values.push(column.get(&key).unwrap()); +// } +// out.push((key, key_values)); +// } +// out +// } +// } impl Column { pub fn new(attribute: String, data: Vec<(K, V)>) -> Self { Self { attribute, data } } + pub fn len(&self) -> usize { + self.data.len() + } + pub fn attribute(&self) -> String { self.attribute.clone() } + pub fn keys(&self) -> Vec + where + K: Clone, + { + self.data.iter().map(|(k, _v)| k.clone()).collect() + } + + pub fn get(&self, key: &K) -> Option + where + K: PartialEq, + V: Clone, + { + self.data + .iter() + .find(|(k, _v)| k == key) + .map(|(_k, v)| v.clone()) + } + pub fn data(&self) -> Vec<(K, V)> where K: Clone, @@ -77,6 +113,14 @@ impl SingleColumnTable { { self.column.clone() } + + pub fn len(&self) -> usize + where + K: Clone, + V: Clone, + { + self.column.data().len() + } } impl MultiColumnTable { @@ -90,6 +134,18 @@ impl MultiColumnTable { self.identifier.clone() } + pub fn num_columns(&self) -> usize { + self.columns.len() + } + + pub fn num_rows(&self) -> usize + where + K: Clone, + V: Clone, + { + self.columns()[0].len() + } + pub fn columns(&self) -> Vec> where K: Clone, @@ -97,4 +153,21 @@ impl MultiColumnTable { { self.columns.clone() } + + pub fn rows(&self) -> Vec<(K, Vec)> + where + K: Clone + PartialEq, + V: Clone, + { + let keys = self.columns[0].keys(); + let mut out = Vec::new(); + for key in keys { + let mut key_values = Vec::new(); + for column in self.columns() { + key_values.push(column.get(&key).unwrap()); + } + out.push((key, key_values)); + } + out + } } diff --git a/hacspec-scrambledb/scrambledb/wasm_demo/index.html b/hacspec-scrambledb/scrambledb/wasm_demo/index.html new file mode 100644 index 0000000..c06dc68 --- /dev/null +++ b/hacspec-scrambledb/scrambledb/wasm_demo/index.html @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + +
+

Data Source

+
+ + + + + + + + + + + + + + + + + +
ID Address Date of Birth Eyecolor
1 Entenhausen 1.4.2918 Green
+ +
+
+ +
+
+ + +
+
+
+
+ +

Data Lake

+
+
+
+
+
+ +
+
+
+
+ +

Data Processor

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 53aa92218b084bfc27a879beb0e46db3b5860fbf Mon Sep 17 00:00:00 2001 From: Jonas Schneider-Bensch Date: Tue, 5 Sep 2023 23:06:18 +0200 Subject: [PATCH 02/24] Nonsense workspace member snuck in --- hacspec-scrambledb/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/hacspec-scrambledb/Cargo.toml b/hacspec-scrambledb/Cargo.toml index 5b44106..53a9838 100644 --- a/hacspec-scrambledb/Cargo.toml +++ b/hacspec-scrambledb/Cargo.toml @@ -1,7 +1,6 @@ [workspace] members = [ "scrambledb", - "scrambledb-demo", "elgamal", "hash-to-curve", "p256", From 501582ad59414ea61a6afe4f059bbbf6907d6689 Mon Sep 17 00:00:00 2001 From: Jonas Schneider-Bensch Date: Tue, 5 Sep 2023 23:08:05 +0200 Subject: [PATCH 03/24] Commit actual demo code --- .../scrambledb/src/wasm_demo.rs | 272 ++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 hacspec-scrambledb/scrambledb/src/wasm_demo.rs diff --git a/hacspec-scrambledb/scrambledb/src/wasm_demo.rs b/hacspec-scrambledb/scrambledb/src/wasm_demo.rs new file mode 100644 index 0000000..8212eda --- /dev/null +++ b/hacspec-scrambledb/scrambledb/src/wasm_demo.rs @@ -0,0 +1,272 @@ +use hacspec_lib::Randomness; +use p256::{NatMod, P256FieldElement}; +use wasm_bindgen::prelude::*; + +use wasm_bindgen::JsCast; +use web_sys::{Document, Element, HtmlTableElement}; + +use crate::table::MultiColumnTable; +use crate::table::SingleColumnTable; +use crate::{ + setup::{ConverterContext, StoreContext}, + table::{BlindTable, Column, ConvertedTable, PlainTable, PseudonymizedTable}, +}; + +#[cfg(feature = "wasm")] +#[wasm_bindgen(start)] +fn run() -> Result<(), JsValue> { + use rand::prelude::*; + use web_sys::console; + let mut rng = rand::thread_rng(); + let mut randomness = [0u8; 1000000]; + rng.fill_bytes(&mut randomness); + let mut randomness = Randomness::new(randomness.to_vec()); + + let converter_context = ConverterContext::setup(&mut randomness).unwrap(); + let lake_context = StoreContext::setup(&mut randomness).unwrap(); + + // == Generate Plain Table == + let plain_table = generate_plain_table(); + + let window = web_sys::window().expect("no global `window` exists"); + let document = window.document().expect("should have a document on window"); + + let plain_table_element = + prep_multicol_table_html_to_dom_id(&"data-source-table-plain", &plain_table, &document); + fill_plain_table_element(&plain_table_element, &plain_table); + + let (lake_ek, lake_bpk) = lake_context.public_keys(); + + // == Blind Table for Pseudonymization == + let blind_table = + crate::split::prepare_split_conversion(lake_ek, lake_bpk, plain_table, &mut randomness) + .unwrap(); + + let blind_table_element = + prep_multicol_table_html_to_dom_id(&"data-source-table-blind", &blind_table, &document); + //fill_blind_table_element(&blind_table_element, &blind_table); + + // // == Blind Pseudonymized Table == + let converted_tables = crate::split::split_conversion( + &converter_context, + lake_bpk, + lake_ek, + blind_table, + &mut randomness, + ) + .unwrap(); + + // == Unblinded Pseudonymized Table == + let lake_tables = + crate::finalize::finalize_conversion(&lake_context, converted_tables).unwrap(); + + for lake_table in lake_tables.iter() { + let lake_table_element = + add_column_html_to_dom_id(&"data-lake-tables", &lake_table, &document); + //fill_pseudonymized_table_element(&lake_table_element, lake_table); + } + + let processor_context = StoreContext::setup(&mut randomness).unwrap(); + + let (bpk_processor, ek_processor) = processor_context.public_keys(); + let blind_tables = crate::join::prepare_join_conversion( + &lake_context, + bpk_processor, + ek_processor, + lake_tables, + &mut randomness, + ) + .unwrap(); + + let converted_join_tables = crate::join::join_conversion( + &converter_context, + bpk_processor, + ek_processor, + blind_tables, + &mut randomness, + ) + .unwrap(); + + let joined_tables = + crate::finalize::finalize_conversion(&lake_context, converted_join_tables).unwrap(); + + for lake_table in joined_tables.iter() { + let lake_table_element = + add_column_html_to_dom_id(&"data-processor-joined", &lake_table, &document); + //fill_pseudonymized_table_element(&lake_table_element, lake_table); + } + console::log_1(&"Okay...".into()); + + Ok(()) +} + +fn fill_pseudonymized_table_element(table_element: &HtmlTableElement, table: &PseudonymizedTable) { + todo!() +} + +fn add_column_html_to_dom_id( + element_id: &str, + table: &PseudonymizedTable, + document: &Document, +) -> HtmlTableElement { + // Create table and add it to given dom element (should be a
) + + let table_div = document.get_element_by_id(element_id).unwrap(); + + let table_element: HtmlTableElement = document + .create_element("table") + .unwrap() + .dyn_into::() + .unwrap(); + + let t_head = table_element.create_t_head(); + + let header_row = document + .create_element("tr") + .unwrap() + .dyn_into::() + .unwrap(); + + // insert a dummy cell in the header for correct alignment of attribute header + let _dummy_cell = header_row.insert_cell().unwrap(); + let header_cell = header_row.insert_cell().unwrap(); + header_cell.set_text_content(Some(&table.column().attribute())); + + t_head.append_child(&header_row).unwrap(); + + let t_caption = table_element.create_caption(); + + t_caption.set_text_content(Some(&table.identifier())); + + table_div.append_child(&table_element).unwrap(); + table_element +} + +fn prep_multicol_table_html_to_dom_id( + element_id: &str, + table: &MultiColumnTable, + document: &Document, +) -> HtmlTableElement +where + K: Clone, + V: Clone, +{ + let table_element: HtmlTableElement = document + .get_element_by_id(element_id) + .expect("Document should have plain table element.") + .dyn_into::() + .unwrap(); + + let t_head = table_element.create_t_head(); + + let header_row = document + .create_element("tr") + .unwrap() + .dyn_into::() + .unwrap(); + + // insert a dummy cell in the header for correct alignment of attribute headers + let _dummy_cell = header_row.insert_cell().unwrap(); + + for colum in table.columns() { + let header_cell = header_row.insert_cell().unwrap(); + header_cell.set_text_content(Some(&colum.attribute())); + } + + t_head.append_child(&header_row).unwrap(); + + let t_caption = table_element.create_caption(); + + t_caption.set_text_content(Some(&table.identifier())); + + table_element +} + +fn fill_plain_table_element(table_element: &HtmlTableElement, plain_table: &PlainTable) { + for row in plain_table.rows() { + let html_row = table_element + .insert_row() + .unwrap() + .dyn_into::() + .unwrap(); + let key_cell = html_row.insert_cell().unwrap(); + key_cell.set_text_content(Some(&row.0)); + for value in row.1 { + let val_cell = html_row.insert_cell().unwrap(); + val_cell.set_text_content(Some(&hex::encode(value.raw_bytes()))) + } + } +} + +fn fill_blind_table_element(table_element: &HtmlTableElement, plain_table: &BlindTable) { + for row in plain_table.rows() { + let html_row = table_element + .insert_row() + .unwrap() + .dyn_into::() + .unwrap(); + let key_cell = html_row.insert_cell().unwrap(); + let key_string = format!( + "G({}), G({})", + hex::encode(row.0 .0.raw_bytes()), + hex::encode(row.0 .1.raw_bytes()) + ); + key_cell.set_text_content(Some(&key_string)); + for value in row.1 { + let val_cell = html_row.insert_cell().unwrap(); + let val_string = format!( + "G({}), G({})", + hex::encode(value.0.raw_bytes()), + hex::encode(value.1.raw_bytes()) + ); + val_cell.set_text_content(Some(&val_string)) + } + } +} + +fn generate_plain_table() -> PlainTable { + let mut columns = Vec::new(); + columns.push(Column::new( + String::from("SampleAttribute1"), + vec![ + ( + String::from("A"), + hash_to_curve::p256_hash::hash_to_curve(b"TestData1", b"sample_dst").unwrap(), + ), + ( + String::from("B"), + hash_to_curve::p256_hash::hash_to_curve(b"TestData2", b"sample_dst").unwrap(), + ), + ], + )); + + columns.push(Column::new( + String::from("SampleAttribute2"), + vec![ + ( + String::from("A"), + hash_to_curve::p256_hash::hash_to_curve(b"TestData3", b"sample_dst").unwrap(), + ), + ( + String::from("B"), + hash_to_curve::p256_hash::hash_to_curve(b"TestData4", b"sample_dst").unwrap(), + ), + ], + )); + + columns.push(Column::new( + String::from("SampleAttribute3"), + vec![ + ( + String::from("A"), + hash_to_curve::p256_hash::hash_to_curve(b"TestData5", b"sample_dst").unwrap(), + ), + ( + String::from("B"), + hash_to_curve::p256_hash::hash_to_curve(b"TestData6", b"sample_dst").unwrap(), + ), + ], + )); + + PlainTable::new(String::from("SampleTable"), columns) +} From 79775e689aba97f220408c25c5d3bd00c2073422 Mon Sep 17 00:00:00 2001 From: Jonas Schneider-Bensch Date: Tue, 5 Sep 2023 23:19:29 +0200 Subject: [PATCH 04/24] Update `index.html` --- .../scrambledb/wasm_demo/index.html | 141 ++++-------------- 1 file changed, 26 insertions(+), 115 deletions(-) diff --git a/hacspec-scrambledb/scrambledb/wasm_demo/index.html b/hacspec-scrambledb/scrambledb/wasm_demo/index.html index c06dc68..784d5ae 100644 --- a/hacspec-scrambledb/scrambledb/wasm_demo/index.html +++ b/hacspec-scrambledb/scrambledb/wasm_demo/index.html @@ -15,7 +15,7 @@ @@ -23,130 +23,41 @@

Data Source

- - - - - - - - - - - - - - - - - +

Plain Table

+
ID Address Date of Birth Eyecolor
1 Entenhausen 1.4.2918 Green
-
-
- -
-
- - +
+

Blinded Table

+ +
-
-
-
- -

Data Lake

-
-
+
+
+

Data Lake

+
+

Pseudonymized Tables

+
+ +
-
- -
-
-
-
- -

Data Processor

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file +
+

Data Lake

+
+

Joined Table

+
+
+
+ + + \ No newline at end of file From b783163caec9d376455cd0d0d6e2f0e8ff65992f Mon Sep 17 00:00:00 2001 From: Jonas Schneider-Bensch Date: Wed, 6 Sep 2023 11:37:03 +0200 Subject: [PATCH 05/24] Demo WIP Fixes --- hacspec-scrambledb/scrambledb/Cargo.toml | 12 +- hacspec-scrambledb/scrambledb/src/table.rs | 30 ++--- .../scrambledb/src/wasm_demo.rs | 123 ++++++++++++------ .../scrambledb/wasm_demo/index.html | 20 +++ 4 files changed, 117 insertions(+), 68 deletions(-) diff --git a/hacspec-scrambledb/scrambledb/Cargo.toml b/hacspec-scrambledb/scrambledb/Cargo.toml index 882e964..e4f04bf 100644 --- a/hacspec-scrambledb/scrambledb/Cargo.toml +++ b/hacspec-scrambledb/scrambledb/Cargo.toml @@ -14,13 +14,15 @@ elgamal.workspace = true prp.workspace = true p256.workspace = true hacspec_lib.workspace = true -wasm-bindgen = { version = "0.2.87", optional = true } -rand = "0.8.5" hash-to-curve.workspace = true -getrandom = { version = "0.2.10", features = ["js"] } -hex = "0.4.3" + +wasm-bindgen = { version = "0.2.87", optional = true } +rand = { version = "0.8.5", optional = true } +getrandom = { version = "0.2.10", features = ["js"], optional = true } +hex = { version = "0.4.3", optional = true } [dependencies.web-sys] version = "0.3.4" +optional = true features = [ 'Document', 'Element', @@ -32,4 +34,4 @@ features = [ 'HtmlTableRowElement', ] [features] -wasm = ["wasm-bindgen"] +wasm = ["wasm-bindgen", "getrandom", "web-sys", "rand", "hex"] diff --git a/hacspec-scrambledb/scrambledb/src/table.rs b/hacspec-scrambledb/scrambledb/src/table.rs index 773aa01..4bda64e 100644 --- a/hacspec-scrambledb/scrambledb/src/table.rs +++ b/hacspec-scrambledb/scrambledb/src/table.rs @@ -26,20 +26,6 @@ pub struct Column { data: Vec<(K, V)>, } -// impl PlainTable { -// pub fn rows(&self) -> Vec<(PlainIdentifier, Vec)> { -// let keys = self.columns[0].keys(); -// let mut out = Vec::new(); -// for key in keys { -// let mut key_values = Vec::new(); -// for column in self.columns() { -// key_values.push(column.get(&key).unwrap()); -// } -// out.push((key, key_values)); -// } -// out -// } -// } impl Column { pub fn new(attribute: String, data: Vec<(K, V)>) -> Self { Self { attribute, data } @@ -154,20 +140,20 @@ impl MultiColumnTable { self.columns.clone() } - pub fn rows(&self) -> Vec<(K, Vec)> + pub fn rows(&self) -> Vec> where K: Clone + PartialEq, V: Clone, { - let keys = self.columns[0].keys(); - let mut out = Vec::new(); - for key in keys { - let mut key_values = Vec::new(); + let mut rows = Vec::new(); + for i in 0..self.num_rows() { + let mut row = Vec::new(); for column in self.columns() { - key_values.push(column.get(&key).unwrap()); + row.push(column.data()[i].clone()); } - out.push((key, key_values)); + rows.push(row) } - out + + rows } } diff --git a/hacspec-scrambledb/scrambledb/src/wasm_demo.rs b/hacspec-scrambledb/scrambledb/src/wasm_demo.rs index 8212eda..14693f4 100644 --- a/hacspec-scrambledb/scrambledb/src/wasm_demo.rs +++ b/hacspec-scrambledb/scrambledb/src/wasm_demo.rs @@ -1,18 +1,22 @@ +#![cfg(feature = "wasm")] + use hacspec_lib::Randomness; -use p256::{NatMod, P256FieldElement}; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; -use web_sys::{Document, Element, HtmlTableElement}; +use web_sys::{Document, HtmlTableElement}; +use crate::table::BlindIdentifier; +use crate::table::BlindPseudonym; +use crate::table::EncryptedValue; use crate::table::MultiColumnTable; -use crate::table::SingleColumnTable; +use crate::table::PlainValue; +use crate::table::Pseudonym; use crate::{ setup::{ConverterContext, StoreContext}, - table::{BlindTable, Column, ConvertedTable, PlainTable, PseudonymizedTable}, + table::{BlindTable, Column, PlainTable, PseudonymizedTable}, }; -#[cfg(feature = "wasm")] #[wasm_bindgen(start)] fn run() -> Result<(), JsValue> { use rand::prelude::*; @@ -44,7 +48,7 @@ fn run() -> Result<(), JsValue> { let blind_table_element = prep_multicol_table_html_to_dom_id(&"data-source-table-blind", &blind_table, &document); - //fill_blind_table_element(&blind_table_element, &blind_table); + fill_blind_table_element(&blind_table_element, &blind_table); // // == Blind Pseudonymized Table == let converted_tables = crate::split::split_conversion( @@ -63,7 +67,7 @@ fn run() -> Result<(), JsValue> { for lake_table in lake_tables.iter() { let lake_table_element = add_column_html_to_dom_id(&"data-lake-tables", &lake_table, &document); - //fill_pseudonymized_table_element(&lake_table_element, lake_table); + fill_pseudonymized_table_element(&lake_table_element, lake_table); } let processor_context = StoreContext::setup(&mut randomness).unwrap(); @@ -93,17 +97,13 @@ fn run() -> Result<(), JsValue> { for lake_table in joined_tables.iter() { let lake_table_element = add_column_html_to_dom_id(&"data-processor-joined", &lake_table, &document); - //fill_pseudonymized_table_element(&lake_table_element, lake_table); + fill_pseudonymized_table_element(&lake_table_element, lake_table); } console::log_1(&"Okay...".into()); Ok(()) } -fn fill_pseudonymized_table_element(table_element: &HtmlTableElement, table: &PseudonymizedTable) { - todo!() -} - fn add_column_html_to_dom_id( element_id: &str, table: &PseudonymizedTable, @@ -126,6 +126,7 @@ fn add_column_html_to_dom_id( .unwrap() .dyn_into::() .unwrap(); + header_row.set_attribute("class", "tableheader").unwrap(); // insert a dummy cell in the header for correct alignment of attribute header let _dummy_cell = header_row.insert_cell().unwrap(); @@ -164,13 +165,14 @@ where .unwrap() .dyn_into::() .unwrap(); - + header_row.set_attribute("class", "tableheader").unwrap(); // insert a dummy cell in the header for correct alignment of attribute headers let _dummy_cell = header_row.insert_cell().unwrap(); for colum in table.columns() { let header_cell = header_row.insert_cell().unwrap(); header_cell.set_text_content(Some(&colum.attribute())); + let _dummy_cell = header_row.insert_cell().unwrap(); } t_head.append_child(&header_row).unwrap(); @@ -189,11 +191,15 @@ fn fill_plain_table_element(table_element: &HtmlTableElement, plain_table: &Plai .unwrap() .dyn_into::() .unwrap(); - let key_cell = html_row.insert_cell().unwrap(); - key_cell.set_text_content(Some(&row.0)); - for value in row.1 { + for (key, value) in row { + let key_cell = html_row.insert_cell().unwrap(); + key_cell.set_attribute("class", "kc").unwrap(); + key_cell.set_text_content(Some(&key)); + let val_cell = html_row.insert_cell().unwrap(); - val_cell.set_text_content(Some(&hex::encode(value.raw_bytes()))) + val_cell.set_attribute("class", "vc").unwrap(); + val_cell.set_text_content(Some(&hex::encode(value.raw_bytes()))); + val_cell.set_text_content(Some(&plain_value_to_string(value))); } } } @@ -205,68 +211,103 @@ fn fill_blind_table_element(table_element: &HtmlTableElement, plain_table: &Blin .unwrap() .dyn_into::() .unwrap(); - let key_cell = html_row.insert_cell().unwrap(); - let key_string = format!( - "G({}), G({})", - hex::encode(row.0 .0.raw_bytes()), - hex::encode(row.0 .1.raw_bytes()) - ); - key_cell.set_text_content(Some(&key_string)); - for value in row.1 { + + for (key, value) in row { + let key_cell = html_row.insert_cell().unwrap(); + key_cell.set_text_content(Some(&blind_id_to_string(key))); + let val_cell = html_row.insert_cell().unwrap(); - let val_string = format!( - "G({}), G({})", - hex::encode(value.0.raw_bytes()), - hex::encode(value.1.raw_bytes()) - ); - val_cell.set_text_content(Some(&val_string)) + val_cell.set_text_content(Some(&encryption_to_string(value))); } } } +fn fill_pseudonymized_table_element(table_element: &HtmlTableElement, table: &PseudonymizedTable) { + for (key, value) in table.column().data() { + let html_row = table_element + .insert_row() + .unwrap() + .dyn_into::() + .unwrap(); + + let key_cell = html_row.insert_cell().unwrap(); + key_cell.set_text_content(Some(&nym_to_string(key))); + + let val_cell = html_row.insert_cell().unwrap(); + val_cell.set_text_content(Some(&plain_value_to_string(value))); + } +} + fn generate_plain_table() -> PlainTable { let mut columns = Vec::new(); columns.push(Column::new( - String::from("SampleAttribute1"), + String::from("Address"), vec![ ( - String::from("A"), + String::from("Alice"), hash_to_curve::p256_hash::hash_to_curve(b"TestData1", b"sample_dst").unwrap(), ), ( - String::from("B"), + String::from("Bob"), hash_to_curve::p256_hash::hash_to_curve(b"TestData2", b"sample_dst").unwrap(), ), ], )); columns.push(Column::new( - String::from("SampleAttribute2"), + String::from("Date of Birth"), vec![ ( - String::from("A"), + String::from("Alice"), hash_to_curve::p256_hash::hash_to_curve(b"TestData3", b"sample_dst").unwrap(), ), ( - String::from("B"), + String::from("Bob"), hash_to_curve::p256_hash::hash_to_curve(b"TestData4", b"sample_dst").unwrap(), ), ], )); columns.push(Column::new( - String::from("SampleAttribute3"), + String::from("Favorite Color"), vec![ ( - String::from("A"), + String::from("Alice"), hash_to_curve::p256_hash::hash_to_curve(b"TestData5", b"sample_dst").unwrap(), ), ( - String::from("B"), + String::from("Bob"), hash_to_curve::p256_hash::hash_to_curve(b"TestData6", b"sample_dst").unwrap(), ), ], )); - PlainTable::new(String::from("SampleTable"), columns) + PlainTable::new(String::from("ExampleTable"), columns) +} + +fn plain_value_to_string(plain_value: PlainValue) -> String { + String::from(format!( + "FELEM({}...)", + &hex::encode(plain_value.raw_bytes())[0..5] + )) +} + +fn encryption_to_string(encrypted_value: EncryptedValue) -> String { + String::from(format!( + "CTXT({}..., {}...)", + &hex::encode(encrypted_value.0.raw_bytes())[0..5], + &hex::encode(encrypted_value.1.raw_bytes())[0..5], + )) +} + +fn blind_id_to_string(blind_id: BlindIdentifier) -> String { + encryption_to_string(blind_id) +} + +fn blind_pseudonym_to_string(blind_nym: BlindPseudonym) -> String { + encryption_to_string(blind_nym) +} + +fn nym_to_string(nym: Pseudonym) -> String { + String::from(format!("BYTES({}...)", &hex::encode(nym)[0..5])) } diff --git a/hacspec-scrambledb/scrambledb/wasm_demo/index.html b/hacspec-scrambledb/scrambledb/wasm_demo/index.html index 784d5ae..b0ddd47 100644 --- a/hacspec-scrambledb/scrambledb/wasm_demo/index.html +++ b/hacspec-scrambledb/scrambledb/wasm_demo/index.html @@ -12,6 +12,26 @@ flex: 50%; padding: 20px; } + + tr.tableheader { + font-weight: bold; + } + + td.kc { + background-color: rgb(228, 116, 140); + } + + td.vc { + background-color: rgb(147, 235, 125); + } + + td.nym { + background-color: rgb(124, 151, 202); + } + + td.enc { + background-color: rgb(124, 151, 202); + }

Data Source

-
-

Plain Table

- -
+ + -
-

Blinded Table

- -
+
let blinded_table = prepare_split_conversion(
+        ek_lake,
+        bpk_lake,
+        source_table,
+        randomness
+    );
+
+ +
+
+ +
+
+

Converter (Pseudonymization)

+
+

Input: Blinded Table

+
+
+
+
+    let converted_split_tables = split_conversion(
+        converter_context,
+        bpk_lake,
+        ek_lake,
+        blinded_table,
+        randomness,
+    );
+
+

Output: Obliviously Converted & Split Tables

+
+
+
+
+

Data Lake

+
let pseudonymized_tables =
+        finalize_conversion(lake_context, converted_split_tables);

Pseudonymized Tables

+
+
+
let join_table_selection = ["Address", "Date of Birth"];
+    let blinded_tables = prepare_join_conversion(
+        lake_context,
+        bpk_processor,
+        ek_processor,
+        join_table_selection,
+        randomness,
+    );
+
+ +
+
+

Converter (Join)

+
+

Input: Blinded Tables

+
+
+
+
+    let converted_tables = join_conversion(
+        converter_context,
+        bpk_processor,
+        ek_processor,
+        blind_pre_join_tables,
+        randomness,
+    );
+
+

Output: Converted Tables

+
+ +
+

Data Lake

+
+    let joined_tables = finalize_conversion(processor_context, converted_tables);

Joined Table

+
- + \ No newline at end of file From 9a49e9dcf4a4286eaea1e4ef0e8063947b293e81 Mon Sep 17 00:00:00 2001 From: jschneider-bensch <124457079+jschneider-bensch@users.noreply.github.com> Date: Tue, 26 Sep 2023 16:41:31 +0200 Subject: [PATCH 18/24] Update hacspec-scrambledb/scrambledb/src/finalize.rs Co-authored-by: Franziskus Kiefer --- hacspec-scrambledb/scrambledb/src/finalize.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/hacspec-scrambledb/scrambledb/src/finalize.rs b/hacspec-scrambledb/scrambledb/src/finalize.rs index 9218f88..6a5093f 100644 --- a/hacspec-scrambledb/scrambledb/src/finalize.rs +++ b/hacspec-scrambledb/scrambledb/src/finalize.rs @@ -24,7 +24,6 @@ pub fn finalize_conversion( for (blinded_pseudonym, encrypted_value) in blinded_table.column().data() { let pseudonym = store_context.finalize_pseudonym(blinded_pseudonym)?; - //let value = encrypted_value.0; let value = store_context.decrypt_value(encrypted_value)?; pseudonymized_column_data.push((pseudonym, value)); From 0d4608b0c66b8ec1ca026b26ec1bc055166f1508 Mon Sep 17 00:00:00 2001 From: jschneider-bensch <124457079+jschneider-bensch@users.noreply.github.com> Date: Tue, 26 Sep 2023 16:42:05 +0200 Subject: [PATCH 19/24] Update hacspec-scrambledb/scrambledb/src/split.rs Co-authored-by: Franziskus Kiefer --- hacspec-scrambledb/scrambledb/src/split.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hacspec-scrambledb/scrambledb/src/split.rs b/hacspec-scrambledb/scrambledb/src/split.rs index 92c9e37..e662ba4 100644 --- a/hacspec-scrambledb/scrambledb/src/split.rs +++ b/hacspec-scrambledb/scrambledb/src/split.rs @@ -10,7 +10,7 @@ use crate::{ error::Error, setup::ConverterContext, table::{BlindTable, Column, ConvertedTable, EncryptedValue, PlainTable}, -}; + table::{BlindTable, Column, ConvertedTable, PlainTable}, pub fn split_identifier(identifier: String, attribute: String) -> String { let mut split_identifier = identifier.clone(); From 366af6009cb1126dd9c80408a5fd87125b984369 Mon Sep 17 00:00:00 2001 From: jschneider-bensch <124457079+jschneider-bensch@users.noreply.github.com> Date: Tue, 26 Sep 2023 16:42:21 +0200 Subject: [PATCH 20/24] Update hacspec-scrambledb/scrambledb/src/split.rs Co-authored-by: Franziskus Kiefer --- hacspec-scrambledb/scrambledb/src/split.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/hacspec-scrambledb/scrambledb/src/split.rs b/hacspec-scrambledb/scrambledb/src/split.rs index e662ba4..2048e32 100644 --- a/hacspec-scrambledb/scrambledb/src/split.rs +++ b/hacspec-scrambledb/scrambledb/src/split.rs @@ -51,7 +51,6 @@ pub fn prepare_split_conversion( )?; let encrypted_value = encrypt(ek_receiver, plaintext_value, randomness)?; - //let encrypted_value = (plaintext_value, plaintext_value); blinded_column_data.push((blinded_id, encrypted_value)); } From a0cd6f5670549bf514f6d1c25201c5de3dc3fc58 Mon Sep 17 00:00:00 2001 From: Jonas Schneider-Bensch Date: Tue, 26 Sep 2023 17:06:46 +0200 Subject: [PATCH 21/24] Fix syntax error --- hacspec-scrambledb/scrambledb/src/split.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hacspec-scrambledb/scrambledb/src/split.rs b/hacspec-scrambledb/scrambledb/src/split.rs index 2048e32..3736817 100644 --- a/hacspec-scrambledb/scrambledb/src/split.rs +++ b/hacspec-scrambledb/scrambledb/src/split.rs @@ -9,8 +9,8 @@ use oprf::coprf::{ use crate::{ error::Error, setup::ConverterContext, - table::{BlindTable, Column, ConvertedTable, EncryptedValue, PlainTable}, table::{BlindTable, Column, ConvertedTable, PlainTable}, +}; pub fn split_identifier(identifier: String, attribute: String) -> String { let mut split_identifier = identifier.clone(); From 7fb067f9713ff93f49b52294e63cc43ac953e0b3 Mon Sep 17 00:00:00 2001 From: Jonas Schneider-Bensch Date: Tue, 26 Sep 2023 17:07:02 +0200 Subject: [PATCH 22/24] Remove debug logs --- .../scrambledb/src/wasm_demo.rs | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/hacspec-scrambledb/scrambledb/src/wasm_demo.rs b/hacspec-scrambledb/scrambledb/src/wasm_demo.rs index 693cc96..3d3c82b 100644 --- a/hacspec-scrambledb/scrambledb/src/wasm_demo.rs +++ b/hacspec-scrambledb/scrambledb/src/wasm_demo.rs @@ -1,5 +1,3 @@ -#![cfg(feature = "wasm")] - use hacspec_lib::Randomness; use wasm_bindgen::prelude::*; @@ -30,9 +28,6 @@ pub fn demo_blind_table() {} pub fn init_table(table: JsValue) { let table: String = table.into_serde().unwrap(); let table: serde_json::Value = serde_json::from_str(&table).unwrap(); - web_sys::console::log_1(&format!("table: {table:x?}").into()); - let address = &table[0]["Address"].as_str().unwrap(); - web_sys::console::log_1(&format!("address 0: {:x?}", address).into()); run(table) } @@ -46,7 +41,7 @@ pub fn generate_plain_table( let mut column_values = vec![]; for i in 0..table.as_array().unwrap().len() { let row = &table[i]; - web_sys::console::log_1(&format!("row {i}: {:x?}", row).into()); + let encoded_value = hash_to_curve::p256_hash::hash_to_curve( row[column].as_str().unwrap().as_bytes(), b"sample_dst", @@ -86,8 +81,6 @@ pub fn run(table: serde_json::Value) { let processor_context = StoreContext::setup(&mut randomness).unwrap(); let (ek_processor, bpk_processor) = processor_context.public_keys(); - web_sys::console::log_1(&"Setup done.".into()); - // Split conversion let blind_source_table = crate::split::prepare_split_conversion( ek_lake, @@ -97,8 +90,6 @@ pub fn run(table: serde_json::Value) { ) .unwrap(); - web_sys::console::log_1(&"Split Blinding done.".into()); - let blind_split_tables = crate::split::split_conversion( &converter_context, bpk_lake, @@ -108,13 +99,9 @@ pub fn run(table: serde_json::Value) { ) .unwrap(); - web_sys::console::log_1(&"Split conversion done.".into()); - let finalized_split_tables = crate::finalize::finalize_conversion(&lake_context, blind_split_tables.clone()).unwrap(); - web_sys::console::log_1(&"Split finalization done.".into()); - // Join conversion let join_table_selection = vec![ finalized_split_tables[0].clone(), @@ -130,8 +117,6 @@ pub fn run(table: serde_json::Value) { ) .unwrap(); - web_sys::console::log_1(&"Join blinding done.".into()); - let blind_joined_tables = crate::join::join_conversion( &converter_context, bpk_processor, @@ -141,13 +126,10 @@ pub fn run(table: serde_json::Value) { ) .unwrap(); - web_sys::console::log_1(&"Join conversion done.".into()); - let joined_tables = crate::finalize::finalize_conversion(&processor_context, blind_joined_tables.clone()) .unwrap(); - web_sys::console::log_1(&"Join finalization done.".into()); // == Visualization == let window = web_sys::window().expect("no global `window` exists"); From 131831ea52e8fa834a21f90f7ffe6e973f4960f4 Mon Sep 17 00:00:00 2001 From: Jonas Schneider-Bensch Date: Tue, 26 Sep 2023 17:07:30 +0200 Subject: [PATCH 23/24] Move feature switch --- hacspec-scrambledb/scrambledb/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hacspec-scrambledb/scrambledb/src/lib.rs b/hacspec-scrambledb/scrambledb/src/lib.rs index ac4259f..debefe6 100644 --- a/hacspec-scrambledb/scrambledb/src/lib.rs +++ b/hacspec-scrambledb/scrambledb/src/lib.rs @@ -207,5 +207,7 @@ pub mod join; pub mod finalize; pub mod error; + +#[cfg(feature="wasm")] pub mod wasm_demo; mod test_util; From df13ded5faea0f3c1a9fc87f369da59e2d435ac1 Mon Sep 17 00:00:00 2001 From: Jonas Schneider-Bensch Date: Tue, 26 Sep 2023 17:08:10 +0200 Subject: [PATCH 24/24] Fmt --- hacspec-scrambledb/scrambledb/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hacspec-scrambledb/scrambledb/src/lib.rs b/hacspec-scrambledb/scrambledb/src/lib.rs index debefe6..48b5a6d 100644 --- a/hacspec-scrambledb/scrambledb/src/lib.rs +++ b/hacspec-scrambledb/scrambledb/src/lib.rs @@ -208,6 +208,6 @@ pub mod finalize; pub mod error; -#[cfg(feature="wasm")] +#[cfg(feature = "wasm")] pub mod wasm_demo; mod test_util;