formats icon indicating copy to clipboard operation
formats copied to clipboard

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

Open dishmaker opened this issue 11 months ago • 7 comments

Closes #1741

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

For example CHOICE does not implement FixedTag, but is always constructed.

dishmaker avatar Mar 27 '25 14:03 dishmaker

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

tarcieri avatar Mar 27 '25 14:03 tarcieri

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

dishmaker avatar Mar 27 '25 15:03 dishmaker

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}

dishmaker avatar Mar 27 '25 15:03 dishmaker

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

tarcieri avatar Mar 27 '25 15:03 tarcieri

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

dishmaker avatar Mar 27 '25 15:03 dishmaker

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 avatar Mar 28 '25 09:03 dishmaker

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 avatar Mar 28 '25 10:03 dishmaker

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 avatar Apr 21 '25 18:04 dishmaker