Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refacto: Policy & keys data structure #119

Merged
merged 38 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b33c054
feat: optimize data struct for MSK and USK
Nov 9, 2023
e89a9a4
feat: add data_struct src
Nov 9, 2023
bd6937e
refacto: factor out linked_hashmap code
Nov 10, 2023
47048f1
feat: add `LinkedVec` data struct
Nov 10, 2023
b025097
docs: write docs for new data structs
Nov 13, 2023
1466ed8
feat: update data struct name and errors
Nov 13, 2023
8d03e81
feat: add prev link for `VersionedHashmap`
Nov 14, 2023
b2af083
feat: add dictionary data struct
Nov 15, 2023
6bc1333
refacto: move vec and free index in inner struct
Nov 15, 2023
dea6e0f
test: add remove and iteration tests
Nov 15, 2023
1b2a8ca
feat: fix `Dict` remove to prevent next insertion order
Nov 15, 2023
4fad2a7
feat: replace `Dimension` HashMap with `Dict`
Nov 15, 2023
7bd0c2c
feat: small optimization for from_iterator
Nov 15, 2023
204bd30
feat: use new struct `Dict` to store attribute in dimensions
Nov 16, 2023
f359590
refacto: improve Dimension encapsulation
Nov 16, 2023
9df31cf
feat: add custom serialization for `Dict`
Nov 16, 2023
c7dc90e
feat: use `VersionedVec` to store USK subkeys
Nov 20, 2023
775d064
feat: implement bfs iter for `VersionedVec`
Nov 20, 2023
7d75c1a
feat: iterate through subkeys using BFS for decaps
Nov 20, 2023
6996475
feat: use `VersionedHashmap` to store MSK subkeys
Nov 20, 2023
da99b83
feat: implement `VersionedVec` creation from Iterator
Nov 21, 2023
30cca34
test: add dict serialization test
Nov 21, 2023
4935d72
feat: store K in `Dict` entries to improve keys iterations efficiency
Nov 22, 2023
3ecaa3c
feat: add macro to serialize/deserialize options
Nov 22, 2023
9f1f870
feat: update secret keys serialization
Nov 22, 2023
e9f2dce
feat: implement deserialization for USK with VersionedVec
Nov 23, 2023
c8d4dd4
feat: adapt current rekey logic to work with new struct
Nov 23, 2023
69a7c37
Update keys data structures
Dec 21, 2023
31e9f5f
refacto: apply review suggestions
Dec 22, 2023
10264d4
doc: add README to detail each data structures
Jan 2, 2024
0ebc698
doc: update benchmarks in README
Jan 2, 2024
9bcc427
fix: false positive identity map warning
Jan 10, 2024
2536a50
refacto: move `refresh` logic to an iterator struct
Jan 11, 2024
5c1a5c3
refacto: use `std::LinkedList` in `RevisinVec` and `RevisionMap`
Jan 12, 2024
61d73f3
feat: use `RefCell` to store User subkeys
Jan 12, 2024
88eb173
refacto: remove clones and clean function signatures
Jan 12, 2024
8460b0d
test: update non regression vector
Jan 12, 2024
d2fbd25
docs: update CHANGELOG and docstring
Jan 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cosmian_cover_crypt"
version = "13.0.0"
version = "13.1.0"
authors = [
"Théophile Brezot <[email protected]>",
"Bruno Grieder <[email protected]>",
Expand Down
17 changes: 10 additions & 7 deletions src/abe_policy/access_policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,8 @@ impl AccessPolicy {
/// given access policy. It is an OR expression of AND expressions.
///
/// - `policy` : global policy
/// - `include_lower_attributes_from_dim` : set to `true` to combine lower attributes
/// - `include_lower_attributes_from_dim` : set to `true` to combine lower
/// attributes
/// from dimension with hierarchical order
ackRow marked this conversation as resolved.
Show resolved Hide resolved
pub fn to_attribute_combinations(
&self,
Expand All @@ -360,12 +361,14 @@ impl AccessPolicy {
.get(&attr.dimension)
.ok_or_else(|| Error::DimensionNotFound(attr.dimension.to_string()))?;
let mut res = vec![vec![attr.clone()]];
if let Some(order) = dim_parameters.order.as_deref() {
if include_lower_attributes_from_dim {
// add attribute values for all attributes below the given one
for name in order.iter().take_while(|&name| name != &attr.name) {
res.push(vec![Attribute::new(&attr.dimension, name)]);
}
if include_lower_attributes_from_dim && dim_parameters.is_ordered() {
// TODO: optimize `Dict` for this step
// add attribute values for all attributes below the given one
for name in dim_parameters
.get_attributes_name()
.take_while(|&name| name != &attr.name)
{
res.push(vec![Attribute::new(&attr.dimension, name)]);
}
}
Ok(res)
Expand Down
255 changes: 152 additions & 103 deletions src/abe_policy/dimension.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::{collections::HashMap, fmt::Debug, vec};
use std::{collections::HashMap, fmt::Debug};

use serde::{Deserialize, Serialize};

use super::{
attribute::{AttributeBuilder, EncryptionHint},
AttributeStatus,
};
use crate::Error;
use crate::{data_struct::Dict, Error};

///
/// Creates a dimension by its name and its underlying attribute properties.
Expand Down Expand Up @@ -69,9 +69,9 @@ impl DimensionBuilder {
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
/// Represents an `Attribute` inside a `Dimension`.
pub struct AttributeParameters {
pub rotation_values: Vec<u32>,
pub encryption_hint: EncryptionHint,
pub write_status: AttributeStatus,
pub(crate) rotation_values: Vec<u32>,
pub(crate) encryption_hint: EncryptionHint,
pub(crate) write_status: AttributeStatus,
}

impl AttributeParameters {
Expand All @@ -94,6 +94,21 @@ impl AttributeParameters {
.expect("Attribute should always have at least one value")
}

pub fn rotate_current_value(&mut self, seed_id: &mut u32) {
*seed_id += 1;
ackRow marked this conversation as resolved.
Show resolved Hide resolved
self.rotation_values.push(*seed_id)
}

pub fn clear_old_rotation_values(&mut self) {
// TODO: use VecDeque ?
let current_val = self.get_current_rotation();
self.rotation_values.retain(|val| val == &current_val);
}

ackRow marked this conversation as resolved.
Show resolved Hide resolved
pub fn all_rotation_values(&self) -> impl '_ + DoubleEndedIterator<Item = u32> {
self.rotation_values.iter().copied()
ackRow marked this conversation as resolved.
Show resolved Hide resolved
}

/// Flattens the properties of the `AttributeParameters` into a vector of
/// tuples where each tuple contains a rotation value, the associated
/// encryption hint, and the `read_only` flag.
Expand All @@ -110,9 +125,9 @@ type AttributeName = String;
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug)]
/// A dimension is a space that holds attributes. It can be ordered (an
/// dimension) or unordered (a set).
pub struct Dimension {
pub order: Option<Vec<AttributeName>>,
pub attributes: HashMap<AttributeName, AttributeParameters>,
pub enum Dimension {
Unordered(HashMap<AttributeName, AttributeParameters>),
Ordered(Dict<AttributeName, AttributeParameters>),
}

impl Dimension {
Expand All @@ -123,33 +138,49 @@ impl Dimension {
///
/// * `dim` - The `DimensionBuilder` to base the dimension on.
/// * `seed_id` - A mutable reference to a seed ID used for generating
/// unique IDs for attributes.
/// unique values for attributes.
pub fn new(dim: &DimensionBuilder, seed_id: &mut u32) -> Self {
let attributes_mapping = dim
ackRow marked this conversation as resolved.
Show resolved Hide resolved
.attributes_properties
.iter()
.map(|attr| {
(
attr.name.clone(),
AttributeParameters::new(attr.encryption_hint, seed_id),
)
})
.collect();
let attributes_mapping = dim.attributes_properties.iter().map(|attr| {
(
attr.name.clone(),
AttributeParameters::new(attr.encryption_hint, seed_id),
)
});

match dim.hierarchical {
true => Self {
order: Some(
dim.attributes_properties
.iter()
.map(|attr| attr.name.clone())
.collect(),
),
attributes: attributes_mapping,
},
false => Self {
order: None,
attributes: attributes_mapping,
},
true => Self::Ordered(attributes_mapping.collect()),
false => Self::Unordered(attributes_mapping.collect()),
}
}

pub fn nb_attributes(&self) -> usize {
match self {
Self::Unordered(attributes) => attributes.len(),
Self::Ordered(attributes) => attributes.len(),
}
}

pub fn is_ordered(&self) -> bool {
match self {
Self::Unordered(_) => false,
Self::Ordered(_) => true,
}
}

/// Returns an iterator over the attributes name.
/// If the dimension is ordered, the names are returned in this order,
/// otherwise they are returned in arbitrary order.
pub fn get_attributes_name(&self) -> Box<dyn '_ + Iterator<Item = &AttributeName>> {
match self {
Self::Unordered(attributes) => Box::new(attributes.keys()),
Self::Ordered(attributes) => Box::new(attributes.keys()),
}
ackRow marked this conversation as resolved.
Show resolved Hide resolved
}

pub fn get_attribute(&self, attr_name: &AttributeName) -> Option<&AttributeParameters> {
match self {
Self::Unordered(attributes) => attributes.get(attr_name),
Self::Ordered(attributes) => attributes.get(attr_name),
}
}

Expand All @@ -169,13 +200,21 @@ impl Dimension {
attr_name: &AttributeName,
seed_id: &mut u32,
) -> Result<(), Error> {
match self.attributes.get_mut(attr_name) {
Some(attr) => {
*seed_id += 1;
attr.rotation_values.push(*seed_id);
Ok(())
}
None => Err(Error::AttributeNotFound(attr_name.to_string())),
match self {
Self::Unordered(attributes) => match attributes.get_mut(attr_name) {
Some(attr) => {
attr.rotate_current_value(seed_id);
Ok(())
}
None => Err(Error::AttributeNotFound(attr_name.to_string())),
},
Self::Ordered(attributes) => match attributes.get_mut(attr_name) {
Some(attr) => {
attr.rotate_current_value(seed_id);
Ok(())
}
None => Err(Error::AttributeNotFound(attr_name.to_string())),
},
}
ackRow marked this conversation as resolved.
Show resolved Hide resolved
}

Expand All @@ -195,20 +234,23 @@ impl Dimension {
encryption_hint: EncryptionHint,
seed_id: &mut u32,
) -> Result<(), Error> {
if self.order.is_some() {
Err(Error::OperationNotPermitted(
match self {
Self::Unordered(attributes) => {
if attributes.contains_key(attr_name) {
Err(Error::OperationNotPermitted(
"Attribute already in dimension".to_string(),
))
} else {
attributes.insert(
attr_name.clone(),
AttributeParameters::new(encryption_hint, seed_id),
ackRow marked this conversation as resolved.
Show resolved Hide resolved
);
Ok(())
}
}
Self::Ordered(_) => Err(Error::OperationNotPermitted(
"Hierarchical dimension are immutable".to_string(),
))
} else if self.attributes.contains_key(attr_name) {
Err(Error::OperationNotPermitted(
"Attribute already in dimension".to_string(),
))
} else {
self.attributes.insert(
attr_name.clone(),
AttributeParameters::new(encryption_hint, seed_id),
);
Ok(())
)),
}
}

Expand All @@ -223,15 +265,14 @@ impl Dimension {
/// Returns an error if the operation is not permitted or if the attribute
/// is not found.
pub fn remove_attribute(&mut self, attr_name: &AttributeName) -> Result<(), Error> {
if self.order.is_some() {
Err(Error::OperationNotPermitted(
"Hierarchical dimension are immutable".to_string(),
))
} else {
self.attributes
match self {
Self::Unordered(attributes) => attributes
.remove(attr_name)
.map(|_| ())
.ok_or(Error::AttributeNotFound(attr_name.to_string()))
ackRow marked this conversation as resolved.
Show resolved Hide resolved
.ok_or(Error::AttributeNotFound(attr_name.to_string())),
Self::Ordered(_) => Err(Error::OperationNotPermitted(
"Hierarchical dimension are immutable".to_string(),
)),
}
}

Expand All @@ -245,10 +286,16 @@ impl Dimension {
///
/// Returns an error if the attribute is not found.
pub fn disable_attribute(&mut self, attr_name: &AttributeName) -> Result<(), Error> {
self.attributes
.get_mut(attr_name)
.map(|attr| attr.write_status = AttributeStatus::DecryptOnly)
.ok_or(Error::AttributeNotFound(attr_name.to_string()))
match self {
Self::Unordered(attributes) => attributes
.get_mut(attr_name)
.map(|attr| attr.write_status = AttributeStatus::DecryptOnly)
.ok_or(Error::AttributeNotFound(attr_name.to_string())),
Self::Ordered(attributes) => attributes
.get_mut(attr_name)
.map(|attr| attr.write_status = AttributeStatus::DecryptOnly)
.ok_or(Error::AttributeNotFound(attr_name.to_string())),
}
}

/// Renames an attribute with a new name.
Expand All @@ -267,24 +314,24 @@ impl Dimension {
attr_name: &AttributeName,
new_name: &str,
) -> Result<(), Error> {
if self.attributes.contains_key(new_name) {
return Err(Error::OperationNotPermitted(
"New attribute name is already used in the same dimension".to_string(),
));
}
match self.attributes.remove(attr_name) {
Some(attr_params) => {
self.attributes.insert(new_name.to_string(), attr_params);
if let Some(order) = self.order.as_mut() {
order.iter_mut().for_each(|name| {
if name == attr_name {
*name = new_name.to_string()
}
})
match self {
Self::Unordered(attributes) => {
if attributes.contains_key(new_name) {
return Err(Error::OperationNotPermitted(
"New attribute name is already used in the same dimension".to_string(),
));
}
match attributes.remove(attr_name) {
Some(attr_params) => {
attributes.insert(new_name.to_string(), attr_params);
Ok(())
ackRow marked this conversation as resolved.
Show resolved Hide resolved
}
None => Err(Error::AttributeNotFound(attr_name.to_string())),
}
Ok(())
}
None => Err(Error::AttributeNotFound(attr_name.to_string())),
Self::Ordered(attributes) => attributes
.update_key(attr_name, new_name.to_string())
.map_err(|e| Error::OperationNotPermitted(e.to_string())),
}
}

Expand All @@ -298,33 +345,35 @@ impl Dimension {
///
/// Returns an error if the attribute is not found.
pub fn clear_old_attribute_values(&mut self, attr_name: &AttributeName) -> Result<(), Error> {
self.attributes
.get_mut(attr_name)
.map(|attr| {
let current_val = attr.get_current_rotation();
attr.rotation_values.retain(|val| val == &current_val);
})
.ok_or(Error::AttributeNotFound(attr_name.to_string()))
match self {
Self::Unordered(attributes) => attributes
.get_mut(attr_name)
.map(|attr| attr.clear_old_rotation_values())
.ok_or(Error::AttributeNotFound(attr_name.to_string())),
Self::Ordered(attributes) => attributes
.get_mut(attr_name)
.map(|attr| attr.clear_old_rotation_values())
.ok_or(Error::AttributeNotFound(attr_name.to_string())),
}
}

/// Returns the list of Attributes of this Policy.
/// Returns an iterator over the AttributesParameters and parameters.
/// If the dimension is ordered, the attributes are returned in order.
pub fn attributes_properties(&self) -> Vec<(String, EncryptionHint)> {
if let Some(ordered_attrs) = &self.order {
ordered_attrs
.iter()
.map(|name| {
(
name.to_string(),
self.attributes.get(name).unwrap().encryption_hint,
)
})
.collect()
} else {
self.attributes
.iter()
.map(|(name, attr_params)| (name.to_string(), attr_params.encryption_hint))
.collect()
pub fn attributes_properties(&self) -> Box<dyn '_ + Iterator<Item = &AttributeParameters>> {
match self {
Self::Unordered(attributes) => Box::new(attributes.values()),
Self::Ordered(attributes) => Box::new(attributes.values()),
}
}

/// Returns an iterator over the Attributes names and parameters.
/// If the dimension is ordered, the attributes are returned in order.
pub fn iter_attributes(
&self,
) -> Box<dyn '_ + Iterator<Item = (&AttributeName, &AttributeParameters)>> {
match self {
Self::Unordered(attributes) => Box::new(attributes.iter()),
Self::Ordered(attributes) => Box::new(attributes.iter()),
}
}
}
4 changes: 2 additions & 2 deletions src/abe_policy/partitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use cosmian_crypto_core::bytes_ser_de::Serializer;
use crate::Error;

/// Partition associated to a subset. It corresponds to a combination
/// of attributes across all axes.
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
/// of attributes across all dimensions.
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Clone, Hash)]
pub struct Partition(pub(crate) Vec<u8>);

impl Partition {
Expand Down
Loading
Loading