use crypto_hpke::{
HpkeError, HpkeExporterSecret, HpkeKeyPair, HpkeOpenOutput, HpkeOpenRequest,
HpkePskOpenRequest, HpkePskSealRequest, HpkeReceiverExportRequest, HpkeSealOutput,
HpkeSealRequest, HpkeSenderExportOutput, HpkeSenderExportRequest, HpkeSuite,
};
use zeroize::ZeroizeOnDrop;
use crate::operations::{
BackendErrorReason, OperationError, PrimitiveErrorReason, ProviderErrorReason,
};
use crate::secret_material::{bind_operation_policy, SecretMaterialOperation};
pub const HPKE_OPERATION_MIN_INPUT_KEY_MATERIAL_LEN: usize = 32;
pub fn keygen(suite: HpkeSuite) -> Result<HpkeKeyPair, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeKeyGeneration);
crypto_hpke::keygen(suite).map_err(map_hpke_error)
}
pub fn derive_keypair(
suite: HpkeSuite,
input_key_material: &[u8],
) -> Result<HpkeKeyPair, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeKeyDerivation);
crypto_hpke::derive_keypair(suite, input_key_material).map_err(map_hpke_error)
}
pub fn derive_keypair_from_ikm(
suite: HpkeSuite,
input_key_material: &[u8],
) -> Result<HpkeKeyPair, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeKeyDerivation);
if input_key_material.len() < HPKE_OPERATION_MIN_INPUT_KEY_MATERIAL_LEN {
return Err(primitive(PrimitiveErrorReason::InvalidLength));
}
crypto_hpke::derive_keypair_from_ikm(suite, input_key_material).map_err(map_hpke_error)
}
pub struct HpkePskSenderSetupOutput {
pub encapsulated_key: Vec<u8>,
pub context: HpkeSenderContext,
}
pub struct HpkeSenderContext {
inner: crypto_hpke::HpkeSenderContext,
}
impl ZeroizeOnDrop for HpkeSenderContext {}
impl HpkeSenderContext {
pub fn seal(&mut self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeSeal);
self.inner.seal(aad, plaintext).map_err(map_hpke_error)
}
}
pub type HpkePskSenderContext = HpkeSenderContext;
pub struct HpkeReceiverContext {
inner: crypto_hpke::HpkeReceiverContext,
}
impl ZeroizeOnDrop for HpkeReceiverContext {}
impl HpkeReceiverContext {
pub fn open(
&mut self,
aad: &[u8],
ciphertext: &[u8],
) -> Result<HpkeOpenOutput, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeOpen);
self.inner.open(aad, ciphertext).map_err(map_hpke_error)
}
}
pub fn setup_sender_psk(
request: &crypto_hpke::HpkePskSenderSetupRequest<'_>,
) -> Result<HpkePskSenderSetupOutput, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeSenderSetup);
let output = crypto_hpke::setup_sender_psk(request).map_err(map_hpke_error)?;
Ok(HpkePskSenderSetupOutput {
encapsulated_key: output.encapsulated_key,
context: HpkeSenderContext {
inner: output.context,
},
})
}
pub fn setup_receiver_psk(
request: &crypto_hpke::HpkePskReceiverSetupRequest<'_>,
) -> Result<HpkeReceiverContext, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeReceiverSetup);
let inner = crypto_hpke::setup_receiver_psk(request).map_err(map_hpke_error)?;
Ok(HpkeReceiverContext { inner })
}
pub fn seal_base(request: &HpkeSealRequest<'_>) -> Result<HpkeSealOutput, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeSeal);
crypto_hpke::seal_base(request).map_err(map_hpke_error)
}
pub fn open_base(request: &HpkeOpenRequest<'_>) -> Result<HpkeOpenOutput, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeOpen);
crypto_hpke::open_base(request).map_err(map_hpke_error)
}
pub fn sender_export(
request: &HpkeSenderExportRequest<'_>,
) -> Result<HpkeSenderExportOutput, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeExport);
crypto_hpke::sender_export(request).map_err(map_hpke_error)
}
pub fn receiver_export(
request: &HpkeReceiverExportRequest<'_>,
) -> Result<HpkeExporterSecret, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeExport);
crypto_hpke::receiver_export(request).map_err(map_hpke_error)
}
pub fn seal_psk(request: &HpkePskSealRequest<'_>) -> Result<HpkeSealOutput, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeSeal);
crypto_hpke::seal_psk(request).map_err(map_hpke_error)
}
pub fn open_psk(request: &HpkePskOpenRequest<'_>) -> Result<HpkeOpenOutput, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeOpen);
crypto_hpke::open_psk(request).map_err(map_hpke_error)
}
#[cfg(feature = "test-vectors")]
pub fn seal_base_derand(
request: &crypto_hpke::HpkeDerandSealRequest<'_>,
) -> Result<HpkeSealOutput, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeSeal);
crypto_hpke::seal_base_derand(request).map_err(map_hpke_error)
}
#[cfg(feature = "test-vectors")]
pub fn sender_export_derand(
request: &crypto_hpke::HpkeDerandSenderExportRequest<'_>,
) -> Result<HpkeSenderExportOutput, OperationError> {
let _policy = bind_operation_policy(SecretMaterialOperation::HpkeExport);
crypto_hpke::sender_export_derand(request).map_err(map_hpke_error)
}
fn map_hpke_error(error: HpkeError) -> OperationError {
match error {
HpkeError::UnsupportedKem
| HpkeError::UnsupportedKdf
| HpkeError::UnsupportedAead
| HpkeError::UnsupportedSuite => OperationError::Provider {
reason: ProviderErrorReason::UnsupportedAlgorithm,
},
HpkeError::InvalidPublicKey => primitive(PrimitiveErrorReason::InvalidPublicKey),
HpkeError::InvalidPrivateKey => primitive(PrimitiveErrorReason::InvalidPrivateKey),
HpkeError::InvalidEncapsulatedKey | HpkeError::InvalidCiphertext => {
primitive(PrimitiveErrorReason::MalformedCiphertext)
}
HpkeError::InvalidInputKeyMaterial
| HpkeError::InvalidInfoLength
| HpkeError::InvalidExporterLength => primitive(PrimitiveErrorReason::InvalidLength),
HpkeError::LengthOverflow => primitive(PrimitiveErrorReason::LengthOverflow),
HpkeError::InvalidPsk | HpkeError::InvalidPskIdentifier | HpkeError::InvalidRandomness => {
primitive(PrimitiveErrorReason::InvalidParameter)
}
HpkeError::OpenFailed => primitive(PrimitiveErrorReason::VerificationFailed),
HpkeError::RandomnessUnavailable => OperationError::Provider {
reason: ProviderErrorReason::RandomnessUnavailable,
},
HpkeError::SealFailed | HpkeError::ExportFailed | HpkeError::KeyGenerationFailed => {
OperationError::Backend {
reason: BackendErrorReason::Internal,
}
}
_ => OperationError::Backend {
reason: BackendErrorReason::Internal,
},
}
}
fn primitive(reason: PrimitiveErrorReason) -> OperationError {
OperationError::Primitive { reason }
}