use crate::{
cdk::types::Principal,
dto::{capability::CapabilityProof, error::Error, rpc::Request},
};
use async_trait::async_trait;
pub(super) struct VerifiedCapability;
pub(super) struct VerificationInput<'a> {
pub(super) capability: &'a Request,
pub(super) capability_version: u16,
pub(super) proof: &'a CapabilityProof,
pub(super) caller: Principal,
pub(super) target_canister: Principal,
pub(super) now_secs: u64,
}
#[async_trait]
pub(super) trait CapabilityProofVerifier {
async fn verify(&self, input: &VerificationInput<'_>) -> Result<VerifiedCapability, Error>;
}
struct StructuralVerifier;
#[async_trait]
impl CapabilityProofVerifier for StructuralVerifier {
async fn verify(&self, input: &VerificationInput<'_>) -> Result<VerifiedCapability, Error> {
super::proof::verify_root_structural_proof(input.capability)?;
Ok(VerifiedCapability)
}
}
struct RoleAttestationVerifier;
#[async_trait]
impl CapabilityProofVerifier for RoleAttestationVerifier {
async fn verify(&self, input: &VerificationInput<'_>) -> Result<VerifiedCapability, Error> {
let CapabilityProof::RoleAttestation(blob) = input.proof else {
return Err(Error::internal(
"role attestation verifier received non-attestation proof",
));
};
let proof = super::proof::decode_role_attestation_blob(blob)?;
super::proof::verify_capability_hash_binding(
input.target_canister,
input.capability_version,
input.capability,
proof.capability_hash,
)?;
crate::api::auth::DelegationApi::verify_role_attestation(&proof.attestation, 0).await?;
Ok(VerifiedCapability)
}
}
struct DelegatedGrantVerifier;
#[async_trait]
impl CapabilityProofVerifier for DelegatedGrantVerifier {
async fn verify(&self, input: &VerificationInput<'_>) -> Result<VerifiedCapability, Error> {
let CapabilityProof::DelegatedGrant(blob) = input.proof else {
return Err(Error::internal(
"delegated grant verifier received non-grant proof",
));
};
let proof = super::proof::decode_delegated_grant_blob(blob)?;
super::proof::verify_capability_hash_binding(
input.target_canister,
input.capability_version,
input.capability,
proof.capability_hash,
)?;
super::verify_delegated_grant_hash_binding(&proof)?;
super::verify_root_delegated_grant_proof(
input.capability,
&proof,
input.caller,
input.target_canister,
input.now_secs,
)?;
Ok(VerifiedCapability)
}
}
pub(super) async fn verify_root_capability_proof(
capability: &Request,
capability_version: u16,
proof: &CapabilityProof,
) -> Result<VerifiedCapability, Error> {
let input = VerificationInput {
capability,
capability_version,
proof,
caller: crate::ops::ic::IcOps::msg_caller(),
target_canister: crate::ops::ic::IcOps::canister_self(),
now_secs: crate::ops::ic::IcOps::now_secs(),
};
match proof {
CapabilityProof::Structural => StructuralVerifier.verify(&input).await,
CapabilityProof::RoleAttestation(_) => RoleAttestationVerifier.verify(&input).await,
CapabilityProof::DelegatedGrant(_) => DelegatedGrantVerifier.verify(&input).await,
}
}