use openmls_traits::{
types::{Ciphersuite, HpkeCiphertext},
OpenMlsCryptoProvider,
};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use tls_codec::{TlsByteVecU8, TlsVecU32};
use crate::{
binary_tree::LeafIndex,
ciphersuite::{HpkePrivateKey, HpkePublicKey},
error::LibraryError,
messages::PathSecret,
schedule::CommitSecret,
treesync::{hashes::ParentHashInput, treekem::UpdatePathNode},
};
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct ParentNode {
pub(super) public_key: HpkePublicKey,
pub(super) parent_hash: TlsByteVecU8,
pub(super) unmerged_leaves: TlsVecU32<LeafIndex>,
private_key_option: Option<HpkePrivateKey>,
}
impl From<(HpkePublicKey, HpkePrivateKey)> for ParentNode {
fn from((public_key, private_key): (HpkePublicKey, HpkePrivateKey)) -> Self {
let mut parent_node: ParentNode = public_key.into();
parent_node.set_private_key(private_key);
parent_node
}
}
impl From<HpkePublicKey> for ParentNode {
fn from(public_key: HpkePublicKey) -> Self {
Self {
public_key,
parent_hash: vec![].into(),
unmerged_leaves: vec![].into(),
private_key_option: None,
}
}
}
#[derive(Debug)]
pub(crate) struct PlainUpdatePathNode {
public_key: HpkePublicKey,
path_secret: PathSecret,
}
impl PlainUpdatePathNode {
pub(in crate::treesync) fn encrypt(
&self,
backend: &impl OpenMlsCryptoProvider,
ciphersuite: Ciphersuite,
public_keys: &[HpkePublicKey],
group_context: &[u8],
) -> UpdatePathNode {
let encrypted_path_secrets: Vec<HpkeCiphertext> = public_keys
.par_iter()
.map(|pk| {
self.path_secret
.encrypt(backend, ciphersuite, pk, group_context)
})
.collect();
UpdatePathNode {
public_key: self.public_key.clone(),
encrypted_path_secrets: encrypted_path_secrets.into(),
}
}
pub(in crate::treesync) fn path_secret(&self) -> &PathSecret {
&self.path_secret
}
}
pub(in crate::treesync) type PathDerivationResult =
(Vec<ParentNode>, Vec<PlainUpdatePathNode>, CommitSecret);
impl ParentNode {
pub(super) fn new(
public_key: HpkePublicKey,
parent_hash: TlsByteVecU8,
unmerged_leaves: TlsVecU32<u32>,
) -> Self {
Self {
public_key,
parent_hash,
unmerged_leaves,
private_key_option: None,
}
}
pub(crate) fn derive_path(
backend: &impl OpenMlsCryptoProvider,
ciphersuite: Ciphersuite,
path_secret: PathSecret,
path_length: usize,
) -> Result<PathDerivationResult, LibraryError> {
let mut next_path_secret = path_secret;
let mut path_secrets = Vec::new();
for _ in 0..path_length {
let path_secret = next_path_secret;
next_path_secret = path_secret.derive_path_secret(backend, ciphersuite)?;
path_secrets.push(path_secret);
}
let (path, update_path_nodes) = path_secrets
.into_par_iter()
.map(|path_secret| {
let (public_key, private_key) =
path_secret.derive_key_pair(backend, ciphersuite)?;
let parent_node = ParentNode::from((public_key.clone(), private_key));
let update_path_node = PlainUpdatePathNode {
public_key,
path_secret,
};
Ok((parent_node, update_path_node))
})
.collect::<Result<Vec<(ParentNode, PlainUpdatePathNode)>, LibraryError>>()?
.into_iter()
.unzip();
let commit_secret = next_path_secret.into();
Ok((path, update_path_nodes, commit_secret))
}
pub(crate) fn public_key(&self) -> &HpkePublicKey {
&self.public_key
}
pub(in crate::treesync) fn private_key(&self) -> Option<&HpkePrivateKey> {
self.private_key_option.as_ref()
}
pub(in crate::treesync) fn set_private_key(&mut self, private_key: HpkePrivateKey) {
self.private_key_option = Some(private_key)
}
pub(in crate::treesync) fn unmerged_leaves(&self) -> &[LeafIndex] {
self.unmerged_leaves.as_slice()
}
pub(in crate::treesync) fn add_unmerged_leaf(&mut self, leaf_index: LeafIndex) {
self.unmerged_leaves.push(leaf_index)
}
pub(in crate::treesync) fn compute_parent_hash(
&self,
backend: &impl OpenMlsCryptoProvider,
ciphersuite: Ciphersuite,
parent_hash: &[u8],
original_child_resolution: &[HpkePublicKey],
) -> Result<Vec<u8>, LibraryError> {
let parent_hash_input =
ParentHashInput::new(&self.public_key, parent_hash, original_child_resolution);
parent_hash_input.hash(backend, ciphersuite)
}
pub(in crate::treesync) fn set_parent_hash(&mut self, parent_hash: Vec<u8>) {
self.parent_hash = parent_hash.into()
}
pub(crate) fn parent_hash(&self) -> &[u8] {
self.parent_hash.as_slice()
}
pub(in crate::treesync) fn clone_without_private_key(&self) -> Self {
Self {
public_key: self.public_key().clone(),
parent_hash: self.parent_hash().to_vec().into(),
unmerged_leaves: self.unmerged_leaves().to_vec().into(),
private_key_option: None,
}
}
}