1use openmls_traits::{
38 crypto::OpenMlsCrypto,
39 types::{Ciphersuite, CryptoError, HpkeCiphertext},
40};
41use thiserror::Error;
42use tls_codec::{Serialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize, VLBytes};
43
44use super::LABEL_PREFIX;
45
46#[cfg(feature = "targeted-messages-draft")]
47use crate::error::LibraryError;
48#[cfg(feature = "targeted-messages-draft")]
49use openmls_traits::crypto::HpkeSealPskResolvedAadError;
50
51#[cfg(feature = "extensions-draft")]
52use crate::component::{ComponentId, ComponentOperationLabel};
53
54#[derive(Error, Debug, PartialEq, Clone)]
56pub enum Error {
57 #[error(
59 "Error while serializing content. This should only happen if a bounds check was missing."
60 )]
61 MissingBoundCheck,
62
63 #[error("Decryption failed.")]
65 DecryptionFailed,
66}
67
68impl From<tls_codec::Error> for Error {
69 fn from(_: tls_codec::Error) -> Self {
70 Self::MissingBoundCheck
71 }
72}
73
74impl From<CryptoError> for Error {
75 fn from(_: CryptoError) -> Self {
76 Self::DecryptionFailed
77 }
78}
79
80#[derive(Debug, Clone, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize)]
82pub struct EncryptContext {
83 label: VLBytes,
85 context: VLBytes,
86}
87
88impl EncryptContext {
89 pub(crate) fn new(label: &str, context: VLBytes) -> Self {
92 let label_string = LABEL_PREFIX.to_owned() + label;
93 let label = label_string.as_bytes().into();
94 Self { label, context }
95 }
96
97 #[cfg(feature = "extensions-draft")]
98 pub(crate) fn new_from_component_operation_label(
99 label: ComponentOperationLabel,
100 context: VLBytes,
101 ) -> Result<Self, Error> {
102 let serialized_label = label.tls_serialize_detached()?;
103
104 let mut label = LABEL_PREFIX.as_bytes().to_vec();
108 label.extend(serialized_label);
109
110 Ok(Self {
111 label: label.into(),
112 context,
113 })
114 }
115}
116
117impl From<(&str, &[u8])> for EncryptContext {
118 fn from((label, context): (&str, &[u8])) -> Self {
119 Self::new(label, context.into())
120 }
121}
122
123pub(crate) fn encrypt_with_label(
125 public_key: &[u8],
126 label: &str,
127 context: &[u8],
128 plaintext: &[u8],
129 ciphersuite: Ciphersuite,
130 crypto: &impl OpenMlsCrypto,
131) -> Result<HpkeCiphertext, Error> {
132 let context: EncryptContext = (label, context).into();
133
134 log_crypto!(
135 debug,
136 "HPKE Encrypt with label `{label}` and ciphersuite `{ciphersuite:?}`:"
137 );
138
139 encrypt_with_label_internal(public_key, context, plaintext, ciphersuite, crypto)
140}
141
142fn encrypt_with_label_internal(
143 public_key: &[u8],
144 context: EncryptContext,
145 plaintext: &[u8],
146 ciphersuite: Ciphersuite,
147 crypto: &impl OpenMlsCrypto,
148) -> Result<HpkeCiphertext, Error> {
149 let context = context.tls_serialize_detached()?;
150
151 log_crypto!(debug, "* context: {context:x?}");
152 log_crypto!(debug, "* public key: {public_key:x?}");
153 log_crypto!(debug, "* plaintext: {plaintext:x?}");
154
155 let cipher = crypto.hpke_seal(
156 ciphersuite.hpke_config(),
157 public_key,
158 &context,
159 &[],
160 plaintext,
161 )?;
162
163 log_crypto!(debug, "* ciphertext: {:x?}", cipher);
164
165 Ok(cipher)
166}
167
168#[cfg(feature = "extensions-draft")]
170pub struct SafeEncryptionContext<'a> {
171 pub component_id: ComponentId,
173
174 pub label: &'a str,
176
177 pub context: &'a [u8],
179}
180
181#[cfg(feature = "extensions-draft")]
187pub fn safe_encrypt_with_label(
188 public_key: &[u8],
189 plaintext: &[u8],
190 ciphersuite: Ciphersuite,
191 context: SafeEncryptionContext,
192 crypto: &impl OpenMlsCrypto,
193) -> Result<HpkeCiphertext, Error> {
194 let component_operation_label =
195 ComponentOperationLabel::new(context.component_id, context.label);
196
197 let context = EncryptContext::new_from_component_operation_label(
198 component_operation_label,
199 context.context.into(),
200 )?;
201
202 encrypt_with_label_internal(public_key, context, plaintext, ciphersuite, crypto)
203}
204
205pub(crate) fn decrypt_with_label(
207 private_key: &[u8],
208 label: &str,
209 context: &[u8],
210 ciphertext: &HpkeCiphertext,
211 ciphersuite: Ciphersuite,
212 crypto: &impl OpenMlsCrypto,
213) -> Result<Vec<u8>, Error> {
214 log_crypto!(
215 debug,
216 "HPKE Decrypt with label `{label}` and `ciphersuite` {ciphersuite:?}:"
217 );
218
219 let context: EncryptContext = (label, context).into();
220
221 decrypt_with_label_internal(private_key, context, ciphertext, ciphersuite, crypto)
222}
223
224fn decrypt_with_label_internal(
225 private_key: &[u8],
226 context: EncryptContext,
227 ciphertext: &HpkeCiphertext,
228 ciphersuite: Ciphersuite,
229 crypto: &impl OpenMlsCrypto,
230) -> Result<Vec<u8>, Error> {
231 let context = context.tls_serialize_detached()?;
232
233 log_crypto!(debug, "* context: {context:x?}");
234 log_crypto!(debug, "* private key: {private_key:x?}");
235 log_crypto!(debug, "* ciphertext: {ciphertext:x?}");
236
237 let plaintext = crypto
238 .hpke_open(
239 ciphersuite.hpke_config(),
240 ciphertext,
241 private_key,
242 &context,
243 &[],
244 )
245 .map_err(|e| e.into());
246
247 log_crypto!(debug, "* plaintext: {plaintext:x?}");
248
249 plaintext
250}
251
252#[cfg(feature = "extensions-draft")]
253pub fn safe_decrypt_with_label(
259 private_key: &[u8],
260 ciphertext: &HpkeCiphertext,
261 ciphersuite: Ciphersuite,
262 context: SafeEncryptionContext,
263 crypto: &impl OpenMlsCrypto,
264) -> Result<Vec<u8>, Error> {
265 let component_operation_label =
266 ComponentOperationLabel::new(context.component_id, context.label);
267
268 let context: EncryptContext = EncryptContext::new_from_component_operation_label(
269 component_operation_label,
270 context.context.into(),
271 )?;
272
273 decrypt_with_label_internal(private_key, context, ciphertext, ciphersuite, crypto)
274}
275
276#[cfg(feature = "targeted-messages-draft")]
280pub(crate) struct PskEncryptParams<'a> {
281 pub label: &'a str,
282 pub context: &'a [u8],
283 pub psk: &'a [u8],
284 pub psk_id: &'a [u8],
285 pub ciphersuite: Ciphersuite,
286}
287
288#[cfg(feature = "targeted-messages-draft")]
289pub(crate) fn encrypt_with_label_psk_resolved_aad<F>(
290 public_key: &[u8],
291 params: PskEncryptParams,
292 plaintext: &[u8],
293 crypto: &impl OpenMlsCrypto,
294 aad_builder: F,
295) -> Result<HpkeCiphertext, LibraryError>
296where
297 F: FnOnce(&[u8]) -> Result<Vec<u8>, LibraryError>,
298{
299 let info = EncryptContext::new(params.label, params.context.into())
300 .tls_serialize_detached()
301 .map_err(LibraryError::missing_bound_check)?;
302 crypto
303 .hpke_seal_psk_resolved_aad(
304 params.ciphersuite.hpke_config(),
305 public_key,
306 &info,
307 plaintext,
308 params.psk,
309 params.psk_id,
310 aad_builder,
311 )
312 .map_err(|e| match e {
313 HpkeSealPskResolvedAadError::CryptoError(e) => LibraryError::unexpected_crypto_error(e),
314 HpkeSealPskResolvedAadError::AadBuildError(e) => e,
315 })
316}
317
318#[cfg(feature = "targeted-messages-draft")]
319pub(crate) fn decrypt_with_label_psk_aad(
320 private_key: &[u8],
321 params: PskEncryptParams,
322 aad: &[u8],
323 ciphertext: &HpkeCiphertext,
324 crypto: &impl OpenMlsCrypto,
325) -> Result<Vec<u8>, Error> {
326 let info = EncryptContext::new(params.label, params.context.into()).tls_serialize_detached()?;
327 let content_bytes = crypto.hpke_open_psk(
328 params.ciphersuite.hpke_config(),
329 ciphertext,
330 private_key,
331 &info,
332 aad,
333 params.psk,
334 params.psk_id,
335 )?;
336
337 Ok(content_bytes)
338}