use super::Meter;
use crate::constraints::{CertificateCount, InhibitAnyPolicy, PolicyConstraints};
use crate::oid;
use crate::policy::{CertificatePolicies, PolicyMappings};
use structured_asn1::Oid;
use structured_asn1::memory::{Memory, MemoryError, OwnedMap, OwnedVec};
#[derive(PartialEq, Eq, Hash)]
struct Carried<'a>(usize, Oid<'a>);
impl structured_asn1::memory::HashWork for Carried<'_> {
fn hash_work(&self) -> Result<u64, MemoryError> {
structured_asn1::memory::HashWork::hash_work(&self.1)?
.checked_add(1)
.ok_or(MemoryError::WorkExceeded)
}
}
const WORKED: u64 = super::cost::ENTRY;
fn identified(policy: Oid<'_>) -> u64 {
policy.as_bytes().len() as u64 / super::cost::COMPARED
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum Overflow {
Limit,
Resource(crate::Error),
}
impl From<crate::Error> for Overflow {
fn from(error: crate::Error) -> Self {
Self::Resource(error)
}
}
fn resource(reason: MemoryError, offset: u64) -> Overflow {
Overflow::Resource(crate::Error::resource(
reason,
"certificationPath.policies",
offset,
))
}
fn group<'a, T>(
index: &mut OwnedMap<Oid<'a>, OwnedVec<T>>,
key: Oid<'a>,
value: T,
execution: &Memory,
offset: u64,
) -> Result<(), Overflow> {
let fail = |reason| resource(reason, offset);
if index.get(&key, execution).map_err(fail)?.is_none() {
index.try_insert(key, execution.vector()).map_err(fail)?;
}
index
.get_mut(&key, execution)
.map_err(fail)?
.expect("the admitted group")
.try_push(value)
.map_err(fail)
}
#[derive(Debug, Clone, Copy)]
pub(super) struct Initial {
pub(super) explicit_policy: bool,
pub(super) policy_mapping: bool,
pub(super) any_policy: bool,
}
impl Default for Initial {
fn default() -> Self {
Self {
explicit_policy: false,
policy_mapping: true,
any_policy: true,
}
}
}
#[derive(Debug)]
struct Node<'a> {
policy: Oid<'a>,
expected: OwnedVec<Oid<'a>>,
parent: Option<usize>,
depth: u32,
children: u32,
live: bool,
}
impl<'a> Node<'a> {
fn expects_count(&self) -> usize {
self.expected.len().max(1)
}
fn expects_at(&self, at: usize) -> Oid<'a> {
self.expected.get(at).copied().unwrap_or(self.policy)
}
}
#[derive(Debug)]
pub(super) struct Processing<'a> {
nodes: Option<OwnedVec<Node<'a>>>,
execution: Memory,
offset: u64,
depth: u32,
explicit_policy: u32,
policy_mapping: u32,
inhibit_any_policy: u32,
expectations: usize,
ceiling: usize,
}
impl<'a> Processing<'a> {
pub(super) fn new(
length: u32,
initial: Initial,
ceiling: usize,
execution: &Memory,
offset: u64,
) -> Result<Self, Overflow> {
if ceiling == 0 {
return Err(Overflow::Limit);
}
let never = length
.checked_add(1)
.ok_or_else(|| resource(MemoryError::CapacityOverflow, offset))?;
let mut nodes = execution.vector();
nodes
.try_reserve_exact(1)
.map_err(|reason| resource(reason, offset))?;
nodes
.try_push(Node {
policy: oid::ANY_POLICY,
expected: execution.vector(),
parent: None,
depth: 0,
children: 0,
live: true,
})
.map_err(|reason| resource(reason, offset))?;
Ok(Self {
nodes: Some(nodes),
execution: execution.clone(),
offset,
depth: 0,
explicit_policy: if initial.explicit_policy { 0 } else { never },
policy_mapping: if initial.policy_mapping { never } else { 0 },
inhibit_any_policy: if initial.any_policy { never } else { 0 },
expectations: 0,
ceiling,
})
}
pub(super) fn nodes(&self) -> usize {
self.nodes.as_ref().map_or(0, |nodes| nodes.len())
}
pub(super) fn present(
&mut self,
policies: Option<&CertificatePolicies<'a>>,
self_issued: bool,
last: bool,
meter: &mut impl Meter,
) -> Result<bool, Overflow> {
self.depth += 1;
match policies {
Some(policies) => self.grow(policies, self_issued, last, meter)?,
None => self.nodes = None,
}
Ok(self.explicit_policy > 0 || self.nodes.is_some())
}
fn grow(
&mut self,
policies: &CertificatePolicies<'a>,
self_issued: bool,
last: bool,
meter: &mut impl Meter,
) -> Result<(), Overflow> {
let Some(nodes) = self.nodes.as_mut() else {
return Ok(());
};
let depth = self.depth;
let parents = depth - 1;
let ceiling = self.ceiling;
let held = self.expectations;
let fail = |reason| resource(reason, self.offset);
let mut expecting: OwnedMap<Oid<'a>, OwnedVec<usize>> = self.execution.map();
let mut anywhere = self.execution.vector();
for at in 0..nodes.len() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if !live_at(nodes, at, parents) {
continue;
}
if nodes[at].policy == oid::ANY_POLICY {
anywhere.try_push(at).map_err(fail)?;
}
for which in 0..nodes[at].expects_count() {
let expected = nodes[at].expects_at(which);
if !meter.spend(WORKED + identified(expected))? {
return Err(Overflow::Limit);
}
group(&mut expecting, expected, at, &self.execution, self.offset)?;
}
}
let mut has_any = false;
for named in policies.policies() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
let policy = named.policy();
if policy == oid::ANY_POLICY {
has_any = true;
continue;
}
if !meter.spend(identified(policy))? {
return Err(Overflow::Limit);
}
match expecting.get(&policy, &self.execution).map_err(fail)? {
Some(parents) => {
for at in parents.iter().copied() {
push(
nodes,
at,
policy,
self.execution.vector(),
depth,
(held, ceiling, self.offset),
)?;
}
}
None => {
for at in anywhere.iter().copied() {
push(
nodes,
at,
policy,
self.execution.vector(),
depth,
(held, ceiling, self.offset),
)?;
}
}
}
}
if has_any && (self.inhibit_any_policy > 0 || (self_issued && !last)) {
carry_any(
nodes,
depth,
(held, ceiling, self.offset),
&self.execution,
meter,
)?;
}
prune(nodes, parents, meter)?;
if !nodes[0].live {
self.nodes = None;
}
Ok(())
}
pub(super) fn carry(
&mut self,
mappings: Option<&PolicyMappings<'a>>,
constraints: Option<PolicyConstraints>,
inhibit: Option<InhibitAnyPolicy>,
self_issued: bool,
meter: &mut impl Meter,
) -> Result<(), Overflow> {
if let Some(mappings) = mappings {
self.map(mappings, meter)?;
}
if !self_issued {
self.explicit_policy = self.explicit_policy.saturating_sub(1);
self.policy_mapping = self.policy_mapping.saturating_sub(1);
self.inhibit_any_policy = self.inhibit_any_policy.saturating_sub(1);
}
if let Some(constraints) = constraints {
narrow(
&mut self.explicit_policy,
constraints.require_explicit_policy(),
);
narrow(
&mut self.policy_mapping,
constraints.inhibit_policy_mapping(),
);
}
if let Some(inhibit) = inhibit {
narrow(&mut self.inhibit_any_policy, Some(inhibit.skip()));
}
Ok(())
}
fn map(
&mut self,
mappings: &PolicyMappings<'a>,
meter: &mut impl Meter,
) -> Result<(), Overflow> {
if self.nodes.is_none() {
return Ok(());
}
if self.policy_mapping == 0 {
return self.unmap(mappings, meter);
}
self.remap(mappings, meter)
}
fn unmap(
&mut self,
mappings: &PolicyMappings<'a>,
meter: &mut impl Meter,
) -> Result<(), Overflow> {
{
let depth = self.depth;
let Some(nodes) = self.nodes.as_mut() else {
return Ok(());
};
let fail = |reason| resource(reason, self.offset);
let mut standing: OwnedMap<Oid<'a>, OwnedVec<usize>> = self.execution.map();
for at in 0..nodes.len() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if live_at(nodes, at, depth) {
if !meter.spend(identified(nodes[at].policy))? {
return Err(Overflow::Limit);
}
group(
&mut standing,
nodes[at].policy,
at,
&self.execution,
self.offset,
)?;
}
}
for mapping in mappings.mappings() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
let issuer = mapping.issuer_domain();
if issuer == oid::ANY_POLICY || mapping.subject_domain() == oid::ANY_POLICY {
continue;
}
if !meter.spend(identified(issuer))? {
return Err(Overflow::Limit);
}
let Some(named) = standing.remove(&issuer, &self.execution).map_err(fail)? else {
continue;
};
for at in named {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
kill(nodes, at, meter)?;
}
}
if !nodes[0].live {
self.nodes = None;
}
}
Ok(())
}
fn remap(
&mut self,
mappings: &PolicyMappings<'a>,
meter: &mut impl Meter,
) -> Result<(), Overflow> {
let entries = mappings.mappings().len();
debug_assert!(self.nodes() <= self.ceiling);
if self.expectations > self.ceiling - self.nodes()
|| entries > self.ceiling - self.nodes() - self.expectations
{
return Err(Overflow::Limit);
}
self.expectations += entries;
let fail = |reason| resource(reason, self.offset);
let mut order = self.execution.vector();
let mut grouped: OwnedMap<Oid<'a>, OwnedVec<Oid<'a>>> = self.execution.map();
for mapping in mappings.mappings() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
let issuer = mapping.issuer_domain();
if issuer == oid::ANY_POLICY || mapping.subject_domain() == oid::ANY_POLICY {
continue;
}
let subject = mapping.subject_domain();
if !meter.spend(identified(issuer) + identified(subject))? {
return Err(Overflow::Limit);
}
if grouped
.get(&issuer, &self.execution)
.map_err(fail)?
.is_none()
{
order.try_push(issuer).map_err(fail)?;
}
group(&mut grouped, issuer, subject, &self.execution, self.offset)?;
}
let Some(nodes) = self.nodes.as_mut() else {
return Ok(());
};
let depth = self.depth;
let ceiling = self.ceiling;
let Level { standing, anywhere } =
level(nodes, depth, &self.execution, self.offset, meter)?;
for issuer in order {
if !meter.spend(identified(issuer))? {
return Err(Overflow::Limit);
}
let subjects = grouped
.remove(&issuer, &self.execution)
.map_err(fail)?
.expect("an issuer just grouped");
let mut matched = false;
if let Some(indices) = standing.get(&issuer, &self.execution).map_err(fail)? {
for at in indices.iter().copied() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if live_at(nodes, at, depth) {
debug_assert!(self.expectations >= nodes[at].expected.len());
let retained = self.expectations - nodes[at].expected.len();
if retained > ceiling - nodes.len()
|| subjects.len() > ceiling - nodes.len() - retained
{
return Err(Overflow::Limit);
}
let copy = subjects.try_copy().map_err(fail)?;
nodes[at].expected = copy;
self.expectations = retained + subjects.len();
matched = true;
}
}
}
if matched {
continue;
}
for at in anywhere.iter().copied() {
if live_at(nodes, at, depth) {
if self.expectations > ceiling - nodes.len()
|| subjects.len() > ceiling - nodes.len() - self.expectations
{
return Err(Overflow::Limit);
}
let expectations = self.expectations + subjects.len();
push(
nodes,
at,
issuer,
subjects,
depth,
(expectations, ceiling, self.offset),
)?;
self.expectations = expectations;
break;
}
}
}
debug_assert!(self.expectations >= entries);
self.expectations -= entries;
Ok(())
}
pub(super) fn settle(
mut self,
constraints: Option<PolicyConstraints>,
wanted: &'a [Oid<'a>],
meter: &mut impl Meter,
) -> Result<Settled<'a>, Overflow> {
self.explicit_policy = self.explicit_policy.saturating_sub(1);
if let Some(constraints) = constraints
&& constraints
.require_explicit_policy()
.is_some_and(CertificateCount::is_zero)
{
self.explicit_policy = 0;
}
let policies = self.intersect(wanted, meter)?;
Ok(Settled {
satisfied: self.explicit_policy > 0 || !policies.is_empty(),
policies,
})
}
fn intersect(
&mut self,
wanted: &'a [Oid<'a>],
meter: &mut impl Meter,
) -> Result<OwnedVec<Oid<'a>>, Overflow> {
let depth = self.depth;
let ceiling = self.ceiling;
let held = self.expectations;
let terminal_any = self.any_policy_at(depth, meter)?;
let anywhere = match (self.nodes.as_ref(), terminal_any) {
(Some(nodes), Some(at)) => nodes[at].parent,
_ => None,
};
let Some(nodes) = self.nodes.as_mut() else {
return Ok(self.execution.vector());
};
let fail = |reason| resource(reason, self.offset);
let mut requested: OwnedMap<Oid<'a>, ()> = self.execution.map();
for policy in wanted {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
requested.try_insert(*policy, ()).map_err(fail)?;
}
let unrestricted = wanted.is_empty()
|| requested
.get(&oid::ANY_POLICY, &self.execution)
.map_err(fail)?
.is_some();
if !unrestricted {
for at in 0..nodes.len() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
let Some(parent) = nodes[at].parent else {
continue;
};
if !nodes[at].live || nodes[parent].policy != oid::ANY_POLICY {
continue;
}
let policy = nodes[at].policy;
if policy != oid::ANY_POLICY
&& requested
.get(&policy, &self.execution)
.map_err(fail)?
.is_none()
{
cut(nodes, at, meter)?;
}
}
if let (Some(parent), Some(any)) = (anywhere, terminal_any) {
let mut standing: OwnedMap<Oid<'a>, ()> = self.execution.map();
for node in nodes.iter() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if node.live
&& node
.parent
.is_some_and(|above| nodes[above].policy == oid::ANY_POLICY)
{
standing.try_insert(node.policy, ()).map_err(fail)?;
}
}
for policy in wanted {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
let policy = *policy;
if standing.try_insert(policy, ()).map_err(fail)?.is_some() {
continue;
}
push(
nodes,
parent,
policy,
self.execution.vector(),
depth,
(held, ceiling, self.offset),
)?;
}
kill(nodes, any, meter)?;
}
}
prune(nodes, depth.saturating_sub(1), meter)?;
if !nodes[0].live {
self.nodes = None;
return Ok(self.execution.vector());
}
terminal_policies(nodes, depth, &self.execution, self.offset, meter)
}
fn any_policy_at(&self, depth: u32, meter: &mut impl Meter) -> Result<Option<usize>, Overflow> {
if let Some(nodes) = self.nodes.as_ref() {
for (at, node) in nodes.iter().enumerate() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if node.live && node.depth == depth && node.policy == oid::ANY_POLICY {
return Ok(Some(at));
}
}
}
Ok(None)
}
}
fn carry_any<'a>(
nodes: &mut OwnedVec<Node<'a>>,
depth: u32,
room: (usize, usize, u64),
execution: &Memory,
meter: &mut impl Meter,
) -> Result<(), Overflow> {
let (held, ceiling, offset) = room;
let parents = depth - 1;
let fail = |reason| resource(reason, offset);
let mut carried: OwnedMap<Carried<'a>, ()> = execution.map();
for node in nodes.iter() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if let Some(parent) = node.parent
&& node.live
{
if !meter.spend(identified(node.policy))? {
return Err(Overflow::Limit);
}
carried
.try_insert(Carried(parent, node.policy), ())
.map_err(fail)?;
}
}
for at in 0..nodes.len() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if !live_at(nodes, at, parents) {
continue;
}
for expected in 0..nodes[at].expects_count() {
let policy = nodes[at].expects_at(expected);
if !meter.spend(WORKED + identified(policy))? {
return Err(Overflow::Limit);
}
if carried
.try_insert(Carried(at, policy), ())
.map_err(fail)?
.is_some()
{
continue;
}
push(
nodes,
at,
policy,
execution.vector(),
depth,
(held, ceiling, offset),
)?;
}
}
Ok(())
}
fn terminal_policies<'a>(
nodes: &[Node<'a>],
depth: u32,
execution: &Memory,
offset: u64,
meter: &mut impl Meter,
) -> Result<OwnedVec<Oid<'a>>, Overflow> {
let fail = |reason| resource(reason, offset);
let mut policies = execution.vector();
for node in nodes {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if node.live && node.depth == depth {
policies.try_push(node.policy).map_err(fail)?;
}
}
execution.charge_work(policies.len() as u64).map_err(fail)?;
let bytes = policies
.iter()
.map(|policy| policy.as_bytes().len() as u64)
.try_fold(0u64, |sum, bytes| {
sum.checked_add(bytes).ok_or(MemoryError::WorkExceeded)
})
.map_err(fail)?;
let rounds = u64::from(policies.len().max(1).ilog2() + 1);
let work = rounds
.checked_mul(bytes)
.ok_or_else(|| fail(MemoryError::WorkExceeded))?;
if !meter.spend(work / super::cost::COMPARED)? {
return Err(Overflow::Limit);
}
policies
.try_sort_unstable_by(|left, right| {
execution.charge_work(left.as_bytes().len().max(right.as_bytes().len()) as u64)?;
Ok::<_, MemoryError>(left.as_bytes().cmp(right.as_bytes()))
})
.map_err(|error| fail(error.into()))?;
let mut unique = 0;
for at in 0..policies.len() {
if unique > 0 {
execution
.charge_work(
policies[at]
.as_bytes()
.len()
.max(policies[unique - 1].as_bytes().len()) as u64,
)
.map_err(fail)?;
if policies[at] == policies[unique - 1] {
continue;
}
}
execution
.charge_work(core::mem::size_of::<Oid<'a>>() as u64)
.map_err(fail)?;
policies[unique] = policies[at];
unique += 1;
}
while policies.len() > unique {
execution.charge_work(1).map_err(fail)?;
policies.pop();
}
Ok(policies)
}
struct Level<'a> {
standing: OwnedMap<Oid<'a>, OwnedVec<usize>>,
anywhere: OwnedVec<usize>,
}
fn level<'a>(
nodes: &[Node<'a>],
depth: u32,
execution: &Memory,
offset: u64,
meter: &mut impl Meter,
) -> Result<Level<'a>, Overflow> {
let fail = |reason| resource(reason, offset);
let mut standing: OwnedMap<Oid<'a>, OwnedVec<usize>> = execution.map();
let mut anywhere = execution.vector();
for at in 0..nodes.len() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if !live_at(nodes, at, depth) {
continue;
}
if nodes[at].policy == oid::ANY_POLICY {
anywhere.try_push(at).map_err(fail)?;
}
if !meter.spend(identified(nodes[at].policy))? {
return Err(Overflow::Limit);
}
group(&mut standing, nodes[at].policy, at, execution, offset)?;
}
Ok(Level { standing, anywhere })
}
#[derive(Debug)]
pub(super) struct Settled<'a> {
policies: OwnedVec<Oid<'a>>,
satisfied: bool,
}
impl<'a> Settled<'a> {
#[cfg(test)]
pub(super) fn policies(&self) -> &[Oid<'a>] {
&self.policies
}
pub(super) fn is_satisfied(&self) -> bool {
self.satisfied
}
pub(super) fn into_policies(self) -> OwnedVec<Oid<'a>> {
self.policies
}
}
fn live_at(nodes: &[Node<'_>], at: usize, depth: u32) -> bool {
nodes[at].live && nodes[at].depth == depth
}
fn push<'a>(
nodes: &mut OwnedVec<Node<'a>>,
parent: usize,
policy: Oid<'a>,
expected: OwnedVec<Oid<'a>>,
depth: u32,
room: (usize, usize, u64),
) -> Result<(), Overflow> {
let (held, ceiling, offset) = room;
if held >= ceiling || nodes.len() >= ceiling - held {
return Err(Overflow::Limit);
}
let children = nodes[parent]
.children
.checked_add(1)
.ok_or_else(|| resource(MemoryError::CapacityOverflow, offset))?;
nodes
.try_push(Node {
policy,
expected,
parent: Some(parent),
depth,
children: 0,
live: true,
})
.map_err(|reason| resource(reason, offset))?;
nodes[parent].children = children;
Ok(())
}
fn kill(nodes: &mut [Node<'_>], at: usize, meter: &mut impl Meter) -> Result<(), Overflow> {
let mut at = at;
loop {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if !nodes[at].live {
return Ok(());
}
nodes[at].live = false;
let Some(parent) = nodes[at].parent else {
return Ok(());
};
nodes[parent].children -= 1;
if nodes[parent].children > 0 {
return Ok(());
}
at = parent;
}
}
fn cut(nodes: &mut [Node<'_>], at: usize, meter: &mut impl Meter) -> Result<(), Overflow> {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if !nodes[at].live {
return Ok(());
}
nodes[at].live = false;
for below in at + 1..nodes.len() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
let orphaned = nodes[below].parent.is_some_and(|above| !nodes[above].live);
if nodes[below].live && orphaned {
nodes[below].live = false;
}
}
let mut at = at;
loop {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
let Some(parent) = nodes[at].parent else {
return Ok(());
};
nodes[parent].children -= 1;
if nodes[parent].children > 0 || !nodes[parent].live {
return Ok(());
}
nodes[parent].live = false;
at = parent;
}
}
fn prune(nodes: &mut [Node<'_>], depth: u32, meter: &mut impl Meter) -> Result<(), Overflow> {
for at in 0..nodes.len() {
if !meter.spend(WORKED)? {
return Err(Overflow::Limit);
}
if nodes[at].live && nodes[at].depth == depth && nodes[at].children == 0 {
kill(nodes, at, meter)?;
}
}
Ok(())
}
fn narrow(counter: &mut u32, count: Option<CertificateCount>) {
if let Some(count) = count.and_then(CertificateCount::certificates)
&& count < *counter
{
*counter = count;
}
}
#[cfg(test)]
mod tests;