canic-core 0.26.11

Canic — a canister orchestration and management toolkit for the Internet Computer
Documentation
use super::{
    CERT_SIGNING_DOMAIN, ROLE_ATTESTATION_SIGNING_DOMAIN, TOKEN_SIGNING_DOMAIN, VerifiedTokenClaims,
};
use crate::{
    InternalError,
    cdk::types::Principal,
    dto::auth::{DelegationCert, RoleAttestation},
    ops::{auth::DelegationValidationError, prelude::*},
};
use candid::encode_one;
use sha2::{Digest, Sha256};

//
// TokenSigningPayload
//

#[derive(CandidType)]
struct TokenSigningPayload {
    cert_hash: Vec<u8>,
    claims: VerifiedTokenClaims,
}

pub(super) fn encode_candid<T: CandidType>(
    context: &'static str,
    value: &T,
) -> Result<Vec<u8>, InternalError> {
    encode_one(value).map_err(|err| {
        DelegationValidationError::EncodeFailed {
            context,
            source: err,
        }
        .into()
    })
}

pub(super) fn cert_hash(cert: &DelegationCert) -> [u8; 32] {
    hash_delegation_cert(cert)
}

pub(super) fn token_signing_hash(
    claims: &VerifiedTokenClaims,
    cert: &DelegationCert,
) -> Result<[u8; 32], InternalError> {
    let payload = TokenSigningPayload {
        cert_hash: cert_hash(cert).to_vec(),
        claims: claims.clone(),
    };

    let encoded = encode_candid("token signing payload", &payload)?;
    Ok(hash_domain_separated(TOKEN_SIGNING_DOMAIN, &encoded))
}

pub(super) fn hash_domain_separated(domain: &[u8], payload: &[u8]) -> [u8; 32] {
    let mut hasher = Sha256::new();
    hasher.update((domain.len() as u64).to_be_bytes());
    hasher.update(domain);
    hasher.update((payload.len() as u64).to_be_bytes());
    hasher.update(payload);
    hasher.finalize().into()
}

fn hash_delegation_cert(cert: &DelegationCert) -> [u8; 32] {
    let mut hasher = Sha256::new();
    hasher.update((CERT_SIGNING_DOMAIN.len() as u64).to_be_bytes());
    hasher.update(CERT_SIGNING_DOMAIN);
    update_principal(&mut hasher, cert.root_pid);
    update_principal(&mut hasher, cert.shard_pid);
    hasher.update(cert.issued_at.to_be_bytes());
    hasher.update(cert.expires_at.to_be_bytes());
    update_strings(&mut hasher, &cert.scopes);
    update_principals(&mut hasher, &cert.aud);
    hasher.finalize().into()
}

fn update_principal(hasher: &mut Sha256, principal: Principal) {
    update_bytes(hasher, principal.as_slice());
}

fn update_principals(hasher: &mut Sha256, principals: &[Principal]) {
    hasher.update((principals.len() as u64).to_be_bytes());
    for principal in principals {
        update_principal(hasher, *principal);
    }
}

fn update_strings(hasher: &mut Sha256, values: &[String]) {
    hasher.update((values.len() as u64).to_be_bytes());
    for value in values {
        update_bytes(hasher, value.as_bytes());
    }
}

fn update_bytes(hasher: &mut Sha256, bytes: &[u8]) {
    hasher.update((bytes.len() as u64).to_be_bytes());
    hasher.update(bytes);
}

pub(super) fn role_attestation_hash(
    attestation: &RoleAttestation,
) -> Result<[u8; 32], InternalError> {
    let payload = encode_candid("role attestation", attestation)?;
    let mut hasher = Sha256::new();
    hasher.update(ROLE_ATTESTATION_SIGNING_DOMAIN);
    hasher.update(payload);
    Ok(hasher.finalize().into())
}