3 # asn2quickder -- Generate header files for C for use with Quick `n' Easy DER
5 # This program owes a lot to asn1ate, which was built to generate pyasn1
6 # classes, but which was so well-written that it could be extended with a
7 # code generator for Quick DER.
9 # Much of the code below is diagonally inspired on the pyasn1 backend, so
10 # a very big thank you to Schneider Electric Buildings AB for helping to
11 # make this program possible!
13 # Copyright 2016 InternetWide.org and the ARPA2.net project.
19 from asn1ate import parser
20 from asn1ate.sema import *
24 """Replace unsupported characters in ASN.1 symbol names"""
25 return str(name).replace(' ', '').replace('-', '_')
29 """Generate the C header files for Quick DER, a.k.a. Quick and Easy DER.
31 There are two things that are generated for each of the ASN.1 syntax
32 declaration symbol of a unit:
34 #define DER_PACK_unit_SyntaxDeclSym \
35 DER_PACK_ENTER | ..., \
40 this is a walking path for the der_pack() and der_unpack() instructions.
41 In addition, there will be a struct for each of the symbols:
43 struct unit_SyntaxDeclSym_ovly {
46 struct unit_EmbeddedSym_ovly field3;
50 The unit prefix will be set to the filename of the module, usually
51 something like rfc5280 when the parsed file is rfc5280.asn1 and the
52 output is then written to rfc5280.h for easy inclusion by the C code.
55 def __init__(self, semamod, outfn, refmods):
56 self.semamod = semamod
57 self.refmods = refmods
58 if outfn [-2:] == '.h':
59 raise Exception('File cannot overwrite itself -- use another extension than .h for input files')
60 self.unit = toCsym(outfn.rsplit('.', 1) [0])
61 self.outfile = open(self.unit + '.h', 'w')
62 self.wout = self.outfile.write
65 DefinedType: self.ovlyDefinedType,
66 ValueAssignment: self.ignore_node,
67 TypeAssignment: self.ovlyTypeAssignment,
68 TaggedType: self.ovlyTaggedType,
69 SimpleType: self.ovlySimpleType,
70 BitStringType: self.ovlySimpleType,
71 ValueListType: self.ovlySimpleType,
72 SequenceType: self.ovlyConstructedType,
73 SetType: self.ovlyConstructedType,
74 ChoiceType: self.ovlyConstructedType,
75 SequenceOfType: self.ovlySimpleType, # var sized
76 SetOfType: self.ovlySimpleType, # var sized
77 ComponentType: self.ovlySimpleType, #TODO#
80 DefinedType: self.packDefinedType,
81 ValueAssignment: self.ignore_node,
82 TypeAssignment: self.packTypeAssignment,
83 TaggedType: self.packTaggedType,
84 SimpleType: self.packSimpleType,
85 BitStringType: self.packSimpleType,
86 ValueListType: self.ovlySimpleType,
87 SequenceType: self.packSequenceType,
88 SetType: self.packSetType,
89 ChoiceType: self.packChoiceType,
90 SequenceOfType: self.packSequenceOfType,
91 SetOfType: self.packSetOfType,
92 ComponentType: self.packSimpleType, #TODO#
95 def newcomma(self, comma, firstcomma=''):
96 self.comma0 = firstcomma
100 self.wout(self.comma0)
101 self.comma0 = self.comma1
106 def generate_head(self):
107 self.wout('/*\n * asn2quickder output for ' + self.semamod.name + ' -- automatically generated\n *\n * For information on Quick `n\' Easy DER, see https://github.com/vanrein/quick-der\n *\n * For information on the code generator, see https://github.com/vanrein/asn2quickder\n *\n */\n\n\n#include <quick-der/api.h>\n\n\n')
109 for rm in self.semamod.imports.module2symbols.keys():
110 rmfn = toCsym(rm.rsplit('.', 1) [0]).lower()
111 self.wout('#include <quick-der/' + rmfn + '.h>\n')
115 for rm in self.semamod.imports.module2symbols.keys():
116 rmfn = toCsym(rm.rsplit('.', 1) [0]).lower()
117 for sym in self.semamod.imports.module2symbols [rm]:
118 self.wout('typedef DER_OVLY_' + toCsym(rmfn) + '_' + toCsym(sym) + ' DER_OVLY_' + toCsym(self.unit) + '_' + toCsym(sym) + ';\n')
122 for rm in self.semamod.imports.module2symbols.keys():
123 rmfn = toCsym(rm.rsplit('.', 1) [0]).lower()
124 for sym in self.semamod.imports.module2symbols [rm]:
125 self.wout('#define DER_PACK_' + toCsym(self.unit) + '_' + toCsym(sym) + ' DER_PACK_' + toCsym(rmfn) + '_' + toCsym(sym) + '\n')
129 def generate_tail(self):
130 self.wout('\n\n/* asn2quickder output for ' + self.semamod.name + ' ends here */\n')
132 def generate_ovly(self):
133 self.wout('\n\n/* Overlay structures with ASN.1 derived nesting and labelling */\n\n')
134 for assigncompos in dependency_sort(self.semamod.assignments):
135 for assign in assigncompos:
136 self.generate_ovly_node(assign)
138 def generate_pack(self):
139 self.wout('\n\n/* Parser definitions in terms of ASN.1 derived bytecode instructions */\n\n')
140 for assigncompos in dependency_sort(self.semamod.assignments):
141 for assign in assigncompos:
143 if tnm in self.pack_funmap:
144 self.pack_funmap [tnm](assign)
146 print 'No pack generator for ' + str(tnm)
148 def generate_ovly_node(self, node):
150 if tnm in self.ovly_funmap:
151 self.ovly_funmap [tnm](node)
153 print('No overlay generator for ' + str(tnm))
154 raise Exception('RAISED WHERE?')
156 def generate_pack_node(self, node):
158 if tnm in self.pack_funmap:
159 self.pack_funmap [tnm](node)
161 print('No pack generator for ' + str(tnm))
163 def ignore_node(self, node):
166 def ovlyTypeAssignment(self, node):
167 self.wout('typedef ')
168 self.generate_ovly_node(node.type_decl)
169 self.wout(' DER_OVLY_' + self.unit + '_' + toCsym(node.type_name) + ';\n\n')
171 def packTypeAssignment(self, node):
172 self.wout('#define DER_PACK_' + self.unit + '_' + toCsym(node.type_name))
173 self.newcomma(', \\\n\t', ' \\\n\t')
174 self.generate_pack_node(node.type_decl)
177 def ovlyDefinedType(self, node):
178 mod = node.module_name or self.unit
179 self.wout('DER_OVLY_' + toCsym(mod) + '_' + toCsym(node.type_name))
181 def packDefinedType(self, node):
182 mod = node.module_name or self.unit
184 self.wout('DER_PACK_' + toCsym(mod) + '_' + toCsym(node.type_name))
186 def ovlySimpleType(self, node):
187 self.wout('dercursor')
189 def packSimpleType(self, node):
191 self.wout('DER_PACK_STORE | DER_TAG_' + node.type_name.replace(' ', '').upper())
193 def ovlyTaggedType(self, node):
195 # tag = tag [:tag.find(']')] + ']'
196 # self.wout('/* ' + tag + ' */ ')
197 # if node.implicity == TagImplicity.IMPLICIT:
198 # tag = tag + ' IMPLICIT'
199 # elif node.implicity == TagImplicity.IMPLICIT:
200 # tag = tag + ' EXPLICIT'
201 self.generate_ovly_node(node.type_decl)
203 def packTaggedType(self, node):
204 #TODO# Need to push down node.implicity == TagImplicity.IMPLICIT
205 #TODO# Need to process tag class
207 self.wout('DER_PACK_ENTER | DER_TAG_' +(node.class_name or 'CONTEXT') + '(' + node.class_number + ')')
208 self.generate_pack_node(node.type_decl)
210 self.wout('DER_PACK_LEAVE')
212 # Sequence, Set, Choice
213 def ovlyConstructedType(self, node):
214 self.wout('struct {\n');
215 for comp in node.components:
216 if isinstance(comp, ExtensionMarker):
217 self.wout('\t/* ...extensions... */\n')
219 if isinstance(comp, ComponentType) and comp.components_of_type is not None:
220 self.wout('\t/* TODO: COMPONENTS OF TYPE ' + str(comp.components_of_type) + ' */\n')
223 self.generate_ovly_node(comp.type_decl)
224 self.wout(' ' + toCsym(comp.identifier) + '; // ' + str(comp.type_decl) + '\n')
227 def packSequenceType(self, node):
229 self.wout('DER_PACK_ENTER | DER_TAG_SEQUENCE')
230 for comp in node.components:
231 if isinstance(comp, ExtensionMarker):
233 self.wout('/* ...ASN.1 extensions... */')
237 self.wout('DER_PACK_OPTIONAL')
238 if comp.type_decl is not None:
239 # TODO: None would be due to components_of_type
240 self.generate_pack_node(comp.type_decl)
242 self.wout('DER_PACK_LEAVE')
244 def packSetType(self, node):
246 self.wout('DER_PACK_ENTER | DER_TAG_SET')
247 for comp in node.components:
248 if isinstance(comp, ExtensionMarker):
250 self.wout('/* ...extensions... */')
254 self.wout('DER_PACK_OPTIONAL')
255 if comp.type_decl is not None:
256 # TODO: None would be due to components_of_type
257 self.generate_pack_node(comp.type_decl)
259 self.wout('DER_PACK_LEAVE')
261 def packChoiceType(self, node):
263 self.wout('DER_PACK_CHOICE_BEGIN')
264 for comp in node.components:
265 if isinstance(comp, ExtensionMarker):
267 self.wout('/* ...extensions... */')
269 if comp.type_decl is not None:
270 # TODO: None would be due to components_of_type
271 self.generate_pack_node(comp.type_decl)
273 self.wout('DER_PACK_CHOICE_END')
275 def packSequenceOfType(self, node):
277 self.wout('DER_PACK_STORE | DER_TAG_SEQUENCE')
279 def packSetOfType(self, node):
281 self.wout('DER_PACK_STORE | DER_TAG_SEQUENCE')
284 """The main program asn2quickder is called with one or more .asn1 files,
285 the first of which is mapped to a C header file and the rest is
286 loaded to fulfil dependencies.
289 if len(sys.argv) < 2:
290 sys.stderr.write('Usage: %s main[.asn1] dependency[.asn1]...\n'
295 for file in sys.argv [1:]:
296 print('Parsing', file)
297 with open(file, 'r') as asn1fh:
298 asn1tree = parser.parse_asn1(asn1fh.read())
299 asn1tree = parser.parse_asn1(asn1txt)
300 print('Building semantic model for', file)
301 asn1sem = build_semantic_model(asn1tree)
302 mods.insert(0, asn1sem [0])
303 print('Realised semantic model for', file)
305 cogen = QuickDERgen(mods [-1], os.path.basename(sys.argv [1]), mods [1:])
307 cogen.generate_head()
308 cogen.generate_ovly()
309 cogen.generate_pack()
310 cogen.generate_tail()