Skip to content
Merged
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
6 changes: 6 additions & 0 deletions mls-rs-core/src/group/proposal_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,10 @@ impl ProposalType {
ProposalType::EXTERNAL_INIT,
ProposalType::GROUP_CONTEXT_EXTENSIONS,
];

/// Determines if this proposal type is required to be implemented
/// by the MLS RFC.
pub fn is_default(&self) -> bool {
Self::DEFAULT.contains(self)
}
}
35 changes: 35 additions & 0 deletions mls-rs/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,8 @@ pub enum MlsError {
ExporterDeleted,
#[cfg_attr(feature = "std", error("Self-remove already proposed"))]
SelfRemoveAlreadyProposed,
#[cfg_attr(feature = "std", error("Default value listed"))]
DefaultValueListed,
}

impl IntoAnyError for MlsError {
Expand Down Expand Up @@ -490,6 +492,11 @@ where
)
.await?;

key_pkg_gen
.key_package
.leaf_node
.validate_no_default_values_listed()?;

let (id, key_package_data) = key_pkg_gen.to_storage()?;

self.config
Expand Down Expand Up @@ -1257,4 +1264,32 @@ mod tests {
let res = bob.validate_group_info(&group_info, &other_signer).await;
assert_matches!(res, Err(MlsError::InvalidSignature));
}

#[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]

async fn cannot_list_default_extensions_in_capabilities() {
let res = TestClientBuilder::new_for_test()
.with_random_signing_identity("client", TEST_CIPHER_SUITE)
.await
.extension_type(ExtensionType::APPLICATION_ID)
.build()
.generate_key_package(Default::default(), Default::default(), Default::default())
.await;

assert_matches!(res, Err(MlsError::DefaultValueListed));
}

#[maybe_async::test(not(mls_build_async), async(mls_build_async, crate::futures_test))]

async fn cannot_list_default_proposals_in_capabilities() {
let res = TestClientBuilder::new_for_test()
.with_random_signing_identity("client", TEST_CIPHER_SUITE)
.await
.custom_proposal_type(ProposalType::ADD)
.build()
.generate_key_package(Default::default(), Default::default(), Default::default())
.await;

assert_matches!(res, Err(MlsError::DefaultValueListed));
}
}
24 changes: 24 additions & 0 deletions mls-rs/src/tree_kem/leaf_node_validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,37 @@ impl<'a, C: IdentityProvider, CP: CipherSuiteProvider> LeafNodeValidator<'a, C,
.map_or(Ok(()), Err)?;
}

leaf_node.validate_no_default_values_listed()?;

#[cfg(feature = "by_ref_proposal")]
self.validate_external_senders_ext_credentials(leaf_node)?;

Ok(())
}
}

impl LeafNode {
pub fn validate_no_default_values_listed(&self) -> Result<(), MlsError> {
// The following proposal and extension types are considered "default" and
// MUST NOT be listed
self.capabilities
.extensions
.iter()
.all(|ext| !ext.is_default())
.then_some(())
.ok_or(MlsError::DefaultValueListed)?;

self.capabilities
.proposals
.iter()
.all(|prop| !prop.is_default())
.then_some(())
.ok_or(MlsError::DefaultValueListed)?;

Ok(())
}
}

#[cfg(test)]
mod tests {
use crate::client::test_utils::TEST_PROTOCOL_VERSION;
Expand Down
Loading