I'm running into a strange issue when setting a component using pyasn1. I construct and empty certificate and put a certificate to be signed in it:
empty = rfc2459.Certificate()
empty['tbsCertificate'] = rfc2459.TBSCertificate()
Now I want to set a version, which fails with an actual version object, but works by automatically creating a type:
empty['tbsCertificate']['version'] = rfc2459.Version('v3')
# PyAsn1Error: Component type error Version('v1') vs Version('v3')
empty['tbsCertificate']['version'] = 'v3'
# works
Which is strange given those two compare equal:
empty['tbsCertificate']['version'] == rfc2459.Version('v3')
# True
So why doesn't the first way work?
Those Version's formally belong to different types and they have different BER tags. The rfc2459.Version is plain INTEGER:
class Version(univ.Integer):
namedValues = namedval.NamedValues(('v1', 0), ('v2', 1), ('v3', 2))
while 'version' field of rfc2459.TBSCertificate SEQUENCE contains a subclass of Version which is defined by means of additional tagging:
class TBSCertificate(univ.Sequence):
componentType = namedtype.NamedTypes(
namedtype.DefaultedNamedType('version',Version('v1').subtype(
explicitTag=tag.Tag(
tag.tagClassContext, tag.tagFormatSimple, 0)
)
)...
That's why you can't put Version object into TBSCertificate['version']. If you could that would formally change TBSCertificate data type and its BER representation.
In the same time TBSCertificate['version'] = 'v1' works due to automatic coercion of Python string 'v1' into Version subtype through its named value (e.g. 'v1').
Their payloads (e.g. 0) indeed compare equal, which is sometimes possible even for different types. Consider:
>>> float(0) == int(0)
True
>>> float == int
False
for example.
To answer you question: I believe the right way is to rely on "coercion". That will guarantee tags/types correctness and will validate your initialiser against destination type constraints (e.g. value range, size etc). Thus you end up with fully compliant instance of ASN.1 structure.