1use crate::{
2 SensitiveBytes,
3 defs::{CiphersuiteId, LeafIndex, ProposalType, ProtocolVersion},
4 group::{GroupId, extensions::Extension},
5 key_package::KeyPackage,
6 key_schedule::{GroupContext, PreSharedKeyId},
7 tree::leaf_node::LeafNode,
8};
9
10#[derive(
11 Debug,
12 Clone,
13 PartialEq,
14 Eq,
15 tls_codec::TlsSerialize,
16 tls_codec::TlsDeserialize,
17 tls_codec::TlsSize,
18)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[repr(u16)]
21pub enum Proposal {
22 #[tls_codec(discriminant = "ProposalType::ADD")]
23 Add(AddProposal),
24 #[tls_codec(discriminant = "ProposalType::UPDATE")]
25 Update(UpdateProposal),
26 #[tls_codec(discriminant = "ProposalType::REMOVE")]
27 Remove(RemoveProposal),
28 #[tls_codec(discriminant = "ProposalType::PSK")]
29 PreSharedKey(PreSharedKeyProposal),
30 #[tls_codec(discriminant = "ProposalType::REINIT")]
31 ReInit(ReInitProposal),
32 #[tls_codec(discriminant = "ProposalType::EXTERNAL_INIT")]
33 ExternalInit(ExternalInitProposal),
34 #[tls_codec(discriminant = "ProposalType::GROUP_CONTEXT_EXTENSIONS")]
35 GroupContextExtensions(GroupContextExtensionsProposal),
36 #[cfg(feature = "draft-ietf-mls-extensions")]
37 #[tls_codec(discriminant = "ProposalType::APP_DATA_UPDATE")]
38 AppDataUpdate(crate::drafts::mls_extensions::safe_application::AppDataUpdate),
39 #[cfg(feature = "draft-ietf-mls-extensions")]
40 #[tls_codec(discriminant = "ProposalType::APP_EPHEMERAL")]
41 AppEphemeral(crate::drafts::mls_extensions::safe_application::AppEphemeral),
42 #[cfg(feature = "draft-ietf-mls-extensions")]
43 #[tls_codec(discriminant = "ProposalType::SELF_REMOVE")]
44 SelfRemove(crate::drafts::mls_extensions::self_remove::SelfRemoveProposal),
45}
46
47impl Proposal {
48 #[inline(always)]
49 pub fn proposal_type(&self) -> ProposalType {
50 self.into()
51 }
52}
53
54impl From<&Proposal> for ProposalType {
55 fn from(val: &Proposal) -> Self {
56 match val {
57 Proposal::Add(_) => ProposalType::new_unchecked(ProposalType::ADD),
58 Proposal::Update(_) => ProposalType::new_unchecked(ProposalType::UPDATE),
59 Proposal::Remove(_) => ProposalType::new_unchecked(ProposalType::REMOVE),
60 Proposal::PreSharedKey(_) => ProposalType::new_unchecked(ProposalType::PSK),
61 Proposal::ReInit(_) => ProposalType::new_unchecked(ProposalType::REINIT),
62 Proposal::ExternalInit(_) => ProposalType::new_unchecked(ProposalType::EXTERNAL_INIT),
63 Proposal::GroupContextExtensions(_) => {
64 ProposalType::new_unchecked(ProposalType::GROUP_CONTEXT_EXTENSIONS)
65 }
66 #[cfg(feature = "draft-ietf-mls-extensions")]
67 Proposal::AppDataUpdate(_) => {
68 ProposalType::new_unchecked(ProposalType::APP_DATA_UPDATE)
69 }
70 #[cfg(feature = "draft-ietf-mls-extensions")]
71 Proposal::AppEphemeral(_) => ProposalType::new_unchecked(ProposalType::APP_EPHEMERAL),
72 #[cfg(feature = "draft-ietf-mls-extensions")]
73 Proposal::SelfRemove(_) => ProposalType::new_unchecked(ProposalType::SELF_REMOVE),
74 }
75 }
76}
77
78impl Proposal {
79 #[inline]
80 pub fn needs_update_path(&self) -> bool {
81 self.proposal_type().needs_update_path()
82 }
83}
84
85#[derive(
86 Debug,
87 Clone,
88 PartialEq,
89 Eq,
90 tls_codec::TlsSerialize,
91 tls_codec::TlsDeserialize,
92 tls_codec::TlsSize,
93)]
94#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
95pub struct AddProposal {
96 pub key_package: KeyPackage,
97}
98
99#[derive(
100 Debug,
101 Clone,
102 PartialEq,
103 Eq,
104 tls_codec::TlsSerialize,
105 tls_codec::TlsDeserialize,
106 tls_codec::TlsSize,
107)]
108#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
109pub struct UpdateProposal {
110 pub leaf_node: LeafNode,
111}
112
113#[derive(
114 Debug,
115 Clone,
116 PartialEq,
117 Eq,
118 tls_codec::TlsSerialize,
119 tls_codec::TlsDeserialize,
120 tls_codec::TlsSize,
121)]
122#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
123pub struct RemoveProposal {
124 pub removed: LeafIndex,
125}
126
127#[derive(
128 Debug,
129 Clone,
130 PartialEq,
131 Eq,
132 tls_codec::TlsSerialize,
133 tls_codec::TlsDeserialize,
134 tls_codec::TlsSize,
135)]
136#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
137pub struct PreSharedKeyProposal {
138 pub psk: PreSharedKeyId,
139}
140
141#[derive(
142 Debug,
143 Clone,
144 PartialEq,
145 Eq,
146 tls_codec::TlsSerialize,
147 tls_codec::TlsDeserialize,
148 tls_codec::TlsSize,
149)]
150#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
151pub struct ReInitProposal {
152 pub group_id: GroupId,
153 pub version: ProtocolVersion,
154 pub cipher_suite: CiphersuiteId,
155 pub extensions: Vec<Extension>,
156}
157
158impl ReInitProposal {
159 pub fn matches_group_context(&self, ctx: &GroupContext) -> bool {
160 self.group_id == ctx.group_id()
161 && self.version == ctx.version
162 && self.cipher_suite == ctx.cipher_suite
163 && self.extensions == ctx.extensions
164 }
165}
166
167#[derive(
168 Debug,
169 Clone,
170 PartialEq,
171 Eq,
172 tls_codec::TlsSerialize,
173 tls_codec::TlsDeserialize,
174 tls_codec::TlsSize,
175)]
176#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
177pub struct ExternalInitProposal {
178 pub kem_output: SensitiveBytes,
179}
180
181#[derive(
182 Debug,
183 Clone,
184 PartialEq,
185 Eq,
186 tls_codec::TlsSerialize,
187 tls_codec::TlsDeserialize,
188 tls_codec::TlsSize,
189)]
190#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
191pub struct GroupContextExtensionsProposal {
192 pub extensions: Vec<Extension>,
193}