use std::fmt::Debug;
use openmls_traits::{
crypto::OpenMlsCrypto,
key_store::{MlsEntity, MlsEntityId, OpenMlsKeyStore},
types::{Ciphersuite, HpkeCiphertext, HpkeKeyPair},
OpenMlsCryptoProvider,
};
use serde::{Deserialize, Serialize};
use tls_codec::{TlsDeserialize, TlsSerialize, TlsSize, VLBytes};
use crate::{
ciphersuite::{hpke, HpkePrivateKey, HpkePublicKey, Secret},
error::LibraryError,
group::config::CryptoConfig,
versions::ProtocolVersion,
};
#[derive(
Debug, Clone, Serialize, Deserialize, TlsSerialize, TlsDeserialize, TlsSize, PartialEq, Eq, Hash,
)]
pub struct EncryptionKey {
key: HpkePublicKey,
}
impl EncryptionKey {
pub(crate) fn key(&self) -> &HpkePublicKey {
&self.key
}
pub(crate) fn as_slice(&self) -> &[u8] {
self.key.as_slice()
}
fn to_bytes_with_prefix(&self) -> Vec<u8> {
let mut key_store_index = ENCRYPTION_KEY_LABEL.to_vec();
key_store_index.extend_from_slice(self.as_slice());
key_store_index
}
pub(crate) fn encrypt(
&self,
backend: &impl OpenMlsCryptoProvider,
ciphersuite: Ciphersuite,
context: &[u8],
plaintext: &[u8],
) -> Result<HpkeCiphertext, LibraryError> {
hpke::encrypt_with_label(
self.as_slice(),
"UpdatePathNode",
context,
plaintext,
ciphersuite,
backend.crypto(),
)
.map_err(|_| LibraryError::custom("Encryption failed. A serialization issue really"))
}
}
#[derive(Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub(crate) struct EncryptionPrivateKey {
key: HpkePrivateKey,
}
impl Debug for EncryptionPrivateKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut ds = f.debug_struct("EncryptionPrivateKey");
#[cfg(feature = "crypto-debug")]
ds.field("key", &self.key);
#[cfg(not(feature = "crypto-debug"))]
ds.field("key", &"***");
ds.finish()
}
}
impl From<Vec<u8>> for EncryptionPrivateKey {
fn from(key: Vec<u8>) -> Self {
Self { key: key.into() }
}
}
impl From<HpkePrivateKey> for EncryptionPrivateKey {
fn from(key: HpkePrivateKey) -> Self {
Self { key }
}
}
impl EncryptionPrivateKey {
pub(crate) fn decrypt(
&self,
backend: &impl OpenMlsCryptoProvider,
ciphersuite: Ciphersuite,
version: ProtocolVersion,
ciphertext: &HpkeCiphertext,
group_context: &[u8],
) -> Result<Secret, hpke::Error> {
hpke::decrypt_with_label(
&self.key,
"UpdatePathNode",
group_context,
ciphertext,
ciphersuite,
backend.crypto(),
)
.map(|secret_bytes| Secret::from_slice(&secret_bytes, version, ciphersuite))
}
}
#[cfg(test)]
impl EncryptionPrivateKey {
pub(crate) fn key(&self) -> &HpkePrivateKey {
&self.key
}
}
impl From<HpkePublicKey> for EncryptionKey {
fn from(key: HpkePublicKey) -> Self {
Self { key }
}
}
#[derive(Debug, Clone, Serialize, Deserialize, TlsDeserialize, TlsSerialize, TlsSize)]
#[cfg_attr(test, derive(PartialEq, Eq))]
pub(crate) struct EncryptionKeyPair {
public_key: EncryptionKey,
private_key: EncryptionPrivateKey,
}
const ENCRYPTION_KEY_LABEL: &[u8; 19] = b"leaf_encryption_key";
impl EncryptionKeyPair {
pub(crate) fn write_to_key_store<KeyStore: OpenMlsKeyStore>(
&self,
backend: &impl OpenMlsCryptoProvider<KeyStoreProvider = KeyStore>,
) -> Result<(), KeyStore::Error> {
backend
.key_store()
.store(&self.public_key().to_bytes_with_prefix(), self)
}
pub(crate) fn read_from_key_store(
backend: &impl OpenMlsCryptoProvider,
encryption_key: &EncryptionKey,
) -> Option<EncryptionKeyPair> {
backend
.key_store()
.read(&encryption_key.to_bytes_with_prefix())
}
pub(crate) fn delete_from_key_store<KeyStore: OpenMlsKeyStore>(
&self,
backend: &impl OpenMlsCryptoProvider<KeyStoreProvider = KeyStore>,
) -> Result<(), KeyStore::Error> {
backend
.key_store()
.delete::<Self>(&self.public_key().to_bytes_with_prefix())
}
pub(crate) fn public_key(&self) -> &EncryptionKey {
&self.public_key
}
pub(crate) fn private_key(&self) -> &EncryptionPrivateKey {
&self.private_key
}
pub(crate) fn random(
backend: &impl OpenMlsCryptoProvider,
config: CryptoConfig,
) -> Result<Self, LibraryError> {
let ikm = Secret::random(config.ciphersuite, backend, config.version)
.map_err(LibraryError::unexpected_crypto_error)?;
Ok(backend
.crypto()
.derive_hpke_keypair(config.ciphersuite.hpke_config(), ikm.as_slice())
.into())
}
}
#[cfg(feature = "test-utils")]
pub mod test_utils {
use super::*;
pub fn read_keys_from_key_store(
backend: &impl OpenMlsCryptoProvider,
encryption_key: &EncryptionKey,
) -> HpkeKeyPair {
let keys = EncryptionKeyPair::read_from_key_store(backend, encryption_key).unwrap();
HpkeKeyPair {
private: keys.private_key.key,
public: keys.public_key.key.as_slice().to_vec(),
}
}
pub fn write_keys_from_key_store(
backend: &impl OpenMlsCryptoProvider,
encryption_key: HpkeKeyPair,
) {
let keypair = EncryptionKeyPair::from(encryption_key);
keypair.write_to_key_store(backend).unwrap();
}
}
#[cfg(test)]
impl EncryptionKeyPair {
pub(crate) fn from_raw(public_key: Vec<u8>, private_key: Vec<u8>) -> Self {
Self {
public_key: EncryptionKey {
key: public_key.into(),
},
private_key: EncryptionPrivateKey {
key: private_key.into(),
},
}
}
}
impl From<(HpkePublicKey, HpkePrivateKey)> for EncryptionKeyPair {
fn from((public_key, private_key): (HpkePublicKey, HpkePrivateKey)) -> Self {
Self {
public_key: public_key.into(),
private_key: private_key.into(),
}
}
}
impl From<HpkeKeyPair> for EncryptionKeyPair {
fn from(hpke_keypair: HpkeKeyPair) -> Self {
let public_bytes: VLBytes = hpke_keypair.public.into();
let private_bytes = hpke_keypair.private;
Self {
public_key: public_bytes.into(),
private_key: private_bytes.into(),
}
}
}
impl From<(EncryptionKey, EncryptionPrivateKey)> for EncryptionKeyPair {
fn from((public_key, private_key): (EncryptionKey, EncryptionPrivateKey)) -> Self {
Self {
public_key,
private_key,
}
}
}
impl MlsEntity for EncryptionKeyPair {
const ID: MlsEntityId = MlsEntityId::EncryptionKeyPair;
}