# -*- coding: UTF-8 -*- #/** # * Software Name : pycrate # * Version : 0.4 # * # * Copyright 2019. 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_mobile/NAS5G.py # * Created : 2019-12-02 # * Authors : Benoit Michau # *-------------------------------------------------------- #*/ from pycrate_core.utils import * from .TS24501_FGMM import FGMMTypeClasses, FGMMSecProtNASMessage from .TS24501_FGSM import FGSMTypeClasses from .TS24501_UEPOL import FGUEPOLTypeClasses from .TS24519_TSNAF import FGTSNAFEthPortTypeClasses, FGTSNAFBridgeTypeClasses from .TS24011_PPSMS import PPSMSCPTypeClasses def parse_NAS5G(buf, inner=True, sec_hdr=True): """Parses a 5G NAS message bytes' buffer Args: buf: 5G NAS message bytes' buffer inner: if True, decode NASMessage within security header if possible decode ? sec_hdr: if True, handle the 5GMM security header otherwise, just consider the NAS message is in plain text Returns: element, err: 2-tuple element: Element instance, if err is null (no error) element: None, if err is not null (standard 5G NAS error code) """ try: # this corresponds actually only to the layout of the 5GMM header pd, shdr, typ = unpack('>BBB', buf[:3]) except Exception: # error 111, unspecified protocol error return None, 111 # if pd == 126: # 5GMM if sec_hdr and shdr in (1, 2, 3, 4): # 5GMM security protected NAS message Msg = FGMMSecProtNASMessage() try: Msg.from_bytes(buf) except Exception: # error 96, invalid mandatory info return None, 96 if inner and shdr in (1, 3): # parse clear-text NAS message container cont, err = parse_NAS5G(Msg[3].get_val(), inner=inner) if cont is not None: Msg.replace(Msg[3], cont) return Msg, err else: return Msg, 0 else: # sec hdr == 0 or undefined # no security, straight 5GMM message try: Msg = FGMMTypeClasses[typ]() except KeyError: # error 97, message type non-existent or not implemented return None, 97 # elif pd == 46: # 5GSM try: if python_version < 3: typ = ord(buf[3:4]) else: typ = buf[3] except: # error 111, unspecified protocol error return None, 111 try: Msg = FGSMTypeClasses[typ]() except KeyError: # error 97, message type non-existent or not implemented return None, 97 # else: # error 97: message type non-existent or not implemented return None, 97 # try: Msg.from_bytes(buf) except Exception: # error 96, invalid mandatory info return None, 96 # if inner: if pd == 126: if typ in (65, 76, 79, 94): nasc = Msg['NASContainer'] if not nasc.get_trans(): # NAS Container present in Msg Cont, err = parse_NAS5G(nasc[-1].get_val(), inner=inner) if err == 0: nasc.replace(nasc['V'], Cont) # if typ in (65, 79, 103, 104): payct, payc = Msg['PayloadContainerType'], Msg['PayloadContainer'] if not payct.get_trans() and not payc.get_trans(): # Payload container present in Msg conttype, contbuf = payct['V'].get_val(), payc['V'].get_val() Cont, err = parse_PayCont(conttype, contbuf) if err == 0: payc.replace(payc['V'], Cont) # elif pd == 46: if typ in (193, 201, 203, 204): ethc = Msg['PortMgmtInfoContainer'] if not ethc.get_trans(): # PortMgmtInfoContainer present in Msg Cont, err = parse_PortMgmtInfoCont(ethc['V'].get_val()) if err == 0: ethc.replace(ethc['V'], Cont) # return Msg, 0 def parse_PayCont(conttype, buf): if conttype == 1 and len(buf) >= 2: # 5GSM return parse_NAS5G(buf, inner=True) elif conttype == 2 and len(buf) >= 2: # SMS PP pd, typ = unpack('>BB', buf[:2]) pd &= 0xF if pd == 9 and typ in (1, 4, 16): Cont = PPSMSCPTypeClasses[typ]() try: Cont.from_bytes(buf) except Exception: # error 96, invalid mandatory info return None, 96 else: return Cont, 0 else: # error 97, Message type non-existent or not implemented return None, 97 elif conttype == 3: # LPP, TODO pass elif conttype == 4 and len(buf) >= 17: # SOR Cont = SORTransContainer() try: Cont.from_bytes(buf) except Exception: # error 96, invalid mandatory info return None, 96 else: return Cont, 0 elif conttype == 5 and len(buf) >= 2: # UE policy _, typ = unpack('>BB', buf[:2]) if 1 <= typ <= 4: Cont = FGUEPOLTypeClasses[typ]() try: Cont.from_bytes(buf) except Exception: # error 96, invalid mandatory info return None, 96 else: return Cont, 0 else: # error 97, Message type non-existent or not implemented return None, 97 elif conttype == 6 and len(buf) >= 17: # UPU Cont = UPUTransContainer() try: Cont.from_bytes(buf) except Exception: # error 96, invalid mandatory info return None, 96 else: return Cont, 0 elif conttype == 7: # LCS, TODO pass elif conttype == 8 and len(buf) >= 1: # CIoT Cont = CIoTSmallDataContainer() try: Cont.from_bytes(buf) except Exception: # error 96, invalid mandatory info return None, 96 else: return Cont, 0 elif conttype == 15 and len(buf) >= 1: # multi Cont = PayloadContainerMult() try: Cont.from_bytes(buf) except Exception: # error 96, invalid mandatory info return None, 96 else: # parse each entry for entry in cont['Entries']: e_conttype, e_buf = Cont['Type'].get_val(), Cont['Cont'].get_val() e_cont, e_err = parse_NAS5GPayCont(e_conttype, e_buf) if e_err == 0: entry.replace(entry['Cont'], e_cont) # error 96, invalid mandatory info return None, 96 def parse_PortMgmtInfoCont(buf): try: # this corresponds actually only to the layout of the 5GMM header typ = unpack('>B', buf[:1]) except Exception: # error 96, invalid mandatory info return None, 96 if 1 <= typ <= 6: Cont = FGTSNAFEthPortTypeClasses[typ]() try: Cont.from_bytes(buf) except Exception: # error 96, invalid mandatory info return None, 96 else: return Cont, 0 else: # error 97, Message type non-existent or not implemented return None, 97