869 lines
35 KiB
Python
869 lines
35 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_ext.py
|
|
# * Created : 2017-01-31
|
|
# * Authors : Benoit Michau
|
|
# *--------------------------------------------------------
|
|
#*/
|
|
|
|
from .utils import *
|
|
from .err import *
|
|
from .dictobj import *
|
|
from .glob import *
|
|
from .refobj import *
|
|
from .setobj import *
|
|
from .asnobj import *
|
|
from .codecs import *
|
|
from .codecs import *
|
|
from .codecs import _with_json
|
|
from .asnobj_basic import *
|
|
from .asnobj_str import *
|
|
from .asnobj_construct import *
|
|
from .asnobj_construct import _CONSTRUCT
|
|
|
|
|
|
_ASN1ObjBasicLUT = {
|
|
TYPE_NULL : NULL,
|
|
TYPE_BOOL : BOOL,
|
|
TYPE_INT : INT,
|
|
TYPE_REAL : REAL,
|
|
TYPE_ENUM : ENUM,
|
|
TYPE_BIT_STR : BIT_STR,
|
|
TYPE_OCT_STR : OCT_STR,
|
|
TYPE_OID : OID,
|
|
TYPE_REL_OID : REL_OID,
|
|
TYPE_STR_IA5 : STR_IA5,
|
|
TYPE_STR_PRINT : STR_PRINT,
|
|
TYPE_STR_NUM : STR_NUM,
|
|
TYPE_STR_VIS : STR_VIS,
|
|
TYPE_STR_BMP : STR_BMP,
|
|
TYPE_STR_UTF8 : STR_UTF8,
|
|
TYPE_STR_ISO646 : STR_ISO646,
|
|
TYPE_STR_TELE : STR_TELE,
|
|
TYPE_STR_VID : STR_VID,
|
|
TYPE_STR_GRAPH : STR_GRAPH,
|
|
TYPE_STR_T61 : STR_T61,
|
|
TYPE_STR_GENE : STR_GENE,
|
|
TYPE_STR_UNIV : STR_UNIV,
|
|
TYPE_OBJ_DESC : OBJ_DESC,
|
|
TYPE_TIME_GEN : TIME_GEN,
|
|
TYPE_TIME_UTC : TIME_UTC
|
|
}
|
|
|
|
|
|
class OPEN(ASN1Obj):
|
|
__doc__ = """
|
|
ASN.1 open type object,
|
|
corresponds to reference to a CLASS field which has no defined type
|
|
|
|
This is in general associated with a table constraint which can be looked-up
|
|
to provide a defined type according to the encoding / decoding context
|
|
|
|
Single value: Python 2-tuple
|
|
the 1st item corresponds to a reference to another ASN.1 object, it can be:
|
|
- a str starting with '_unk_$ind' for unknown content, the 2nd item will then be some bytes
|
|
$ind is a list of digits, used as a dummy index
|
|
- a str corresponding to an ASN.1 native basic type (TYPE_*, but not constructed one) or
|
|
a typeref taken from the constraint of self
|
|
- a 2-tuple (module_name, object_name) corresponding to any user-defined ASN.1 object
|
|
- an ASN1Obj instance
|
|
and the 2nd item corresponds to a single value compliant to the object
|
|
referenced in the 1st item
|
|
|
|
%s
|
|
""" % ASN1Obj_docstring
|
|
|
|
TYPE = TYPE_OPEN
|
|
TAG = None
|
|
|
|
# this enables object's table constraint lookup for OPEN types when decoding it
|
|
_TAB_LUT = True
|
|
|
|
_ASN_RE = re.compile('(?:\'([\s01]{0,})\'B)|(?:\'([\s0-9A-F]{0,})\'H)')
|
|
|
|
def _get_val_obj(self, ref):
|
|
const_tr = self._get_const_tr()
|
|
if isinstance(ref, str_types):
|
|
if ref in const_tr:
|
|
return const_tr[ref]
|
|
elif ref in _ASN1ObjBasicLUT:
|
|
return _ASN1ObjBasicLUT[ref]()
|
|
else:
|
|
raise(ASN1ObjErr('{0}: invalid object reference, {1!r}'\
|
|
.format(self.fullname(), ref)))
|
|
elif isinstance(ref, (tuple, list)):
|
|
if ref in const_tr:
|
|
return const_tr[ref]
|
|
else:
|
|
try:
|
|
return GLOBAL.MOD[ref[0]][ref[1]]
|
|
except Exception:
|
|
raise(ASN1ObjErr('{0}: invalid object reference, {1!r}'\
|
|
.format(self.fullname(), ref)))
|
|
else:
|
|
raise(ASN1ObjErr('{0}: invalid object reference, {1!r}'\
|
|
.format(self.fullname(), ref)))
|
|
|
|
def _get_const_tr(self):
|
|
if hasattr(self, '__const_tr__'):
|
|
return self.__const_tr__
|
|
else:
|
|
const_tr = {}
|
|
if self._const_val:
|
|
# collect all types in the constraint value
|
|
for C in self._const_val.root:
|
|
if C._typeref is not None:
|
|
# put both complete module ref, and obj-only ref
|
|
const_tr[C._typeref.called] = C
|
|
const_tr[C._typeref.called[1]] = C
|
|
else:
|
|
const_tr[C.TYPE] = C
|
|
if self._const_val.ext:
|
|
for C in self._const_val.ext:
|
|
if C._typeref is not None:
|
|
const_tr[C._typeref.calld] = C
|
|
const_tr[C._typeref.called[1]] = C
|
|
else:
|
|
const_tr[C.TYPE] = C
|
|
if self._TAB_LUT and self._const_tab and self._const_tab_at:
|
|
# collect all types from the table constraint
|
|
assert( hasattr(self, '_const_tab_id') )
|
|
for O in self._const_tab(self._const_tab_id)[::-1]:
|
|
if O._typeref is not None:
|
|
# put both complete module ref, and obj-only ref
|
|
const_tr[O._typeref.called] = O
|
|
const_tr[O._typeref.called[1]] = O
|
|
else:
|
|
const_tr[O.TYPE] = O
|
|
self.__const_tr__ = const_tr
|
|
return const_tr
|
|
|
|
def _safechk_val(self, val):
|
|
if isinstance(val, tuple) and len(val) == 2:
|
|
if isinstance(val[0], ASN1Obj):
|
|
val[0]._safechk_val(val[1])
|
|
elif isinstance(val[0], str_types):
|
|
if re.match('_unk_[0-9]{1,}', val[0]):
|
|
if not isinstance(val[1], bytes_types):
|
|
raise(ASN1ObjErr('{0}: invalid value, {1!r}'.format(self.fullname(), val)))
|
|
else:
|
|
self._get_val_obj(val[0])._safechk_val(val[1])
|
|
elif isinstance(val[0], tuple) and len(val[0]) == 2:
|
|
self._get_val_obj(val[0])._safechk_val(val[1])
|
|
else:
|
|
raise(ASN1ObjErr('{0}: invalid value, {1!r}'.format(self.fullname(), val)))
|
|
else:
|
|
raise(ASN1ObjErr('{0}: invalid value, {1!r}'.format(self.fullname(), val)))
|
|
|
|
def _safechk_bnd(self, val):
|
|
if isinstance(val[0], ASN1Obj):
|
|
val[0]._safechk_bnd(val[1])
|
|
elif val[0][:5] != '_unk_':
|
|
self._get_val_obj(val[0])._safechk_bnd(val[1])
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 syntax
|
|
###
|
|
|
|
def _from_asn1(self, txt):
|
|
m = self._ASN_RE.match(txt)
|
|
if m:
|
|
grp = m.groups()
|
|
if hasattr(self, '_val_tag'):
|
|
ident = '_unk_%i%i%i' % self._val_tag
|
|
else:
|
|
ident = '_unk_004'
|
|
if grp[0] is not None:
|
|
# BSTRING
|
|
bs = re.subn('\s{1,}', '', grp[0])[0]
|
|
self._val = (ident, uint_to_bytes(int(bs, 2), len(bs)))
|
|
else:
|
|
# HSTRING
|
|
hs = re.subn('\s{1,}', '', grp[1])[0]
|
|
if len(hs)%2:
|
|
self._val = (ident, unhexlify(hs + '0'))
|
|
else:
|
|
self._val = (ident, unhexlify(hs))
|
|
return txt[m.end():].strip()
|
|
else:
|
|
# we must pick-up a type from a defined constraint
|
|
# TODO: must implement the Module.value notation to compare to tuples
|
|
# into const_tr, in addition to simple object name
|
|
const_tr_keys = [name for name in self._get_const_tr() if isinstance(name, str_types)]
|
|
m = re.match('\s{0,}:|'.join(const_tr_keys) + '\s{0,}:', txt)
|
|
if m is not None:
|
|
ident = m.group().split(':')[0].strip()
|
|
txt = txt[m.end():].strip()
|
|
Obj = self._get_const_tr()[ident]
|
|
txt = Obj._from_asn1(txt)
|
|
if Obj._typeref is not None:
|
|
self._val = (Obj._typeref.called[1], Obj._val)
|
|
else:
|
|
self._val = (Obj.TYPE, Obj._val)
|
|
return txt
|
|
else:
|
|
ASN1NotSuppErr('{0}: reference parsing unsupported'.format(self.fullname()))
|
|
|
|
def _to_asn1(self):
|
|
if isinstance(self._val[0], str_types):
|
|
if self._val[0][:5] == '_unk_':
|
|
# HSTRING
|
|
if python_version >= 3:
|
|
return '\'%s\'H' % hexlify(self._val[1]).decode('ascii').upper()
|
|
else:
|
|
return '\'%s\'H' % hexlify(self._val[1]).upper()
|
|
else:
|
|
ident = self._val[0]
|
|
Obj = self._get_val_obj(self._val[0])
|
|
elif isinstance(self._val[0], tuple):
|
|
ident = '.'.join(self._val[0])
|
|
Obj = self._get_val_obj(self._val[0])
|
|
else:
|
|
# self._val[0] is an ASN1Obj instance
|
|
ident = '%s.%s' % (self._val[0]._mod, self._val[0]._name)
|
|
Obj = self._val[0]
|
|
Obj._val = self._val[1]
|
|
return '%s: %s' % (ident, Obj.to_asn1())
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 PER encoding
|
|
###
|
|
|
|
def _from_per_ws(self, char):
|
|
# try to get a defined object from a table constraint
|
|
if self._TAB_LUT and self._const_tab and self._const_tab_at:
|
|
const_obj_type, const_obj = self._get_tab_obj()
|
|
if const_obj_type == CLASET_NONE:
|
|
if not self._SILENT:
|
|
asnlog('OPEN._from_per_ws: %s, unable to retrieve a table-looked up object'\
|
|
% (self.fullname()))
|
|
Obj = None
|
|
elif const_obj_type == CLASET_UNIQ:
|
|
Obj = const_obj
|
|
else:
|
|
# const_obj_type == CLASET_MULT
|
|
# with PER, no tag to select a given object
|
|
Obj = const_obj[0]
|
|
else:
|
|
# TODO: another way to provide a (set of) potential defined object(s)
|
|
# is to look into value constraint self._const_val
|
|
# if we have multiple, then we would have to bruteforce the decoding
|
|
# until a correct one is found !!!
|
|
Obj = None
|
|
#
|
|
if Obj is None:
|
|
if self._const_val:
|
|
asnlog('OPEN._from_per_ws: %s, potential type constraint(s) available but unused'\
|
|
% self.fullname())
|
|
val, GEN = ASN1CodecPER.decode_unconst_open_ws(char, wrapped=None)
|
|
assert( isinstance(val, bytes_types) )
|
|
self._val = ('_unk_004', val)
|
|
else:
|
|
if Obj._typeref is not None:
|
|
val, GEN = ASN1CodecPER.decode_unconst_open_ws(char, wrapped=Obj._tr)
|
|
self._val = (Obj._typeref.called[1], val)
|
|
else:
|
|
val, GEN = ASN1CodecPER.decode_unconst_open_ws(char, wrapped=Obj)
|
|
self._val = (Obj.TYPE, val)
|
|
self._struct = Envelope(self._name, GEN=tuple(GEN))
|
|
return
|
|
|
|
def _from_per(self, char):
|
|
# try to get a defined object from a table constraint
|
|
if self._TAB_LUT and self._const_tab and self._const_tab_at:
|
|
const_obj_type, const_obj = self._get_tab_obj()
|
|
if const_obj_type == CLASET_NONE:
|
|
if not self._SILENT:
|
|
asnlog('OPEN._from_per: %s, unable to retrieve a table-looked up object'\
|
|
% (self.fullname()))
|
|
Obj = None
|
|
elif const_obj_type == CLASET_UNIQ:
|
|
Obj = const_obj
|
|
else:
|
|
# const_obj_type == CLASET_MULT
|
|
# with PER, no tag to select a given object
|
|
Obj = const_obj[0]
|
|
else:
|
|
# TODO: another way to provide a (set of) potential defined object(s)
|
|
# is to look into value constraint self._const_val
|
|
# if we have multiple, then we would have to bruteforce the decoding
|
|
# until a correct one is found !!!
|
|
Obj = None
|
|
#
|
|
val = ASN1CodecPER.decode_unconst_open(char, wrapped=Obj)
|
|
if Obj is None:
|
|
if self._const_val:
|
|
asnlog('OPEN._from_per: %s, potential type constraint(s) available but unused'\
|
|
% self.fullname())
|
|
assert( isinstance(val, bytes_types) )
|
|
self._val = ('_unk_004', val)
|
|
else:
|
|
if Obj._typeref is not None:
|
|
self._val = (Obj._typeref.called[1], val)
|
|
else:
|
|
self._val = (Obj.TYPE, val)
|
|
return
|
|
|
|
def _to_per_ws(self):
|
|
if isinstance(self._val[0], ASN1Obj):
|
|
Obj = self._val[0]
|
|
else:
|
|
# isinstance(self._val[0], str_types)
|
|
if self._val[0][:5] == '_unk_':
|
|
GEN = ASN1CodecPER.encode_unconst_buf_ws(self._val[1])
|
|
self._struct = Envelope(self._name, GEN=tuple(GEN))
|
|
return self._struct
|
|
Obj = self._get_val_obj(self._val[0])
|
|
Obj._val = self._val[1]
|
|
GEN = ASN1CodecPER.encode_unconst_open_ws(Obj)
|
|
self._struct = Envelope(self._name, GEN=tuple(GEN))
|
|
return self._struct
|
|
|
|
def _to_per(self):
|
|
if isinstance(self._val[0], ASN1Obj):
|
|
Obj = self._val[0]
|
|
else:
|
|
# isinstance(self._val[0], str_types)
|
|
if self._val[0][:5] == '_unk_':
|
|
return ASN1CodecPER.encode_unconst_buf(self._val[1])
|
|
Obj = self._get_val_obj(self._val[0])
|
|
Obj._val = self._val[1]
|
|
ret = ASN1CodecPER.encode_unconst_open(Obj)
|
|
return ret
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 BER encoding
|
|
###
|
|
|
|
def _decode_ber_cont_ws(self, char, tlv):
|
|
# tlv: list of list of tag, length and value corresponding to the CHOICE
|
|
if not isinstance(tlv, list):
|
|
raise(ASN1BERDecodeErr('{0}: invalid OPEN / ANY primitive structure'\
|
|
.format(self.fullname())))
|
|
# select the inner encoding
|
|
tlv = tlv[0]
|
|
Tag, cl, pc, tval, Len, lval = tlv[0:6]
|
|
tag, Objs, obj_mult = (cl, tval), [], False
|
|
# try to get a defined object from a table constraint
|
|
if self._TAB_LUT and self._const_tab and self._const_tab_at:
|
|
const_obj_type, const_obj = self._get_tab_obj()
|
|
if const_obj_type == CLASET_NONE:
|
|
if not self._SILENT:
|
|
asnlog('OPEN._decode_ber_cont_ws: %s, unable to retrieve a table-looked up object'\
|
|
% self.fullname())
|
|
elif const_obj_type == CLASET_UNIQ:
|
|
Objs = [const_obj]
|
|
else:
|
|
# const_obj_type == CLASET_MULT
|
|
obj_mult = True
|
|
Objs = get_obj_by_tag(self, tag, const_obj)
|
|
#
|
|
elif self._const_val is not None:
|
|
# another way to provide a (set of) potential defined object(s)
|
|
# is to look into value constraint self._const_val
|
|
# we must select the right one according to the decoded tag
|
|
Objs = get_obj_by_tag(self, tag)
|
|
#
|
|
elif hasattr(self, '_defby') and self._defby is not None:
|
|
# TODO: 3rd way to specify the potential defined object
|
|
# is to use a DEFINED BY specification (this is old-school)
|
|
if not self._SILENT:
|
|
asnlog('OPEN._decode_ber_cont_ws: %s, DEFINED BY lookup not supported' % self.fullname())
|
|
#
|
|
decoded = False
|
|
if Objs:
|
|
# we found at least one (or more) defined object
|
|
char_cur, char_lb = char._cur, char._len_bit
|
|
for Obj in Objs:
|
|
try:
|
|
Obj._from_ber_ws(char, [tlv])
|
|
except Exception:
|
|
# decoding failed
|
|
char._cur, char._len_bit = char_cur, char_lb
|
|
else:
|
|
# set value
|
|
if Obj._typeref is not None:
|
|
if obj_mult:
|
|
self._val = (Obj._typeref.called, Obj._val)
|
|
else:
|
|
self._val = (Obj._typeref.called[1], Obj._val)
|
|
else:
|
|
self._val = (Obj.TYPE, Obj._val)
|
|
V = Obj._struct
|
|
decoded = True
|
|
break
|
|
if not decoded and not self._SILENT:
|
|
asnlog('OPEN._decode_ber_cont_ws: %s, decoding failed for all possible objects'\
|
|
% self.fullname())
|
|
#
|
|
if not decoded:
|
|
# we did not find a defined object, or failed to decode it
|
|
# hence we decode this as a simple buffer
|
|
# with BER, we need to absolutely keep track of the decoded tag if
|
|
# we want to be able to re-encode it
|
|
if pc == 1:
|
|
# constructed object
|
|
#
|
|
# char._cur will be updated in scan_tlv()
|
|
# but we also need to extend char._len_bit according to tlv in some way...
|
|
# however, the content is constructed and we don't have straight boundaries
|
|
# as for the primitive case below, hence we extend the char buffer to its
|
|
# maximum
|
|
char._len_bit = 8*len(char._buf)
|
|
val = ASN1CodecBER.scan_tlv_ws(char, tlv)
|
|
self._val_tag = (cl, pc, tval)
|
|
ident = '_unk_%i%i%i' % self._val_tag
|
|
self._val = (ident, val)
|
|
V = Envelope('V', GEN=(Tag, Len, Buf(ident, val=val, bl=8*len(val), rep=REPR_HEX)))
|
|
elif lval >= 0:
|
|
# primitive object
|
|
char._cur, char._len_bit = tlv[6][0], tlv[6][1]
|
|
Val = Buf('_buf_', bl=8*lval, rep=REPR_HEX)
|
|
Val._from_char(char)
|
|
self._val_tag = (cl, pc, tval)
|
|
self._val = ('_unk_%i%i%i' % self._val_tag, Val.to_bytes())
|
|
V = Envelope('V', GEN=(Tag, Len, Val))
|
|
else:
|
|
raise(ASN1BERDecodeErr('{0}: invalid OPEN / ANY tag and length, {1!r}, {2!r}'\
|
|
.format(self.fullname(), (cl, pc, tval), lval)))
|
|
#
|
|
return V
|
|
|
|
def _decode_ber_cont(self, char, tlv):
|
|
# tlv: list of list of tag, length and value corresponding to the CHOICE
|
|
if not isinstance(tlv, list):
|
|
raise(ASN1BERDecodeErr('{0}: invalid OPEN / ANY primitive structure'\
|
|
.format(self.fullname())))
|
|
# select the inner encoding
|
|
tlv = tlv[0]
|
|
cl, pc, tval, lval = tlv[0:4]
|
|
tag, Objs, obj_mult = (cl, tval), [], False
|
|
# try to get a defined object from a table constraint
|
|
if self._TAB_LUT and self._const_tab and self._const_tab_at:
|
|
const_obj_type, const_obj = self._get_tab_obj()
|
|
if const_obj_type == CLASET_NONE:
|
|
if not self._SILENT:
|
|
asnlog('OPEN._decode_ber_cont: %s, unable to retrieve a table-looked up object'\
|
|
% self.fullname())
|
|
elif const_obj_type == CLASET_UNIQ:
|
|
Objs = [const_obj]
|
|
else:
|
|
# const_obj_type == CLASET_MULT
|
|
obj_mult = True
|
|
Objs = get_obj_by_tag(self, tag, const_obj)
|
|
#
|
|
elif self._const_val is not None:
|
|
# another way to provide a (set of) potential defined object(s)
|
|
# is to look into value constraint self._const_val
|
|
# we must select the right one according to the decoded tag
|
|
Objs = get_obj_by_tag(self, tag)
|
|
#
|
|
elif hasattr(self, '_defby') and self._defby is not None:
|
|
# TODO: 3rd way to specify the potential defined object
|
|
# is to use a DEFINED BY specification (this is old-school)
|
|
if not self._SILENT:
|
|
asnlog('OPEN._decode_ber_cont: %s, DEFINED BY lookup not supported' % self.fullname())
|
|
#
|
|
decoded = False
|
|
if Objs:
|
|
# we found at least one (or more) defined object
|
|
char_cur, char_lb = char._cur, char._len_bit
|
|
for Obj in Objs:
|
|
try:
|
|
Obj._from_ber(char, [tlv])
|
|
except Exception:
|
|
char._cur, char._len_bit = char_cur, char_lb
|
|
else:
|
|
# set value
|
|
if Obj._typeref is not None:
|
|
if obj_mult:
|
|
self._val = (Obj._typeref.called, Obj._val)
|
|
else:
|
|
self._val = (Obj._typeref.called[1], Obj._val)
|
|
else:
|
|
self._val = (Obj.TYPE, Obj._val)
|
|
decoded = True
|
|
break
|
|
if not decoded and not self._SILENT:
|
|
asnlog('OPEN._decode_ber_cont: %s, decoding failed for all possible objects'\
|
|
% self.fullname())
|
|
#
|
|
if not decoded:
|
|
# we did not find a defined object, or failed to decode it
|
|
# hence we decode this as a simple buffer
|
|
# with BER, we need to absolutely keep track of the decoded tag if
|
|
# we want to be able to re-encode it
|
|
if pc == 1:
|
|
# constructed object
|
|
#
|
|
# char._cur will be updated in scan_tlv()
|
|
# but we also need to extend char._len_bit according to tlv in some way...
|
|
# however, the content is constructed and we don't have straight boundaries
|
|
# as for the primitive case below, hence we extend the char buffer to its
|
|
# maximum
|
|
char._len_bit = 8*len(char._buf)
|
|
#
|
|
self._val_tag = (cl, pc, tval)
|
|
self._val = ('_unk_%i%i%i' % self._val_tag, ASN1CodecBER.scan_tlv(char, tlv))
|
|
elif lval >= 0:
|
|
# primitive object
|
|
char._cur, char._len_bit = tlv[4][0], tlv[4][1]
|
|
self._val_tag = (cl, pc, tval)
|
|
self._val = ('_unk_%i%i%i' % self._val_tag, char.get_bytes(8*lval))
|
|
else:
|
|
raise(ASN1BERDecodeErr('{0}: invalid OPEN / ANY tag and length, {1!r}, {2!r}'\
|
|
.format(self.fullname(), (cl, pc, tval), lval)))
|
|
|
|
def _encode_ber_cont_ws(self):
|
|
if isinstance(self._val[0], ASN1Obj):
|
|
Obj = self._val[0]
|
|
Obj._val = self._val[1]
|
|
TLV = Obj._to_ber_ws()
|
|
else:
|
|
# isinstance(self._val[0], str_bytes)
|
|
if self._val[0][:5] == '_unk_':
|
|
try:
|
|
cl, pc, tval = int(self._val[0][5:6]), \
|
|
int(self._val[0][6:7]), \
|
|
int(self._val[0][7:])
|
|
except Exception:
|
|
cl, pc, tval = 0, 0, 4
|
|
TLV = ASN1CodecBER.encode_tlv_ws(cl, tval, self._val[1], pc=pc)
|
|
else:
|
|
Obj = self._get_val_obj(self._val[0])
|
|
Obj._val = self._val[1]
|
|
TLV = Obj._to_ber_ws()
|
|
if ASN1CodecBER.ENC_LUNDEF:
|
|
return 1, -1, TLV
|
|
else:
|
|
lval = TLV.get_bl() >> 3
|
|
return 1, lval, TLV
|
|
|
|
def _encode_ber_cont(self):
|
|
if isinstance(self._val[0], ASN1Obj):
|
|
Obj = self._val[0]
|
|
Obj._val = self._val[1]
|
|
TLV = Obj._to_ber()
|
|
else:
|
|
# isinstance(self._val[0], str_bytes)
|
|
if self._val[0][:5] == '_unk_':
|
|
try:
|
|
cl, pc, tval = int(self._val[0][5:6]), \
|
|
int(self._val[0][6:7]), \
|
|
int(self._val[0][7:])
|
|
except Exception:
|
|
cl, pc, tval = 0, 0, 4
|
|
TLV = ASN1CodecBER.encode_tlv(cl, tval, self._val[1], pc=pc)
|
|
else:
|
|
Obj = self._get_val_obj(self._val[0])
|
|
Obj._val = self._val[1]
|
|
TLV = Obj._to_ber()
|
|
if ASN1CodecBER.ENC_LUNDEF:
|
|
return 1, -1, TLV
|
|
else:
|
|
lval = sum([f[2] for f in TLV]) >> 3
|
|
return 1, lval, TLV
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 JER encoding
|
|
###
|
|
|
|
if _with_json:
|
|
|
|
def _from_jval(self, val):
|
|
# same as PER decodeing
|
|
# try to get a defined object from a table constraint
|
|
if self._TAB_LUT and self._const_tab and self._const_tab_at:
|
|
const_obj_type, const_obj = self._get_tab_obj()
|
|
if const_obj_type == CLASET_NONE:
|
|
if not self._SILENT:
|
|
asnlog('OPEN._from_jval: %s, unable to retrieve a table-looked up object, %s'\
|
|
% (self.fullname(), err))
|
|
Obj = None
|
|
elif const_obj_type == CLASET_UNIQ:
|
|
Obj = const_obj
|
|
else:
|
|
# const_obj_type == CLASET_MULT
|
|
Obj = const_obj[0]
|
|
else:
|
|
Obj = None
|
|
#
|
|
if Obj is None:
|
|
if isinstance(val, str_types):
|
|
try:
|
|
self._val = ('_unk_004', unhexlify(val))
|
|
except TypeError:
|
|
raise(ASN1JERDecodeErr('{0}: invalid json value, {1!r}'\
|
|
.format(self.fullname(), val)))
|
|
else:
|
|
#raise(ASN1JERDecodeErr('{0}: unknown wrapped object, {1!r}'\
|
|
# .format(self.fullname(), val)))
|
|
if not self._SILENT:
|
|
asnlog('OPEN._from_jval: %s, unknown value type, %r' % (self.fullname(), val))
|
|
self._val = ('_unk_004', val)
|
|
else:
|
|
Obj._from_jval(val)
|
|
if Obj._typeref is not None:
|
|
self._val = (Obj._typeref.called[1], Obj._val)
|
|
else:
|
|
self._val = (Obj.TYPE, Obj._val)
|
|
|
|
def _to_jval(self):
|
|
if isinstance(self._val[0], ASN1Obj):
|
|
Obj = self._val[0]
|
|
else:
|
|
if isinstance(self._val[0], str_types) and self._val[0][:5] == '_unk_':
|
|
if isinstance(self._val[1], bytes_types):
|
|
return hexlify(self._val[1]).decode()
|
|
else:
|
|
return self._val[1]
|
|
Obj = self._get_val_obj(self._val[0])
|
|
Obj._val = self._val[1]
|
|
return Obj._to_jval()
|
|
|
|
###
|
|
# conversion between internal value and ASN.1 OER/COER encoding
|
|
###
|
|
|
|
def _from_oer(self, char):
|
|
# try to get a defined object from a table constraint
|
|
if self._TAB_LUT and self._const_tab and self._const_tab_at:
|
|
const_obj_type, const_obj = self._get_tab_obj()
|
|
if const_obj_type == CLASET_NONE:
|
|
if not self._SILENT:
|
|
asnlog('OPEN._from_oer: %s, unable to retrieve a table-looked up object' \
|
|
% (self.fullname()))
|
|
Obj = None
|
|
elif const_obj_type == CLASET_UNIQ:
|
|
Obj = const_obj
|
|
else:
|
|
# const_obj_type == CLASET_MULT
|
|
# with PER, no tag to select a given object
|
|
Obj = const_obj[0]
|
|
else:
|
|
# TODO: another way to provide a (set of) potential defined object(s)
|
|
# is to look into value constraint self._const_val
|
|
# if we have multiple, then we would have to bruteforce the decoding
|
|
# until a correct one is found !!!
|
|
Obj = None
|
|
#
|
|
val_bytes = ASN1CodecOER.decode_open_type(char)
|
|
val = Obj.from_oer(val_bytes) if (Obj is not None) else val_bytes
|
|
|
|
if Obj is None:
|
|
if self._const_val:
|
|
asnlog('OPEN._from_per: %s, potential type constraint(s) available but unused' \
|
|
% self.fullname())
|
|
assert( isinstance(val, bytes_types) )
|
|
self._val = ('_unk_004', val)
|
|
else:
|
|
if Obj._typeref is not None:
|
|
self._val = (Obj._typeref.called[1], val)
|
|
else:
|
|
self._val = (Obj.TYPE, val)
|
|
return
|
|
|
|
def _from_oer_ws(self, char):
|
|
# try to get a defined object from a table constraint
|
|
if self._TAB_LUT and self._const_tab and self._const_tab_at:
|
|
const_obj_type, const_obj = self._get_tab_obj()
|
|
if const_obj_type == CLASET_NONE:
|
|
if not self._SILENT:
|
|
asnlog('OPEN._from_per_ws: %s, unable to retrieve a table-looked up object' \
|
|
% (self.fullname()))
|
|
Obj = None
|
|
elif const_obj_type == CLASET_UNIQ:
|
|
Obj = const_obj
|
|
else:
|
|
# const_obj_type == CLASET_MULT
|
|
# with PER, no tag to select a given object
|
|
Obj = const_obj[0]
|
|
else:
|
|
# TODO: another way to provide a (set of) potential defined object(s)
|
|
# is to look into value constraint self._const_val
|
|
# if we have multiple, then we would have to bruteforce the decoding
|
|
# until a correct one is found !!!
|
|
Obj = None
|
|
#
|
|
val_bytes, GEN = ASN1CodecOER.decode_open_type_ws(char)
|
|
if Obj is None:
|
|
if self._const_val:
|
|
asnlog('OPEN._from_per_ws: %s, potential type constraint(s) available but unused' \
|
|
% self.fullname())
|
|
self._val = ('_unk_004', val_bytes)
|
|
else:
|
|
val = Obj.from_oer(val_bytes)
|
|
if Obj._typeref is not None:
|
|
self._val = (Obj._typeref.called[1], val)
|
|
else:
|
|
self._val = (Obj.TYPE, val)
|
|
self._struct = Envelope(self._name, GEN=tuple(GEN))
|
|
|
|
def _to_oer(self):
|
|
if isinstance(self._val[0], ASN1Obj):
|
|
Obj = self._val[0]
|
|
else:
|
|
if self._val[0][:5] == '_unk_':
|
|
return ASN1CodecOER.encode_open_type(self._val[1])
|
|
Obj = self._get_val_obj(self._val[0])
|
|
Obj._val = self._val[1]
|
|
return ASN1CodecOER.encode_open_type(Obj.to_oer())
|
|
|
|
def _to_oer_ws(self):
|
|
if isinstance(self._val[0], ASN1Obj):
|
|
Obj = self._val[0]
|
|
else:
|
|
if self._val[0][:5] == '_unk_':
|
|
_gen = ASN1CodecOER.encode_open_type_ws(self._val[1])
|
|
return Envelope(self._name, GEN=(_gen,))
|
|
Obj = self._get_val_obj(self._val[0])
|
|
Obj._val = self._val[1]
|
|
_gen = ASN1CodecOER.encode_open_type_ws(Obj.to_oer())
|
|
self._struct = Envelope(self._name, GEN=(_gen,))
|
|
return self._struct
|
|
|
|
|
|
class ANY(OPEN):
|
|
|
|
TYPE = TYPE_ANY
|
|
TAG = None
|
|
|
|
# ANY is just like OPEN type,
|
|
# with the additional DEFINED BY construction
|
|
|
|
|
|
# TYPE-IDENTIFIER and ABSTRACT-SYNTAX are bothes subtypes of CLASS:
|
|
#
|
|
# TYPE-IDENTIFIER ::= CLASS {
|
|
# &id OBJECT IDENTIFIER UNIQUE,
|
|
# &Type }
|
|
# WITH SYNTAX {&Type IDENTIFIED BY &id}
|
|
#
|
|
# ABSTRACT-SYNTAX ::= CLASS {
|
|
# &id OBJECT IDENTIFIER,
|
|
# &Type,
|
|
# &property BIT STRING {handles-invalid-encodings(0)} DEFAULT {} }
|
|
# WITH SYNTAX { &Type IDENTIFIED BY &id [HAS PROPERTY &property] }
|
|
#
|
|
# they are both generated as is into the _IMPL_ module,
|
|
# when a specification requires it
|
|
|
|
|
|
class EXT(SEQ):
|
|
"""
|
|
ASN.1 context switching type EXTERNAL object
|
|
|
|
associated type:
|
|
[UNIVERSAL 8] IMPLICIT SEQUENCE {
|
|
identification [0] EXPLICIT CHOICE {
|
|
syntaxes [0] SEQUENCE {
|
|
abstract [0] OBJECT IDENTIFIER,
|
|
transfer [1] OBJECT IDENTIFIER
|
|
},
|
|
syntax [1] OBJECT IDENTIFIER,
|
|
presentation-context-id [2] INTEGER,
|
|
context-negotiation [3] SEQUENCE {
|
|
presentation-context-id [0] INTEGER,
|
|
transfer-syntax [1] OBJECT IDENTIFIER
|
|
},
|
|
transfer-syntax [4] OBJECT IDENTIFIER,
|
|
fixed [5] NULL
|
|
},
|
|
data-value-descriptor [1] ObjectDescriptor OPTIONAL,
|
|
data-value [2] OCTET STRING
|
|
} (WITH COMPONENTS {
|
|
...,
|
|
identification (WITH COMPONENTS {
|
|
...,
|
|
syntaxes ABSENT,
|
|
transfer-syntax ABSENT,
|
|
fixed ABSENT })
|
|
})
|
|
"""
|
|
|
|
TYPE = TYPE_EXT
|
|
TAG = 8
|
|
|
|
|
|
class EMB_PDV(SEQ):
|
|
"""
|
|
ASN.1 context switching type EMBEDDED PDV object
|
|
|
|
associated type:
|
|
[UNIVERSAL 11] IMPLICIT SEQUENCE {
|
|
identification [0] EXPLICIT CHOICE {
|
|
syntaxes [0] SEQUENCE {
|
|
abstract [0] OBJECT IDENTIFIER,
|
|
transfer [1] OBJECT IDENTIFIER
|
|
},
|
|
syntax [1] OBJECT IDENTIFIER,
|
|
presentation-context-id [2] INTEGER,
|
|
context-negotiation [3] SEQUENCE {
|
|
presentation-context-id [0] INTEGER,
|
|
transfer-syntax [1] OBJECT IDENTIFIER
|
|
},
|
|
transfer-syntax [4] OBJECT IDENTIFIER,
|
|
fixed [5] NULL
|
|
},
|
|
data-value-descriptor [1] ObjectDescriptor OPTIONAL,
|
|
data-value [2] OCTET STRING
|
|
} (WITH COMPONENTS {
|
|
...,
|
|
data-value-descriptor ABSENT
|
|
})
|
|
"""
|
|
|
|
TYPE = TYPE_EMB_PDV
|
|
TAG = 11
|
|
|
|
|
|
class CHAR_STR(SEQ):
|
|
"""
|
|
ASN.1 context switching type CHARACTER STRING object
|
|
|
|
associated type:
|
|
[UNIVERSAL 29] IMPLICIT SEQUENCE {
|
|
identification [0] EXPLICIT CHOICE {
|
|
syntaxes [0] SEQUENCE {
|
|
abstract [0] OBJECT IDENTIFIER,
|
|
transfer [1] OBJECT IDENTIFIER
|
|
},
|
|
syntax [1] OBJECT IDENTIFIER,
|
|
presentation-context-id [2] INTEGER,
|
|
context-negotiation [3] SEQUENCE {
|
|
presentation-context-id [0] INTEGER,
|
|
transfer-syntax [1] OBJECT IDENTIFIER
|
|
},
|
|
transfer-syntax [4] OBJECT IDENTIFIER,
|
|
fixed [5] NULL
|
|
},
|
|
string-value [1] OCTET STRING
|
|
}
|
|
"""
|
|
|
|
TYPE = TYPE_CHAR_STR
|
|
TAG = 29
|
|
|