pythonx509derpyasn1

Parsing X509 extensions with pyasn1


I've got an extension which is described as follows:

Extension().setComponentByPosition(0, ObjectIdentifier(2.5.29.19))
           .setComponentByPosition(1, Boolean('False'))
           .setComponentByPosition(2, Any(hexValue='04023000'))

So based on the id, it's a BasicConstraints extension. However if I try to parse the value as the extension itself, I get an error:

decoder.decode(decoder.decode(e['extnValue'])[0], rfc2459.BasicConstraints())
# PyAsn1Error: Uninitialized component #0 at BasicConstraints()

That string decodes to an empty sequence, so it can legally be a BasicConstraints - name length optional/missing and ca is default, so not encoded in DER.

So what am I missing here? How do I decode this extension to the BasicConstraints class?

PS. this issue seems similar to an issue in mailing list, but I'm using 0.1.8 which should already include the mentioned fix


Solution

  • Since BasicConstraints is a SEQUENCE derivative, its minimum possible serialisation is SEQUENCE tag and zero length. Then its value could be an empty string, as you suggested. And this is in fact how it is:

    >>> derSerialisation, _ = decode(OctetString(hexValue='04023000'))
    >>> derSerialisation.prettyPrint()
    '0x3000'
    >>> constraint, _ = decode(derSerialisation)
    >>> constraint.prettyPrint()
    'Sequence:\n'
    

    The ANY value is opaque (tagless), but embedded DER serialisation is itself encoded as OCTET STRING. So make sure you extract DER contents from OCTET STRING serialisation before passing it to decoder to recover BasicConstraints.

    Mailing list bug is not relevant - that was in indefinite encoding mode.

    UPDATED

    It turned out to be a bug in rfc2459.BasicConstraints specification. While official fix/release is being prepared, I could advise the following monkey patch to pyasn1_modules.rfc2459:

    >>> from pyasn1.type import namedtype
    >>> from pyasn1_modules import rfc2459
    >>> rfc2459.BasicConstraints.componentType = namedtype.NamedTypes(
    ...     namedtype.DefaultedNamedType(*rfc2459.BasicConstraints.componentType[0]),
    ...     rfc2459.BasicConstraints.componentType[1]
    ... )
    

    which basically marks 'cA' component as defaulted. Once applied, your serialization can be decoded:

    >>> s
    Any(hexValue='04023000')
    >>> basicConstraints, _ = decoder.decode(decoder.decode(s)[0], rfc2459.BasicConstraints())
    >>> print(basicConstraints.prettyPrint())
    BasicConstraints:
     cA='False'
    

    UPDATED AGAIN

    The above bug is fixed in pyasn1-modules 0.0.7