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}
143#[cfg(feature = "std")]
144impl<N: std::error::Error + 'static, A: std::error::Error + 'static> std::error::Error
145 for CheckError<N, A>
146{
147 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
148 match self {
149 Self::Processing(error) => Some(error),
150 Self::Native(error) => Some(error),
151 Self::Alternative(error) => Some(error),
152 }
153 }
154}
155
156#[derive(Debug)]
158pub struct Prepared<'a> {
159 algorithm: AlgorithmIdentifier<'a>,
160 signature: SignatureValue<'a>,
161 preimage: Preimage<'a>,
162}
163impl<'a> Prepared<'a> {
164 #[must_use]
166 pub fn algorithm(&self) -> AlgorithmIdentifier<'a> {
167 self.algorithm
168 }
169 #[must_use]
171 pub fn signature(&self) -> SignatureValue<'a> {
172 self.signature
173 }
174 #[must_use]
176 pub fn preimage(&self) -> &Preimage<'a> {
177 &self.preimage
178 }
179
180 pub fn verify<W: Work, V: AlternativeVerifier<W>>(
182 &self,
183 issuer_key: PublicKeyInfo<'_>,
184 verifier: &V,
185 work: &W,
186 ) -> CallbackResult<Verified, V::Error> {
187 issuer_key
190 .execution_in(work)
191 .charge_work(1)
192 .map_err(|e| Error::resource(e, FIELD, issuer_key.key_offset()))?;
193 if let Some(set) = ml_dsa::resolve(self.algorithm, work)? {
194 let key = crate::key::ml_dsa::read(issuer_key, work)?;
195 if key.parameter_set() != set {
196 return Err(Error::new(
197 ErrorCode::AlgorithmMismatch,
198 FIELD,
199 issuer_key.key_offset(),
200 )
201 .into());
202 }
203 self.signature.ml_dsa_in(set, work)?;
204 }
205 verifier
206 .verify(
207 self.algorithm,
208 issuer_key,
209 &self.preimage,
210 self.signature,
211 work,
212 )
213 .map_err(CallbackError::Callback)
214 }
215}
216
217pub fn inspect_certificate<'a, W: Work>(
219 certificate: &crate::Certificate<'a>,
220 work: &W,
221) -> Result<Components<'a>> {
222 components::extensions(
223 certificate.tbs().extensions(),
224 &certificate.execution_in(work),
225 certificate.span().start,
226 )
227}
228
229pub fn prepare_certificate<'a, W: Work>(
231 certificate: &crate::Certificate<'a>,
232 work: &W,
233) -> Result<Prepared<'a>> {
234 prepare_certificate_key(certificate, work).map(|(prepared, _)| prepared)
235}
236pub(crate) fn prepare_certificate_key<'a, W: Work>(
237 certificate: &crate::Certificate<'a>,
238 work: &W,
239) -> Result<(Prepared<'a>, PublicKeyInfo<'a>)> {
240 let components = components::extensions(
241 certificate.tbs().extensions(),
242 &certificate.execution_in(work),
243 certificate.span().start,
244 )?;
245 prepare_certificate_components(certificate, &components, work)
246}
247
248pub(crate) fn prepare_certificate_components<'a, W: Work>(
249 certificate: &crate::Certificate<'a>,
250 components: &Components<'a>,
251 work: &W,
252) -> Result<(Prepared<'a>, PublicKeyInfo<'a>)> {
253 let key = components.key.require()?;
254 validate_key(key, work)?;
255 let (algorithm, signature, remove) = components.signature_fields()?;
256 let reader = certificate.tbs().reader_scoped(work)?;
257 let preimage = preimage::extensions(reader, certificate.tbs().signature(), 3, remove)?;
258 Ok((
259 Prepared {
260 algorithm,
261 signature,
262 preimage,
263 },
264 key,
265 ))
266}
267
268pub(crate) fn critical_key_understood<W: Work, R: crate::Recognizer<W>>(
269 components: &Components<'_>,
270 recognizer: &R,
271 work: &W,
272) -> CallbackResult<bool, R::Error> {
273 if components.key.critical() != Some(true) {
274 return Ok(true);
275 }
276 let key = components.key.require()?;
277 if ml_dsa::resolve(key.algorithm(), work)?.is_some() {
281 crate::key::ml_dsa::read(key, work)?;
282 return Ok(true);
283 }
284 key.execution_in(work)
285 .charge_work(1)
286 .map_err(|e| Error::resource(e, FIELD, key.key_offset()))?;
287 recognizer
288 .recognizes(SUBJECT_ALT_PUBLIC_KEY_INFO, work)
289 .map_err(CallbackError::Callback)
290}
291
292pub fn inspect_crl<'a, W: Work>(
294 crl: &crate::crl::CertificateList<'a>,
295 work: &W,
296) -> Result<Components<'a>> {
297 components::extensions(
298 crl.tbs().extensions(),
299 &crl.execution_in(work),
300 crl.span().start,
301 )
302}
303
304pub fn prepare_crl<'a, W: Work>(
306 crl: &crate::crl::CertificateList<'a>,
307 work: &W,
308) -> Result<Prepared<'a>> {
309 let components = inspect_crl(crl, work)?;
310 let (algorithm, signature, remove) = components.signature_fields()?;
311 let reader = crl.tbs().reader_scoped(work)?;
312 let preimage = preimage::extensions(reader, crl.tbs().signature(), 0, remove)?;
313 Ok(Prepared {
314 algorithm,
315 signature,
316 preimage,
317 })
318}
319
320fn alternative<W: Work, V: AlternativeVerifier<W>>(
321 prepared: Result<Prepared<'_>>,
322 key: PublicKeyInfo<'_>,
323 verifier: &V,
324 work: &W,
325) -> CallbackResult<Alternative, V::Error> {
326 let result = match prepared {
327 Ok(prepared) => prepared
328 .verify(key, verifier, work)
329 .map(Alternative::Verified),
330 Err(error) => Err(CallbackError::Processing(error)),
331 };
332 match result {
333 Err(CallbackError::Processing(error)) if !terminal(&error) => {
334 Ok(Alternative::Nonconforming(Fault::of(&error)))
335 }
336 other => other,
337 }
338}
339
340pub(super) fn validate_key<W: Work>(key: PublicKeyInfo<'_>, work: &W) -> Result<()> {
341 if ml_dsa::resolve(key.algorithm(), work)?.is_some() {
342 crate::key::ml_dsa::read(key, work)?;
343 }
344 Ok(())
345}
346pub(crate) fn terminal(error: &Error) -> bool {
347 error.is_resource() || error.code() == ErrorCode::ForeignReader
348}
349
350pub fn check_certificate<W: Work, N: Verify<W>, A: AlternativeVerifier<W>>(
352 certificate: &crate::Certificate<'_>,
353 native_key: WorkingKey<'_>,
354 alternative_key: PublicKeyInfo<'_>,
355 native: &N,
356 verifier: &A,
357 work: &W,
358) -> core::result::Result<Signatures, CheckError<N::Error, A::Error>> {
359 certificate
360 .execution_in(work)
361 .charge_work(1)
362 .map_err(|e| Error::resource(e, FIELD, certificate.span().start))?;
363 native_key
364 .key()
365 .execution_in(work)
366 .charge_work(1)
367 .map_err(|e| Error::resource(e, FIELD, native_key.key().key_offset()))?;
368 let result = native
369 .verify(
370 &certificate.signature_algorithm(),
371 native_key,
372 certificate.signed_bytes(),
373 certificate.signature_value(),
374 work,
375 )
376 .map_err(CheckError::Native)?;
377 let alternative = alternative(
378 prepare_certificate(certificate, work),
379 alternative_key,
380 verifier,
381 work,
382 )
383 .map_err(|e| match e {
384 CallbackError::Processing(e) => CheckError::Processing(e),
385 CallbackError::Callback(e) => CheckError::Alternative(e),
386 })?;
387 Ok(Signatures {
388 native: result,
389 alternative,
390 })
391}
392
393pub fn check_crl<W: Work, N: Verify<W>, A: AlternativeVerifier<W>>(
396 crl: &crate::crl::CertificateList<'_>,
397 native_key: WorkingKey<'_>,
398 alternative_key: PublicKeyInfo<'_>,
399 native: &N,
400 verifier: &A,
401 work: &W,
402) -> core::result::Result<Signatures, CheckError<N::Error, A::Error>> {
403 crl.execution_in(work)
404 .charge_work(1)
405 .map_err(|e| Error::resource(e, FIELD, crl.span().start))?;
406 native_key
407 .key()
408 .execution_in(work)
409 .charge_work(1)
410 .map_err(|e| Error::resource(e, FIELD, native_key.key().key_offset()))?;
411 let result = native
412 .verify(
413 &crl.signature_algorithm(),
414 native_key,
415 crl.signed_bytes(),
416 crl.signature_value(),
417 work,
418 )
419 .map_err(CheckError::Native)?;
420 let alternative = alternative(prepare_crl(crl, work), alternative_key, verifier, work)
421 .map_err(|e| match e {
422 CallbackError::Processing(e) => CheckError::Processing(e),
423 CallbackError::Callback(e) => CheckError::Alternative(e),
424 })?;
425 Ok(Signatures {
426 native: result,
427 alternative,
428 })
429}
430
431fn required<'a>(
432 reader: &mut ScopedReader<'a, '_>,
433 tag: Tag,
434) -> Result<structured_asn1::Element<'a>> {
435 reader.expect(tag).field(FIELD)
436}
437
438fn identifier_work<W: Work>(kind: Oid<'_>, work: &W, at: u64) -> Result<()> {
439 let units = (kind.as_bytes().len() as u64)
440 .checked_mul(3)
441 .and_then(|n| n.checked_add(1))
442 .ok_or_else(|| {
443 Error::resource(
444 structured_asn1::memory::MemoryError::WorkExceeded,
445 FIELD,
446 at,
447 )
448 })?;
449 work.charge_work(units)
450 .map_err(|e| Error::resource(e, FIELD, at))
451}