structured_pades/material/vri/
status.rs1use super::{BindingError, NameError, name_for_ranges};
4use crate::{
5 error::{Error, ErrorKind},
6 material::{MaterialError, Parsed, Value, Vri},
7 model::admit,
8};
9use structured_asn1::memory::Work;
10use structured_pkix::{algorithm::digest::AsnDigest, signature::SignatureValue};
11
12pub struct StatusBinding<'b, 'd> {
15 vri: &'b Vri,
16 item: usize,
17 signature: SignatureValue<'d>,
18}
19impl<'d> StatusBinding<'_, 'd> {
20 #[must_use]
22 pub fn vri(&self) -> &Vri {
23 self.vri
24 }
25 #[must_use]
27 pub fn item(&self) -> usize {
28 self.item
29 }
30 #[must_use]
32 pub fn signature(&self) -> SignatureValue<'d> {
33 self.signature
34 }
35}
36
37#[derive(Debug)]
39pub enum StatusError<'b, E> {
40 Binding(BindingError<E>),
42 Unreadable(&'b MaterialError),
44 Ocsp(&'b structured_pkix::Error),
46 NotStatus,
48 NotSigned,
50}
51
52impl<'v, D: Copy + Eq, G: Copy + Eq> Parsed<'v, '_, D, G> {
53 pub fn bind_status_vri<'b, 'p, P: AsnDigest<'p>>(
62 &'b self,
63 item: usize,
64 provider: &'p P,
65 operation: &impl Work,
66 ) -> Result<Option<StatusBinding<'b, 'v>>, StatusError<'b, P::Error>> {
67 let work = self.owner.session.query(operation);
68 let location = self.owner.items.get(item).map_or_else(
69 || {
70 crate::source::Location::new(
71 crate::source::Source::File,
72 0,
73 self.owner.stamp.length,
74 )
75 .expect("original file")
76 },
77 crate::material::Item::location,
78 );
79 admit(&work, location, 1)
80 .map_err(|cause| StatusError::Binding(BindingError::Pdf(cause)))?;
81 let value = self.values.get(item).ok_or_else(|| {
82 StatusError::Binding(BindingError::Pdf(Error {
83 kind: ErrorKind::Foreign,
84 location,
85 }))
86 })?;
87 let signature = match value {
88 Value::Crl(value) => value.signature_value(),
89 Value::Ocsp {
90 basic: Ok(Some(value)),
91 ..
92 } => value.signature_value(),
93 Value::Ocsp {
94 basic: Ok(None), ..
95 } => return Err(StatusError::NotSigned),
96 Value::Ocsp {
97 basic: Err(cause), ..
98 } => return Err(StatusError::Ocsp(cause)),
99 Value::Unreadable(cause) => return Err(StatusError::Unreadable(cause)),
100 _ => return Err(StatusError::NotStatus),
101 };
102 if self.owner.vri_names.is_empty() {
103 return Ok(None);
104 }
105 let name = status_name(signature, provider, &work, location)
106 .map_err(|cause| StatusError::Binding(BindingError::Name(cause)))?
107 .ok_or(StatusError::Binding(BindingError::UnsupportedName))?;
108 let matched = self.owner.vri_names.get(&name, &work).map_err(|cause| {
109 StatusError::Binding(BindingError::Pdf(Error {
110 kind: ErrorKind::Resource(cause),
111 location,
112 }))
113 })?;
114 Ok(matched.map(|&ordinal| StatusBinding {
115 vri: &self.owner.vri[ordinal],
116 item,
117 signature,
118 }))
119 }
120}
121
122pub(super) fn status_name<'p, P: AsnDigest<'p>>(
123 signature: SignatureValue<'_>,
124 provider: &'p P,
125 operation: &impl Work,
126 location: crate::source::Location,
127) -> Result<Option<super::Name>, NameError<P::Error>> {
128 let work = signature.execution_in(operation);
129 admit(&work, location, 1).map_err(NameError::Pdf)?;
130 let bytes = signature.octets().ok_or(NameError::PaddedSignature)?;
131 let mut header = [0; core::mem::size_of::<usize>() + 2];
132 header[0] = 4;
133 let end = if bytes.len() < 128 {
134 header[1] = u8::try_from(bytes.len()).expect("short length");
135 2
136 } else {
137 let length = bytes.len().to_be_bytes();
138 let mut first = 0;
139 while length[first] == 0 {
140 first += 1;
141 }
142 let count = length.len() - first;
143 header[1] = 0x80 | u8::try_from(count).expect("machine-sized length");
144 header[2..2 + count].copy_from_slice(&length[first..]);
145 2 + count
146 };
147 name_for_ranges(provider, &mut [&header[..end], bytes], &work, location)
148}