Skip to main content

structured_cades/
participants.rs

1//! Complete actual actor census for whole-signature and archive scopes.
2
3use crate::{
4    Cades, ISSUER,
5    basic::SignerBinding,
6    counters::Paths,
7    credential::Complete,
8    source::{DocumentId, Node},
9};
10use structured_ltv::{
11    pki::{self, ParticipantError, ParticipantRequirements},
12    source::{Query, Source},
13    timestamp::TimestampPaths,
14};
15
16/// Unavailable historical proofs remain distinct from source/census refusal.
17#[derive(Debug)]
18pub enum Error {
19    /// Original shared actor-census refusal.
20    Participants(ParticipantError<Node>),
21    /// The complete authority set of this actual historical proof is unavailable.
22    Preservation {
23        /// Original preservation attribute.
24        property: Node,
25        /// Exact historical mechanism.
26        kind: crate::preservation::Kind,
27    },
28    /// An actual parallel source could not supply a complete history census.
29    /// The original cause remains in preparation's preservation findings.
30    PreservationSource {
31        /// Original unreadable source or malformed historical property.
32        source: Node,
33    },
34}
35
36/// Derive every actual signer/counter/credential/TSA authority from the SAME
37/// sealed format receipts. The full original timestamp and counter census is
38/// checked by the existing LTV kernel; successful subsets cannot enter here.
39/// Use the returned census's `close_statuses` for the actual complete/delta
40/// CRL and OCSP authority closure. No parsing or authentication is repeated.
41pub fn participants<'view, 'path, 'm, 'd, E, Q>(
42    signer: &'view SignerBinding<'_, 'm, 'd, 'path>,
43    counters: &'view Paths<'_, 'm, 'd, 'path>,
44    credentials: &'view Complete<'_, 'm, 'd, 'path, E>,
45    timestamps: &'view TimestampPaths<'view, '_, 'd, Node, DocumentId<'m, 'd>, (), Cades>,
46    query: &Q,
47) -> Result<ParticipantRequirements<'view, 'd, Node, DocumentId<'m, 'd>, (), Cades>, Error>
48where
49    Q: Query<Id = Node, Document = DocumentId<'m, 'd>, Generation = ()>,
50    'path: 'view,
51    'd: 'path,
52{
53    participants_in(signer, counters, credentials, timestamps, None, query)
54}
55
56/// Same complete actor census after a source-bound ERS/PoE closure. Every
57/// actual contained TSA must STILL occur in the supplied `TimestampPaths`;
58/// historical proofs cannot substitute a successful subset or erase actors.
59pub fn participants_preserved<'view, 'path, 'm, 'd, E, Q>(
60    signer: &'view SignerBinding<'_, 'm, 'd, 'path>,
61    counters: &'view Paths<'_, 'm, 'd, 'path>,
62    credentials: &'view Complete<'_, 'm, 'd, 'path, E>,
63    timestamps: &'view TimestampPaths<'view, '_, 'd, Node, DocumentId<'m, 'd>, (), Cades>,
64    preservation: &crate::evidence::Closure<'_, '_, 'm, 'd, '_>,
65    query: &Q,
66) -> Result<ParticipantRequirements<'view, 'd, Node, DocumentId<'m, 'd>, (), Cades>, Error>
67where
68    Q: Query<Id = Node, Document = DocumentId<'m, 'd>, Generation = ()>,
69    'path: 'view,
70    'd: 'path,
71{
72    participants_in(
73        signer,
74        counters,
75        credentials,
76        timestamps,
77        Some(preservation),
78        query,
79    )
80}
81fn participants_in<'view, 'path, 'm, 'd, E, Q>(
82    signer: &'view SignerBinding<'_, 'm, 'd, 'path>,
83    counters: &'view Paths<'_, 'm, 'd, 'path>,
84    credentials: &'view Complete<'_, 'm, 'd, 'path, E>,
85    timestamps: &'view TimestampPaths<'view, '_, 'd, Node, DocumentId<'m, 'd>, (), Cades>,
86    preservation: Option<&crate::evidence::Closure<'_, '_, 'm, 'd, '_>>,
87    query: &Q,
88) -> Result<ParticipantRequirements<'view, 'd, Node, DocumentId<'m, 'd>, (), Cades>, Error>
89where
90    Q: Query<Id = Node, Document = DocumentId<'m, 'd>, Generation = ()>,
91    'path: 'view,
92    'd: 'path,
93{
94    let prepared = signer.prepared();
95    if let Some(proofs) = preservation
96        && !core::ptr::eq(proofs.prepared(), prepared)
97    {
98        return Err(Error::Participants(ParticipantError::Foreign {
99            signature: Node::of(prepared.signer().span()),
100        }));
101    }
102    let selected = prepared.document().signer(prepared.signer()).map_err(|_| {
103        Error::Participants(ParticipantError::Foreign {
104            signature: Node::of(prepared.signer().span()),
105        })
106    })?;
107    if prepared.document().snapshot() != query.snapshot() {
108        return Err(Error::Participants(ParticipantError::Foreign {
109            signature: selected.node,
110        }));
111    }
112    query
113        .admit(selected.node, 1)
114        .map_err(|cause| Error::Participants(ParticipantError::Scope(cause)))?;
115    for required in prepared.preservation() {
116        query
117            .admit(required.property, 1)
118            .map_err(|cause| Error::Participants(ParticipantError::Scope(cause)))?;
119        // Archives and reference timestamps have distinct format-selected
120        // input checks. The SAME complete timestamp census below requires
121        // their actual actors; ERS/PoE also requires its sealed input/history.
122        if !required.kind.is_archive() && !required.kind.is_reference_timestamp() {
123            if let Some(proofs) = preservation
124                && required.kind.is_evidence()
125                && proofs
126                    .contains(required, query.execution())
127                    .map_err(|cause| {
128                        Error::Participants(ParticipantError::Resource {
129                            node: required.property,
130                            cause,
131                        })
132                    })?
133            {
134                continue;
135            }
136
137            return Err(Error::Preservation {
138                property: required.property,
139                kind: required.kind,
140            });
141        }
142    }
143    if !prepared.preservation().is_empty() {
144        for finding in prepared.preservation_findings() {
145            query
146                .admit(finding.location.node, 1)
147                .map_err(|cause| Error::Participants(ParticipantError::Scope(cause)))?;
148            if finding.severity == crate::prepare::Severity::Required {
149                return Err(Error::PreservationSource {
150                    source: finding.location.node,
151                });
152            }
153        }
154    }
155    pki::participants(ISSUER, signer, counters, credentials, timestamps, query)
156        .map_err(Error::Participants)
157}