pub use core::str::Utf8Error;
use crate::{Length, Tag};
use core::{convert::Infallible, fmt, num::TryFromIntError};
#[cfg(feature = "oid")]
use crate::asn1::ObjectIdentifier;
#[cfg(feature = "pem")]
use crate::pem;
#[cfg(doc)]
use crate::{Reader, Writer};
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Error {
kind: ErrorKind,
position: Option<Length>,
}
impl Error {
#[must_use]
pub const fn new(kind: ErrorKind, position: Length) -> Error {
Error {
kind,
position: Some(position),
}
}
pub(crate) const fn from_kind(kind: ErrorKind) -> Error {
Error {
kind,
position: None,
}
}
#[must_use]
pub fn incomplete(actual_len: Length) -> Self {
match actual_len + Length::ONE {
Ok(expected_len) => ErrorKind::Incomplete {
expected_len,
actual_len,
}
.at(actual_len),
Err(err) => err.kind().at(actual_len),
}
}
#[must_use]
pub fn kind(self) -> ErrorKind {
self.kind
}
#[must_use]
pub fn position(self) -> Option<Length> {
self.position
}
pub(crate) fn nested(self, nested_position: Length) -> Self {
let position = (nested_position + self.position.unwrap_or_default()).ok();
Self {
kind: self.kind,
position,
}
}
}
impl core::error::Error for Error {}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.kind)?;
if let Some(pos) = self.position {
write!(f, " at DER byte {pos}")?;
}
Ok(())
}
}
impl From<ErrorKind> for Error {
fn from(kind: ErrorKind) -> Error {
Error::from_kind(kind)
}
}
impl From<Infallible> for Error {
fn from(_: Infallible) -> Error {
unreachable!()
}
}
impl From<TryFromIntError> for Error {
fn from(_: TryFromIntError) -> Error {
Error {
kind: ErrorKind::Overflow,
position: None,
}
}
}
impl From<Utf8Error> for Error {
fn from(err: Utf8Error) -> Error {
Error {
kind: ErrorKind::Utf8(err),
position: None,
}
}
}
#[cfg(feature = "alloc")]
impl From<alloc::string::FromUtf8Error> for Error {
fn from(err: alloc::string::FromUtf8Error) -> Error {
ErrorKind::Utf8(err.utf8_error()).into()
}
}
#[cfg(feature = "oid")]
impl From<const_oid::Error> for Error {
fn from(_: const_oid::Error) -> Error {
ErrorKind::OidMalformed.into()
}
}
#[cfg(feature = "pem")]
impl From<pem::Error> for Error {
fn from(err: pem::Error) -> Error {
ErrorKind::Pem(err).into()
}
}
#[cfg(feature = "std")]
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
match err.kind() {
std::io::ErrorKind::NotFound => ErrorKind::FileNotFound,
std::io::ErrorKind::PermissionDenied => ErrorKind::PermissionDenied,
other => ErrorKind::Io(other),
}
.into()
}
}
#[cfg(feature = "time")]
impl From<time::error::ComponentRange> for Error {
fn from(_: time::error::ComponentRange) -> Error {
ErrorKind::DateTime.into()
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ErrorKind {
DateTime,
EncodingRules,
Failed,
#[cfg(feature = "std")]
FileNotFound,
Incomplete {
expected_len: Length,
actual_len: Length,
},
#[cfg(feature = "std")]
Io(std::io::ErrorKind),
IndefiniteLength,
Length {
tag: Tag,
},
Noncanonical {
tag: Tag,
},
OidMalformed,
#[cfg(feature = "oid")]
OidUnknown {
oid: ObjectIdentifier,
},
SetDuplicate,
SetOrdering,
Overflow,
Overlength,
#[cfg(feature = "pem")]
Pem(pem::Error),
#[cfg(feature = "std")]
PermissionDenied,
Reader,
TagModeUnknown,
TagNumberInvalid,
TagUnexpected {
expected: Option<Tag>,
actual: Tag,
},
TagUnknown {
byte: u8,
},
TrailingData {
decoded: Length,
remaining: Length,
},
Utf8(Utf8Error),
Value {
tag: Tag,
},
}
impl ErrorKind {
#[must_use]
pub fn at(self, position: Length) -> Error {
Error::new(self, position)
}
#[must_use]
pub fn to_error(self) -> Error {
Error::from_kind(self)
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorKind::DateTime => write!(f, "date/time error"),
ErrorKind::EncodingRules => write!(f, "invalid encoding rules"),
ErrorKind::Failed => write!(f, "operation failed"),
#[cfg(feature = "std")]
ErrorKind::FileNotFound => write!(f, "file not found"),
ErrorKind::Incomplete {
expected_len,
actual_len,
} => write!(
f,
"ASN.1 DER message is incomplete: expected {expected_len}, actual {actual_len}"
),
#[cfg(feature = "std")]
ErrorKind::Io(err) => write!(f, "I/O error: {err:?}"),
ErrorKind::IndefiniteLength => write!(f, "indefinite length disallowed/malformed"),
ErrorKind::Length { tag } => write!(f, "incorrect length for {tag}"),
ErrorKind::Noncanonical { tag } => {
write!(f, "ASN.1 {tag} not canonically encoded as DER")
}
ErrorKind::OidMalformed => write!(f, "malformed OID"),
#[cfg(feature = "oid")]
ErrorKind::OidUnknown { oid } => {
write!(f, "unknown/unsupported OID: {oid}")
}
ErrorKind::SetDuplicate => write!(f, "SET OF contains duplicate"),
ErrorKind::SetOrdering => write!(f, "SET OF ordering error"),
ErrorKind::Overflow => write!(f, "integer overflow"),
ErrorKind::Overlength => write!(f, "ASN.1 DER message is too long"),
#[cfg(feature = "pem")]
ErrorKind::Pem(e) => write!(f, "PEM error: {e}"),
#[cfg(feature = "std")]
ErrorKind::PermissionDenied => write!(f, "permission denied"),
ErrorKind::Reader => write!(f, "reader does not support the requested operation"),
ErrorKind::TagModeUnknown => write!(f, "unknown tag mode"),
ErrorKind::TagNumberInvalid => write!(f, "invalid tag number"),
ErrorKind::TagUnexpected { expected, actual } => {
write!(f, "unexpected ASN.1 DER tag: ")?;
if let Some(tag) = expected {
write!(f, "expected {tag}, ")?;
}
write!(f, "got {actual}")
}
ErrorKind::TagUnknown { byte } => {
write!(f, "unknown/unsupported ASN.1 DER tag: 0x{byte:02x}")
}
ErrorKind::TrailingData { decoded, remaining } => {
write!(
f,
"trailing data at end of DER message: decoded {decoded} bytes, {remaining} bytes remaining"
)
}
ErrorKind::Utf8(e) => write!(f, "{e}"),
ErrorKind::Value { tag } => write!(f, "malformed ASN.1 DER value for {tag}"),
}
}
}