blob: 902863d4986bda9c81f011ed2b47db2917c71779 [file] [log] [blame]
//! Error types.
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;
/// Result type.
pub type Result<T> = core::result::Result<T, Error>;
/// Error type.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Error {
/// Kind of error.
kind: ErrorKind,
/// Position inside of message where error occurred.
position: Option<Length>,
}
impl Error {
/// Create a new [`Error`].
pub fn new(kind: ErrorKind, position: Length) -> Error {
Error {
kind,
position: Some(position),
}
}
/// Create a new [`ErrorKind::Incomplete`] for the given length.
///
/// Computes the expected len as being one greater than `actual_len`.
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),
}
}
/// Get the [`ErrorKind`] which occurred.
pub fn kind(self) -> ErrorKind {
self.kind
}
/// Get the position inside of the message where the error occurred.
pub fn position(self) -> Option<Length> {
self.position
}
/// For errors occurring inside of a nested message, extend the position
/// count by the location where the nested message occurs.
pub(crate) fn nested(self, nested_position: Length) -> Self {
// TODO(tarcieri): better handle length overflows occurring in this calculation?
let position = (nested_position + self.position.unwrap_or_default()).ok();
Self {
kind: self.kind,
position,
}
}
}
#[cfg(feature = "std")]
impl std::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 {
kind,
position: None,
}
}
}
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()
}
}
/// Error type.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum ErrorKind {
/// Date-and-time related errors.
DateTime,
/// This error indicates a previous DER parsing operation resulted in
/// an error and tainted the state of a `Decoder` or `Encoder`.
///
/// Once this occurs, the overall operation has failed and cannot be
/// subsequently resumed.
Failed,
/// File not found error.
#[cfg(feature = "std")]
FileNotFound,
/// Message is incomplete and does not contain all of the expected data.
Incomplete {
/// Expected message length.
///
/// Note that this length represents a *minimum* lower bound on how
/// much additional data is needed to continue parsing the message.
///
/// It's possible upon subsequent message parsing that the parser will
/// discover even more data is needed.
expected_len: Length,
/// Actual length of the message buffer currently being processed.
actual_len: Length,
},
/// I/O errors.
#[cfg(feature = "std")]
Io(std::io::ErrorKind),
/// Indefinite length disallowed.
IndefiniteLength,
/// Incorrect length for a given field.
Length {
/// Tag of the value being decoded.
tag: Tag,
},
/// Message is not canonically encoded.
Noncanonical {
/// Tag of the value which is not canonically encoded.
tag: Tag,
},
/// OID is improperly encoded.
OidMalformed,
/// Unknown OID.
///
/// This error is intended to be used by libraries which parse DER-based
/// formats which encounter unknown or unsupported OID libraries.
///
/// It enables passing back the OID value to the caller, which allows them
/// to determine which OID(s) are causing the error (and then potentially
/// contribute upstream support for algorithms they care about).
#[cfg(feature = "oid")]
OidUnknown {
/// OID value that was unrecognized by a parser for a DER-based format.
oid: ObjectIdentifier,
},
/// `SET` cannot contain duplicates.
SetDuplicate,
/// `SET` ordering error: items not in canonical order.
SetOrdering,
/// Integer overflow occurred (library bug!).
Overflow,
/// Message is longer than this library's internal limits support.
Overlength,
/// PEM encoding errors.
#[cfg(feature = "pem")]
Pem(pem::Error),
/// Permission denied reading file.
#[cfg(feature = "std")]
PermissionDenied,
/// Reader does not support the requested operation.
Reader,
/// Unknown tag mode.
TagModeUnknown,
/// Invalid tag number.
///
/// The "tag number" is the lower 5-bits of a tag's octet.
/// This error occurs in the case that all 5-bits are set to `1`,
/// which indicates a multi-byte tag which is unsupported by this library.
TagNumberInvalid,
/// Unexpected tag.
TagUnexpected {
/// Tag the decoder was expecting (if there is a single such tag).
///
/// `None` if multiple tags are expected/allowed, but the `actual` tag
/// does not match any of them.
expected: Option<Tag>,
/// Actual tag encountered in the message.
actual: Tag,
},
/// Unknown/unsupported tag.
TagUnknown {
/// Raw byte value of the tag.
byte: u8,
},
/// Undecoded trailing data at end of message.
TrailingData {
/// Length of the decoded data.
decoded: Length,
/// Total length of the remaining data left in the buffer.
remaining: Length,
},
/// UTF-8 errors.
Utf8(Utf8Error),
/// Unexpected value.
Value {
/// Tag of the unexpected value.
tag: Tag,
},
}
impl ErrorKind {
/// Annotate an [`ErrorKind`] with context about where it occurred,
/// returning an error.
pub fn at(self, position: Length) -> Error {
Error::new(self, position)
}
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorKind::DateTime => write!(f, "date/time error"),
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 {}, actual {}",
expected_len, actual_len
),
#[cfg(feature = "std")]
ErrorKind::Io(err) => write!(f, "I/O error: {:?}", err),
ErrorKind::IndefiniteLength => write!(f, "indefinite length disallowed"),
ErrorKind::Length { tag } => write!(f, "incorrect length for {}", tag),
ErrorKind::Noncanonical { tag } => {
write!(f, "ASN.1 {} not canonically encoded as DER", tag)
}
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{:02x}", byte)
}
ErrorKind::TrailingData { decoded, remaining } => {
write!(
f,
"trailing data at end of DER message: decoded {} bytes, {} bytes remaining",
decoded, remaining
)
}
ErrorKind::Utf8(e) => write!(f, "{}", e),
ErrorKind::Value { tag } => write!(f, "malformed ASN.1 DER value for {}", tag),
}
}
}