pycrate/pycrate_asn1c/refobj.py

305 lines
9.6 KiB
Python

# -*- coding: UTF-8 -*-
#/**
# * Software Name : pycrate
# * Version : 0.4
# *
# * Copyright 2016. Benoit Michau. ANSSI.
# *
# * This library is free software; you can redistribute it and/or
# * modify it under the terms of the GNU Lesser General Public
# * License as published by the Free Software Foundation; either
# * version 2.1 of the License, or (at your option) any later version.
# *
# * This library is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# * Lesser General Public License for more details.
# *
# * You should have received a copy of the GNU Lesser General Public
# * License along with this library; if not, write to the Free Software
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# * MA 02110-1301 USA
# *
# *--------------------------------------------------------
# * File Name : pycrate_asn1c/refobj.py
# * Created : 2016-07-06
# * Authors : Benoit Michau
# *--------------------------------------------------------
#*/
from .utils import integer_types, NoneType
from .err import ASN1Err
RefObj_docstring = """
Init args:
called (2-tuple or ASN1RefParam): name of the referenced ASN.1 module and
object if 2-tuple,
or ASN1RefParam referring the name of the formal parameter
ced_path (list of str or int): path of the specific content referenced
inside the called object, can be empty
"""
class ASN1Ref(object):
__doc__ = """
Generic parent class to handle cross-reference between user-defined ASN.1
objects
%s
""" % RefObj_docstring
KW = ('called', 'ced_path')
def __init__(self, called, ced_path=[]):
self.called = called
self.ced_path = ced_path
# methods for emulating a dictionnary
# this enables to reference path within ASN.1 objects in the form of
# list of str or int; e.g. path = ['const', 0, 'root', 0, 'ub']
def __getitem__(self, kw):
if kw in self.KW:
return getattr(self, kw)
else:
return object.__getitem__(self, kw)
def __setitem__(self, kw, arg):
if kw in self.KW:
return setattr(self, kw, arg)
else:
return object.__setitem__(self, kw, arg)
def __eq__(self, other):
# enable ASN1Ref equality test
if hasattr(self, 'name'):
# ASN1RefParam
return False
if type(other) != type(self):
return False
if other.called != self.called:
return False
if other.ced_path != self.ced_path:
return False
return True
def __hash__(self):
# enable the construction of set of unique references
if hasattr(self, 'name'):
return hash(self.name)
else:
return hash(self.called) + hash(tuple(self.ced_path))
def _safechk(self):
if not isinstance(self.called, (NoneType, tuple, ASN1RefParam)):
raise(ASN1Err('{0}: invalid called'.format(self.__class__.__name__)))
if not isinstance(self.ced_path, list) or \
not all([isinstance(e, (str, integer_types)) for e in self.ced_path]):
raise(ASN1Err('{0}: invalid ced_path'.format(self.__class__.__name__)))
def copy(self):
"""
returns an equal but independent copy of self
"""
if isinstance(self.called, tuple):
return self.__class__(tuple(self.called), self.ced_path[:])
elif isinstance(self.called, ASN1RefParam):
return self.__class__(self.called.copy(), self.ced_path[:])
else:
assert()
class ASN1RefType(ASN1Ref):
__doc__ = """
Class to handle a reference to a user-defined ASN.1 type object
e.g. MyNewType ::= MyType
%s
""" % RefObj_docstring
def __repr__(self):
# self.called is 2-tuple or ASN1RefParam
# self.ced_path is empty
if isinstance(self.called, tuple):
return 'ASN1RefType({0}.{1})'.format(self.called[0], self.called[1])
else:
return 'ASN1RefType({0!r})'.format(self.called)
class ASN1RefInstOf(ASN1Ref):
__doc__ = """
Class to handle a reference to a subclass of TYPE-IDENTIFIER
e.g. MyTypeIdent ::= TYPE-IDENTIFIER
MyInstOf ::= INSTANCE OF MyTypeIdent
%s
""" % RefObj_docstring
def __repr__(self):
# self.called is 2-tuple
# self.ced_path is empty
return 'ASN1RefInstOf({0}.{1})'.format(self.called[0], self.called[1])
class ASN1RefChoiceComp(ASN1Ref):
__doc__ = """
Class to handle a reference to a (chain of) component(s) within a
user-defined ASN.1 CHOICE object
e.g. MyNewType ::= alt32<alt3<MyChoice
%s
""" % RefObj_docstring
def __repr__(self):
# self.called is 2-tuple or ASN1RefParam
# self.ced_path is not empty
if isinstance(self.called, tuple):
return 'ASN1RefChoiceComp({0}<{1}.{2})'\
.format('<'.join(self.ced_path), self.called[0], self.called[1])
else:
return 'ASN1RefChoiceComp({0}<{1!r})'\
.format('<'.join(self.ced_path), self.called)
class ASN1RefClassField(ASN1Ref):
__doc__ = """
Class to handle a reference to a (chain of) field(s) within a user-defined
ASN.1 CLASS object
e.g. MyNewType ::= MYCLASS.&field3.&field32
%s
""" % RefObj_docstring
def __repr__(self):
# self.called is 2-tuple or ASN1RefParam
# self.ced_path is not empty
if isinstance(self.called, tuple):
return 'ASN1RefClassField({0}.{1}.&{2})'\
.format(self.called[0], self.called[1], '.&'.join(self.ced_path))
else:
return 'ASN1RefClassField({0!r}.&{1})'\
.format(self.called, '.&'.join(self.ced_path))
class ASN1RefClassIntern(ASN1Ref):
__doc__ = """
Class to handle an local reference within a user-defined ASN.1 CLASS,
from one field to another
e.g. MYCLASS ::= CLASS {
&MyType,
&myVal &MyType }
%s
""" % RefObj_docstring
def __repr__(self):
# self.called is None
# self.ced_path is not empty
return 'ASN1RefClassIntern(&{0})'.format('.&'.join(self.ced_path))
class ASN1RefClassValField(ASN1Ref):
__doc__ = """
Class to handle a reference to a field within a user-defined ASN.1 CLASS
value
e.g. MyType ::= myClassValue.&MyType
%s
""" % RefObj_docstring
def __repr__(self):
# self.called is 2-tuple or ASN1RefParam
# self.ced_path is not empty
if isinstance(self.called, tuple):
return 'ASN1RefClassValField({0}.{1}.&{2})'\
.format(self.called[0], self.called[1], '.&'.join(self.ced_path))
else:
return 'ASN1RefClassValField({0!r}.&{1})'\
.format(self.called, '.&'.join(self.ced_path))
class ASN1RefValue(ASN1Ref):
__doc__ = """
Class to handle a reference to a user-defined ASN.1 value object
e.g. myNewValue ::= myValue
or a bit more complex...
myNewValue {MYCLASS:myClass} ::= myClass.&myValue
%s
""" % RefObj_docstring
def __repr__(self):
# self.called is 2-tuple or ASN1RefParam
if isinstance(self.called, tuple):
if self.ced_path:
return 'ASN1RefValue({0}.{1}.&{2})'\
.format(self.called[0], self.called[1], '.&'.join(self.ced_path))
else:
return 'ASN1RefValue({0}.{1})'.format(self.called[0], self.called[1])
else:
if self.ced_path:
return 'ASN1RefValue({0!r}.&{1})'\
.format(self.called, '.&'.join(self.ced_path))
else:
return 'ASN1RefValue({0!r})'.format(self.called)
class ASN1RefSet(ASN1Ref):
__doc__ = """
Class to handle a reference to a user-defined ASN.1 set object
e.g. MyNewSet ::= {MySet}
or a bit more complex...
MyNewSet {MYCLASS:myClass} ::= myClass.&MySet
%s
""" % RefObj_docstring
def __repr__(self):
# self.called is 2-tuple or ASN1RefParam
if isinstance(self.called, tuple):
if self.ced_path:
return 'ASN1RefSet({0}.{1}.&{2})'\
.format(self.called[0], self.called[1], '.&'.join(self.ced_path))
else:
return 'ASN1RefSet({0}.{1})'.format(self.called[0], self.called[1])
else:
if self.ced_path:
return 'ASN1RefSet({0!r}.&{1})'\
.format(self.called, '.&'.join(self.ced_path))
else:
return 'ASN1RefSet({0!r})'.format(self.called)
class ASN1RefParam(ASN1Ref):
"""
Class to handle a reference to a formal parameter within a user-defined
ASN.1 object
e.g. MyInt {INTEGER:low} ::= INTEGER (low..1000)
Init args:
name (str): name of the formal parameter referrenced
"""
KW = ('name', )
def __init__(self, name):
self.name = name
def _safechk(self):
if not isinstance(self.name, str):
raise(ASN1Err('{0}: invalid name'\
.format(self.__class__.__name__)))
def copy(self):
"""
returns an equal but independent copy of self
"""
return self.__class__(self.name)
def __repr__(self):
return 'ASN1RefParam({0})'.format(self.name)
def __str__(self):
# this helps when we need to check the 1st letter case of an object name
return self.name