use openmls_traits::{
crypto::OpenMlsCrypto,
types::{Ciphersuite, CryptoError, HpkeCiphertext},
};
use thiserror::Error;
use tls_codec::{Serialize, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize, VLBytes};
use super::LABEL_PREFIX;
#[cfg(feature = "extensions-draft-08")]
use crate::component::{ComponentId, ComponentOperationLabel};
#[derive(Error, Debug, PartialEq, Clone)]
pub enum Error {
#[error(
"Error while serializing content. This should only happen if a bounds check was missing."
)]
MissingBoundCheck,
#[error("Decryption failed.")]
DecryptionFailed,
}
impl From<tls_codec::Error> for Error {
fn from(_: tls_codec::Error) -> Self {
Self::MissingBoundCheck
}
}
impl From<CryptoError> for Error {
fn from(_: CryptoError) -> Self {
Self::DecryptionFailed
}
}
#[derive(Debug, Clone, TlsSerialize, TlsDeserialize, TlsDeserializeBytes, TlsSize)]
pub struct EncryptContext {
label: VLBytes,
context: VLBytes,
}
impl EncryptContext {
pub(crate) fn new(label: &str, context: VLBytes) -> Self {
let label_string = LABEL_PREFIX.to_owned() + label;
let label = label_string.as_bytes().into();
Self { label, context }
}
#[cfg(feature = "extensions-draft-08")]
pub(crate) fn new_from_component_operation_label(
label: ComponentOperationLabel,
context: VLBytes,
) -> Result<Self, Error> {
let serialized_label = label.tls_serialize_detached()?;
let mut label = LABEL_PREFIX.as_bytes().to_vec();
label.extend(serialized_label);
Ok(Self {
label: label.into(),
context,
})
}
}
impl From<(&str, &[u8])> for EncryptContext {
fn from((label, context): (&str, &[u8])) -> Self {
Self::new(label, context.into())
}
}
pub(crate) fn encrypt_with_label(
public_key: &[u8],
label: &str,
context: &[u8],
plaintext: &[u8],
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
) -> Result<HpkeCiphertext, Error> {
let context: EncryptContext = (label, context).into();
log_crypto!(
debug,
"HPKE Encrypt with label `{label}` and ciphersuite `{ciphersuite:?}`:"
);
encrypt_with_label_internal(public_key, context, plaintext, ciphersuite, crypto)
}
fn encrypt_with_label_internal(
public_key: &[u8],
context: EncryptContext,
plaintext: &[u8],
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
) -> Result<HpkeCiphertext, Error> {
let context = context.tls_serialize_detached()?;
log_crypto!(debug, "* context: {context:x?}");
log_crypto!(debug, "* public key: {public_key:x?}");
log_crypto!(debug, "* plaintext: {plaintext:x?}");
let cipher = crypto.hpke_seal(
ciphersuite.hpke_config(),
public_key,
&context,
&[],
plaintext,
)?;
log_crypto!(debug, "* ciphertext: {:x?}", cipher);
Ok(cipher)
}
#[cfg(feature = "extensions-draft-08")]
pub struct SafeEncryptionContext<'a> {
pub component_id: ComponentId,
pub label: &'a str,
pub context: &'a [u8],
}
#[cfg(feature = "extensions-draft-08")]
pub fn safe_encrypt_with_label(
public_key: &[u8],
plaintext: &[u8],
ciphersuite: Ciphersuite,
context: SafeEncryptionContext,
crypto: &impl OpenMlsCrypto,
) -> Result<HpkeCiphertext, Error> {
let component_operation_label =
ComponentOperationLabel::new(context.component_id, context.label);
let context = EncryptContext::new_from_component_operation_label(
component_operation_label,
context.context.into(),
)?;
encrypt_with_label_internal(public_key, context, plaintext, ciphersuite, crypto)
}
pub(crate) fn decrypt_with_label(
private_key: &[u8],
label: &str,
context: &[u8],
ciphertext: &HpkeCiphertext,
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
) -> Result<Vec<u8>, Error> {
log_crypto!(
debug,
"HPKE Decrypt with label `{label}` and `ciphersuite` {ciphersuite:?}:"
);
let context: EncryptContext = (label, context).into();
decrypt_with_label_internal(private_key, context, ciphertext, ciphersuite, crypto)
}
fn decrypt_with_label_internal(
private_key: &[u8],
context: EncryptContext,
ciphertext: &HpkeCiphertext,
ciphersuite: Ciphersuite,
crypto: &impl OpenMlsCrypto,
) -> Result<Vec<u8>, Error> {
let context = context.tls_serialize_detached()?;
log_crypto!(debug, "* context: {context:x?}");
log_crypto!(debug, "* private key: {private_key:x?}");
log_crypto!(debug, "* ciphertext: {ciphertext:x?}");
let plaintext = crypto
.hpke_open(
ciphersuite.hpke_config(),
ciphertext,
private_key,
&context,
&[],
)
.map_err(|e| e.into());
log_crypto!(debug, "* plaintext: {plaintext:x?}");
plaintext
}
#[cfg(feature = "extensions-draft-08")]
pub fn safe_decrypt_with_label(
private_key: &[u8],
ciphertext: &HpkeCiphertext,
ciphersuite: Ciphersuite,
context: SafeEncryptionContext,
crypto: &impl OpenMlsCrypto,
) -> Result<Vec<u8>, Error> {
let component_operation_label =
ComponentOperationLabel::new(context.component_id, context.label);
let context: EncryptContext = EncryptContext::new_from_component_operation_label(
component_operation_label,
context.context.into(),
)?;
decrypt_with_label_internal(private_key, context, ciphertext, ciphersuite, crypto)
}