use bc_ur::prelude::*;
#[cfg(feature = "pqcrypto")]
use crate::MLKEMPublicKey;
use crate::{
Digest, EncapsulationCiphertext, EncapsulationScheme, Encrypter,
PrivateKeyBase, Reference, ReferenceProvider, SymmetricKey,
X25519PublicKey, tags,
};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum EncapsulationPublicKey {
X25519(X25519PublicKey),
#[cfg(feature = "pqcrypto")]
MLKEM(MLKEMPublicKey),
}
impl EncapsulationPublicKey {
pub fn encapsulation_scheme(&self) -> EncapsulationScheme {
match self {
Self::X25519(_) => EncapsulationScheme::X25519,
#[cfg(feature = "pqcrypto")]
Self::MLKEM(pk) => match pk.level() {
crate::MLKEM::MLKEM512 => EncapsulationScheme::MLKEM512,
crate::MLKEM::MLKEM768 => EncapsulationScheme::MLKEM768,
crate::MLKEM::MLKEM1024 => EncapsulationScheme::MLKEM1024,
},
}
}
pub fn encapsulate_new_shared_secret(
&self,
) -> (SymmetricKey, EncapsulationCiphertext) {
match self {
EncapsulationPublicKey::X25519(public_key) => {
let emphemeral_sender = PrivateKeyBase::new();
let ephemeral_private_key =
emphemeral_sender.x25519_private_key();
let ephemeral_public_key = ephemeral_private_key.public_key();
let shared_key =
ephemeral_private_key.shared_key_with(public_key);
(
shared_key,
EncapsulationCiphertext::X25519(ephemeral_public_key),
)
}
#[cfg(feature = "pqcrypto")]
EncapsulationPublicKey::MLKEM(public_key) => {
let (shared_key, ciphertext) =
public_key.encapsulate_new_shared_secret();
(shared_key, EncapsulationCiphertext::MLKEM(ciphertext))
}
}
}
}
impl Encrypter for EncapsulationPublicKey {
fn encapsulation_public_key(&self) -> EncapsulationPublicKey {
self.clone()
}
fn encapsulate_new_shared_secret(
&self,
) -> (SymmetricKey, EncapsulationCiphertext) {
self.encapsulate_new_shared_secret()
}
}
impl From<EncapsulationPublicKey> for CBOR {
fn from(public_key: EncapsulationPublicKey) -> Self {
match public_key {
EncapsulationPublicKey::X25519(public_key) => public_key.into(),
#[cfg(feature = "pqcrypto")]
EncapsulationPublicKey::MLKEM(public_key) => public_key.into(),
}
}
}
impl TryFrom<CBOR> for EncapsulationPublicKey {
type Error = dcbor::Error;
fn try_from(cbor: CBOR) -> std::result::Result<Self, dcbor::Error> {
match cbor.as_case() {
CBORCase::Tagged(tag, _) => match tag.value() {
tags::TAG_X25519_PUBLIC_KEY => {
Ok(EncapsulationPublicKey::X25519(
X25519PublicKey::try_from(cbor)?,
))
}
#[cfg(feature = "pqcrypto")]
tags::TAG_MLKEM_PUBLIC_KEY => {
Ok(EncapsulationPublicKey::MLKEM(MLKEMPublicKey::try_from(
cbor,
)?))
}
_ => Err(dcbor::Error::msg("Invalid encapsulation public key")),
},
_ => Err(dcbor::Error::msg("Invalid encapsulation public key")),
}
}
}
impl ReferenceProvider for EncapsulationPublicKey {
fn reference(&self) -> Reference {
Reference::from_digest(Digest::from_image(self.to_cbor_data()))
}
}
impl std::fmt::Display for EncapsulationPublicKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let display_key = match self {
EncapsulationPublicKey::X25519(key) => key.to_string(),
#[cfg(feature = "pqcrypto")]
EncapsulationPublicKey::MLKEM(key) => key.to_string(),
};
write!(
f,
"EncapsulationPublicKey({}, {})",
self.ref_hex_short(),
display_key
)
}
}