structured_ltv/timestamp/
signature.rs1use super::{Interval, Purpose, TemporalError, Verified};
4use crate::pki::{SignerReceipt, TimestampPath};
5use crate::source::{Query, ScopeError};
6use structured_pkix::temporal::LeapTable;
7
8pub struct SignatureTime<'receipt, S, N, D, G, I> {
11 signer: &'receipt S,
12 timestamp: &'receipt Verified<N, D, G, I>,
13 generation: Interval,
14}
15
16impl<'receipt, S, N, D, G, I> SignatureTime<'receipt, S, N, D, G, I> {
17 #[must_use]
19 pub fn signer(&self) -> &'receipt S {
20 self.signer
21 }
22 #[must_use]
24 pub fn timestamp(&self) -> &'receipt Verified<N, D, G, I> {
25 self.timestamp
26 }
27 #[must_use]
29 pub fn generation(&self) -> Interval {
30 self.generation
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum SignatureTimeFailure {
37 Certificate,
39 Interval,
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum SignatureTimeError<N> {
46 Scope(ScopeError<N>),
48 Foreign {
50 property: N,
52 },
53 Purpose {
56 property: N,
58 },
59 Time {
61 property: N,
63 cause: TemporalError,
65 },
66}
67
68pub enum SignatureTimeBinding<'receipt, S, N, D, G, I> {
70 Bound(SignatureTime<'receipt, S, N, D, G, I>),
72 Unproven(SignatureTimeFailure),
74}
75
76pub type SignatureTimeResult<'receipt, S, Q, I> = Result<
78 SignatureTimeBinding<
79 'receipt,
80 S,
81 <Q as Query>::Id,
82 <Q as Query>::Document,
83 <Q as Query>::Generation,
84 I,
85 >,
86 SignatureTimeError<<Q as Query>::Id>,
87>;
88
89pub fn signature_time<'receipt, 'path, 'document, S, Q, I>(
99 signer: &'receipt S,
100 timestamp: &'receipt Verified<Q::Id, Q::Document, Q::Generation, I>,
101 utc: &LeapTable<'_>,
102 query: &Q,
103) -> SignatureTimeResult<'receipt, S, Q, I>
104where
105 'document: 'path,
106 S: SignerReceipt<
107 'path,
108 'document,
109 Id = Q::Id,
110 Document = Q::Document,
111 Generation = Q::Generation,
112 >,
113 Q: Query,
114{
115 bind(signer, timestamp, query, || {
116 timestamp.interval(utc, query.execution())
117 })
118}
119
120pub fn signature_time_path<'receipt, 'path, 'document, S, Q, I>(
125 signer: &'receipt S,
126 path: &'receipt TimestampPath<'_, '_, '_, Q::Id, Q::Document, Q::Generation, I>,
127 query: &Q,
128) -> SignatureTimeResult<'receipt, S, Q, I>
129where
130 'document: 'path,
131 S: SignerReceipt<
132 'path,
133 'document,
134 Id = Q::Id,
135 Document = Q::Document,
136 Generation = Q::Generation,
137 >,
138 Q: Query,
139{
140 bind(signer, path.actor().verified(), query, || {
141 Ok(path.generation())
142 })
143}
144
145fn bind<'receipt, 'path, 'document, S, Q, I>(
146 signer: &'receipt S,
147 timestamp: &'receipt Verified<Q::Id, Q::Document, Q::Generation, I>,
148 query: &Q,
149 interval: impl FnOnce() -> Result<Interval, TemporalError>,
150) -> SignatureTimeResult<'receipt, S, Q, I>
151where
152 'document: 'path,
153 S: SignerReceipt<
154 'path,
155 'document,
156 Id = Q::Id,
157 Document = Q::Document,
158 Generation = Q::Generation,
159 >,
160 Q: Query,
161{
162 let selected = signer.signature();
163 let property = timestamp.property();
164 if selected.snapshot != query.snapshot() || timestamp.token().snapshot != selected.snapshot {
165 return Err(SignatureTimeError::Foreign { property });
166 }
167 if !query
168 .equal(timestamp.signature(), selected.node)
169 .map_err(|cause| {
170 SignatureTimeError::Scope(ScopeError::Resource {
171 node: property,
172 cause,
173 })
174 })?
175 {
176 return Err(SignatureTimeError::Foreign { property });
177 }
178 query
179 .admit(selected.node, 1)
180 .map_err(SignatureTimeError::Scope)?;
181 if timestamp.purpose() != Purpose::Signature {
182 return Err(SignatureTimeError::Purpose { property });
183 }
184 let Some(path) = signer.path() else {
185 return Ok(SignatureTimeBinding::Unproven(
186 SignatureTimeFailure::Certificate,
187 ));
188 };
189 let generation = interval().map_err(|cause| SignatureTimeError::Time { property, cause })?;
190 query
191 .admit(selected.node, 1)
192 .map_err(SignatureTimeError::Scope)?;
193 let checked = path.interval();
194 if checked.from_instant() > generation.earliest || checked.to_instant() < generation.latest {
195 return Ok(SignatureTimeBinding::Unproven(
196 SignatureTimeFailure::Interval,
197 ));
198 }
199 Ok(SignatureTimeBinding::Bound(SignatureTime {
200 signer,
201 timestamp,
202 generation,
203 }))
204}