From 78aad4a66c3ed0efcd209373b502a35c871df4f4 Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Mon, 25 Sep 2023 17:14:30 +0200 Subject: [PATCH 01/18] feat: update pyo3 covercrypt with new policy dimensions --- crates/cover_crypt/Cargo.toml | 4 ++-- crates/cover_crypt/src/pyo3/mod.rs | 5 ----- crates/cover_crypt/src/pyo3/py_abe_policy.rs | 15 +++++++-------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/crates/cover_crypt/Cargo.toml b/crates/cover_crypt/Cargo.toml index 553a434b..2b5bd0bd 100644 --- a/crates/cover_crypt/Cargo.toml +++ b/crates/cover_crypt/Cargo.toml @@ -18,7 +18,7 @@ python = ["pyo3"] wasm_bindgen = ["js-sys", "wasm-bindgen"] [dependencies] -cosmian_cover_crypt = { version = "12.0.3", features = ["serialization"] } +cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", branch = "develop", features = ["serialization"] } cosmian_crypto_core = { workspace = true } serde_json = "1.0.107" @@ -30,5 +30,5 @@ pyo3 = { workspace = true, features = ["extension-module"], optional = true } wasm-bindgen = { workspace = true, optional = true } [dev-dependencies] -cosmian_cover_crypt = { version = "12.0.3", features = ["test_utils"] } +cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", branch = "develop", features = ["test_utils"] } wasm-bindgen-test = "0.3.37" diff --git a/crates/cover_crypt/src/pyo3/mod.rs b/crates/cover_crypt/src/pyo3/mod.rs index 8035385f..91270f06 100644 --- a/crates/cover_crypt/src/pyo3/mod.rs +++ b/crates/cover_crypt/src/pyo3/mod.rs @@ -12,11 +12,6 @@ macro_rules! impl_key_byte { ($py_type:ty, $rust_type:ty) => { #[pymethods] impl $py_type { - /// Clones the key - pub fn deep_copy(&self) -> Self { - Self(self.0.clone()) - } - /// Converts key to bytes pub fn to_bytes(&self, py: Python) -> PyResult> { Ok(PyBytes::new( diff --git a/crates/cover_crypt/src/pyo3/py_abe_policy.rs b/crates/cover_crypt/src/pyo3/py_abe_policy.rs index 1342fc3a..01ec598d 100644 --- a/crates/cover_crypt/src/pyo3/py_abe_policy.rs +++ b/crates/cover_crypt/src/pyo3/py_abe_policy.rs @@ -1,7 +1,7 @@ use std::result::Result; use cosmian_cover_crypt::abe_policy::{ - Attribute as AttributeRust, EncryptionHint, Policy as PolicyRust, PolicyAxis as PolicyAxisRust, + Attribute as AttributeRust, DimensionBuilder, EncryptionHint, Policy as PolicyRust, }; use pyo3::{ exceptions::{PyException, PyTypeError, PyValueError}, @@ -30,7 +30,7 @@ impl Attribute { /// Returns: /// str pub fn get_axis(&self) -> &str { - &self.0.axis + &self.0.dimension } /// Gets the attribute name. @@ -75,7 +75,7 @@ impl Attribute { /// and encryption hint /// hierarchical (bool): set the axis to be hierarchical #[pyclass] -pub struct PolicyAxis(PolicyAxisRust); +pub struct PolicyAxis(DimensionBuilder); #[pymethods] impl PolicyAxis { @@ -101,7 +101,7 @@ impl PolicyAxis { }) .collect::>()?; - Ok(Self(PolicyAxisRust::new(name, attributes, hierarchical))) + Ok(Self(DimensionBuilder::new(name, attributes, hierarchical))) } /// Returns the number of attributes belonging to this axis. @@ -177,15 +177,14 @@ impl Policy { /// creations (revocation + addition) allowed. /// Default maximum of attribute creations is u32::MAX #[new] - #[pyo3(signature = (max_attribute_creations = 4_294_967_295))] - fn new(max_attribute_creations: u32) -> Self { - Self(PolicyRust::new(max_attribute_creations)) + fn new() -> Self { + Self(PolicyRust::new()) } /// Adds the given policy axis to the policy. pub fn add_axis(&mut self, axis: &PolicyAxis) -> PyResult<()> { self.0 - .add_axis(axis.0.clone()) + .add_dimension(axis.0.clone()) .map_err(|e| PyException::new_err(e.to_string())) } From 506fafb6277875a7eca0f41700f79d7d21016fb3 Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Mon, 25 Sep 2023 17:22:27 +0200 Subject: [PATCH 02/18] feat: update ffi and wasm to use new policy dimensions --- crates/cover_crypt/src/ffi/abe_policy.rs | 4 ++-- crates/cover_crypt/src/wasm_bindgen/abe_policy.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/cover_crypt/src/ffi/abe_policy.rs b/crates/cover_crypt/src/ffi/abe_policy.rs index 58c52fde..3d1ab4a7 100644 --- a/crates/cover_crypt/src/ffi/abe_policy.rs +++ b/crates/cover_crypt/src/ffi/abe_policy.rs @@ -8,7 +8,7 @@ pub unsafe extern "C" fn h_policy( policy_len: *mut i32, max_attribute_creations: i32, ) -> i32 { - let policy = Policy::new(max_attribute_creations as u32); + let policy = Policy::new(); let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error deserializing policy"); ffi_write_bytes!("policy", &policy_bytes, policy_ptr, policy_len); 0 @@ -34,7 +34,7 @@ pub unsafe extern "C" fn h_add_policy_axis( "error deserializing policy axis" ); - ffi_unwrap!(policy.add_axis(axis), "error adding policy axis"); + ffi_unwrap!(policy.add_dimension(axis), "error adding policy axis"); let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); ffi_write_bytes!( diff --git a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs index c6d09061..c06fdf7c 100644 --- a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs +++ b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs @@ -1,4 +1,4 @@ -use cosmian_cover_crypt::abe_policy::{Attribute, EncryptionHint, Policy, PolicyAxis}; +use cosmian_cover_crypt::abe_policy::{Attribute, DimensionBuilder, EncryptionHint, Policy}; use js_sys::{Array, Boolean, JsString, Reflect}; use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; @@ -40,7 +40,7 @@ pub fn webassembly_policy_axis( }) .collect::, _>>()?; - serde_json::to_string(&PolicyAxis::new( + serde_json::to_string(&DimensionBuilder::new( &name, attribute_properties .iter() @@ -53,7 +53,7 @@ pub fn webassembly_policy_axis( #[wasm_bindgen] pub fn webassembly_policy(nb_creations: u32) -> Result, JsValue> { - serde_json::to_vec(&Policy::new(nb_creations)).map_err(|e| JsValue::from_str(&e.to_string())) + serde_json::to_vec(&Policy::new()).map_err(|e| JsValue::from_str(&e.to_string())) } #[wasm_bindgen] @@ -63,7 +63,7 @@ pub fn webassembly_add_axis(policy: Vec, axis: String) -> Result, Js "Error deserializing the policy" ); wasm_unwrap!( - policy.add_axis(wasm_unwrap!( + policy.add_dimension(wasm_unwrap!( serde_json::from_str(&axis), "Error deserializing the policy axis" )), From 90154ab537b7da25e9c4f8d39a1a6757b52587cb Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Tue, 26 Sep 2023 11:09:15 +0200 Subject: [PATCH 03/18] feat: expose new policy functions in PyO3 --- .../cloudproof_cover_crypt/__init__.pyi | 67 ++++++++++++------- .../python/tests/cover_crypt_test.py | 22 ++---- crates/cover_crypt/src/ffi/abe_policy.rs | 2 +- crates/cover_crypt/src/pyo3/py_abe_policy.rs | 52 ++++++++++++++ .../src/wasm_bindgen/abe_policy.rs | 2 +- .../src/implementations/sqlite/findex.rs | 2 +- crates/findex/src/pyo3/py_callbacks.rs | 8 +-- 7 files changed, 109 insertions(+), 46 deletions(-) diff --git a/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi b/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi index c730d8b7..9f786fbf 100644 --- a/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi +++ b/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi @@ -98,20 +98,59 @@ class PolicyAxis: class Policy: """A policy is a set of policy axes. A fixed number of attribute creations (revocations + additions) is allowed. - - Args: - max_attribute_creations (int): number of attribute creations allowed. Defaults to 2**32 - 1 """ - def __init__(self, max_attribute_creations: int = 2**32 - 1): ... + def __init__(self): ... def add_axis(self, axis: PolicyAxis) -> None: """Adds the given policy axis to the policy. Args: axis (PolicyAxis) """ + def remove_axis(self, axis_name: str) -> None: + """Removes the given axis from the policy. + Fails if there is no such axis in the policy. + + Args: + axis_name (str) + """ + def add_attribute(self, attribute: Attribute, is_hybridized: bool) -> None: + """Adds the given attribute to the policy. + Fails if the axis of the attribute does not exist in the policy. + + Args: + attribute (Attribute): The name and axis of the new attribute. + is_hybridized (bool): Whether to use post quantum keys for this attribute + """ + def remove_attribute(self, attribute: Attribute) -> None: + """Removes the given attribute from the policy. + Encrypting and decrypting for this attribute will no longer be possible once the keys are updated. + + Args: + attribute (Attribute) + """ + def disable_attribute(self, attribute: Attribute) -> None: + """Marks an attribute as read only. + The corresponding attribute key will be removed from the public key. + But the decryption key will be kept to allow reading old ciphertext. + + Args: + attribute (Attribute) + """ + def rename_attribute(self, attribute: Attribute) -> None: + """Changes the name of an attribute. + + Args: + attribute (Attribute) + """ def rotate(self, attribute: Attribute) -> None: - """Rotates an attribute, changing its underlying value with an unused value. + """Rotates an attribute, changing its underlying value with a new value. + + Args: + attribute (Attribute) + """ + def clear_old_rotations(self, attribute: Attribute) -> None: + """Removes old rotations value of an attribute Args: attribute (Attribute) @@ -171,12 +210,6 @@ class MasterSecretKey: Returns: bytes """ - def deep_copy(self) -> MasterSecretKey: - """Clones the key. - - Returns: - MasterSecretKey - """ @staticmethod def from_bytes(key_bytes: bytes) -> MasterSecretKey: """Reads key from bytes. @@ -195,12 +228,6 @@ class MasterPublicKey: Returns: bytes """ - def deep_copy(self) -> MasterPublicKey: - """Clones the key. - - Returns: - MasterPublicKey - """ @staticmethod def from_bytes(key_bytes: bytes) -> MasterPublicKey: """Reads key from bytes. @@ -219,12 +246,6 @@ class UserSecretKey: Returns: bytes """ - def deep_copy(self) -> UserSecretKey: - """Clones the key. - - Returns: - UserSecretKey - """ @staticmethod def from_bytes(key_bytes: bytes) -> UserSecretKey: """Reads key from bytes. diff --git a/crates/cover_crypt/python/tests/cover_crypt_test.py b/crates/cover_crypt/python/tests/cover_crypt_test.py index e5116b98..79e1840d 100644 --- a/crates/cover_crypt/python/tests/cover_crypt_test.py +++ b/crates/cover_crypt/python/tests/cover_crypt_test.py @@ -4,10 +4,10 @@ from cloudproof_cover_crypt import ( Attribute, CoverCrypt, + MasterPublicKey, MasterSecretKey, Policy, PolicyAxis, - MasterPublicKey, SymmetricKey, UserSecretKey, ) @@ -15,7 +15,7 @@ class TestPolicy(unittest.TestCase): def policy(self) -> Policy: - policy = Policy(100) + policy = Policy() policy.add_axis( PolicyAxis( 'Country', @@ -52,7 +52,7 @@ def test_policy_axis(self) -> None: ) self.assertEqual( country_axis.to_string(), - 'Country: [AxisAttributeProperties { name: "France", encryption_hint: Classic }, AxisAttributeProperties { name: "UK", encryption_hint: Classic }, AxisAttributeProperties { name: "Spain", encryption_hint: Classic }, AxisAttributeProperties { name: "Germany", encryption_hint: Classic }], hierarchical: false', + 'Country: [AttributeBuilder { name: "France", encryption_hint: Classic }, AttributeBuilder { name: "UK", encryption_hint: Classic }, AttributeBuilder { name: "Spain", encryption_hint: Classic }, AttributeBuilder { name: "Germany", encryption_hint: Classic }], hierarchical: false', ) secrecy_axis = PolicyAxis( 'Secrecy', @@ -61,7 +61,7 @@ def test_policy_axis(self) -> None: ) self.assertEqual( secrecy_axis.to_string(), - 'Secrecy: [AxisAttributeProperties { name: "Low", encryption_hint: Classic }, AxisAttributeProperties { name: "Medium", encryption_hint: Classic }, AxisAttributeProperties { name: "High", encryption_hint: Hybridized }], hierarchical: true', + 'Secrecy: [AttributeBuilder { name: "Low", encryption_hint: Classic }, AttributeBuilder { name: "Medium", encryption_hint: Classic }, AttributeBuilder { name: "High", encryption_hint: Hybridized }], hierarchical: true', ) self.assertTrue(PolicyAxis('Test', [], False).is_empty()) @@ -111,7 +111,7 @@ def setUp(self) -> None: secrecy_axis = PolicyAxis( 'Secrecy', [('Low', False), ('Medium', False), ('High', True)], True ) - self.policy = Policy(100) + self.policy = Policy() self.policy.add_axis(country_axis) self.policy.add_axis(secrecy_axis) @@ -119,13 +119,6 @@ def setUp(self) -> None: self.msk, self.pk = self.cc.generate_master_keys(self.policy) def test_master_key_serialization(self) -> None: - # test deep copy - copy_msk = self.msk.deep_copy() - self.assertIsInstance(copy_msk, MasterSecretKey) - - copy_pk = self.pk.deep_copy() - self.assertIsInstance(copy_pk, MasterPublicKey) - # test serialization msk_bytes = self.msk.to_bytes() self.assertIsInstance(MasterSecretKey.from_bytes(msk_bytes), MasterSecretKey) @@ -143,9 +136,6 @@ def test_user_key_serialization(self) -> None: 'Secrecy::High && (Country::France || Country::Spain)', self.policy, ) - # test deep copy - copy_usk = self.msk.deep_copy() - self.assertIsInstance(copy_usk, MasterSecretKey) # test serialization usk_bytes = usk.to_bytes() @@ -179,7 +169,7 @@ def setUp(self) -> None: secrecy_axis = PolicyAxis( 'Secrecy', [('Low', False), ('Medium', False), ('High', True)], True ) - self.policy = Policy(100) + self.policy = Policy() self.policy.add_axis(country_axis) self.policy.add_axis(secrecy_axis) diff --git a/crates/cover_crypt/src/ffi/abe_policy.rs b/crates/cover_crypt/src/ffi/abe_policy.rs index 3d1ab4a7..552008df 100644 --- a/crates/cover_crypt/src/ffi/abe_policy.rs +++ b/crates/cover_crypt/src/ffi/abe_policy.rs @@ -6,7 +6,7 @@ use cosmian_ffi_utils::{ffi_read_bytes, ffi_read_string, ffi_unwrap, ffi_write_b pub unsafe extern "C" fn h_policy( policy_ptr: *mut i8, policy_len: *mut i32, - max_attribute_creations: i32, + _max_attribute_creations: i32, ) -> i32 { let policy = Policy::new(); let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error deserializing policy"); diff --git a/crates/cover_crypt/src/pyo3/py_abe_policy.rs b/crates/cover_crypt/src/pyo3/py_abe_policy.rs index 01ec598d..66a7cdf4 100644 --- a/crates/cover_crypt/src/pyo3/py_abe_policy.rs +++ b/crates/cover_crypt/src/pyo3/py_abe_policy.rs @@ -188,6 +188,51 @@ impl Policy { .map_err(|e| PyException::new_err(e.to_string())) } + /// Removes the given axis from the policy. + pub fn remove_axis(&mut self, axis_name: String) -> PyResult<()> { + self.0 + .remove_dimension(axis_name) + .map_err(|e| PyException::new_err(e.to_string())) + } + + /// Adds the given attribute to the policy. + pub fn add_attribute(&mut self, attribute: &Attribute, is_hybridized: bool) -> PyResult<()> { + self.0 + .add_attribute( + attribute.0.clone(), + if is_hybridized { + EncryptionHint::Hybridized + } else { + EncryptionHint::Classic + }, + ) + .map_err(|e| PyException::new_err(e.to_string())) + } + + /// Removes the given attribute from the policy + /// Encrypting and decrypting for this attribute will no longer be possible once the keys are updated. + pub fn remove_attribute(&mut self, attribute: &Attribute) -> PyResult<()> { + self.0 + .remove_attribute(attribute.0.clone()) + .map_err(|e| PyException::new_err(e.to_string())) + } + + /// Marks an attribute as read only. + /// The corresponding attribute key will be removed from the public key. + /// But the decryption key will be kept to allow reading old ciphertext. + pub fn disable_attribute(&mut self, attr: &Attribute) -> PyResult<()> { + self.0 + .disable_attribute(attr.0.clone()) + .map_err(|e| PyException::new_err(e.to_string())) + } + + /// Changes the name of an attribute. + pub fn rename_attribute(&mut self, attribute: &Attribute, new_name: &str) -> PyResult<()> { + self.0 + .rename_attribute(attribute.0.clone(), new_name) + .map_err(|e| PyException::new_err(e.to_string())) + } + /// Rotates an attribute, changing its underlying value with an unused /// value. pub fn rotate(&mut self, attribute: &Attribute) -> PyResult<()> { @@ -196,6 +241,13 @@ impl Policy { .map_err(|e| PyException::new_err(e.to_string())) } + /// Removes old rotations id of an attribute. + pub fn clear_old_rotations(&mut self, attr: &Attribute) -> PyResult<()> { + self.0 + .clear_old_rotations(&attr.0) + .map_err(|e| PyException::new_err(e.to_string())) + } + /// Returns the list of Attributes of this Policy. pub fn attributes(&self) -> Vec { self.0.attributes().into_iter().map(Attribute).collect() diff --git a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs index c06fdf7c..fd30bb0b 100644 --- a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs +++ b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs @@ -52,7 +52,7 @@ pub fn webassembly_policy_axis( } #[wasm_bindgen] -pub fn webassembly_policy(nb_creations: u32) -> Result, JsValue> { +pub fn webassembly_policy(_nb_creations: u32) -> Result, JsValue> { serde_json::to_vec(&Policy::new()).map_err(|e| JsValue::from_str(&e.to_string())) } diff --git a/crates/findex/src/implementations/sqlite/findex.rs b/crates/findex/src/implementations/sqlite/findex.rs index 1d063c3a..43ba5994 100644 --- a/crates/findex/src/implementations/sqlite/findex.rs +++ b/crates/findex/src/implementations/sqlite/findex.rs @@ -121,7 +121,7 @@ impl FindexCallbacks for SqliteFindex { .lock() .expect("Rusqlite connection lock poisoned"); let tx = cnx.transaction()?; - for (uid, value) in items.iter() { + for (uid, value) in &*items { tx.execute( "INSERT INTO chain_table (uid, value) VALUES (?1, ?2)", [uid.to_vec(), value.clone()], diff --git a/crates/findex/src/pyo3/py_callbacks.rs b/crates/findex/src/pyo3/py_callbacks.rs index 8850289b..4073be97 100644 --- a/crates/findex/src/pyo3/py_callbacks.rs +++ b/crates/findex/src/pyo3/py_callbacks.rs @@ -156,7 +156,7 @@ impl FindexCallbacks for InternalFindex { let empty_vec = &vec![]; Python::with_gil(|py| { let py_entry_table = PyDict::new(py); - for (key, (old_value, new_value)) in items.iter() { + for (key, (old_value, new_value)) in &*items { py_entry_table .set_item( PyBytes::new(py, key), @@ -190,7 +190,7 @@ impl FindexCallbacks for InternalFindex { ) -> Result<(), FindexPyo3Error> { Python::with_gil(|py| { let py_chain_table = PyDict::new(py); - for (key, value) in items.iter() { + for (key, value) in &*items { py_chain_table .set_item(PyBytes::new(py, key), PyBytes::new(py, value)) .map_err(|e| FindexPyo3Error::ConversionError(format!("{e} (insert_chain)")))?; @@ -210,7 +210,7 @@ impl FindexCallbacks for InternalFindex { ) -> Result<(), FindexPyo3Error> { Python::with_gil(|py| { let py_entry_table_items = PyDict::new(py); - for (key, value) in new_encrypted_entry_table_items.iter() { + for (key, value) in &*new_encrypted_entry_table_items { py_entry_table_items .set_item(PyBytes::new(py, key), PyBytes::new(py, value)) .map_err(|e| FindexPyo3Error::ConversionError(format!("{e} (update_lines)")))?; @@ -223,7 +223,7 @@ impl FindexCallbacks for InternalFindex { .collect(); let py_chain_table_items = PyDict::new(py); - for (key, value) in new_encrypted_chain_table_items.iter() { + for (key, value) in &*new_encrypted_chain_table_items { py_chain_table_items .set_item(PyBytes::new(py, key), PyBytes::new(py, value)) .map_err(|e| FindexPyo3Error::ConversionError(format!("{e} (update_lines)")))?; From 1b9cb96cbc7aa75020d375b59ab0c382e049bf95 Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Tue, 26 Sep 2023 11:29:17 +0200 Subject: [PATCH 04/18] test: add policy editing Python tests --- .../cloudproof_cover_crypt/__init__.pyi | 2 +- .../python/tests/cover_crypt_test.py | 82 +++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) diff --git a/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi b/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi index 9f786fbf..65fcaaea 100644 --- a/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi +++ b/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi @@ -137,7 +137,7 @@ class Policy: Args: attribute (Attribute) """ - def rename_attribute(self, attribute: Attribute) -> None: + def rename_attribute(self, attribute: Attribute, new_name: str) -> None: """Changes the name of an attribute. Args: diff --git a/crates/cover_crypt/python/tests/cover_crypt_test.py b/crates/cover_crypt/python/tests/cover_crypt_test.py index 79e1840d..acc321a0 100644 --- a/crates/cover_crypt/python/tests/cover_crypt_test.py +++ b/crates/cover_crypt/python/tests/cover_crypt_test.py @@ -84,6 +84,88 @@ def test_policy_creation_rotation(self) -> None: new_france_value = policy.attribute_current_value(france_attribute) self.assertEqual(new_france_value, 8) self.assertEqual(policy.attribute_values(france_attribute), [8, 1]) + # clear rotation + policy.clear_old_rotations(france_attribute) + self.assertEqual(policy.attribute_values(france_attribute), [8]) + # clearing rotation of non existing attribute will raise an Error + with self.assertRaises(Exception): + policy.clear_old_rotations(Attribute('Department', 'Missing')) + + def test_edit_policy(self) -> None: + # Create and initialize policy + policy = self.policy() + self.assertEqual(len(policy.attributes()), 7) + + # Rename R&D to Research + policy.rename_attribute(Attribute('Country', 'Spain'), 'Espagne') + + # Try renaming Research to already used name MKG + with self.assertRaises(Exception): + policy.rename_attribute(Attribute('Country', 'Japan'), 'Japon') + self.assertEqual(len(policy.attributes()), 7) + + # Add new attribute Japan + new_attr = Attribute('Country', 'Japan') + policy.add_attribute(new_attr, False) + self.assertEqual(len(policy.attributes()), 8) + + # Try adding already existing attribute + duplicate_attr = Attribute('Country', 'France') + with self.assertRaises(Exception): + policy.add_attribute(duplicate_attr, False) + + # Try adding attribute to non-existing dimension + missing_dimension = Attribute('Missing', 'dimension') + with self.assertRaises(Exception): + policy.add_attribute(missing_dimension, False) + + # Remove research attribute + delete_attr = Attribute('Country', 'Espagne') + policy.remove_attribute(delete_attr) + self.assertEqual(len(policy.attributes()), 7) + + # Duplicate remove + with self.assertRaises(Exception): + policy.remove_attribute(delete_attr) + + # Missing dimension remove + with self.assertRaises(Exception): + policy.remove_attribute(missing_dimension) + + # Remove all attributes from a dimension + policy.remove_attribute(new_attr) + policy.remove_attribute(Attribute('Country', 'France')) + policy.remove_attribute(Attribute('Country', 'UK')) + policy.remove_attribute(Attribute('Country', 'Germany')) + self.assertEqual(len(policy.attributes()), 3) + + # Add new dimension + new_dimension = PolicyAxis( + 'DimensionTest', + [ + ('Attr1', False), + ('Attr2', False), + ], + False, + ) + policy.add_axis(new_dimension) + self.assertEqual(len(policy.attributes()), 5) + + # Remove the new dimension + policy.remove_axis('DimensionTest') + self.assertEqual(len(policy.attributes()), 3) + + # Try removing non-existing dimension + with self.assertRaises(Exception): + policy.remove_axis('MissingDim') + + # Try modifying hierarchical dimension + with self.assertRaises(Exception): + policy.remove_attribute(Attribute('Secrecy', 'Top Secret')) + + # Removing a hierarchical dimension is permitted + policy.remove_axis('Secrecy') + self.assertEqual(len(policy.attributes()), 0) def test_policy_cloning_serialization(self) -> None: policy = self.policy() From 4cd66a053f2518a29c84db52ce4a06686fa26945 Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Tue, 26 Sep 2023 14:59:36 +0200 Subject: [PATCH 05/18] test: python encryption and decryption on edited policy --- .../python/tests/cover_crypt_test.py | 131 +++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/crates/cover_crypt/python/tests/cover_crypt_test.py b/crates/cover_crypt/python/tests/cover_crypt_test.py index acc321a0..4e8fbe36 100644 --- a/crates/cover_crypt/python/tests/cover_crypt_test.py +++ b/crates/cover_crypt/python/tests/cover_crypt_test.py @@ -325,7 +325,6 @@ def test_policy_rotation_encryption_decryption(self) -> None: ) france_attribute = Attribute('Country', 'France') - # new_policy = deepcopy(self.policy) self.policy.rotate(france_attribute) self.cc.update_master_keys(self.policy, self.msk, self.pk) @@ -403,6 +402,136 @@ def test_decomposed_encryption_decryption(self) -> None: ) self.assertEqual(bytes(decrypted_data), self.plaintext) + def test_add_attribute(self) -> None: + # User secret key + decryption_policy = 'Secrecy::Low' + low_secret_usk = self.cc.generate_user_secret_key( + self.msk, decryption_policy, self.policy + ) + + # Add sales department + self.policy.add_attribute(Attribute('Country', 'Japan'), False) + + # Update the master keys + self.cc.update_master_keys(self.policy, self.msk, self.pk) + + # Encrypt + plaintext = b'My secret data' + ciphertext = self.cc.encrypt( + self.policy, + 'Secrecy::Low && Country::Japan', + self.pk, + plaintext, + ) + + # User cannot decrypt new message without refreshing its key + with self.assertRaises(Exception): + self.cc.decrypt(low_secret_usk, ciphertext) + + self.cc.refresh_user_secret_key( + low_secret_usk, decryption_policy, self.msk, self.policy, False + ) + + decrypted_text, _ = self.cc.decrypt(low_secret_usk, ciphertext) + self.assertEqual(decrypted_text, plaintext) + + def test_delete_attribute(self) -> None: + # User secret key + decryption_policy = 'Secrecy::High && (Country::France || Country::UK)' + usk = self.cc.generate_user_secret_key(self.msk, decryption_policy, self.policy) + + # Encrypt + plaintext = b'My secret data' + ciphertext = self.cc.encrypt( + self.policy, + 'Secrecy::High && Country::France', + self.pk, + plaintext, + ) + + # Remove the attribute + self.policy.remove_attribute(Attribute('Country', 'France')) + + # Update the master keys + self.cc.update_master_keys(self.policy, self.msk, self.pk) + + # Decrypt with old key + decrypted_text, _ = self.cc.decrypt(usk, ciphertext) + self.assertEqual(decrypted_text, plaintext) + + # Refreshing the user key will remove access to removed partitions + new_decryption_policy = 'Secrecy::High && Country::UK' + self.cc.refresh_user_secret_key( + usk, new_decryption_policy, self.msk, self.policy, True + ) + with self.assertRaises(Exception): + self.cc.decrypt(usk, ciphertext) + + def test_disable_attribute(self) -> None: + # User secret key + decryption_policy = 'Secrecy::High && Country::France' + usk = self.cc.generate_user_secret_key(self.msk, decryption_policy, self.policy) + + # Encrypt + plaintext = b'My secret data' + ciphertext = self.cc.encrypt( + self.policy, + 'Secrecy::High && Country::France', + self.pk, + plaintext, + ) + + # Disable the attribute + self.policy.disable_attribute(Attribute('Country', 'France')) + + # Update the master keys + self.cc.update_master_keys(self.policy, self.msk, self.pk) + + # Can no longer encrypt for this attribute + with self.assertRaises(Exception): + self.cc.encrypt( + self.policy, + 'Secrecy::High && Country::France', + self.pk, + b'Test', + ) + + # Refreshing the user key will keep access to the attribute + new_decryption_policy = 'Secrecy::High && Country::France' + self.cc.refresh_user_secret_key( + usk, new_decryption_policy, self.msk, self.policy, False + ) + decrypted_text, _ = self.cc.decrypt(usk, ciphertext) + self.assertEqual(decrypted_text, plaintext) + + def test_rename_attribute(self) -> None: + # User secret key + decryption_policy = 'Secrecy::High && Country::Spain' + usk = self.cc.generate_user_secret_key(self.msk, decryption_policy, self.policy) + + # Encrypt + plaintext = b'My secret data' + ciphertext = self.cc.encrypt( + self.policy, + 'Secrecy::High && Country::Spain', + self.pk, + plaintext, + ) + + # Disable the attribute + self.policy.rename_attribute(Attribute('Country', 'Spain'), 'Espagne') + + # Update the master keys + self.cc.update_master_keys(self.policy, self.msk, self.pk) + + # Refreshing the user key will keep access to the attribute + new_decryption_policy = 'Secrecy::High && Country::Espagne' + self.cc.refresh_user_secret_key( + usk, new_decryption_policy, self.msk, self.policy, False + ) + decrypted_text, _ = self.cc.decrypt(usk, ciphertext) + self.assertEqual(decrypted_text, plaintext) + if __name__ == '__main__': unittest.main() From b6ce6b1118212a308da1de63bccfa808ba552d64 Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Thu, 28 Sep 2023 17:16:00 +0200 Subject: [PATCH 06/18] feat: expose policy editing functions in FFI and WASM --- crates/cover_crypt/src/ffi/abe_policy.rs | 405 +++++++++++++++++- crates/cover_crypt/src/pyo3/py_abe_policy.rs | 3 +- .../src/wasm_bindgen/abe_policy.rs | 155 ++++++- 3 files changed, 560 insertions(+), 3 deletions(-) diff --git a/crates/cover_crypt/src/ffi/abe_policy.rs b/crates/cover_crypt/src/ffi/abe_policy.rs index 552008df..6db9eaee 100644 --- a/crates/cover_crypt/src/ffi/abe_policy.rs +++ b/crates/cover_crypt/src/ffi/abe_policy.rs @@ -1,4 +1,4 @@ -use cosmian_cover_crypt::abe_policy::{AccessPolicy, Attribute, Policy}; +use cosmian_cover_crypt::abe_policy::{AccessPolicy, Attribute, EncryptionHint, Policy}; use cosmian_ffi_utils::{ffi_read_bytes, ffi_read_string, ffi_unwrap, ffi_write_bytes}; /// # Safety @@ -47,6 +47,197 @@ pub unsafe extern "C" fn h_add_policy_axis( 0 } +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn h_remove_policy_axis( + updated_policy_ptr: *mut i8, + updated_policy_len: *mut i32, + current_policy_ptr: *const i8, + current_policy_len: i32, + axis_name_ptr: *const i8, +) -> i32 { + let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); + let mut policy = ffi_unwrap!( + Policy::parse_and_convert(policy_bytes), + "error deserializing policy" + ); + + let axis_name = ffi_read_string!("axis name", axis_name_ptr); + + ffi_unwrap!( + policy.remove_dimension(axis_name), + "error removing policy axis" + ); + + let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); + ffi_write_bytes!( + "updated policy", + &policy_bytes, + updated_policy_ptr, + updated_policy_len + ); + + 0 +} + +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn h_add_policy_attribute( + updated_policy_ptr: *mut i8, + updated_policy_len: *mut i32, + current_policy_ptr: *const i8, + current_policy_len: i32, + attribute: *const i8, + is_hybridized: bool, +) -> i32 { + let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); + let mut policy = ffi_unwrap!( + Policy::parse_and_convert(policy_bytes), + "error deserializing policy" + ); + + let attr_string = ffi_read_string!("attribute", attribute); + let attr = ffi_unwrap!( + Attribute::try_from(attr_string.as_str()), + "error parsing attribute" + ); + + ffi_unwrap!( + policy.add_attribute( + attr, + if is_hybridized { + EncryptionHint::Hybridized + } else { + EncryptionHint::Classic + } + ), + "error adding policy attribute" + ); + + let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); + ffi_write_bytes!( + "updated policy", + &policy_bytes, + updated_policy_ptr, + updated_policy_len + ); + + 0 +} + +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn h_remove_policy_attribute( + updated_policy_ptr: *mut i8, + updated_policy_len: *mut i32, + current_policy_ptr: *const i8, + current_policy_len: i32, + attribute: *const i8, +) -> i32 { + let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); + let mut policy = ffi_unwrap!( + Policy::parse_and_convert(policy_bytes), + "error deserializing policy" + ); + + let attr_string = ffi_read_string!("attribute", attribute); + let attr = ffi_unwrap!( + Attribute::try_from(attr_string.as_str()), + "error parsing attribute" + ); + + ffi_unwrap!( + policy.remove_attribute(attr), + "error removing policy attribute" + ); + + let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); + ffi_write_bytes!( + "updated policy", + &policy_bytes, + updated_policy_ptr, + updated_policy_len + ); + + 0 +} + +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn h_disable_policy_attribute( + updated_policy_ptr: *mut i8, + updated_policy_len: *mut i32, + current_policy_ptr: *const i8, + current_policy_len: i32, + attribute: *const i8, +) -> i32 { + let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); + let mut policy = ffi_unwrap!( + Policy::parse_and_convert(policy_bytes), + "error deserializing policy" + ); + + let attr_string = ffi_read_string!("attribute", attribute); + let attr = ffi_unwrap!( + Attribute::try_from(attr_string.as_str()), + "error parsing attribute" + ); + + ffi_unwrap!( + policy.disable_attribute(attr), + "error disabling policy attribute" + ); + + let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); + ffi_write_bytes!( + "updated policy", + &policy_bytes, + updated_policy_ptr, + updated_policy_len + ); + + 0 +} + +#[no_mangle] +pub unsafe extern "C" fn h_rename_policy_attribute( + updated_policy_ptr: *mut i8, + updated_policy_len: *mut i32, + current_policy_ptr: *const i8, + current_policy_len: i32, + attribute: *const i8, + new_attribute_name_ptr: *const i8, +) -> i32 { + let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); + let mut policy = ffi_unwrap!( + Policy::parse_and_convert(policy_bytes), + "error deserializing policy" + ); + + let attr_string = ffi_read_string!("attribute", attribute); + let attr = ffi_unwrap!( + Attribute::try_from(attr_string.as_str()), + "error parsing attribute" + ); + + let new_attribute_name = ffi_read_string!("new attribute name", new_attribute_name_ptr); + + ffi_unwrap!( + policy.rename_attribute(attr, &new_attribute_name), + "error renaming policy attribute" + ); + + let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); + ffi_write_bytes!( + "updated policy", + &policy_bytes, + updated_policy_ptr, + updated_policy_len + ); + + 0 +} + /// # Safety #[no_mangle] pub unsafe extern "C" fn h_rotate_attribute( @@ -80,6 +271,42 @@ pub unsafe extern "C" fn h_rotate_attribute( 0 } +/// # Safety +#[no_mangle] +pub unsafe extern "C" fn h_clear_old_rotations_attribute( + updated_policy_ptr: *mut i8, + updated_policy_len: *mut i32, + current_policy_ptr: *const i8, + current_policy_len: i32, + attribute: *const i8, +) -> i32 { + let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); + let mut policy = ffi_unwrap!( + Policy::parse_and_convert(policy_bytes), + "error deserializing policy" + ); + let attr_string = ffi_read_string!("attribute", attribute); + let attr = ffi_unwrap!( + Attribute::try_from(attr_string.as_str()), + "error parsing attribute" + ); + + ffi_unwrap!( + policy.clear_old_rotations(&attr), + "error clearing old rotations policy" + ); + + let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); + ffi_write_bytes!( + "updated policy", + &policy_bytes, + updated_policy_ptr, + updated_policy_len + ); + + 0 +} + /// # Safety #[no_mangle] pub unsafe extern "C" fn h_validate_boolean_expression(boolean_expression_ptr: *const i8) -> i32 { @@ -181,5 +408,181 @@ mod tests { // assert ffi and non-ffi have same behavior. assert_eq!(policy, ffi_rotated_policy); + + // clear old rotations for attribute 2 + let attr_rotations = ffi_rotated_policy.attribute_values(&attributes[2]).unwrap(); + assert_eq!(attr_rotations.len(), 2); + policy_bytes = unsafe { + let current_policy_ptr = policy_bytes.as_ptr().cast(); + let current_policy_len = policy_bytes.len() as i32; + let mut updated_policy_bytes = vec![0u8; 8192]; + let updated_policy_ptr = updated_policy_bytes.as_mut_ptr().cast(); + let mut updated_policy_len = updated_policy_bytes.len() as i32; + + let res = h_clear_old_rotations_attribute( + updated_policy_ptr, + &mut updated_policy_len, + current_policy_ptr, + current_policy_len, + attribute.as_ptr().cast(), + ); + if res != 0 { + let mut error = vec![0u8; 8192]; + let error_ptr = error.as_mut_ptr().cast(); + let mut error_len = error.len() as i32; + h_get_error(error_ptr, &mut error_len); + panic!("{}", CStr::from_ptr(error_ptr).to_str().unwrap()); + } + std::slice::from_raw_parts(updated_policy_ptr.cast(), updated_policy_len as usize) + .to_vec() + }; + let ffi_rotated_policy = Policy::parse_and_convert(&policy_bytes).unwrap(); + let attr_rotations = ffi_rotated_policy.attribute_values(&attributes[2]).unwrap(); + assert_eq!(attr_rotations.len(), 1); + } + + #[test] + fn test_edit_policy() { + let policy = policy().unwrap(); + let mut policy_bytes = >::try_from(&policy).unwrap(); + let attributes = policy.attributes(); + + assert_eq!(attributes.len(), 9); + // Remove Security Level axis + policy_bytes = unsafe { + let current_policy_ptr = policy_bytes.as_ptr().cast(); + let current_policy_len = policy_bytes.len() as i32; + let mut updated_policy_bytes = vec![0u8; 8192]; + let updated_policy_ptr = updated_policy_bytes.as_mut_ptr().cast(); + let mut updated_policy_len = updated_policy_bytes.len() as i32; + let axis_name = "Security Level".to_string(); + + let res = h_remove_policy_axis( + updated_policy_ptr, + &mut updated_policy_len, + current_policy_ptr, + current_policy_len, + axis_name.as_ptr().cast(), + ); + assert_eq!(res, 0); + std::slice::from_raw_parts(updated_policy_ptr.cast(), updated_policy_len as usize) + .to_vec() + }; + + let ffi_rotated_policy = Policy::parse_and_convert(&policy_bytes).unwrap(); + let ffi_attributes = ffi_rotated_policy.attributes(); + // Check policy size + assert_eq!(ffi_attributes.len(), 4); + + // Add attribute Sales + policy_bytes = unsafe { + let current_policy_ptr = policy_bytes.as_ptr().cast(); + let current_policy_len = policy_bytes.len() as i32; + let mut updated_policy_bytes = vec![0u8; 8192]; + let updated_policy_ptr = updated_policy_bytes.as_mut_ptr().cast(); + let mut updated_policy_len = updated_policy_bytes.len() as i32; + let attr = Attribute::new("Department", "Sales"); + let c_attr = CString::new(attr.to_string()).unwrap(); + + let res = h_add_policy_attribute( + updated_policy_ptr, + &mut updated_policy_len, + current_policy_ptr, + current_policy_len, + c_attr.as_ptr().cast(), + false, + ); + assert_eq!(res, 0); + std::slice::from_raw_parts(updated_policy_ptr.cast(), updated_policy_len as usize) + .to_vec() + }; + + let ffi_rotated_policy = Policy::parse_and_convert(&policy_bytes).unwrap(); + let ffi_attributes = ffi_rotated_policy.attributes(); + // Check policy size + assert_eq!(ffi_attributes.len(), 5); + + // Remove attribute + policy_bytes = unsafe { + let current_policy_ptr = policy_bytes.as_ptr().cast(); + let current_policy_len = policy_bytes.len() as i32; + let mut updated_policy_bytes = vec![0u8; 8192]; + let updated_policy_ptr = updated_policy_bytes.as_mut_ptr().cast(); + let mut updated_policy_len = updated_policy_bytes.len() as i32; + let attr = Attribute::new("Department", "R&D"); + let c_attr = CString::new(attr.to_string()).unwrap(); + + let res = h_remove_policy_attribute( + updated_policy_ptr, + &mut updated_policy_len, + current_policy_ptr, + current_policy_len, + c_attr.as_ptr().cast(), + ); + assert_eq!(res, 0); + std::slice::from_raw_parts(updated_policy_ptr.cast(), updated_policy_len as usize) + .to_vec() + }; + + let ffi_rotated_policy = Policy::parse_and_convert(&policy_bytes).unwrap(); + let ffi_attributes = ffi_rotated_policy.attributes(); + // Check policy size + assert_eq!(ffi_attributes.len(), 4); + + // Disable attribute + policy_bytes = unsafe { + let current_policy_ptr = policy_bytes.as_ptr().cast(); + let current_policy_len = policy_bytes.len() as i32; + let mut updated_policy_bytes = vec![0u8; 8192]; + let updated_policy_ptr = updated_policy_bytes.as_mut_ptr().cast(); + let mut updated_policy_len = updated_policy_bytes.len() as i32; + let attr = Attribute::new("Department", "MKG"); + let c_attr = CString::new(attr.to_string()).unwrap(); + + let res = h_disable_policy_attribute( + updated_policy_ptr, + &mut updated_policy_len, + current_policy_ptr, + current_policy_len, + c_attr.as_ptr().cast(), + ); + assert_eq!(res, 0); + std::slice::from_raw_parts(updated_policy_ptr.cast(), updated_policy_len as usize) + .to_vec() + }; + + let ffi_rotated_policy = Policy::parse_and_convert(&policy_bytes).unwrap(); + let ffi_attributes = ffi_rotated_policy.attributes(); + // Check policy size + assert_eq!(ffi_attributes.len(), 4); + + // Rename attribute + policy_bytes = unsafe { + let current_policy_ptr = policy_bytes.as_ptr().cast(); + let current_policy_len = policy_bytes.len() as i32; + let mut updated_policy_bytes = vec![0u8; 8192]; + let updated_policy_ptr = updated_policy_bytes.as_mut_ptr().cast(); + let mut updated_policy_len = updated_policy_bytes.len() as i32; + let attr = Attribute::new("Department", "FIN"); + let c_attr = CString::new(attr.to_string()).unwrap(); + let c_new_attribute_name = CString::new("Finance".to_string()).unwrap(); + + let res = h_rename_policy_attribute( + updated_policy_ptr, + &mut updated_policy_len, + current_policy_ptr, + current_policy_len, + c_attr.as_ptr().cast(), + c_new_attribute_name.as_ptr().cast(), + ); + assert_eq!(res, 0); + std::slice::from_raw_parts(updated_policy_ptr.cast(), updated_policy_len as usize) + .to_vec() + }; + + let ffi_rotated_policy = Policy::parse_and_convert(&policy_bytes).unwrap(); + let ffi_attributes = ffi_rotated_policy.attributes(); + // Check policy size + assert_eq!(ffi_attributes.len(), 4); } } diff --git a/crates/cover_crypt/src/pyo3/py_abe_policy.rs b/crates/cover_crypt/src/pyo3/py_abe_policy.rs index 66a7cdf4..5b2d1a87 100644 --- a/crates/cover_crypt/src/pyo3/py_abe_policy.rs +++ b/crates/cover_crypt/src/pyo3/py_abe_policy.rs @@ -210,7 +210,8 @@ impl Policy { } /// Removes the given attribute from the policy - /// Encrypting and decrypting for this attribute will no longer be possible once the keys are updated. + /// Encrypting and decrypting for this attribute will no longer be possible + /// once the keys are updated. pub fn remove_attribute(&mut self, attribute: &Attribute) -> PyResult<()> { self.0 .remove_attribute(attribute.0.clone()) diff --git a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs index fd30bb0b..4dc5a1cd 100644 --- a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs +++ b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs @@ -76,10 +76,133 @@ pub fn webassembly_add_axis(policy: Vec, axis: String) -> Result, Js }) } +#[wasm_bindgen] +pub fn webassembly_remove_axis(policy: Vec, axis_name: String) -> Result, JsValue> { + let mut policy = wasm_unwrap!( + Policy::parse_and_convert(&policy), + "Error deserializing the policy" + ); + wasm_unwrap!( + policy.remove_dimension(axis_name), + "Error removing axis from the policy" + ); + serde_json::to_vec(&policy).map_err(|e| { + JsValue::from_str(&format!( + "Error serializing the policy into the response: {e}" + )) + }) +} + +#[wasm_bindgen] +pub fn webassembly_add_attribute( + policy: Vec, + attribute: String, + is_hybridized: bool, +) -> Result, JsValue> { + let mut policy = wasm_unwrap!( + Policy::parse_and_convert(&policy), + "Error deserializing the policy" + ); + let attr = wasm_unwrap!( + Attribute::try_from(String::from(JsString::from(attribute)).as_str()), + "Error deserializing the attribute" + ); + wasm_unwrap!( + policy.add_attribute( + attr, + if is_hybridized { + EncryptionHint::Hybridized + } else { + EncryptionHint::Classic + } + ), + "Error adding attribute to the policy" + ); + serde_json::to_vec(&policy).map_err(|e| { + JsValue::from_str(&format!( + "Error serializing the policy into the response: {e}" + )) + }) +} + +#[wasm_bindgen] +pub fn webassembly_remove_attribute( + policy: Vec, + attribute: String, +) -> Result, JsValue> { + let mut policy = wasm_unwrap!( + Policy::parse_and_convert(&policy), + "Error deserializing the policy" + ); + let attr = wasm_unwrap!( + Attribute::try_from(String::from(JsString::from(attribute)).as_str()), + "Error deserializing the attribute" + ); + wasm_unwrap!( + policy.remove_attribute(attr), + "Error removing attribute from the policy" + ); + serde_json::to_vec(&policy).map_err(|e| { + JsValue::from_str(&format!( + "Error serializing the policy into the response: {e}" + )) + }) +} + +#[wasm_bindgen] +pub fn webassembly_disable_attribute( + policy: Vec, + attribute: String, +) -> Result, JsValue> { + let mut policy = wasm_unwrap!( + Policy::parse_and_convert(&policy), + "Error deserializing the policy" + ); + let attr = wasm_unwrap!( + Attribute::try_from(String::from(JsString::from(attribute)).as_str()), + "Error deserializing the attribute" + ); + wasm_unwrap!( + policy.disable_attribute(attr), + "Error disabling attribute from the policy" + ); + serde_json::to_vec(&policy).map_err(|e| { + JsValue::from_str(&format!( + "Error serializing the policy into the response: {e}" + )) + }) +} + +#[wasm_bindgen] +pub fn webassembly_rename_attribute( + policy: Vec, + attribute: String, + new_attribute_name: String, +) -> Result, JsValue> { + let mut policy = wasm_unwrap!( + Policy::parse_and_convert(&policy), + "Error deserializing the policy" + ); + let attr = wasm_unwrap!( + Attribute::try_from(String::from(JsString::from(attribute)).as_str()), + "Error deserializing the attribute" + ); + let new_name = String::from(JsString::from(new_attribute_name)); + wasm_unwrap!( + policy.rename_attribute(attr, &new_name), + "Error renaming attribute from the policy" + ); + serde_json::to_vec(&policy).map_err(|e| { + JsValue::from_str(&format!( + "Error serializing the policy into the response: {e}" + )) + }) +} + /// Rotates attributes, changing their underlying values with that of an unused /// slot /// -/// - `attributes` : user access policy (boolean expression as string) +/// - `attributes` : list of attributes to rotate /// - `policy` : global policy data (bytes) /// /// Returns the `rotated` policy @@ -109,3 +232,33 @@ pub fn webassembly_rotate_attributes( )) }) } + +#[wasm_bindgen] +pub fn webassembly_clear_old_rotations_attributes( + attributes: Attributes, + policy: Vec, +) -> Result, JsValue> { + let attributes = Array::from(&JsValue::from(attributes)); + let mut policy = wasm_unwrap!( + Policy::parse_and_convert(&policy), + "Error deserializing the policy" + ); + + // Rotate attributes of the current policy + for attr in attributes.values() { + let attribute = wasm_unwrap!( + Attribute::try_from(String::from(JsString::from(attr?)).as_str()), + "Error deserializing the attribute" + ); + wasm_unwrap!( + policy.clear_old_rotations(&attribute), + "Error clearing old rotations from the policy" + ); + } + + serde_json::to_vec(&policy).map_err(|e| { + JsValue::from_str(&format!( + "Error serializing the policy into the response: {e}" + )) + }) +} From 725f14245d44b7324c81fe8abe661a301da9389d Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Fri, 6 Oct 2023 10:59:02 +0200 Subject: [PATCH 07/18] chore: update covercrypt cloudproof reexport --- crates/cloudproof/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/cloudproof/Cargo.toml b/crates/cloudproof/Cargo.toml index 2608969a..8f5d8a1e 100644 --- a/crates/cloudproof/Cargo.toml +++ b/crates/cloudproof/Cargo.toml @@ -52,7 +52,7 @@ ffi = [ ###### cloudproof_aesgcm = { version = "0.1.2", optional = true } cloudproof_anonymization = { version = "0.1.1", optional = true } -cloudproof_cover_crypt = { version = "12.0.3", optional = true } +cloudproof_cover_crypt = { path = "../cover_crypt", optional = true } cloudproof_ecies = { version = "0.1.2", optional = true } cloudproof_findex = { version = "5.0.4", optional = true } cloudproof_fpe = { version = "0.2.1", optional = true } From 1e9f26deb790d2747a5ad0176826d941de5c19de Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Wed, 11 Oct 2023 09:37:10 +0200 Subject: [PATCH 08/18] fix: use last covercrypt branch --- crates/anonymization/Cargo.toml | 2 +- crates/cover_crypt/Cargo.toml | 4 ++-- .../cover_crypt/python/tests/cover_crypt_test.py | 15 ++++++++++++++- crates/cover_crypt/src/pyo3/py_abe_policy.rs | 9 +-------- crates/findex/Cargo.toml | 4 ++-- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/crates/anonymization/Cargo.toml b/crates/anonymization/Cargo.toml index eb92b117..251a4972 100644 --- a/crates/anonymization/Cargo.toml +++ b/crates/anonymization/Cargo.toml @@ -29,7 +29,7 @@ cosmian_crypto_core = { workspace = true } hex = { workspace = true } rand = { workspace = true } rand_distr = "0.4" -regex = "1.9" +regex = "1.10" sha2 = "0.10" tiny-keccak = { version = "2.0.2", features = ["sha3"] } diff --git a/crates/cover_crypt/Cargo.toml b/crates/cover_crypt/Cargo.toml index 2b5bd0bd..0a4feb08 100644 --- a/crates/cover_crypt/Cargo.toml +++ b/crates/cover_crypt/Cargo.toml @@ -18,7 +18,7 @@ python = ["pyo3"] wasm_bindgen = ["js-sys", "wasm-bindgen"] [dependencies] -cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", branch = "develop", features = ["serialization"] } +cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", branch = "fix/edit_policy", features = ["serialization"] } cosmian_crypto_core = { workspace = true } serde_json = "1.0.107" @@ -30,5 +30,5 @@ pyo3 = { workspace = true, features = ["extension-module"], optional = true } wasm-bindgen = { workspace = true, optional = true } [dev-dependencies] -cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", branch = "develop", features = ["test_utils"] } +cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", branch = "fix/edit_policy", features = ["test_utils"] } wasm-bindgen-test = "0.3.37" diff --git a/crates/cover_crypt/python/tests/cover_crypt_test.py b/crates/cover_crypt/python/tests/cover_crypt_test.py index 4e8fbe36..c2b45361 100644 --- a/crates/cover_crypt/python/tests/cover_crypt_test.py +++ b/crates/cover_crypt/python/tests/cover_crypt_test.py @@ -221,7 +221,20 @@ def test_user_key_serialization(self) -> None: # test serialization usk_bytes = usk.to_bytes() - self.assertIsInstance(UserSecretKey.from_bytes(usk_bytes), UserSecretKey) + deserialized_usk = UserSecretKey.from_bytes(usk_bytes) + self.assertIsInstance(deserialized_usk, UserSecretKey) + + # test KMAC authenticity of the deserialized key + france_attribute = Attribute('Country', 'France') + self.policy.rotate(france_attribute) + self.cc.update_master_keys(self.policy, self.msk, self.pk) + self.cc.refresh_user_secret_key( + deserialized_usk, + 'Secrecy::High && (Country::France || Country::Spain)', + self.msk, + self.policy, + keep_old_accesses=True, + ) with self.assertRaises(Exception): UserSecretKey.from_bytes(b'wrong data') diff --git a/crates/cover_crypt/src/pyo3/py_abe_policy.rs b/crates/cover_crypt/src/pyo3/py_abe_policy.rs index 5b2d1a87..dd6dec73 100644 --- a/crates/cover_crypt/src/pyo3/py_abe_policy.rs +++ b/crates/cover_crypt/src/pyo3/py_abe_policy.rs @@ -198,14 +198,7 @@ impl Policy { /// Adds the given attribute to the policy. pub fn add_attribute(&mut self, attribute: &Attribute, is_hybridized: bool) -> PyResult<()> { self.0 - .add_attribute( - attribute.0.clone(), - if is_hybridized { - EncryptionHint::Hybridized - } else { - EncryptionHint::Classic - }, - ) + .add_attribute(attribute.0.clone(), EncryptionHint::new(is_hybridized)) .map_err(|e| PyException::new_err(e.to_string())) } diff --git a/crates/findex/Cargo.toml b/crates/findex/Cargo.toml index 5f4a0b16..96b29881 100644 --- a/crates/findex/Cargo.toml +++ b/crates/findex/Cargo.toml @@ -67,12 +67,12 @@ log = { version = "0.4.20", optional = true } pyo3 = { workspace = true, features = ["extension-module"], optional = true } rand = { workspace = true, optional = true } redis = { version = "0.23", features = ["aio", "ahash", "script", "connection-manager", "tokio-comp"], optional = true } -reqwest = { version = "0.11.20", features = ["rustls-tls"], default-features = false, optional = true } +reqwest = { version = "0.11.22", features = ["rustls-tls"], default-features = false, optional = true } rusqlite = { version = "0.29", features = ["bundled"], optional = true } serde = { version = "1.0", features = ["derive"], optional = true } serde_json = { version = "1.0.107", optional = true } thiserror = { workspace = true, optional = true } -tokio = { version = "1.32.0", optional = true } +tokio = { version = "1.33.0", optional = true } tracing = { workspace = true, optional = true } tracing-log = { workspace = true, optional = true } tracing-subscriber = { workspace = true, optional = true } From 11d2db240086d5d20a89ba22792e67ec08f1fd41 Mon Sep 17 00:00:00 2001 From: Manuthor Date: Mon, 16 Oct 2023 11:21:01 +0200 Subject: [PATCH 09/18] ci: test with KMS server including new cover_crypt --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f059b6ed..dcb5a934 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,10 +13,10 @@ jobs: with: project-name: cloudproof_rust toolchain: stable - kms-version: 4.5.0 + kms-version: feat/edit_policy findex-cloud-version: 0.1.0 branch-java: develop branch-js: v9.2.0 branch-flutter: develop - branch-python: v4.1.0 + branch-python: feat/edit_policy secrets: inherit From b857c92cc506e207661c85d3d512a1c073c95eda Mon Sep 17 00:00:00 2001 From: Manuthor Date: Mon, 16 Oct 2023 11:44:51 +0200 Subject: [PATCH 10/18] ci: test with KMS server including new cover_crypt (bad container name) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dcb5a934..995ecbff 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: with: project-name: cloudproof_rust toolchain: stable - kms-version: feat/edit_policy + kms-version: feat-edit_policy findex-cloud-version: 0.1.0 branch-java: develop branch-js: v9.2.0 From 98e984ba8644e789e6812e4070f0136fb164f0cc Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Tue, 17 Oct 2023 14:50:22 +0200 Subject: [PATCH 11/18] ci: update java and JS branch --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 995ecbff..4e6dcf9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -15,8 +15,8 @@ jobs: toolchain: stable kms-version: feat-edit_policy findex-cloud-version: 0.1.0 - branch-java: develop - branch-js: v9.2.0 + branch-java: feat/edit_policy + branch-js: feat/edit_policy branch-flutter: develop branch-python: feat/edit_policy secrets: inherit From 291fc9f588982b7eeb6af52ffbcfe6885204bb58 Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Thu, 19 Oct 2023 17:06:59 +0200 Subject: [PATCH 12/18] test: add python test for rekeying disabled hybrid keys --- crates/cover_crypt/python/tests/cover_crypt_test.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/crates/cover_crypt/python/tests/cover_crypt_test.py b/crates/cover_crypt/python/tests/cover_crypt_test.py index c2b45361..a6bfacce 100644 --- a/crates/cover_crypt/python/tests/cover_crypt_test.py +++ b/crates/cover_crypt/python/tests/cover_crypt_test.py @@ -517,6 +517,11 @@ def test_disable_attribute(self) -> None: decrypted_text, _ = self.cc.decrypt(usk, ciphertext) self.assertEqual(decrypted_text, plaintext) + # Rotating the disabled attribute + self.policy.rotate(Attribute('Country', 'France')) + # Update the master keys without error + self.cc.update_master_keys(self.policy, self.msk, self.pk) + def test_rename_attribute(self) -> None: # User secret key decryption_policy = 'Secrecy::High && Country::Spain' From 8225df79d567ecf5f6271beb8c296da30d4ea884 Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Mon, 23 Oct 2023 17:44:32 +0200 Subject: [PATCH 13/18] refacto: wasm serialize policy inplace after editing --- crates/cover_crypt/src/ffi/abe_policy.rs | 20 +-- crates/cover_crypt/src/pyo3/py_abe_policy.rs | 19 +- .../src/wasm_bindgen/abe_policy.rs | 165 +++++++++--------- 3 files changed, 99 insertions(+), 105 deletions(-) diff --git a/crates/cover_crypt/src/ffi/abe_policy.rs b/crates/cover_crypt/src/ffi/abe_policy.rs index 6db9eaee..165d1f83 100644 --- a/crates/cover_crypt/src/ffi/abe_policy.rs +++ b/crates/cover_crypt/src/ffi/abe_policy.rs @@ -3,11 +3,7 @@ use cosmian_ffi_utils::{ffi_read_bytes, ffi_read_string, ffi_unwrap, ffi_write_b /// # Safety #[no_mangle] -pub unsafe extern "C" fn h_policy( - policy_ptr: *mut i8, - policy_len: *mut i32, - _max_attribute_creations: i32, -) -> i32 { +pub unsafe extern "C" fn h_policy(policy_ptr: *mut i8, policy_len: *mut i32) -> i32 { let policy = Policy::new(); let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error deserializing policy"); ffi_write_bytes!("policy", &policy_bytes, policy_ptr, policy_len); @@ -65,7 +61,7 @@ pub unsafe extern "C" fn h_remove_policy_axis( let axis_name = ffi_read_string!("axis name", axis_name_ptr); ffi_unwrap!( - policy.remove_dimension(axis_name), + policy.remove_dimension(&axis_name), "error removing policy axis" ); @@ -147,7 +143,7 @@ pub unsafe extern "C" fn h_remove_policy_attribute( ); ffi_unwrap!( - policy.remove_attribute(attr), + policy.remove_attribute(&attr), "error removing policy attribute" ); @@ -184,7 +180,7 @@ pub unsafe extern "C" fn h_disable_policy_attribute( ); ffi_unwrap!( - policy.disable_attribute(attr), + policy.disable_attribute(&attr), "error disabling policy attribute" ); @@ -223,7 +219,7 @@ pub unsafe extern "C" fn h_rename_policy_attribute( let new_attribute_name = ffi_read_string!("new attribute name", new_attribute_name_ptr); ffi_unwrap!( - policy.rename_attribute(attr, &new_attribute_name), + policy.rename_attribute(&attr, &new_attribute_name), "error renaming policy attribute" ); @@ -273,7 +269,7 @@ pub unsafe extern "C" fn h_rotate_attribute( /// # Safety #[no_mangle] -pub unsafe extern "C" fn h_clear_old_rotations_attribute( +pub unsafe extern "C" fn h_clear_old_attribute_values( updated_policy_ptr: *mut i8, updated_policy_len: *mut i32, current_policy_ptr: *const i8, @@ -292,7 +288,7 @@ pub unsafe extern "C" fn h_clear_old_rotations_attribute( ); ffi_unwrap!( - policy.clear_old_rotations(&attr), + policy.clear_old_attribute_values(&attr), "error clearing old rotations policy" ); @@ -419,7 +415,7 @@ mod tests { let updated_policy_ptr = updated_policy_bytes.as_mut_ptr().cast(); let mut updated_policy_len = updated_policy_bytes.len() as i32; - let res = h_clear_old_rotations_attribute( + let res = h_clear_old_attribute_values( updated_policy_ptr, &mut updated_policy_len, current_policy_ptr, diff --git a/crates/cover_crypt/src/pyo3/py_abe_policy.rs b/crates/cover_crypt/src/pyo3/py_abe_policy.rs index dd6dec73..b586372a 100644 --- a/crates/cover_crypt/src/pyo3/py_abe_policy.rs +++ b/crates/cover_crypt/src/pyo3/py_abe_policy.rs @@ -16,6 +16,7 @@ use pyo3::{ /// axis (str): policy axis the attributes belongs to /// name (str): unique attribute name within this axis #[pyclass] +#[derive(Clone)] pub struct Attribute(AttributeRust); #[pymethods] @@ -189,16 +190,16 @@ impl Policy { } /// Removes the given axis from the policy. - pub fn remove_axis(&mut self, axis_name: String) -> PyResult<()> { + pub fn remove_axis(&mut self, axis_name: &str) -> PyResult<()> { self.0 .remove_dimension(axis_name) .map_err(|e| PyException::new_err(e.to_string())) } /// Adds the given attribute to the policy. - pub fn add_attribute(&mut self, attribute: &Attribute, is_hybridized: bool) -> PyResult<()> { + pub fn add_attribute(&mut self, attribute: Attribute, is_hybridized: bool) -> PyResult<()> { self.0 - .add_attribute(attribute.0.clone(), EncryptionHint::new(is_hybridized)) + .add_attribute(attribute.0, EncryptionHint::new(is_hybridized)) .map_err(|e| PyException::new_err(e.to_string())) } @@ -207,23 +208,23 @@ impl Policy { /// once the keys are updated. pub fn remove_attribute(&mut self, attribute: &Attribute) -> PyResult<()> { self.0 - .remove_attribute(attribute.0.clone()) + .remove_attribute(&attribute.0) .map_err(|e| PyException::new_err(e.to_string())) } /// Marks an attribute as read only. /// The corresponding attribute key will be removed from the public key. /// But the decryption key will be kept to allow reading old ciphertext. - pub fn disable_attribute(&mut self, attr: &Attribute) -> PyResult<()> { + pub fn disable_attribute(&mut self, attribute: &Attribute) -> PyResult<()> { self.0 - .disable_attribute(attr.0.clone()) + .disable_attribute(&attribute.0) .map_err(|e| PyException::new_err(e.to_string())) } /// Changes the name of an attribute. pub fn rename_attribute(&mut self, attribute: &Attribute, new_name: &str) -> PyResult<()> { self.0 - .rename_attribute(attribute.0.clone(), new_name) + .rename_attribute(&attribute.0, new_name) .map_err(|e| PyException::new_err(e.to_string())) } @@ -236,9 +237,9 @@ impl Policy { } /// Removes old rotations id of an attribute. - pub fn clear_old_rotations(&mut self, attr: &Attribute) -> PyResult<()> { + pub fn clear_old_attribute_values(&mut self, attr: &Attribute) -> PyResult<()> { self.0 - .clear_old_rotations(&attr.0) + .clear_old_attribute_values(&attr.0) .map_err(|e| PyException::new_err(e.to_string())) } diff --git a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs index 4dc5a1cd..c69d3ef4 100644 --- a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs +++ b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs @@ -2,6 +2,25 @@ use cosmian_cover_crypt::abe_policy::{Attribute, DimensionBuilder, EncryptionHin use js_sys::{Array, Boolean, JsString, Reflect}; use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +macro_rules! update_policy { + ($policy:expr, $attribute:expr, $action:ident, $error_msg:expr) => {{ + let mut cc_policy = wasm_unwrap!( + Policy::parse_and_convert(&$policy), + "Error deserializing the policy" + ); + let attr = wasm_unwrap!( + Attribute::try_from(String::from(JsString::from($attribute)).as_str()), + "Error deserializing the attribute" + ); + wasm_unwrap!(cc_policy.$action(&attr), $error_msg); + wasm_unwrap!( + serde_json::to_writer(&mut $policy, &cc_policy), + "Error serializing the policy into the response" + ); + Ok($policy) + }}; +} + #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "Array")] @@ -52,54 +71,54 @@ pub fn webassembly_policy_axis( } #[wasm_bindgen] -pub fn webassembly_policy(_nb_creations: u32) -> Result, JsValue> { +pub fn webassembly_policy() -> Result, JsValue> { serde_json::to_vec(&Policy::new()).map_err(|e| JsValue::from_str(&e.to_string())) } #[wasm_bindgen] -pub fn webassembly_add_axis(policy: Vec, axis: String) -> Result, JsValue> { - let mut policy = wasm_unwrap!( +pub fn webassembly_add_axis(mut policy: Vec, axis: String) -> Result, JsValue> { + let mut cc_policy = wasm_unwrap!( Policy::parse_and_convert(&policy), "Error deserializing the policy" ); wasm_unwrap!( - policy.add_dimension(wasm_unwrap!( + cc_policy.add_dimension(wasm_unwrap!( serde_json::from_str(&axis), "Error deserializing the policy axis" )), "Error adding axis to the policy" ); - serde_json::to_vec(&policy).map_err(|e| { - JsValue::from_str(&format!( - "Error serializing the policy into the response: {e}" - )) - }) + wasm_unwrap!( + serde_json::to_writer(&mut policy, &cc_policy), + "Error serializing the policy into the response" + ); + Ok(policy) } #[wasm_bindgen] -pub fn webassembly_remove_axis(policy: Vec, axis_name: String) -> Result, JsValue> { - let mut policy = wasm_unwrap!( +pub fn webassembly_remove_axis(mut policy: Vec, axis_name: &str) -> Result, JsValue> { + let mut cc_policy = wasm_unwrap!( Policy::parse_and_convert(&policy), "Error deserializing the policy" ); wasm_unwrap!( - policy.remove_dimension(axis_name), + cc_policy.remove_dimension(axis_name), "Error removing axis from the policy" ); - serde_json::to_vec(&policy).map_err(|e| { - JsValue::from_str(&format!( - "Error serializing the policy into the response: {e}" - )) - }) + wasm_unwrap!( + serde_json::to_writer(&mut policy, &cc_policy), + "Error serializing the policy into the response" + ); + Ok(policy) } #[wasm_bindgen] pub fn webassembly_add_attribute( - policy: Vec, + mut policy: Vec, attribute: String, is_hybridized: bool, ) -> Result, JsValue> { - let mut policy = wasm_unwrap!( + let mut cc_policy = wasm_unwrap!( Policy::parse_and_convert(&policy), "Error deserializing the policy" ); @@ -108,7 +127,7 @@ pub fn webassembly_add_attribute( "Error deserializing the attribute" ); wasm_unwrap!( - policy.add_attribute( + cc_policy.add_attribute( attr, if is_hybridized { EncryptionHint::Hybridized @@ -118,68 +137,46 @@ pub fn webassembly_add_attribute( ), "Error adding attribute to the policy" ); - serde_json::to_vec(&policy).map_err(|e| { - JsValue::from_str(&format!( - "Error serializing the policy into the response: {e}" - )) - }) + wasm_unwrap!( + serde_json::to_writer(&mut policy, &cc_policy), + "Error serializing the policy into the response" + ); + Ok(policy) } #[wasm_bindgen] pub fn webassembly_remove_attribute( - policy: Vec, + mut policy: Vec, attribute: String, ) -> Result, JsValue> { - let mut policy = wasm_unwrap!( - Policy::parse_and_convert(&policy), - "Error deserializing the policy" - ); - let attr = wasm_unwrap!( - Attribute::try_from(String::from(JsString::from(attribute)).as_str()), - "Error deserializing the attribute" - ); - wasm_unwrap!( - policy.remove_attribute(attr), + update_policy!( + policy, + attribute, + remove_attribute, "Error removing attribute from the policy" - ); - serde_json::to_vec(&policy).map_err(|e| { - JsValue::from_str(&format!( - "Error serializing the policy into the response: {e}" - )) - }) + ) } #[wasm_bindgen] pub fn webassembly_disable_attribute( - policy: Vec, + mut policy: Vec, attribute: String, ) -> Result, JsValue> { - let mut policy = wasm_unwrap!( - Policy::parse_and_convert(&policy), - "Error deserializing the policy" - ); - let attr = wasm_unwrap!( - Attribute::try_from(String::from(JsString::from(attribute)).as_str()), - "Error deserializing the attribute" - ); - wasm_unwrap!( - policy.disable_attribute(attr), + update_policy!( + policy, + attribute, + disable_attribute, "Error disabling attribute from the policy" - ); - serde_json::to_vec(&policy).map_err(|e| { - JsValue::from_str(&format!( - "Error serializing the policy into the response: {e}" - )) - }) + ) } #[wasm_bindgen] pub fn webassembly_rename_attribute( - policy: Vec, + mut policy: Vec, attribute: String, new_attribute_name: String, ) -> Result, JsValue> { - let mut policy = wasm_unwrap!( + let mut cc_policy = wasm_unwrap!( Policy::parse_and_convert(&policy), "Error deserializing the policy" ); @@ -189,14 +186,14 @@ pub fn webassembly_rename_attribute( ); let new_name = String::from(JsString::from(new_attribute_name)); wasm_unwrap!( - policy.rename_attribute(attr, &new_name), + cc_policy.rename_attribute(&attr, &new_name), "Error renaming attribute from the policy" ); - serde_json::to_vec(&policy).map_err(|e| { - JsValue::from_str(&format!( - "Error serializing the policy into the response: {e}" - )) - }) + wasm_unwrap!( + serde_json::to_writer(&mut policy, &cc_policy), + "Error serializing the policy into the response" + ); + Ok(policy) } /// Rotates attributes, changing their underlying values with that of an unused @@ -209,10 +206,10 @@ pub fn webassembly_rename_attribute( #[wasm_bindgen] pub fn webassembly_rotate_attributes( attributes: Attributes, - policy: Vec, + mut policy: Vec, ) -> Result, JsValue> { let attributes = Array::from(&JsValue::from(attributes)); - let mut policy = wasm_unwrap!( + let mut cc_policy = wasm_unwrap!( Policy::parse_and_convert(&policy), "Error deserializing the policy" ); @@ -223,23 +220,23 @@ pub fn webassembly_rotate_attributes( Attribute::try_from(String::from(JsString::from(attr?)).as_str()), "Error deserializing the attribute" ); - wasm_unwrap!(policy.rotate(&attribute), "Error rotating the policy"); + wasm_unwrap!(cc_policy.rotate(&attribute), "Error rotating the policy"); } - serde_json::to_vec(&policy).map_err(|e| { - JsValue::from_str(&format!( - "Error serializing the policy into the response: {e}" - )) - }) + wasm_unwrap!( + serde_json::to_writer(&mut policy, &cc_policy), + "Error serializing the policy into the response" + ); + Ok(policy) } #[wasm_bindgen] -pub fn webassembly_clear_old_rotations_attributes( +pub fn webassembly_clear_old_attribute_values( attributes: Attributes, - policy: Vec, + mut policy: Vec, ) -> Result, JsValue> { let attributes = Array::from(&JsValue::from(attributes)); - let mut policy = wasm_unwrap!( + let mut cc_policy = wasm_unwrap!( Policy::parse_and_convert(&policy), "Error deserializing the policy" ); @@ -251,14 +248,14 @@ pub fn webassembly_clear_old_rotations_attributes( "Error deserializing the attribute" ); wasm_unwrap!( - policy.clear_old_rotations(&attribute), + cc_policy.clear_old_attribute_values(&attribute), "Error clearing old rotations from the policy" ); } - serde_json::to_vec(&policy).map_err(|e| { - JsValue::from_str(&format!( - "Error serializing the policy into the response: {e}" - )) - }) + wasm_unwrap!( + serde_json::to_writer(&mut policy, &cc_policy), + "Error serializing the policy into the response" + ); + Ok(policy) } From 083f67bb4bf311e5814b80481d85f12d2f717f2a Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Tue, 24 Oct 2023 17:46:58 +0200 Subject: [PATCH 14/18] refacto: factor policy functions using macro for FFI and Wasm --- crates/cover_crypt/Cargo.toml | 4 +- crates/cover_crypt/src/ffi/abe_policy.rs | 269 ++++++++---------- .../src/wasm_bindgen/abe_policy.rs | 108 +++---- 3 files changed, 176 insertions(+), 205 deletions(-) diff --git a/crates/cover_crypt/Cargo.toml b/crates/cover_crypt/Cargo.toml index 0a4feb08..8e651482 100644 --- a/crates/cover_crypt/Cargo.toml +++ b/crates/cover_crypt/Cargo.toml @@ -18,7 +18,7 @@ python = ["pyo3"] wasm_bindgen = ["js-sys", "wasm-bindgen"] [dependencies] -cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", branch = "fix/edit_policy", features = ["serialization"] } +cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", rev = "f1a2948", features = ["serialization"] } cosmian_crypto_core = { workspace = true } serde_json = "1.0.107" @@ -30,5 +30,5 @@ pyo3 = { workspace = true, features = ["extension-module"], optional = true } wasm-bindgen = { workspace = true, optional = true } [dev-dependencies] -cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", branch = "fix/edit_policy", features = ["test_utils"] } +cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", rev = "f1a2948", features = ["test_utils"] } wasm-bindgen-test = "0.3.37" diff --git a/crates/cover_crypt/src/ffi/abe_policy.rs b/crates/cover_crypt/src/ffi/abe_policy.rs index 165d1f83..81a54a4c 100644 --- a/crates/cover_crypt/src/ffi/abe_policy.rs +++ b/crates/cover_crypt/src/ffi/abe_policy.rs @@ -1,6 +1,63 @@ use cosmian_cover_crypt::abe_policy::{AccessPolicy, Attribute, EncryptionHint, Policy}; use cosmian_ffi_utils::{ffi_read_bytes, ffi_read_string, ffi_unwrap, ffi_write_bytes}; +/// This macro handles deserializing the policy from JS, deserializing an +/// attribute from JS, and performing a specified action on the policy. It also +/// ensures proper error handling and serialization of the updated policy into +/// the response. +/// +/// +/// # Parameters +/// +/// TODO: +/// - `$cc_policy`: A placeholder name for the deserialized policy. +/// - `$cc_attr`: A placeholder name for the deserialized attribute. +/// - `$action`: The action to perform on the policy. +/// - `$error_msg`: The error message to display in case of failures. +/// +/// # Returns +/// +/// The serialized updated Policy. +/// ``` +macro_rules! update_policy { + ( + $updated_policy_ptr:ident, + $updated_policy_len:ident, + $current_policy_ptr:ident, + $current_policy_len:ident, + $attr_bytes:ident, + $cc_policy:ident, + $cc_attr:ident, + $action:expr, + $error_msg:expr + ) => {{ + let policy_bytes = + ffi_read_bytes!("current policy", $current_policy_ptr, $current_policy_len); + let mut $cc_policy = ffi_unwrap!( + Policy::parse_and_convert(policy_bytes), + "error deserializing policy" + ); + + let attr_string = ffi_read_string!("attribute", $attr_bytes); + let $cc_attr = ffi_unwrap!( + Attribute::try_from(attr_string.as_str()), + "error parsing attribute" + ); + + ffi_unwrap!($action, $error_msg); + + let policy_bytes = + ffi_unwrap!(>::try_from(&$cc_policy), "error serializing policy"); + ffi_write_bytes!( + "updated policy", + &policy_bytes, + $updated_policy_ptr, + $updated_policy_len + ); + 0 + }}; +} + /// # Safety #[no_mangle] pub unsafe extern "C" fn h_policy(policy_ptr: *mut i8, policy_len: *mut i32) -> i32 { @@ -86,39 +143,17 @@ pub unsafe extern "C" fn h_add_policy_attribute( attribute: *const i8, is_hybridized: bool, ) -> i32 { - let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); - let mut policy = ffi_unwrap!( - Policy::parse_and_convert(policy_bytes), - "error deserializing policy" - ); - - let attr_string = ffi_read_string!("attribute", attribute); - let attr = ffi_unwrap!( - Attribute::try_from(attr_string.as_str()), - "error parsing attribute" - ); - - ffi_unwrap!( - policy.add_attribute( - attr, - if is_hybridized { - EncryptionHint::Hybridized - } else { - EncryptionHint::Classic - } - ), - "error adding policy attribute" - ); - - let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); - ffi_write_bytes!( - "updated policy", - &policy_bytes, + update_policy!( updated_policy_ptr, - updated_policy_len - ); - - 0 + updated_policy_len, + current_policy_ptr, + current_policy_len, + attribute, + cc_policy, + cc_attr, + cc_policy.add_attribute(cc_attr, EncryptionHint::new(is_hybridized)), + "error adding policy attribute" + ) } /// # Safety @@ -130,32 +165,17 @@ pub unsafe extern "C" fn h_remove_policy_attribute( current_policy_len: i32, attribute: *const i8, ) -> i32 { - let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); - let mut policy = ffi_unwrap!( - Policy::parse_and_convert(policy_bytes), - "error deserializing policy" - ); - - let attr_string = ffi_read_string!("attribute", attribute); - let attr = ffi_unwrap!( - Attribute::try_from(attr_string.as_str()), - "error parsing attribute" - ); - - ffi_unwrap!( - policy.remove_attribute(&attr), - "error removing policy attribute" - ); - - let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); - ffi_write_bytes!( - "updated policy", - &policy_bytes, + update_policy!( updated_policy_ptr, - updated_policy_len - ); - - 0 + updated_policy_len, + current_policy_ptr, + current_policy_len, + attribute, + cc_policy, + cc_attr, + cc_policy.remove_attribute(&cc_attr), + "error removing policy attribute" + ) } /// # Safety @@ -167,32 +187,17 @@ pub unsafe extern "C" fn h_disable_policy_attribute( current_policy_len: i32, attribute: *const i8, ) -> i32 { - let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); - let mut policy = ffi_unwrap!( - Policy::parse_and_convert(policy_bytes), - "error deserializing policy" - ); - - let attr_string = ffi_read_string!("attribute", attribute); - let attr = ffi_unwrap!( - Attribute::try_from(attr_string.as_str()), - "error parsing attribute" - ); - - ffi_unwrap!( - policy.disable_attribute(&attr), - "error disabling policy attribute" - ); - - let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); - ffi_write_bytes!( - "updated policy", - &policy_bytes, + update_policy!( updated_policy_ptr, - updated_policy_len - ); - - 0 + updated_policy_len, + current_policy_ptr, + current_policy_len, + attribute, + cc_policy, + cc_attr, + cc_policy.disable_attribute(&cc_attr), + "error disabling policy attribute" + ) } #[no_mangle] @@ -204,34 +209,19 @@ pub unsafe extern "C" fn h_rename_policy_attribute( attribute: *const i8, new_attribute_name_ptr: *const i8, ) -> i32 { - let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); - let mut policy = ffi_unwrap!( - Policy::parse_and_convert(policy_bytes), - "error deserializing policy" - ); - - let attr_string = ffi_read_string!("attribute", attribute); - let attr = ffi_unwrap!( - Attribute::try_from(attr_string.as_str()), - "error parsing attribute" - ); - let new_attribute_name = ffi_read_string!("new attribute name", new_attribute_name_ptr); - ffi_unwrap!( - policy.rename_attribute(&attr, &new_attribute_name), - "error renaming policy attribute" - ); - - let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); - ffi_write_bytes!( - "updated policy", - &policy_bytes, + update_policy!( updated_policy_ptr, - updated_policy_len - ); - - 0 + updated_policy_len, + current_policy_ptr, + current_policy_len, + attribute, + cc_policy, + cc_attr, + cc_policy.rename_attribute(&cc_attr, &new_attribute_name), + "error renaming policy attribute" + ) } /// # Safety @@ -243,28 +233,17 @@ pub unsafe extern "C" fn h_rotate_attribute( current_policy_len: i32, attribute: *const i8, ) -> i32 { - let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); - let mut policy = ffi_unwrap!( - Policy::parse_and_convert(policy_bytes), - "error deserializing policy" - ); - let attr_string = ffi_read_string!("attribute", attribute); - let attr = ffi_unwrap!( - Attribute::try_from(attr_string.as_str()), - "error parsing attribute" - ); - - ffi_unwrap!(policy.rotate(&attr), "error rotating policy"); - - let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); - ffi_write_bytes!( - "updated policy", - &policy_bytes, + update_policy!( updated_policy_ptr, - updated_policy_len - ); - - 0 + updated_policy_len, + current_policy_ptr, + current_policy_len, + attribute, + cc_policy, + cc_attr, + cc_policy.rotate(&cc_attr), + "error rotating policy" + ) } /// # Safety @@ -276,31 +255,17 @@ pub unsafe extern "C" fn h_clear_old_attribute_values( current_policy_len: i32, attribute: *const i8, ) -> i32 { - let policy_bytes = ffi_read_bytes!("current policy", current_policy_ptr, current_policy_len); - let mut policy = ffi_unwrap!( - Policy::parse_and_convert(policy_bytes), - "error deserializing policy" - ); - let attr_string = ffi_read_string!("attribute", attribute); - let attr = ffi_unwrap!( - Attribute::try_from(attr_string.as_str()), - "error parsing attribute" - ); - - ffi_unwrap!( - policy.clear_old_attribute_values(&attr), - "error clearing old rotations policy" - ); - - let policy_bytes = ffi_unwrap!(>::try_from(&policy), "error serializing policy"); - ffi_write_bytes!( - "updated policy", - &policy_bytes, + update_policy!( updated_policy_ptr, - updated_policy_len - ); - - 0 + updated_policy_len, + current_policy_ptr, + current_policy_len, + attribute, + cc_policy, + cc_attr, + cc_policy.clear_old_attribute_values(&cc_attr), + "error clearing old rotations policy" + ) } /// # Safety diff --git a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs index c69d3ef4..ec6d5c5a 100644 --- a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs +++ b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs @@ -2,22 +2,49 @@ use cosmian_cover_crypt::abe_policy::{Attribute, DimensionBuilder, EncryptionHin use js_sys::{Array, Boolean, JsString, Reflect}; use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; +/// This macro handles deserializing the policy from JS, deserializing an +/// attribute from JS, and performing a specified action on the policy. It also +/// ensures proper error handling and serialization of the updated policy into +/// the response. +/// +/// +/// # Parameters +/// +/// - `$policy_bytes`: The input policy bytes that will be updated. +/// - `$attr_bytes`: The attribute to be processed. +/// - `$cc_policy`: A placeholder name for the deserialized policy. +/// - `$cc_attr`: A placeholder name for the deserialized attribute. +/// - `$action`: The action to perform on the policy. +/// - `$error_msg`: The error message to display in case of failures. +/// +/// # Returns +/// +/// The serialized updated Policy. +/// ``` macro_rules! update_policy { - ($policy:expr, $attribute:expr, $action:ident, $error_msg:expr) => {{ - let mut cc_policy = wasm_unwrap!( - Policy::parse_and_convert(&$policy), + ( + $policy_bytes:ident, + $attr_bytes:ident, + $cc_policy:ident, + $cc_attr:ident, + $action:expr, + $error_msg:expr + ) => {{ + let mut $cc_policy = wasm_unwrap!( + Policy::parse_and_convert(&$policy_bytes), "Error deserializing the policy" ); - let attr = wasm_unwrap!( - Attribute::try_from(String::from(JsString::from($attribute)).as_str()), + let $cc_attr = wasm_unwrap!( + Attribute::try_from(String::from(JsString::from($attr_bytes)).as_str()), "Error deserializing the attribute" ); - wasm_unwrap!(cc_policy.$action(&attr), $error_msg); + + wasm_unwrap!($action, $error_msg); wasm_unwrap!( - serde_json::to_writer(&mut $policy, &cc_policy), + serde_json::to_writer(&mut $policy_bytes, &$cc_policy), "Error serializing the policy into the response" ); - Ok($policy) + Ok($policy_bytes) }}; } @@ -118,30 +145,14 @@ pub fn webassembly_add_attribute( attribute: String, is_hybridized: bool, ) -> Result, JsValue> { - let mut cc_policy = wasm_unwrap!( - Policy::parse_and_convert(&policy), - "Error deserializing the policy" - ); - let attr = wasm_unwrap!( - Attribute::try_from(String::from(JsString::from(attribute)).as_str()), - "Error deserializing the attribute" - ); - wasm_unwrap!( - cc_policy.add_attribute( - attr, - if is_hybridized { - EncryptionHint::Hybridized - } else { - EncryptionHint::Classic - } - ), + update_policy!( + policy, + attribute, + cc_policy, + cc_attr, + cc_policy.add_attribute(cc_attr, EncryptionHint::new(is_hybridized)), "Error adding attribute to the policy" - ); - wasm_unwrap!( - serde_json::to_writer(&mut policy, &cc_policy), - "Error serializing the policy into the response" - ); - Ok(policy) + ) } #[wasm_bindgen] @@ -152,7 +163,9 @@ pub fn webassembly_remove_attribute( update_policy!( policy, attribute, - remove_attribute, + cc_policy, + cc_attr, + cc_policy.remove_attribute(&cc_attr), "Error removing attribute from the policy" ) } @@ -165,7 +178,9 @@ pub fn webassembly_disable_attribute( update_policy!( policy, attribute, - disable_attribute, + cc_policy, + cc_attr, + cc_policy.disable_attribute(&cc_attr), "Error disabling attribute from the policy" ) } @@ -176,24 +191,15 @@ pub fn webassembly_rename_attribute( attribute: String, new_attribute_name: String, ) -> Result, JsValue> { - let mut cc_policy = wasm_unwrap!( - Policy::parse_and_convert(&policy), - "Error deserializing the policy" - ); - let attr = wasm_unwrap!( - Attribute::try_from(String::from(JsString::from(attribute)).as_str()), - "Error deserializing the attribute" - ); let new_name = String::from(JsString::from(new_attribute_name)); - wasm_unwrap!( - cc_policy.rename_attribute(&attr, &new_name), + update_policy!( + policy, + attribute, + cc_policy, + cc_attr, + cc_policy.rename_attribute(&cc_attr, &new_name), "Error renaming attribute from the policy" - ); - wasm_unwrap!( - serde_json::to_writer(&mut policy, &cc_policy), - "Error serializing the policy into the response" - ); - Ok(policy) + ) } /// Rotates attributes, changing their underlying values with that of an unused @@ -241,7 +247,7 @@ pub fn webassembly_clear_old_attribute_values( "Error deserializing the policy" ); - // Rotate attributes of the current policy + // Remove old attribute values from the policy for attr in attributes.values() { let attribute = wasm_unwrap!( Attribute::try_from(String::from(JsString::from(attr?)).as_str()), @@ -249,7 +255,7 @@ pub fn webassembly_clear_old_attribute_values( ); wasm_unwrap!( cc_policy.clear_old_attribute_values(&attribute), - "Error clearing old rotations from the policy" + "Error clearing old attribute values from the policy" ); } From 778568d7e147684e1c7a0a7463674a0d4ad8073b Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Wed, 25 Oct 2023 09:42:55 +0200 Subject: [PATCH 15/18] fix: reverse serialization of wasm policy --- crates/cover_crypt/src/ffi/abe_policy.rs | 14 ++-- .../src/wasm_bindgen/abe_policy.rs | 78 +++++++++---------- 2 files changed, 48 insertions(+), 44 deletions(-) diff --git a/crates/cover_crypt/src/ffi/abe_policy.rs b/crates/cover_crypt/src/ffi/abe_policy.rs index 81a54a4c..418a0393 100644 --- a/crates/cover_crypt/src/ffi/abe_policy.rs +++ b/crates/cover_crypt/src/ffi/abe_policy.rs @@ -9,11 +9,15 @@ use cosmian_ffi_utils::{ffi_read_bytes, ffi_read_string, ffi_unwrap, ffi_write_b /// /// # Parameters /// -/// TODO: -/// - `$cc_policy`: A placeholder name for the deserialized policy. -/// - `$cc_attr`: A placeholder name for the deserialized attribute. -/// - `$action`: The action to perform on the policy. -/// - `$error_msg`: The error message to display in case of failures. +/// - `$updated_policy_ptr`: output serialized updated policy. +/// - `$updated_policy_len`: output serialized updated policy length. +/// - `$current_policy_ptr`: serialized policy to update. +/// - `$current_policy_len`: serialized policy length to update. +/// - `$attr_bytes`: serialized attribute to be processed. +/// - `$cc_policy`: placeholder name for the deserialized policy. +/// - `$cc_attr`: placeholder name for the deserialized attribute. +/// - `$action`: action to perform on the policy. +/// - `$error_msg`: error message to display in case of failures. /// /// # Returns /// diff --git a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs index ec6d5c5a..e588f764 100644 --- a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs +++ b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs @@ -10,12 +10,12 @@ use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; /// /// # Parameters /// -/// - `$policy_bytes`: The input policy bytes that will be updated. -/// - `$attr_bytes`: The attribute to be processed. -/// - `$cc_policy`: A placeholder name for the deserialized policy. -/// - `$cc_attr`: A placeholder name for the deserialized attribute. -/// - `$action`: The action to perform on the policy. -/// - `$error_msg`: The error message to display in case of failures. +/// - `$policy_bytes`: serialized policy to update. +/// - `$attr_bytes`: serialized attribute to be processed. +/// - `$cc_policy`: placeholder name for the deserialized policy. +/// - `$cc_attr`: placeholder name for the deserialized attribute. +/// - `$action`: action to perform on the policy. +/// - `$error_msg`: error message to display in case of failures. /// /// # Returns /// @@ -40,11 +40,11 @@ macro_rules! update_policy { ); wasm_unwrap!($action, $error_msg); - wasm_unwrap!( - serde_json::to_writer(&mut $policy_bytes, &$cc_policy), - "Error serializing the policy into the response" - ); - Ok($policy_bytes) + serde_json::to_vec(&$cc_policy).map_err(|e| { + JsValue::from_str(&format!( + "Error serializing the policy into the response: {e}" + )) + }) }}; } @@ -103,7 +103,7 @@ pub fn webassembly_policy() -> Result, JsValue> { } #[wasm_bindgen] -pub fn webassembly_add_axis(mut policy: Vec, axis: String) -> Result, JsValue> { +pub fn webassembly_add_axis(policy: Vec, axis: String) -> Result, JsValue> { let mut cc_policy = wasm_unwrap!( Policy::parse_and_convert(&policy), "Error deserializing the policy" @@ -115,15 +115,15 @@ pub fn webassembly_add_axis(mut policy: Vec, axis: String) -> Result )), "Error adding axis to the policy" ); - wasm_unwrap!( - serde_json::to_writer(&mut policy, &cc_policy), - "Error serializing the policy into the response" - ); - Ok(policy) + serde_json::to_vec(&cc_policy).map_err(|e| { + JsValue::from_str(&format!( + "Error serializing the policy into the response: {e}" + )) + }) } #[wasm_bindgen] -pub fn webassembly_remove_axis(mut policy: Vec, axis_name: &str) -> Result, JsValue> { +pub fn webassembly_remove_axis(policy: Vec, axis_name: &str) -> Result, JsValue> { let mut cc_policy = wasm_unwrap!( Policy::parse_and_convert(&policy), "Error deserializing the policy" @@ -132,16 +132,16 @@ pub fn webassembly_remove_axis(mut policy: Vec, axis_name: &str) -> Result, + policy: Vec, attribute: String, is_hybridized: bool, ) -> Result, JsValue> { @@ -157,7 +157,7 @@ pub fn webassembly_add_attribute( #[wasm_bindgen] pub fn webassembly_remove_attribute( - mut policy: Vec, + policy: Vec, attribute: String, ) -> Result, JsValue> { update_policy!( @@ -172,7 +172,7 @@ pub fn webassembly_remove_attribute( #[wasm_bindgen] pub fn webassembly_disable_attribute( - mut policy: Vec, + policy: Vec, attribute: String, ) -> Result, JsValue> { update_policy!( @@ -187,7 +187,7 @@ pub fn webassembly_disable_attribute( #[wasm_bindgen] pub fn webassembly_rename_attribute( - mut policy: Vec, + policy: Vec, attribute: String, new_attribute_name: String, ) -> Result, JsValue> { @@ -212,7 +212,7 @@ pub fn webassembly_rename_attribute( #[wasm_bindgen] pub fn webassembly_rotate_attributes( attributes: Attributes, - mut policy: Vec, + policy: Vec, ) -> Result, JsValue> { let attributes = Array::from(&JsValue::from(attributes)); let mut cc_policy = wasm_unwrap!( @@ -229,17 +229,17 @@ pub fn webassembly_rotate_attributes( wasm_unwrap!(cc_policy.rotate(&attribute), "Error rotating the policy"); } - wasm_unwrap!( - serde_json::to_writer(&mut policy, &cc_policy), - "Error serializing the policy into the response" - ); - Ok(policy) + serde_json::to_vec(&cc_policy).map_err(|e| { + JsValue::from_str(&format!( + "Error serializing the policy into the response: {e}" + )) + }) } #[wasm_bindgen] pub fn webassembly_clear_old_attribute_values( attributes: Attributes, - mut policy: Vec, + policy: Vec, ) -> Result, JsValue> { let attributes = Array::from(&JsValue::from(attributes)); let mut cc_policy = wasm_unwrap!( @@ -259,9 +259,9 @@ pub fn webassembly_clear_old_attribute_values( ); } - wasm_unwrap!( - serde_json::to_writer(&mut policy, &cc_policy), - "Error serializing the policy into the response" - ); - Ok(policy) + serde_json::to_vec(&cc_policy).map_err(|e| { + JsValue::from_str(&format!( + "Error serializing the policy into the response: {e}" + )) + }) } From 47161bece86c60a8b9484a6ed699946cf78bbab3 Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Wed, 25 Oct 2023 11:30:18 +0200 Subject: [PATCH 16/18] ci: update flutter branch --- .github/workflows/build.yml | 2 +- crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi | 4 ++-- crates/cover_crypt/python/tests/cover_crypt_test.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4e6dcf9a..a4a0b208 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,6 +17,6 @@ jobs: findex-cloud-version: 0.1.0 branch-java: feat/edit_policy branch-js: feat/edit_policy - branch-flutter: develop + branch-flutter: feat/edit_policy branch-python: feat/edit_policy secrets: inherit diff --git a/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi b/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi index 65fcaaea..e9c3edf6 100644 --- a/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi +++ b/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi @@ -149,8 +149,8 @@ class Policy: Args: attribute (Attribute) """ - def clear_old_rotations(self, attribute: Attribute) -> None: - """Removes old rotations value of an attribute + def clear_old_attribute_values(self, attribute: Attribute) -> None: + """Removes old attribute values of an attribute Args: attribute (Attribute) diff --git a/crates/cover_crypt/python/tests/cover_crypt_test.py b/crates/cover_crypt/python/tests/cover_crypt_test.py index a6bfacce..42b53537 100644 --- a/crates/cover_crypt/python/tests/cover_crypt_test.py +++ b/crates/cover_crypt/python/tests/cover_crypt_test.py @@ -85,11 +85,11 @@ def test_policy_creation_rotation(self) -> None: self.assertEqual(new_france_value, 8) self.assertEqual(policy.attribute_values(france_attribute), [8, 1]) # clear rotation - policy.clear_old_rotations(france_attribute) + policy.clear_old_attribute_values(france_attribute) self.assertEqual(policy.attribute_values(france_attribute), [8]) # clearing rotation of non existing attribute will raise an Error with self.assertRaises(Exception): - policy.clear_old_rotations(Attribute('Department', 'Missing')) + policy.clear_old_attribute_values(Attribute('Department', 'Missing')) def test_edit_policy(self) -> None: # Create and initialize policy From 8f3c22387a30c7d7dda54e913fd9128f2f8eb18a Mon Sep 17 00:00:00 2001 From: Hugo Rosenkranz-Costa Date: Wed, 25 Oct 2023 17:47:54 +0200 Subject: [PATCH 17/18] ci: use last cover_crypt commit --- crates/cover_crypt/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/cover_crypt/Cargo.toml b/crates/cover_crypt/Cargo.toml index 8e651482..ebd9a093 100644 --- a/crates/cover_crypt/Cargo.toml +++ b/crates/cover_crypt/Cargo.toml @@ -18,7 +18,7 @@ python = ["pyo3"] wasm_bindgen = ["js-sys", "wasm-bindgen"] [dependencies] -cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", rev = "f1a2948", features = ["serialization"] } +cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", rev = "6e127c5", features = ["serialization"] } cosmian_crypto_core = { workspace = true } serde_json = "1.0.107" @@ -30,5 +30,5 @@ pyo3 = { workspace = true, features = ["extension-module"], optional = true } wasm-bindgen = { workspace = true, optional = true } [dev-dependencies] -cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", rev = "f1a2948", features = ["test_utils"] } +cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", rev = "6e127c5", features = ["test_utils"] } wasm-bindgen-test = "0.3.37" From fe816ee48180a23189333a219e196b5ed633021a Mon Sep 17 00:00:00 2001 From: Manuthor Date: Mon, 6 Nov 2023 12:28:16 +0100 Subject: [PATCH 18/18] chore: bump cover_crypt to 13.0.0 --- Cargo.toml | 6 +++--- crates/aesgcm/Cargo.toml | 2 +- crates/anonymization/Cargo.toml | 2 +- crates/anonymization/src/core/hash.rs | 5 +---- crates/cover_crypt/Cargo.toml | 8 ++++---- crates/ecies/Cargo.toml | 2 +- crates/findex/Cargo.toml | 6 +++--- crates/findex/src/cloud.rs | 2 +- crates/findex/src/implementations/redis/findex.rs | 4 ++-- crates/findex/tests/redis/findex.rs | 6 +----- crates/fpe/Cargo.toml | 2 +- 11 files changed, 19 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 68aaff62..d92c6cf8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,8 @@ members = [ resolver = "1" [workspace.dependencies] -async-trait = "0.1.73" -base64 = "0.21.4" +async-trait = "0.1.74" +base64 = "0.21.5" cosmian_crypto_core = "9.3.0" cosmian_ffi_utils = "0.1.2" hex = "0.4.3" @@ -26,7 +26,7 @@ tracing = "0.1" tracing-log = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-tree = "0.2" -wasm-bindgen = "0.2.87" +wasm-bindgen = "0.2.88" [profile.release] lto = true diff --git a/crates/aesgcm/Cargo.toml b/crates/aesgcm/Cargo.toml index d2e35ee8..e5bbb8d9 100644 --- a/crates/aesgcm/Cargo.toml +++ b/crates/aesgcm/Cargo.toml @@ -27,4 +27,4 @@ pyo3 = { workspace = true, optional = true } wasm-bindgen = { workspace = true, optional = true } [dev-dependencies] -wasm-bindgen-test = "0.3.37" +wasm-bindgen-test = "0.3.38" diff --git a/crates/anonymization/Cargo.toml b/crates/anonymization/Cargo.toml index 251a4972..f780689d 100644 --- a/crates/anonymization/Cargo.toml +++ b/crates/anonymization/Cargo.toml @@ -40,4 +40,4 @@ wasm-bindgen = { workspace = true, optional = true } [dev-dependencies] approx = "0.5.1" -wasm-bindgen-test = "0.3.37" +wasm-bindgen-test = "0.3.38" diff --git a/crates/anonymization/src/core/hash.rs b/crates/anonymization/src/core/hash.rs index 3da119c7..4b1bddc2 100644 --- a/crates/anonymization/src/core/hash.rs +++ b/crates/anonymization/src/core/hash.rs @@ -90,10 +90,7 @@ impl Hasher { } hasher.update(data); // Convert hash output to a fixed size array - let output = hasher - .finalize() - .try_into() - .expect("Sha256 hash should be 32 bytes long."); + let output = hasher.finalize().into(); Ok(output) } diff --git a/crates/cover_crypt/Cargo.toml b/crates/cover_crypt/Cargo.toml index ebd9a093..66b35f44 100644 --- a/crates/cover_crypt/Cargo.toml +++ b/crates/cover_crypt/Cargo.toml @@ -18,9 +18,9 @@ python = ["pyo3"] wasm_bindgen = ["js-sys", "wasm-bindgen"] [dependencies] -cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", rev = "6e127c5", features = ["serialization"] } +cosmian_cover_crypt = { version = "13.0.0", features = ["serialization"] } cosmian_crypto_core = { workspace = true } -serde_json = "1.0.107" +serde_json = "1.0.108" # Optional dependencies cosmian_ffi_utils = { workspace = true, optional = true } @@ -30,5 +30,5 @@ pyo3 = { workspace = true, features = ["extension-module"], optional = true } wasm-bindgen = { workspace = true, optional = true } [dev-dependencies] -cosmian_cover_crypt = { git = "https://github.com/Cosmian/cover_crypt", rev = "6e127c5", features = ["test_utils"] } -wasm-bindgen-test = "0.3.37" +cosmian_cover_crypt = { version = "13.0.0", features = ["test_utils"] } +wasm-bindgen-test = "0.3.38" diff --git a/crates/ecies/Cargo.toml b/crates/ecies/Cargo.toml index 98f9683a..cde1c817 100644 --- a/crates/ecies/Cargo.toml +++ b/crates/ecies/Cargo.toml @@ -27,4 +27,4 @@ pyo3 = { workspace = true, features = ["extension-module"], optional = true } wasm-bindgen = { workspace = true, optional = true } [dev-dependencies] -wasm-bindgen-test = "0.3.37" +wasm-bindgen-test = "0.3.38" diff --git a/crates/findex/Cargo.toml b/crates/findex/Cargo.toml index 96b29881..1e08a2e3 100644 --- a/crates/findex/Cargo.toml +++ b/crates/findex/Cargo.toml @@ -60,7 +60,7 @@ cosmian_findex = "5.0.3" base64 = { workspace = true, optional = true } cosmian_ffi_utils = { workspace = true, optional = true } faker_rand = { version = "0.1", optional = true } -futures = { version = "0.3.28", optional = true } +futures = { version = "0.3.29", optional = true } hex = { workspace = true, optional = true } js-sys = { workspace = true, optional = true } log = { version = "0.4.20", optional = true } @@ -70,7 +70,7 @@ redis = { version = "0.23", features = ["aio", "ahash", "script", "connection-ma reqwest = { version = "0.11.22", features = ["rustls-tls"], default-features = false, optional = true } rusqlite = { version = "0.29", features = ["bundled"], optional = true } serde = { version = "1.0", features = ["derive"], optional = true } -serde_json = { version = "1.0.107", optional = true } +serde_json = { version = "1.0.108", optional = true } thiserror = { workspace = true, optional = true } tokio = { version = "1.33.0", optional = true } tracing = { workspace = true, optional = true } @@ -78,7 +78,7 @@ tracing-log = { workspace = true, optional = true } tracing-subscriber = { workspace = true, optional = true } tracing-tree = { workspace = true, optional = true } wasm-bindgen = { workspace = true, optional = true } -wasm-bindgen-futures = { version = "0.4.37", optional = true } +wasm-bindgen-futures = { version = "0.4.38", optional = true } wasm-logger = { version = "0.2.0", optional = true } [dev-dependencies] diff --git a/crates/findex/src/cloud.rs b/crates/findex/src/cloud.rs index 69f03904..85532335 100644 --- a/crates/findex/src/cloud.rs +++ b/crates/findex/src/cloud.rs @@ -213,7 +213,7 @@ impl FromStr for Token { let prefix = bytes[pos]; let callback = ::try_from(prefix).map_err(|()| FindexCloudError::MalformedToken { - error: format!("unknown prefix {prefix:?} at keys section offset {}", pos), + error: format!("unknown prefix {prefix:?} at keys section offset {pos}"), })?; pos += 1; diff --git a/crates/findex/src/implementations/redis/findex.rs b/crates/findex/src/implementations/redis/findex.rs index 41c90a4d..0c969368 100644 --- a/crates/findex/src/implementations/redis/findex.rs +++ b/crates/findex/src/implementations/redis/findex.rs @@ -45,7 +45,7 @@ impl FindexRedis { /// The conditional upsert script used to /// only update a table if the previous value matches ARGV[2]. /// When the value does not match, the previous value is returned - const CONDITIONAL_UPSERT_SCRIPT: &'static str = r#" + const CONDITIONAL_UPSERT_SCRIPT: &'static str = r" local value=redis.call('GET',ARGV[1]) if((value==false) or (not(value == false) and (ARGV[2] == value))) then redis.call('SET', ARGV[1], ARGV[3]) @@ -53,7 +53,7 @@ impl FindexRedis { else return value end; - "#; + "; /// Connect to a Redis server /// diff --git a/crates/findex/tests/redis/findex.rs b/crates/findex/tests/redis/findex.rs index 77e21d5e..dbbe9f5d 100644 --- a/crates/findex/tests/redis/findex.rs +++ b/crates/findex/tests/redis/findex.rs @@ -158,11 +158,7 @@ async fn assert_french_search( for location in locations { let bytes: &[u8] = location; let index = u16::from_be_bytes(bytes.try_into().unwrap()); - assert!( - expected_values.contains(&index), - "index {} not found", - index - ); + assert!(expected_values.contains(&index), "index {index} not found"); } } diff --git a/crates/fpe/Cargo.toml b/crates/fpe/Cargo.toml index 4265d67e..2682cc6c 100644 --- a/crates/fpe/Cargo.toml +++ b/crates/fpe/Cargo.toml @@ -39,4 +39,4 @@ getrandom = { version = "0.2", features = ["js"] } # required by wasm-bindgen-te rand = "0.8" rand_chacha = "0.3" rand_distr = "0.4" -wasm-bindgen-test = "0.3.37" +wasm-bindgen-test = "0.3.38"