pycrate/pycrate_mobile/TS24007.py

421 lines
14 KiB
Python

# -*- coding: UTF-8 -*-
#/**
# * Software Name : pycrate
# * Version : 0.2
# *
# * Copyright 2017. Benoit Michau. ANSSI.
# *
# * This program is free software; you can redistribute it and/or
# * modify it under the terms of the GNU General Public License
# * as published by the Free Software Foundation; either version 2
# * of the License, or (at your option) any later version.
# *
# * This program 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 General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# * 02110-1301, USA.
# *
# *--------------------------------------------------------
# * File Name : pycrate_mobile/TS24007.py
# * Created : 2017-06-08
# * Authors : Benoit Michau
# *--------------------------------------------------------
#*/
#------------------------------------------------------------------------------#
# 3GPP TS 24.007: Mobile radio interface signalling layer 3
# release 13 (d00)
#------------------------------------------------------------------------------#
from binascii import hexlify
from pycrate_core.utils import *
from pycrate_core.elt import Element, Envelope, REPR_RAW, REPR_HEX, REPR_BIN
from pycrate_core.base import *
from pycrate_core.repr import *
from pycrate_csn1.csnobj import CSN1Obj
#------------------------------------------------------------------------------#
# Components of a standard L3 message
# TS 24.007, section 11.2.1
#------------------------------------------------------------------------------#
class Layer3(Envelope):
ENV_SEL_TRANS = False
def __init__(self, *args, **kw):
if 'val' in kw:
val = kw['val']
del kw['val']
else:
val = None
Envelope.__init__(self, *args, **kw)
# build a list of (tag length, tag value) for the optional part
# configure IE set by **kw as non-transparent and set their value
self._opts = []
if val is None:
# go faster by just looking for optional IE
for ie in self._content:
if isinstance(ie, (Type1TV, Type2, Type3TV, Type4TLV, Type6TLVE)):
# optional IE
T = ie[0]
self._opts.append( (T.get_bl(), T(), ie) )
else:
for ie in self._content:
if isinstance(ie, (Type1V, Type3V, Type4LV, Type6LVE)) and \
val and ie._name in val:
# setting value for non-optional IE
if isinstance(val[ie._name], bytes_types):
# setting raw value
ie['V'].set_val(val[ie._name])
else:
# setting embedded IE structure
ie.set_IE(val=val[ie._name])
elif isinstance(ie, (Type1TV, Type3TV, Type4TLV, Type6TLVE)):
# optional IE
T = ie[0]
self._opts.append( (T.get_bl(), T(), ie) )
if val and ie._name in val:
ie._trans = False
if isinstance(val[ie._name], bytes_types):
# setting raw value
ie['V'].set_val(val[ie._name])
else:
# setting embedded IE structure
ie.set_IE(val=val[ie._name])
elif isinstance(ie, Type2):
# optional Tag-only IE
self._opts.append( (8, ie[0](), ie) )
if val and ie._name in val:
ie._trans = False
elif val and ie._name in val:
ie.set_val(val[ie._name])
def reset_opts(self):
"""reset the optional part of the message
"""
[opt[2].set_trans(True) for opt in self._opts]
def get_opts(self):
"""returns the list of optional IE of the message
"""
return [opt[2] for opt in self._opts]
def _from_char(self, char):
# in case some optional IE are set (with transparency enabled)
# they are decoded as much as the char buffer allows it
# 1) decode mandatory part
Envelope._from_char(self, char)
# 2) decode optional part
opts = self._opts[:]
while char.len_bit() >= 8:
T4, T8, dec = char.to_uint(4), char.to_uint(8), False
for i, opt in enumerate(opts):
# check the list of optional IEs in order
# opt[0] is the tag length: 4 or 8
# opt[1] is the tag value: 0 <= T <= 255
if (opt[0] == 4 and opt[1] == T4) or opt[1] == T8:
opt[2]._trans = False
opt[2]._from_char(char)
dec = True
del opts[i]
break
if not dec:
# unknown IEI
char._cur += 8
self._dec_unk_ie(T8, char)
def _dec_unk_ie(self, T8, char):
if T8 & 0x80:
# 1 byte IE
log('%s, _dec_unk_ie: unknown Type2 IE, 0x%x' % (self._name, T8))
self.append( Type2('_T_%i' % T8, val=[T8]) )
else:
# Type4TLV IE
L = char.get_uint(8)
V = char.get_bytes(8*L)
log('%s, _dec_unk_ie: unknown Type4TLV IE, T: 0x%x, V: 0x%s' \
% (self._name, T8, hexlify(V)))
self.append( Type4TLV('_T_%i' % T8, val=[T8, L, V]) )
def repr(self):
# element transparency
if self.get_trans():
trans = ' [transparent]'
else:
trans = ''
# additional description
if self._desc:
desc = ' [%s]' % self._desc
else:
desc = ''
#
return '<%s%s%s : %s>' % \
(self._name, desc, trans, ''.join(map(repr, self._content)))
class Layer3EPS(Layer3):
def _dec_unk_ie(self, T8, char):
if T8 & 0x80:
# 1 byte IE
log('%s, _dec_unk_ie: unknown Type2 IE, 0x%x' % (self._name, T8))
self.append( Type2('_T_%i' % T8, val=[T8]) )
elif T8 & 0x70 == 0x70:
# Type6 TLV IE
L = char.get_uint(16)
V = char.get_bytes(8*L)
log('%s, _dec_unk_ie: unknown Type6TLVE IE, T: 0x%x, V: 0x%s' \
% (self._name, T8, hexlify(V)))
self.append( Type6TLVE('_T_%i' % T8, val=[T8, L, V]) )
else:
# Type4TLV IE
L = char.get_uint(8)
V = char.get_bytes(8*L)
log('%s, _dec_unk_ie: unknown Type4TLV IE, T: 0x%x, V: 0x%s' \
% (self._name, T8, hexlify(V)))
self.append( Type4TLV('_T_%i' % T8, val=[T8, L, V]) )
class IE(Envelope):
# do not represent transparent IE
REPR_TRANS = False
_IE = None
_V = None
def __init__(self, *args, **kw):
if 'IE' in kw:
if isinstance(kw['IE'], (Element, CSN1Obj)):
self._IE = kw['IE'].clone()
elif self._SAFE_STAT:
raise(PycrateErr('IE [__init__]: IE type is {0}, expecting Element'\
.format(type(kw['IE']).__name__)))
Envelope.__init__(self, *args, **kw)
def _from_char(self, char):
Envelope._from_char(self, char)
# in case self._IE is defined, use it to decode char instead of V
if self._IE is not None:
iebl = self[-1].get_bl()
char_cur = char._cur
char_lb = char._len_bit
char._cur -= iebl
char._len_bit = char._cur + iebl
try:
self._IE._from_char(char)
except:
log('%s, _from_char: unable to decode IE, %s'\
% (self._name, self._IE._name))
else:
if char.len_bit() > 0:
log('%s, _from_char: uncorrect decoding for IE, %s'\
% (self._name, self._IE._name))
else:
# save the V buffer
self._V = self[-1]
# replace it with the IE structure
self.replace(self[-1], self._IE)
if self[-2]._name == 'L':
# Type4 and Type6
self[-2].set_valauto( self[-1].get_len )
char._cur = char_cur
char._len_bit = char_lb
def clone(self):
kw = {}
if self._desc != self.__class__._desc:
kw['desc'] = self._desc
if self._hier != self.__class__._hier:
kw['hier'] = self._hier
if self._trans != self.__class__._trans:
kw['trans'] = self._trans
if self._IE is not None:
# additional attribute, compared to Envelope.clone()
kw['IE'] = self._IE
# substitute the Envelope generator with clones of the current
# envelope's content
kw['GEN'] = tuple([elt.clone() for elt in self._content])
return self.__class__(self._name, **kw)
# new methods, specific to IE
def set_IE(self, *args, **kw):
if self._IE is not None:
self._IE.__init__(*args, **kw)
assert( self[-1]._name == 'V' )
self._V = self[-1]
self.replace(self[-1], self._IE)
if self[-2]._name == 'L':
# Type4 and Type6
self[-2].set_valauto( self[-1].get_len )
def unset_IE(self, *args):
if self._IE is not None:
assert( self._V is not None )
self[-1] = self._V
if args:
self[-1].__init__(val=args[0])
if self[-2]._name == 'L':
# Type4 and Type6
self[-2].set_valauto( self[-1].get_len )
self[-1].set_blauto( lambda: 8*self[-2].get_val() )
class Type1V(IE):
"""The Type1_V IE is a mandatory IE,
its content is a single 4 bit indicator
"""
_GEN = (
Uint('V', bl=4),
)
def __init__(self, *args, **kw):
if 'dic' in kw:
dic = kw['dic']
del kw['dic']
else:
dic = None
IE.__init__(self, *args, **kw)
if dic is not None:
self[0]._dic = dic
class Type1TV(IE):
"""The Type1_TV IE is an optional IE,
its content is a 4 bit tag and a 4 bit value
"""
_GEN = (
Uint('T', bl=4),
Uint('V', bl=4)
)
def __init__(self, *args, **kw):
if 'dic' in kw:
dic = kw['dic']
del kw['dic']
else:
dic = None
IE.__init__(self, *args, **kw)
if dic is not None:
self[1]._dic = dic
class Type2(IE):
"""The Type2 IE is an optional IE,
its content is a single 8 bit tag (i.e. a flag)
"""
_GEN = (
Uint8('T'),
)
class Type3V(IE):
"""The Type3_V IE is a mandatory IE,
its content is a simple buffer
"""
_GEN = (
Buf('V', rep=REPR_HEX),
)
class Type3TV(IE):
"""The Type3_TV IE is an optional IE,
its content is a 8 bit tag and a simple buffer
"""
_GEN = (
Uint8('T'),
Buf('V', rep=REPR_HEX)
)
class Type4LV(IE):
"""The Type4_LV IE is a mandatory IE
its content is a 8 bit length and a buffer of given length
"""
_GEN = (
Uint8('L'),
Buf('V', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
IE.__init__(self, *args, **kwargs)
self[0].set_valauto( self[1].get_len )
self[1].set_blauto( lambda : 8*self[0]() )
class Type4TLV(IE):
"""The Type4_TLV IE is an optional IE
its content is a 8 bit tag, a 8 bit length and a buffer of given length
"""
_GEN = (
Uint8('T'),
Uint8('L'),
Buf('V', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
IE.__init__(self, *args, **kwargs)
self[1].set_valauto( self[2].get_len )
self[2].set_blauto( lambda : 8*self[1].get_val() )
class Type6LVE(IE):
"""The Type6_LVE IE is a mandatory IE only used in EPS
its content is a 16 bit length and a buffer of given length
"""
_GEN = (
Uint16('L'),
Buf('V', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
IE.__init__(self, *args, **kwargs)
self[1].set_valauto( self[2].get_len )
self[2].set_blauto( lambda : 8*self[1].get_val() )
class Type6TLVE(IE):
"""The Type6_TLVE IE is an optional IE only used in EPS
its content is a 8 bit tag, a 16 bit length and a buffer of given length
"""
_GEN = (
Uint8('T'),
Uint16('L'),
Buf('V', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
IE.__init__(self, *args, **kwargs)
self[1].set_valauto( self[2].get_len )
self[2].set_blauto( lambda : 8*self[1].get_val() )
#------------------------------------------------------------------------------#
# Imperative part of a standard L3 message
# TS 24.007, section 11.2.3
#------------------------------------------------------------------------------#
ProtDisc_dict = {
0 : 'GCC',
1 : 'BCC',
2 : 'ESM',
3 : 'CC',
4 : 'GTTP',
5 : 'MM',
6 : 'RRM',
7 : 'EMM',
8 : 'GMM',
9 : 'SMS',
10: 'SM',
11: 'SS',
12: 'LCS',
13: 'extended ProtDisc',
14: 'testing',
}