Skip to content

WIP: End-to-end testing of encryption/decryption with concurrent CGKA ops #75

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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 keyhive_core/benches/bench_cgka.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use keyhive_core::{
use divan::Bencher;
use keyhive_core::{
cgka::{
error::CgkaError,
test_utils::{
Expand Down
31 changes: 31 additions & 0 deletions keyhive_core/src/cgka.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ impl Cgka {
let current_pcs_key = if !self.has_pcs_key() {
let new_share_secret_key = ShareSecretKey::generate(csprng);
let new_share_key = new_share_secret_key.share_key();
println!("!@ new_app_secret update op for {}", self.owner_id);
let (pcs_key, update_op) = self.update(new_share_key, new_share_secret_key, csprng)?;
self.insert_pcs_key(&pcs_key, Digest::hash(&update_op));
op = Some(update_op);
Expand Down Expand Up @@ -163,17 +164,21 @@ impl Cgka {
&mut self,
encrypted: &EncryptedContent<T, Cr>,
) -> Result<SymmetricKey, CgkaError> {
println!("!@ decryption_key_for 0");
let pcs_key =
self.pcs_key_from_hashes(&encrypted.pcs_key_hash, &encrypted.pcs_update_op_hash)?;
println!("!@ decryption_key_for 1");
if !self.pcs_keys.contains_key(&encrypted.pcs_key_hash) {
self.insert_pcs_key(&pcs_key, encrypted.pcs_update_op_hash);
}
println!("!@ decryption_key_for 2");
let app_secret = pcs_key.derive_application_secret(
&encrypted.nonce,
&encrypted.content_ref,
&encrypted.pred_refs,
&encrypted.pcs_update_op_hash,
);
println!("!@ decryption_key_for 3");
Ok(app_secret.key())
}

Expand All @@ -192,6 +197,7 @@ impl Cgka {
if self.tree.contains_id(&id) {
return Ok(None);
}
println!("!@ Calling Cgka::add()");
if self.should_replay() {
self.replay_ops_graph()?;
}
Expand Down Expand Up @@ -227,6 +233,7 @@ impl Cgka {
if !self.tree.contains_id(&id) {
return Ok(None);
}
println!("!@ Calling Cgka::remove()");
if self.should_replay() {
self.replay_ops_graph()?;
}
Expand Down Expand Up @@ -254,6 +261,7 @@ impl Cgka {
new_sk: ShareSecretKey,
csprng: &mut R,
) -> Result<(PcsKey, CgkaOperation), CgkaError> {
println!("!@ Calling Cgka::update()");
if self.should_replay() {
self.replay_ops_graph()?;
}
Expand All @@ -269,6 +277,7 @@ impl Cgka {
predecessors,
doc_id: self.doc_id,
};
println!("!@ -- Op Digest: {}", Digest::hash(&op));
self.ops_graph.add_local_op(&op);
self.insert_pcs_key(&pcs_key, Digest::hash(&op));
Ok((pcs_key, op))
Expand All @@ -289,6 +298,11 @@ impl Cgka {
/// membership changes and we receive a concurrent update, we can apply it
/// immediately.
pub fn merge_concurrent_operation(&mut self, op: Rc<CgkaOperation>) -> Result<(), CgkaError> {
println!(
"\nMerging operation at {}: {:?}\n",
self.owner_id,
op.name()
);
if self.ops_graph.contains_op_hash(&Digest::hash(op.borrow())) {
return Ok(());
}
Expand All @@ -315,6 +329,10 @@ impl Cgka {
Ok(())
}

pub fn ops(&self) -> Result<NonEmpty<CgkaEpoch>, CgkaError> {
self.ops_graph.topsort_graph()
}

/// Apply a [`CgkaOperation`].
fn apply_operation(&mut self, op: Rc<CgkaOperation>) -> Result<(), CgkaError> {
if self.ops_graph.contains_op_hash(&Digest::hash(op.borrow())) {
Expand Down Expand Up @@ -397,15 +415,20 @@ impl Cgka {
pcs_key_hash: &Digest<PcsKey>,
update_op_hash: &Digest<CgkaOperation>,
) -> Result<PcsKey, CgkaError> {
println!("pcs_key_from_hashes 0");
if let Some(pcs_key) = self.pcs_keys.get(pcs_key_hash) {
println!("pcs_key_from_hashes DONE");
Ok(*pcs_key.clone())
} else {
if self.has_pcs_key() {
println!("pcs_key_from_hashes tree root");
let pcs_key = self.pcs_key_from_tree_root()?;
println!("pcs_key_from_hashes tree root FOUND");
if &Digest::hash(&pcs_key) == pcs_key_hash {
return Ok(pcs_key);
}
}
println!("pcs_key_from_hashes derive pcs key");
self.derive_pcs_key_for_op(update_op_hash)
}
}
Expand All @@ -415,12 +438,15 @@ impl Cgka {
&mut self,
op_hash: &Digest<CgkaOperation>,
) -> Result<PcsKey, CgkaError> {
println!("!@ derive_pcs_key_for_op 0");
if !self.ops_graph.contains_op_hash(op_hash) {
return Err(CgkaError::UnknownPcsKey);
}
println!("!@ derive_pcs_key_for_op 1");
let mut heads = HashSet::new();
heads.insert(*op_hash);
let ops = self.ops_graph.topsort_for_heads(&heads)?;
println!("!@ derive_pcs_key_for_op 2");
self.rebuild_pcs_key(ops)
}

Expand Down Expand Up @@ -459,12 +485,17 @@ impl Cgka {
epochs.last()[0].borrow(),
&CgkaOperation::Update { .. }
));
println!("rebuild_pcs_key 0");
let mut rebuilt_cgka =
Cgka::new(self.doc_id, self.original_member.0, self.original_member.1)?
.with_new_owner(self.owner_id, self.owner_sks.clone())?;
println!("rebuild_pcs_key 1");
rebuilt_cgka.apply_epochs(&epochs)?;
println!("rebuild_pcs_key 2");
let pcs_key = rebuilt_cgka.pcs_key_from_tree_root()?;
println!("rebuild_pcs_key 3");
self.insert_pcs_key(&pcs_key, Digest::hash(&epochs.last()[0]));
println!("rebuild_pcs_key 4");
Ok(pcs_key)
}

Expand Down
3 changes: 3 additions & 0 deletions keyhive_core/src/cgka/beekem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ impl BeeKem {
sks: &mut ShareKeyMap,
csprng: &mut R,
) -> Result<Option<(PcsKey, PathChange)>, CgkaError> {
println!("!@ encrypt_path by {}: pk {}", id, pk);
let leaf_idx = *self.leaf_index_for_id(id)?;
debug_assert!(self.id_for_leaf(leaf_idx).unwrap() == id);
let mut new_path = PathChange {
Expand Down Expand Up @@ -330,6 +331,8 @@ impl BeeKem {
/// Applies a [`PathChange`] representing new public and encrypted secret keys for each
/// node on a path.
pub(crate) fn apply_path(&mut self, new_path: &PathChange) {
println!("!@ apply_path received from {}", new_path.leaf_id);

// If this id has been concurrently removed, it might no longer be present
// when we try to apply the concurrent update at that id.
if !self.id_to_leaf_idx.contains_key(&new_path.leaf_id) {
Expand Down
17 changes: 16 additions & 1 deletion keyhive_core/src/cgka/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use topological_sort::TopologicalSort;

/// An ordered [`NonEmpty`] of concurrent [`CgkaOperation`]s.
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct CgkaEpoch(NonEmpty<Rc<CgkaOperation>>);
pub struct CgkaEpoch(NonEmpty<Rc<CgkaOperation>>);

impl From<NonEmpty<Rc<CgkaOperation>>> for CgkaEpoch {
fn from(item: NonEmpty<Rc<CgkaOperation>>) -> Self {
Expand Down Expand Up @@ -61,6 +61,15 @@ pub enum CgkaOperation {
}

impl CgkaOperation {
// FIXME: Remove
pub(crate) fn name(&self) -> String {
match self {
CgkaOperation::Add { .. } => String::from("Add"),
CgkaOperation::Remove { .. } => String::from("Remove"),
CgkaOperation::Update { .. } => String::from("Update"),
}
}

/// The zero or more immediate causal predecessors of this operation.
pub(crate) fn predecessors(&self) -> HashSet<Digest<CgkaOperation>> {
match self {
Expand Down Expand Up @@ -135,11 +144,13 @@ impl CgkaOperationGraph {

/// Add an operation that was created locally to the graph.
pub(crate) fn add_local_op(&mut self, op: &CgkaOperation) {
println!("!@ OpsGraph: adding local {}", Digest::hash(op));
self.add_op_and_update_heads(op, None);
}

/// Add an operation to the graph.
pub(crate) fn add_op(&mut self, op: &CgkaOperation, heads: &HashSet<Digest<CgkaOperation>>) {
println!("!@ OpsGraph: adding remote {}", Digest::hash(op));
self.add_op_and_update_heads(op, Some(heads));
}

Expand Down Expand Up @@ -211,6 +222,7 @@ impl CgkaOperationGraph {
&self,
heads: &HashSet<Digest<CgkaOperation>>,
) -> Result<NonEmpty<CgkaEpoch>, CgkaError> {
println!("!@ topsort_for_heads 0");
debug_assert!(heads.iter().all(|head| self.cgka_ops.contains_key(head)));
let mut op_hashes = Vec::new();
let mut dependencies = TopologicalSort::<Digest<CgkaOperation>>::new();
Expand All @@ -225,9 +237,11 @@ impl CgkaOperationGraph {
}
// Populate dependencies and successors with all ancestors of the initial heads.
while let Some(op_hash) = frontier.pop_front() {
println!("!@ topsort_for_heads loopy op_hash: {}", op_hash);
let preds = self
.predecessors_for(&op_hash)
.ok_or(CgkaError::OperationNotFound)?;
println!("!@ -- preds: {:?}", preds);
for update_pred in preds {
dependencies.add_dependency(*update_pred, op_hash);
successors.entry(*update_pred).or_default().insert(op_hash);
Expand Down Expand Up @@ -304,6 +318,7 @@ impl CgkaOperationGraph {
}
}
for hash in next_set {
println!("!@ topsort_for_heads pushy");
next_epoch.push(
self.cgka_ops
.get(&hash)
Expand Down
Loading