1use super::{IssuerSerial, ObjectDigestInfo, end, issuer_serial, object_digest, sequence, tag};
9use crate::signer::required;
10use crate::{Error, ErrorCode, Result};
11use core::ops::Range;
12use structured_asn1::memory::{Execution, OwnedVec, Work};
13use structured_asn1::{Class, Element, Oid, Parser, ScopedReader, Span, Tag};
14use structured_pkix::Extension;
15use structured_pkix::general_name::{self, GeneralName};
16
17pub(super) const FIELD: &str = "attributeCertificate.targetInformation";
18
19pub const IDENTIFIER: Oid<'static> = Oid::from_bytes(&[0x55, 0x1d, 55]);
21
22#[derive(Debug)]
24pub struct Group<'a> {
25 pub element: Element<'a>,
27 pub targets: Range<usize>,
29}
30
31#[derive(Debug)]
33pub struct Target<'a> {
34 pub element: Element<'a>,
36 pub group: usize,
38 pub value: Value<'a>,
40}
41
42#[derive(Debug)]
44pub enum Value<'a> {
45 Name(GeneralName<'a>),
47 Group(GeneralName<'a>),
49 Certificate(usize),
51}
52
53#[derive(Debug)]
55pub struct TargetCertificate<'a> {
56 pub target: usize,
58 pub element: Element<'a>,
60 pub certificate: IssuerSerial<'a>,
62 pub name: Option<GeneralName<'a>>,
64 pub digest: Option<ObjectDigestInfo<'a>>,
66}
67
68#[derive(Debug)]
71pub struct TargetInformation<'a> {
72 parser: &'a Parser,
73 element: Element<'a>,
74 critical: bool,
75 groups: OwnedVec<Group<'a>>,
76 targets: OwnedVec<Target<'a>>,
77 certificates: OwnedVec<TargetCertificate<'a>>,
78}
79
80impl<'a> TargetInformation<'a> {
81 pub fn read<W: Work>(extension: &Extension<'a>, work: &W) -> Result<Self> {
86 Self::read_as(extension, work, IDENTIFIER, FIELD)
87 }
88
89 pub(super) fn read_as<W: Work>(
90 extension: &Extension<'a>,
91 work: &W,
92 identifier: Oid<'_>,
93 field: &'static str,
94 ) -> Result<Self> {
95 let execution = work.execution_for(extension.parser());
96 execution
97 .charge_work(identifier.as_bytes().len() as u64 + 1)
98 .map_err(|e| Error::resource(e, field, extension.span().start))?;
99 if extension.kind() != identifier {
100 return Err(Error::schema(
101 ErrorCode::UnexpectedContentType,
102 field,
103 extension.span().start,
104 ));
105 }
106 let mut reader = extension
107 .reader_scoped(&execution)
108 .map_err(|e| Error::encoding(e, field))?;
109 let element = required(&mut reader, field)?;
110 tag(&element, sequence(), field)?;
111 end(&reader, field)?;
112 let mut inside = reader
113 .enter(&element)
114 .map_err(|e| Error::encoding(e, field))?;
115 let mut groups = execution.vector();
116 let mut targets = execution.vector();
117 let mut certificates = execution.vector();
118 while let Some(group) = inside
119 .next_element()
120 .map_err(|e| Error::encoding(e, field))?
121 {
122 tag(&group, sequence(), field)?;
123 groups
124 .try_reserve(1)
125 .map_err(|e| Error::resource(e, field, group.span().start))?;
126 let start = targets.len();
127 let mut entries = inside
128 .enter(&group)
129 .map_err(|e| Error::encoding(e, field))?;
130 while let Some(entry) = entries
131 .next_element()
132 .map_err(|e| Error::encoding(e, field))?
133 {
134 let at = entry.span().start;
135 targets
136 .try_reserve(1)
137 .map_err(|e| Error::resource(e, field, at))?;
138 let value = if entry.tag() == Tag::context(true, 0)
139 || entry.tag() == Tag::context(true, 1)
140 {
141 let mut name = entries
144 .enter(&entry)
145 .map_err(|e| Error::encoding(e, field))?;
146 let encoded = required(&mut name, field)?;
147 let decoded =
148 general_name::read(&name, &encoded, field).map_err(Error::pkix)?;
149 end(&name, field)?;
150 if entry.tag() == Tag::context(true, 0) {
151 Value::Name(decoded)
152 } else {
153 Value::Group(decoded)
154 }
155 } else if entry.tag() == Tag::context(true, 2) {
156 certificates
157 .try_reserve(1)
158 .map_err(|e| Error::resource(e, field, at))?;
159 let decoded = certificate(&entries, &entry, targets.len(), field)?;
160 let index = certificates.len();
161 certificates
162 .try_push(decoded)
163 .map_err(|e| Error::resource(e, field, at))?;
164 Value::Certificate(index)
165 } else {
166 return Err(Error::schema(ErrorCode::UnexpectedTag, field, at));
167 };
168 targets
169 .try_push(Target {
170 element: entry,
171 group: groups.len(),
172 value,
173 })
174 .map_err(|e| Error::resource(e, field, at))?;
175 }
176 groups
177 .try_push(Group {
178 element: group,
179 targets: start..targets.len(),
180 })
181 .map_err(|e| Error::resource(e, field, group.span().start))?;
182 }
183 Ok(Self {
184 parser: extension.parser(),
185 element,
186 critical: extension.is_critical(),
187 groups: groups.into_owned(),
188 targets: targets.into_owned(),
189 certificates: certificates.into_owned(),
190 })
191 }
192
193 #[must_use]
195 pub fn element(&self) -> Element<'a> {
196 self.element
197 }
198 #[must_use]
200 pub fn is_critical(&self) -> bool {
201 self.critical
202 }
203 #[must_use]
206 pub fn groups(&self) -> &[Group<'a>] {
207 &self.groups
208 }
209 #[must_use]
211 pub fn targets(&self) -> &[Target<'a>] {
212 &self.targets
213 }
214 #[must_use]
216 pub fn certificates(&self) -> &[TargetCertificate<'a>] {
217 &self.certificates
218 }
219
220 pub fn check<W: Work, E>(
237 &self,
238 recipients: &[GeneralName<'_>],
239 work: &W,
240 mut membership: impl FnMut(
241 &GeneralName<'a>,
242 &Execution<'_>,
243 ) -> core::result::Result<Membership, E>,
244 ) -> core::result::Result<Decision, CheckError<E>> {
245 let execution = work.execution_for(self.parser);
246 let pay = |at| {
247 execution
248 .charge_work(1)
249 .map_err(|e| CheckError::Processing(Error::resource(e, FIELD, at)))
250 };
251 pay(self.element.span().start)?;
252 if !self.critical {
253 return Ok(Decision::NonCritical);
254 }
255 if let Some(certificate) = self.certificates.first() {
256 return Ok(Decision::ProhibitedTargetCertificate {
257 target: certificate.target,
258 });
259 }
260 let mut unknown = false;
261 for (index, target) in self.targets.iter().enumerate() {
262 match matches_target(target, recipients, &execution, FIELD, &mut membership)? {
263 Membership::Member => return Ok(Decision::Matches { target: index }),
264 Membership::NotMember => {}
265 Membership::Unknown => unknown = true,
266 }
267 }
268 Ok(if unknown {
269 Decision::Unproven
270 } else {
271 Decision::Differs
272 })
273 }
274}
275
276pub(super) fn opaque(name: &GeneralName<'_>) -> bool {
277 matches!(
278 name,
279 GeneralName::Other { .. } | GeneralName::X400(_) | GeneralName::EdiParty(_)
280 )
281}
282
283pub(super) fn matches_target<'a, E>(
286 target: &Target<'a>,
287 recipients: &[GeneralName<'_>],
288 execution: &Execution<'_>,
289 field: &'static str,
290 membership: &mut impl FnMut(&GeneralName<'a>, &Execution<'_>) -> core::result::Result<Membership, E>,
291) -> core::result::Result<Membership, CheckError<E>> {
292 let at = target.element.span().start;
293 let pay = || {
294 execution
295 .charge_work(1)
296 .map_err(|e| CheckError::Processing(Error::resource(e, field, at)))
297 };
298 pay()?;
299 match &target.value {
300 Value::Name(name) => {
301 if opaque(name) {
302 return Ok(Membership::Unknown);
303 }
304 for recipient in recipients {
305 pay()?;
306 if general_name::matches_at(name, recipient, execution, at, field)
307 .map_err(|e| CheckError::Processing(Error::pkix(e)))?
308 {
309 return Ok(Membership::Member);
310 }
311 }
312 Ok(Membership::NotMember)
313 }
314 Value::Group(name) => membership(name, execution).map_err(|source| CheckError::Callback {
315 target: target.element.span(),
316 source,
317 }),
318 Value::Certificate(_) => unreachable!("complete certificate preflight"),
319 }
320}
321
322fn certificate<'a>(
323 parent: &ScopedReader<'a, '_>,
324 element: &Element<'a>,
325 target: usize,
326 field: &'static str,
327) -> Result<TargetCertificate<'a>> {
328 let mut inside = parent
329 .enter(element)
330 .map_err(|e| Error::encoding(e, field))?;
331 let issuer = required(&mut inside, field)?;
332 tag(&issuer, sequence(), field)?;
333 let certificate = issuer_serial(&inside, &issuer, field)?;
334 let mut next = inside
335 .next_element()
336 .map_err(|e| Error::encoding(e, field))?;
337 let name = if next
338 .as_ref()
339 .is_some_and(|e| e.tag().class() == Class::ContextSpecific)
340 {
341 let decoded = general_name::read(&inside, next.as_ref().expect("present name"), field)
342 .map_err(Error::pkix)?;
343 next = inside
344 .next_element()
345 .map_err(|e| Error::encoding(e, field))?;
346 Some(decoded)
347 } else {
348 None
349 };
350 let digest = if let Some(value) = next {
351 tag(&value, sequence(), field)?;
352 Some(object_digest(&inside, &value, field)?)
353 } else {
354 None
355 };
356 end(&inside, field)?;
357 Ok(TargetCertificate {
358 target,
359 element: *element,
360 certificate,
361 name,
362 digest,
363 })
364}
365
366#[derive(Debug, Clone, Copy, PartialEq, Eq)]
368pub enum Membership {
369 Member,
371 NotMember,
373 Unknown,
375}
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq)]
379pub enum Decision {
380 Matches {
382 target: usize,
384 },
385 Differs,
387 Unproven,
389 NonCritical,
391 ProhibitedTargetCertificate {
393 target: usize,
395 },
396}
397
398#[derive(Debug)]
400pub enum CheckError<E> {
401 Processing(Error),
403 Callback {
405 target: Span,
407 source: E,
409 },
410}