use crypto_core::Algorithm;
use crate::AlgorithmError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ProviderOperation {
GenerateKeyPair,
DeriveKeyPair,
Sign,
Verify,
DeriveSharedSecret,
KemEncapsulate,
KemDecapsulate,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ProviderKind {
PackageOwnedRust,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ProviderLane {
Native,
Wasm,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum KeyResidency {
ProcessMemory,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum KeyCopyBoundary {
NoSecretInput,
BorrowedCallerSecret,
ProviderCreatesSecret,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ProviderOutputPolicy {
PublicOnly,
ZeroizingSecret,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ProviderPolicyReason {
SelectedCompiledImplementation,
RejectedOperationMismatch,
RejectedFeatureDisabled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ProviderDisposition {
Selected,
Rejected,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum FallbackPolicy {
Prohibited,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct ProviderDecision {
pub operation: ProviderOperation,
pub algorithm: Algorithm,
pub provider_kind: ProviderKind,
pub lane: ProviderLane,
pub disposition: ProviderDisposition,
pub reason: ProviderPolicyReason,
pub key_residency: KeyResidency,
pub key_copy_boundary: KeyCopyBoundary,
pub output_policy: ProviderOutputPolicy,
pub fallback: FallbackPolicy,
}
impl ProviderDecision {
#[must_use]
pub fn is_selected(self) -> bool {
self.disposition == ProviderDisposition::Selected
}
}
#[must_use]
pub fn provider_decision(operation: ProviderOperation, algorithm: Algorithm) -> ProviderDecision {
let operation_supported = operation_supports_algorithm(operation, algorithm);
let implementation_compiled = implementation_is_compiled(algorithm);
let (disposition, reason) = if !operation_supported {
(
ProviderDisposition::Rejected,
ProviderPolicyReason::RejectedOperationMismatch,
)
} else if !implementation_compiled {
(
ProviderDisposition::Rejected,
ProviderPolicyReason::RejectedFeatureDisabled,
)
} else {
(
ProviderDisposition::Selected,
ProviderPolicyReason::SelectedCompiledImplementation,
)
};
ProviderDecision {
operation,
algorithm,
provider_kind: ProviderKind::PackageOwnedRust,
lane: compiled_lane(),
disposition,
reason,
key_residency: KeyResidency::ProcessMemory,
key_copy_boundary: key_copy_boundary(operation),
output_policy: output_policy(operation),
fallback: FallbackPolicy::Prohibited,
}
}
pub(crate) fn require_provider(
operation: ProviderOperation,
algorithm: Algorithm,
) -> Result<ProviderDecision, AlgorithmError> {
let decision = provider_decision(operation, algorithm);
if decision.is_selected() {
Ok(decision)
} else {
Err(AlgorithmError::UnsupportedAlgorithm(algorithm))
}
}
const fn compiled_lane() -> ProviderLane {
#[cfg(target_arch = "wasm32")]
{
ProviderLane::Wasm
}
#[cfg(not(target_arch = "wasm32"))]
{
ProviderLane::Native
}
}
const fn key_copy_boundary(operation: ProviderOperation) -> KeyCopyBoundary {
match operation {
ProviderOperation::GenerateKeyPair => KeyCopyBoundary::ProviderCreatesSecret,
ProviderOperation::DeriveKeyPair
| ProviderOperation::Sign
| ProviderOperation::DeriveSharedSecret
| ProviderOperation::KemDecapsulate => KeyCopyBoundary::BorrowedCallerSecret,
ProviderOperation::Verify | ProviderOperation::KemEncapsulate => {
KeyCopyBoundary::NoSecretInput
}
}
}
const fn output_policy(operation: ProviderOperation) -> ProviderOutputPolicy {
match operation {
ProviderOperation::GenerateKeyPair
| ProviderOperation::DeriveKeyPair
| ProviderOperation::DeriveSharedSecret
| ProviderOperation::KemEncapsulate
| ProviderOperation::KemDecapsulate => ProviderOutputPolicy::ZeroizingSecret,
ProviderOperation::Sign | ProviderOperation::Verify => ProviderOutputPolicy::PublicOnly,
}
}
const fn operation_supports_algorithm(operation: ProviderOperation, algorithm: Algorithm) -> bool {
match operation {
ProviderOperation::GenerateKeyPair | ProviderOperation::DeriveKeyPair => {
!matches!(algorithm, Algorithm::SlhDsaSha2_128s)
}
ProviderOperation::Sign | ProviderOperation::Verify => matches!(
algorithm,
Algorithm::Ed25519
| Algorithm::P256
| Algorithm::P384
| Algorithm::P521
| Algorithm::Secp256k1
| Algorithm::MlDsa44
| Algorithm::MlDsa65
| Algorithm::MlDsa87
),
ProviderOperation::DeriveSharedSecret => matches!(
algorithm,
Algorithm::P256 | Algorithm::P384 | Algorithm::P521 | Algorithm::X25519
),
ProviderOperation::KemEncapsulate | ProviderOperation::KemDecapsulate => matches!(
algorithm,
Algorithm::MlKem512 | Algorithm::MlKem768 | Algorithm::MlKem1024 | Algorithm::XWing768
),
}
}
const fn implementation_is_compiled(algorithm: Algorithm) -> bool {
match algorithm {
Algorithm::Ed25519 => cfg!(feature = "ed25519"),
Algorithm::X25519 => cfg!(feature = "x25519"),
Algorithm::P256 => cfg!(feature = "p256"),
Algorithm::P384 => cfg!(feature = "p384"),
Algorithm::P521 => cfg!(feature = "p521"),
Algorithm::Secp256k1 => cfg!(feature = "secp256k1"),
Algorithm::MlDsa44 => cfg!(feature = "ml-dsa-44"),
Algorithm::MlDsa65 => cfg!(feature = "ml-dsa-65"),
Algorithm::MlDsa87 => cfg!(feature = "ml-dsa-87"),
Algorithm::SlhDsaSha2_128s => false,
Algorithm::MlKem512 => cfg!(feature = "ml-kem-512"),
Algorithm::MlKem768 => cfg!(feature = "ml-kem-768"),
Algorithm::MlKem1024 => cfg!(feature = "ml-kem-1024"),
Algorithm::XWing768 => cfg!(feature = "x-wing"),
}
}