1use openmls_traits::{
4 crypto::OpenMlsCrypto, signatures::Signer, storage::StorageProvider, types::Ciphersuite,
5 OpenMlsProvider,
6};
7use tls_codec::Serialize;
8
9use crate::{
10 binary_tree::LeafNodeIndex,
11 components::{
12 vc_derivation_info::{
13 load_vc_epoch_state_and_tree, merge_vc_derivation_info,
14 require_newest_vc_derivation_epoch, resolve_vc_leaf_dictionary, DerivationInfo,
15 DerivationInfoTbe, EpochEncryptionKey, EpochId, KeyPackageInfo, OperationSecret,
16 VirtualClientOperationType, VirtualClientsError,
17 },
18 vc_operation_tree::OperationSecretTree,
19 },
20 credentials::CredentialWithKey,
21 extensions::AppDataDictionary,
22 group::GroupId,
23 key_packages::{
24 errors::KeyPackageNewError, KeyPackage, KeyPackageBuilder, KeyPackageBundle,
25 KeyPackageLeafNodeParams,
26 },
27};
28
29#[derive(Debug)]
33pub struct VcKeyPackageBatch {
34 pub epoch_id: EpochId,
41 pub generation: u32,
43 pub key_packages: Vec<(
45 KeyPackageBundle,
46 crate::components::vc_derivation_info::KeyPackageInfo,
47 )>,
48}
49
50#[derive(Debug)]
56pub struct VcKeyPackageBatchBuilder {
57 epoch_id: EpochId,
58 emulation_ciphersuite: Ciphersuite,
60 epoch_encryption_key: EpochEncryptionKey,
61 emulation_leaf_index: LeafNodeIndex,
62 generation: u32,
63 operation_secret: OperationSecret,
64 operation_tree: OperationSecretTree,
68 key_packages: Vec<(KeyPackageBundle, KeyPackageInfo)>,
69}
70
71impl VcKeyPackageBatchBuilder {
72 pub fn new(
83 provider: &impl OpenMlsProvider,
84 emulation_group_id: &GroupId,
85 ) -> Result<Self, KeyPackageNewError> {
86 Self::with_capacity(provider, emulation_group_id, 0)
87 }
88
89 pub fn with_capacity(
91 provider: &impl OpenMlsProvider,
92 emulation_group_id: &GroupId,
93 capacity: usize,
94 ) -> Result<Self, KeyPackageNewError> {
95 let epoch_id = require_newest_vc_derivation_epoch(provider.storage(), emulation_group_id)?;
96 Self::with_capacity_at_epoch(provider, epoch_id, capacity)
97 }
98
99 pub(crate) fn with_capacity_at_epoch(
102 provider: &impl OpenMlsProvider,
103 epoch_id: EpochId,
104 capacity: usize,
105 ) -> Result<Self, KeyPackageNewError> {
106 let (state, mut operation_tree) = load_vc_epoch_state_and_tree(provider, &epoch_id)?;
107 let (emulation_leaf_index, epoch_encryption_key, emulation_ciphersuite) =
108 state.into_parts();
109 let (generation, operation_secret) = operation_tree.next_operation_secret(
110 provider.crypto(),
111 emulation_ciphersuite,
112 &epoch_id,
113 emulation_leaf_index,
114 VirtualClientOperationType::KeyPackage,
115 b"",
116 )?;
117 Ok(Self {
118 epoch_id,
119 emulation_ciphersuite,
120 epoch_encryption_key,
121 emulation_leaf_index,
122 generation,
123 operation_secret,
124 operation_tree,
125 key_packages: Vec::with_capacity(capacity),
126 })
127 }
128
129 pub fn add_key_package(
140 &mut self,
141 builder: KeyPackageBuilder,
142 ciphersuite: Ciphersuite,
143 crypto: &impl OpenMlsCrypto,
144 signer: &impl Signer,
145 credential_with_key: CredentialWithKey,
146 ) -> Result<&KeyPackageInfo, KeyPackageNewError> {
147 if ciphersuite.signature_algorithm() != signer.signature_scheme() {
148 return Err(KeyPackageNewError::CiphersuiteSignatureSchemeMismatch);
149 }
150
151 crypto
152 .supports(ciphersuite)
153 .map_err(|_| KeyPackageNewError::UnsupportedCiphersuite(ciphersuite))?;
154
155 let resolved_dictionary = resolve_vc_leaf_dictionary(
157 builder.leaf_node_capabilities.as_ref(),
158 builder.leaf_node_extensions.as_ref(),
159 None,
160 )?;
161
162 let key_package_index = self.key_packages.len() as u32;
163 self.key_packages.push(self.build_vc_key_package_for_index(
164 builder,
165 ciphersuite,
166 crypto,
167 signer,
168 credential_with_key,
169 &resolved_dictionary,
170 key_package_index,
171 )?);
172
173 let (_, info) = self.key_packages.last().expect("logic error: just pushed");
174 Ok(info)
175 }
176
177 pub fn finalize(
183 self,
184 provider: &impl OpenMlsProvider,
185 ) -> Result<VcKeyPackageBatch, KeyPackageNewError> {
186 if self.key_packages.is_empty() {
187 return Err(KeyPackageNewError::EmptyBatch);
188 }
189
190 provider
195 .storage()
196 .write_vc_operation_tree(&self.epoch_id, &self.operation_tree)
197 .map_err(|e| {
198 log::error!("vc: persist advanced operation tree in build_vc_batch failed: {e:?}");
199 VirtualClientsError::StorageError
200 })?;
201 for (full_kp, info) in &self.key_packages {
202 provider
203 .storage()
204 .write_key_package(&info.key_package_ref, full_kp)
205 .map_err(|_| KeyPackageNewError::StorageError)?;
206 }
207
208 Ok(VcKeyPackageBatch {
209 epoch_id: self.epoch_id,
210 generation: self.generation,
211 key_packages: self.key_packages,
212 })
213 }
214
215 #[expect(clippy::too_many_arguments)]
217 fn build_vc_key_package_for_index(
218 &self,
219 mut builder: KeyPackageBuilder,
220 ciphersuite: Ciphersuite,
221 crypto: &impl OpenMlsCrypto,
222 signer: &impl Signer,
223 credential_with_key: CredentialWithKey,
224 resolved_dictionary: &AppDataDictionary,
225 key_package_index: u32,
226 ) -> Result<(KeyPackageBundle, KeyPackageInfo), KeyPackageNewError> {
227 let seed = self.operation_secret.derive_key_package_seed_secret(
228 crypto,
229 ciphersuite,
230 key_package_index,
231 )?;
232 let init_key_pair = seed
233 .derive_init_key_secret(crypto, ciphersuite)?
234 .generate_init_key_pair(crypto, ciphersuite)?;
235 let encryption_key_pair = seed
236 .derive_encryption_key_secret(crypto, ciphersuite)?
237 .generate_encryption_key_pair(crypto, ciphersuite)?;
238
239 let leaf_encryption_key = encryption_key_pair
243 .public_key()
244 .tls_serialize_detached()
245 .map_err(VirtualClientsError::from)?;
246 let tbe = DerivationInfoTbe::KeyPackage {
247 leaf_index: self.emulation_leaf_index,
248 generation: self.generation,
249 key_package_index,
250 };
251 let derivation_info = DerivationInfo::encrypt(
252 crypto,
253 self.emulation_ciphersuite,
254 &self.epoch_encryption_key,
255 self.epoch_id.clone(),
256 &leaf_encryption_key,
257 &tbe,
258 )?;
259 let derivation_info_bytes = derivation_info
260 .tls_serialize_detached()
261 .map_err(VirtualClientsError::from)?;
262 builder.ensure_last_resort();
263 let leaf_node_extensions = merge_vc_derivation_info(
264 builder.leaf_node_extensions.as_ref(),
265 resolved_dictionary.clone(),
266 derivation_info_bytes,
267 )
268 .map_err(KeyPackageNewError::LibraryError)?;
269
270 let leaf_node_params = KeyPackageLeafNodeParams {
271 lifetime: builder.key_package_lifetime.unwrap_or_default(),
272 capabilities: builder.leaf_node_capabilities.unwrap_or_default(),
273 extensions: leaf_node_extensions,
274 };
275 let (key_package, encryption_key_pair) = KeyPackage::new_from_vc_keys(
276 ciphersuite,
277 signer,
278 credential_with_key,
279 builder.key_package_extensions.unwrap_or_default(),
280 leaf_node_params,
281 init_key_pair.public.into(),
282 encryption_key_pair,
283 )?;
284
285 let key_package_ref = key_package.hash_ref(crypto)?;
286 let full_kp = KeyPackageBundle {
287 key_package,
288 private_init_key: init_key_pair.private,
289 private_encryption_key: encryption_key_pair.private_key().clone(),
290 };
291
292 Ok((
293 full_kp,
294 KeyPackageInfo {
295 key_package_ref,
296 cipher_suite: ciphersuite,
297 key_package_index,
298 },
299 ))
300 }
301}