Skip to content
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

Add extension request support #78

Merged
merged 4 commits into from
Mar 3, 2021
Merged

Conversation

Geobert
Copy link
Contributor

@Geobert Geobert commented Mar 2, 2021

Address #77 , on behalf of Isode Ltd.

Some precision on this PR:

attributes field type

the attributes field ended up being Implicit<Option<ApplicationTag0<Attribute>>> instead of
Implicit<Option<ApplicationTag0<Asn1SetOf<Attribute>>>> because after testing the output of openssl, it seems that the Asn1SetOf is not present.

using the attached files, you can use
openssl req -config req.txt -new -key key.pem -out req.pem

to generate the CSR and see that there's no SET after [0]

I was curious on what's the output of openssl if we add a challengePassword to the request: add attributes = req_attributes to the [ req ] section and rerun the openssl command, you'll see:

image

so after the [0], no SET and two consecutive SEQUENCE.

We don't need challengePassword yet, but if we add support later, I think ApplicationTag0 will need some way to have a SET without serializing it.

Unit test

I've added a Unit test for extensions using the config attached and openssl to create the PEM test data.
As attributes has change default value, the deserialize_csr test is broken, I think it needs to be updated but I prefer to ask before doing anything on this.
EDIT: maybe it's not the test, but the encoding, I've lost the [0] so ignore this point

Copyright

The legal team of my company is happy to license the contribution under the dual-license MIT-Apache-2.0 but would like to have Isode Ltd. mentioned somewhere. Some crates have a Contributor.txt file listing contributors. Would you (and Devolutions) agree to add this file to the repository? It would look like this (header copied from oxide-auth crate's repository):

# A list of people who have contributed to this project, with optional metadata
# The list is sorted chronologically, excluding the current maintainer who should always be listed first
# Format: Foo Bar <optional mail address> (comment e.g. company affiliation)

Benoît Cortier (current maintainer)
Jonathan Trépanier (on behalf of Devolutions)
Marc-André Moreau (on behalf of Devolutions)
Jean-Francois Theroux
François Dubois
Sébastien Duquette (on behalf of Devolutions)
Johann Dufaud
Richard Markiewicz
Ionuț Mihalcea
Kim Altintop
Joe Ellis
Hugues de Valon
Geobert Quach (on behalf of Isode Ltd.)

I took the time to sort the different contributors chronologically :)

Conclusion and Recap

As the message is quite large, here is a summary on what's left:

  • agree on the type of attributes
  • fix the unit test deserialize_csr because of change of attributes type
  • agree on adding a contributors list file

req.zip

@Geobert
Copy link
Contributor Author

Geobert commented Mar 2, 2021

On the test failing, I tried to change the type from Implicit<Option<ApplicationTag0<Attribute>>> to Implicit<ApplicationTag0<Option<Attribute>>> to try to get back that [0] but it seems that I need the HeaderOnly<ApplicationTag0<()>> construct for the serialization to work?

@CBenoit
Copy link
Member

CBenoit commented Mar 2, 2021

Hi @Geobert , thank you! Great first PR 🎉

the attributes field ended up being Implicit<Option<ApplicationTag0>> instead of Implicit<Option<ApplicationTag0<Asn1SetOf>>> because after testing the output of openssl, it seems that the Asn1SetOf is not present.
...
I was curious on what's the output of openssl if we add a challengePassword to the request: add attributes = req_attributes to the [ req ] section and rerun the openssl command, you'll see:

so after the [0], no SET and two consecutive SEQUENCE.

[EDIT: my explanation was wrong so I removed it to avoid misleading other people]

I asked my team leader @awakecoding about the Contributor.txt. We usually simply use the package.authors field in Cargo.toml for this purpose. I believe that's the standard way to proceed in the Rust ecosystem, and names are displayed on crates.io:
image
Given that you contributed to picky-asn1-x509 crate you would add your name in this Cargo.toml.
Would this be alright for you?
If it's okay for you, you could also add

"Sergey Noskov <snoskov@avito.ru>"
"Kim Altintop <kim@monadic.xyz>"
"Joe Ellis <joe.ellis@arm.com>"
"Hugues de Valon <hugues.devalon@arm.com>"
"Geobert Quach <geobert.quach@isode.com>"

to picky-asn-x509 and

"Ionut Mihalcea <ionut.mihalcea@arm.com>"
"Kim Altintop <kim@monadic.xyz>"

to picky. 🙂

EDIT: I'll have a deeper look at the code once you fixed the SET part 🙂

@awakecoding
Copy link
Contributor

@Geobert just to clarify about the copyright - Rust projects normally use the Cargo.toml file to declare authors, so if we add a Contributor.txt file, we'd have to maintain the list of authors in two places. Is there a reason why declaring authors in the Cargo.toml file would not be suitable?

@Geobert
Copy link
Contributor Author

Geobert commented Mar 3, 2021

Copyright

@Geobert just to clarify about the copyright - Rust projects normally use the Cargo.toml file to declare authors, so if we add a Contributor.txt file, we'd have to maintain the list of authors in two places. Is there a reason why declaring authors in the Cargo.toml file would not be suitable?

Thank you for your answer! I think having the copyright in Cargo.toml is perfect :) I totally forgot this field ^^'

ApplicationTag

I tried pub attributes: ApplicationTag0<Asn1SetOf<Attribute>>,

which leads to:

pub fn new(subject: Name, subject_public_key_info: SubjectPublicKeyInfo) -> Self {
        // It shall be 0 for this version of the standard.
        Self {
            version: 0,
            subject,
            subject_public_key_info,
            attributes: ApplicationTag0(Asn1SetOf(Vec::new())),
        }
    }

but this gives:
image

The SET tag is not replaced with the ApplicationTag0 tag :-/

@Geobert
Copy link
Contributor Author

Geobert commented Mar 3, 2021

From what Benoît said, my understanding of ApplicationTag is that its tag replace the tag of the object it contains, so ApplicationTag0<Asn1SetOf<Attribute>> should serialize by replacing the 0x31 tag from the SET by 0xA0 from the ApplicationTag but we can see that it is not happening.

I'm trying to understand how ApplicationTag0 is serialized and I can only see in ser::Serializer::serialize_newtype_struct that in the case of Tag0, we "encapsulate" which I'm not sure to understand :-/

Is the ApplicationTag* ser broken? This seems unlikely as you use the crate with no issue and have plenty of tests to back that up. So I'm confused :-/

@CBenoit
Copy link
Member

CBenoit commented Mar 3, 2021

From what Benoît said, my understanding of ApplicationTag is that its tag replace the tag of the object it contains, so ApplicationTag0<Asn1SetOf<Attribute>> should serialize by replacing the 0x31 tag from the SET by 0xA0 from the ApplicationTag but we can see that it is not happening.

I'm trying to understand how ApplicationTag0 is serialized and I can only see in ser::Serializer::serialize_newtype_struct that in the case of Tag0, we "encapsulate" which I'm not sure to understand :-/

Is the ApplicationTag* ser broken? This seems unlikely as you use the crate with no issue and have plenty of tests to back that up. So I'm confused :-/

Oh, let me check, maybe something is wrong somewhere.

@Geobert
Copy link
Contributor Author

Geobert commented Mar 3, 2021

Oh, let me check, maybe something is wrong somewhere.

I don't know if it can help, but I noticed that is_context_specific is called in h_write_header but I can't find any call to is_application anywhere

EDIT: by modifying in ser::Serializer::h_write_header and adding the testing of is_application:

Some(last_encapsulator_tag)
                if last_encapsulator_tag.is_context_specific() || last_encapsulator_tag.is_application() =>
            {
                written = self.h_write_encapsulator(len)?;
            }

the serialization seems correct, but for a weird reason, it breaks the deserialization:

thread 'certification_request::tests::deserialize_csr' panicked at 'failed certification_request_info deserialization: TruncatedData',

@CBenoit
Copy link
Member

CBenoit commented Mar 3, 2021

Oh, let me check, maybe something is wrong somewhere.

I don't know if it can help, but I noticed that is_context_specific is called in h_write_header but I can't find any call to is_application anywhere

I think I misunderstood something 🤦
Tag is replaced only if field is marked as IMPLICIT. If EXPLICIT, application tag is basically wrapping similarly to a SEQUENCE.
Apparently I implemented application tags as always EXPLICIT and context specific tags as always IMPLICIT.
Sadly, we need an "IMPLICIT" 0xA0. I think I'll need to perform a refactor at some point to fix that issue. For now, let's use something like Implicit<Option<ApplicationTag0<Attribute>>>. If it's okay with you I'll push modifications to make it work until I refactor properly.

@CBenoit
Copy link
Member

CBenoit commented Mar 3, 2021

EDIT: by modifying in ser::Serializer::h_write_header and adding the testing of is_application:

Some(last_encapsulator_tag)
                if last_encapsulator_tag.is_context_specific() || last_encapsulator_tag.is_application() =>
            {
                written = self.h_write_encapsulator(len)?;
            }

the serialization seems correct, but for a weird reason, it breaks the deserialization:

thread 'certification_request::tests::deserialize_csr' panicked at 'failed certification_request_info deserialization: TruncatedData',

Yes, deserializer would need to be modified the same way. However, as said above, issue is a bit deeper here 😕

@Geobert
Copy link
Contributor Author

Geobert commented Mar 3, 2021

However, as said above, issue is a bit deeper here

In that case, the PR is complete with Implicit<Option<ApplicationTag0<Attribute>>> instead of Implicit<ApplicationTag0<Asn1SetOf<Attribute>>> which needs refactoring to work :)

The unit test for extensions works, but the empty ApplicationTag0 test is broken of course

@CBenoit
Copy link
Member

CBenoit commented Mar 3, 2021

However, as said above, issue is a bit deeper here

In that case, the PR is complete with Implicit<Option<ApplicationTag0<Attribute>>> instead of Implicit<ApplicationTag0<Asn1SetOf<Attribute>>> which needs refactoring to work :)

The unit test for extensions works, but the empty ApplicationTag0 test is broken of course

Good! I'll fix that, merge and publish 🙂

CBenoit
CBenoit previously approved these changes Mar 3, 2021
Copy link
Member

@CBenoit CBenoit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I implemented a workaround so this behaves like a proper IMPLICIT [0] on a sequence.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants