So as stated before, the eNB emulator has to implement S1AP and S1-U interfaces, and in case we are using a real HSS/AuC, we need also to handle the authentication and key derivation.
root@ubuntu:/home/fabricio/Documents# lsusb | grep ZTE
Bus 002 Device 000: ID 19d2:2000 ZTE WCDMA Technologies MSM MF627/MF628/MF628+/MF636+ HSDPA/HSUPA
root@ubuntu:/home/fabricio/Documents# more /lib/systemd/system/ModemManager.service
[Unit]
Description=Modem Manager
After=polkit.service
Requires=polkit.service
[Service]
Type=dbus
BusName=org.freedesktop.ModemManager1
ExecStart=/usr/sbin/ModemManager --filter-policy=default
StandardError=null
Restart=on-abort
CapabilityBoundingSet=CAP_SYS_ADMIN
ProtectSystem=true
ProtectHome=true
PrivateTmp=true
RestrictAddressFamilies=AF_NETLINK AF_UNIX
NoNewPrivileges=true
User=root
[Install]
WantedBy=multi-user.target
Alias=dbus-org.freedesktop.ModemManager1.service
root@ubuntu:/home/fabricio/Documents# more /etc/udev/rules.d/99zte.rules
ATTRS{idVendor}=="19d2" ATTRS{idProduct}=="0001", ENV{ID_MM_DEVICE_IGNORE}="1"
ATTRS{idVendor}=="19d2" ATTRS{idProduct}=="2000", ENV{ID_MM_DEVICE_IGNORE}="1"
root@ubuntu:/home/fabricio/Documents# udevadm control --reload-rules
from pycrate_asn1dir import S1AP
from pycrate_asn1rt.utils import *
from CryptoMobile.CM import *
from Crypto.Hash import HMAC
from Crypto.Hash import SHA256
import serial
- SCTP (IETF RFC 4960 [5]) shall be supported as the transport layer of S1-MME signalling bearer. The Payload Protocol Identifier assigned by IANA to be used by SCTP for the application layer protocol S1AP is 18.
-
There shall be only one SCTP association established between one MME and eNB pair.
The eNB shall establish the SCTP association. The SCTP Destination Port number value assigned by IANA to be used for S1AP is 36412.
Within the SCTP association established between one MME and eNB pair:
- a single pair of stream identifiers shall be reserved for the sole use of S1AP elementary procedures that utilize non UE-associated signalling.
- At least one pair of stream identifiers shall be reserved for the sole use of S1AP elementary procedures that utilize UE-associated signallings. However a few pairs (i.e. more than one) should be reserved.
- A single UE-associated signalling shall use one SCTP stream and the stream should not be changed during the communication of the UE-associated signalling.
import socket def main(): server_address = ('1.1.1.1', 36412) client = socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_SCTP) client.bind(('2.2.2.2', 0)) sctp_default_send_param = bytearray(client.getsockopt(132,10,32)) sctp_default_send_param[11]= 18 client.setsockopt(132, 10, sctp_default_send_param) client.connect(server_address)
PDU = S1AP.S1AP_PDU_Descriptions.S1AP_PDU
def set_stream(client, stream): sctp_default_send_param = bytearray(client.getsockopt(132,10,32)) sctp_default_send_param[0]= stream client.setsockopt(132, 10, sctp_default_send_param) return client
def session_dict_initialization(session_dict):
session_dict['STATE'] = 0
session_dict['ENB-UE-S1AP-ID'] = 1000
session_dict['ENB-NAME'] = 'Fabricio-eNB'
session_dict['ENB-PLMN'] = return_plmn(PLMN)
session_dict['XRES'] = b'xresxres'
session_dict['KASME'] = b'kasme kasme kasme kasme '
# hex: 6b61736d652020206b61736d652020206b61736d652020206b61736d65202020
session_dict['ENB-GTP-ADDRESS-INT'] = ''
session_dict['RAB-ID'] = []
session_dict['SGW-GTP-ADDRESS'] = []
session_dict['SGW-TEID'] = []
session_dict['EPS-BEARER-IDENTITY'] = []
session_dict['EPS-BEARER-TYPE'] = [] # default 0, dedicated 1
session_dict['EPS-BEARER-STATE'] = [] # active 1, inactive 0
session_dict['EPS-BEARER-APN'] = []
session_dict['PDN-ADDRESS'] = []
session_dict['PDN-ADDRESS-IPV4'] = None
session_dict['PDN-ADDRESS-IPV6'] = None
session_dict['ENB-TAC'] = b'\x00\x01'
session_dict['ENB-TAC-NBIOT'] = b'\x00\x02'
session_dict['ENB-ID'] = 1
session_dict['ENB-CELLID'] = 1000000
session_dict['NAS-KEY-EEA1'] = return_key(session_dict['KASME'],1,'NAS-ENC')
session_dict['NAS-KEY-EEA2'] = return_key(session_dict['KASME'],2,'NAS-ENC')
session_dict['NAS-KEY-EEA3'] = return_key(session_dict['KASME'],3,'NAS-ENC')
session_dict['NAS-KEY-EIA1'] = return_key(session_dict['KASME'],1,'NAS-INT')
session_dict['NAS-KEY-EIA2'] = return_key(session_dict['KASME'],2,'NAS-INT')
session_dict['NAS-KEY-EIA3'] = return_key(session_dict['KASME'],3,'NAS-INT')
session_dict['UP-COUNT'] = -1
session_dict['DOWN-COUNT'] = -1
session_dict['ENC-ALG'] = 0
session_dict['INT-ALG'] = 0
session_dict['ENC-KEY'] = None
session_dict['INT-KEY'] = None
session_dict['APN'] = APN
session_dict['NAS-SMS-MT'] = None
if session_dict['LOCAL_KEYS'] == True:
if session_dict['IMSI'] == None:
session_dict['IMSI'] = IMSI
else:
if session_dict['IMSI'] == None:
try:
session_dict['IMSI'] = get_imsi(session_dict['SERIAL-INTERFACE'])
except:
session_dict['LOCAL_KEYS'] = True
session_dict['IMSI'] = IMSI
if session_dict['IMEISV'] == None:
session_dict['IMEISV'] = IMEISV
session_dict['ENCODED-IMSI'] = eNAS.encode_imsi(session_dict['IMSI'])
session_dict['ENCODED-IMEI'] = eNAS.encode_imei(IMEISV)
session_dict['ENCODED-GUTI'] = eNAS.encode_guti(12345,32769,1,12345678)
session_dict['S-TMSI'] = None
session_dict['TMSI'] = None
session_dict['LAI'] = None
session_dict['CPSR-TYPE'] = 0
session_dict['S1-TYPE'] = "4G"
session_dict['MOBILE-IDENTITY'] = session_dict['ENCODED-IMSI']
session_dict['MOBILE-IDENTITY-TYPE'] = "IMSI"
session_dict['SESSION-SESSION-TYPE'] = None
session_dict['SESSION-TYPE'] = "4G"
session_dict['SESSION-TYPE-TUN'] = 1
session_dict['PDP-TYPE'] = 1
session_dict['ATTACH-PDN'] = None
session_dict['ATTACH-TYPE'] = 1
session_dict['TAU-TYPE'] = 0
session_dict['SMS-UPDATE-TYPE'] = False
session_dict['NBIOT-SESSION-TYPE'] = "NONE"
session_dict['CPSR-TYPE'] = 0
session_dict['UECONTEXTRELEASE-CSFB'] = False
session_dict['PROCESS-PAGING'] = True
session_dict['LOG'] = []
return session_dict
def S1SetupRequest(dic):
IEs = []
IEs.append({'id': 59, 'value': ('Global-ENB-ID', {'pLMNidentity': dic['ENB-PLMN'], 'eNB-ID' : ('macroENB-ID', (dic['ENB-ID'], 20))}), 'criticality': 'reject'})
IEs.append({'id': 60, 'value': ('ENBname', dic['ENB-NAME']), 'criticality': 'ignore'})
if dic['S1-TYPE'] == "4G" :
IEs.append({'id': 64, 'value': ('SupportedTAs', [{'tAC': dic['ENB-TAC'], 'broadcastPLMNs': [dic['ENB-PLMN']]}]), 'criticality': 'reject'})
elif dic['S1-TYPE'] == "NBIOT":
IEs.append({'id': 64, 'value': ('SupportedTAs', [{'tAC': dic['ENB-TAC-NBIOT'], 'broadcastPLMNs': [dic['ENB-PLMN']], 'iE-Extensions': [{'id':232, 'criticality': 'reject', 'extensionValue':('RAT-Type','nbiot')}]}]), 'criticality': 'reject'})
elif dic['S1-TYPE'] == "BOTH":
IEs.append({'id': 64, 'value': ('SupportedTAs', [{'tAC': dic['ENB-TAC'], 'broadcastPLMNs': [dic['ENB-PLMN']]}, {'tAC': dic['ENB-TAC-NBIOT'], 'broadcastPLMNs': [dic['ENB-PLMN']], 'iE-Extensions': [{'id':232, 'criticality': 'reject', 'extensionValue':('RAT-Type','nbiot')}]}]), 'criticality': 'reject'})
IEs.append({'id': 137, 'value': ('PagingDRX', 'v128'), 'criticality': 'ignore'})
if dic['S1-TYPE'] == "NBIOT" or dic['S1-TYPE'] == "BOTH":
IEs.append({'id': 234, 'value': ('NB-IoT-DefaultPagingDRX', 'v256'), 'criticality': 'ignore'})
val = ('initiatingMessage', {'procedureCode': 17, 'value': ('S1SetupRequest', {'protocolIEs': IEs}), 'criticality': 'ignore'})
dic = eMENU.print_log(dic, "S1AP: sending S1SetupRequest")
return val
PDU.set_val(S1SetupRequest(session_dict)) message = PDU.to_aper() client = set_stream(client, 0) bytes_sent = client.send(message)
def ProcessS1AP(PDU, client, session_dict): buffer = client.recv(4096) PDU.from_aper(buffer) (type, pdu_dict) = PDU() if type == 'initiatingMessage': procedure, protocolIEs_list = pdu_dict['value'][0], pdu_dict['value'][1]['protocolIEs'] #Non UE Related: if procedure == 'MMEConfigurationUpdate': session_dict = eMENU.print_log(session_dict, "S1AP: MMEConfigurationUpdate received") answer, session_dict = MMEConfigurationUpdateAcknowledge(protocolIEs_list, session_dict) PDU.set_val(answer) message = PDU.to_aper() client = set_stream(client, 0) bytes_sent = client.send(message) client = set_stream(client, 1) #UE Related: elif procedure == 'DownlinkNASTransport': session_dict = eMENU.print_log(session_dict, "S1AP: DownlinkNASTransport received") answer_list, session_dict = ProcessDownlinkNASTransport(protocolIEs_list, session_dict) for answer in answer_list: if answer != None: PDU.set_val(answer) message = PDU.to_aper() bytes_sent = client.send(message) elif procedure == 'InitialContextSetupRequest': session_dict = eMENU.print_log(session_dict, "S1AP: InitialContextSetupRequest received") answer_list, session_dict= ProcessInitialContextSetupRequest(protocolIEs_list, session_dict) for answer in answer_list: if answer != None: PDU.set_val(answer) message = PDU.to_aper() bytes_sent = client.send(message) elif procedure == 'UEContextReleaseCommand': session_dict = eMENU.print_log(session_dict, "S1AP: UEContextReleaseCommand received") answer, session_dict = ProcessUEContextReleaseCommand(protocolIEs_list, session_dict) if answer != None: PDU.set_val(answer) message = PDU.to_aper() bytes_sent = client.send(message) elif procedure == 'Paging': if session_dict['PROCESS-PAGING'] == True: session_dict = eMENU.print_log(session_dict, "S1AP: Paging received") answer, session_dict = ProcessPaging(protocolIEs_list, session_dict) if answer != None: PDU.set_val(answer) message = PDU.to_aper() bytes_sent = client.send(message) elif procedure == 'E-RABSetupRequest': session_dict = eMENU.print_log(session_dict, "S1AP: ERABSetupRequest received") answer_list, session_dict = ProcessERABSetupRequest(protocolIEs_list, session_dict) for answer in answer_list: if answer != None: PDU.set_val(answer) message = PDU.to_aper() bytes_sent = client.send(message) elif procedure == 'E-RABReleaseCommand': session_dict = eMENU.print_log(session_dict, "S1AP: ERABReleaseCommand received") answer, session_dict, answer2 = ProcessERABReleaseCommand(protocolIEs_list, session_dict) if answer != None: PDU.set_val(answer) message = PDU.to_aper() bytes_sent = client.send(message) if answer2 != None: #if also uplinkNAStransport message needed PDU.set_val(answer2) message = PDU.to_aper() bytes_sent = client.send(message) elif procedure == 'LocationReportingControl': session_dict = eMENU.print_log(session_dict, "S1AP: LocationReportingControl received") answer, session_dict = ProcessLocationReportingControl(protocolIEs_list, session_dict) if answer != None: PDU.set_val(answer) message = PDU.to_aper() bytes_sent = client.send(message) elif procedure == 'UEContextModificationRequest': session_dict = eMENU.print_log(session_dict, "S1AP: UEContextModificationRequest received") answer_list, session_dict = ProcessUEContextModificationRequest(protocolIEs_list, session_dict) for answer in answer_list: if answer != None: PDU.set_val(answer) message = PDU.to_aper() bytes_sent = client.send(message) else: session_dict = eMENU.print_log(session_dict, "S1AP: " + procedure + " received") elif type == 'successfulOutcome': procedure, protocolIEs_list = pdu_dict['value'][0], pdu_dict['value'][1]['protocolIEs'] if procedure == "S1SetupResponse": session_dict = eMENU.print_log(session_dict, "S1AP: S1SetupResponse received") session_dict = S1SetupResponseProcessing(protocolIEs_list, session_dict) elif procedure == "ResetAcknowledge": session_dict = eMENU.print_log(session_dict, "S1AP: ResetAcknowledge received") else: session_dict = eMENU.print_log(session_dict, "S1AP: " + procedure + " received") elif type == 'unsuccessfulOutcome': exit(1) return PDU, client, session_dict def S1SetupResponseProcessing(IEs, dic):
mme_name = '' servedPLMNs = b'' servedGroupIDs = b'' servedMMECs = b'' RelativeMMECapacity = 0 for i in IEs: if i['id'] == 61: mme_name = i['value'][1] elif i['id'] == 105: servedPLMNs = i['value'][1][0]['servedPLMNs'][0] servedGroupIDs = i['value'][1][0]['servedGroupIDs'][0] servedMMECs = i['value'][1][0]['servedMMECs'][0] elif i['id'] == 87: RelativeMMECapacity = i['value'][1] dic['MME-NAME'] = mme_name dic['MME-PLMN'] = servedPLMNs dic['MME-GROUP-ID'] = servedGroupIDs dic['MME-CODE'] = servedMMECs dic['MME-RELATIVE-CAPACITY'] = RelativeMMECapacity dic['STATE'] = 1 return dic
def nas_encode(nas_list):
nas = b''
protocol_discriminator = nas_list[0][0]
security_header = nas_list[0][1]
nas += bytes([(security_header*16)+protocol_discriminator])
for i in range(1,len(nas_list)):
if nas_list[i][0] == 0:
if nas_list[i][1] == 'V':
nas += nas_list[i][2]
elif nas_list[i][1] == 'LV':
nas += bytes([len(nas_list[i][2])]) + nas_list[i][2]
elif nas_list[i][1] == 'LV-E':
nas += bytes([len(nas_list[i][2])//256]) + bytes([len(nas_list[i][2])%256]) + nas_list[i][2]
else:
if nas_list[i][1] == "TV":
if nas_list[i][0] < 16:
nas += bytes([(nas_list[i][0]*16) + nas_list[i][2]])
else:
nas += bytes([nas_list[i][0]]) + nas_list[i][2]
if nas_list[i][1] == "TLV":
nas += bytes([nas_list[i][0]]) + bytes([len(nas_list[i][2])]) + nas_list[i][2]
if nas_list[i][1] == "TLV-E":
nas += bytes([nas_list[i][0]]) + bytes([len(nas_list[i][2])//256]) + bytes([len(nas_list[i][2])%256]) + nas_list[i][2]
return nas
def nas_attach_request(type, esm_information_transfer_flag, eps_identity, pdp_type, attach_type, tmsi, lai, sms_update):
emm_list = []
emm_list.append((7,0)) # protocol discriminator
emm_list.append((0,'V',bytes([65]))) # message type: attach request
emm_list.append((0,'V',bytes([attach_type]))) # eps attach type / nas key set identifier
emm_list.append((0,'LV',eps_identity)) # eps mobile identity
if type[0] == "4G":
emm_list.append((0,'LV',unhexlify('f0f0c04009')))
elif type[0] == "NBIOT":
emm_list.append((0,'LV',unhexlify('f0f0000008a4')))
elif type[0] == "5G":
emm_list.append((0,'LV',unhexlify('f0f0c0c0000010')))
emm_list.append((0,'LV-E',nas_pdn_connectivity(0,1,pdp_type,None,None,esm_information_transfer_flag)))
if type[0] == "4G":
if attach_type == 2 and lai != None:
emm_list.append((0x13, 'TV', lai))
if attach_type == 2 and tmsi == None:
emm_list.append((0x9, 'TV', 0))
if sms_update == True:
emm_list.append((0xF, 'TV', 1))
if attach_type == 2 and tmsi != None:
emm_list.append((0x10, 'TLV', tmsi[-3:-2] + bytes([(tmsi[-2]//64)*64])))
elif type[0] == "NBIOT":
if attach_type == 2 and lai != None:
emm_list.append((0x13, 'TV', lai))
if attach_type == 2 and tmsi == None:
emm_list.append((0x9, 'TV', 0))
if sms_update == True:
emm_list.append((0xF, 'TV', 5))
else:
emm_list.append((0xF, 'TV', 4))
emm_list.append((0xC, 'TV', 1))
if attach_type == 2 and tmsi != None:
emm_list.append((0x10, 'TLV', tmsi[-3:-2] + bytes([(tmsi[-2]//64)*64])))
if type[1] == "PSM" or type[1] == "BOTH":
emm_list.append((0x6A, 'TLV', b'\x0f')) # 15*2=30 sec.
emm_list.append((0x5E, 'TLV', b'\x41'))
if type[1] == "EDRX" or type[1] == "BOTH":
emm_list.append((0x6E, 'TLV', b'\x75'))
elif type[0] == "5G":
if attach_type == 2 and lai != None:
emm_list.append((0x13, 'TV', lai))
if attach_type == 2 and tmsi == None:
emm_list.append((0x9, 'TV', 0))
if sms_update == True:
emm_list.append((0xF, 'TV', 1))
if attach_type == 2 and tmsi != None:
emm_list.append((0x10, 'TLV', tmsi[-3:-2] + bytes([(tmsi[-2]//64)*64])))
emm_list.append((0x6F, 'TLV', b'\xf0\x00\xf0\x00'))
return eNAS.nas_encode(emm_list)
def nas_pdn_connectivity(eps_bearer_identity, pti, pdp_type, apn, pco, esm_information_transfer_flag):
esm_list = []
esm_list.append((2,eps_bearer_identity)) # protocol discriminator / eps bearer identity
esm_list.append((0,'V',bytes([pti]))) # procedure trnasaction identity
esm_list.append((0,'V',bytes([208]))) # message type: pdn connectivity request
esm_list.append((0,'V',bytes([(pdp_type*16) + 1])))
if esm_information_transfer_flag != None:
esm_list.append((0xD,'TV',esm_information_transfer_flag))
if apn != None:
esm_list.append((0x28,'TLV',apn))
if pco != None:
esm_list.append((0x27,'TLV',pco))
return eNAS.nas_encode(esm_list)
def InitialUEMessage(dic): IEs = [] IEs.append({'id': 8, 'value': ('ENB-UE-S1AP-ID', dic['ENB-UE-S1AP-ID']), 'criticality': 'reject'}) IEs.append({'id': 26, 'value': ('NAS-PDU', dic['NAS']), 'criticality': 'reject'}) if dic['SESSION-TYPE'] == "4G" or dic['SESSION-TYPE'] == "5G": IEs.append({'id': 67, 'value': ('TAI', {'pLMNidentity': dic['ENB-PLMN'], 'tAC': dic['ENB-TAC']}), 'criticality': 'reject'}) elif dic['SESSION-TYPE'] == "NBIOT": IEs.append({'id': 67, 'value': ('TAI', {'pLMNidentity': dic['ENB-PLMN'], 'tAC': dic['ENB-TAC-NBIOT']}), 'criticality': 'reject'}) IEs.append({'id': 100, 'value': ('EUTRAN-CGI', {'cell-ID': (dic['ENB-CELLID'], 28), 'pLMNidentity': dic['ENB-PLMN']}), 'criticality': 'ignore'}) IEs.append({'id': 134, 'value': ('RRC-Establishment-Cause', 'mo-Signalling'), 'criticality': 'ignore'}) if dic['S-TMSI'] != None: IEs.append({'id': 96, 'value': ('S-TMSI', {'mMEC': dic['S-TMSI'][0:1], 'm-TMSI': dic['S-TMSI'][1:5]}), 'criticality': 'reject'}) val = ('initiatingMessage', {'procedureCode': 12, 'value': ('InitialUEMessage', {'protocolIEs': IEs}), 'criticality': 'ignore'}) dic = eMENU.print_log(dic, "S1AP: sending InitialUEMessage") return val
root@ubuntu:/home/fabricio/Documents# python3 eNB_LOCAL.py -h Usage: eNB_LOCAL.py [options] Options: -h, --help show this help message and exit -i ENB_IP, --ip=ENB_IP eNB Local IP Address -m MME_IP, --mme=MME_IP MME IP Address -g GATEWAY_IP_ADDRESS, --gateway_ip_address=GATEWAY_IP_ADDRESS gateway IP address -u SERIAL_INTERFACE, --usb_device=SERIAL_INTERFACE usb tty (e.g /dev/ttyUSBx) -I IMSI, --imsi=IMSI IMSI (15 digits) -E IMEI, --imei=IMEI IMEI-SV (16 digits)
- S1 Setup type: LTE, NB-IoT, or both
- Mobile Identity Type: IMSI or GUTI
- Attach PDN: Default APN, or Specific APN
- Session Type: 4G, 5G or NB-IoT
- NB-IoT Session Type: No PSM and eDRX, PSM, eDRX or both PSM and eDRX
- PDN Type ipv4, ipv6 or ipv4v6
- Control Plane Service Request with Radio Bearer or without Radio Bearer
- Attach Type: EPS Attach or Combined EPS/IMSI Attach
- TAU Type: TA Updating, Combined TA/LA Updating or Combined TA/LA Updating with IMSI Attach
- Process Paging: Enabled or Disabled
- SMS Update type: Additional Update Type SMS Only: False or True
- eNB Cell can change
- S1 Setup Request
- S1 Reset
- Attach
- Detach
- TAU
- TAU Periodic
- Service Request
- UE Context Release
- Send SMS
- Control Plane Service Request
- E-RAB Modification Indication
- Secondary RAT Data Usage Report
- PDP Connectivity
- PDN Disconnect
- Activate/Deactivate GTP-U for Control Plane
- Activate/Deactivate Data over NAS
This is awesome. Just to clarify, SIM reader is ordinary ZTE dongle that can return auth. vectors. Brilliant.
ReplyDeleteCan you share this on github ?
No. You send the RAND and AUTN to the SIM, and it answers back with CK, IK and RES (in case AUTN is valid). With CK and IK and PLMN MNC/MNC you can calculate the KASME and derive the other keys. I will probably share all the code in github.
DeleteHello ,
ReplyDeleteI had a chance to explore the test setup eNB and did test few ICMP ping to external entities via Open5Gs network.
I also did see few logics implemented for NBIoT. I would like to test a NBIoT. Is there any Github links that works on NBIoT which I can make use of for testing NBIoT.
Thanks in advance.