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 = "extensions-draft-08")]
47use crate::component::{ComponentId, ComponentOperationLabel};
48
49#[derive(Error, Debug, PartialEq, Clone)]
51pub enum Error {
52 #[error(
54 "Error while serializing content. This should only happen if a bounds check was missing."
55 )]
56 MissingBoundCheck,
57
58 #[error("Decryption failed.")]
60 DecryptionFailed,
61}
62
63impl From<tls_codec::Error> for Error {
64 fn from(_: tls_codec::Error) -> Self {
65 Self::MissingBoundCheck
66 }
67}
68
69impl From<CryptoError> for Error {
70 fn from(_: CryptoError) -> Self {
71 Self::DecryptionFailed
72 }
73}
74
75#[derive(Debug, Clone, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize)]
77pub struct EncryptContext {
78 label: VLBytes,
80 context: VLBytes,
81}
82
83impl EncryptContext {
84 pub(crate) fn new(label: &str, context: VLBytes) -> Self {
87 let label_string = LABEL_PREFIX.to_owned() + label;
88 let label = label_string.as_bytes().into();
89 Self { label, context }
90 }
91
92 #[cfg(feature = "extensions-draft-08")]
93 pub(crate) fn new_from_component_operation_label(
94 label: ComponentOperationLabel,
95 context: VLBytes,
96 ) -> Result<Self, Error> {
97 let serialized_label = label.tls_serialize_detached()?;
98
99 let mut label = LABEL_PREFIX.as_bytes().to_vec();
103 label.extend(serialized_label);
104
105 Ok(Self {
106 label: label.into(),
107 context,
108 })
109 }
110}
111
112impl From<(&str, &[u8])> for EncryptContext {
113 fn from((label, context): (&str, &[u8])) -> Self {
114 Self::new(label, context.into())
115 }
116}
117
118pub(crate) fn encrypt_with_label(
120 public_key: &[u8],
121 label: &str,
122 context: &[u8],
123 plaintext: &[u8],
124 ciphersuite: Ciphersuite,
125 crypto: &impl OpenMlsCrypto,
126) -> Result<HpkeCiphertext, Error> {
127 let context: EncryptContext = (label, context).into();
128
129 log_crypto!(
130 debug,
131 "HPKE Encrypt with label `{label}` and ciphersuite `{ciphersuite:?}`:"
132 );
133
134 encrypt_with_label_internal(public_key, context, plaintext, ciphersuite, crypto)
135}
136
137fn encrypt_with_label_internal(
138 public_key: &[u8],
139 context: EncryptContext,
140 plaintext: &[u8],
141 ciphersuite: Ciphersuite,
142 crypto: &impl OpenMlsCrypto,
143) -> Result<HpkeCiphertext, Error> {
144 let context = context.tls_serialize_detached()?;
145
146 log_crypto!(debug, "* context: {context:x?}");
147 log_crypto!(debug, "* public key: {public_key:x?}");
148 log_crypto!(debug, "* plaintext: {plaintext:x?}");
149
150 let cipher = crypto.hpke_seal(
151 ciphersuite.hpke_config(),
152 public_key,
153 &context,
154 &[],
155 plaintext,
156 )?;
157
158 log_crypto!(debug, "* ciphertext: {:x?}", cipher);
159
160 Ok(cipher)
161}
162
163#[cfg(feature = "extensions-draft-08")]
165pub struct SafeEncryptionContext<'a> {
166 pub component_id: ComponentId,
168
169 pub label: &'a str,
171
172 pub context: &'a [u8],
174}
175
176#[cfg(feature = "extensions-draft-08")]
182pub fn safe_encrypt_with_label(
183 public_key: &[u8],
184 plaintext: &[u8],
185 ciphersuite: Ciphersuite,
186 context: SafeEncryptionContext,
187 crypto: &impl OpenMlsCrypto,
188) -> Result<HpkeCiphertext, Error> {
189 let component_operation_label =
190 ComponentOperationLabel::new(context.component_id, context.label);
191
192 let context = EncryptContext::new_from_component_operation_label(
193 component_operation_label,
194 context.context.into(),
195 )?;
196
197 encrypt_with_label_internal(public_key, context, plaintext, ciphersuite, crypto)
198}
199
200pub(crate) fn decrypt_with_label(
202 private_key: &[u8],
203 label: &str,
204 context: &[u8],
205 ciphertext: &HpkeCiphertext,
206 ciphersuite: Ciphersuite,
207 crypto: &impl OpenMlsCrypto,
208) -> Result<Vec<u8>, Error> {
209 log_crypto!(
210 debug,
211 "HPKE Decrypt with label `{label}` and `ciphersuite` {ciphersuite:?}:"
212 );
213
214 let context: EncryptContext = (label, context).into();
215
216 decrypt_with_label_internal(private_key, context, ciphertext, ciphersuite, crypto)
217}
218
219fn decrypt_with_label_internal(
220 private_key: &[u8],
221 context: EncryptContext,
222 ciphertext: &HpkeCiphertext,
223 ciphersuite: Ciphersuite,
224 crypto: &impl OpenMlsCrypto,
225) -> Result<Vec<u8>, Error> {
226 let context = context.tls_serialize_detached()?;
227
228 log_crypto!(debug, "* context: {context:x?}");
229 log_crypto!(debug, "* private key: {private_key:x?}");
230 log_crypto!(debug, "* ciphertext: {ciphertext:x?}");
231
232 let plaintext = crypto
233 .hpke_open(
234 ciphersuite.hpke_config(),
235 ciphertext,
236 private_key,
237 &context,
238 &[],
239 )
240 .map_err(|e| e.into());
241
242 log_crypto!(debug, "* plaintext: {plaintext:x?}");
243
244 plaintext
245}
246
247#[cfg(feature = "extensions-draft-08")]
248pub fn safe_decrypt_with_label(
254 private_key: &[u8],
255 ciphertext: &HpkeCiphertext,
256 ciphersuite: Ciphersuite,
257 context: SafeEncryptionContext,
258 crypto: &impl OpenMlsCrypto,
259) -> Result<Vec<u8>, Error> {
260 let component_operation_label =
261 ComponentOperationLabel::new(context.component_id, context.label);
262
263 let context: EncryptContext = EncryptContext::new_from_component_operation_label(
264 component_operation_label,
265 context.context.into(),
266 )?;
267
268 decrypt_with_label_internal(private_key, context, ciphertext, ciphersuite, crypto)
269}