de_mls/mls_crypto/service/
openmls.rs1use std::sync::Arc;
8
9use openmls::{
10 group::{
11 GroupId, MlsGroup, MlsGroupCreateConfig, MlsGroupJoinConfig, StagedCommit, StagedWelcome,
12 },
13 key_packages::KeyPackage,
14 prelude::{DeserializeBytes, MlsMessageBodyIn, MlsMessageIn},
15};
16use openmls_rust_crypto::RustCrypto;
17use openmls_traits::OpenMlsProvider;
18
19use crate::mls_crypto::{
20 DeMlsStorage, KeyPackageBytes, MlsCredentials, MlsError,
21 service::{CIPHERSUITE, backend::MlsProvider},
22};
23
24pub struct OpenMlsService<S: DeMlsStorage> {
29 pub(super) storage: S,
30 pub(super) crypto: RustCrypto,
31 pub(super) credentials: Arc<MlsCredentials>,
32 pub(super) conversation_id: String,
33 pub(super) group: MlsGroup,
34 pub(super) pending_staged_commit: Option<StagedCommit>,
35}
36
37impl<S: DeMlsStorage> OpenMlsService<S> {
38 pub fn new_as_creator(
40 conversation_id: String,
41 storage: S,
42 credentials: Arc<MlsCredentials>,
43 ) -> Result<Self, MlsError> {
44 let crypto = RustCrypto::default();
45 let group = {
46 let provider = MlsProvider::new(&crypto, storage.mls_storage());
47 let config = MlsGroupCreateConfig::builder()
48 .use_ratchet_tree_extension(true)
49 .build();
50 MlsGroup::new_with_group_id(
51 &provider,
52 credentials.signer(),
53 &config,
54 GroupId::from_slice(conversation_id.as_bytes()),
55 credentials.credential().clone(),
56 )?
57 };
58
59 Ok(Self {
60 storage,
61 crypto,
62 credentials,
63 conversation_id,
64 group,
65 pending_staged_commit: None,
66 })
67 }
68
69 pub fn new_from_welcome(
76 welcome_bytes: &[u8],
77 storage: S,
78 credentials: Arc<MlsCredentials>,
79 ) -> Result<Option<Self>, MlsError> {
80 let crypto = RustCrypto::default();
81
82 let (mls_message, _) = MlsMessageIn::tls_deserialize_bytes(welcome_bytes)?;
83 let welcome = match mls_message.extract() {
84 MlsMessageBodyIn::Welcome(w) => w,
85 _ => return Ok(None),
86 };
87
88 let is_for_us = welcome.secrets().iter().any(|s| {
89 storage
90 .is_our_key_package(s.new_member().as_slice())
91 .unwrap_or(false)
92 });
93 if !is_for_us {
94 return Ok(None);
95 }
96
97 for secret in welcome.secrets() {
98 storage.remove_key_package_ref(secret.new_member().as_slice())?;
99 }
100
101 let group = {
102 let provider = MlsProvider::new(&crypto, storage.mls_storage());
103 let config = MlsGroupJoinConfig::builder()
104 .use_ratchet_tree_extension(true)
105 .build();
106 StagedWelcome::new_from_welcome(&provider, &config, welcome, None)?
107 .into_group(&provider)?
108 };
109
110 let conversation_id = String::from_utf8_lossy(group.group_id().as_slice()).to_string();
111 Ok(Some(Self {
112 storage,
113 crypto,
114 credentials,
115 conversation_id,
116 group,
117 pending_staged_commit: None,
118 }))
119 }
120
121 pub fn generate_key_package(
128 storage: &S,
129 credentials: &MlsCredentials,
130 ) -> Result<KeyPackageBytes, MlsError> {
131 let crypto = RustCrypto::default();
132 let provider = MlsProvider::new(&crypto, storage.mls_storage());
133
134 let kp_bundle = KeyPackage::builder().build(
135 CIPHERSUITE,
136 &provider,
137 credentials.signer(),
138 credentials.credential().clone(),
139 )?;
140
141 let kp = kp_bundle.key_package();
142 let hash_ref = kp.hash_ref(provider.crypto())?.as_slice().to_vec();
143 let bytes = serde_json::to_vec(kp).map_err(MlsError::InvalidJson)?;
144
145 storage.store_key_package_ref(&hash_ref)?;
146
147 let identity_bytes = credentials
148 .credential()
149 .credential
150 .serialized_content()
151 .to_vec();
152
153 Ok(KeyPackageBytes::new(bytes, identity_bytes))
154 }
155}