1use crate::error::{Error, ErrorCode, Located, Result, gate};
9use crate::extension::{self, Extension};
10use structured_asn1::memory::{Memory, OwnedVec};
11use structured_asn1::{Oid, Parser, Tag, universal};
12
13#[derive(Debug)]
15pub struct PolicyInformation<'a> {
16 policy: Oid<'a>,
17 qualifiers: OwnedVec<PolicyQualifier<'a>>,
18 at: u64,
19}
20
21impl<'a> PolicyInformation<'a> {
22 #[must_use]
24 pub fn policy(&self) -> Oid<'a> {
25 self.policy
26 }
27
28 #[must_use]
30 pub fn qualifiers(&self) -> &[PolicyQualifier<'a>] {
31 &self.qualifiers
32 }
33}
34
35#[derive(Debug, Clone, Copy)]
37pub struct PolicyQualifier<'a> {
38 kind: Oid<'a>,
39 value: &'a [u8],
40 at: u64,
44 outer: u32,
46}
47
48impl<'a> PolicyQualifier<'a> {
49 #[must_use]
51 pub fn kind(self) -> Oid<'a> {
52 self.kind
53 }
54
55 #[must_use]
58 pub fn value(self) -> &'a [u8] {
59 self.value
60 }
61}
62
63#[derive(Debug)]
65pub struct CertificatePolicies<'a> {
66 policies: OwnedVec<PolicyInformation<'a>>,
67}
68
69impl<'a> CertificatePolicies<'a> {
70 #[must_use]
72 pub fn policies(&self) -> &[PolicyInformation<'a>] {
73 &self.policies
74 }
75
76 pub fn has(&self, policy: Oid<'_>, execution: &Memory) -> Result<bool> {
82 for information in &self.policies {
83 let work = 1 + information
84 .policy
85 .as_bytes()
86 .len()
87 .max(policy.as_bytes().len()) as u64;
88 execution.charge_work(work).map_err(|reason| {
89 Error::resource(reason, "extensions.certificatePolicies", information.at)
90 })?;
91 if information.policy == policy {
92 return Ok(true);
93 }
94 }
95 Ok(false)
96 }
97
98 pub fn read(extension: &Extension<'a>, execution: &Memory) -> Result<Self> {
100 const FIELD: &str = "extensions.certificatePolicies";
101 let fail = |code| Error::new(code, FIELD, extension.span().start);
102
103 let mut inside = extension::sequence(extension, FIELD, execution)?;
104 let mut policies = inside.memory().vector();
105 let mut identifiers = inside.memory().map::<Oid<'a>, ()>();
106
107 while let Some(entry) = inside.next_element().field(FIELD)? {
108 let at = |code| Error::new(code, FIELD, entry.span().start);
109 if entry.tag() != Tag::constructed(universal::SEQUENCE) {
110 return Err(at(ErrorCode::Encoding(
111 structured_asn1::ErrorCode::UnexpectedTag,
112 )));
113 }
114
115 let mut information = inside.enter(&entry).field(FIELD)?;
116 let policy = information.read_oid().field(FIELD)?;
117 let mut qualifiers = inside.memory().vector();
118
119 if let Some(list) = information.next_element().field(FIELD)? {
120 if list.tag() != Tag::constructed(universal::SEQUENCE) {
121 return Err(at(ErrorCode::Encoding(
122 structured_asn1::ErrorCode::UnexpectedTag,
123 )));
124 }
125 let mut each = information.enter(&list).field(FIELD)?;
126 while let Some(qualifier) = each.next_element().field(FIELD)? {
127 if qualifier.tag() != Tag::constructed(universal::SEQUENCE) {
128 return Err(at(ErrorCode::Encoding(
129 structured_asn1::ErrorCode::UnexpectedTag,
130 )));
131 }
132 let mut pair = each.enter(&qualifier).field(FIELD)?;
133 let kind = pair.read_oid().field(FIELD)?;
134 let value = pair
135 .next_element()
136 .field(FIELD)?
137 .ok_or_else(|| at(ErrorCode::MissingField))?;
138 if !pair.is_empty() {
139 return Err(at(ErrorCode::TrailingData));
140 }
141 crate::name::walk_der(&pair, &value, FIELD, crate::name::Strings::Checked)?;
149 qualifiers
150 .try_push(PolicyQualifier {
151 kind,
152 value: value.as_bytes(),
153 at: value.span().start,
154 outer: each.depth(),
155 })
156 .map_err(|reason| Error::resource(reason, FIELD, qualifier.span().start))?;
157 }
158 if qualifiers.is_empty() {
159 return Err(at(ErrorCode::EmptySequence));
160 }
161 }
162
163 if !information.is_empty() {
164 return Err(at(ErrorCode::TrailingData));
165 }
166
167 if identifiers
171 .try_insert(policy, ())
172 .map_err(|reason| Error::resource(reason, FIELD, entry.span().start))?
173 .is_some()
174 {
175 return Err(at(ErrorCode::MalformedValue));
176 }
177
178 policies
179 .try_push(PolicyInformation {
180 policy,
181 qualifiers,
182 at: entry.span().start,
183 })
184 .map_err(|reason| Error::resource(reason, FIELD, entry.span().start))?;
185 }
186
187 if policies.is_empty() {
188 return Err(fail(ErrorCode::EmptySequence));
189 }
190 Ok(Self { policies })
191 }
192}
193
194#[derive(Debug, Clone, Copy)]
196pub struct PolicyMapping<'a> {
197 issuer_domain: Oid<'a>,
198 subject_domain: Oid<'a>,
199}
200
201impl<'a> PolicyMapping<'a> {
202 #[must_use]
204 pub fn issuer_domain(self) -> Oid<'a> {
205 self.issuer_domain
206 }
207
208 #[must_use]
210 pub fn subject_domain(self) -> Oid<'a> {
211 self.subject_domain
212 }
213}
214
215#[derive(Debug)]
217pub struct PolicyMappings<'a> {
218 mappings: OwnedVec<PolicyMapping<'a>>,
219}
220
221impl<'a> PolicyMappings<'a> {
222 #[must_use]
224 pub fn mappings(&self) -> &[PolicyMapping<'a>] {
225 &self.mappings
226 }
227
228 pub fn read(extension: &Extension<'a>, execution: &Memory) -> Result<Self> {
230 const FIELD: &str = "extensions.policyMappings";
231 let fail = |code| Error::new(code, FIELD, extension.span().start);
232
233 let mut inside = extension::sequence(extension, FIELD, execution)?;
234 let mut mappings = inside.memory().vector();
235 let mut seen = inside.memory().map::<(Oid<'a>, Oid<'a>), ()>();
236
237 while let Some(entry) = inside.next_element().field(FIELD)? {
238 let at = |code| Error::new(code, FIELD, entry.span().start);
239 if entry.tag() != Tag::constructed(universal::SEQUENCE) {
240 return Err(at(ErrorCode::Encoding(
241 structured_asn1::ErrorCode::UnexpectedTag,
242 )));
243 }
244
245 let mut pair = inside.enter(&entry).field(FIELD)?;
246 let issuer_domain = pair.read_oid().field(FIELD)?;
247 let subject_domain = pair.read_oid().field(FIELD)?;
248 if !pair.is_empty() {
249 return Err(at(ErrorCode::TrailingData));
250 }
251
252 if issuer_domain == crate::oid::ANY_POLICY || subject_domain == crate::oid::ANY_POLICY {
255 return Err(at(ErrorCode::MalformedValue));
256 }
257
258 if seen
266 .try_insert((issuer_domain, subject_domain), ())
267 .map_err(|reason| Error::resource(reason, FIELD, entry.span().start))?
268 .is_none()
269 {
270 mappings
271 .try_push(PolicyMapping {
272 issuer_domain,
273 subject_domain,
274 })
275 .map_err(|reason| Error::resource(reason, FIELD, entry.span().start))?;
276 }
277 }
278
279 if mappings.is_empty() {
280 return Err(fail(ErrorCode::EmptySequence));
281 }
282 Ok(Self { mappings })
283 }
284}
285
286impl CertificatePolicies<'_> {
287 pub(crate) fn understood(&self, parser: &Parser, execution: &Memory) -> Result<bool> {
295 for qualifier in self.policies.iter().flat_map(PolicyInformation::qualifiers) {
296 execution
297 .charge_work(1)
298 .map_err(|reason| Error::resource(reason, QUALIFIER_FIELD, qualifier.at))?;
299 if !qualifier_understood(parser, *qualifier, execution)? {
300 return Ok(false);
301 }
302 }
303 Ok(true)
304 }
305}
306
307const QUALIFIER_FIELD: &str = "extensions.certificatePolicies";
309
310fn qualifier_understood(
311 parser: &Parser,
312 qualifier: PolicyQualifier<'_>,
313 execution: &Memory,
314) -> Result<bool> {
315 let Some(mut reader) = gate(
319 parser
320 .read_nested_in(
321 qualifier.value,
322 qualifier.at,
323 structured_asn1::Encoding::Der,
324 qualifier.outer,
325 execution,
326 )
327 .field(QUALIFIER_FIELD),
328 )?
329 else {
330 return Ok(false);
331 };
332 let Some(Some(value)) = gate(reader.next_element().field(QUALIFIER_FIELD))? else {
333 return Ok(false);
334 };
335 if !reader.is_empty() {
336 return Ok(false);
337 }
338
339 Ok(if qualifier.kind == crate::oid::CPS_QUALIFIER {
340 reader
346 .charge(value.contents().len() as u64)
347 .field(QUALIFIER_FIELD)?;
348 value.tag() == Tag::primitive(universal::IA5_STRING)
349 && crate::name::is_ia5(value.contents())
350 && core::str::from_utf8(value.contents()).is_ok_and(crate::general_name::is_uri)
351 } else if qualifier.kind == crate::oid::USER_NOTICE_QUALIFIER {
352 user_notice_understood(&reader, &value)?
353 } else {
354 false
355 })
356}
357
358fn user_notice_understood(
363 parent: &structured_asn1::Reader<'_>,
364 value: &structured_asn1::Element<'_>,
365) -> Result<bool> {
366 if value.tag() != Tag::constructed(universal::SEQUENCE) {
367 return Ok(false);
368 }
369 let Some(mut inside) = gate(parent.enter(value).field(QUALIFIER_FIELD))? else {
370 return Ok(false);
371 };
372 let Some(mut next) = gate(inside.next_element().field(QUALIFIER_FIELD))? else {
373 return Ok(false);
374 };
375 if let Some(reference) = next
376 && reference.tag() == Tag::constructed(universal::SEQUENCE)
377 {
378 if !notice_reference_understood(&inside, &reference)? {
379 return Ok(false);
380 }
381 let Some(following) = gate(inside.next_element().field(QUALIFIER_FIELD))? else {
382 return Ok(false);
383 };
384 next = following;
385 }
386 if let Some(text) = next
387 && !is_display_text(&inside, &text)?
388 {
389 return Ok(false);
390 }
391 Ok(inside.is_empty())
392}
393
394fn notice_reference_understood(
397 parent: &structured_asn1::Reader<'_>,
398 reference: &structured_asn1::Element<'_>,
399) -> Result<bool> {
400 let Some(mut inside) = gate(parent.enter(reference).field(QUALIFIER_FIELD))? else {
401 return Ok(false);
402 };
403 let Some(Some(organization)) = gate(inside.next_element().field(QUALIFIER_FIELD))? else {
404 return Ok(false);
405 };
406 if !is_display_text(&inside, &organization)? {
407 return Ok(false);
408 }
409 let Some(Some(numbers)) = gate(inside.next_element().field(QUALIFIER_FIELD))? else {
410 return Ok(false);
411 };
412 if numbers.tag() != Tag::constructed(universal::SEQUENCE) || !inside.is_empty() {
413 return Ok(false);
414 }
415 let Some(mut each) = gate(inside.enter(&numbers).field(QUALIFIER_FIELD))? else {
416 return Ok(false);
417 };
418 loop {
419 match gate(each.next_element().field(QUALIFIER_FIELD))? {
420 Some(Some(number)) => {
421 if number.tag() != Tag::primitive(universal::INTEGER)
425 || gate(each.integer(&number).field(QUALIFIER_FIELD))?.is_none()
426 {
427 return Ok(false);
428 }
429 }
430 Some(None) => return Ok(true),
434 None => return Ok(false),
435 }
436 }
437}
438
439fn is_display_text(
444 reader: &structured_asn1::Reader<'_>,
445 element: &structured_asn1::Element<'_>,
446) -> Result<bool> {
447 let tag = element.tag();
448 let contents = element.contents();
449 if contents.len() > 200 * 4 {
454 return Ok(false);
455 }
456 reader
460 .charge(contents.len() as u64)
461 .field(QUALIFIER_FIELD)?;
462 let characters = if tag == Tag::primitive(universal::IA5_STRING) {
463 contents.iter().all(u8::is_ascii).then_some(contents.len())
465 } else if tag == Tag::primitive(universal::UTF8_STRING) {
466 core::str::from_utf8(contents)
467 .ok()
468 .map(|text| text.chars().count())
469 } else if tag == Tag::primitive(26) {
470 contents
473 .iter()
474 .all(|byte| matches!(byte, 0x20..=0x7e))
475 .then_some(contents.len())
476 } else if tag == Tag::primitive(30) {
477 crate::name::wide_char_count(contents, 2)
482 } else {
483 return Ok(false);
484 };
485 Ok(characters.is_some_and(|count| (1..=200).contains(&count)))
486}
487
488#[cfg(test)]
489mod tests;