libtom / libtomcrypt

LibTomCrypt is a fairly comprehensive, modular and portable cryptographic toolkit that provides developers with a vast array of well known published block ciphers, one-way hash functions, chaining modes, pseudo-random number generators, public key cryptography and a plethora of other routines.
https://www.libtom.net
Other
1.51k stars 449 forks source link

How to encode [0] IMPLICIT SET OF? #622

Open stevemit opened 1 year ago

stevemit commented 1 year ago

Prerequisites

Description

How can I construct, using libtomcrypt release 1.18.2, a struct of type ltc_asn1_list that, when nested inside a SEQUENCE and encoded, will produce an encoded DER SET OF with an IMPLICIT CONTEXT SENSITIVE [0] tag (as in CMS SignedData certificates, for example)?

If I set list.type = LTC_ASN1_SETOF, I get a properly encoded SET but the tag is 0x31 (UNIVERSAL SET). I can manually change the tag after encoding to 0xA0, except that the SET is nested inside layers of ASN.1 data. If I use list.type = LTC_ASN1_CUSTOM_TYPE, I can get the tag I want, but the function der_encode_custom_type() encodes the items like a SEQUENCE, not in sorted order like a DER SET. If I use the macro LTC_SET_ASN1_CUSTOM_CONSTRUCTED, I can nest a SET inside a custom typed SEQUENCE, creating an EXPLICIT tag.

Am I missing something? Is there a solution in the latest developer version? It seems that I must either change the tag manually after encoding, or else pre-sort the items manually within the array list.data. A fix would be to look during der_encode_custom_type() for list.used=LTC_ASN1_SETOF.

Steps to Reproduce

Version

Release 1.18.2

Additional Information

sjaeckel commented 1 year ago

It's possible that this currently can't be done with any ltc version.

Can you provide an example "CMS SignedData certificate" in encoded format (i.e. the expected output)?

stevemit commented 1 year ago

The CMS spec RFC 5652 uses a CONTEXT SENSITIVE [0] IMPLICIT SET OF for the certificates member of a SignedData structure. A second example in CMS is the signedAttrs member of a SignerInfo structure.

But, maybe a toy example will be more useful here. In the example below, the integers are sorted by their DER encoding. This is implemented in der_encode_setof() but it isn't called from der_encode_custom_type().

Toy ::= SEQUENCE {
  hello        UTF8String,
  numbers [0]  IMPLICIT SET OF Number  }

Number ::= INTEGER { zero(0), one(1) }

30 0F 
  0C 02 68 69 
  A0 09 
    02 01 00
    02 01 00
    02 01 01

When the data type LTC_ASN1_CUSTOM_TYPE represents a primitive ASN.1 data type, the member used indicates the structure of the data member. Maybe that API could work also for a constructed ASN.1 data type, to distinguish SEQUENCE, SET and SET OF.

stevemit commented 1 year ago

The function der_encode_set() overwrites the member ltc_asn1_list.used, then passes the list to the function der_encode_sequence_ex(). Maybe der_encode_set() should use the member ltc_asn1_list.optional instead for set ordering.

It wouldn't be a bad idea to modify ltc_asn1_list to use an anonymous union to rename the members used and optional (for decoding) to custom_type and set_order (for encoding).