1use crate::algorithm::{AlgorithmIdentifier, ml_dsa};
10use crate::error::{CallbackError, CallbackResult, Located};
11use crate::path::{Verified, Verify, WorkingKey};
12use crate::signature::SignatureValue;
13use crate::{Error, ErrorCode, PublicKeyInfo, Result};
14use structured_asn1::memory::Work;
15use structured_asn1::{Oid, ScopedReader, Tag};
16
17mod components;
18mod preimage;
19pub mod request;
20pub use components::{Component, Components};
21pub use preimage::Preimage;
22
23pub const SUBJECT_ALT_PUBLIC_KEY_INFO: Oid<'static> = Oid::from_bytes(&[0x55, 0x1d, 72]);
25pub const ALT_SIGNATURE_ALGORITHM: Oid<'static> = Oid::from_bytes(&[0x55, 0x1d, 73]);
27pub const ALT_SIGNATURE_VALUE: Oid<'static> = Oid::from_bytes(&[0x55, 0x1d, 74]);
29const FIELD: &str = "hybrid";
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct Fault {
34 code: ErrorCode,
35 field: &'static str,
36 offset: u64,
37}
38impl Fault {
39 #[must_use]
41 pub fn error(self) -> Error {
42 Error::new(self.code, self.field, self.offset)
43 }
44 pub(crate) fn of(error: &Error) -> Self {
45 Self {
46 code: error.code(),
47 field: error.field(),
48 offset: error.offset(),
49 }
50 }
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum Alternative {
56 Verified(Verified),
58 Nonconforming(Fault),
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct Signatures {
65 pub native: Verified,
67 pub alternative: Alternative,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum Requirement {
74 Alternative,
76 Both,
78}
79impl Requirement {
80 #[must_use]
82 pub fn accepts(self, result: Signatures) -> bool {
83 result.alternative == Alternative::Verified(Verified::Yes)
84 && (self == Self::Alternative || result.native == Verified::Yes)
85 }
86 pub(crate) fn answer(self, result: Signatures) -> Verified {
87 if self.accepts(result) {
88 return Verified::Yes;
89 }
90 if result.alternative == Alternative::Verified(Verified::Unsupported)
91 || (self == Self::Both && result.native == Verified::Unsupported)
92 {
93 Verified::Unsupported
94 } else {
95 Verified::No
96 }
97 }
98}
99
100pub trait AlternativeVerifier<W: Work> {
102 type Error;
104 fn verify(
109 &self,
110 algorithm: AlgorithmIdentifier<'_>,
111 key: PublicKeyInfo<'_>,
112 preimage: &Preimage<'_>,
113 signature: SignatureValue<'_>,
114 work: &W,
115 ) -> core::result::Result<Verified, Self::Error>;
116}
117
118#[derive(Debug)]
120pub enum CheckError<N, A> {
121 Processing(Error),
123 Native(N),
125 Alternative(A),
127}
128impl<N, A> From<Error> for CheckError<N, A> {
129 fn from(error: Error) -> Self {
130 Self::Processing(error)
131 }
132}
133
134impl<N: core::fmt::Display, A: core::fmt::Display> core::fmt::Display for CheckError<N, A> {
135 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
136 match self {
137 Self::Processing(error) => error.fmt(f),
138 Self::Native(error) => write!(f, "native verifier: {error}"),
139 Self::Alternative(error) => write!(f, "alternative verifier: {error}"),
140 }
141 }
142}
143impl<N: core::error::Error + 'static, A: core::error::Error + 'static> core::error::Error
144 for CheckError<N, A>
145{
146 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
147 match self {
148 Self::Processing(error) => Some(error),
149 Self::Native(error) => Some(error),
150 Self::Alternative(error) => Some(error),
151 }
152 }
153}
154
155#[derive(Debug)]
157pub struct Prepared<'a> {
158 algorithm: AlgorithmIdentifier<'a>,
159 signature: SignatureValue<'a>,
160 preimage: Preimage<'a>,
161}
162impl<'a> Prepared<'a> {
163 #[must_use]
165 pub fn algorithm(&self) -> AlgorithmIdentifier<'a> {
166 self.algorithm
167 }
168 #[must_use]
170 pub fn signature(&self) -> SignatureValue<'a> {
171 self.signature
172 }
173 #[must_use]
175 pub fn preimage(&self) -> &Preimage<'a> {
176 &self.preimage
177 }
178
179 pub fn verify<W: Work, V: AlternativeVerifier<W>>(
181 &self,
182 issuer_key: PublicKeyInfo<'_>,
183 verifier: &V,
184 work: &W,
185 ) -> CallbackResult<Verified, V::Error> {
186 issuer_key
189 .execution_in(work)
190 .charge_work(1)
191 .map_err(|e| Error::resource(e, FIELD, issuer_key.key_offset()))?;
192 if let Some(set) = ml_dsa::resolve(self.algorithm, work)? {
193 let key = crate::key::ml_dsa::read(issuer_key, work)?;
194 if key.parameter_set() != set {
195 return Err(Error::new(
196 ErrorCode::AlgorithmMismatch,
197 FIELD,
198 issuer_key.key_offset(),
199 )
200 .into());
201 }
202 self.signature.ml_dsa_in(set, work)?;
203 }
204 verifier
205 .verify(
206 self.algorithm,
207 issuer_key,
208 &self.preimage,
209 self.signature,
210 work,
211 )
212 .map_err(CallbackError::Callback)
213 }
214}
215
216pub fn inspect_certificate<'a, W: Work>(
218 certificate: &crate::Certificate<'a>,
219 work: &W,
220) -> Result<Components<'a>> {
221 components::extensions(
222 certificate.tbs().extensions(),
223 &certificate.execution_in(work),
224 certificate.span().start,
225 )
226}
227
228pub fn prepare_certificate<'a, W: Work>(
230 certificate: &crate::Certificate<'a>,
231 work: &W,
232) -> Result<Prepared<'a>> {
233 prepare_certificate_key(certificate, work).map(|(prepared, _)| prepared)
234}
235pub(crate) fn prepare_certificate_key<'a, W: Work>(
236 certificate: &crate::Certificate<'a>,
237 work: &W,
238) -> Result<(Prepared<'a>, PublicKeyInfo<'a>)> {
239 let components = components::extensions(
240 certificate.tbs().extensions(),
241 &certificate.execution_in(work),
242 certificate.span().start,
243 )?;
244 prepare_certificate_components(certificate, &components, work)
245}
246
247pub(crate) fn prepare_certificate_components<'a, W: Work>(
248 certificate: &crate::Certificate<'a>,
249 components: &Components<'a>,
250 work: &W,
251) -> Result<(Prepared<'a>, PublicKeyInfo<'a>)> {
252 let key = components.key.require()?;
253 validate_key(key, work)?;
254 let (algorithm, signature, remove) = components.signature_fields()?;
255 let reader = certificate.tbs().reader_scoped(work)?;
256 let preimage = preimage::extensions(reader, certificate.tbs().signature(), 3, remove)?;
257 Ok((
258 Prepared {
259 algorithm,
260 signature,
261 preimage,
262 },
263 key,
264 ))
265}
266
267pub(crate) fn critical_key_understood<W: Work, R: crate::Recognizer<W>>(
268 components: &Components<'_>,
269 recognizer: &R,
270 work: &W,
271) -> CallbackResult<bool, R::Error> {
272 if components.key.critical() != Some(true) {
273 return Ok(true);
274 }
275 let key = components.key.require()?;
276 if ml_dsa::resolve(key.algorithm(), work)?.is_some() {
280 crate::key::ml_dsa::read(key, work)?;
281 return Ok(true);
282 }
283 key.execution_in(work)
284 .charge_work(1)
285 .map_err(|e| Error::resource(e, FIELD, key.key_offset()))?;
286 recognizer
287 .recognizes(SUBJECT_ALT_PUBLIC_KEY_INFO, work)
288 .map_err(CallbackError::Callback)
289}
290
291pub fn inspect_crl<'a, W: Work>(
293 crl: &crate::crl::CertificateList<'a>,
294 work: &W,
295) -> Result<Components<'a>> {
296 components::extensions(
297 crl.tbs().extensions(),
298 &crl.execution_in(work),
299 crl.span().start,
300 )
301}
302
303pub fn prepare_crl<'a, W: Work>(
305 crl: &crate::crl::CertificateList<'a>,
306 work: &W,
307) -> Result<Prepared<'a>> {
308 let components = inspect_crl(crl, work)?;
309 let (algorithm, signature, remove) = components.signature_fields()?;
310 let reader = crl.tbs().reader_scoped(work)?;
311 let preimage = preimage::extensions(reader, crl.tbs().signature(), 0, remove)?;
312 Ok(Prepared {
313 algorithm,
314 signature,
315 preimage,
316 })
317}
318
319fn alternative<W: Work, V: AlternativeVerifier<W>>(
320 prepared: Result<Prepared<'_>>,
321 key: PublicKeyInfo<'_>,
322 verifier: &V,
323 work: &W,
324) -> CallbackResult<Alternative, V::Error> {
325 let result = match prepared {
326 Ok(prepared) => prepared
327 .verify(key, verifier, work)
328 .map(Alternative::Verified),
329 Err(error) => Err(CallbackError::Processing(error)),
330 };
331 match result {
332 Err(CallbackError::Processing(error)) if !terminal(&error) => {
333 Ok(Alternative::Nonconforming(Fault::of(&error)))
334 }
335 other => other,
336 }
337}
338
339pub(super) fn validate_key<W: Work>(key: PublicKeyInfo<'_>, work: &W) -> Result<()> {
340 if ml_dsa::resolve(key.algorithm(), work)?.is_some() {
341 crate::key::ml_dsa::read(key, work)?;
342 }
343 Ok(())
344}
345pub(crate) fn terminal(error: &Error) -> bool {
346 error.is_resource() || error.code() == ErrorCode::ForeignReader
347}
348
349pub fn check_certificate<W: Work, N: Verify<W>, A: AlternativeVerifier<W>>(
351 certificate: &crate::Certificate<'_>,
352 native_key: WorkingKey<'_>,
353 alternative_key: PublicKeyInfo<'_>,
354 native: &N,
355 verifier: &A,
356 work: &W,
357) -> core::result::Result<Signatures, CheckError<N::Error, A::Error>> {
358 certificate
359 .execution_in(work)
360 .charge_work(1)
361 .map_err(|e| Error::resource(e, FIELD, certificate.span().start))?;
362 native_key
363 .key()
364 .execution_in(work)
365 .charge_work(1)
366 .map_err(|e| Error::resource(e, FIELD, native_key.key().key_offset()))?;
367 let result = native
368 .verify(
369 &certificate.signature_algorithm(),
370 native_key,
371 certificate.signed_bytes(),
372 certificate.signature_value(),
373 work,
374 )
375 .map_err(CheckError::Native)?;
376 let alternative = alternative(
377 prepare_certificate(certificate, work),
378 alternative_key,
379 verifier,
380 work,
381 )
382 .map_err(|e| match e {
383 CallbackError::Processing(e) => CheckError::Processing(e),
384 CallbackError::Callback(e) => CheckError::Alternative(e),
385 })?;
386 Ok(Signatures {
387 native: result,
388 alternative,
389 })
390}
391
392pub fn check_crl<W: Work, N: Verify<W>, A: AlternativeVerifier<W>>(
395 crl: &crate::crl::CertificateList<'_>,
396 native_key: WorkingKey<'_>,
397 alternative_key: PublicKeyInfo<'_>,
398 native: &N,
399 verifier: &A,
400 work: &W,
401) -> core::result::Result<Signatures, CheckError<N::Error, A::Error>> {
402 crl.execution_in(work)
403 .charge_work(1)
404 .map_err(|e| Error::resource(e, FIELD, crl.span().start))?;
405 native_key
406 .key()
407 .execution_in(work)
408 .charge_work(1)
409 .map_err(|e| Error::resource(e, FIELD, native_key.key().key_offset()))?;
410 let result = native
411 .verify(
412 &crl.signature_algorithm(),
413 native_key,
414 crl.signed_bytes(),
415 crl.signature_value(),
416 work,
417 )
418 .map_err(CheckError::Native)?;
419 let alternative = alternative(prepare_crl(crl, work), alternative_key, verifier, work)
420 .map_err(|e| match e {
421 CallbackError::Processing(e) => CheckError::Processing(e),
422 CallbackError::Callback(e) => CheckError::Alternative(e),
423 })?;
424 Ok(Signatures {
425 native: result,
426 alternative,
427 })
428}
429
430fn required<'a>(
431 reader: &mut ScopedReader<'a, '_>,
432 tag: Tag,
433) -> Result<structured_asn1::Element<'a>> {
434 reader.expect(tag).field(FIELD)
435}
436
437fn identifier_work<W: Work>(kind: Oid<'_>, work: &W, at: u64) -> Result<()> {
438 let units = (kind.as_bytes().len() as u64)
439 .checked_mul(3)
440 .and_then(|n| n.checked_add(1))
441 .ok_or_else(|| {
442 Error::resource(
443 structured_asn1::memory::MemoryError::WorkExceeded,
444 FIELD,
445 at,
446 )
447 })?;
448 work.charge_work(units)
449 .map_err(|e| Error::resource(e, FIELD, at))
450}