P1sec / pycrate

A Python library to ease the development of encoders and decoders for various protocols and file formats; contains ASN.1 and CSN.1 compilers.
GNU Lesser General Public License v2.1
382 stars 132 forks source link

Wiki needs more info on compiling custom ASN.1 definitions #254

Open James-E-A opened 9 months ago

James-E-A commented 9 months ago

I'm trying to add a new AlgorithmIdentifier in to X.509 at application start-up time, using the function described on the Wiki, but I'm seeing this bizarre behavior where the first compile_text() fails, but the second succeeds, for some reason:

# Python 3.11.7 (tags/v3.11.7:fa7a6f2, Dec  4 2023, 19:24:49) [MSC v.1937 64 bit (AMD64)] on win32
from pycrate_asn1c import asnproc

asnproc.compile_text("""\
HelloX509 DEFINITIONS ::=

BEGIN

-- EXPORTS All

IMPORTS
  algorithm, authenticationFramework
    FROM UsefulDefinitions {joint-iso-itu-t ds(5) module(1) usefulDefinitions(0) 8}

  ALGORITHM, AlgorithmIdentifier{}, SupportedAlgorithms, SupportedCurves
    FROM AuthenticationFramework authenticationFramework;

  ID ::= OBJECT IDENTIFIER

  null-with-null ID ::= {iso(1) standard(0) 20248 digital-signature-methods(1) null-with-null(1)}

  null-with-null-Algorithm ALGORITHM ::= {
  -- PARMS      ABSENT
  IDENTIFIED BY null-with-null }

END -- HelloX509
""")
# [proc] module HelloX509 (oid: []): 4 ASN.1 assignments found
# --- compilation cycle ---
# Traceback (most recent call last):
#   File "…\pycrate_asn1c\asnobj.py", line 92, in get_asnobj
#     mod = GLOBAL.MOD[mod_name]
#           ~~~~~~~~~~^^^^^^^^^^
#   File "…\pycrate_asn1c\dictobj.py", line 69, in __getitem__
#     return self._dict[key]
#            ~~~~~~~~~~^^^^^
# KeyError: 'AuthenticationFramework'
#
# During handling of the above exception, another exception occurred:
#
# Traceback (most recent call last):
#   File "…\pycrate_asn1c\asnobj.py", line 675, in get_typeref
#     tr = get_asnobj(ref.called[0], ref.called[1])
#          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#   File "…\pycrate_asn1c\asnobj.py", line 94, in get_asnobj
#     raise(ASN1Err('module {0}, undefined'.format(mod_name)))
# pycrate_asn1c.err.ASN1Err: module AuthenticationFramework, undefined
#
# During handling of the above exception, another exception occurred:
#
# Traceback (most recent call last):
#   File "<stdin>", line 1, in <module>
#   File "…\pycrate_asn1c\asnproc.py", line 254, in compile_text
#     compile_modules(remain)
#   File "…\pycrate_asn1c\asnproc.py", line 942, in compile_modules
#     ObjNew = asnobj_compile(Obj)
#              ^^^^^^^^^^^^^^^^^^^
#   File "…\pycrate_asn1c\asnproc.py", line 866, in asnobj_compile
#     text = Obj.parse_def(text)
#            ^^^^^^^^^^^^^^^^^^^
#   File "…\pycrate_asn1c\asnobj.py", line 1703, in parse_def
#     text = self._parse_type(text)
#            ^^^^^^^^^^^^^^^^^^^^^^
#   File "…\pycrate_asn1c\asnobj.py", line 1929, in _parse_type
#     tr = self.get_typeref()
#          ^^^^^^^^^^^^^^^^^^
#   File "…\pycrate_asn1c\asnobj.py", line 677, in get_typeref
#     raise(ASN1ProcTextErr('{0}: {1}'\
# pycrate_asn1c.err.ASN1ProcTextErr: null-with-null-Algorithm: module AuthenticationFramework, undefined

asnproc.compile_text("""\
HelloX509 DEFINITIONS ::=

BEGIN

-- EXPORTS All

IMPORTS
  algorithm, authenticationFramework
    FROM UsefulDefinitions {joint-iso-itu-t ds(5) module(1) usefulDefinitions(0) 8}

  ALGORITHM, AlgorithmIdentifier{}, SupportedAlgorithms, SupportedCurves
    FROM AuthenticationFramework authenticationFramework;

  ID ::= OBJECT IDENTIFIER

  null-with-null ID ::= {iso(1) standard(0) 20248 digital-signature-methods(1) null-with-null(1)}

  null-with-null-Algorithm ALGORITHM ::= {
  -- PARMS      ABSENT
  IDENTIFIED BY null-with-null }

END -- HelloX509
""")
# [proc] module HelloX509 (oid: []): 4 ASN.1 assignments found
# [proc] module HelloX509: already compiled but OID missing or mismatch
# --- verifications ---
# [proc] ASN.1 modules processed: ['HelloX509']
# [proc] ASN.1 objects compiled: 1 types, 0 sets, 2 values
# [proc] done

print(asnproc.GLOBAL.MOD['HelloX509'])
# {
# _name_: 'HelloX509',
# _oidstr_: '',
# _oid_: [],
# _tag_: 'EXPLICIT',
# _ext_: None,
# _exp_: None,
# _imp_: {'algorithm': 'UsefulDefinitions', 'authenticationFramework': 'UsefulDefinitions', 'ALGORITHM': 'AuthenticationFramework', 'AlgorithmIdentifier': 'AuthenticationFramework', 'SupportedAlgorithms': 'AuthenticationFramework', 'SupportedCurves': 'AuthenticationFramework'},
# _obj_: ['ID', 'null-with-null', 'null-with-null-Algorithm'],
# _type_: ['ID'],
# _set_: [],
# _val_: ['null-with-null', 'null-with-null-Algorithm'],
# _class_: [],
# _param_: [],
# ID: <ID (OBJECT IDENTIFIER)>,
# null-with-null: <null-with-null ([ID] OBJECT IDENTIFIER): [1, 0, 20248, 1, 1]>,
# null-with-null-Algorithm: <null-with-null-Algorithm ([ALGORITHM] None):  >
# }

This is obviously pretty disturbing, and I'd much rather see the "right" way to do this, but the Wiki doesn't seem to go into any detail...

mitshell commented 7 months ago

Thank you for bringing this issue. You are using an internal function of the whole ASN.1 compiler. You are getting an error because your module is importing undefined objects. You should better use the tool pycrate_asn1compile.py to compile this kind of module. I have to admit the Wiki maybe a bit dated for your case...

Please note a new repository has been setup, including new fixes. This repo will keep being maintained : https://github.com/pycrate-org/pycrate. Any contribution related to the Wiki or any other parts of the library will be much appreciated.