-
Notifications
You must be signed in to change notification settings - Fork 141
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds SignedData type to pkcs7
#813
Conversation
Thanks for opening this! I'll try it with some PKCS7 blobs I have locally. I had a similar line of work prepared (https://github.com/RustCrypto/formats/compare/master...woodruffw-forks:formats:ww/signeddata?expand=1), but this looks like it's closer to complete. |
/// ```text | ||
/// DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier | ||
/// ``` | ||
type DigestAlgorithmIdentifiers<'a> = SetOfVec<DigestAlgorithmIdentifier<'a>>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I did something weird locally, but I'm surprised this isn't complaining about the lack of the alloc
feature from the der
crate. Did you build with all features enabled locally?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that you say it I'm wondering as well. I ran everything without additional features enabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, it built for me locally without additional features as well, strange.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're pulling in the x509-cert
crate which unconditionally activates the alloc
feature, and feature unification means it's activated here too.
It would probably be good to factor the relevant parts into a common crate which can be shared. We had the Edit: oh wait, you need x501
crate for this purpose at one point, although I'm not sure that's quite what you need.Certificate
. Never mind.
Since that's the case, you might as well unconditionally link liballoc and activate the alloc
feature of the der
crate directly for clarity.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for explaining!
If there is interest in maintaining a non-alloc
part of pkcs7
, this is how I did it in my own changes:
[features]
alloc = ["der/alloc"]
std = ["alloc"]
signed-data = ["alloc", "dep:x509-cert"]
(with x509-cert
then being marked as optional.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing that, like #765, this crate should probably move to all owned types and make alloc
a hard dependency, since heapless support probably won't be practical for real-world use cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What should we do for now? I don't want to mix this PR with the larger effort of doing something like #765 for this crate as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm +1 on doing the owned refactor in a follow-up (I can help with that) to keep the diff small 🙂
With the change in #813 (comment), this gets through most of the MS authroot PKCS7 blob I'm testing with: $ ./target/debug/examples/demo ~/Downloads/authroot.stl
thread 'main' panicked at 'failed to load CTL: Der(Error { kind: Length { tag: Tag(0x02: INTEGER) }, position: Some(Length(165291)) })', examples/demo.rs:12:29
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
$ wc -c ~/Downloads/authroot.stl
165282 /Users/william/Downloads/authroot.stl It seems to jump off the end of the file, though. For context, here's the file (zipped): authroot.stl.zip |
@woodruffw weird, I'll take a look when I get a chance, but can you put up the relevant document at https://lapo.it/asn1js/ or something and I can take a look? It looks like the length isn't being correctly decoded, although I would expect an error in the event it's encountering a BER production it doesn't understand, rather than misdecoding the length. |
It's around 165KB, so it looks like it won't fit into a shareable URL 😅 I narrowed it down to the (The only integers seem to be the |
I was able to parse your blob using my fork after changing EncapsulatedContentInfo to use AnyRef instead of OctetStringRef: carl-wallace@6988578.
From: William Woodruff ***@***.***>
Reply-To: RustCrypto/formats ***@***.***>
Date: Tuesday, December 20, 2022 at 1:26 PM
To: RustCrypto/formats ***@***.***>
Cc: Subscribed ***@***.***>
Subject: Re: [RustCrypto/formats] Adds SignedData type to `pkcs7` (PR #813)
weird, I'll take a look when I get a chance, but can you put up the relevant document at https://lapo.it/asn1js/ or something and I can take a look?
It's around 165KB, so it looks like it won't fit into a shareable URL 😅
I narrowed it down to the SET OF SignerInfo, which I've screencapped below:
(The only integers seem to be the CMSVersion for the SignerInfo itself and a 150-bit integer for the SubjectKeyIdentifier).
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
Thanks for testing. It's weird that it works there, but not here -- are you allowing BER encodings for integers? |
It's me using a u8 instead of UintRef in |
I can confirm that makes the parse succeed for me locally! Thanks a ton for debugging! |
I see it’s OBE now, but I made no changes to tolerate BER (but that’s likely going to be necessary).
From: William Woodruff ***@***.***>
Reply-To: RustCrypto/formats ***@***.***>
Date: Tuesday, December 20, 2022 at 1:59 PM
To: RustCrypto/formats ***@***.***>
Cc: Carl Wallace ***@***.***>, Comment ***@***.***>
Subject: Re: [RustCrypto/formats] Adds SignedData type to `pkcs7` (PR #813)
I was able to parse your blob using my fork after changing EncapsulatedContentInfo to use AnyRef instead of OctetStringRef: ***@***.***
Thanks for testing. It's weird that it works there, but not here -- are you allowing BER encoding for integers?
—
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you commented.Message ID: ***@***.***>
|
I checked @carl-wallace's code and saw a lot of similar code in a new |
@smndtrl add it to |
I played around with this a bit, and I noticed that the current On the other hand, if I change it to this: #[derive(Clone, Copy, Debug, Eq, PartialEq, Sequence)]
pub struct EncapsulatedContentInfo<'a> {
/// indicates the type of content.
pub e_content_type: ObjectIdentifier,
/// encapsulated content
pub e_content: Option<AnyRef<'a>>,
} I get back the interior Curious about thoughts on this -- it surprises me that the current behavior (i.e. with |
@woodruffw if that's true, that sounds like a bug. Could you put together a minimal reproduction and file an issue? |
To clarify what I mean: without the changes above, the whereas I expect it to point to the parent
Yep, can do! |
Just answering my own question/confusion, based on investigation in #815: I think the behavior here is currently correct, and I was just holding the |
@roblabla this work may also be interesting to you (I saw you're also working on Authenticode support in the repo history). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM! I've been testing this locally for the past week, without problems.
I think the only thing that could go into this is sanity-checking the SignedData
content based on the CmsVersion
, but it looks like x509-cert
doesn't do that for its version either. So maybe that's a follow-on item.
pkcs7/src/cms_version.rs
Outdated
#[derive(Clone, Copy, Debug, Eq, PartialEq)] | ||
pub enum CmsVersion { | ||
/// syntax version 0 | ||
V0 = 0, | ||
/// syntax version 1 | ||
V1 = 1, | ||
/// syntax version 2 | ||
V2 = 2, | ||
/// syntax version 3 | ||
V3 = 3, | ||
/// syntax version 4 | ||
V4 = 4, | ||
/// syntax version 5 | ||
V5 = 5, | ||
} | ||
|
||
impl FixedTag for CmsVersion { | ||
const TAG: Tag = Tag::Integer; | ||
} | ||
|
||
impl From<CmsVersion> for u8 { | ||
fn from(version: CmsVersion) -> Self { | ||
version as u8 | ||
} | ||
} | ||
|
||
impl TryFrom<u8> for CmsVersion { | ||
type Error = der::Error; | ||
fn try_from(byte: u8) -> der::Result<CmsVersion> { | ||
match byte { | ||
0 => Ok(CmsVersion::V0), | ||
1 => Ok(CmsVersion::V1), | ||
2 => Ok(CmsVersion::V2), | ||
3 => Ok(CmsVersion::V3), | ||
4 => Ok(CmsVersion::V4), | ||
5 => Ok(CmsVersion::V5), | ||
_ => Err(Self::TAG.value_error()), | ||
} | ||
} | ||
} | ||
|
||
impl<'a> DecodeValue<'a> for CmsVersion { | ||
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<CmsVersion> { | ||
CmsVersion::try_from(u8::decode_value(reader, header)?) | ||
} | ||
} | ||
|
||
impl EncodeValue for CmsVersion { | ||
fn value_len(&self) -> der::Result<Length> { | ||
u8::from(*self).value_len() | ||
} | ||
|
||
fn encode_value(&self, writer: &mut dyn Writer) -> der::Result<()> { | ||
u8::from(*self).encode_value(writer) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't tested this edit specifically to ensure it adds all of these same impls, but you should be able to eliminate much of the boilerplate code here using custom derive
#[derive(Clone, Copy, Debug, Eq, PartialEq)] | |
pub enum CmsVersion { | |
/// syntax version 0 | |
V0 = 0, | |
/// syntax version 1 | |
V1 = 1, | |
/// syntax version 2 | |
V2 = 2, | |
/// syntax version 3 | |
V3 = 3, | |
/// syntax version 4 | |
V4 = 4, | |
/// syntax version 5 | |
V5 = 5, | |
} | |
impl FixedTag for CmsVersion { | |
const TAG: Tag = Tag::Integer; | |
} | |
impl From<CmsVersion> for u8 { | |
fn from(version: CmsVersion) -> Self { | |
version as u8 | |
} | |
} | |
impl TryFrom<u8> for CmsVersion { | |
type Error = der::Error; | |
fn try_from(byte: u8) -> der::Result<CmsVersion> { | |
match byte { | |
0 => Ok(CmsVersion::V0), | |
1 => Ok(CmsVersion::V1), | |
2 => Ok(CmsVersion::V2), | |
3 => Ok(CmsVersion::V3), | |
4 => Ok(CmsVersion::V4), | |
5 => Ok(CmsVersion::V5), | |
_ => Err(Self::TAG.value_error()), | |
} | |
} | |
} | |
impl<'a> DecodeValue<'a> for CmsVersion { | |
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<CmsVersion> { | |
CmsVersion::try_from(u8::decode_value(reader, header)?) | |
} | |
} | |
impl EncodeValue for CmsVersion { | |
fn value_len(&self) -> der::Result<Length> { | |
u8::from(*self).value_len() | |
} | |
fn encode_value(&self, writer: &mut dyn Writer) -> der::Result<()> { | |
u8::from(*self).encode_value(writer) | |
} | |
} | |
#[derive(Clone, Copy, Debug, Enumerated, Eq, PartialEq)] | |
#[asn1(type = "INTEGER")] | |
#[repr(u8)] | |
pub enum CmsVersion { | |
/// syntax version 0 | |
V0 = 0, | |
/// syntax version 1 | |
V1 = 1, | |
/// syntax version 2 | |
V2 = 2, | |
/// syntax version 3 | |
V3 = 3, | |
/// syntax version 4 | |
V4 = 4, | |
/// syntax version 5 | |
V5 = 5, | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to work fine just as is. Pushed the change to use the macro
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good aside from using custom derive for CmsVersion
Would this patch properly parse certificate bundles like http://repo.fpki.gov/fcpca/caCertsIssuedByfcpcag2.p7c? As I was trying to parse it I ran across the same error It "found" the signed content. Perhaps a good test case? (See https://playbooks.idmanagement.gov/fpki/common/certificates/) I'm new to Rust and was banging my head against the wall trying to figure out why this wasn't working... |
From a quick look, that does indeed look like a PKCS#7 SignedData. So this patch would (hopefully!) parse it, although it would then be up to you to interpret the SignedData's body correctly (and to actually verify the signature on it). |
// TODO: figure out what ordering makes sense - if any | ||
impl ValueOrd for SignerInfo<'_> { | ||
fn value_cmp(&self, _other: &Self) -> der::Result<Ordering> { | ||
Ok(Ordering::Equal) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to derive ValueOrd
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That'll end up requiring ValueOrd on CmsVersion and SignerIdentifier (which works nice with just adding ValueOrd there) but
the method `der_cmp` exists for enum `CmsVersion`, but its trait bounds were not satisfied
the following trait bounds were not satisfied:
`CmsVersion: ValueOrd`
which is required by `CmsVersion: DerOrd`
which ends up in when adding ValueOrd
invalid `asn1` attribute (valid options are `tag_mode`)
on CmsVersion
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright, I can go ahead and merge and see if I can figure it out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened #825 to address this
@smndtrl if you could open a PR which adds test vectors for the original BER-encoded e.g. Apple MDM signature (or just paste the relevant bytes here as e.g. hex), I can see if I can find a solution for handling BER for this specific case |
ContentInfo
x509-cert
now for some typesMissing
AttributeCertificateV2
) I haven't encounterede_content
ambiguity in PKCS#7 vs CMS standard was handled