Skip to content
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

Add Mutable and Immutable Metadata #17

Merged
merged 18 commits into from
Feb 10, 2024
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- [#1464](https://github.com/openmls/openmls/pull/1464): Add builder pattern for `MlsGroup`; split `MlsGroupJoinConfig` into `MlsGroupCreateConfig` and `MlsGroupJoinConfig`
- [#1473](https://github.com/openmls/openmls/pull/1473): Allow setting group context extensions when building an MlsGroup(Config).
- [#1475](https://github.com/openmls/openmls/pull/1475): Fully process GroupContextExtension proposals
- [#1477](https://github.com/openmls/openmls/pull/1477): Allow setting leaf node extensions and capabilities of the group creator when creating an MlsGroup(Config)
- [#1478](https://github.com/openmls/openmls/pull/1478): Remove explicit functions to set `RequiredCapabilitiesExtension` and `ExternalSendersExtension` when building an MlsGroup(Config) in favor of the more general function to set group context extensions
- [#1479](https://github.com/openmls/openmls/pull/1479): Allow the use of extensions with `ExtensionType::Unknown` in group context, key packages and leaf nodes

## 0.5.0 (XXXX-XX-XX)

Expand Down
2 changes: 2 additions & 0 deletions book/src/message_validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ The following is a list of the individual semantic validation steps performed by
| `ValSem205` | Confirmation tag must be successfully verified | ✅ | ✅ | `openmls/src/group/tests/test_commit_validation.rs` |
| `ValSem206` | Path leaf node encryption key must be unique among proposals & members | ✅ | ✅ | `openmls/src/group/tests/test_commit_validation.rs` |
| `ValSem207` | Path encryption keys must be unique among proposals & members | ✅ | ✅ | `openmls/src/group/tests/test_commit_validation.rs` |
| `ValSem208` | Only one GroupContextExtensions proposal in a commit | ✅ | | |
| `ValSem209` | GroupContextExtensions proposals may only contain extensions support by all members | ✅ | | |

### External Commit message validation

Expand Down
9 changes: 7 additions & 2 deletions book/src/user_manual/group_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ Two very similar structs can help configure groups upon their creation: `MlsGrou

| Name | Type | Explanation |
| ------------------------------ | ------------------------------- | ------------------------------------------------------------------------------------------------ |
| `required_capabilities` | `RequiredCapabilitiesExtension` | Required capabilities (extensions and proposal types). |
| `external_senders` | `ExternalSendersExtensions` | List credentials of non-group members that are allowed to send proposals to the group. |
| `group_context_extensions` | `Extensions` | Optional group-level extensions, e.g. `RequiredCapabilitiesExtension`. |
| `capabilities` . | `Capabilities` | Lists the capabilities of the group's creator. |
| `leaf_extensions` . | `Extensions` | Extensions to be included in the group creator's leaf |

Both ways of group configurations can be specified by using the struct's builder pattern, or choosing their default values. The default value contains safe values for all parameters and is suitable for scenarios without particular requirements.

Expand All @@ -33,3 +34,7 @@ Example create configuration:
```rust,no_run,noplayground
{{#include ../../../openmls/tests/book_code.rs:mls_group_create_config_example}}
```

## Unknown extensions

Some extensions carry data, but don't alter the behaviour of the protocol (e.g. the application_id extension). OpenMLS allows the use of arbitrary such extensions in the group context, key packages and leaf nodes. Such extensions can be instantiated and retrieved through the use of the `UnknownExtension` struct and the `ExtensionType::Unknown` extension type. Such "unknown" extensions are handled transparently by OpenMLS, but can be used by the application, e.g. to have a group agree on pieces of data.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supporting arbitrary extensions in the GroupContext is not RFC compliant. Extensions in the GroupContext need to be supported by every member (ie, also have an indication of support in their capabilities field).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you think that's not RFC compliant?
Indicating support in capabilities if of course required. But MLS does not restrict extensions. In the contrary, implementations are supposed to handle any (unknown) extension gracefully. That's what the grease mechanisms tries to ensure.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It says in section 13.4:

  • Clients must verify they support every extension in a GroupContext. Otherwise, it must refuse to join a group
  • Clients must verify every other client supports extensions in the GroupContext

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the documentation a mis-statement, or does openmls actually allow arbitrary GroupContext extensions?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It allows arbitrary group context extension, as long as all members support them.
As such, it is compliant with the RFC. The difference to other extensions is that the MLS implementation isn't aware of them. But it doesn't need to be as long as they are only relevant for the application. This way using especially private extension (0xF000 - 0xFFFF) becomes much easier, because the protocol implementation doesn't need to be made aware of them.
That said, @keks found an issue in the validation check for this (see openmls#1487). This is part of the larger effort of getting all validation checks into OpeMLS (see the validation label for some of them).

12 changes: 6 additions & 6 deletions openmls/src/extensions/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::extensions::{
UnknownExtension,
};

use super::{last_resort::LastResortExtension, protected_metadata::ProtectedMetadata};
use super::{last_resort::LastResortExtension, metadata::Metadata};

fn vlbytes_len_len(length: usize) -> usize {
if length < 0x40 {
Expand Down Expand Up @@ -37,7 +37,7 @@ impl Size for Extension {
Extension::ExternalPub(e) => e.tls_serialized_len(),
Extension::ExternalSenders(e) => e.tls_serialized_len(),
Extension::LastResort(e) => e.tls_serialized_len(),
Extension::ProtectedMetadata(e) => e.tls_serialized_len(),
Extension::ImmutableMetadata(e) => e.tls_serialized_len(),
Extension::Unknown(_, e) => e.0.len(),
};

Expand Down Expand Up @@ -70,7 +70,7 @@ impl Serialize for Extension {
Extension::ExternalPub(e) => e.tls_serialize(&mut extension_data),
Extension::ExternalSenders(e) => e.tls_serialize(&mut extension_data),
Extension::LastResort(e) => e.tls_serialize(&mut extension_data),
Extension::ProtectedMetadata(e) => e.tls_serialize(&mut extension_data),
Extension::ImmutableMetadata(e) => e.tls_serialize(&mut extension_data),
Extension::Unknown(_, e) => extension_data
.write_all(e.0.as_slice())
.map(|_| e.0.len())
Expand Down Expand Up @@ -120,9 +120,9 @@ impl Deserialize for Extension {
ExtensionType::LastResort => {
Extension::LastResort(LastResortExtension::tls_deserialize(&mut extension_data)?)
}
ExtensionType::ProtectedMetadata => Extension::ProtectedMetadata(
ProtectedMetadata::tls_deserialize(&mut extension_data)?,
),
ExtensionType::ImmutableMetadata => {
Extension::ImmutableMetadata(Metadata::tls_deserialize(&mut extension_data)?)
}
ExtensionType::Unknown(unknown) => {
Extension::Unknown(unknown, UnknownExtension(extension_data.to_vec()))
}
Expand Down
5 changes: 5 additions & 0 deletions openmls/src/extensions/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,9 @@ pub enum InvalidExtensionError {
"The provided extension list contains an extension that is not allowed in group contexts."
)]
IllegalInGroupContext,
/// The provided extension list contains an extension that is not allowed in leaf nodes
#[error(
"The provided extension list contains an extension that is not allowed in leaf nodes."
)]
IllegalInLeafNodes,
}
23 changes: 23 additions & 0 deletions openmls/src/extensions/metadata.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use super::{Deserialize, Serialize};
use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize};

/// Metadata is an extension that keeps arbitrary application-specific metadata, in the form of a
/// byte sequence. The application is responsible for specifying a format and parsing the contents.
#[derive(
PartialEq, Eq, Clone, Debug, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize,
)]
pub struct Metadata {
metadata: Vec<u8>,
}

impl Metadata {
/// Create a new [`Metadata`] extension.
pub fn new(metadata: Vec<u8>) -> Self {
Self { metadata }
}

/// Get the metadata bytes.
pub fn metadata(&self) -> &Vec<u8> {
&self.metadata
}
}
42 changes: 21 additions & 21 deletions openmls/src/extensions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ mod codec;
mod external_pub_extension;
mod external_sender_extension;
mod last_resort;
mod protected_metadata;
mod metadata;
mod ratchet_tree_extension;
mod required_capabilities;
use errors::*;
Expand All @@ -54,7 +54,7 @@ use tls_codec::{
Size, TlsSize,
};

pub use protected_metadata::ProtectedMetadata;
pub use metadata::Metadata;

#[cfg(test)]
mod test_extensions;
Expand Down Expand Up @@ -100,9 +100,9 @@ pub enum ExtensionType {
/// scenario.
LastResort,

/// Protected metadata extension for policies of the group. GroupContext
/// extension
ProtectedMetadata,
/// Immutable metadata extension for the GroupContext.
/// This can only be set on creation of the group.
ImmutableMetadata,

/// A currently unknown extension type.
Unknown(u16),
Expand Down Expand Up @@ -155,7 +155,7 @@ impl From<u16> for ExtensionType {
4 => ExtensionType::ExternalPub,
5 => ExtensionType::ExternalSenders,
10 => ExtensionType::LastResort,
11 => ExtensionType::ProtectedMetadata,
0xf000 => ExtensionType::ImmutableMetadata,
unknown => ExtensionType::Unknown(unknown),
}
}
Expand All @@ -170,7 +170,7 @@ impl From<ExtensionType> for u16 {
ExtensionType::ExternalPub => 4,
ExtensionType::ExternalSenders => 5,
ExtensionType::LastResort => 10,
ExtensionType::ProtectedMetadata => 11,
ExtensionType::ImmutableMetadata => 0xf000,
ExtensionType::Unknown(unknown) => unknown,
}
}
Expand All @@ -187,7 +187,7 @@ impl ExtensionType {
| ExtensionType::ExternalPub
| ExtensionType::ExternalSenders
| ExtensionType::LastResort
| ExtensionType::ProtectedMetadata
| ExtensionType::ImmutableMetadata
)
}
}
Expand Down Expand Up @@ -226,8 +226,8 @@ pub enum Extension {
/// A [`LastResortExtension`]
LastResort(LastResortExtension),

/// A [`ProtectedMetadata`] extension
ProtectedMetadata(ProtectedMetadata),
/// An immutable [`Metadata`] extension
ImmutableMetadata(Metadata),

/// A currently unknown extension.
Unknown(u16, UnknownExtension),
Expand Down Expand Up @@ -420,11 +420,11 @@ impl Extensions {
})
}

/// Get a reference to the [`ProtectedMetadata`] if there is any.
pub fn protected_metadata(&self) -> Option<&ProtectedMetadata> {
self.find_by_type(ExtensionType::ProtectedMetadata)
/// Get a reference to the immutable [`Metadata`] if there is any.
pub fn immutable_metadata(&self) -> Option<&Metadata> {
self.find_by_type(ExtensionType::ImmutableMetadata)
.and_then(|e| match e {
Extension::ProtectedMetadata(e) => Some(e),
Extension::ImmutableMetadata(e) => Some(e),
_ => None,
})
}
Expand Down Expand Up @@ -495,14 +495,14 @@ impl Extension {
}
}

/// Get a reference to this extension as [`ProtectedMetadata`].
/// Get a reference to this extension as immutable [`Metadata`].
/// Returns an [`ExtensionError::InvalidExtensionType`] error if called on
/// an [`Extension`] that's not a [`ProtectedMetadata`] extension.
pub fn as_protected_metadata_extension(&self) -> Result<&ProtectedMetadata, ExtensionError> {
/// an [`Extension`] that's not an immutable [`Metadata`] extension.
pub fn as_immutable_metadata_extension(&self) -> Result<&Metadata, ExtensionError> {
match self {
Self::ProtectedMetadata(e) => Ok(e),
Self::ImmutableMetadata(e) => Ok(e),
_ => Err(ExtensionError::InvalidExtensionType(
"This is not an ProtectedMetadata".into(),
"This is not an immutable metadata extensions".into(),
)),
}
}
Expand All @@ -517,7 +517,7 @@ impl Extension {
Extension::ExternalPub(_) => ExtensionType::ExternalPub,
Extension::ExternalSenders(_) => ExtensionType::ExternalSenders,
Extension::LastResort(_) => ExtensionType::LastResort,
Extension::ProtectedMetadata(_) => ExtensionType::ProtectedMetadata,
Extension::ImmutableMetadata(_) => ExtensionType::ImmutableMetadata,
Extension::Unknown(kind, _) => ExtensionType::Unknown(*kind),
}
}
Expand Down Expand Up @@ -622,7 +622,7 @@ mod test {

#[test]
fn that_unknown_extensions_are_de_serialized_correctly() {
let extension_types = [0x0000u16, 0x0A0A, 0x7A7A, 0xF000, 0xFFFF];
let extension_types = [0x0000u16, 0x0A0A, 0x7A7A, 0xF100, 0xFFFF];
let extension_datas = [vec![], vec![0], vec![1, 2, 3]];

for extension_type in extension_types.into_iter() {
Expand Down
Loading
Loading