use crypto_core::Algorithm;
use crypto_dispatch::AlgorithmError;
use zeroize::Zeroizing;
use crate::operations::{
BackendErrorReason, OperationError, PrimitiveErrorReason, ProviderErrorReason,
};
use crate::secret_material::{bind_operation_policy, SecretMaterialOperation};
pub struct KemKeyPair {
pub public_key: Vec<u8>,
pub secret_key: Zeroizing<Vec<u8>>,
}
pub struct KemEncapsulation {
pub ciphertext: Vec<u8>,
pub shared_secret: Zeroizing<Vec<u8>>,
}
pub fn generate_key_pair(algorithm: Algorithm) -> Result<KemKeyPair, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::KemKeyGeneration);
ensure_kem_algorithm(algorithm)?;
crypto_dispatch::generate_keypair(algorithm)
.map(kem_key_pair)
.map_err(map_dispatch_error)
}
pub fn derive_key_pair(
algorithm: Algorithm,
secret_key: &[u8],
) -> Result<KemKeyPair, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::KemKeyDerivation);
ensure_kem_algorithm(algorithm)?;
crypto_dispatch::derive_keypair(algorithm, secret_key)
.map(kem_key_pair)
.map_err(map_dispatch_error)
}
pub fn encapsulate(
algorithm: Algorithm,
public_key: &[u8],
) -> Result<KemEncapsulation, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::KemEncapsulate);
ensure_kem_algorithm(algorithm)?;
crypto_dispatch::kem_encapsulate(algorithm, public_key)
.map(kem_encapsulation)
.map_err(map_dispatch_error)
}
#[cfg(feature = "test-vectors")]
pub fn encapsulate_derand(
algorithm: Algorithm,
public_key: &[u8],
randomness: &[u8],
) -> Result<KemEncapsulation, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::KemEncapsulate);
ensure_kem_algorithm(algorithm)?;
match algorithm {
Algorithm::MlKem512 => {
#[cfg(feature = "ml-kem-512")]
{
crypto_ml_kem_512::ml_kem_512_encapsulate_derand(public_key, randomness)
.map(kem_encapsulation_from_primitive)
.map_err(map_crypto_error)
}
#[cfg(not(feature = "ml-kem-512"))]
{
let _ = (public_key, randomness);
Err(unsupported_algorithm())
}
}
Algorithm::MlKem768 => {
#[cfg(feature = "ml-kem-768")]
{
crypto_ml_kem_768::ml_kem_768_encapsulate_derand(public_key, randomness)
.map(kem_encapsulation_from_primitive)
.map_err(map_crypto_error)
}
#[cfg(not(feature = "ml-kem-768"))]
{
let _ = (public_key, randomness);
Err(unsupported_algorithm())
}
}
Algorithm::MlKem1024 => {
#[cfg(feature = "ml-kem-1024")]
{
crypto_ml_kem_1024::ml_kem_1024_encapsulate_derand(public_key, randomness)
.map(kem_encapsulation_from_primitive)
.map_err(map_crypto_error)
}
#[cfg(not(feature = "ml-kem-1024"))]
{
let _ = (public_key, randomness);
Err(unsupported_algorithm())
}
}
Algorithm::XWing768 => {
#[cfg(feature = "x-wing")]
{
crypto_x_wing::x_wing_768_encapsulate_derand(public_key, randomness)
.map(kem_encapsulation_from_primitive)
.map_err(map_crypto_error)
}
#[cfg(not(feature = "x-wing"))]
{
let _ = (public_key, randomness);
Err(unsupported_algorithm())
}
}
_ => Err(unsupported_algorithm()),
}
}
pub fn decapsulate(
algorithm: Algorithm,
ciphertext: &[u8],
secret_key: &[u8],
) -> Result<Zeroizing<Vec<u8>>, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::KemDecapsulate);
ensure_kem_algorithm(algorithm)?;
crypto_dispatch::kem_decapsulate(algorithm, ciphertext, secret_key).map_err(map_dispatch_error)
}
pub fn is_kem_algorithm(algorithm: Algorithm) -> bool {
matches!(
algorithm,
Algorithm::MlKem512 | Algorithm::MlKem768 | Algorithm::MlKem1024 | Algorithm::XWing768
)
}
fn ensure_kem_algorithm(algorithm: Algorithm) -> Result<(), OperationError> {
if is_kem_algorithm(algorithm) {
Ok(())
} else {
Err(unsupported_algorithm())
}
}
fn kem_key_pair((public_key, secret_key): (Vec<u8>, Zeroizing<Vec<u8>>)) -> KemKeyPair {
KemKeyPair {
public_key,
secret_key,
}
}
fn kem_encapsulation(
(shared_secret, ciphertext): (Zeroizing<Vec<u8>>, Vec<u8>),
) -> KemEncapsulation {
KemEncapsulation {
ciphertext,
shared_secret,
}
}
#[cfg(feature = "test-vectors")]
fn kem_encapsulation_from_primitive(
(ciphertext, shared_secret): (Vec<u8>, Zeroizing<Vec<u8>>),
) -> KemEncapsulation {
KemEncapsulation {
ciphertext,
shared_secret,
}
}
fn unsupported_algorithm() -> OperationError {
OperationError::Provider {
reason: ProviderErrorReason::UnsupportedAlgorithm,
}
}
fn map_dispatch_error(error: AlgorithmError) -> OperationError {
match error {
AlgorithmError::UnsupportedAlgorithm(_)
| AlgorithmError::UnsupportedAeadAlgorithm(_)
| AlgorithmError::UnsupportedHashAlgorithm(_)
| AlgorithmError::UnsupportedMacAlgorithm(_) => unsupported_algorithm(),
AlgorithmError::InvalidKey(_) => OperationError::Primitive {
reason: PrimitiveErrorReason::InvalidKey,
},
AlgorithmError::SignatureInvalid(_) => OperationError::Primitive {
reason: PrimitiveErrorReason::VerificationFailed,
},
AlgorithmError::Crypto(error) => map_crypto_error(error),
_ => OperationError::Backend {
reason: BackendErrorReason::Internal,
},
}
}
fn map_crypto_error(error: crypto_core::CryptoError) -> OperationError {
match error {
crypto_core::CryptoError::InvalidKey => OperationError::Primitive {
reason: PrimitiveErrorReason::InvalidKey,
},
crypto_core::CryptoError::InvalidCiphertextLength { .. } => OperationError::Primitive {
reason: PrimitiveErrorReason::InvalidLength,
},
crypto_core::CryptoError::KemFailure { kind } => match kind {
crypto_core::KemFailureKind::KeyGenerationFailed => OperationError::Backend {
reason: BackendErrorReason::Internal,
},
crypto_core::KemFailureKind::EncapsulateFailed => OperationError::Primitive {
reason: PrimitiveErrorReason::InvalidKey,
},
crypto_core::KemFailureKind::DecapsulateFailed => OperationError::Primitive {
reason: PrimitiveErrorReason::VerificationFailed,
},
_ => OperationError::Backend {
reason: BackendErrorReason::Internal,
},
},
crypto_core::CryptoError::KeyAgreementFailure { kind } => match kind {
crypto_core::KeyAgreementFailureKind::DeriveSharedSecretFailed => {
OperationError::Primitive {
reason: PrimitiveErrorReason::InvalidSharedSecret,
}
}
crypto_core::KeyAgreementFailureKind::KeyGenerationFailed => OperationError::Backend {
reason: BackendErrorReason::Internal,
},
_ => OperationError::Backend {
reason: BackendErrorReason::Internal,
},
},
crypto_core::CryptoError::Unsupported => unsupported_algorithm(),
_ => OperationError::Backend {
reason: BackendErrorReason::Internal,
},
}
}