Skip to content

der: add IsConstructed trait, impl'ed on any FixedTag #1744

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

Merged

Conversation

dishmaker
Copy link
Contributor

@dishmaker dishmaker commented Mar 27, 2025

Closes #1741

Depends on:

This PR drops the need to implement Tagged on all structs that are IMPLICIT.

Such change simplifies trait bounds for generic IMPLICIT fields and enforces strict const constructed bit check.

@tarcieri
Copy link
Member

This doesn't seem to do a whole lot as-is. What else are you planning on impl'ing IsConstructed on?

@dishmaker
Copy link
Contributor Author

On trait Choice and my weird little edge case where derive(Choice) needed Tagged on something that wasn't.

@dishmaker
Copy link
Contributor Author

dishmaker commented Mar 27, 2025

If I remember correctly, the edge case is here:
https://github.com/monai/node-passport/blob/master/lib/pkcs15/cryptographic_information_framework.asn1#L181

So it's a CHOICE with [0] CHOICE inside for ObjectValue {RSAPublicKeyChoice}

@tarcieri
Copy link
Member

You should try to add it there, as I think for what you’re proposing the blanket impls(?) would conflict

@dishmaker dishmaker marked this pull request as draft March 27, 2025 15:27
@dishmaker
Copy link
Contributor Author

It will take me some time to recreate the Tagged IMPLICIT bug with CHOICE inside [0] CHOICE :)

@dishmaker
Copy link
Contributor Author

dishmaker commented Mar 28, 2025

I give up. It's pretty much impossible to implement this case in current der, without splitting EXPLICIT and IMPLICIT encoders.

#[derive(Choice)]
pub enum ObjectValue<T>
where
    for<'a> T: /* what to put here ? */,
{
    Indirect(Path),

    #[asn1(context_specific = "0", tag_mode = "IMPLICIT")]
    Direct(T),
}
[Spoiler] error[E0277]
error[E0277]: the trait bound `RSAPublicKeyChoice: FixedTag` is not satisfied
   --> der/tests/derive.rs:261:52
    |
261 |             let object_value = ObjectValue::Direct(rsa_choice);
    |                                ------------------- ^^^^^^^^^^ the trait `der::Sequence<'_>` is not implemented for `RSAPublicKeyChoice`
    |                                |
    |                                required by a bound introduced by this call
    |
    = note: required for `RSAPublicKeyChoice` to implement `FixedTag`
note: required by a bound in `ObjectValue::Direct`
   --> der/tests/derive.rs:220:78
    |
220 |             for<'a> T: Encode + Decode<'a> + EncodeValue + DecodeValue<'a> + FixedTag,
    |                                                                              ^^^^^^^^ required by this bound in `ObjectValue::Direct`
...
225 |             Direct(T),
    |             ------ required by a bound in this tuple variant

So ObjectValue::Direct should be bound:

  • only by EncodeValue + DecodeValue<'a> + IsConstructed,
  • but now Encode + Decode<'a> + EncodeValue + DecodeValue<'a> + FixedTag is required.

Tagged and FixedTag traits are forced elsewere in the crate by ContextSpecific wrapper.

@dishmaker
Copy link
Contributor Author

Btw CHOICE inside CHOICE isn't a problem. Such case can be simplified by hand.

/// ```asn1
/// ReferencedValue {Type} ::= CHOICE {
///     path Path,
///     url URL
/// }
/// URL ::= CHOICE {
///     url CHOICE {
///         printable PrintableString, 
///         ia5 IA5String
///     },
///     urlWithDigest [3] SEQUENCE {
///         url IA5String,
///         digest DigestInfoWithDefault
///     }
/// }
/// ```
#[derive(Choice, Clone, Debug, Eq, PartialEq)]
#[tag_mode = "IMPLICIT"]
pub enum ReferencedValue {
    Path(Path),
    PrintableStringUrl(PrintableString),
    Ia5StringUrl(Ia5String),
    #[asn1(context_specific = "3")]
    UrlWithDigest(()),
}

@dishmaker dishmaker force-pushed the dishmaker/der_isconstructed_trait branch from dda1215 to 5ee5ef6 Compare April 3, 2025 14:25
@dishmaker dishmaker force-pushed the dishmaker/der_isconstructed_trait branch from 5ee5ef6 to be4197a Compare April 21, 2025 18:11
@dishmaker
Copy link
Contributor Author

Nice. IsConstructed trait actually found a bug by itself.

NegHints ::= SEQUENCE {
     hintName[0] GeneralString OPTIONAL,

AnyRef can be primitive or constructed, but GeneralString is strictly primitive.

@dishmaker dishmaker marked this pull request as ready for review April 21, 2025 18:42
@dishmaker dishmaker force-pushed the dishmaker/der_isconstructed_trait branch from 3a595d8 to 26d6959 Compare April 21, 2025 18:45
der_derive: impl IsConstructed on derive(Choice)

der: draft of test for IsConstructed vs Tagged edge case

der: test: generic CHOICE inside [0] IMPLICIT CHOICE

Revert "der: test: generic CHOICE inside [0] IMPLICIT CHOICE"

This reverts commit dda1215.

Revert "der: draft of test for IsConstructed vs Tagged edge case"

This reverts commit 5daa9ba.

der: add IsConstructed test

docs

der: format tests

simplify IsConstructed test
@dishmaker dishmaker force-pushed the dishmaker/der_isconstructed_trait branch from 26d6959 to 5115c1a Compare April 25, 2025 20:40
@tarcieri tarcieri merged commit be1caa3 into RustCrypto:master Apr 26, 2025
107 checks passed
Sign up for free to 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: add IsConstructed trait
2 participants