Closed cuserdzy closed 2 years ago
I'm not sure if this function should process if hdr.class is DerClass::ContextSpecific
fn der_read_element_content_recursive<'a>(
i: &'a [u8],
hdr: DerObjectHeader<'a>,
max_depth: usize,
) -> DerResult<'a> {
match hdr.class {
DerClass::Universal | DerClass::Private => (),
_ => {
let (i, content) = ber_get_object_content(i, &hdr, max_depth)?;
let content = DerObjectContent::Unknown(hdr.tag, content);
let obj = DerObject::from_header_and_content(hdr, content);
return Ok((i, obj));
}
}
match der_read_element_content_as(i, hdr.tag, hdr.len, hdr.is_constructed(), max_depth) {
Ok((rem, content)) => Ok((rem, DerObject::from_header_and_content(hdr, content))),
Err(Err::Error(BerError::UnknownTag)) => {
let (rem, content) = ber_get_object_content(i, &hdr, max_depth)?;
let content = DerObjectContent::Unknown(hdr.tag, content);
let obj = DerObject::from_header_and_content(hdr, content);
Ok((rem, obj))
}
Err(e) => Err(e),
}
}
Hmm, parse_der
may be too restrictive here. If I remember correctly, recursive parsing is not done, because this function has not way to determine if parsing is explicit or implicit from the encoding only (this function has no context). Maybe I should let parse_der
try to parse a valid header, and descend if possible, but I'm unsure this would be a good idea.
The usual method with der-parser
is to specify the expected tag and parsing function (for ex using parse_der_tagged_explicit
). See for example krb5_parser.rs.
What are you trying to do? Maybe I can help if you give more details
@chifflier FWIW I am encountering a similar issue. In my case, I am attempting to parse a CMS (RFC 5652) data structure, specifically the signed and unsigned attributes types, where the structure of the data is not assigned within the RFC that I'm parsing.
I've observed that a context-specific [0]
tag nested deeply within that structure yields the same EOC data pattern that @cuserdzy describes; this is an undesirable outcome in my case.
@cuserdzy don't know if you're still facing this issue (or someone else may come along who does have a similar issue), but you can reparse the data blobs. The following code snippet could probably use some cleaning up, but it will reparse into such structures recursively:
fn reparse_unknown_o<'a>(o: BerObject<'a>) -> BerResult<'a> {
// Workaround for https://github.com/rusticata/der-parser/issues/38.
let new_o = match o.content {
BerObjectContent::Sequence(v) => {
let (_, new_v) = reparse_unknown_v(v)?;
BerObject::from_header_and_content(o.header, BerObjectContent::Sequence(new_v))
}
BerObjectContent::Set(v) => {
let (_, new_v) = reparse_unknown_v(v)?;
BerObject::from_header_and_content(o.header, BerObjectContent::Set(new_v))
}
BerObjectContent::Optional(Some(boxed_obj)) => {
let (_, new_o) = reparse_unknown_o(*boxed_obj)?;
BerObject::from_header_and_content(
o.header,
BerObjectContent::Optional(Some(Box::new(new_o))),
)
}
BerObjectContent::Tagged(class, tag, boxed_obj) => {
let (_, new_o) = reparse_unknown_o(*boxed_obj)?;
BerObject::from_header_and_content(
o.header,
BerObjectContent::Tagged(class, tag, Box::new(new_o)),
)
}
BerObjectContent::Unknown(_tag, data) => {
if o.header.class == BerClass::ContextSpecific && o.header.structured == 1 {
// Tagged entries may contain multiple objects. The BerObjectContent enum
// doesn't have a way to represent this directly, so we wrap this in an
// implicit sequence.
let mut objs: Vec<BerObject<'a>> = Vec::new();
let mut i = data;
while !i.is_empty() {
let (i2, obj) = parse_der(i)?;
let (_, obj) = reparse_unknown_o(obj)?;
objs.push(obj);
i = i2;
}
BerObject::from_header_and_content(o.header, BerObjectContent::Sequence(objs))
} else {
o
}
}
_ => o,
};
Ok((&[], new_o))
}
fn reparse_unknown_v<'a>(v: Vec<BerObject<'a>>) -> BerResult<Vec<BerObject<'a>>> {
let new_v: Result<Vec<BerObject<'a>>, nom::Err<BerError>> = v
.into_iter()
.map(|o| match reparse_unknown_o(o) {
Ok((_i, new_o)) => Ok(new_o),
Err(e) => Err(e),
})
.collect();
match new_v {
Ok(v) => Ok((&[], v)),
Err(e) => Err(e),
}
}
I would add to the problem that I also share the same Unknown content for CMS data. And it get worse, as in X.509 ber sequence, with extra information (extension).
PKCS#7/CMS data contains a nested list of certificate X.509, which also has a context-specific [0]
data. I have to manually parse the content as a sequence.
Why don't we support 0xA0 tag? It is in the specs anyway.
https://stackoverflow.com/a/43056617/13173256
P.S: the binary for my data is the signature blob in Mach-O (Apple binary) for signed binary. So I would expect the binary data is correctly encoded.
P.S 2: BER encoding, but DER also share the same
Hi,
It's not that tag 0xA0
is not supported: the problem is using the function parse_der
, because it cannot automatically guess the contents of the tagged value. Without the ASN.1 grammar, it's not possible to know if the value is EXPLICIT or IMPLICIT, and so it cannot be decoded.
When dealing with such cases, the best way is to guide the parser by giving the expected functions, as DER parser design doc section explains.
Note: I have a crate (not yet published, but the code is mostly ready) to decode most PKCS formats, including PKCS#7 and CMS (the crate does not perform cryptographic operations). I intended to publish it later, but if you are interested I can push it to github.
FWIW I am unsubscribing as I no longer have the need for CMS parsing.
I would love to see the crate published. But I don't insist pushing something to the public when you're not feeling it. Having a good parser for most certificate standards is very useful. Thank you.
Just a guessing, I think you use serialization to make the decodable/codable (cryptographic standard) structs. Because manually parsing is more error-prone than serializing. And while serialization is experimental, it would be too early to publish code using the feature.
The initial problem is now fixed (master
branch, to be released as 7.0.0
), so I'm closing this issue.
Please open other issues if needed.
Thanks
I have a signed data encoded by DER, but get strange output while parse
The input
The output
As show above, I think 0xA0 is not a unknown tag and the tag number should not be parsed as EOC, this is a context-specific tag.