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