#[cfg(feature = "jer")]
use core::num::ParseIntError;
use super::strings::PermittedAlphabetError;
use alloc::{boxed::Box, string::ToString};
#[cfg(feature = "jer")]
use jzon::JsonValue;
use snafu::Snafu;
#[cfg(feature = "backtraces")]
use snafu::{Backtrace, GenerateImplicitData};
use crate::de::Error;
use crate::types::{constraints::Bounded, variants::Variants, Tag};
use crate::Codec;
use num_bigint::BigInt;
#[derive(Debug)]
#[non_exhaustive]
pub enum CodecDecodeError {
Ber(BerDecodeErrorKind),
Cer(CerDecodeErrorKind),
Der(DerDecodeErrorKind),
Uper(UperDecodeErrorKind),
Aper(AperDecodeErrorKind),
#[cfg(feature = "jer")]
Jer(JerDecodeErrorKind),
Oer(OerDecodeErrorKind),
Coer(CoerDecodeErrorKind),
}
macro_rules! impl_from {
($variant:ident, $error_kind:ty) => {
impl From<$error_kind> for DecodeError {
fn from(error: $error_kind) -> Self {
Self::from_codec_kind(CodecDecodeError::$variant(error))
}
}
};
}
impl_from!(Ber, BerDecodeErrorKind);
impl_from!(Cer, CerDecodeErrorKind);
impl_from!(Der, DerDecodeErrorKind);
impl_from!(Uper, UperDecodeErrorKind);
impl_from!(Aper, AperDecodeErrorKind);
#[cfg(feature = "jer")]
impl_from!(Jer, JerDecodeErrorKind);
impl_from!(Oer, OerDecodeErrorKind);
impl_from!(Coer, CoerDecodeErrorKind);
impl From<CodecDecodeError> for DecodeError {
fn from(error: CodecDecodeError) -> Self {
Self::from_codec_kind(error)
}
}
#[derive(Debug)]
#[allow(clippy::module_name_repetitions)]
pub struct DecodeError {
pub kind: Box<DecodeErrorKind>,
pub codec: Codec,
#[cfg(feature = "backtraces")]
pub backtrace: Backtrace,
}
impl core::fmt::Display for DecodeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
writeln!(f, "Error Kind: {}", self.kind)?;
writeln!(f, "Codec: {}", self.codec)?;
#[cfg(feature = "backtraces")]
write!(f, "\nBacktrace:\n{}", self.backtrace)?;
Ok(())
}
}
impl DecodeError {
#[must_use]
pub fn permitted_alphabet_error(reason: PermittedAlphabetError, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::PermittedAlphabetError { reason }, codec)
}
#[must_use]
pub fn size_constraint_not_satisfied(
size: Option<usize>,
expected: alloc::string::String,
codec: Codec,
) -> Self {
Self::from_kind(
DecodeErrorKind::SizeConstraintNotSatisfied { size, expected },
codec,
)
}
#[must_use]
pub fn value_constraint_not_satisfied(
value: BigInt,
expected: Bounded<i128>,
codec: Codec,
) -> Self {
Self::from_kind(
DecodeErrorKind::ValueConstraintNotSatisfied { value, expected },
codec,
)
}
#[must_use]
pub fn discriminant_value_not_found(discriminant: isize, codec: Codec) -> Self {
Self::from_kind(
DecodeErrorKind::DiscriminantValueNotFound { discriminant },
codec,
)
}
#[must_use]
pub fn range_exceeds_platform_width(needed: u32, present: u32, codec: Codec) -> Self {
Self::from_kind(
DecodeErrorKind::RangeExceedsPlatformWidth { needed, present },
codec,
)
}
#[must_use]
pub fn fixed_string_conversion_failed(
tag: Tag,
actual: usize,
expected: usize,
codec: Codec,
) -> Self {
Self::from_kind(
DecodeErrorKind::FixedStringConversionFailed {
tag,
actual,
expected,
},
codec,
)
}
#[must_use]
pub fn incorrect_item_number_in_sequence(expected: usize, actual: usize, codec: Codec) -> Self {
Self::from_kind(
DecodeErrorKind::IncorrectItemNumberInSequence { expected, actual },
codec,
)
}
#[must_use]
pub fn integer_overflow(max_width: u32, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::IntegerOverflow { max_width }, codec)
}
#[must_use]
pub fn integer_type_conversion_failed(msg: alloc::string::String, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::IntegerTypeConversionFailed { msg }, codec)
}
#[must_use]
pub fn invalid_bit_string(bits: u8, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::InvalidBitString { bits }, codec)
}
#[must_use]
pub fn missing_tag_class_or_value_in_sequence_or_set(
class: crate::types::Class,
value: u32,
codec: Codec,
) -> Self {
Self::from_kind(
DecodeErrorKind::MissingTagClassOrValueInSequenceOrSet { class, value },
codec,
)
}
#[must_use]
pub fn type_not_extensible(codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::TypeNotExtensible, codec)
}
#[must_use]
pub fn parser_fail(msg: alloc::string::String, codec: Codec) -> Self {
DecodeError::from_kind(DecodeErrorKind::Parser { msg }, codec)
}
#[must_use]
pub fn required_extension_not_present(tag: Tag, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::RequiredExtensionNotPresent { tag }, codec)
}
#[must_use]
pub fn enumeration_index_not_found(index: usize, extended_list: bool, codec: Codec) -> Self {
Self::from_kind(
DecodeErrorKind::EnumerationIndexNotFound {
index,
extended_list,
},
codec,
)
}
#[must_use]
pub fn choice_index_exceeds_platform_width(
needed: u32,
present: DecodeError,
codec: Codec,
) -> Self {
Self::from_kind(
DecodeErrorKind::ChoiceIndexExceedsPlatformWidth { needed, present },
codec,
)
}
#[must_use]
pub fn length_exceeds_platform_width(msg: alloc::string::String, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::LengthExceedsPlatformWidth { msg }, codec)
}
#[must_use]
pub fn choice_index_not_found(index: usize, variants: Variants, codec: Codec) -> Self {
Self::from_kind(
DecodeErrorKind::ChoiceIndexNotFound { index, variants },
codec,
)
}
#[must_use]
pub fn string_conversion_failed(tag: Tag, msg: alloc::string::String, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::StringConversionFailed { tag, msg }, codec)
}
#[must_use]
pub fn unexpected_extra_data(length: usize, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::UnexpectedExtraData { length }, codec)
}
#[must_use]
pub fn unexpected_empty_input(codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::UnexpectedEmptyInput, codec)
}
pub fn assert_length(
expected: usize,
actual: usize,
codec: Codec,
) -> core::result::Result<(), DecodeError> {
if expected == actual {
Ok(())
} else {
Err(DecodeError::from_kind(
DecodeErrorKind::MismatchedLength { expected, actual },
codec,
))
}
}
pub fn map_nom_err<T: core::fmt::Debug>(
error: nom::Err<nom::error::Error<T>>,
codec: Codec,
) -> DecodeError {
let msg = match error {
nom::Err::Incomplete(needed) => return DecodeError::incomplete(needed, codec),
err => alloc::format!("Parsing Failure: {err}"),
};
DecodeError::parser_fail(msg, codec)
}
#[must_use]
pub fn from_kind(kind: DecodeErrorKind, codec: Codec) -> Self {
Self {
kind: Box::new(kind),
codec,
#[cfg(feature = "backtraces")]
backtrace: Backtrace::generate(),
}
}
#[must_use]
fn from_codec_kind(inner: CodecDecodeError) -> Self {
let codec = match inner {
CodecDecodeError::Ber(_) => crate::Codec::Ber,
CodecDecodeError::Cer(_) => crate::Codec::Cer,
CodecDecodeError::Der(_) => crate::Codec::Der,
CodecDecodeError::Uper(_) => crate::Codec::Uper,
CodecDecodeError::Aper(_) => crate::Codec::Aper,
#[cfg(feature = "jer")]
CodecDecodeError::Jer(_) => crate::Codec::Jer,
CodecDecodeError::Oer(_) => crate::Codec::Oer,
CodecDecodeError::Coer(_) => crate::Codec::Coer,
};
Self {
kind: Box::new(DecodeErrorKind::CodecSpecific { inner }),
codec,
#[cfg(feature = "backtraces")]
backtrace: Backtrace::generate(),
}
}
}
#[derive(Snafu)]
#[snafu(visibility(pub))]
#[derive(Debug)]
#[non_exhaustive]
pub enum DecodeErrorKind {
#[snafu(display("Alphabet constraint not satisfied {}", reason))]
PermittedAlphabetError { reason: PermittedAlphabetError },
#[snafu(display("Size constraint not satisfied: expected: {expected}; actual: {size:?}"))]
SizeConstraintNotSatisfied {
size: Option<usize>,
expected: alloc::string::String,
},
#[snafu(display("Value constraint not satisfied: expected: {expected}; actual: {value}"))]
ValueConstraintNotSatisfied {
value: BigInt,
expected: Bounded<i128>,
},
#[snafu(display("Wrapped codec-specific decode error"))]
CodecSpecific { inner: CodecDecodeError },
#[snafu(display(
"Enumeration index '{}' did not match any variant. Extended list: {}",
index,
extended_list
))]
EnumerationIndexNotFound {
index: usize,
extended_list: bool,
},
#[snafu(display("choice index '{index}' did not match any variant"))]
ChoiceIndexNotFound {
index: usize,
variants: Variants,
},
#[snafu(display("integer range larger than possible to address on this platform. needed: {needed} present: {present}"))]
ChoiceIndexExceedsPlatformWidth {
needed: u32,
present: DecodeError,
},
#[snafu(display("Custom: {}", msg))]
Custom {
msg: alloc::string::String,
},
#[snafu(display("Discriminant value '{}' did not match any variant", discriminant))]
DiscriminantValueNotFound {
discriminant: isize,
},
#[snafu(display("Duplicate field for `{}`", name))]
DuplicateField {
name: &'static str,
},
#[snafu(display("Expected maximum of {} items", length))]
ExceedsMaxLength {
length: num_bigint::BigUint,
},
#[snafu(display(
"Length of the incoming data is either incorrect or your device is up by miracle."
))]
LengthExceedsPlatformWidth { msg: alloc::string::String },
#[snafu(display("Error when decoding field `{}`: {}", name, nested))]
FieldError {
name: &'static str,
nested: Box<DecodeError>,
},
#[snafu(display("Need more BITS to continue: ({:?}).", needed))]
Incomplete {
needed: nom::Needed,
},
#[snafu(display(
"Invalid item number in Sequence: expected {}, actual {}",
expected,
actual
))]
IncorrectItemNumberInSequence {
expected: usize,
actual: usize,
},
#[snafu(display("Actual integer larger than expected {} bits", max_width))]
IntegerOverflow {
max_width: u32,
},
#[snafu(display("Failed to cast integer to another integer type: {msg} "))]
IntegerTypeConversionFailed { msg: alloc::string::String },
#[snafu(display("BitString contains an invalid amount of unused bits: {}", bits))]
InvalidBitString {
bits: u8,
},
#[snafu(display(
"Bool value is not `0` or `0xFF` as canonical requires. Actual: {}",
value
))]
InvalidBool { value: u8 },
#[snafu(display("Length of Length cannot be zero"))]
ZeroLengthOfLength,
#[snafu(display("Expected {:?} bytes, actual length: {:?}", expected, actual))]
MismatchedLength {
expected: usize,
actual: usize,
},
#[snafu(display("Missing field `{}`", name))]
MissingField {
name: &'static str,
},
#[snafu(display("Expected class: {}, value: {} in sequence or set Missing tag class or value in sequence or set", class, value))]
MissingTagClassOrValueInSequenceOrSet {
class: crate::types::Class,
value: u32,
},
#[snafu(display("integer range larger than possible to address on this platform. needed: {needed} present: {present}"))]
RangeExceedsPlatformWidth {
needed: u32,
present: u32,
},
#[snafu(display("Extension with class `{}` and tag `{}` required, but not present", tag.class, tag.value))]
RequiredExtensionNotPresent { tag: crate::types::Tag },
#[snafu(display("Extension {} required but not present", tag.class))]
ExtensionRequiredButNotPresent { tag: crate::types::Tag },
#[snafu(display("Error in Parser: {}", msg))]
Parser {
msg: alloc::string::String,
},
#[snafu(display(
"Failed to convert byte array into valid ASN.1 string. String type as tag: {} Error: {}",
tag,
msg
))]
StringConversionFailed {
tag: Tag,
msg: alloc::string::String,
},
#[snafu(display(
"Failed to convert byte array into valid fixed-sized ASN.1 string. String type as tag: {}, actual: {}, expected: {}",
tag,
actual,
expected
))]
FixedStringConversionFailed {
tag: Tag,
expected: usize,
actual: usize,
},
#[snafu(display("No valid choice for `{}`", name))]
NoValidChoice {
name: &'static str,
},
#[snafu(display("Attempted to decode extension on non-extensible type"))]
TypeNotExtensible,
#[snafu(display("Unexpected extra data found: length `{}` bytes", length))]
UnexpectedExtraData {
length: usize,
},
#[snafu(display("Unknown field with index {} and tag {}", index, tag))]
UnknownField { index: usize, tag: Tag },
#[snafu(display("SEQUENCE has at least one required field, but no input provided"))]
UnexpectedEmptyInput,
}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum BerDecodeErrorKind {
#[snafu(display("Indefinite length encountered but not allowed."))]
IndefiniteLengthNotAllowed,
#[snafu(display("Invalid constructed identifier for ASN.1 value: not primitive."))]
InvalidConstructedIdentifier,
#[snafu(display("Invalid date string: {}", msg))]
InvalidDate { msg: alloc::string::String },
#[snafu(display("Invalid object identifier with missing or corrupt root nodes."))]
InvalidObjectIdentifier,
#[snafu(display("Expected {:?} tag, actual tag: {:?}", expected, actual))]
MismatchedTag {
expected: Tag,
actual: Tag,
},
}
impl BerDecodeErrorKind {
#[must_use]
pub fn invalid_date(msg: alloc::string::String) -> CodecDecodeError {
CodecDecodeError::Ber(Self::InvalidDate { msg })
}
pub fn assert_tag(expected: Tag, actual: Tag) -> core::result::Result<(), DecodeError> {
if expected == actual {
Ok(())
} else {
Err(BerDecodeErrorKind::MismatchedTag { expected, actual }.into())
}
}
}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum CerDecodeErrorKind {}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum DerDecodeErrorKind {
#[snafu(display("Constructed encoding encountered but not allowed."))]
ConstructedEncodingNotAllowed,
}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum JerDecodeErrorKind {
#[snafu(display("Unexpected end of input while decoding JER JSON."))]
EndOfInput {},
#[snafu(display(
"Found mismatching JSON value. Expected type {}. Found value {}.",
needed,
found
))]
TypeMismatch {
needed: &'static str,
found: alloc::string::String,
},
#[cfg(feature = "jer")]
#[snafu(display("Found invalid byte in bit string. {parse_int_err}"))]
InvalidJerBitstring { parse_int_err: ParseIntError },
#[cfg(feature = "jer")]
#[snafu(display("Found invalid character in octet string."))]
InvalidJerOctetString {},
#[cfg(feature = "jer")]
#[snafu(display("Failed to construct OID from value {value}",))]
InvalidOIDString { value: JsonValue },
#[snafu(display("Found invalid enumerated discriminant {discriminant}",))]
InvalidEnumDiscriminant { discriminant: alloc::string::String },
}
#[cfg(feature = "jer")]
impl JerDecodeErrorKind {
pub fn eoi() -> CodecDecodeError {
CodecDecodeError::Jer(JerDecodeErrorKind::EndOfInput {})
}
}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum UperDecodeErrorKind {}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum AperDecodeErrorKind {}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum OerDecodeErrorKind {
#[snafu(display("Invalid tag class when decoding choice: actual {:?}", class))]
InvalidTagClassOnChoice {
class: u8,
},
#[snafu(display("Invalid tag number when decoding Choice. Value: {value}"))]
InvalidTagNumberOnChoice { value: u32 },
#[snafu(display(
"Tag not found from the variants of the platform when decoding Choice. Tag: {value}, extensible status: {is_extensible}"
))]
InvalidTagVariantOnChoice { value: Tag, is_extensible: bool },
InvalidExtensionHeader {
msg: alloc::string::String,
},
#[snafu(display("Invalid BitString: {msg}"))]
InvalidOerBitString {
msg: alloc::string::String,
},
#[snafu(display("Invalid preamble: {msg}"))]
InvalidPreamble { msg: alloc::string::String },
}
impl OerDecodeErrorKind {
#[must_use]
pub fn invalid_tag_number_on_choice(value: u32) -> DecodeError {
CodecDecodeError::Oer(Self::InvalidTagNumberOnChoice { value }).into()
}
#[must_use]
pub fn invalid_tag_variant_on_choice(value: Tag, is_extensible: bool) -> DecodeError {
CodecDecodeError::Oer(Self::InvalidTagVariantOnChoice {
value,
is_extensible,
})
.into()
}
#[must_use]
pub fn invalid_extension_header(msg: alloc::string::String) -> DecodeError {
CodecDecodeError::Oer(Self::InvalidExtensionHeader { msg }).into()
}
#[must_use]
pub fn invalid_bit_string(msg: alloc::string::String) -> DecodeError {
CodecDecodeError::Oer(Self::InvalidOerBitString { msg }).into()
}
#[must_use]
pub fn invalid_preamble(msg: alloc::string::String) -> DecodeError {
CodecDecodeError::Oer(Self::InvalidPreamble { msg }).into()
}
}
#[derive(Snafu, Debug)]
#[snafu(visibility(pub))]
#[non_exhaustive]
pub enum CoerDecodeErrorKind {
#[snafu(display("Invalid Canonical Octet Encoding, not encoded as the smallest possible number of octets: {msg}"))]
NotValidCanonicalEncoding { msg: alloc::string::String },
}
impl crate::de::Error for DecodeError {
fn custom<D: core::fmt::Display>(msg: D, codec: Codec) -> Self {
Self::from_kind(
DecodeErrorKind::Custom {
msg: msg.to_string(),
},
codec,
)
}
fn incomplete(needed: nom::Needed, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::Incomplete { needed }, codec)
}
fn exceeds_max_length(length: num_bigint::BigUint, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::ExceedsMaxLength { length }, codec)
}
fn missing_field(name: &'static str, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::MissingField { name }, codec)
}
fn no_valid_choice(name: &'static str, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::NoValidChoice { name }, codec)
}
fn field_error(name: &'static str, nested: DecodeError, codec: Codec) -> Self {
Self::from_kind(
DecodeErrorKind::FieldError {
name,
nested: Box::new(nested),
},
codec,
)
}
fn duplicate_field(name: &'static str, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::DuplicateField { name }, codec)
}
fn unknown_field(index: usize, tag: Tag, codec: Codec) -> Self {
Self::from_kind(DecodeErrorKind::UnknownField { index, tag }, codec)
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
#[test]
fn test_ber_decode_date() {
use crate::error::{DecodeError, DecodeErrorKind};
let data = [
23, 17, 50, 51, 48, 49, 50, 50, 49, 51, 48, 48, 48, 48, 45, 48, 53, 48, 90,
];
let result = crate::ber::decode::<UtcTime>(&data);
match result {
Err(DecodeError { kind, .. }) => {
if let DecodeErrorKind::CodecSpecific {
inner:
crate::error::CodecDecodeError::Ber(
crate::error::BerDecodeErrorKind::InvalidDate { msg },
),
..
} = *kind
{
assert_eq!(msg, "230122130000-050Z");
} else {
panic!("Unexpected error kind: {kind}");
}
}
Ok(_) => panic!("Expected error"),
}
}
#[test]
fn test_uper_missing_choice_index() {
use crate as rasn;
use crate::error::{DecodeError, DecodeErrorKind};
use crate::Codec;
#[derive(AsnType, Decode, Debug, PartialEq)]
#[rasn(choice, automatic_tags)]
enum MyChoice {
Normal(Integer),
High(Integer),
Medium(Integer),
}
let data = [192, 128, 83, 64];
let result = Codec::Uper.decode_from_binary::<MyChoice>(&data);
match result {
Ok(_) => {
panic!("Unexpected OK!");
}
Err(DecodeError { kind, .. }) => {
if let DecodeErrorKind::ChoiceIndexNotFound { index, .. } = *kind {
assert_eq!(index, 3);
} else {
panic!("Unexpected error kind: {kind}");
}
}
}
}
}