0

Being fairly new to pyasn1 (pyasn1 0.4.8, pyasn1-modules 0.2.8) and ASN.1 in general, I'm trying to construct a GeneralName:

>>> from pyasn1.codec.der.encoder import encode
>>> from pyasn1.type import char
>>> from pyasn1_modules import rfc2459
>>> from pyasn1_modules.rfc2459 import (
...     AttributeTypeAndValue, GeneralName, Name, RelativeDistinguishedName, RDNSequence)
>>> 
>>> rdn = RelativeDistinguishedName()
>>> attr_type_and_value = AttributeTypeAndValue()
>>> attr_type_and_value['type'] = rfc2459.id_at_countryName
>>> attr_type_and_value['value'] = encode(char.UTF8String('DE'))
>>> rdn.append(attr_type_and_value)
>>> 
>>> rdn_sequence = RDNSequence()
>>> rdn_sequence.append(rdn)
>>> 
>>> name = Name()
>>> name[0] = rdn_sequence
>>> 
>>> general_name = GeneralName()
>>> general_name['directoryName'] = name
Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/pyasn1/type/univ.py", line 2246, in __setitem__
    self.setComponentByName(idx, value)
  File "/usr/lib/python3.6/site-packages/pyasn1/type/univ.py", line 2413, in setComponentByName
    idx, value, verifyConstraints, matchTags, matchConstraints
  File "/usr/lib/python3.6/site-packages/pyasn1/type/univ.py", line 3119, in setComponentByPosition
    Set.setComponentByPosition(self, idx, value, verifyConstraints, matchTags, matchConstraints)
  File "/usr/lib/python3.6/site-packages/pyasn1/type/univ.py", line 2601, in setComponentByPosition
    raise error.PyAsn1Error('Component value is tag-incompatible: %r vs %r' % (value, componentType))
pyasn1.error.PyAsn1Error: Component value is tag-incompatible: <Name value object, tagSet=<TagSet object, untagged>, [...]

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.6/site-packages/pyasn1/type/univ.py", line 2250, in __setitem__
    raise KeyError(sys.exc_info()[1])
KeyError: PyAsn1Error('Component value is tag-incompatible: <Name value object, tagSet=<TagSet object, untagged>, [...]

I truncated the very long exception messages. The essence is, as I understand it, that the supplied Name object is untagged while certain tags are expected. I can work around the exception by using general_name.setComponentByName('directoryName', name, matchTags=False), but I'm not sure whether this just turns off a desired/important check and will bite me at a later point. The pyasn1 Tag and TagSet documentation didn't enlighten me, but that's possibly because I haven't really understood the purpose of tags in ASN.1.

So, my main question is: How do I correctly create a GeneralName with pyasn1? Subquestions:

  • What's the purpose of tags in ASN.1? I thought of them as type specifiers (as in: "this is an integer, boolean, sequence, etc."), but apparently I'm missing something.
  • I use a UTF8String. Decoding a time-stamping response created with OpenSSL, I found that a PrintableString was used in a GeneralName. I intuitively chose the former, but can that lead to a problem (depending on the context of use)?
user686249
  • 669
  • 6
  • 17

1 Answers1

2

I think this is a bug or a limitation of pyasn1

I wonder if this is related to this: https://github.com/etingof/pyasn1/issues/179

The spec says

GeneralName ::= CHOICE {
           otherName                       [0]     OtherName,
           rfc822Name                      [1]     IA5String,
           dNSName                         [2]     IA5String,
           x400Address                     [3]     ORAddress,
           directoryName                   [4]     Name,
           ediPartyName                    [5]     EDIPartyName,
           uniformResourceIdentifier       [6]     IA5String,
           iPAddress                       [7]     OCTET STRING,
           registeredID                    [8]     OBJECT IDENTIFIER}

Name            ::=   CHOICE { -- only one possibility for now --
                                 rdnSequence  RDNSequence }

RDNSequence     ::=   SEQUENCE OF RelativeDistinguishedName

With ASN.1 there is usually a compilation step that generates some code. With pyasn1, you either write the code yourself or include some modules.

As you include pyasn1_modules.rfc2459, the only work is to use the classes.

Your writing looks very legitimate

>>> rdn_sequence = RDNSequence()
>>> rdn_sequence.append(rdn)
>>> 
>>> name = Name()
>>> name[0] = rdn_sequence
>>> 
>>> general_name = GeneralName()
>>> general_name['directoryName'] = name

But it seems that pyasn1 only allows the shorthand access

>>> rdn_sequence = RDNSequence()
>>> rdn_sequence.append(rdn)
>>> 
>>> general_name = GeneralName()
>>> general_name['directoryName'][''] = rdn_sequence

I think both should be allowed ...

As for the tags: as you are using the pyasn1 modules, you should not have to worry about them. They are needed to encode the messages when the encoding form is Tag/Length/Value (So, BER, CER and DER ASN.1 encoding rules).

As for the types (like UTF8String): you can't change them, they have to be the types you read from the ASN.1 Specification. They have a (so called universal) tag associated with them and your encoded message won't be understood by the receiver.

Note that there is a slight discrepancy between the implementation of Name and the spec (the spec has a named Type while the implementation has no name). This was allowed in the olden days.

class Name(univ.Choice):
    componentType = namedtype.NamedTypes(
        namedtype.NamedType('', RDNSequence())
    )

But I don't think this is a problem.

Community
  • 1
  • 1
YaFred
  • 9,698
  • 3
  • 28
  • 40