1use super::super::{AttributeCertificate, DigestedObject};
10use crate::Error;
11use structured_asn1::digest::Resolution;
12use structured_asn1::memory::Work;
13use structured_pkix::Certificate;
14use structured_pkix::algorithm::digest::{self as algorithms, AsnDigest};
15use structured_pkix::key::effective::{EffectiveSpki, VisitError};
16use structured_pkix::path::WorkingKey;
17
18const FIELD: &str = "attributeCertificate.holder.objectDigestInfo";
19
20pub trait ObjectDigestProvider<'p>: AsnDigest<'p> {
31 fn validate_public_key<W: Work>(
33 &self,
34 key: WorkingKey<'_>,
35 work: &W,
36 ) -> Result<Resolution<()>, Self::Error>;
37}
38
39#[derive(Debug, Clone, Copy)]
41pub enum Object<'c, 'a> {
42 Certificate(&'c Certificate<'a>),
44 PublicKey(WorkingKey<'a>),
46}
47
48#[derive(Debug)]
51pub struct BoundObject<'r, 'a, 's> {
52 certificate: &'r AttributeCertificate<'a>,
53 object: Object<'r, 's>,
54}
55impl<'r, 'a, 's> BoundObject<'r, 'a, 's> {
56 #[must_use]
58 pub fn certificate(&self) -> &'r AttributeCertificate<'a> {
59 self.certificate
60 }
61 #[must_use]
63 pub fn object(&self) -> Object<'r, 's> {
64 self.object
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum Failure {
71 Absent,
73 ObjectType,
75 OtherObject,
77 Algorithm,
79 Hash,
81 KeyEncoding,
83 Differs,
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub enum Capability {
90 Digest,
92 Key,
94}
95
96#[derive(Debug)]
98pub enum Binding<'r, 'a, 's> {
99 Bound(BoundObject<'r, 'a, 's>),
101 Rejected(Failure),
103 Unsupported(Capability),
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub enum Phase {
110 KeyEncoding,
112 Start,
114 CertificateInput,
116 Finish,
118 Comparison,
120}
121
122#[derive(Debug)]
124pub enum BindingError<E> {
125 Processing(Error),
127 Callback {
129 phase: Phase,
131 source: E,
133 },
134 KeyInput(VisitError<E>),
136 OutputWidth {
138 expected: usize,
140 actual: usize,
142 },
143}
144impl<E> From<Error> for BindingError<E> {
145 fn from(error: Error) -> Self {
146 Self::Processing(error)
147 }
148}
149
150impl<'a> AttributeCertificate<'a> {
151 pub fn bind_holder_digest<'r, 's, 'p, P: ObjectDigestProvider<'p>, W: Work>(
160 &'r self,
161 object: Object<'r, 's>,
162 provider: &'p P,
163 work: &W,
164 ) -> Result<Binding<'r, 'a, 's>, BindingError<P::Error>> {
165 let execution = work.execution_for(self.parser);
166 let at = self.signed_element().span().start;
167 execution
168 .charge_work(4)
169 .map_err(|e| Error::resource(e, FIELD, at))?;
170 let Some(assertion) = &self.information.holder.object_digest else {
171 return Ok(Binding::Rejected(Failure::Absent));
172 };
173 if assertion.object_type == DigestedObject::Other || assertion.other_type.is_some() {
174 return Ok(Binding::Rejected(Failure::OtherObject));
175 }
176 if !matches!(
177 (&assertion.object_type, object),
178 (DigestedObject::PublicKey, Object::PublicKey(_))
179 | (DigestedObject::PublicKeyCertificate, Object::Certificate(_))
180 ) {
181 return Ok(Binding::Rejected(Failure::ObjectType));
182 }
183 let algorithm =
184 match algorithms::algorithm(&assertion.algorithm, &execution).map_err(Error::pkix)? {
185 Resolution::Supported(value) => value,
186 Resolution::Unsupported => return Ok(Binding::Unsupported(Capability::Digest)),
187 Resolution::Malformed => return Ok(Binding::Rejected(Failure::Algorithm)),
188 };
189 let Some(expected) = assertion
190 .digest
191 .octets()
192 .filter(|bytes| bytes.len() == algorithm.width())
193 else {
194 return Ok(Binding::Rejected(Failure::Hash));
195 };
196 let Some(identity) = provider.identity(algorithm) else {
197 return Ok(Binding::Unsupported(Capability::Digest));
198 };
199 check_width(provider.width(&identity), algorithm.width())?;
200 let calculation = match prepare(object, provider, &execution, |input| {
201 hash(
202 input,
203 provider,
204 &identity,
205 algorithm.width(),
206 expected,
207 &execution,
208 )
209 })? {
210 Resolution::Supported(calculation) => calculation,
211 Resolution::Unsupported => return Ok(Binding::Unsupported(Capability::Key)),
212 Resolution::Malformed => return Ok(Binding::Rejected(Failure::KeyEncoding)),
213 };
214 let matches = match calculation {
215 Resolution::Supported(matches) => matches,
216 Resolution::Unsupported => return Ok(Binding::Unsupported(Capability::Digest)),
217 Resolution::Malformed => return Ok(Binding::Rejected(Failure::Algorithm)),
218 };
219 Ok(if matches {
220 Binding::Bound(BoundObject {
221 certificate: self,
222 object,
223 })
224 } else {
225 Binding::Rejected(Failure::Differs)
226 })
227 }
228}
229
230fn check_width<E>(actual: Option<usize>, expected: usize) -> Result<(), BindingError<E>> {
231 if let Some(actual) = actual
232 && actual != expected
233 {
234 return Err(BindingError::OutputWidth { expected, actual });
235 }
236 Ok(())
237}
238
239#[derive(Clone, Copy)]
240enum Input<'v, 's> {
241 Certificate(&'v Certificate<'s>),
242 Key(&'v EffectiveSpki<'s>),
243}
244
245fn prepare<'s, 'p, P: ObjectDigestProvider<'p>, W: Work, T>(
246 object: Object<'_, 's>,
247 provider: &'p P,
248 work: &W,
249 visit: impl FnOnce(Input<'_, 's>) -> Result<T, BindingError<P::Error>>,
250) -> Result<Resolution<T>, BindingError<P::Error>> {
251 let key = match object {
252 Object::Certificate(certificate) => {
253 return visit(Input::Certificate(certificate)).map(Resolution::Supported);
254 }
255 Object::PublicKey(key) => key,
256 };
257 let encoded = EffectiveSpki::read(key, work).map_err(Error::pkix)?;
258 Ok(
259 match provider
260 .validate_public_key(key, &encoded.execution_in(work))
261 .map_err(|source| BindingError::Callback {
262 phase: Phase::KeyEncoding,
263 source,
264 })? {
265 Resolution::Supported(()) => {
266 return visit(Input::Key(&encoded)).map(Resolution::Supported);
267 }
268 Resolution::Unsupported => Resolution::Unsupported,
269 Resolution::Malformed => Resolution::Malformed,
270 },
271 )
272}
273
274fn hash<'p, P: ObjectDigestProvider<'p>, W: Work>(
275 input: Input<'_, '_>,
276 provider: &'p P,
277 identity: &P::Identity,
278 width: usize,
279 expected: &[u8],
280 work: &W,
281) -> Result<Resolution<bool>, BindingError<P::Error>> {
282 let mut state =
283 match provider
284 .start(identity, work)
285 .map_err(|source| BindingError::Callback {
286 phase: Phase::Start,
287 source,
288 })? {
289 Resolution::Supported(state) => state,
290 Resolution::Unsupported => return Ok(Resolution::Unsupported),
291 Resolution::Malformed => return Ok(Resolution::Malformed),
292 };
293 match input {
294 Input::Certificate(certificate) => {
295 let execution = certificate.execution_in(work);
296 execution.charge_work(1).map_err(|e| {
297 Error::resource(
298 e,
299 "attributeCertificate.objectDigestInfo.certificateInput",
300 certificate.span().start,
301 )
302 })?;
303 provider
304 .update(&mut state, certificate.as_bytes(), &execution)
305 .map_err(|source| BindingError::Callback {
306 phase: Phase::CertificateInput,
307 source,
308 })?;
309 }
310 Input::Key(encoded) => encoded
311 .visit(work, |_, bytes, source| {
312 provider.update(&mut state, bytes, source)
313 })
314 .map_err(BindingError::KeyInput)?,
315 }
316 let output = provider
317 .finish(state, work)
318 .map_err(|source| BindingError::Callback {
319 phase: Phase::Finish,
320 source,
321 })?;
322 check_width(Some(output.as_ref().len()), width)?;
323 provider
324 .matches(identity, &output, expected, work)
325 .map(Resolution::Supported)
326 .map_err(|source| BindingError::Callback {
327 phase: Phase::Comparison,
328 source,
329 })
330}