pyasn1

Handling nested DER, packed into OCTET STRINGs, in pyasn1


Here's how RFC 5280 defines an X.509 extension field:

Extension  ::=  SEQUENCE  {
     extnID      OBJECT IDENTIFIER,
     critical    BOOLEAN DEFAULT FALSE,
     extnValue   OCTET STRING
                 -- contains the DER encoding of an ASN.1 value
                 -- corresponding to the extension type identified
                 -- by extnID
     }

When I use the pyasn1 decoder, I'll get an object foo whose foo['extnValue'].asOctets() can be further decoded, with another call to the decoder, using the schema appropriate to foo['extnID'].

Question: supposing that there's one special extnID in my application, is it possible in pyasn1 to define a schema that (a) doesn't accept any OBJECT IDENTIFIER, only the special one; and (b) steps past the OCTET STRING wrapper to decode the payload according to the appropriate special "sub-schema" ?

I can do this by special-case logic in the code, but I'd prefer to define a special-case schema if that is supported.


Solution

  • You should use OpenType for extnValue type definition. When you pass decoder the map of extnID -> ASN.1 type, the decoder then will step past the outermost extnValue wrapper trying to decode its contents.

    Here's a quick example:

    openType = opentype.OpenType(
            'id',
            {1: univ.Integer(),
             2: univ.OctetString()}
        )
        self.s = univ.Sequence(
            componentType=namedtype.NamedTypes(
                namedtype.NamedType('id', univ.Integer()),
                namedtype.NamedType('blob', univ.Any(), openType=openType)
            )
        )
    

    Make sure to pass decodeOpenTypes=True argument to the decode() function if you want the decoder to unwrap these open types.

    BTW, if you look at the pyasn1-modules, that map is already there. If you use these definitions and just pass decodeOpenTypes=True keyword argument to decoder, you should get your extensions unwrapped.

    There is no ready-made way to fail the decoder on unknown extnID. You can probably model that my giving it your own map (perhaps based on dict) which would fail on unsuccessful key in my_map operation.