diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f059b6ed..a4a0b208 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-java: feat/edit_policy + branch-js: feat/edit_policy + branch-flutter: feat/edit_policy + branch-python: feat/edit_policy secrets: inherit 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 eb92b117..f780689d 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"] } @@ -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/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 } diff --git a/crates/cover_crypt/Cargo.toml b/crates/cover_crypt/Cargo.toml index 553a434b..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 = { version = "12.0.3", 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 = { version = "12.0.3", 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/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi b/crates/cover_crypt/python/cloudproof_cover_crypt/__init__.pyi index c730d8b7..e9c3edf6 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, new_name: str) -> 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_attribute_values(self, attribute: Attribute) -> None: + """Removes old attribute values 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..42b53537 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()) @@ -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_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_attribute_values(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() @@ -111,7 +193,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 +201,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,13 +218,23 @@ 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() - 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') @@ -179,7 +264,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) @@ -253,7 +338,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) @@ -331,6 +415,141 @@ 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) + + # 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' + 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() diff --git a/crates/cover_crypt/src/ffi/abe_policy.rs b/crates/cover_crypt/src/ffi/abe_policy.rs index 58c52fde..418a0393 100644 --- a/crates/cover_crypt/src/ffi/abe_policy.rs +++ b/crates/cover_crypt/src/ffi/abe_policy.rs @@ -1,14 +1,71 @@ -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}; +/// 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 +/// +/// - `$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 +/// +/// 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, - max_attribute_creations: i32, -) -> i32 { - let policy = Policy::new(max_attribute_creations as u32); +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); 0 @@ -34,7 +91,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!( @@ -49,25 +106,25 @@ pub unsafe extern "C" fn h_add_policy_axis( /// # Safety #[no_mangle] -pub unsafe extern "C" fn h_rotate_attribute( +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, - attribute: *const i8, + 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 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 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!( @@ -80,6 +137,141 @@ pub unsafe extern "C" fn h_rotate_attribute( 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 { + update_policy!( + updated_policy_ptr, + 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 +#[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 { + update_policy!( + updated_policy_ptr, + 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 +#[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 { + update_policy!( + updated_policy_ptr, + 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] +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 new_attribute_name = ffi_read_string!("new attribute name", new_attribute_name_ptr); + + update_policy!( + updated_policy_ptr, + 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 +#[no_mangle] +pub unsafe extern "C" fn h_rotate_attribute( + updated_policy_ptr: *mut i8, + updated_policy_len: *mut i32, + current_policy_ptr: *const i8, + current_policy_len: i32, + attribute: *const i8, +) -> i32 { + update_policy!( + updated_policy_ptr, + updated_policy_len, + current_policy_ptr, + current_policy_len, + attribute, + cc_policy, + cc_attr, + cc_policy.rotate(&cc_attr), + "error rotating policy" + ) +} + +/// # Safety +#[no_mangle] +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, + current_policy_len: i32, + attribute: *const i8, +) -> i32 { + update_policy!( + updated_policy_ptr, + 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 #[no_mangle] pub unsafe extern "C" fn h_validate_boolean_expression(boolean_expression_ptr: *const i8) -> i32 { @@ -181,5 +373,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_attribute_values( + 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/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..b586372a 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}, @@ -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] @@ -30,7 +31,7 @@ impl Attribute { /// Returns: /// str pub fn get_axis(&self) -> &str { - &self.0.axis + &self.0.dimension } /// Gets the attribute name. @@ -75,7 +76,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 +102,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 +178,53 @@ 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())) + } + + /// Removes the given axis from the policy. + 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<()> { + self.0 + .add_attribute(attribute.0, EncryptionHint::new(is_hybridized)) + .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) + .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, attribute: &Attribute) -> PyResult<()> { + self.0 + .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, new_name) .map_err(|e| PyException::new_err(e.to_string())) } @@ -197,6 +236,13 @@ impl Policy { .map_err(|e| PyException::new_err(e.to_string())) } + /// Removes old rotations id of an attribute. + pub fn clear_old_attribute_values(&mut self, attr: &Attribute) -> PyResult<()> { + self.0 + .clear_old_attribute_values(&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 c6d09061..e588f764 100644 --- a/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs +++ b/crates/cover_crypt/src/wasm_bindgen/abe_policy.rs @@ -1,7 +1,53 @@ -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}; +/// 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`: 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 +/// +/// The serialized updated Policy. +/// ``` +macro_rules! update_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 $cc_attr = wasm_unwrap!( + Attribute::try_from(String::from(JsString::from($attr_bytes)).as_str()), + "Error deserializing the attribute" + ); + + wasm_unwrap!($action, $error_msg); + serde_json::to_vec(&$cc_policy).map_err(|e| { + JsValue::from_str(&format!( + "Error serializing the policy into the response: {e}" + )) + }) + }}; +} + #[wasm_bindgen] extern "C" { #[wasm_bindgen(typescript_type = "Array")] @@ -40,7 +86,7 @@ pub fn webassembly_policy_axis( }) .collect::, _>>()?; - serde_json::to_string(&PolicyAxis::new( + serde_json::to_string(&DimensionBuilder::new( &name, attribute_properties .iter() @@ -52,34 +98,114 @@ 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())) +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!( + let mut cc_policy = wasm_unwrap!( Policy::parse_and_convert(&policy), "Error deserializing the policy" ); wasm_unwrap!( - policy.add_axis(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| { + 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(policy: Vec, axis_name: &str) -> Result, JsValue> { + let mut cc_policy = wasm_unwrap!( + Policy::parse_and_convert(&policy), + "Error deserializing the policy" + ); + wasm_unwrap!( + cc_policy.remove_dimension(axis_name), + "Error removing axis from the 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_add_attribute( + policy: Vec, + attribute: String, + is_hybridized: bool, +) -> Result, JsValue> { + 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_bindgen] +pub fn webassembly_remove_attribute( + policy: Vec, + attribute: String, +) -> Result, JsValue> { + update_policy!( + policy, + attribute, + cc_policy, + cc_attr, + cc_policy.remove_attribute(&cc_attr), + "Error removing attribute from the policy" + ) +} + +#[wasm_bindgen] +pub fn webassembly_disable_attribute( + policy: Vec, + attribute: String, +) -> Result, JsValue> { + update_policy!( + policy, + attribute, + cc_policy, + cc_attr, + cc_policy.disable_attribute(&cc_attr), + "Error disabling attribute from the policy" + ) +} + +#[wasm_bindgen] +pub fn webassembly_rename_attribute( + policy: Vec, + attribute: String, + new_attribute_name: String, +) -> Result, JsValue> { + let new_name = String::from(JsString::from(new_attribute_name)); + update_policy!( + policy, + attribute, + cc_policy, + cc_attr, + cc_policy.rename_attribute(&cc_attr, &new_name), + "Error renaming attribute from the policy" + ) +} + /// 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 @@ -89,7 +215,7 @@ pub fn webassembly_rotate_attributes( 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" ); @@ -100,10 +226,40 @@ 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(&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, + policy: Vec, +) -> Result, JsValue> { + let attributes = Array::from(&JsValue::from(attributes)); + let mut cc_policy = wasm_unwrap!( + Policy::parse_and_convert(&policy), + "Error deserializing the 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()), + "Error deserializing the attribute" + ); + wasm_unwrap!( + cc_policy.clear_old_attribute_values(&attribute), + "Error clearing old attribute values from the policy" + ); } - serde_json::to_vec(&policy).map_err(|e| { + serde_json::to_vec(&cc_policy).map_err(|e| { JsValue::from_str(&format!( "Error serializing the policy into the response: {e}" )) 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 5f4a0b16..1e08a2e3 100644 --- a/crates/findex/Cargo.toml +++ b/crates/findex/Cargo.toml @@ -60,25 +60,25 @@ 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 } 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 } +serde_json = { version = "1.0.108", 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 } 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/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)")))?; 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"