Skip to content

Commit

Permalink
chore: Merge branch 'release/v13.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Manuthor committed Nov 6, 2023
2 parents bd37ec7 + 3befcc0 commit 0c1dcc8
Show file tree
Hide file tree
Showing 20 changed files with 1,652 additions and 507 deletions.
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,32 @@

All notable changes to this project will be documented in this file.

## [13.0.0] - 2023-11-06

### Bug Fixes

- KMAC compute deterministic & Policy edit edge case (#117)

### Features

- Add KMAC to attest the authenticity of user keys (#114) and make policy editable (#115):
* In Covercrypt, we have the following properties:

the number of attribute values grows with the number of attribute modifications performed: rotations add a new value for an existing attribute while attribute creations add a new attribute with a new value;
the number of partitions is equal to the number of combinations of attribute values that can be created by using one value associated to an attribute from each axis;
the number of keys in each master key is equal to the number of partitions that can be created using their associated policy.

* Hence, if a great number of attributes are created or a great number of rotations are performed, the size of both the policy and the master keys will grow drastically.

* To prevent this, we need to allow dropping attribute values from the policy:

removing an attribute from a policy axis could prevent the number of attributes from growing too big;
retaining only a given number of values per attribute could allow purging the policy from old rotated attribute values.

* Then a master key update should synchronize the master keys with the updated policy.

* **Note**: this is not a problem for user secret keys since they generally hold a small subset of the policy rights; they also can be purged from old sub-keys at each refresh by setting the keep_old_rights parameter to false which prevents rotations from rendering them unmanageable.

## [12.0.3] - 2023-09-18

### Features
Expand Down
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cosmian_cover_crypt"
version = "12.0.3"
version = "13.0.0"
authors = [
"Théophile Brezot <[email protected]>",
"Bruno Grieder <[email protected]>",
Expand Down Expand Up @@ -33,7 +33,7 @@ cosmian_crypto_core = { version = "9.2.0", default-features = false, features =
pqc_kyber = { version = "0.4", features = ["std", "hazmat"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tiny-keccak = { version = "2.0.2", features = ["shake"] }
tiny-keccak = { version = "2.0.2", features = ["shake", "kmac"] }
zeroize = "1.6.0"

[dev-dependencies]
Expand Down
94 changes: 68 additions & 26 deletions benches/benches.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
use cosmian_cover_crypt::{
abe_policy::{AccessPolicy, EncryptionHint, Policy, PolicyAxis},
abe_policy::{AccessPolicy, Attribute, DimensionBuilder, EncryptionHint, Policy},
Covercrypt, EncryptedHeader, Error,
};
use criterion::{criterion_group, criterion_main, Criterion};
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};

// Policy settings
fn policy() -> Result<Policy, Error> {
#[cfg(not(feature = "hybridized_bench"))]
let (security_level, department) = {
(
PolicyAxis::new(
DimensionBuilder::new(
"Security Level",
vec![
("Protected", EncryptionHint::Classic),
Expand All @@ -18,7 +18,7 @@ fn policy() -> Result<Policy, Error> {
],
true,
),
PolicyAxis::new(
DimensionBuilder::new(
"Department",
vec![
("R&D", EncryptionHint::Classic),
Expand All @@ -34,7 +34,7 @@ fn policy() -> Result<Policy, Error> {
#[cfg(feature = "hybridized_bench")]
let (security_level, department) = {
(
PolicyAxis::new(
DimensionBuilder::new(
"Security Level",
vec![
("Protected", EncryptionHint::Hybridized),
Expand All @@ -43,7 +43,7 @@ fn policy() -> Result<Policy, Error> {
],
true,
),
PolicyAxis::new(
DimensionBuilder::new(
"Department",
vec![
("R&D", EncryptionHint::Hybridized),
Expand All @@ -56,12 +56,54 @@ fn policy() -> Result<Policy, Error> {
),
)
};
let mut policy = Policy::new(100);
policy.add_axis(security_level)?;
policy.add_axis(department)?;
let mut policy = Policy::new();
policy.add_dimension(security_level)?;
policy.add_dimension(department)?;
Ok(policy)
}

fn bench_policy_editing(c: &mut Criterion) {
let cover_crypt = Covercrypt::default();
let new_dep_attr = Attribute::new("Department", "Tech");
let new_dep_name = "IT";
let remove_dep_attr = Attribute::new("Department", "FIN");
let old_sl_attr = Attribute::new("Security Level", "Protected");
let new_sl_name = "Open";
let disable_sl_attr = Attribute::new("Security Level", "Confidential");

let mut group = c.benchmark_group("Edit Policy");
//for (n_partition, access_policy) in access_policies.iter().enumerate() {
group.bench_function("edit policy", |b| {
b.iter_batched(
|| {
let policy = policy().expect("cannot generate policy");

let (msk, mpk) = cover_crypt
.generate_master_keys(&policy)
.expect("cannot generate master keys");
(policy, msk, mpk)
},
|(mut policy, mut msk, mut mpk)| {
policy
.add_attribute(new_dep_attr.clone(), EncryptionHint::Classic)
.unwrap();
policy
.rename_attribute(&new_dep_attr, new_dep_name)
.unwrap();
policy.remove_attribute(&remove_dep_attr).unwrap();

policy.rename_attribute(&old_sl_attr, new_sl_name).unwrap();
policy.disable_attribute(&disable_sl_attr).unwrap();

cover_crypt
.update_master_keys(&policy, &mut msk, &mut mpk)
.unwrap();
},
BatchSize::SmallInput,
);
});
}

/// Generate access policies up to 5 partitions along with a user access policy
/// that allows decrypting headers for all these access policies.
///
Expand Down Expand Up @@ -197,23 +239,22 @@ fn bench_serialization(c: &mut Criterion) {
);
}

let mut group = c.benchmark_group("Key serialization");
group.bench_function("MSK", |b| {
b.iter(|| msk.serialize().expect("cannot serialize msk"));
});
group.bench_function("MPK", |b| {
b.iter(|| mpk.serialize().expect("cannot serialize mpk"));
});

let usk = cover_crypt
.generate_user_secret_key(&msk, &user_access_policies[0], &policy)
.unwrap();
group.bench_function("USK 1 partition", |b| {
b.iter(|| usk.serialize().expect("cannot serialize usk"));
});
{
let mut group = c.benchmark_group("Key serialization");
group.bench_function("MSK", |b| {
b.iter(|| msk.serialize().expect("cannot serialize msk"));
});
group.bench_function("MPK", |b| {
b.iter(|| mpk.serialize().expect("cannot serialize mpk"));
});

// removes borrow checker warning about several mutable reference on `c`
drop(group);
let usk = cover_crypt
.generate_user_secret_key(&msk, &user_access_policies[0], &policy)
.unwrap();
group.bench_function("USK 1 partition", |b| {
b.iter(|| usk.serialize().expect("cannot serialize usk"));
});
}

let mut group = c.benchmark_group("Header serialization");
for (n_partition, access_policy) in access_policies.iter().enumerate() {
Expand Down Expand Up @@ -328,6 +369,7 @@ criterion_group!(
name = benches;
config = Criterion::default().sample_size(5000);
targets =
bench_policy_editing,
bench_header_encryption,
bench_header_decryption
);
Expand All @@ -336,7 +378,7 @@ criterion_group!(
criterion_group!(
name = benches_serialization;
config = Criterion::default().sample_size(5000);
targets = bench_serialization
targets = bench_serialization,
);

#[cfg(feature = "full_bench")]
Expand Down
12 changes: 6 additions & 6 deletions examples/runme.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! This is the demo given in `README.md` and `lib.rs`
use cosmian_cover_crypt::{
abe_policy::{AccessPolicy, Attribute, EncryptionHint, Policy, PolicyAxis},
abe_policy::{AccessPolicy, Attribute, DimensionBuilder, EncryptionHint, Policy},
Covercrypt, EncryptedHeader,
};

Expand All @@ -10,7 +10,7 @@ fn main() {
// This axis is hierarchical, i.e. users matching
// `Security Level::Confidential` can also decrypt
// messages encrypted for `Security Level::Protected`.
let sec_level = PolicyAxis::new(
let sec_level = DimensionBuilder::new(
"Security Level",
vec![
("Protected", EncryptionHint::Classic),
Expand All @@ -22,7 +22,7 @@ fn main() {

// Another attribute axis will be department names.
// This axis is *not* hierarchical.
let department = PolicyAxis::new(
let department = DimensionBuilder::new(
"Department",
vec![
("R&D", EncryptionHint::Classic),
Expand All @@ -34,11 +34,11 @@ fn main() {
);

// Generate a new `Policy` object with a 100 revocations allowed.
let mut policy = Policy::new(100);
let mut policy = Policy::new();

// Add the two generated axes to the policy
policy.add_axis(sec_level).unwrap();
policy.add_axis(department).unwrap();
policy.add_dimension(sec_level).unwrap();
policy.add_dimension(department).unwrap();

// Setup Covercrypt and generate master keys
let cover_crypt = Covercrypt::default();
Expand Down
51 changes: 25 additions & 26 deletions src/abe_policy/access_policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::{
};

use crate::{
abe_policy::{policy::Policy, Attribute},
abe_policy::{Attribute, Policy},
Error,
};

Expand Down Expand Up @@ -38,7 +38,7 @@ impl AccessPolicy {
///
/// Shortcut for
/// ```ignore
/// AccessPolicy::Attr(Attribute::new(axis, attribute))
/// AccessPolicy::Attr(Attribute::new(dimension, attribute))
/// ```
///
/// Access Policies can easily be created using it
Expand All @@ -48,8 +48,8 @@ impl AccessPolicy {
/// & (AccessPolicy::new("Department", "MKG") | AccessPolicy::new("Department", "FIN"));
/// ```
#[must_use]
pub fn new(axis: &str, attribute: &str) -> Self {
Self::Attr(Attribute::new(axis, attribute))
pub fn new(dimension: &str, attribute: &str) -> Self {
Self::Attr(Attribute::new(dimension, attribute))
}

/// Converts policy to integer value (for comparison).
Expand Down Expand Up @@ -100,7 +100,7 @@ impl AccessPolicy {
}

/// Sanitizes spaces in boolean expression around parenthesis and operators
/// but keep spaces inside axis & attribute names.
/// but keep spaces inside dimension & attribute names.
///
/// Useless spaces are removed:
/// - before and after operator. Example: `A && B` --> `A&&B`
Expand Down Expand Up @@ -291,7 +291,7 @@ impl AccessPolicy {
|| attribute_vec[1].is_empty()
{
return Err(Error::InvalidBooleanExpression(format!(
"'{boolean_expression}' does not respect the format <axis::name>. \
"'{boolean_expression}' does not respect the format <dimension::name>. \
Example: {boolean_expression_example}"
)));
}
Expand Down Expand Up @@ -345,37 +345,36 @@ impl AccessPolicy {
/// Returns the list of attribute combinations that can be built from the
/// given access policy. It is an OR expression of AND expressions.
///
/// - `policy` : global policy
/// - `follow_hierarchical_axes` : set to `true` to combine lower axis
/// attributes
/// - `policy` : global policy
/// - `include_lower_attributes_from_dim` : set to `true` to combine lower attributes
/// from dimension with hierarchical order
pub fn to_attribute_combinations(
&self,
policy: &Policy,
follow_hierarchical_axes: bool,
include_lower_attributes_from_dim: bool,
) -> Result<Vec<Vec<Attribute>>, Error> {
match self {
Self::Attr(attr) => {
let axis_parameters = policy
.axes
.get(&attr.axis)
.ok_or_else(|| Error::AxisNotFound(attr.axis.to_string()))?;
let dim_parameters = policy
.dimensions
.get(&attr.dimension)
.ok_or_else(|| Error::DimensionNotFound(attr.dimension.to_string()))?;
let mut res = vec![vec![attr.clone()]];
if axis_parameters.is_hierarchical && follow_hierarchical_axes {
// add attribute values for all attributes below the given one
for name in &axis_parameters.attribute_names {
if *name == attr.name {
break;
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)]);
}
res.push(vec![Attribute::new(&attr.axis, name)]);
}
}
Ok(res)
}
Self::And(ap_left, ap_right) => {
let combinations_left =
ap_left.to_attribute_combinations(policy, follow_hierarchical_axes)?;
let combinations_right =
ap_right.to_attribute_combinations(policy, follow_hierarchical_axes)?;
ap_left.to_attribute_combinations(policy, include_lower_attributes_from_dim)?;
let combinations_right = ap_right
.to_attribute_combinations(policy, include_lower_attributes_from_dim)?;
let mut res =
Vec::with_capacity(combinations_left.len() * combinations_right.len());
for value_left in combinations_left {
Expand All @@ -390,9 +389,9 @@ impl AccessPolicy {
}
Self::Or(ap_left, ap_right) => {
let combinations_left =
ap_left.to_attribute_combinations(policy, follow_hierarchical_axes)?;
let combinations_right =
ap_right.to_attribute_combinations(policy, follow_hierarchical_axes)?;
ap_left.to_attribute_combinations(policy, include_lower_attributes_from_dim)?;
let combinations_right = ap_right
.to_attribute_combinations(policy, include_lower_attributes_from_dim)?;
let mut res =
Vec::with_capacity(combinations_left.len() + combinations_right.len());
res.extend(combinations_left);
Expand Down
Loading

0 comments on commit 0c1dcc8

Please sign in to comment.