Skip to content

support private tags and tag numbers >30 that are stored in long form - #1416

Closed
kamulos wants to merge 4 commits into
RustCrypto:masterfrom
kamulos:private_long_form_tags
Closed

support private tags and tag numbers >30 that are stored in long form#1416
kamulos wants to merge 4 commits into
RustCrypto:masterfrom
kamulos:private_long_form_tags

Conversation

@kamulos

Copy link
Copy Markdown

closes#1381

This PR implements two things:

  • Allow the use of tag numbers >30. In the der representation they are stored in the long form as multiple bytes.
  • Allow PRIVATE tags be annotated like context specific ones in the derive macros

Current State

Currently this is a draft to get feedback on the chosen approach. This code is in use in my project and works correctly for it's use-case: a lot of private tags in a deep hierarchy of choices and sequences.

For the most part this code should be in a state to be reviewed. There are a few parts that are still lacking:

  • The code duplication of the private module, that is basically just a copy of the context_sensitive module
  • The tag parsing and serializing code is too complicated
  • The tests have not been adjusted / written

Overview

TagNumber is now represented by an u16. This should be enough for any use-case imaginable, but also small enough to be embedded-friendly. It's content is now pub instead of pub(super). This was necessary because der_derive wants to match against constant TagNumbers. In general having this public should not be an issue, because all 2^16 tag numbers are now valid and it is not possible to create an invalid state by having access to the u16.

Also the Private and PrivateRef types exist now. In the derive macros private can be annotated in the same way as context_specific. Internally this is represented as class: Option<Class> instead of context_sensitive: Option<TagNumber>.

Breaking changes that I found no way to avoid are:

  • TagNumber::new() and TagNumber::value() use u16 now
  • Conversions from and to u8 for Tag and TagNumber do not make sense anymore, this also applies to Tag::octet()
  • The Reader trait now needs a function to peek multiple bytes in advance to be able to peek a tag (Reader::peek_offset())
  • Length::for_tlv() just assumed, that a tag is always one byte, it now somehow needs to know about the specific tag we are looking at

Comment threadder/src/reader.rs Outdated
@dishmaker

dishmaker commented May 28, 2024

Copy link
Copy Markdown
Contributor
[Spoiler] Unrelated to >30 tag breaking change

The code duplication of the private module, that is basically just a copy of the context_sensitive module

I've made it generic for my use case.

/// Application classtypeApplication<T,constTAG:u16> = CustomClass<T,TAG,0b01>;/// Context-specific classtypeContextSpecific<T,constTAG:u16> = CustomClass<T,TAG,0b10>;/// Private classtypePrivate<T,constTAG:u16> = CustomClass<T,TAG,0b11>;/// Application, Context-specific or Private class field which wraps an owned inner value.////// This type decodes/encodes a field which is specific to a particular context/// and is identified by a [`TagNumber`].#[derive(Copy,Clone,Debug,Eq,PartialEq,PartialOrd,Ord)]pubstructCustomClass<T,constTAG:u16,constCLASS:u8>{/// Tag mode: `EXPLICIT` VS `IMPLICIT`.pubtag_mode:TagMode,/// Value of the field.pubvalue:T,}

Shouldn't tag_mode be generic too?

@dishmaker

Copy link
Copy Markdown
Contributor

This crate is still unusable for the European Tachograph regulation.

There are many 2-byte tags.

https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=celex%3A02016R0799-20200226

FieldField IDTagLength (bytes)ASN.1 data type
ECC CertificateC7F 21varcustom SEQUENCE
ECC Certificate BodyB7F 4Evarcustom SEQUENCE
Certificate Profile IdentifierCPI5F 291INTEGER(0..255)
Certificate Authority ReferenceCAR428KeyIdentifier
Certificate Holder AuthorisationCHA5F 4C7CertificateHolder Authorisation
Public KeyPK7F 49varcustom SEQUENCE
Domain ParametersDP06varOBJECT IDENTIFIER
Public PointPP86varOCTET STRING
Certificate Holder ReferenceCHR5F 208KeyIdentifier
Certificate Effective DateCEfD5F 254TimeReal
Certificate Expiration DateCExD5F 244TimeReal
ECC Certificate SignatureS5F 37varOCTET STRING

@baloo

Copy link
Copy Markdown
Member

@dishmaker we're still open for breaking changes in this release cycle. There is a legitimate use-case, PRs are welcome!

@kamulos

Copy link
Copy Markdown
Author

Sorry for the slow reaction. In a week I am back and try to get in some more progress here.

@tarcieri

Copy link
Copy Markdown
Member

@kamulos I've finished making the other changes I consider blockers for this one, though it's created quite a few conflicts.

If you can get this PR green though, I can make it a priority for the next to merge. I don't expect there will be many other major changes to der itself.

@kamulos

Copy link
Copy Markdown
Author

@tarcieri Thanks for your work ❤️ And sorry for being very slow, the workload at the job was high and I also had vacations.

Currently we are actually releasing this code into production and things will calm down from here. So I will get back to this next week and hopefully get some version ready that is good quality.

@tarcieri

Copy link
Copy Markdown
Member

@kamulos no worries, we'd like to do a release "soon", but that's probably in a month or two so you have some time

@kamulos
kamulosforce-pushed the private_long_form_tags branch 3 times, most recently from 39e8b97 to 1f43a08CompareOctober 2, 2024 14:33
@kamulos

Copy link
Copy Markdown
Author

@tarcieri I rebased and polished the implementation. The unit tests for the Private type are still broken, because I did not adjust them and I should definitely write some more unit Tests for the encoding and decoding of the Tag type.

But other than that I think it is ready for review. 👍

There are some noteworthy points:

  1. The decode_with function in Private and ContextSpecific requires peeking for a Tag but maybe getting None as return. Therefore I added another function Tag::peek_optional that is only crate internal. This feels a little bit wring, but I did not want to modify the signature of public functions or traits.
  2. Private is basically a copy of ContextSpecific with one difference: In decode_with I removed the skipping of lower tag numbers. This did not work for my use-case and I did not understand it.

@kamulos
kamulos marked this pull request as ready for review October 2, 2024 14:50
@dishmaker

dishmaker commented Oct 4, 2024

Copy link
Copy Markdown
Contributor
[Spoiler] Unrelated to >30 tags

Private is basically a copy of ContextSpecific

Well, I also need Application 🥺

I think the best solution is using generics:

pubtypeApplicationExplicit<constTAG:u16,T> = CustomClassExplicit<T,TAG,CLASS_APPLICATION>;pubtypeContextSpecificExplicit<constTAG:u16,T> = CustomClassExplicit<T,TAG,CLASS_CONTEXT_SPECIFIC>;pubtypePrivateExplicit<constTAG:u16,T> = CustomClassExplicit<T,TAG,CLASS_PRIVATE>;pubtypeApplicationImplicit<constTAG:u16,T> = CustomClassImplicit<T,TAG,CLASS_APPLICATION>;pubtypeContextSpecificImplicit<constTAG:u16,T> = CustomClassImplicit<T,TAG,CLASS_CONTEXT_SPECIFIC>;pubtypePrivateImplicit<constTAG:u16,T> = CustomClassImplicit<T,TAG,CLASS_PRIVATE>;/// EXPLICIT#[derive(Copy,Clone,Debug,Eq,PartialEq,PartialOrd,Ord)]pubstructCustomClassExplicit<T:for<'a>Decode<'a>,constTAG:u16,constCLASS:u8>{pubvalue:T,}/// IMPLICIT#[derive(Copy,Clone,Debug,Eq,PartialEq,PartialOrd,Ord)]pubstructCustomClassImplicit<T:for<'a>DecodeValue<'a>,constTAG:u16,constCLASS:u8>{pubvalue:T,}pubconstCLASS_APPLICATION:u8 = 0b01000000;pubconstCLASS_CONTEXT_SPECIFIC:u8 = 0b10000000;pubconstCLASS_PRIVATE:u8 = 0b11000000;

@dishmaker

Copy link
Copy Markdown
Contributor

I've fixed all dependencies and added these 3 custom tags as generic ones :)

@tarcieri

Copy link
Copy Markdown
Member

@kamulos if you are still interested in this, I would prefer a much more minimal PR which only changes the handling of the Tag type, and doesn't try to add higher-level APIs beyond that, which are really what's blocking getting this merged

@kamulos
kamulosforce-pushed the private_long_form_tags branch from 1f43a08 to ad41d37CompareFebruary 13, 2025 17:29
@kamulos

Copy link
Copy Markdown
Author

@tarcieri thanks for the message. I looked into it, rebased my commit and removed the stuff in a new commit.

With my last commit I added a three unit test cases, where the decoder is pretty forgiving at the moment. The second test case ("universal tag in long form") might be ok, but the other two accept invalid der encoding. Should they be rejected? I saw in the API that there is also a .from_ber() function now, and these encodings might be acceptable in the ber encoding. (not sure about that)

@tarcieri

Copy link
Copy Markdown
Member

@kamulos thanks, though now we have three PRs open for this. #1651 is looking pretty good and has working tests

@kamulos

Copy link
Copy Markdown
Author

@tarcieri Yeah, that PR is good. u32 instead of u16 seems reasonable to me.

But obviously, the issues I codified in my test cases here affect that one too, because the code is a copy. So you might still want to have a look at the cases.

The adjusted Test case in the other PR, where they put in ReasonFlags::from_der(&hex!("FF03079F80"));` is what made me realize that there might still be questions about the exact behavior open.

@tarcieri

Copy link
Copy Markdown
Member

@kamulos can you perhaps open a PR to #1651 to add your test cases?

@turbocool3r

Copy link
Copy Markdown
Contributor

@kamulos can you perhaps open a PR to #1651 to add your test cases?

I'll be unavailable for about 4 days, but I'll be happy to accept a PR or include the necessary changes myself. Also I should add @kamulos to PR authors somehow, is there any convention for this? Are you ok with this @kamulos?

@tarcieri

Copy link
Copy Markdown
Member

Ended up going with #1651 instead

@tarcieritarcieri closed this May 5, 2025
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

der: Encoding higher tag number

5 participants

@kamulos@dishmaker@baloo@tarcieri@turbocool3r