use std::collections::{HashMap, HashSet};
use std::fmt::Display;
use std::marker::PhantomData;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::crypto::hkdf::{HkdfError, hkdf};
use crate::crypto::{Rng, RngError, Secret};
use crate::key_bundle::OneTimeKeyBundle;
use crate::traits::{
AckedGroupMembership, IdentityHandle, IdentityManager, IdentityRegistry, OperationId,
PreKeyManager, PreKeyRegistry,
};
use crate::two_party::{TwoParty, TwoPartyError, TwoPartyMessage, TwoPartyState};
const RATCHET_KEY_SIZE: usize = 32;
pub struct Dcgka<ID, OP, PKI, DGM, KMG> {
_marker: PhantomData<(ID, OP, PKI, DGM, KMG)>,
}
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
pub struct DcgkaState<ID, OP, PKI, DGM, KMG>
where
ID: IdentityHandle,
OP: OperationId,
PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
DGM: AckedGroupMembership<ID, OP>,
KMG: IdentityManager<KMG::State> + PreKeyManager,
{
pub(crate) pki: PKI::State,
pub(crate) my_keys: KMG::State,
pub(crate) my_id: ID,
pub(crate) next_seed: Option<NextSeed>,
pub(crate) two_party: HashMap<ID, TwoPartyState<OneTimeKeyBundle>>,
pub(crate) member_secrets: HashMap<(ID, OP, ID), ChainSecret>,
pub(crate) ratchet: HashMap<ID, ChainSecret>,
pub(crate) dgm: DGM::State,
}
impl<ID, OP, PKI, DGM, KMG> Dcgka<ID, OP, PKI, DGM, KMG>
where
ID: IdentityHandle,
OP: OperationId,
PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
DGM: AckedGroupMembership<ID, OP>,
KMG: IdentityManager<KMG::State> + PreKeyManager,
{
pub fn init(
my_id: ID,
my_keys: KMG::State,
pki: PKI::State,
dgm: DGM::State,
) -> DcgkaState<ID, OP, PKI, DGM, KMG> {
DcgkaState {
pki,
my_id,
my_keys,
next_seed: None,
two_party: HashMap::new(),
member_secrets: HashMap::new(),
ratchet: HashMap::new(),
dgm,
}
}
pub fn process_remote(
y: DcgkaState<ID, OP, PKI, DGM, KMG>,
input: ProcessInput<ID, OP, DGM>,
rng: &Rng,
) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
let ProcessInput {
sender,
seq,
direct_message,
control_message,
} = input;
assert_ne!(sender, y.my_id, "do not process own control messages");
let (y_i, output) = match control_message {
ControlMessage::Create { initial_members } => {
Self::process_create(y, sender, seq, initial_members, direct_message, rng)?
}
ControlMessage::Ack {
ack_sender,
ack_seq,
} => Self::process_ack(y, sender, (&ack_sender, ack_seq), direct_message)?,
ControlMessage::Update => Self::process_update(y, sender, seq, direct_message, rng)?,
ControlMessage::Remove { removed } => {
Self::process_remove(y, sender, seq, &removed, direct_message, rng)?
}
ControlMessage::Add { added } => {
Self::process_add(y, sender, seq, added, direct_message, rng)?
}
ControlMessage::AddAck {
ack_sender,
ack_seq,
} => Self::process_add_ack(y, sender, (&ack_sender, ack_seq), direct_message)?,
};
Ok((y_i, output))
}
pub fn process_local(
y: DcgkaState<ID, OP, PKI, DGM, KMG>,
seq: OP,
input: OperationOutput<ID, OP, DGM>,
rng: &Rng,
) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
let my_id = y.my_id;
let (y_i, output) = match input.control_message {
ControlMessage::Create {
ref initial_members,
} => Self::process_create(y, my_id, seq, initial_members.clone(), None, rng)?,
ControlMessage::Update => Self::process_update(y, my_id, seq, None, rng)?,
ControlMessage::Remove { removed } => {
Self::process_remove(y, my_id, seq, &removed, None, rng)?
}
ControlMessage::Add { added } => Self::process_add(y, my_id, seq, added, None, rng)?,
_ => panic!(
"only call process_local after local create, update, remove or add operations"
),
};
assert!(output.control_message.is_none());
assert!(output.direct_messages.is_empty());
Ok((
y_i,
OperationOutput {
control_message: input.control_message,
direct_messages: input.direct_messages,
me_update_secret: Some(output.sender_update_secret.unwrap()),
},
))
}
pub fn create(
y: DcgkaState<ID, OP, PKI, DGM, KMG>,
initial_members: Vec<ID>,
rng: &Rng,
) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
let mut initial_members: Vec<ID> =
initial_members.into_iter().fold(Vec::new(), |mut acc, id| {
if !acc.contains(&id) {
acc.push(id);
}
acc
});
if !initial_members.contains(&y.my_id) {
initial_members.push(y.my_id);
}
let control_message = ControlMessage::Create {
initial_members: initial_members.clone(),
};
let (y_ii, direct_messages) = Self::generate_seed(y, &initial_members, rng)?;
Ok((
y_ii,
OperationOutput {
control_message,
direct_messages,
me_update_secret: None,
},
))
}
fn process_create(
mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
sender: ID,
seq: OP,
initial_members: Vec<ID>,
direct_message: Option<DirectMessage<ID, OP, DGM>>,
rng: &Rng,
) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
y.dgm =
DGM::create(y.my_id, &initial_members).map_err(|err| DcgkaError::DgmOperation(err))?;
Self::process_seed(y, &sender, seq, direct_message, rng)
}
fn process_ack(
mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
sender: ID,
ack: (&ID, OP),
direct_message: Option<DirectMessage<ID, OP, DGM>>,
) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
if DGM::is_add(&y.dgm, ack.1) && DGM::is_remove(&y.dgm, ack.1) && sender != y.my_id {
y.dgm = DGM::ack(y.dgm, sender, ack.1).map_err(|err| DcgkaError::DgmOperation(err))?;
}
let member_secret = y.member_secrets.remove(&(*ack.0, ack.1, sender));
let (y_i, sender_member_secret) = match (member_secret, direct_message) {
(None, None) => return Ok((y, ProcessOutput::default())),
(Some(member_secret), _) => (y, member_secret),
(
None,
Some(DirectMessage {
recipient,
content: DirectMessageContent::Forward { ciphertext },
..
}),
) => {
if recipient != y.my_id {
return Ok((y, ProcessOutput::default()));
}
let (y_i, plaintext) = Self::decrypt_from(y, &sender, ciphertext)?;
(y_i, ChainSecret::try_from_bytes(&plaintext)?)
}
(None, Some(direct_message)) => {
return Err(DcgkaError::UnexpectedDirectMessageType(
DirectMessageType::Forward,
direct_message.message_type(),
));
}
};
let (y_ii, sender_update_secret) =
Self::update_ratchet(y_i, &sender, sender_member_secret)?;
Ok((
y_ii,
ProcessOutput {
control_message: None,
direct_messages: Vec::new(),
sender_update_secret: Some(sender_update_secret),
me_update_secret: None,
},
))
}
pub fn update(
y: DcgkaState<ID, OP, PKI, DGM, KMG>,
rng: &Rng,
) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
let control_message = ControlMessage::Update;
let recipient_ids: Vec<ID> = Self::member_view(&y, &y.my_id)?
.into_iter()
.filter(|member| member != &y.my_id)
.collect();
let (y_i, direct_messages) = Self::generate_seed(y, &recipient_ids, rng)?;
Ok((
y_i,
OperationOutput {
control_message,
direct_messages,
me_update_secret: None,
},
))
}
fn process_update(
y: DcgkaState<ID, OP, PKI, DGM, KMG>,
sender: ID,
seq: OP,
direct_message: Option<DirectMessage<ID, OP, DGM>>,
rng: &Rng,
) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
Self::process_seed(y, &sender, seq, direct_message, rng)
}
pub fn remove(
y: DcgkaState<ID, OP, PKI, DGM, KMG>,
removed: ID,
rng: &Rng,
) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
let control_message = ControlMessage::Remove { removed };
let recipient_ids: Vec<ID> = Self::member_view(&y, &y.my_id)?
.into_iter()
.filter(|member| member != &y.my_id && member != &removed)
.collect();
let (y_i, direct_messages) = Self::generate_seed(y, &recipient_ids, rng)?;
Ok((
y_i,
OperationOutput {
control_message,
direct_messages,
me_update_secret: None,
},
))
}
fn process_remove(
mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
sender: ID,
seq: OP,
removed: &ID,
direct_message: Option<DirectMessage<ID, OP, DGM>>,
rng: &Rng,
) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
y.dgm = DGM::remove(y.dgm, sender, removed, seq)
.map_err(|err| DcgkaError::DgmOperation(err))?;
Self::process_seed(y, &sender, seq, direct_message, rng)
}
pub fn add(
y: DcgkaState<ID, OP, PKI, DGM, KMG>,
added: ID,
rng: &Rng,
) -> DcgkaOperationResult<ID, OP, PKI, DGM, KMG> {
let control_message = ControlMessage::Add { added };
let (y_i, ciphertext) = {
let chain_secret_bytes = y
.ratchet
.get(&y.my_id)
.ok_or(DcgkaError::MissingRatchetSecret)?
.as_bytes()
.to_vec();
Self::encrypt_to(y, &added, &chain_secret_bytes, rng)?
};
let direct_message = DirectMessage {
recipient: added,
content: DirectMessageContent::Welcome {
ciphertext,
history: {
y_i.dgm.clone()
},
},
};
Ok((
y_i,
OperationOutput {
control_message,
direct_messages: vec![direct_message],
me_update_secret: None,
},
))
}
fn process_add(
mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
sender: ID,
seq: OP,
added: ID,
direct_message: Option<DirectMessage<ID, OP, DGM>>,
rng: &Rng,
) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
if added == y.my_id {
let Some(DirectMessage {
recipient,
content:
DirectMessageContent::Welcome {
ciphertext,
history,
},
..
}) = direct_message
else {
return match direct_message {
Some(direct_message) => Err(DcgkaError::UnexpectedDirectMessageType(
DirectMessageType::Welcome,
direct_message.message_type(),
)),
None => Err(DcgkaError::MissingDirectMessage(DirectMessageType::Welcome)),
};
};
if recipient != y.my_id {
return Err(DcgkaError::NotOurDirectMessage(y.my_id, recipient));
}
return Self::process_welcome(y, sender, seq, history, ciphertext);
}
y.dgm = DGM::add(y.dgm, sender, added, seq).map_err(|err| DcgkaError::DgmOperation(err))?;
let is_concurrent = !Self::member_view(&y, &sender)?
.iter()
.any(|member| member == &y.my_id);
let (y_ii, sender_update_secret) = if is_concurrent {
(y, None)
} else {
let (mut y_i, sender_member_secret) =
Self::update_ratchet(y, &sender, ChainSecret::from_welcome())?;
y_i.member_secrets.insert(
(sender, seq, added),
ChainSecret::from(sender_member_secret),
);
let (y_ii, sender_update_secret) =
Self::update_ratchet(y_i, &sender, ChainSecret::from_add())?;
(y_ii, Some(sender_update_secret))
};
if sender == y_ii.my_id {
return Ok((
y_ii,
ProcessOutput {
control_message: None,
direct_messages: Vec::new(),
sender_update_secret,
me_update_secret: None,
},
));
}
let control = ControlMessage::AddAck {
ack_sender: sender,
ack_seq: seq,
};
let (y_iii, ciphertext) = {
let chain_secret_bytes = y_ii
.ratchet
.get(&y_ii.my_id)
.ok_or(DcgkaError::MissingRatchetSecret)?
.as_bytes()
.to_vec();
Self::encrypt_to(y_ii, &added, &chain_secret_bytes, rng)?
};
let forward = DirectMessage {
recipient: added,
content: DirectMessageContent::Forward { ciphertext },
};
let (y_iv, output) = {
let my_id = y_iii.my_id;
Self::process_add_ack(y_iii, my_id, (&sender, seq), None)?
};
let me_update_secret = output.sender_update_secret;
Ok((
y_iv,
ProcessOutput {
control_message: Some(control),
direct_messages: vec![forward],
sender_update_secret,
me_update_secret,
},
))
}
fn process_add_ack(
mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
sender: ID,
ack: (&ID, OP),
direct_message: Option<DirectMessage<ID, OP, DGM>>,
) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
y.dgm = DGM::ack(y.dgm, sender, ack.1).map_err(|err| DcgkaError::DgmOperation(err))?;
let y_i = if let Some(direct_message) = direct_message {
if let DirectMessage {
recipient,
content: DirectMessageContent::Forward { ciphertext },
..
} = direct_message
{
if recipient != y.my_id {
return Err(DcgkaError::NotOurDirectMessage(y.my_id, recipient));
}
let (mut y_i, plaintext) = Self::decrypt_from(y, &sender, ciphertext)?;
let chain_secret = ChainSecret::try_from_bytes(&plaintext)?;
y_i.ratchet.insert(sender, chain_secret);
y_i
} else {
return Err(DcgkaError::UnexpectedDirectMessageType(
DirectMessageType::Forward,
direct_message.message_type(),
));
}
} else {
y
};
let is_concurrent = !Self::member_view(&y_i, &sender)?
.iter()
.any(|member| member == &y_i.my_id);
if !is_concurrent {
let (y_ii, sender_update_secret) =
Self::update_ratchet(y_i, &sender, ChainSecret::from_add())?;
return Ok((
y_ii,
ProcessOutput {
control_message: None,
direct_messages: Vec::new(),
sender_update_secret: Some(sender_update_secret),
me_update_secret: None,
},
));
}
Ok((y_i, ProcessOutput::default()))
}
fn process_welcome(
mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
sender: ID,
seq: OP,
history: DGM::State,
ciphertext: TwoPartyMessage,
) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
y.dgm = DGM::from_welcome(y.dgm, history).map_err(|err| DcgkaError::DgmOperation(err))?;
y.dgm =
DGM::add(y.dgm, sender, y.my_id, seq).map_err(|err| DcgkaError::DgmOperation(err))?;
let y_i = {
let (mut y_i, plaintext) = Self::decrypt_from(y, &sender, ciphertext)?;
let chain_secret = ChainSecret::try_from_bytes(&plaintext)?;
y_i.ratchet.insert(sender, chain_secret);
y_i
};
let y_ii = {
let (mut y_ii, member_secret) =
Self::update_ratchet(y_i, &sender, ChainSecret::from_welcome())?;
y_ii.member_secrets
.insert((sender, seq, y_ii.my_id), ChainSecret::from(member_secret));
y_ii
};
let (y_iii, sender_update_secret) =
Self::update_ratchet(y_ii, &sender, ChainSecret::from_add())?;
let control = ControlMessage::Ack {
ack_sender: sender,
ack_seq: seq,
};
let (y_iv, output) = {
let my_id = y_iii.my_id;
Self::process_ack(y_iii, my_id, (&sender, seq), None)?
};
let me_update_secret = output.sender_update_secret;
Ok((
y_iv,
ProcessOutput {
control_message: Some(control),
direct_messages: Vec::new(),
sender_update_secret: Some(sender_update_secret),
me_update_secret: Some(
me_update_secret.expect("sender update secret from process_ack"),
),
},
))
}
fn generate_seed(
mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
recipients: &[ID],
rng: &Rng,
) -> GenerateSeedResult<ID, OP, PKI, DGM, KMG> {
let mut direct_messages: Vec<DirectMessage<ID, OP, DGM>> =
Vec::with_capacity(recipients.len());
let next_seed_bytes = rng.random_array()?;
y.next_seed = Some(NextSeed::from_bytes(next_seed_bytes));
let y_i = {
let mut y_loop = y;
for recipient in recipients {
if recipient == &y_loop.my_id {
continue;
}
let (y_next, ciphertext) =
Self::encrypt_to(y_loop, recipient, &next_seed_bytes, rng)?;
y_loop = y_next;
direct_messages.push(DirectMessage {
recipient: *recipient,
content: DirectMessageContent::TwoParty { ciphertext },
});
}
y_loop
};
Ok((y_i, direct_messages))
}
fn process_seed(
mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
sender: &ID,
seq: OP,
direct_message: Option<DirectMessage<ID, OP, DGM>>,
rng: &Rng,
) -> DcgkaProcessResult<ID, OP, PKI, DGM, KMG> {
let recipients: Vec<ID> = Self::member_view(&y, sender)?
.into_iter()
.filter(|member| member != sender)
.collect();
let (mut y_i, next_seed) = if sender == &y.my_id {
let next_seed = y.next_seed.take().expect("seed was generated before"); (y, next_seed)
} else if recipients.iter().any(|member| member == &y.my_id) {
let Some(DirectMessage {
recipient,
content: DirectMessageContent::TwoParty { ciphertext },
..
}) = direct_message
else {
return match direct_message {
Some(direct_message) => Err(DcgkaError::UnexpectedDirectMessageType(
DirectMessageType::TwoParty,
direct_message.message_type(),
)),
None => Err(DcgkaError::MissingDirectMessage(
DirectMessageType::TwoParty,
)),
};
};
if recipient != y.my_id {
return Err(DcgkaError::NotOurDirectMessage(y.my_id, recipient));
}
let (y_i, plaintext) = Self::decrypt_from(y, sender, ciphertext)?;
(y_i, NextSeed::try_from_bytes(&plaintext)?)
} else {
let control = ControlMessage::Ack {
ack_sender: *sender,
ack_seq: seq,
};
return Ok((
y,
ProcessOutput {
control_message: Some(control),
direct_messages: Vec::new(),
sender_update_secret: None,
me_update_secret: None,
},
));
};
for recipient in &recipients {
let recipient_identity_key = PKI::identity_key(&y_i.pki, recipient)
.map_err(|err| DcgkaError::IdentityRegistry(err))?
.ok_or(DcgkaError::MissingIdentityKey(*recipient))?;
let recipient_member_secret: [u8; RATCHET_KEY_SIZE] = hkdf(
b"update",
&{
let mut ikm = Vec::with_capacity(RATCHET_KEY_SIZE * 2);
ikm.extend_from_slice(next_seed.as_bytes());
ikm.extend_from_slice(recipient_identity_key.as_bytes());
ikm
},
None,
)?;
y_i.member_secrets.insert(
(*sender, seq, *recipient),
ChainSecret::from_bytes(recipient_member_secret),
);
}
let (y_ii, sender_update_secret) = {
let sender_identity_key = PKI::identity_key(&y_i.pki, sender)
.map_err(|err| DcgkaError::IdentityRegistry(err))?
.ok_or(DcgkaError::MissingIdentityKey(*sender))?;
let sender_member_secret: [u8; RATCHET_KEY_SIZE] = hkdf(
b"update",
&{
let mut ikm = Vec::with_capacity(RATCHET_KEY_SIZE * 2);
ikm.extend_from_slice(next_seed.as_bytes());
ikm.extend_from_slice(sender_identity_key.as_bytes());
ikm
},
None,
)?;
Self::update_ratchet(y_i, sender, ChainSecret::from_bytes(sender_member_secret))?
};
drop(next_seed);
if sender == &y_ii.my_id {
return Ok((
y_ii,
ProcessOutput {
control_message: None,
direct_messages: Vec::new(),
sender_update_secret: Some(sender_update_secret),
me_update_secret: None,
},
));
}
let control = ControlMessage::Ack {
ack_sender: *sender,
ack_seq: seq,
};
let (y_iii, forward_messages) = {
let members: Vec<ID> = Self::member_view(&y_ii, &y_ii.my_id)?
.into_iter()
.filter(|member| member != sender && !recipients.contains(member))
.collect();
let mut forward_messages = Vec::with_capacity(members.len());
let mut y_loop = y_ii;
for member in members {
let member_secret_bytes = y_loop
.member_secrets
.get(&(*sender, seq, y_loop.my_id))
.ok_or(DcgkaError::MissingMemberSecret(*sender, seq))?
.as_bytes()
.to_vec();
let (y_next, ciphertext) =
Self::encrypt_to(y_loop, &member, &member_secret_bytes, rng)?;
y_loop = y_next;
forward_messages.push(DirectMessage {
recipient: member,
content: DirectMessageContent::Forward { ciphertext },
});
}
(y_loop, forward_messages)
};
let (y_iv, output) = {
let my_id = y_iii.my_id;
Self::process_ack(y_iii, my_id, (sender, seq), None)?
};
let me_update_secret = output.sender_update_secret;
Ok((
y_iv,
ProcessOutput {
control_message: Some(control),
direct_messages: forward_messages,
sender_update_secret: Some(sender_update_secret),
me_update_secret,
},
))
}
fn encrypt_to(
mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
recipient: &ID,
plaintext: &[u8],
rng: &Rng,
) -> DcgkaResult<ID, OP, PKI, DGM, KMG, TwoPartyMessage> {
let y_2sm = match y.two_party.remove(recipient) {
Some(y_2sm) => y_2sm,
None => {
let (pki_i, prekey_bundle) = PKI::key_bundle(y.pki, recipient)
.map_err(|err| DcgkaError::PreKeyRegistry(err))?;
y.pki = pki_i;
let prekey_bundle = prekey_bundle.ok_or(DcgkaError::MissingPreKeys(*recipient))?;
TwoParty::<KMG, OneTimeKeyBundle>::init(prekey_bundle)
}
};
let (y_2sm_i, ciphertext) =
TwoParty::<KMG, OneTimeKeyBundle>::send(y_2sm, &y.my_keys, plaintext, rng)?;
y.two_party.insert(*recipient, y_2sm_i);
Ok((y, ciphertext))
}
fn decrypt_from(
mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
sender: &ID,
ciphertext: TwoPartyMessage,
) -> DcgkaResult<ID, OP, PKI, DGM, KMG, Vec<u8>> {
let y_2sm = match y.two_party.remove(sender) {
Some(y_2sm) => y_2sm,
None => {
let (pki_i, prekey_bundle) = PKI::key_bundle(y.pki, sender)
.map_err(|err| DcgkaError::PreKeyRegistry(err))?;
y.pki = pki_i;
let prekey_bundle = prekey_bundle.ok_or(DcgkaError::MissingPreKeys(*sender))?;
TwoParty::<KMG, OneTimeKeyBundle>::init(prekey_bundle)
}
};
let (y_2sm_i, y_my_keys_i, plaintext) =
TwoParty::<KMG, OneTimeKeyBundle>::receive(y_2sm, y.my_keys, ciphertext)?;
y.my_keys = y_my_keys_i;
y.two_party.insert(*sender, y_2sm_i);
Ok((y, plaintext))
}
fn update_ratchet(
mut y: DcgkaState<ID, OP, PKI, DGM, KMG>,
member: &ID,
member_secret: ChainSecret,
) -> DcgkaResult<ID, OP, PKI, DGM, KMG, UpdateSecret> {
let identity_key = PKI::identity_key(&y.pki, member)
.map_err(|err| DcgkaError::IdentityRegistry(err))?
.ok_or(DcgkaError::MissingIdentityKey(*member))?;
let previous_outer_ratchet_key = y.ratchet.get(member);
let update_secret: [u8; RATCHET_KEY_SIZE] = hkdf(
b"update",
&{
let mut ikm = Vec::with_capacity(RATCHET_KEY_SIZE * 3);
if let Some(previous_outer_ratchet_key) = previous_outer_ratchet_key {
ikm.extend_from_slice(previous_outer_ratchet_key.as_bytes());
}
ikm.extend_from_slice(member_secret.as_bytes());
ikm.extend_from_slice(identity_key.as_bytes());
ikm
},
None,
)?;
let next_outer_ratchet_key: [u8; RATCHET_KEY_SIZE] = hkdf(
b"chain",
&{
let mut ikm = Vec::with_capacity(RATCHET_KEY_SIZE * 3);
if let Some(previous_outer_ratchet_key) = previous_outer_ratchet_key {
ikm.extend_from_slice(previous_outer_ratchet_key.as_bytes());
}
ikm.extend_from_slice(member_secret.as_bytes());
ikm.extend_from_slice(identity_key.as_bytes());
ikm
},
None,
)?;
drop(member_secret); y.ratchet
.insert(*member, ChainSecret::from_bytes(next_outer_ratchet_key));
Ok((y, UpdateSecret::from_bytes(update_secret)))
}
pub fn member_view(
y: &DcgkaState<ID, OP, PKI, DGM, KMG>,
viewer: &ID,
) -> Result<HashSet<ID>, DcgkaError<ID, OP, PKI, DGM, KMG>> {
let members =
DGM::members_view(&y.dgm, viewer).map_err(|err| DcgkaError::MembersView(err))?;
Ok(members)
}
}
pub type GenerateSeedResult<ID, OP, PKI, DGM, KMG> = Result<
(
DcgkaState<ID, OP, PKI, DGM, KMG>,
Vec<DirectMessage<ID, OP, DGM>>,
),
DcgkaError<ID, OP, PKI, DGM, KMG>,
>;
pub type DcgkaResult<ID, OP, PKI, DGM, KMG, T> =
Result<(DcgkaState<ID, OP, PKI, DGM, KMG>, T), DcgkaError<ID, OP, PKI, DGM, KMG>>;
pub type DcgkaProcessResult<ID, OP, PKI, DGM, KMG> =
DcgkaResult<ID, OP, PKI, DGM, KMG, ProcessOutput<ID, OP, DGM>>;
pub type DcgkaOperationResult<ID, OP, PKI, DGM, KMG> =
DcgkaResult<ID, OP, PKI, DGM, KMG, OperationOutput<ID, OP, DGM>>;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ControlMessage<ID, OP> {
Create { initial_members: Vec<ID> },
Ack { ack_sender: ID, ack_seq: OP },
Update,
Remove { removed: ID },
Add { added: ID },
AddAck { ack_sender: ID, ack_seq: OP },
}
impl<ID, OP> Display for ControlMessage<ID, OP> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
ControlMessage::Create { .. } => "create",
ControlMessage::Ack { .. } => "ack",
ControlMessage::Update => "update",
ControlMessage::Remove { .. } => "remove",
ControlMessage::Add { .. } => "add",
ControlMessage::AddAck { .. } => "add_ack",
}
)
}
}
#[derive(Clone, Debug)]
pub struct ProcessInput<ID, OP, DGM>
where
DGM: AckedGroupMembership<ID, OP>,
{
pub seq: OP,
pub sender: ID,
pub control_message: ControlMessage<ID, OP>,
pub direct_message: Option<DirectMessage<ID, OP, DGM>>,
}
#[derive(Debug)]
pub struct ProcessOutput<ID, OP, DGM>
where
DGM: AckedGroupMembership<ID, OP>,
{
pub control_message: Option<ControlMessage<ID, OP>>,
pub direct_messages: Vec<DirectMessage<ID, OP, DGM>>,
pub sender_update_secret: Option<UpdateSecret>,
pub me_update_secret: Option<UpdateSecret>,
}
impl<ID, OP, DGM> Default for ProcessOutput<ID, OP, DGM>
where
DGM: AckedGroupMembership<ID, OP>,
{
fn default() -> Self {
Self {
control_message: None,
direct_messages: Vec::new(),
sender_update_secret: None,
me_update_secret: None,
}
}
}
pub struct OperationOutput<ID, OP, DGM>
where
DGM: AckedGroupMembership<ID, OP>,
{
pub control_message: ControlMessage<ID, OP>,
pub direct_messages: Vec<DirectMessage<ID, OP, DGM>>,
pub me_update_secret: Option<UpdateSecret>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DirectMessage<ID, OP, DGM>
where
DGM: AckedGroupMembership<ID, OP>,
{
pub recipient: ID,
pub content: DirectMessageContent<ID, OP, DGM>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum DirectMessageContent<ID, OP, DGM>
where
DGM: AckedGroupMembership<ID, OP>,
{
Welcome {
ciphertext: TwoPartyMessage,
history: DGM::State,
},
TwoParty {
ciphertext: TwoPartyMessage,
},
Forward {
ciphertext: TwoPartyMessage,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum DirectMessageType {
Welcome,
TwoParty,
Forward,
}
impl Display for DirectMessageType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
DirectMessageType::Welcome => "welcome",
DirectMessageType::TwoParty => "2sm",
DirectMessageType::Forward => "forward",
}
)
}
}
impl<ID, OP, DGM> DirectMessage<ID, OP, DGM>
where
DGM: AckedGroupMembership<ID, OP>,
{
pub fn message_type(&self) -> DirectMessageType {
match self.content {
DirectMessageContent::Welcome { .. } => DirectMessageType::Welcome,
DirectMessageContent::TwoParty { .. } => DirectMessageType::TwoParty,
DirectMessageContent::Forward { .. } => DirectMessageType::Forward,
}
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
pub struct NextSeed(Secret<RATCHET_KEY_SIZE>);
impl NextSeed {
pub fn from_bytes(bytes: [u8; RATCHET_KEY_SIZE]) -> Self {
Self(Secret::from_bytes(bytes))
}
pub fn try_from_bytes<ID, OP, PKI, DGM, KMG>(
bytes: &[u8],
) -> Result<Self, DcgkaError<ID, OP, PKI, DGM, KMG>>
where
PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
DGM: AckedGroupMembership<ID, OP>,
KMG: PreKeyManager,
{
let bytes: [u8; RATCHET_KEY_SIZE] =
bytes.try_into().map_err(|_| DcgkaError::InvalidKeySize)?;
Ok(Self::from_bytes(bytes))
}
pub(crate) fn as_bytes(&self) -> &[u8; RATCHET_KEY_SIZE] {
self.0.as_bytes()
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
pub struct ChainSecret(Secret<RATCHET_KEY_SIZE>);
impl ChainSecret {
pub fn from_welcome() -> Self {
Self::from_bytes([
168, 234, 241, 118, 147, 12, 137, 47, 48, 26, 61, 243, 183, 11, 158, 143, 99, 219, 142,
131, 41, 18, 245, 167, 132, 195, 241, 26, 89, 106, 154, 134,
])
}
pub fn from_add() -> Self {
Self::from_bytes([
58, 3, 204, 193, 45, 117, 68, 208, 41, 238, 11, 13, 169, 250, 180, 215, 22, 4, 43, 226,
179, 34, 182, 188, 85, 49, 221, 39, 150, 98, 220, 156,
])
}
pub fn from_bytes(bytes: [u8; RATCHET_KEY_SIZE]) -> Self {
Self(Secret::from_bytes(bytes))
}
pub fn try_from_bytes<ID, OP, PKI, DGM, KEY>(
bytes: &[u8],
) -> Result<Self, DcgkaError<ID, OP, PKI, DGM, KEY>>
where
PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
DGM: AckedGroupMembership<ID, OP>,
KEY: PreKeyManager,
{
let bytes: [u8; RATCHET_KEY_SIZE] =
bytes.try_into().map_err(|_| DcgkaError::InvalidKeySize)?;
Ok(Self::from_bytes(bytes))
}
pub(crate) fn as_bytes(&self) -> &[u8; RATCHET_KEY_SIZE] {
self.0.as_bytes()
}
}
impl From<UpdateSecret> for ChainSecret {
fn from(value: UpdateSecret) -> Self {
Self(value.0)
}
}
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(any(test, feature = "test_utils"), derive(Clone))]
pub struct UpdateSecret(Secret<RATCHET_KEY_SIZE>);
impl UpdateSecret {
pub fn from_bytes(bytes: [u8; RATCHET_KEY_SIZE]) -> Self {
Self(Secret::from_bytes(bytes))
}
pub fn try_from_bytes<ID, OP, PKI, DGM, KMG>(
bytes: &[u8],
) -> Result<Self, DcgkaError<ID, OP, PKI, DGM, KMG>>
where
PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
DGM: AckedGroupMembership<ID, OP>,
KMG: PreKeyManager,
{
let bytes: [u8; RATCHET_KEY_SIZE] =
bytes.try_into().map_err(|_| DcgkaError::InvalidKeySize)?;
Ok(Self::from_bytes(bytes))
}
#[cfg(any(test, feature = "test_utils"))]
pub fn as_bytes(&self) -> &[u8; RATCHET_KEY_SIZE] {
self.0.as_bytes()
}
}
impl From<UpdateSecret> for Secret<RATCHET_KEY_SIZE> {
fn from(update_secret: UpdateSecret) -> Self {
update_secret.0
}
}
#[derive(Debug, Error)]
pub enum DcgkaError<ID, OP, PKI, DGM, KMG>
where
PKI: IdentityRegistry<ID, PKI::State> + PreKeyRegistry<ID, OneTimeKeyBundle>,
DGM: AckedGroupMembership<ID, OP>,
KMG: PreKeyManager,
{
#[error("the given key does not match the required 32 byte length")]
InvalidKeySize,
#[error("expected ratchet secret but couldn't find anything")]
MissingRatchetSecret,
#[error("expected message secret for {0} at seq {1} but couldn't find anything")]
MissingMemberSecret(ID, OP),
#[error("expected direct message of type \"{0}\" but got nothing instead")]
MissingDirectMessage(DirectMessageType),
#[error("expected direct message of type \"{0}\" but got message of type \"{1}\" instead")]
UnexpectedDirectMessageType(DirectMessageType, DirectMessageType),
#[error("direct message recipient mismatch, expected recipient: {1}, actual recipient: {0}")]
NotOurDirectMessage(ID, ID),
#[error("computing members view from dgm failed: {0}")]
MembersView(DGM::Error),
#[error("dgm operation failed: {0}")]
DgmOperation(DGM::Error),
#[error("failed retrieving bundle from pre key registry: {0}")]
PreKeyRegistry(<PKI as PreKeyRegistry<ID, OneTimeKeyBundle>>::Error),
#[error("failed retrieving identity from registry: {0}")]
IdentityRegistry(<PKI as IdentityRegistry<ID, PKI::State>>::Error),
#[error("missing key bundle for member {0}")]
MissingPreKeys(ID),
#[error("missing identity key for member {0}")]
MissingIdentityKey(ID),
#[error(transparent)]
Rng(#[from] RngError),
#[error(transparent)]
KeyManager(KMG::Error),
#[error(transparent)]
TwoParty(#[from] TwoPartyError),
#[error(transparent)]
Hdkf(#[from] HkdfError),
}