use super::{RootKeyPolicy, TokenVerificationError, TokenVerificationLimits, binding, root, rules};
use crate::canonical::cert_hash;
use ic_auth_protocol_types::{DelegationCert, DelegationProof, Principal};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct DelegationProofVerificationLimits {
pub max_cert_ttl_ns: u64,
pub max_token_ttl_ns: u64,
pub max_future_skew_ns: u64,
pub max_variable_bytes: usize,
pub max_witness_steps: usize,
}
impl From<TokenVerificationLimits> for DelegationProofVerificationLimits {
fn from(limits: TokenVerificationLimits) -> Self {
Self {
max_cert_ttl_ns: limits.max_cert_ttl_ns,
max_token_ttl_ns: limits.max_token_ttl_ns,
max_future_skew_ns: limits.max_future_skew_ns,
max_variable_bytes: limits.max_variable_bytes,
max_witness_steps: limits.max_witness_steps,
}
}
}
pub struct DelegationProofVerificationContext<'a> {
pub expected_issuer: Principal,
pub root_key: &'a RootKeyPolicy,
pub now_ns: u64,
pub limits: DelegationProofVerificationLimits,
}
#[derive(Clone, Debug)]
pub struct VerifiedDelegationProof<'a> {
certificate: &'a DelegationCert,
certificate_hash: [u8; 32],
expires_at_ns: u64,
}
impl<'a> VerifiedDelegationProof<'a> {
pub fn certificate(&self) -> &'a DelegationCert {
self.certificate
}
pub const fn certificate_hash(&self) -> [u8; 32] {
self.certificate_hash
}
pub const fn expires_at_ns(&self) -> u64 {
self.expires_at_ns
}
}
pub fn verify_delegation_proof<'a>(
proof: &'a DelegationProof,
context: &DelegationProofVerificationContext<'_>,
) -> Result<VerifiedDelegationProof<'a>, TokenVerificationError> {
let mut remaining = context.limits.max_variable_bytes;
rules::check_proof_size(proof, context.limits.max_witness_steps, &mut remaining)?;
let cert = &proof.cert;
for (field, principal) in [("issuer", cert.issuer_pid), ("root", cert.root_pid)] {
if principal == Principal::anonymous() {
return Err(TokenVerificationError::AnonymousPrincipal { field });
}
}
binding(
cert.root_pid == context.root_key.root_canister_id,
"root_canister_id",
)?;
binding(
cert.issuer_pid == context.expected_issuer,
"issuer_canister_id",
)?;
rules::verify_certificate_window(cert, context.now_ns, context.limits)?;
rules::verify_certificate_binding(cert)?;
let certificate_hash = cert_hash(cert)?;
root::verify(
cert,
&proof.root_proof,
context.root_key,
context.now_ns,
context.limits.max_future_skew_ns,
)?;
Ok(VerifiedDelegationProof {
certificate: cert,
certificate_hash,
expires_at_ns: cert.expires_at_ns.min(context.root_key.accept_until_ns),
})
}