2018 lines
74 KiB
Python
2018 lines
74 KiB
Python
# -*- coding: UTF-8 -*-
|
|
#/**
|
|
# * Software Name : pycrate
|
|
# * Version : 0.4
|
|
# *
|
|
# * Copyright 2017. Benoit Michau. ANSSI.
|
|
# * Copyright 2018. Benoit Michau. P1Sec.
|
|
# *
|
|
# * 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_asn1rt/asnobj.py
|
|
# * Created : 2017-01-31
|
|
# * Authors : Benoit Michau
|
|
# *--------------------------------------------------------
|
|
#*/
|
|
|
|
from .utils import *
|
|
from .err import *
|
|
from .refobj import *
|
|
from .dictobj import *
|
|
from .setobj import *
|
|
from .codecs import *
|
|
from .codecs import _with_json
|
|
|
|
|
|
ASN1Obj_docstring = """
|
|
Common object attributes:
|
|
|
|
- name: str, the identifier of the object
|
|
|
|
- mode: str (MODE_*), the mode of the object after compilation.
|
|
- MODE_TYPE : ASN.1 subtype or subclass
|
|
- MODE_VALUE: ASN.1 value
|
|
- MODE_SET : ASN.1 set of values
|
|
|
|
- param: bool, True in case the object is a parameterized one.
|
|
Parameterized objects are "empty" and mainly useless for encoding / decoding.
|
|
They are still required to keep track of object references and tag chain.
|
|
|
|
- tag: None or tuple, contains the tagging of the ASN.1 type.
|
|
If defined, it has the following format:
|
|
(tag value (int),
|
|
tag class (str in TAG_CONTEXT_SPEC / TAG_PRIVATE / TAG_APPLICATION / TAG_UNIVERSAL),
|
|
tag mode (str in TAG_IMPLICIT / TAG_EXPLICIT))
|
|
|
|
- typeref: None or ASN1Ref, provides the reference to the subtype of the object
|
|
in case it derives from another user-defined one (and not a native one).
|
|
|
|
- tr: None or ASN1Obj, provides the actual subtype object
|
|
|
|
- cont: type-dependent.
|
|
|
|
- root: None or list of str, provides the identifiers of the root content if
|
|
defined.
|
|
|
|
- ext: None or list of str, provides the identifiers of the extended content if
|
|
defined.
|
|
|
|
- val: None for MODE_TYPE,
|
|
single_value (type-dependent) for MODE_VALUE,
|
|
ASN1Set for MODE_SET.
|
|
|
|
Attributes for object generic constraints:
|
|
|
|
- const_val: None or ASN1Set, provides the set of values that constraints the type
|
|
|
|
- const_tab: None or ASN1Obj (MODE_SET), provides the CLASS set for table lookup
|
|
|
|
- const_tab_id: None or str, only set if const_tab is set,
|
|
provides the identifier that corresponds to self in const_tab
|
|
|
|
- const_tab_at: None or tuple of str, only set if const_tab is set,
|
|
provides the path of identifiers to which self must refer for the table lookup in const_tab
|
|
|
|
Attributes defined for components of a constructed or CLASS type:
|
|
|
|
- parent: None or ASN1Obj, indicates the parent ASN.1 object container
|
|
|
|
- opt: bool, indicates if the component is OPTIONAL
|
|
|
|
- def: None or single value, indicates the DEFAULT value
|
|
|
|
- uniq: bool, indicates if the component is UNIQUE
|
|
|
|
- group: None or int, indicates the extension group index for grouped extended components
|
|
|
|
Special attributes that exists only for some objects:
|
|
|
|
- root_mand (for SEQUENCE, SET, CLASS)
|
|
- root_opt (for SEQUENCE, SET, CLASS)
|
|
- cont_rev (for ENUM)
|
|
- cont_tags (for CHOICE, SEQUENCE, SET)
|
|
- ext_ident (for CHOICE, SEQUENCE, SET)
|
|
- ext_group (for CHOICE, SEQUENCE, SET)
|
|
- ext_group_obj (for SEQUENCE, SET)
|
|
- ext_nest (for SEQUENCE, SET)
|
|
- defby (for ANY)
|
|
|
|
Attributes for object specific constraints:
|
|
|
|
- const_sz (for BIT STRING, OCTET STRING, *String, SEQUENCE OF, SET OF)
|
|
- const_ind (for ENUM and CHOICE, constraint on the max index)
|
|
- const_cont (for BIT STRING, OCTET STRING)
|
|
- const_cont_enc (for BIT STRING, OCTET STRING)
|
|
- const_alpha (for *String)
|
|
|
|
Attributes used at run-time:
|
|
|
|
- struct: None or pycrate.elt.Element instance, provides the complete
|
|
structure of the transfer syntax with, generated when encoding and
|
|
decoding the object when using from_*_ws() and to_*_ws() methods.
|
|
"""
|
|
|
|
class ASN1Obj(Element):
|
|
|
|
# in order to disable any asnlog() during the runtime
|
|
_SILENT = False
|
|
|
|
# this enables object verification during Python module initialization
|
|
_SAFE_INIT = True
|
|
# this enables object value verification when using set_val()
|
|
_SAFE_VAL = True
|
|
# this enables object's constraints verification when using set_val()
|
|
_SAFE_BND = True
|
|
# this enables object's table constraint verification when using set_val()
|
|
_SAFE_BNDTAB = True
|
|
|
|
#--------------------------------------------------------------------------#
|
|
# class attributes, initialization and safe checking methods
|
|
#--------------------------------------------------------------------------#
|
|
|
|
_name = ''
|
|
_mode = MODE_TYPE
|
|
_tag = None
|
|
_typeref = None
|
|
_tr = None
|
|
_param = False
|
|
_parent = None
|
|
_opt = False
|
|
_def = None
|
|
_uniq = False
|
|
_group = None
|
|
_cont = None
|
|
_root = None
|
|
_ext = None
|
|
_val = None
|
|
|
|
_const_val = None
|
|
# _const_sz is only defined for types which can have a SIZE constraint
|
|
#_const_sz = None
|
|
# _const_alpha is only defined for _String subtypes
|
|
#_const_alpha = None
|
|
# _const_cont is only defined for BIT STRING and OCTET STRING
|
|
#_const_cont = None
|
|
#_const_cont_enc = None
|
|
_const_tab = None
|
|
# _const_tab_id and _const_tab_at are only defined if _const_tab is not None
|
|
#_const_tab_id = None
|
|
#_const_tab_at = None
|
|
|
|
|
|
TYPE = None
|
|
TAG = None
|
|
|
|
def __init__(self, **kwargs):
|
|
if 'name' in kwargs: self._name = kwargs['name']
|
|
if 'mode' in kwargs: self._mode = kwargs['mode']
|
|
if 'tag' in kwargs: self._tag = kwargs['tag']
|
|
if 'typeref' in kwargs: self._typeref = kwargs['typeref']
|
|
if 'param' in kwargs: self._param = kwargs['param']
|
|
if 'opt' in kwargs: self._opt = kwargs['opt']
|
|
elif 'default' in kwargs: self._def = kwargs['default']
|
|
if 'defby' in kwargs: self._defby = kwargs['defby']
|
|
if 'uniq' in kwargs: self._uniq = kwargs['uniq']
|
|
if 'group' in kwargs: self._group = kwargs['group']
|
|
|
|
def _safechk_obj(self):
|
|
"""
|
|
ensures all internal attributes at initialization have the correct format
|
|
"""
|
|
# name
|
|
if not isinstance(self._name, str_types):
|
|
raise(ASN1ObjErr('invalid name, {0!r}'.format(self._name)))
|
|
# mode
|
|
if self._mode not in (MODE_TYPE, MODE_SET, MODE_VALUE):
|
|
raise(ASN1ObjErr('{0}: invalid mode, {1!r}'\
|
|
.format(self.fullname(), self._mode)))
|
|
# tag
|
|
if not isinstance(self._tag, (NoneType, tuple)):
|
|
raise(ASN1ObjErr('{0}: invalid tag, {1!r}'\
|
|
.format(self.fullname(), self._tag)))
|
|
elif isinstance(self._tag, tuple) and ( \
|
|
len(self._tag) != 3 or \
|
|
not isinstance(self._tag[0], integer_types) or \
|
|
self._tag[1] not in (TAG_CONTEXT_SPEC, TAG_PRIVATE, TAG_APPLICATION, TAG_UNIVERSAL) or \
|
|
self._tag[2] not in (TAG_IMPLICIT, TAG_EXPLICIT)):
|
|
raise(ASN1ObjErr('{0}: invalid tag, {1!r}'\
|
|
.format(self.fullname(), self._tag)))
|
|
# typeref
|
|
if not isinstance(self._typeref, (NoneType, ASN1Ref)):
|
|
raise(ASN1ObjErr('{0}: invalid typeref, {1!r}'\
|
|
.format(self.fullname(), self._typeref)))
|
|
# param
|
|
if not isinstance(self._param, bool):
|
|
raise(ASN1ObjErr('{0}: invalid param, {1!r}'\
|
|
.format(self.fullname(), self._param)))
|
|
# parent
|
|
if not isinstance(self._parent, (NoneType, ASN1Obj)):
|
|
raise(ASN1ObjErr('{0}: invalid parent, {1!r}'\
|
|
.format(self.fullname(), self._parent)))
|
|
# opt
|
|
if not isinstance(self._opt, bool):
|
|
raise(ASN1ObjErr('{0}: invalid opt, {1!r}'\
|
|
.format(self.fullname(), self._opt)))
|
|
# default
|
|
if not isinstance(self._def, NoneType):
|
|
if self._mode == MODE_SET:
|
|
self._safechk_set(self._def)
|
|
else:
|
|
self._safechk_val(self._def)
|
|
# uniq
|
|
if not isinstance(self._uniq, bool):
|
|
raise(ASN1ObjErr('{0}: invalid uniq, {1!r}'\
|
|
.format(self.fullname(), self._uniq)))
|
|
# group
|
|
if not isinstance(self._group, (NoneType, integer_types)):
|
|
raise(ASN1ObjErr('{0}: invalid group, {1!r}'\
|
|
.format(self.fullname(), self._group)))
|
|
|
|
def _safechk_val(self, val):
|
|
"""
|
|
ensures the value val has the correct format according to self
|
|
"""
|
|
# check val format, implemented for each specific object
|
|
pass
|
|
|
|
def _safechk_val_int(self, val):
|
|
if not isinstance(val, integer_types):
|
|
raise(ASN1ObjErr('{0}: invalid INTEGER value, {1!r}'\
|
|
.format(self.fullname(), val)))
|
|
|
|
def _safechk_val_real(self, val):
|
|
if not isinstance(val, tuple) or len(val) != 3 or \
|
|
not isinstance(val[0], integer_types) or \
|
|
val[1] not in (2, 10) or \
|
|
not isinstance(val[2], integer_types):
|
|
raise(ASN1ObjErr('{0}: invalid REAL value, {1!r}'\
|
|
.format(self.fullname(), val)))
|
|
|
|
def _safechk_val_str(self, val):
|
|
if not isinstance(val, str_types):
|
|
raise(ASN1ObjErr('{0}: invalid _String value, {1!r}'\
|
|
.format(self.fullname(), val)))
|
|
|
|
def _safechk_set(self, s):
|
|
"""
|
|
ensures the set of values s has the correct format according to self
|
|
"""
|
|
if not isinstance(s, ASN1Set):
|
|
raise(ASN1ObjErr('{0}: invalid set, {1!r}'.format(self.fullname(), s)))
|
|
for v in s._rv:
|
|
self._safechk_val(v)
|
|
if s._ev is not None:
|
|
for v in s._ev:
|
|
self._safechk_val(v)
|
|
|
|
def _safechk_set_int(self, si):
|
|
if not isinstance(s, ASN1Set):
|
|
raise(ASN1ObjErr('{0}: invalid set, {1!r}'.format(self.fullname(), s)))
|
|
for v in s._rv:
|
|
self._safechk_val(v)
|
|
for vr in s._rr:
|
|
if not isinstance(vr, ASN1RangeInt):
|
|
raise(ASN1ObjErr('{0}: invalid INTEGER range, {1!r}'\
|
|
.format(self.fullname(), vr)))
|
|
if s._ev is not None:
|
|
for v in s._ev:
|
|
self._safechk_val(v)
|
|
for vr in s._er:
|
|
if not isinstance(vr, ASN1RangeInt):
|
|
raise(ASN1ObjErr('{0}: invalid INTEGER range, {1!r}'\
|
|
.format(self.fullname(), vr)))
|
|
|
|
def _safechk_set_real(self, sr):
|
|
if not isinstance(s, ASN1Set):
|
|
raise(ASN1ObjErr('{0}: invalid set, {1!r}'.format(self.fullname(), s)))
|
|
for v in s._rv:
|
|
self._safechk_val(v)
|
|
for vr in s._rr:
|
|
if not isinstance(vr, ASN1RangeReal):
|
|
raise(ASN1ObjErr('{0}: invalid REAL range, {1!r}'\
|
|
.format(self.fullname(), vr)))
|
|
if s._ev is not None:
|
|
for v in s._evt:
|
|
self._safechk_val(v)
|
|
for vr in s._er:
|
|
if not isinstance(vr, ASN1RangeReal):
|
|
raise(ASN1ObjErr('{0}: invalid REAL range, {1!r}'\
|
|
.format(self.fullname(), vr)))
|
|
|
|
def _safechk_set_str(self, ss):
|
|
if not isinstance(s, ASN1Set):
|
|
raise(ASN1ObjErr('{0}: invalid set, {1!r}'.format(self.fullname(), s)))
|
|
for v in s._rv:
|
|
self._safechk_val(v)
|
|
for vr in s._rr:
|
|
if not isinstance(vr, ASN1RangeStr):
|
|
raise(ASN1ObjErr('{0}: invalid _String range, {1!r}'\
|
|
.format(self.fullname(), vr)))
|
|
if s._ev is not None:
|
|
for v in s._ev:
|
|
self._safechk_val(v)
|
|
for vr in s._er:
|
|
if not isinstance(vr, ASN1RangeStr):
|
|
raise(ASN1ObjErr('{0}: invalid _String range, {1!r}'\
|
|
.format(self.fullname(), vr)))
|
|
|
|
def _safechk_bnd(self, val):
|
|
"""
|
|
ensures the value val is within potential constraints defined for self
|
|
"""
|
|
# check val against potential constraints
|
|
if self._const_val and \
|
|
self._const_val.ext is None and \
|
|
val not in self._const_val:
|
|
raise(ASN1ObjErr('{0}: {1} value out of constraint, {2!r}'\
|
|
.format(self.fullname(), self.TYPE, val)))
|
|
if self._SAFE_BNDTAB and self._const_tab and self._const_tab_at:
|
|
# check val against a constraint defined within the table constraint
|
|
const_val_type, const_val = self._get_tab_obj()
|
|
if const_val_type == CLASET_NONE:
|
|
if not self._SILENT:
|
|
asnlog('%s._safechk_bnd: %s, unable to retrieve a defined object'\
|
|
% (self.__class__.__name__, self._name))
|
|
elif self._mode == MODE_VALUE and const_val_type == CLASET_UNIQ:
|
|
if val != const_val:
|
|
raise(ASN1ObjErr('{0}: value out of table constraint, {1!r}'\
|
|
.format(self.fullname(), val)))
|
|
elif self._mode == MODE_SET or const_val_type == CLASET_MULT:
|
|
if val not in const_val:
|
|
raise(ASN1ObjErr('{0}: value out of table constraint, {1!r}'\
|
|
.format(self.fullname(), val)))
|
|
|
|
def _get_tab_obj(self):
|
|
ret = (CLASET_NONE, None)
|
|
try:
|
|
IndIdent = self._get_obj_by_path(self._const_tab_at)._const_tab_id
|
|
IndVal = self._get_val_by_path(self._const_tab_at)
|
|
except Exception:
|
|
return ret
|
|
cla_val_type, cla_val = self._const_tab.get(IndIdent, IndVal)
|
|
if cla_val_type == CLASET_UNIQ and self._const_tab_id in cla_val:
|
|
return (CLASET_UNIQ, cla_val[self._const_tab_id])
|
|
elif cla_val_type == CLASET_MULT:
|
|
# filter cla_val for the given tab_id
|
|
cla_val = [val[self._const_tab_id] for val in cla_val if self._const_tab_id in val]
|
|
if len(cla_val) > 1:
|
|
return (CLASET_MULT, cla_val)
|
|
elif cla_val:
|
|
return (CLASET_UNIQ, cla_val[0])
|
|
return ret
|
|
|
|
def _get_tab_obj_uniq(self):
|
|
try:
|
|
IndIdent = self._get_obj_by_path(self._const_tab_at)._const_tab_id
|
|
IndVal = self._get_val_by_path(self._const_tab_at)
|
|
except Exception:
|
|
raise(ASN1ObjErr('{0}: invalid table constraint @ path, {1!r}'\
|
|
.format(self.fullname(), self._const_tab_at)))
|
|
claval = self._const_tab.get_uniq(IndIdent, IndVal)
|
|
if claval is None:
|
|
raise(ASN1ObjErr('{0}: non-existent value {1} for identifier {2} in the table constraint'\
|
|
.format(self.fullname(), IndVal, IndIdent)))
|
|
else:
|
|
try:
|
|
return claval[self._const_tab_id]
|
|
except KeyError:
|
|
raise(ASN1ObjErr('{0}: non-existent ident {1} within table constraint value'\
|
|
.format(self.fullname(), self._const_tab_id)))
|
|
|
|
def _get_tab_obj_nonuniq(self):
|
|
ret = []
|
|
try:
|
|
IndIdent = self._get_obj_by_path(self._const_tab_at)._const_tab_id
|
|
IndVal = self._get_val_by_path(self._const_tab_at)
|
|
except Exception:
|
|
return []
|
|
clavals = self._const_tab.get_mult(IndIdent, IndVal)
|
|
if not clavals:
|
|
return []
|
|
else:
|
|
ret = []
|
|
for claval in clavals:
|
|
try:
|
|
ret.append( claval[self._const_tab_id] )
|
|
except KeyError:
|
|
pass
|
|
return ret
|
|
|
|
#--------------------------------------------------------------------------#
|
|
# user-friendly generic representation
|
|
#--------------------------------------------------------------------------#
|
|
|
|
def fullname(self):
|
|
name = [self._name]
|
|
obj = self
|
|
while obj._parent is not None:
|
|
obj = obj._parent
|
|
name.append(obj._name)
|
|
return '.'.join(reversed(name))
|
|
|
|
def __repr__(self):
|
|
if self._typeref is not None:
|
|
if isinstance(self._typeref, (ASN1RefType, ASN1RefInstOf)):
|
|
try:
|
|
typeref = self._typeref.called[1]
|
|
except AttributeError:
|
|
# .called is ASN1RefParam
|
|
typeref = repr(self._typeref.called)
|
|
elif isinstance(self._typeref, ASN1RefChoiceComp):
|
|
try:
|
|
typeref = '%s<' % self._typeref.called[1] + \
|
|
'<'.join(self._typeref.ced_path)
|
|
except AttributeError:
|
|
# .called is ASN1RefParam
|
|
typeref = '%s<' % repr(self._typeref.called) + \
|
|
'<'.join(self._typeref.ced_path)
|
|
elif isinstance(self._typeref, ASN1RefClassField):
|
|
try:
|
|
typeref = '%s.&' % self._typeref.called[1] + \
|
|
'.&'.join(self._typeref.ced_path)
|
|
except AttributeError:
|
|
# .called is ASN1RefParam
|
|
typeref = '%s.&' % repr(self._typeref.called) + \
|
|
'.&'.join(self._typeref.ced_path)
|
|
elif isinstance(self._typeref, ASN1RefClassIntern):
|
|
typeref = '&%s' % '.&'.join(self._typeref.ced_path)
|
|
elif isinstance(self._typeref, ASN1RefClassValField):
|
|
try:
|
|
typeref = '%s.&%s' % (self._typeref.called[1],
|
|
'.&'.join(self._typeref.ced_path))
|
|
except AttributeError:
|
|
# .called is ASN1RefParam
|
|
typeref = '%s.&%s' % (repr(self._typeref.called),
|
|
'.&'.join(self._typeref.ced_path))
|
|
else:
|
|
assert()
|
|
else:
|
|
typeref = None
|
|
#
|
|
if self._mode == MODE_TYPE:
|
|
if typeref is not None:
|
|
return '<%s ([%s] %s)>' % (self._name,
|
|
typeref,
|
|
self.TYPE)
|
|
else:
|
|
return '<%s (%s)>' % (self._name,
|
|
self.TYPE)
|
|
elif self._mode == MODE_VALUE:
|
|
if self._val is not None:
|
|
val = self._val
|
|
else:
|
|
val = ' '
|
|
if typeref is not None:
|
|
return '<%s ([%s] %s): %s>' % (self._name,
|
|
typeref,
|
|
self.TYPE,
|
|
val)
|
|
else:
|
|
return '<%s (%s): %s>' % (self._name,
|
|
self.TYPE,
|
|
val)
|
|
elif self._mode == MODE_SET:
|
|
if self._val is not None:
|
|
val = repr(self._val)
|
|
else:
|
|
val = ' '
|
|
if typeref is not None:
|
|
return '<%s ([%s] %s): %s>' % (self._name,
|
|
typeref,
|
|
self.TYPE,
|
|
val)
|
|
else:
|
|
return '<%s (%s): %s>' % (self._name,
|
|
self.TYPE,
|
|
val)
|
|
|
|
# TODO: make repr() and show() similar to those from pycrate_core.elt.Envelope
|
|
|
|
#--------------------------------------------------------------------------#
|
|
# internal attributes access method
|
|
#--------------------------------------------------------------------------#
|
|
|
|
def get_internals(self):
|
|
"""
|
|
returns all internal attributes within a dict
|
|
"""
|
|
internals = {
|
|
'name' : self._name,
|
|
'mode' : self._mode,
|
|
'tag' : self._tag,
|
|
'typeref': self._typeref,
|
|
'tr' : self._tr,
|
|
'param' : self._param,
|
|
'parent' : self._parent,
|
|
'opt' : self._opt,
|
|
'def' : self._def,
|
|
'uniq' : self._uniq,
|
|
'group' : self._group,
|
|
'cont' : self._cont,
|
|
'root' : self._root,
|
|
'ext' : self._ext,
|
|
'val' : self._val,
|
|
'const_val': self._const_val,
|
|
'const_tab': self._const_tab
|
|
}
|
|
if hasattr(self, 'cont_tags'):
|
|
internals['cont_tags'] = self._cont_tags
|
|
if hasattr(self, '_root_mand'):
|
|
internals['root_mand'] = self._root_mand
|
|
if hasattr(self, '_root_ext'):
|
|
internals['root_ext'] = self._root_ext
|
|
if hasattr(self, '_ext_group'):
|
|
internals['ext_group'] = self._ext_group
|
|
if hasattr(self, '_ext_group_obj'):
|
|
internals['ext_group_obj'] = self._ext_group_obj
|
|
if hasattr(self, '_ext_ident'):
|
|
internals['ext_ident'] = self._ext_ident
|
|
if hasattr(self, '_const_sz'):
|
|
internals['const_sz'] = self._const_sz
|
|
if hasattr(self, '_const_alpha'):
|
|
internals['const_alpha'] = self._const_alpha
|
|
if hasattr(self, '_const_cont'):
|
|
internals['const_cont'] = self._const_cont
|
|
if hasattr(self, '_const_cont_enc'):
|
|
internals['const_cont_enc'] = self._const_cont_enc
|
|
if hasattr(self, '_const_tab_id'):
|
|
internals['const_tab_id'] = self._const_tab_id
|
|
if hasattr(self, '_const_tab_at'):
|
|
internals['const_tab_at'] = self._const_tab_at
|
|
return internals
|
|
|
|
def get_typeref(self):
|
|
"""
|
|
returns the ASN.1 object which is the parent type of self
|
|
or None if self is defined as a direct subtype of an ASN.1 built-in type
|
|
"""
|
|
if self._typeref is None:
|
|
return None
|
|
else:
|
|
return self._typeref.get()
|
|
|
|
def get_typeref_list(self):
|
|
"""
|
|
returns the list of ASN.1 objects which are parent type of self
|
|
|
|
see .get_typeref()
|
|
"""
|
|
tl, obj = [], self
|
|
while obj._typeref is not None:
|
|
par = obj._typeref.get()
|
|
if par is None:
|
|
# something went wrong
|
|
break
|
|
else:
|
|
tl.append(par)
|
|
obj = par
|
|
return tl
|
|
|
|
def get_type_list(self):
|
|
"""
|
|
returns the list of object names which are parent of self
|
|
"""
|
|
tl, obj = [], self
|
|
while obj._typeref is not None:
|
|
par = obj._typeref.get()
|
|
if par is None:
|
|
# something went wrong
|
|
break
|
|
else:
|
|
tl.append(par._name)
|
|
obj = par
|
|
tl.append( self.TYPE )
|
|
return tl
|
|
|
|
def get_const(self):
|
|
"""
|
|
returns the dict of constraints that apply to the object
|
|
"""
|
|
const = {}
|
|
if self._const_val is not None:
|
|
const['val'] = self._const_val
|
|
if self._const_tab is not None:
|
|
const['tab'] = self._const_tab
|
|
if hasattr(self, '_const_sz') and self._const_sz is not None:
|
|
const['sz'] = self._const_sz
|
|
if hasattr(self, '_const_alpha') and self._const_alpha is not None:
|
|
const['alpha'] = self._const_alpha
|
|
if hasattr(self, '_const_cont') and self._const_cont is not None:
|
|
const['cont'] = self._const_cont
|
|
if hasattr(self, '_const_cont_enc') and self._const_cont_enc is not None:
|
|
const['cont_enc'] = self._const_cont_enc
|
|
if hasattr(self, '_const_tab_id') and self._const_tab_id is not None:
|
|
const['tab_id'] = self._const_tab_id
|
|
if hasattr(self, '_const_tab_at') and self._const_tab_at is not None:
|
|
const['tab_at'] = self._const_tab_at
|
|
return const
|
|
|
|
def get_proto(self, w_open=True, w_opt=False, w_enum=False,
|
|
print_recurs=False,
|
|
blacklist=set()):
|
|
"""
|
|
returns the prototype of the object
|
|
|
|
Args:
|
|
w_open : bool,
|
|
if True, inspect the content of OPEN objects
|
|
w_opt : bool
|
|
if True, add (OPT) to every optional component name
|
|
w_enum : bool
|
|
if True, lists the content of the ENUMERATED
|
|
print_recurs: bool,
|
|
if True, prints paths that lead to recursion
|
|
blacklist : set of str,
|
|
list of blacklisted constructed object names, that won't
|
|
be expanded
|
|
|
|
Returns:
|
|
type: str if self is of basic type,
|
|
2-tuple (type_str, content_dict) if self is of constructed type
|
|
"""
|
|
if not hasattr(self, '_proto_recur'):
|
|
root = True
|
|
self._proto_recur = [id(self)]
|
|
self._proto_path = []
|
|
else:
|
|
root = False
|
|
#
|
|
if self.TYPE in (TYPE_OPEN, TYPE_ANY):
|
|
if w_open and self._name not in blacklist:
|
|
cont = ASN1Dict()
|
|
for (ident, Comp) in self._get_const_tr().items():
|
|
if isinstance(ident, str_types):
|
|
continue
|
|
if id(Comp) in self._proto_recur:
|
|
if print_recurs:
|
|
asnlog('[+] recursion detected: %s, at path %r'\
|
|
% (Comp._name, self._proto_path + [ident]))
|
|
cont[ident] = Comp.TYPE
|
|
else:
|
|
Comp._proto_recur = self._proto_recur + [id(Comp)]
|
|
Comp._proto_path = self._proto_path + [ident]
|
|
cont[ident] = Comp.get_proto(
|
|
w_open,
|
|
w_opt,
|
|
w_enum,
|
|
print_recurs,
|
|
blacklist)
|
|
del Comp._proto_recur, Comp._proto_path
|
|
ret = (self.TYPE, cont)
|
|
else:
|
|
ret = self.TYPE
|
|
#
|
|
elif self.TYPE in (TYPE_CHOICE, TYPE_SEQ, TYPE_SET, TYPE_CLASS):
|
|
if self._name not in blacklist:
|
|
cont = ASN1Dict()
|
|
for (ident, Comp) in self._cont.items():
|
|
if w_opt and hasattr(self, '_root_mand') and ident not in self._root_mand:
|
|
ident_ret = '%s (OPT)' % ident
|
|
else:
|
|
ident_ret = ident
|
|
if id(Comp) in self._proto_recur:
|
|
if print_recurs:
|
|
asnlog('[+] recursion detected: %s, at path %r'\
|
|
% (Comp._name, self._proto_path + [ident]))
|
|
cont[ident_ret] = Comp.TYPE
|
|
else:
|
|
Comp._proto_recur = self._proto_recur + [id(Comp)]
|
|
Comp._proto_path = self._proto_path + [ident]
|
|
cont[ident_ret] = Comp.get_proto(
|
|
w_open,
|
|
w_opt,
|
|
w_enum,
|
|
print_recurs,
|
|
blacklist)
|
|
del Comp._proto_recur, Comp._proto_path
|
|
ret = (self.TYPE, cont)
|
|
else:
|
|
ret = self.TYPE
|
|
#
|
|
elif self.TYPE in (TYPE_SEQ_OF, TYPE_SET_OF):
|
|
Comp = self._cont
|
|
if id(Comp) in self._proto_recur:
|
|
if print_recurs:
|
|
asnlog('[+] recursion detected: %s, at path %r'\
|
|
% (Comp._name, self._proto_path + [None]))
|
|
ret = self.TYPE
|
|
else:
|
|
Comp._proto_recur = self._proto_recur + [id(Comp)]
|
|
Comp._proto_path = self._proto_path + [None]
|
|
ret = (
|
|
self.TYPE,
|
|
self._cont.get_proto(
|
|
w_open,
|
|
w_opt,
|
|
w_enum,
|
|
print_recurs,
|
|
blacklist)
|
|
)
|
|
del Comp._proto_recur, Comp._proto_path
|
|
#
|
|
elif self.TYPE in (TYPE_BIT_STR, TYPE_OCT_STR) and self._const_cont:
|
|
Comp = self._const_cont
|
|
if id(Comp) in self._proto_recur:
|
|
if print_recurs:
|
|
asnlog('[+] recursion detected: %s, at path %r'\
|
|
% (Comp._name, self._proto_path + [None]))
|
|
ret = self.TYPE
|
|
else:
|
|
Comp._proto_recur = self._proto_recur + [id(Comp)]
|
|
Comp._proto_path = self._proto_path + [None]
|
|
ret = (
|
|
self.TYPE,
|
|
self._const_cont.get_proto(
|
|
w_open,
|
|
w_opt,
|
|
w_enum,
|
|
print_recurs,
|
|
blacklist)
|
|
)
|
|
del Comp._proto_recur, Comp._proto_path
|
|
#
|
|
elif self.TYPE == TYPE_ENUM and w_enum:
|
|
enum = self._root[:]
|
|
if self._ext is not None:
|
|
enum.append('...')
|
|
enum.extend(self._ext)
|
|
ret = (self.TYPE, enum)
|
|
#
|
|
else:
|
|
assert( self.TYPE in TYPES_BASIC + TYPES_EXT )
|
|
ret = self.TYPE
|
|
#
|
|
if root:
|
|
del self._proto_recur, self._proto_path
|
|
return ret
|
|
|
|
def get_complexity(self, w_open=True, w_opt=True, print_recurs=False, blacklist=set()):
|
|
"""
|
|
returns the number of basic types objects referenced from self,
|
|
the maximum depth possible within self,
|
|
and the list of paths that lead to recursion
|
|
|
|
Args:
|
|
w_open : bool,
|
|
if True, inspects the potential content of OPEN objects
|
|
w_opt : bool
|
|
if True, inspect optional components to count into the
|
|
complexity
|
|
print_recurs: bool,
|
|
if True, prints paths that lead to recursion
|
|
blacklist : set of str,
|
|
list of blacklisted constructed object names, that won't
|
|
account into the complexity
|
|
|
|
Returns:
|
|
num, depth: uint, uint
|
|
"""
|
|
num, depth, recur = 0, 0, []
|
|
#
|
|
if not hasattr(self, '_proto_recur'):
|
|
root = True
|
|
self._proto_recur = [id(self)]
|
|
self._proto_path = []
|
|
else:
|
|
root = False
|
|
#
|
|
if self.TYPE == TYPE_OPEN:
|
|
if w_open and self._name not in blacklist:
|
|
loc_depth = []
|
|
for (ident, Comp) in self._get_const_tr().items():
|
|
if isinstance(ident, str_types):
|
|
continue
|
|
if id(Comp) in self._proto_recur:
|
|
recur_path = self._proto_path + [ident]
|
|
if print_recurs:
|
|
asnlog('[+] recursion detected: %s, at path %r'\
|
|
% (Comp._name, recur_path))
|
|
recur.append( recur_path )
|
|
else:
|
|
Comp._proto_recur = self._proto_recur + [id(Comp)]
|
|
Comp._proto_path = self._proto_path + [ident]
|
|
comp_num, comp_depth, comp_recur = Comp.get_complexity(
|
|
w_open,
|
|
w_opt,
|
|
print_recurs,
|
|
blacklist)
|
|
del Comp._proto_recur, Comp._proto_path
|
|
num += comp_num
|
|
loc_depth.append( comp_depth )
|
|
recur.extend( comp_recur )
|
|
if loc_depth:
|
|
depth += 1 + max(loc_depth)
|
|
else:
|
|
num += 1
|
|
#
|
|
elif self.TYPE in (TYPE_CHOICE, TYPE_SEQ, TYPE_SET, TYPE_CLASS):
|
|
if self._name not in blacklist:
|
|
loc_depth = []
|
|
for (ident, Comp) in self._cont.items():
|
|
if id(Comp) in self._proto_recur:
|
|
recur_path = self._proto_path + [ident]
|
|
if print_recurs:
|
|
asnlog('[+] recursion detected: %s, at path %r'\
|
|
% (Comp._name, recur_path))
|
|
recur.append( recur_path )
|
|
elif w_opt or not hasattr(self, '_root_mand') \
|
|
or Comp._name in self._root_mand:
|
|
Comp._proto_recur = self._proto_recur + [id(Comp)]
|
|
Comp._proto_path = self._proto_path + [ident]
|
|
comp_num, comp_depth, comp_recur = Comp.get_complexity(
|
|
w_open,
|
|
w_opt,
|
|
print_recurs,
|
|
blacklist)
|
|
del Comp._proto_recur, Comp._proto_path
|
|
num += comp_num
|
|
loc_depth.append( comp_depth )
|
|
recur.extend( comp_recur )
|
|
if loc_depth:
|
|
depth += 1 + max(loc_depth)
|
|
#
|
|
elif self.TYPE in (TYPE_SEQ_OF, TYPE_SET_OF):
|
|
Comp = self._cont
|
|
if id(Comp) in self._proto_recur:
|
|
recur_path = self._proto_path + [None]
|
|
if print_recurs:
|
|
asnlog('[+] recursion detected: %s, at path %r'\
|
|
% (Comp._name, recur_path))
|
|
recur.append( recur_path )
|
|
else:
|
|
Comp._proto_recur = self._proto_recur + [id(Comp)]
|
|
Comp._proto_path = self._proto_path + [None]
|
|
comp_num, comp_depth, comp_recur = Comp.get_complexity(
|
|
w_open,
|
|
w_opt,
|
|
print_recurs,
|
|
blacklist)
|
|
del Comp._proto_recur, Comp._proto_path
|
|
num += comp_num
|
|
depth += 1 + comp_depth
|
|
recur.extend( comp_recur )
|
|
#
|
|
elif self.TYPE in (TYPE_BIT_STR, TYPE_OCT_STR) and self._const_cont:
|
|
Comp = self._const_cont
|
|
if id(Comp) in self._proto_recur:
|
|
recur_path = self._proto_path + [None]
|
|
if print_recurs:
|
|
asnlog('[+] recursion detected: %s, at path %r'\
|
|
% (Comp._name, recur_path))
|
|
recur.append( recur_path )
|
|
else:
|
|
Comp._proto_recur = self._proto_recur + [id(Comp)]
|
|
Comp._proto_path = self._proto_path + [None]
|
|
comp_num, comp_depth, comp_recur = Comp.get_complexity(
|
|
w_open,
|
|
w_opt,
|
|
print_recurs,
|
|
blacklist)
|
|
del Comp._proto_recur, Comp._proto_path
|
|
num += comp_num
|
|
depth += 1 + comp_depth
|
|
recur.extend( comp_recur )
|
|
#
|
|
else:
|
|
assert( self.TYPE in TYPES_BASIC + TYPES_EXT )
|
|
num += 1
|
|
#
|
|
if root:
|
|
del self._proto_recur, self._proto_path
|
|
return num, depth, recur
|
|
|
|
def _get_obj_by_path(self, path):
|
|
# this is used for solving table constraint lookups
|
|
obj = self
|
|
for p in path:
|
|
if p == '..':
|
|
obj = obj._parent
|
|
else:
|
|
obj = obj._cont[p]
|
|
return obj
|
|
|
|
def _get_val_by_path(self, path):
|
|
# this is used for solving table constraint lookups
|
|
obj = self
|
|
for p in path:
|
|
if p == '..':
|
|
obj = obj._parent
|
|
else:
|
|
obj = obj._cont[p]
|
|
return obj._val
|
|
|
|
def get_root(self):
|
|
"""
|
|
returns the root object containing self in case self is within a
|
|
constructed or CLASS object
|
|
|
|
WARNING: you will not always get the root object you expect...
|
|
"""
|
|
# WNG: in case an object from a CLASS has been inserted after a table
|
|
# constraint lookup, the parent will be incorrect
|
|
obj = self
|
|
while obj._parent is not None:
|
|
obj = obj._parent
|
|
return obj
|
|
|
|
def get_root_path(self):
|
|
"""
|
|
returns the list of successive objects' name containing self up to the
|
|
root one in case self is within a constructed or CLASS object
|
|
|
|
WARNING: you will not always get the root object you expect...
|
|
"""
|
|
# WNG: in case an object from a CLASS has been inserted after a table
|
|
# constraint lookup, the parent will be incorrect
|
|
path = [self._name]
|
|
par = self._parent
|
|
while par is not None:
|
|
path.append(par._name)
|
|
par = par._parent
|
|
path.reverse()
|
|
return path
|
|
|
|
def in_class(self):
|
|
"""
|
|
returns True in case self is a field of a CLASS, False otherwise
|
|
"""
|
|
par = self._parent
|
|
while par is not None:
|
|
if par.TYPE == TYPE_CLASS:
|
|
return True
|
|
else:
|
|
par = par._parent
|
|
return False
|
|
|
|
def get_at(self, path):
|
|
"""
|
|
returns the sub-object into self at the given relative path
|
|
|
|
Args:
|
|
path: list of str
|
|
|
|
Returns:
|
|
ASN1Obj instance
|
|
|
|
Raises:
|
|
ASN1Err, if `path' is invalid
|
|
"""
|
|
Obj, selected = self, []
|
|
for p in path:
|
|
selected.append(p)
|
|
try:
|
|
if Obj.TYPE in (TYPE_CHOICE, TYPE_SEQ, TYPE_SET, TYPE_REAL,
|
|
TYPE_EXT, TYPE_EMB_PDV, TYPE_CHAR_STR):
|
|
if p[:5] == '_ext_':
|
|
break
|
|
else:
|
|
Obj = Obj._cont[p]
|
|
elif Obj.TYPE in (TYPE_SEQ_OF, TYPE_SET_OF):
|
|
# p is not used
|
|
Obj = Obj._cont
|
|
elif Obj.TYPE in (TYPE_OPEN, TYPE_ANY):
|
|
if p[:5] == '_unk_':
|
|
break
|
|
else:
|
|
Obj = Obj._get_val_obj(p)
|
|
elif Obj.TYPE in (TYPE_BIT_STR, TYPE_OCT_STR) \
|
|
and Obj._const_cont is not None:
|
|
# p is not used
|
|
Obj = Obj._const_cont
|
|
else:
|
|
# invalid path, go to the exception case
|
|
raise()
|
|
except Exception:
|
|
raise(ASN1Err('{0}: invalid path {1}'.format(self.fullname(), selected)))
|
|
return Obj
|
|
|
|
#--------------------------------------------------------------------------#
|
|
# internal value access methods
|
|
#--------------------------------------------------------------------------#
|
|
|
|
def __call__(self):
|
|
return self._val
|
|
|
|
def get_val(self):
|
|
return self._val
|
|
|
|
def get_val_paths(self, curpath=[], paths=[]):
|
|
"""
|
|
returns the list of paths of each individual basic value set into self
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
list of 2-tuple: (path_to_basic_value, basic_value)
|
|
"""
|
|
if self._val is None:
|
|
return []
|
|
#
|
|
if self.TYPE in (TYPE_CHOICE, TYPE_ANY, TYPE_OPEN) or \
|
|
self.TYPE in (TYPE_BIT_STR, TYPE_OCT_STR) and \
|
|
isinstance(self._val, tuple) and \
|
|
isinstance(self._val[0], str_types):
|
|
# value is (component_name, component_value)
|
|
curpath.append( self._val[0] )
|
|
if self._val[0][:5] in ('_ext_', '_unk_'):
|
|
# take care of unknown / extended objects
|
|
paths.append( (curpath[:], self._val[1]) )
|
|
else:
|
|
Comp = self.get_at( [self._val[0]] )
|
|
_comp_val = Comp._val
|
|
Comp._val = self._val[1]
|
|
paths = Comp.get_val_paths(curpath[:], paths[:])
|
|
Comp._val = _comp_val
|
|
del curpath[-1]
|
|
#
|
|
elif self.TYPE in (TYPE_SEQ, TYPE_SET, TYPE_EXT, TYPE_EMB_PDV, TYPE_CHAR_STR):
|
|
# value is dict {component_name: component_value)
|
|
val_ids = list(self._val.keys())
|
|
for comp_name in self._cont:
|
|
if comp_name in val_ids[:]:
|
|
curpath.append( comp_name )
|
|
comp_val = self._val[comp_name]
|
|
Comp = self._cont[comp_name]
|
|
_comp_val = Comp._val
|
|
Comp._val = comp_val
|
|
paths = Comp.get_val_paths(curpath[:], paths[:])
|
|
Comp._val = _comp_val
|
|
del curpath[-1]
|
|
val_ids.remove(comp_name)
|
|
if val_ids:
|
|
# take care of remaining unknown / extension objects
|
|
val_ids.sort()
|
|
for comp_name in val_ids:
|
|
curpath.append( comp_name )
|
|
assert( comp_name[:5] in ('_ext_', '_unk_') )
|
|
paths.append( (curpath[:], self._val[comp_name][1]) )
|
|
del curpath[-1]
|
|
#
|
|
elif self.TYPE in (TYPE_SEQ_OF, TYPE_SET_OF):
|
|
# value is a list of component_value
|
|
Comp = self._cont
|
|
_comp_val = Comp._val
|
|
for i, val in enumerate(self._val):
|
|
Comp._val = val
|
|
curpath.append( i )
|
|
paths = Comp.get_val_paths(curpath[:], paths[:])
|
|
del curpath[-1]
|
|
Comp._val = _comp_val
|
|
#
|
|
elif self.TYPE in TYPES_BASIC:
|
|
# basic value reached
|
|
paths.append( (curpath[:], self._val) )
|
|
#
|
|
return paths[:]
|
|
|
|
def get_val_at(self, path):
|
|
"""
|
|
returns the value into self at the given relative path
|
|
|
|
Args:
|
|
path: list of str or int
|
|
|
|
Returns:
|
|
value of the corresponding ASN1Obj
|
|
"""
|
|
if self._val is None:
|
|
raise(ASN1Err('{0}: no value defined'.format(self.fullname())))
|
|
Obj, val = self, self._val
|
|
for p in path:
|
|
try:
|
|
if Obj.TYPE in (TYPE_CHOICE, TYPE_ANY, TYPE_OPEN, TYPE_BIT_STR, TYPE_OCT_STR):
|
|
Obj = Obj.get_at([p])
|
|
if p == val[0]:
|
|
val = val[1]
|
|
else:
|
|
raise()
|
|
elif Obj.TYPE in (TYPE_SEQ, TYPE_SET, TYPE_EXT, TYPE_EMB_PDV, TYPE_CHAR_STR):
|
|
Obj = Obj._cont[p]
|
|
val = val[p]
|
|
elif Obj.TYPE in (TYPE_SEQ_OF, TYPE_SET_OF):
|
|
Obj = Obj._cont
|
|
val = val[p]
|
|
elif Obj.TYPE == TYPE_REAL:
|
|
Obj = None
|
|
if instance(p, integer_types):
|
|
val = val[p]
|
|
elif p == 'mantissa':
|
|
val = val[0]
|
|
elif p == 'base':
|
|
val = val[1]
|
|
elif p == 'exponent':
|
|
val = val[2]
|
|
else:
|
|
raise()
|
|
else:
|
|
raise()
|
|
except Exception:
|
|
raise(ASN1Err('{0}: invalid value path, {1!r}'.format(self.fullname(), path)))
|
|
return val
|
|
|
|
def set_val_at(self, path, newval):
|
|
"""
|
|
sets a new value into self at the given relative path
|
|
|
|
Args:
|
|
path : list of str or int
|
|
newval: ASN1Obj value
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
# Warning: all wrapping objects (tuples, dict, list) in the value
|
|
# need to be re-written
|
|
if not path:
|
|
self.set_val(newval)
|
|
return
|
|
elif self._val is None:
|
|
raise(ASN1Err('{0}: no value already defined'.format(self.fullname())))
|
|
# ensure the path is correct and raise it not
|
|
_ = self.get_val_at(path)
|
|
#
|
|
for i in range(1, 1+len(path)):
|
|
p = path[-i]
|
|
parval = self.get_val_at(path[:-i])
|
|
if isinstance(parval, tuple) and p == parval[0]:
|
|
# TYPE_CHOICE, TYPE_ANY, TYPE_OPEN, TYPE_BIT_STR, TYPE_OCT_STR
|
|
parval = (p, newval)
|
|
elif isinstance(parval, dict) and p in parval:
|
|
# TYPE_SEQ, TYPE_SET, TYPE_EXT, TYPE_EMB_PDV, TYPE_CHAR_STR
|
|
parval = dict(parval)
|
|
parval[p] = newval
|
|
elif isinstance(parval, list):
|
|
# TYPE_SEQ_OF, TYPE_SET_OF
|
|
parval = parval[:]
|
|
parval[p] = newval
|
|
elif isinstance(parval, tuple) and len(parval) == 3:
|
|
# TYPE_REAL
|
|
parval = list(parval)
|
|
if instance(p, integer_types):
|
|
ind = p
|
|
elif p == 'mantissa':
|
|
ind = 0
|
|
elif p == 'base':
|
|
ind = 1
|
|
elif p == 'exponent':
|
|
ind = 2
|
|
else:
|
|
raise()
|
|
parval[ind] = newval
|
|
parval = tuple(parval)
|
|
else:
|
|
assert()
|
|
newval = parval
|
|
self.set_val(newval)
|
|
|
|
def set_val(self, val):
|
|
"""sets the given value `val' into self
|
|
"""
|
|
self._val = val
|
|
if self._SAFE_VAL:
|
|
self._safechk_val(self._val)
|
|
if self._SAFE_BND:
|
|
self._safechk_bnd(self._val)
|
|
|
|
def unset_val(self):
|
|
"""reset internal values corresponding to self._val and its impacted
|
|
sub-components
|
|
"""
|
|
for path, val in self.get_val_paths()[::-1]:
|
|
for path_ind in range(len(path), 0, -1):
|
|
Obj = self.get_at(path[:path_ind])
|
|
if Obj._val is not None:
|
|
del Obj._val
|
|
del self._val
|
|
|
|
def reset_val(self):
|
|
"""reset all internal values into self and all its sub-components
|
|
"""
|
|
if hasattr(self, '_reset'):
|
|
# avoid recursion
|
|
return
|
|
else:
|
|
self._reset = True
|
|
if '_val' in self.__dict__:
|
|
# a specific value is set within the instance: delete it
|
|
del self._val
|
|
if self.TYPE in (TYPE_BIT_STR, TYPE_OCT_STR) and self._const_cont is not None:
|
|
self._const_cont.reset_val()
|
|
elif self.TYPE in (TYPE_SEQ_OF, TYPE_SET_OF):
|
|
self._cont.reset_val()
|
|
elif self.TYPE in (TYPE_CHOICE, TYPE_SEQ, TYPE_SET):
|
|
for Comp in self._cont.values():
|
|
Comp.reset_val()
|
|
elif self.TYPE == TYPE_OPEN:
|
|
for Obj in self._get_const_tr().values():
|
|
Obj.reset_val()
|
|
del self._reset
|
|
|
|
def convert_named_val(self):
|
|
"""convert all INTEGER and BIT STRING values within self to named values and
|
|
sets of named bits when possible
|
|
"""
|
|
for path, val in self.get_val_paths():
|
|
Obj = self.get_at(path)
|
|
if Obj.TYPE == TYPE_INT and Obj._cont:
|
|
Obj.set_val(val)
|
|
name = Obj.get_name()
|
|
if name:
|
|
self.set_val_at(path, name)
|
|
elif Obj.TYPE == TYPE_BIT_STR and Obj._cont:
|
|
Obj.set_val(val)
|
|
names = Obj.get_names()
|
|
if names:
|
|
self.set_val_at(path, names)
|
|
|
|
#--------------------------------------------------------------------------#
|
|
# encoding / decoding methods
|
|
#--------------------------------------------------------------------------#
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 syntax
|
|
###
|
|
|
|
def _from_asn1(self, txt):
|
|
raise(ASN1NotSuppErr(self.fullname()))
|
|
|
|
def _to_asn1(self):
|
|
raise(ASN1NotSuppErr(self.fullname()))
|
|
|
|
def from_asn1(self, txt):
|
|
txt = clean_text(txt)
|
|
ret = self._from_asn1(txt)
|
|
if self._SAFE_BND:
|
|
self._safechk_bnd(self._val)
|
|
return ret
|
|
|
|
def to_asn1(self, val=None):
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
return self._to_asn1()
|
|
else:
|
|
return None
|
|
|
|
def show(self):
|
|
return '<~ASN1~: %s>' % self.to_asn1()
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 PER encoding
|
|
###
|
|
|
|
def _from_per(self, char):
|
|
raise(ASN1NotSuppErr(self.fullname()))
|
|
|
|
def _to_per(self):
|
|
raise(ASN1NotSuppErr(self.fullname()))
|
|
|
|
def from_uper(self, buf):
|
|
ASN1CodecPER.ALIGNED = False
|
|
if isinstance(buf, bytes_types):
|
|
char = Charpy(buf)
|
|
else:
|
|
char = buf
|
|
#assert( char.len_bit() % 8 == 0 )
|
|
off0 = char._cur
|
|
self._from_per(char)
|
|
off1 = char._cur
|
|
if off1 == off0:
|
|
# char was not consumed at all (all decoded values were implicit)
|
|
# hence a null byte must be consumed for outer decoding
|
|
null = char.get_bytes(8)
|
|
assert( null == b'\0' )
|
|
elif (off1 - off0) % 8:
|
|
# realignement required for outer decoding
|
|
char.forward(8 - ((off1 - off0)%8))
|
|
if self._SAFE_BND:
|
|
self._safechk_bnd(self._val)
|
|
|
|
def to_uper(self, val=None):
|
|
ASN1CodecPER.ALIGNED = False
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
ret = pack_val(*self._to_per())[0]
|
|
if ret:
|
|
return ret
|
|
else:
|
|
return b'\0'
|
|
else:
|
|
return None
|
|
|
|
def from_aper(self, buf):
|
|
ASN1CodecPER.ALIGNED = True
|
|
ASN1CodecPER._off.append(0)
|
|
if isinstance(buf, bytes_types):
|
|
char = Charpy(buf)
|
|
else:
|
|
char = buf
|
|
assert( char.len_bit() % 8 == 0 )
|
|
self._from_per(char)
|
|
if ASN1CodecPER._off[-1] == 0:
|
|
# char was not consumed at all (all decoded values were implicit)
|
|
# hence a null byte must be consumed
|
|
null = char.get_bytes(8)
|
|
assert( null == b'\0' )
|
|
elif ASN1CodecPER._off[-1] % 8:
|
|
# realignement required for outer decoding
|
|
char.forward(8 - (ASN1CodecPER._off[-1]%8))
|
|
del ASN1CodecPER._off[-1]
|
|
if self._SAFE_BND:
|
|
self._safechk_bnd(self._val)
|
|
|
|
def to_aper(self, val=None):
|
|
ASN1CodecPER.ALIGNED = True
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
ASN1CodecPER._off.append(0)
|
|
ret = pack_val(*self._to_per())[0]
|
|
if not ret:
|
|
ret = b'\0'
|
|
del ASN1CodecPER._off[-1]
|
|
return ret
|
|
else:
|
|
return None
|
|
|
|
# methods generating complete transfer structure in _struct attributes
|
|
|
|
def _from_per_ws(self, char):
|
|
raise(ASN1NotSuppErr(self.fullname()))
|
|
|
|
def _to_per_ws(self):
|
|
raise(ASN1NotSuppErr(self.fullname()))
|
|
|
|
def from_uper_ws(self, buf):
|
|
ASN1CodecPER.ALIGNED = False
|
|
if isinstance(buf, bytes_types):
|
|
char = Charpy(buf)
|
|
else:
|
|
char = buf
|
|
#assert( char.len_bit() % 8 == 0 )
|
|
off0 = char._cur
|
|
self._from_per_ws(char)
|
|
off1 = char._cur
|
|
pad = None
|
|
if off1 == off0:
|
|
# char was not consumed at all (all decoded values were implicit)
|
|
# hence a null byte must be consumed
|
|
pad = Uint('P', val=0, bl=8, rep=REPR_BIN)
|
|
pad._from_char(char)
|
|
self._struct.append(pad)
|
|
assert( pad() == 0 )
|
|
elif (off1 - off0) % 8:
|
|
# realignment required for outer decoding
|
|
pad = Uint('P', val=0, bl=(8-((off1 - off0)%8)), rep=REPR_BIN)
|
|
pad._from_char(char)
|
|
self._struct.append(pad)
|
|
assert( pad() == 0 )
|
|
if self._SAFE_BND:
|
|
self._safechk_bnd(self._val)
|
|
|
|
def to_uper_ws(self, val=None):
|
|
ASN1CodecPER.ALIGNED = False
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
_struct = self._to_per_ws()
|
|
bl = _struct.get_bl()
|
|
if bl == 0:
|
|
_struct.append( Uint('P', val=0, bl=8, rep=REPR_BIN) )
|
|
elif bl % 8:
|
|
_struct.append( Uint('P', val=0, bl=(8-(bl%8)), rep=REPR_BIN) )
|
|
return _struct.to_bytes()
|
|
else:
|
|
return None
|
|
|
|
def from_aper_ws(self, buf):
|
|
ASN1CodecPER.ALIGNED = True
|
|
ASN1CodecPER._off.append(0)
|
|
if isinstance(buf, bytes_types):
|
|
char = Charpy(buf)
|
|
else:
|
|
char = buf
|
|
assert( char.len_bit() % 8 == 0 )
|
|
self._from_per_ws(char)
|
|
if ASN1CodecPER._off[-1] == 0:
|
|
# char was not consumed at all (all decoded values were implicit)
|
|
# hence a null byte must be consumed
|
|
pad = Uint('P', val=0, bl=8, rep=REPR_BIN)
|
|
pad._from_char(char)
|
|
self._struct.append(pad)
|
|
assert( pad() == 0 )
|
|
elif ASN1CodecPER._off[-1] % 8:
|
|
# realignement required for outer decoding
|
|
pad = Uint('P', val=0, bl=(8 - (ASN1CodecPER._off[-1]%8)), rep=REPR_BIN)
|
|
pad._from_char(char)
|
|
self._struct.append(pad)
|
|
assert( pad() == 0 )
|
|
del ASN1CodecPER._off[-1]
|
|
if self._SAFE_BND:
|
|
self._safechk_bnd(self._val)
|
|
|
|
def to_aper_ws(self, val=None):
|
|
ASN1CodecPER.ALIGNED = True
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
ASN1CodecPER._off.append(0)
|
|
_struct = self._to_per_ws()
|
|
if ASN1CodecPER._off[-1] == 0:
|
|
_struct.append( Uint('P', val=0, bl=8, rep=REPR_BIN) )
|
|
elif ASN1CodecPER._off[-1] % 8:
|
|
_struct.append( Uint('P', val=0, bl=(8-(ASN1CodecPER._off[-1]%8)), rep=REPR_BIN) )
|
|
del ASN1CodecPER._off[-1]
|
|
return _struct.to_bytes()
|
|
else:
|
|
return None
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 BER encoding
|
|
###
|
|
|
|
# for BER encoding, we can enable per-object encoding options
|
|
# set this to False to disable per-object encoding options
|
|
_BER_ENC_OPT = True
|
|
|
|
# following are the different BER encoding options supported
|
|
# see codecs.ASN1CodecBER class for further description
|
|
_BER_ENC_ARGS = (
|
|
# for tag prefix
|
|
'TAG_LEXT',
|
|
# for length prefix
|
|
'LLONG',
|
|
'LUNDEF',
|
|
# for DEFAULT values
|
|
'DEF_CANON',
|
|
# for BOOLEAN object
|
|
'BOOLTRUE',
|
|
# for BIT / OCTET STRING object
|
|
'BSTR_FRAG',
|
|
'OSTR_FRAG',
|
|
# for *OID* object
|
|
'OID_LEXT',
|
|
# for *Time* object
|
|
'TIME_CANON',
|
|
)
|
|
|
|
def __to_ber_codec_set(self):
|
|
ber_enc_args = {}
|
|
for arg in self._BER_ENC_ARGS:
|
|
attr_g, attr_l = 'ENC_%s' % arg, '_BER_ENC_%s' % arg
|
|
if hasattr(self, attr_l):
|
|
# save the global attribute for restoration
|
|
# after object has been encoded
|
|
ber_enc_args[attr_g] = getattr(ASN1CodecBER, attr_g)
|
|
# set the global attribute to the object's local value
|
|
setattr(ASN1CodecBER, attr_g, getattr(self, attr_l))
|
|
return ber_enc_args
|
|
|
|
def __to_ber_codec_unset(self, ber_enc_args):
|
|
for k, v in ber_enc_args.items():
|
|
setattr(ASN1CodecBER, k, v)
|
|
|
|
# std BER decoding / encoding routines
|
|
|
|
def _from_ber(self, char, TLV):
|
|
# 1) decode the tag chain
|
|
tlv, pc = TLV, 1
|
|
for t in self._tagc:
|
|
try:
|
|
tlv = tlv[0]
|
|
except IndexError:
|
|
raise(ASN1BERDecodeErr('{0}: missing tag buffer'.format(self.fullname())))
|
|
cl, pc, tval, lval = tlv[0:4]
|
|
if (cl, tval) != t or (t != self._tagc[-1] and pc == 0):
|
|
raise(ASN1BERDecodeErr('{0}: invalid tag class / pc / value, {1!r}'\
|
|
.format(self.fullname(), (cl, pc, tval))))
|
|
tlv = tlv[4]
|
|
if pc == 0:
|
|
# 2a) decode primitive content value
|
|
# here, tlv is actually a 2-tuple with the value boundaries
|
|
assert( isinstance(tlv, tuple) )
|
|
else:
|
|
# 2b) decode constructed content
|
|
assert( isinstance(tlv, list) )
|
|
self._decode_ber_cont(char, tlv)
|
|
|
|
def from_ber(self, buf, single=True):
|
|
if isinstance(buf, bytes_types):
|
|
char = Charpy(buf)
|
|
else:
|
|
char = buf
|
|
# decode the whole char buffer into tag, length and value boundary
|
|
if single:
|
|
TLV = [ASN1CodecBER.decode_single(char)[0]]
|
|
else:
|
|
TLV = ASN1CodecBER.decode_all(char)
|
|
char_cur, char_lb = char._cur, char._len_bit
|
|
# decode all value content
|
|
self._from_ber(char, TLV)
|
|
char._cur, char._len_bit = char_cur, char_lb
|
|
if self._SAFE_BND:
|
|
self._safechk_bnd(self._val)
|
|
|
|
def _to_ber(self):
|
|
# 0) set potential BER codec locals
|
|
if self._BER_ENC_OPT:
|
|
_ber_enc_args = self.__to_ber_codec_set()
|
|
#
|
|
# 1) encode the most inner TLV part
|
|
pc, lval, V = self._encode_ber_cont()
|
|
if not self._tagc:
|
|
# in case no tag is associated to the object (CHOICE, OPEN / ANY)
|
|
# we only have the inner encoding
|
|
ret = V
|
|
else:
|
|
TLV = ASN1CodecBER.encode_tag(self._tagc[-1][0], pc, self._tagc[-1][1])
|
|
TLV.extend( ASN1CodecBER.encode_len(lval) )
|
|
TLV.extend( V )
|
|
if lval == -1:
|
|
TLV.append( (T_BYTES, b'\0\0', 16) )
|
|
# 2) encode the outer part of the object, i.e. the rest of the tag chain
|
|
if len(self._tagc) > 1:
|
|
GEN = [TLV]
|
|
if ASN1CodecBER.ENC_LUNDEF:
|
|
for t in reversed(self._tagc[:-1]):
|
|
TL = ASN1CodecBER.encode_tag(t[0], 1, t[1])
|
|
TL.extend( ASN1CodecBER.encode_len(-1) )
|
|
# append an EOC marker after the value
|
|
TLV.append( (T_BYTES, b'\0\0', 16) )
|
|
GEN.append(TL)
|
|
else:
|
|
lval = sum([f[2] for f in TLV]) >> 3
|
|
for t in reversed(self._tagc[:-1]):
|
|
TL = ASN1CodecBER.encode_tag(t[0], 1, t[1])
|
|
TL.extend( ASN1CodecBER.encode_len(lval) )
|
|
lval += sum([f[2] for f in TL]) >> 3
|
|
GEN.append(TL)
|
|
# revert and flatten GEN
|
|
ret = [i for j in reversed(GEN) for i in j]
|
|
else:
|
|
ret = TLV
|
|
#
|
|
# 2) restore potential BER encoder globals
|
|
if self._BER_ENC_OPT:
|
|
self.__to_ber_codec_unset(_ber_enc_args)
|
|
return ret
|
|
|
|
def to_ber(self, val=None):
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
return pack_val(*self._to_ber())[0]
|
|
else:
|
|
return None
|
|
|
|
# methods generating complete transfer structure in _struct attributes
|
|
|
|
def _from_ber_ws(self, char, TLV):
|
|
# 1) decode the tag chain
|
|
tlv, TL, pc = TLV, [], 1
|
|
for t in self._tagc:
|
|
try:
|
|
tlv = tlv[0]
|
|
except IndexError:
|
|
raise(ASN1BERDecodeErr('{0}: missing tag buffer'.format(self.fullname())))
|
|
Tag, cl, pc, tval, Len, lval = tlv[0:6]
|
|
if (cl, tval) != t or (t != self._tagc[-1] and pc == 0):
|
|
raise(ASN1BERDecodeErr('{0}: invalid tag class / pc / value, {1!r}'\
|
|
.format(self.fullname(), (cl, pc, tval))))
|
|
# select the value as a new inner tlv
|
|
tlv = tlv[6]
|
|
if lval == -1:
|
|
# keep track of the End-Of-Content marker
|
|
TL.append( (Tag, Len, tlv[-1][0], tlv[-1][4]) )
|
|
else:
|
|
TL.append( (Tag, Len) )
|
|
#
|
|
if pc == 0:
|
|
# 2a) decode primitive content value
|
|
# here, tlv is actually a 2-tuple with the value boundaries
|
|
assert( isinstance(tlv, tuple) )
|
|
else:
|
|
# 2b) decode constructed content
|
|
assert( isinstance(tlv, list) )
|
|
V = self._decode_ber_cont_ws(char, tlv)
|
|
#
|
|
# 3) generate the complete TLV structure
|
|
if TL:
|
|
TL.reverse()
|
|
for tl in TL:
|
|
if len(tl) == 4:
|
|
# EOC marker
|
|
TLV = Envelope('TLV', GEN=(tl[0], tl[1], V, tl[2], tl[3]))
|
|
else:
|
|
TLV = Envelope('TLV', GEN=(tl[0], tl[1], V))
|
|
V = TLV
|
|
V._name = 'V'
|
|
else:
|
|
# in case no tag is associated to the object (CHOICE, OPEN / ANY)
|
|
# we only have the inner encoding
|
|
TLV = V
|
|
# set object name and final struct
|
|
TLV._name = self._name
|
|
self._struct = TLV
|
|
|
|
def from_ber_ws(self, buf, single=True):
|
|
if isinstance(buf, bytes_types):
|
|
char = Charpy(buf)
|
|
else:
|
|
char = buf
|
|
# decode the whole char buffer into tag, length and value boundary
|
|
if single:
|
|
TLV = [ASN1CodecBER.decode_single_ws(char)[0]]
|
|
else:
|
|
TLV = ASN1CodecBER.decode_all_ws(char)
|
|
char_cur, char_lb = char._cur, char._len_bit
|
|
# decode all value content
|
|
self._from_ber_ws(char, TLV)
|
|
char._cur, char._len_bit = char_cur, char_lb
|
|
if self._SAFE_BND:
|
|
self._safechk_bnd(self._val)
|
|
|
|
def _to_ber_ws(self):
|
|
# 0) set potential BER codec locals
|
|
if self._BER_ENC_OPT:
|
|
_ber_enc_args = self.__to_ber_codec_set()
|
|
#
|
|
# 1) encode the most inner TLV part
|
|
pc, lval, V = self._encode_ber_cont_ws()
|
|
if not self._tagc:
|
|
# in case no tag is associated to the object (CHOICE, OPEN / ANY)
|
|
# we only have the inner encoding
|
|
TLV = V
|
|
else:
|
|
if pc == 1 and ASN1CodecBER.ENC_LUNDEF:
|
|
TLV = Envelope('TLV', GEN=(
|
|
ASN1CodecBER.encode_tag_ws(self._tagc[-1][0], pc, self._tagc[-1][1]),
|
|
ASN1CodecBER.encode_len_ws(-1),
|
|
V,
|
|
ASN1CodecBER.encode_tag_ws(0, 0, 0),
|
|
ASN1CodecBER.encode_len_ws(0)))
|
|
else:
|
|
TLV = Envelope('TLV', GEN=(
|
|
ASN1CodecBER.encode_tag_ws(self._tagc[-1][0], pc, self._tagc[-1][1]),
|
|
ASN1CodecBER.encode_len_ws(lval),
|
|
V))
|
|
# 2) encode the outer part of the object, i.e. the rest of the tag chain
|
|
if len(self._tagc) > 1:
|
|
if ASN1CodecBER.ENC_LUNDEF:
|
|
for t in reversed(self._tagc[:-1]):
|
|
TLV._name = 'V'
|
|
TLV = Envelope('TLV', GEN=(ASN1CodecBER.encode_tag_ws(t[0], 1, t[1]),
|
|
ASN1CodecBER.encode_len_ws(-1),
|
|
TLV,
|
|
ASN1CodecBER.encode_tag_ws(0, 0, 0),
|
|
ASN1CodecBER.encode_len_ws(0)))
|
|
else:
|
|
lval += (TLV[0].get_bl() + TLV[1].get_bl()) >> 3
|
|
for t in reversed(self._tagc[:-1]):
|
|
TLV._name = 'V'
|
|
TLV = Envelope('TLV', GEN=(ASN1CodecBER.encode_tag_ws(t[0], 1, t[1]),
|
|
ASN1CodecBER.encode_len_ws(lval),
|
|
TLV))
|
|
lval += (TLV[0].get_bl() + TLV[1].get_bl()) >> 3
|
|
# set object name and final struct
|
|
TLV._name = self._name
|
|
self._struct = TLV
|
|
#
|
|
# 2) restore potential BER encoder globals
|
|
if self._BER_ENC_OPT:
|
|
self.__to_ber_codec_unset(_ber_enc_args)
|
|
return TLV
|
|
|
|
def to_ber_ws(self, val=None):
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
return self._to_ber_ws().to_bytes()
|
|
else:
|
|
return None
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 CER encoding
|
|
# reusing the BER encoder
|
|
###
|
|
|
|
def from_cer(self, buf):
|
|
_save_ber_params()
|
|
ASN1CodecBER.ENC_LLONG = 0
|
|
ASN1CodecBER.ENC_LUNDEF = True
|
|
ASN1CodecBER.ENC_BOOLTRUE = 0xff
|
|
ASN1CodecBER.ENC_REALNR = 3
|
|
ASN1CodecBER.ENC_BSTR_FRAG = 1000
|
|
ASN1CodecBER.ENC_OSTR_FRAG = 1000
|
|
ASN1CodecBER.ENC_TIME_CANON = True
|
|
ASN1CodecBER.ENC_DEF_CANON = True
|
|
ret = self.from_ber(buf)
|
|
_restore_ber_params()
|
|
return ret
|
|
|
|
def to_cer(self, val=None):
|
|
_save_ber_params()
|
|
ASN1CodecBER.ENC_LLONG = 0
|
|
ASN1CodecBER.ENC_LUNDEF = True
|
|
ASN1CodecBER.ENC_BOOLTRUE = 0xff
|
|
ASN1CodecBER.ENC_REALNR = 3
|
|
ASN1CodecBER.ENC_BSTR_FRAG = 1000
|
|
ASN1CodecBER.ENC_OSTR_FRAG = 1000
|
|
ASN1CodecBER.ENC_TIME_CANON = True
|
|
ASN1CodecBER.ENC_DEF_CANON = True
|
|
ret = self.to_ber(val)
|
|
_restore_ber_params()
|
|
return ret
|
|
|
|
# methods generating complete transfer structure in _struct attributes
|
|
|
|
def from_cer_ws(self, buf):
|
|
_save_ber_params()
|
|
ASN1CodecBER.ENC_LLONG = 0
|
|
ASN1CodecBER.ENC_LUNDEF = True
|
|
ASN1CodecBER.ENC_BOOLTRUE = 0xff
|
|
ASN1CodecBER.ENC_REALNR = 3
|
|
ASN1CodecBER.ENC_BSTR_FRAG = 1000
|
|
ASN1CodecBER.ENC_OSTR_FRAG = 1000
|
|
ASN1CodecBER.ENC_TIME_CANON = True
|
|
ASN1CodecBER.ENC_DEF_CANON = True
|
|
ret = self.from_ber_ws(buf)
|
|
_restore_ber_params()
|
|
return ret
|
|
|
|
def to_cer_ws(self, val=None):
|
|
_save_ber_params()
|
|
ASN1CodecBER.ENC_LLONG = 0
|
|
ASN1CodecBER.ENC_LUNDEF = True
|
|
ASN1CodecBER.ENC_BOOLTRUE = 0xff
|
|
ASN1CodecBER.ENC_REALNR = 3
|
|
ASN1CodecBER.ENC_BSTR_FRAG = 1000
|
|
ASN1CodecBER.ENC_OSTR_FRAG = 1000
|
|
ASN1CodecBER.ENC_TIME_CANON = True
|
|
ASN1CodecBER.ENC_DEF_CANON = True
|
|
ret = self.to_ber_ws(val)
|
|
_restore_ber_params()
|
|
return ret
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 DER encoding
|
|
# reusing the BER encoder
|
|
###
|
|
|
|
def from_der(self, buf):
|
|
_save_ber_params()
|
|
ASN1CodecBER.ENC_LLONG = 0
|
|
ASN1CodecBER.ENC_LUNDEF = False
|
|
ASN1CodecBER.ENC_BOOLTRUE = 0xff
|
|
ASN1CodecBER.ENC_REALNR = 3
|
|
ASN1CodecBER.ENC_BSTR_FRAG = 0
|
|
ASN1CodecBER.ENC_OSTR_FRAG = 0
|
|
ASN1CodecBER.ENC_TIME_CANON = True
|
|
ASN1CodecBER.ENC_DEF_CANON = True
|
|
ret = self.from_ber(buf)
|
|
_restore_ber_params()
|
|
return ret
|
|
|
|
def to_der(self, val=None):
|
|
_save_ber_params()
|
|
ASN1CodecBER.ENC_LLONG = 0
|
|
ASN1CodecBER.ENC_LUNDEF = False
|
|
ASN1CodecBER.ENC_BOOLTRUE = 0xff
|
|
ASN1CodecBER.ENC_REALNR = 3
|
|
ASN1CodecBER.ENC_BSTR_FRAG = 0
|
|
ASN1CodecBER.ENC_OSTR_FRAG = 0
|
|
ASN1CodecBER.ENC_TIME_CANON = True
|
|
ASN1CodecBER.ENC_DEF_CANON = True
|
|
ret = self.to_ber(val)
|
|
_restore_ber_params()
|
|
return ret
|
|
|
|
# methods generating complete transfer structure in _struct attributes
|
|
|
|
def from_der_ws(self, buf):
|
|
_save_ber_params()
|
|
ASN1CodecBER.ENC_LLONG = 0
|
|
ASN1CodecBER.ENC_LUNDEF = False
|
|
ASN1CodecBER.ENC_BOOLTRUE = 0xff
|
|
ASN1CodecBER.ENC_REALNR = 3
|
|
ASN1CodecBER.ENC_BSTR_FRAG = 0
|
|
ASN1CodecBER.ENC_OSTR_FRAG = 0
|
|
ASN1CodecBER.ENC_TIME_CANON = True
|
|
ASN1CodecBER.ENC_DEF_CANON = True
|
|
ret = self.from_ber_ws(buf)
|
|
_restore_ber_params()
|
|
return ret
|
|
|
|
def to_der_ws(self, val=None):
|
|
_save_ber_params()
|
|
ASN1CodecBER.ENC_LLONG = 0
|
|
ASN1CodecBER.ENC_LUNDEF = False
|
|
ASN1CodecBER.ENC_BOOLTRUE = 0xff
|
|
ASN1CodecBER.ENC_REALNR = 3
|
|
ASN1CodecBER.ENC_BSTR_FRAG = 0
|
|
ASN1CodecBER.ENC_OSTR_FRAG = 0
|
|
ASN1CodecBER.ENC_TIME_CANON = True
|
|
ASN1CodecBER.ENC_DEF_CANON = True
|
|
ret = self.to_ber_ws(val)
|
|
_restore_ber_params()
|
|
return ret
|
|
|
|
###
|
|
# convert internal value to ASN.1 GSER encoding
|
|
###
|
|
# TODO
|
|
|
|
def from_gser(self, char):
|
|
raise(ASN1NotSuppErr(self.fullname()))
|
|
|
|
def to_gser(self, buf, val=None):
|
|
raise(ASN1NotSuppErr(self.fullname()))
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 JER encoding
|
|
###
|
|
|
|
if _with_json:
|
|
|
|
def _from_jval(self, val):
|
|
raise(ASN1NotSuppErr(self.fullname()))
|
|
|
|
def from_jer(self, txt):
|
|
try:
|
|
val = JsonDec.decode(txt)
|
|
except JSONDecodeError as err:
|
|
raise(ASN1JERDecodeErr('{0}: invalid json, {1}'\
|
|
.format(self.fullname(), err)))
|
|
self._from_jval(val)
|
|
if self._SAFE_BND:
|
|
self._safechk_bnd(self._val)
|
|
|
|
def _to_jval(self):
|
|
raise(ASN1NotSuppErr(self.fullname()))
|
|
|
|
def to_jer(self, val=None):
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
val = self._to_jval()
|
|
return JsonEnc.encode(val)
|
|
else:
|
|
return None
|
|
|
|
# align API with pycrate_core
|
|
to_json = to_jer
|
|
from_json = from_jer
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 OER/COER encoding
|
|
###
|
|
|
|
def _from_oer(self, char):
|
|
raise (ASN1NotSuppErr(self.fullname()))
|
|
|
|
def _from_oer_ws(self, char):
|
|
raise (ASN1NotSuppErr(self.fullname()))
|
|
|
|
def _to_oer(self):
|
|
raise (ASN1NotSuppErr(self.fullname()))
|
|
|
|
def _to_oer_ws(self):
|
|
raise (ASN1NotSuppErr(self.fullname()))
|
|
|
|
def from_oer(self, buf):
|
|
# ASN1CodecOER.CANONICAL = False
|
|
if isinstance(buf, bytes_types):
|
|
char = Charpy(buf)
|
|
else:
|
|
char = buf
|
|
#
|
|
self._from_oer(char)
|
|
#
|
|
if self._SAFE_BND:
|
|
self._safechk_bnd(self._val)
|
|
|
|
def from_oer_ws(self, buf):
|
|
# ASN1CodecOER.CANONICAL = False
|
|
if isinstance(buf, bytes_types):
|
|
char = Charpy(buf)
|
|
else:
|
|
char = buf
|
|
#
|
|
self._from_oer_ws(char)
|
|
#
|
|
if self._SAFE_BND:
|
|
self._safechk_bnd(self._val)
|
|
|
|
def to_oer(self, val=None):
|
|
ASN1CodecOER.CANONICAL = False
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
ret = pack_val(*self._to_oer())[0]
|
|
if ret:
|
|
return ret
|
|
else:
|
|
return b'\0'
|
|
else:
|
|
return None
|
|
|
|
def to_oer_ws(self, val=None):
|
|
ASN1CodecOER.CANONICAL = False
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
_struct = self._to_oer_ws()
|
|
ret = _struct.to_bytes()
|
|
if ret:
|
|
return ret
|
|
else:
|
|
return b'\0'
|
|
else:
|
|
return None
|
|
|
|
def from_coer(self, buf):
|
|
self.from_oer(buf)
|
|
|
|
def from_coer_ws(self, buf):
|
|
self.from_oer_ws(buf)
|
|
|
|
def to_coer(self, val=None):
|
|
ASN1CodecOER.CANONICAL = True
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
ret = pack_val(*self._to_oer())[0]
|
|
if ret:
|
|
return ret
|
|
else:
|
|
return b'\0'
|
|
else:
|
|
return None
|
|
|
|
def to_coer_ws(self, val=None):
|
|
ASN1CodecOER.CANONICAL = True
|
|
if val is not None:
|
|
self.set_val(val)
|
|
if self._val is not None:
|
|
_struct = self._to_oer_ws()
|
|
ret = _struct.to_bytes()
|
|
if ret:
|
|
return ret
|
|
else:
|
|
return b'\0'
|
|
else:
|
|
return None
|
|
|
|
|
|
def _save_ber_params():
|
|
global __ber_enc_llong
|
|
global __ber_enc_lundef
|
|
global __ber_enc_booltrue
|
|
global __ber_enc_realnr
|
|
global __ber_enc_bstr_frag
|
|
global __ber_enc_ostr_frag
|
|
global __ber_enc_time_canon
|
|
global __ber_enc_def_canon
|
|
__ber_enc_llong = ASN1CodecBER.ENC_LLONG
|
|
__ber_enc_lundef = ASN1CodecBER.ENC_LUNDEF
|
|
__ber_enc_booltrue = ASN1CodecBER.ENC_BOOLTRUE
|
|
__ber_enc_realnr = ASN1CodecBER.ENC_REALNR
|
|
__ber_enc_bstr_frag = ASN1CodecBER.ENC_BSTR_FRAG
|
|
__ber_enc_ostr_frag = ASN1CodecBER.ENC_OSTR_FRAG
|
|
__ber_enc_time_canon = ASN1CodecBER.ENC_TIME_CANON
|
|
__ber_enc_def_canon = ASN1CodecBER.ENC_DEF_CANON
|
|
|
|
def _restore_ber_params():
|
|
global __ber_enc_llong
|
|
global __ber_enc_lundef
|
|
global __ber_enc_booltrue
|
|
global __ber_enc_realnr
|
|
global __ber_enc_bstr_frag
|
|
global __ber_enc_ostr_frag
|
|
global __ber_enc_time_canon
|
|
global __ber_enc_def_canon
|
|
ASN1CodecBER.ENC_LLONG = __ber_enc_llong
|
|
ASN1CodecBER.ENC_LUNDEF = __ber_enc_lundef
|
|
ASN1CodecBER.ENC_BOOLTRUE = __ber_enc_booltrue
|
|
ASN1CodecBER.ENC_REALNR = __ber_enc_realnr
|
|
ASN1CodecBER.ENC_BSTR_FRAG = __ber_enc_bstr_frag
|
|
ASN1CodecBER.ENC_OSTR_FRAG = __ber_enc_ostr_frag
|
|
ASN1CodecBER.ENC_TIME_CANON = __ber_enc_time_canon
|
|
ASN1CodecBER.ENC_DEF_CANON = __ber_enc_def_canon
|
|
|