xml_sec/xmldsig/verify/session/
lookup.rs1use super::*;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum BareNameTarget {
8 Element(NodeIdentity),
10 NotBareName,
12 Unresolved,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum DigestMethodResolution {
19 Supported(DigestAlgorithm),
21 Unsupported,
23 Malformed,
25}
26
27pub struct SessionDigest<'p> {
29 algorithm: DigestAlgorithm,
30 state: Box<dyn crate::provider::DigestState + 'p>,
31}
32
33impl SessionDigest<'_> {
34 pub fn output_len(&self) -> usize {
36 self.algorithm.output_len()
37 }
38
39 pub fn update<Q: Work>(&mut self, data: &[u8], work: &Q) -> Result<(), DsigError> {
41 work.charge_work(u64::try_from(data.len()).map_err(|_| MemoryError::WorkExceeded)?)?;
42 self.state.update(data).map_err(DsigError::from)
43 }
44
45 pub fn finish<Q: Work>(self, output: &mut [u8], work: &Q) -> Result<(), DsigError> {
49 let width = self.algorithm.output_len();
50 if output.len() != width {
51 return Err(DsigError::InvalidRequest {
52 reason: "digest output buffer has the wrong width",
53 });
54 }
55 work.charge_work(width as u64)?;
56 let _temporary = work.memory().reserve(width)?;
57 let digest = super::super::super::digest::finish_digest(self.algorithm, self.state)
58 .map_err(DsigError::from)?;
59 output.copy_from_slice(&digest);
60 Ok(())
61 }
62}
63
64impl<W: Work> VerifiedSession<'_, '_, '_, W> {
65 pub fn resolve_bare_name_in<Q: Work>(
71 &self,
72 uri: &str,
73 work: &Q,
74 ) -> Result<BareNameTarget, DsigError> {
75 work.charge_work(1)?;
76 let target = self
77 .resolver
78 .bare_name_node_in(
79 uri,
80 Some(self.budgets.transforms.node_set_materialization()),
81 &crate::c14n::scratch::Accounted(work),
82 )
83 .map_err(|error| {
84 DsigError::Reference(ReferenceProcessingError::UriDereference(error))
85 })?;
86 Ok(match target {
87 None => BareNameTarget::NotBareName,
88 Some(Some(node)) if node.is_element() => {
89 BareNameTarget::Element(self.view.node_identity(node))
90 }
91 Some(_) => BareNameTarget::Unresolved,
92 })
93 }
94
95 pub fn resolve_digest_method_in<Q: Work>(
98 &self,
99 method: NodeIdentity,
100 work: &Q,
101 ) -> Result<DigestMethodResolution, DsigError> {
102 work.charge_work(1)?;
103 let node = self.view.resolve_node(method)?;
104 if !node.has_tag_name((XMLDSIG_NS, "DigestMethod")) {
105 return Err(DsigError::InvalidRequest {
106 reason: "digest method identity is not a ds:DigestMethod",
107 });
108 }
109 let mut algorithm = None;
110 for attribute in node.attributes() {
111 work.charge_work(
112 u64::try_from(attribute.name().len() + 1).map_err(|_| MemoryError::WorkExceeded)?,
113 )?;
114 if attribute.namespace().is_none() && attribute.name() == "Algorithm" {
115 algorithm = Some(attribute.value());
116 break;
117 }
118 }
119 let Some(uri) = algorithm else {
120 return Ok(DigestMethodResolution::Malformed);
121 };
122 work.charge_work(u64::try_from(uri.len()).map_err(|_| MemoryError::WorkExceeded)?)?;
123 let uri = uri.trim_matches([' ', '\t', '\r', '\n']);
128 let Some(algorithm) = DigestAlgorithm::from_uri(uri) else {
129 return Ok(DigestMethodResolution::Unsupported);
130 };
131 self.context.policy.check_digest_algorithm(algorithm)?;
132 Ok(DigestMethodResolution::Supported(algorithm))
133 }
134
135 pub fn begin_digest(&self, algorithm: DigestAlgorithm) -> Result<SessionDigest<'_>, DsigError> {
138 self.context.policy.check_digest_algorithm(algorithm)?;
139 let state = super::super::super::digest::begin_digest(self.context.provider, algorithm)
140 .map_err(DsigError::from)?;
141 Ok(SessionDigest { algorithm, state })
142 }
143
144 pub fn execution(&self) -> &W {
148 self.work
149 }
150
151 pub fn certificate_parser(&self) -> structured_asn1::Parser {
157 super::super::super::pkix::CertificateSession::new(&self.context.policy.resources)
158 .into_parser()
159 }
160
161 pub fn certificate_path_bounds(&self) -> structured_pkix::path::ValidationBounds {
164 super::super::super::pkix::path_bounds(self.context.policy.key_trust.max_x509_chain_depth)
165 }
166
167 pub fn certificate_path_verifier<'a, 'p>(
170 &'p self,
171 chain: &'p [&'a structured_pkix::Certificate<'a>],
172 anchor: &'a structured_pkix::Certificate<'a>,
173 ) -> super::super::super::pkix::CertificatePathVerifier<'a, 'p> {
174 super::super::super::pkix::CertificatePathVerifier::new(
175 self.context.provider,
176 &self.context.policy.key_trust,
177 chain,
178 anchor,
179 )
180 }
181}
182
183#[cfg(test)]
184mod digest_admission_tests {
185 use super::*;
186 use std::cell::Cell;
187
188 struct Observed<'a>(&'a Cell<bool>);
189 impl crate::provider::DigestState for Observed<'_> {
190 fn update(&mut self, _: &[u8]) -> Result<(), crate::provider::ProviderError> {
191 Ok(())
192 }
193 fn finalize(self: Box<Self>) -> Result<Vec<u8>, crate::provider::ProviderError> {
194 self.0.set(true);
195 Ok(vec![0; 32])
196 }
197 }
198
199 #[test]
200 fn digest_refusals_never_enter_provider_finalize() {
201 for (capacity, work, width) in [(31, 100, 32), (32, 31, 32), (32, 100, 31), (32, 100, 32)] {
204 let memory = structured_asn1::memory::Memory::new(std::sync::Arc::new(
205 structured_asn1::memory::MemoryAccount::new(capacity, work),
206 ));
207 let called = Cell::new(false);
208 let state = SessionDigest {
209 algorithm: DigestAlgorithm::Sha256,
210 state: Box::new(Observed(&called)),
211 };
212 let mut output = [0; 32];
213 let result = state.finish(&mut output[..width], &memory);
214 let admitted = capacity == 32 && work == 100 && width == 32;
215 assert_eq!(result.is_ok(), admitted, "{result:?}");
216 assert_eq!(called.get(), admitted);
217 assert_eq!(memory.used(), 0);
218 }
219 }
220}