use crate::algorithm::{AlgorithmIdentifier, ml_dsa};
use crate::error::{CallbackError, CallbackResult, Located};
use crate::path::{Verified, Verify, WorkingKey};
use crate::signature::SignatureValue;
use crate::{Error, ErrorCode, PublicKeyInfo, Result};
use structured_asn1::memory::Work;
use structured_asn1::{Oid, ScopedReader, Tag};
mod components;
mod preimage;
pub mod request;
pub use components::{Component, Components};
pub use preimage::Preimage;
pub const SUBJECT_ALT_PUBLIC_KEY_INFO: Oid<'static> = Oid::from_bytes(&[0x55, 0x1d, 72]);
pub const ALT_SIGNATURE_ALGORITHM: Oid<'static> = Oid::from_bytes(&[0x55, 0x1d, 73]);
pub const ALT_SIGNATURE_VALUE: Oid<'static> = Oid::from_bytes(&[0x55, 0x1d, 74]);
const FIELD: &str = "hybrid";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Fault {
code: ErrorCode,
field: &'static str,
offset: u64,
}
impl Fault {
#[must_use]
pub fn error(self) -> Error {
Error::new(self.code, self.field, self.offset)
}
pub(crate) fn of(error: &Error) -> Self {
Self {
code: error.code(),
field: error.field(),
offset: error.offset(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Alternative {
Verified(Verified),
Nonconforming(Fault),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Signatures {
pub native: Verified,
pub alternative: Alternative,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Requirement {
Alternative,
Both,
}
impl Requirement {
#[must_use]
pub fn accepts(self, result: Signatures) -> bool {
result.alternative == Alternative::Verified(Verified::Yes)
&& (self == Self::Alternative || result.native == Verified::Yes)
}
pub(crate) fn answer(self, result: Signatures) -> Verified {
if self.accepts(result) {
return Verified::Yes;
}
if result.alternative == Alternative::Verified(Verified::Unsupported)
|| (self == Self::Both && result.native == Verified::Unsupported)
{
Verified::Unsupported
} else {
Verified::No
}
}
}
pub trait AlternativeVerifier<W: Work> {
type Error;
fn verify(
&self,
algorithm: AlgorithmIdentifier<'_>,
key: PublicKeyInfo<'_>,
preimage: &Preimage<'_>,
signature: SignatureValue<'_>,
work: &W,
) -> core::result::Result<Verified, Self::Error>;
}
#[derive(Debug)]
pub enum CheckError<N, A> {
Processing(Error),
Native(N),
Alternative(A),
}
impl<N, A> From<Error> for CheckError<N, A> {
fn from(error: Error) -> Self {
Self::Processing(error)
}
}
impl<N: core::fmt::Display, A: core::fmt::Display> core::fmt::Display for CheckError<N, A> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Processing(error) => error.fmt(f),
Self::Native(error) => write!(f, "native verifier: {error}"),
Self::Alternative(error) => write!(f, "alternative verifier: {error}"),
}
}
}
#[cfg(feature = "std")]
impl<N: std::error::Error + 'static, A: std::error::Error + 'static> std::error::Error
for CheckError<N, A>
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Processing(error) => Some(error),
Self::Native(error) => Some(error),
Self::Alternative(error) => Some(error),
}
}
}
#[derive(Debug)]
pub struct Prepared<'a> {
algorithm: AlgorithmIdentifier<'a>,
signature: SignatureValue<'a>,
preimage: Preimage<'a>,
}
impl<'a> Prepared<'a> {
#[must_use]
pub fn algorithm(&self) -> AlgorithmIdentifier<'a> {
self.algorithm
}
#[must_use]
pub fn signature(&self) -> SignatureValue<'a> {
self.signature
}
#[must_use]
pub fn preimage(&self) -> &Preimage<'a> {
&self.preimage
}
pub fn verify<W: Work, V: AlternativeVerifier<W>>(
&self,
issuer_key: PublicKeyInfo<'_>,
verifier: &V,
work: &W,
) -> CallbackResult<Verified, V::Error> {
issuer_key
.execution_in(work)
.charge_work(1)
.map_err(|e| Error::resource(e, FIELD, issuer_key.key_offset()))?;
if let Some(set) = ml_dsa::resolve(self.algorithm, work)? {
let key = crate::key::ml_dsa::read(issuer_key, work)?;
if key.parameter_set() != set {
return Err(Error::new(
ErrorCode::AlgorithmMismatch,
FIELD,
issuer_key.key_offset(),
)
.into());
}
self.signature.ml_dsa_in(set, work)?;
}
verifier
.verify(
self.algorithm,
issuer_key,
&self.preimage,
self.signature,
work,
)
.map_err(CallbackError::Callback)
}
}
pub fn inspect_certificate<'a, W: Work>(
certificate: &crate::Certificate<'a>,
work: &W,
) -> Result<Components<'a>> {
components::extensions(
certificate.tbs().extensions(),
&certificate.execution_in(work),
certificate.span().start,
)
}
pub fn prepare_certificate<'a, W: Work>(
certificate: &crate::Certificate<'a>,
work: &W,
) -> Result<Prepared<'a>> {
prepare_certificate_key(certificate, work).map(|(prepared, _)| prepared)
}
pub(crate) fn prepare_certificate_key<'a, W: Work>(
certificate: &crate::Certificate<'a>,
work: &W,
) -> Result<(Prepared<'a>, PublicKeyInfo<'a>)> {
let components = components::extensions(
certificate.tbs().extensions(),
&certificate.execution_in(work),
certificate.span().start,
)?;
prepare_certificate_components(certificate, &components, work)
}
pub(crate) fn prepare_certificate_components<'a, W: Work>(
certificate: &crate::Certificate<'a>,
components: &Components<'a>,
work: &W,
) -> Result<(Prepared<'a>, PublicKeyInfo<'a>)> {
let key = components.key.require()?;
validate_key(key, work)?;
let (algorithm, signature, remove) = components.signature_fields()?;
let reader = certificate.tbs().reader_scoped(work)?;
let preimage = preimage::extensions(reader, certificate.tbs().signature(), 3, remove)?;
Ok((
Prepared {
algorithm,
signature,
preimage,
},
key,
))
}
pub(crate) fn critical_key_understood<W: Work, R: crate::Recognizer<W>>(
components: &Components<'_>,
recognizer: &R,
work: &W,
) -> CallbackResult<bool, R::Error> {
if components.key.critical() != Some(true) {
return Ok(true);
}
let key = components.key.require()?;
if ml_dsa::resolve(key.algorithm(), work)?.is_some() {
crate::key::ml_dsa::read(key, work)?;
return Ok(true);
}
key.execution_in(work)
.charge_work(1)
.map_err(|e| Error::resource(e, FIELD, key.key_offset()))?;
recognizer
.recognizes(SUBJECT_ALT_PUBLIC_KEY_INFO, work)
.map_err(CallbackError::Callback)
}
pub fn inspect_crl<'a, W: Work>(
crl: &crate::crl::CertificateList<'a>,
work: &W,
) -> Result<Components<'a>> {
components::extensions(
crl.tbs().extensions(),
&crl.execution_in(work),
crl.span().start,
)
}
pub fn prepare_crl<'a, W: Work>(
crl: &crate::crl::CertificateList<'a>,
work: &W,
) -> Result<Prepared<'a>> {
let components = inspect_crl(crl, work)?;
let (algorithm, signature, remove) = components.signature_fields()?;
let reader = crl.tbs().reader_scoped(work)?;
let preimage = preimage::extensions(reader, crl.tbs().signature(), 0, remove)?;
Ok(Prepared {
algorithm,
signature,
preimage,
})
}
fn alternative<W: Work, V: AlternativeVerifier<W>>(
prepared: Result<Prepared<'_>>,
key: PublicKeyInfo<'_>,
verifier: &V,
work: &W,
) -> CallbackResult<Alternative, V::Error> {
let result = match prepared {
Ok(prepared) => prepared
.verify(key, verifier, work)
.map(Alternative::Verified),
Err(error) => Err(CallbackError::Processing(error)),
};
match result {
Err(CallbackError::Processing(error)) if !terminal(&error) => {
Ok(Alternative::Nonconforming(Fault::of(&error)))
}
other => other,
}
}
pub(super) fn validate_key<W: Work>(key: PublicKeyInfo<'_>, work: &W) -> Result<()> {
if ml_dsa::resolve(key.algorithm(), work)?.is_some() {
crate::key::ml_dsa::read(key, work)?;
}
Ok(())
}
pub(crate) fn terminal(error: &Error) -> bool {
error.is_resource() || error.code() == ErrorCode::ForeignReader
}
pub fn check_certificate<W: Work, N: Verify<W>, A: AlternativeVerifier<W>>(
certificate: &crate::Certificate<'_>,
native_key: WorkingKey<'_>,
alternative_key: PublicKeyInfo<'_>,
native: &N,
verifier: &A,
work: &W,
) -> core::result::Result<Signatures, CheckError<N::Error, A::Error>> {
certificate
.execution_in(work)
.charge_work(1)
.map_err(|e| Error::resource(e, FIELD, certificate.span().start))?;
native_key
.key()
.execution_in(work)
.charge_work(1)
.map_err(|e| Error::resource(e, FIELD, native_key.key().key_offset()))?;
let result = native
.verify(
&certificate.signature_algorithm(),
native_key,
certificate.signed_bytes(),
certificate.signature_value(),
work,
)
.map_err(CheckError::Native)?;
let alternative = alternative(
prepare_certificate(certificate, work),
alternative_key,
verifier,
work,
)
.map_err(|e| match e {
CallbackError::Processing(e) => CheckError::Processing(e),
CallbackError::Callback(e) => CheckError::Alternative(e),
})?;
Ok(Signatures {
native: result,
alternative,
})
}
pub fn check_crl<W: Work, N: Verify<W>, A: AlternativeVerifier<W>>(
crl: &crate::crl::CertificateList<'_>,
native_key: WorkingKey<'_>,
alternative_key: PublicKeyInfo<'_>,
native: &N,
verifier: &A,
work: &W,
) -> core::result::Result<Signatures, CheckError<N::Error, A::Error>> {
crl.execution_in(work)
.charge_work(1)
.map_err(|e| Error::resource(e, FIELD, crl.span().start))?;
native_key
.key()
.execution_in(work)
.charge_work(1)
.map_err(|e| Error::resource(e, FIELD, native_key.key().key_offset()))?;
let result = native
.verify(
&crl.signature_algorithm(),
native_key,
crl.signed_bytes(),
crl.signature_value(),
work,
)
.map_err(CheckError::Native)?;
let alternative = alternative(prepare_crl(crl, work), alternative_key, verifier, work)
.map_err(|e| match e {
CallbackError::Processing(e) => CheckError::Processing(e),
CallbackError::Callback(e) => CheckError::Alternative(e),
})?;
Ok(Signatures {
native: result,
alternative,
})
}
fn required<'a>(
reader: &mut ScopedReader<'a, '_>,
tag: Tag,
) -> Result<structured_asn1::Element<'a>> {
reader.expect(tag).field(FIELD)
}
fn identifier_work<W: Work>(kind: Oid<'_>, work: &W, at: u64) -> Result<()> {
let units = (kind.as_bytes().len() as u64)
.checked_mul(3)
.and_then(|n| n.checked_add(1))
.ok_or_else(|| {
Error::resource(
structured_asn1::memory::MemoryError::WorkExceeded,
FIELD,
at,
)
})?;
work.charge_work(units)
.map_err(|e| Error::resource(e, FIELD, at))
}