If you need some example pcap traces generated by any of these tools, just send an email to fasferraz@gmail.com


7/23/20

MME Part I - eNB Emulator

After years of doing applications that interact with the GGSN/PGW, I finally had the time to start and finish and old project that i had in mind for some time:

Create an eNB emulator to interact with MME (S1AP) and SGW (S1-U), once again to abstract the radio component. Something that I could use in a laptop, with connectivity to a real MME and SGW, to perform and simulate EPS Attach, TAU, PDN Connectivity Requests, and so on, and also user plane traffic.

This was done using python3, and in the following post I will talk about some of the issues I faced, and how I solved them.

So when we want to create an eNB to interact with a real network, the first concern, is that this time, I need to take into account the authentication, integrity and ciphering. When you emulate an MME or SGW to communicate with a PGW, you are bypassing all this, speaking GTP directly with the node, and filling each information element of the GTP protocol with the values you want. You can chose the IMSI, the MSISDN, the APN, the User Location, the IMEI, and so on.

With the eNB is different: if you choose an IMSI, you need to have the capability to answer with the proper XRES for the RAND/AUTN present in the Authentication Request.
But once you have that ability, you can also perform whatever procedures you want, define and change the characteristics and capabilities of the emulated terminal, and also the underlying features of the emulated eNB (like supporting 4G, 5G or NB-IoT, etc...).

So, a lot of interesting stuff can be done!


The following picture shows a simplified version of what is needed:



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.

As I already spoke about in previous posts, I have a ZTE dongle that supports the needed AT commands (AT+CRSM and AT+CSIM) which allows me to get the CK and IK from the RAND/AUTN.

With the CK and IK and Serving Network (MCC/MNC) I can calculate the KASME, and derive the NAS keys for ciphering and integrity according to the algorithms negotiated in the Security Mode Command - So this part of the problem is solved!

Nevertheless, to simplify the process, I implemented also a simple Diameter HSS Server, that returns always the same quartet (RAND, AUTN, XRES and KASME) to the MME, so that the eNB can work without the dongle and a real HSS.

On of the issues i faced with the authentication process, was that Ubuntu was blocking my serial communication with the modem from time to time, due to the fact of the modem manager was also trying to retrieve information from the modem.
To avoid this conflict I used the answer from this post in stackoverflow: https://stackoverflow.com/questions/24696527/modem-manager-and-ttyacm-in-use
       
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
   
In resume, you need to get the idVendor and idProduct from the modem using lsusb to create a new rules file for this modem with ENV{ID_MM_DEVICE_IGNORE}="1", and then change the filter-policy from strict to default in ModemManager.service, and reload the rules in the end.
After doing this no Ubuntu process tries to communicate with the modem.


One of the main challenges of building an eNB emulator, is the fact that S1AP interface uses SCTP, and is based on PER (Packed Encoding Rules) ASN.1.

I had previously done some experiments with SCTP using the native socket module from python3, but starting a ASN.1 module from scratch was a big challenge. Fortunately I found some magnificent python modules for ASN.1 and S1AP done by P1 Security that I highly recommend:


They have plenty of modules for almost anything related to Mobile Developments, but for my project i just used the S1AP from pycrate_asn1dir module, and CM from the CryptoMobile module (that has all the ciphering and integrity protocols needed for NAS). In order to derive the integrity and ciphering keys from KASME/CK/IK) i used another module: the Crypto.Hash (pip3 install pycryptodome) that has the HMAC and SHA256 functions needed for KDF.
For serial communication i use the pyserial module (pip3 install pyserial)

In resume:
       
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

 

For the usage of SCTP under S1AP, specification 36.412 has some requirements:

  1. 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.
  2. There shall be only one SCTP association established between one MME and eNB pair.

  3. The eNB shall establish the SCTP association. The SCTP Destination Port number value assigned by IANA to be used for S1AP is 36412.

  4. 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.


The following code shows how to create an SCTP socket for S1AP, with Payload Protocol Identifier = 18. In this example, MME has the IP 1.1.1.1, and eNB client has the IP 2.2.2.2. The last line is to setup the variable PDU that is used to set and read the S1AP messages:
       
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


In order to handle the SCTP streams, I decided that I would use stream 0 for non UE associated signalling, and stream 1 for UE associated signalling.
To change the stream before sending the S1AP message over SCTP, I use the following code:

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      


One important variable that I use across the application, is a dictionary with all the settings and parameters the application uses. This dictionary has state session variables, like ciphering and integrity keys, GUTI, M-TMSI, APNs, IP-Adresses, IMEI, NAS Message (Received/To be Sent), State information, etc... related to S1AP and NAS protocols.

This is the initialization of the dictionary. (Some keys could also be initialized through the CLI options when starting the app):
          

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
  
 

So, a lot of different settings and variables inside this dictionary!

The first step after being connected to the MME is sending the non-UE related S1SetupRequest message to bring-up the eNB.

The pycrate module uses a very python-way of creating the ASN.1 messages for S1AP.

It's not straightforward, but once you get into it, it becomes more easier.
Of course you need to be good with ASN.1, at least reading it properly in the 3GPP 36.413 specification! 😅 

You need to know the exact names of information elements!

This is what a S1SetupRequest message looks like:
       

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


       
 

To send this S1SetupRequest to the MME, I use this code:
       
    PDU.set_val(S1SetupRequest(session_dict))
    message = PDU.to_aper()
    client = set_stream(client, 0)        
    bytes_sent = client.send(message)

The first line puts the list in the PDU variable, and the second line transform that into bytes in PER ASN.1 format according to 36.413.
The third line sets the stream to 0, because we are sending this message in the non UE strream, and the forth line is just sending the bytes out through the SCTP socket towards the MME.

2/27/19

5G Security

This is an application that implements all the security procedures for 5G according to Annex A and Annex D of 3GPP 33.501. The ciphering and integrity algorithms are the same for LTE, and key derivation function is also the same, using different FCs.

The next image shows the key hierarchy in 5G:



The key distribution and key derivation scheme for 5G network nodes is shown in the next image:




The key distribution and key derivation scheme for 5G UE is shown in the next image:




5G Algorithms:
  • Ciphering algorithm 128-NEA-1 and Integrity algortihm 128-NIA-1 are based on SNOW 3G and UEA2 defined in 3GPP (identical to 128-EEA-1 and EIA1)
  • Ciphering algorithm 128-NEA-2 and Integrity algortihm 128-NIA-2 are based on AES defined in NIST specifications (identical to 128-EEA-2 and EIA2) 
  • Ciphering algorithm 128-NEA-3 and Integrity algortihm 128-NIA-3 are based on ZUC defined in 3GPP specifications (identical to 128-EEA-3 and EIA3)

The Key derivation function that is needed for every key derivation in 5G is explained in 33.220, and is the following:

“The input parameters and their lengths shall be concatenated into a string S as follows:

1. The length of each input parameter measured in octets shall be encoded into a two octet-long string:

a) express the number of octets in input parameter Pi as a number k in the range [0, 65535].

b) Li is then a 16-bit long encoding of the number k, encoded as described in clause B.2.1.

2. String S shall be constructed from n+1 input parameters as follows:

S = FC || P0 || L0 || P1 || L1 || P2 || L2 || P3 || L3 ||... || Pn || Ln

where

FC is single octet used to distinguish between different instances of the algorithm,

P0 ... Pn are the n+1 input parameter encodings, and

L0 ... Ln are the two-octet representations of the length of the corresponding input parameter encodings P0.. Pn.

In this specification the following restriction applies to P0:  P0 is a static ASCII-encoded string.
This restriction is not part of the KDF definition and does not apply to the KDF when used by other 3GPP specifications unless explicitly stated so in those specifications.

3. The final output, i.e. the derived key is equal to the KDF computed on the string S using the key, denoted Key. The present document defines the following KDF:

derived key = HMAC-SHA-256 ( Key , S )

as specified in [22] and [23].”



In Annex A of 3GPP 33.501 we have for each Key Derivation the FC, Pn and Ln values to apply.


The app has several tabs and is divided between Annex A functions and Annex D Algorithms.


For Annex A, there is a tab for each chapter of this Annex. 
For example Annex A.8 is for deriving the algorithm keys for each one of these situations:

  • N-NAS encryption
  • N-NAS integrity
  • N-RRC encryption
  • N-RRC integrity
  • N-UP encryption
  • N-UP integrity

For NAS algorithm key derivations, the input key shall be the 256-bit KAMF, and for UP and RRC algorithm key derivations, the input key shall be the 256-bit KgNB.



For Annex D, there are two tabs, one for the ciphering and another one for the integrity.

In the ciphering tab is possible after doing the decryption of the message, to replace the bytes in the pcap file, in order to properly see the decoded message. 

The other tab, is just to confirm that the mac is correctly calculated in a given message, since this is always in clear text.

If you want to download this application, please contact me through email (fasferraz@gmail.com), since this app will not be available for free. A small donation is required.

10/26/17

SoftGGSN for Raspberry Pi

One application that I’ve done some years ago was a GTP dialer for Linux/OSX OS. It’s an application that basically creates a GTP tunnel with a GGSN, and then the IP received in the CreatePDPContext Response is set as tunnel interface in the machine. After that we set the default route to the tunnel interface (keeping always a /32 route for the tunnel endpoint towards the previous default gateway) and we can use the GTP tunnel to send and receive traffic from the internet, or to whatever networks the APN give us connectivity.
It’s different from my SGSN/MME/SGW Emulator because that tool is focused on the control plane side of things, while the GTP dialer is just like a VPN dialer, and the focus is the user plane.
It’s very useful to perform service tests in the GGSN/PGW, since we can use any application in the laptop (ex. web browser), and it also supports all PDP Types (IPv4, IPv6 or IPv4v6).

I have a Raspberry in my house and I was thinking in doing some kind of http/https proxy or implementing some tunneling protocol just to be able to access the internet using my home IP. This way I could for example, use services that are only available in my home country when I’m abroad.
Then I notice that I already had this GTP dialer that is in its own way a tunneling mechanism…. So I come up with the following idea:
What if I do a simple GGSN in my Raspberry Pi that could be used with my GTP dialer? I can be anywhere in the world with my laptop, connected to a Wlan or Ethernet network, and as long I have internet access, I can connect and establish a GTP tunnel to my Raspberry Pi in my house, which will give me a private IP from my internal network, and then all the traffic from my laptop will be sent encapsulated in GTP to the Raspberry IP, and then stripped and NATted in my Home Gateway (HGW) to the internet?
Sounds easy, right?
After this initial idea, I thought that I could extend it and allow more than one connection at the same time (multiple PDPs from different clients), and using DHCP for IP address allocation in the HGW. For example my internal network is the usual 192.168.1.0/24. Most of these addresses are available in the DHCP server of my HGW to be assigned to machines that use my Wlan or Ethernet network. I have a 200Mbits WAN connection to my service provider, but the Raspberry Pi is connected via Ethernet cable with a 100Mbits speed, so I think that up to 10 connections could have a decent bandwidth.
There are several technical issues that need to be addressed:

  • If the Raspberry Pi uses its MAC address for all these communications, the DHCP server will assign always the same IP address, so for each one of these PDP requests arriving at the Raspberry Pi, a different MAC address should be used, so that the HGW assigns a different IPs. To forge these MAC addresses, we need a RAW socket for the uplink packets and a RAW socket in promiscuous mode for the downlink packets, since the Raspberry Pi needs to listen and process the packets sent by the HGW to these fictitious MAC addresses.
  • The Raspberry needs to track all ARP requests in internal LAN, since it must answers in behalf of all internal IP assigned to the current active PDP sessions, and answer those ARP Request with the correct MAC Address. This can be done with a RAW socket in promiscuous mode.
  • The Raspberry Pi needs to track the DHCP timers for all IPs assigned to the PDP sessions. It needs to renew the IP at half the lease time if the PDP is still up, and release the IP when the PDP is deleted.
  • Port forwarding for GTP-C and GTP-U must be configured in the HGW (e.g. UDP ports 2123 and 2152)
  • The GTP dialer, can be behind a NAT device. In the normal GTP-U operation, uplink packets from a SGSN are sent to UDP port 2152 in the GGSN, and downlink packets are sent from the GGSN to the UDP Port 2152 in the SGSN, and the source port can be any port. This can be problematic, because port 2152 could not be open in the NAT, or if it’s open, it can be assigned to a different internal client that is also behind the same NAT device. To overcome this, the GGSN can detect if the client is behind a NAT by comparing the source IP with the GSN IP Address for Control and Data received in the CreatePDPContext Request message. If they are different, then the client is behind a NAT device, so the GGSN instead of sending the downlink GTP-U packets to UDP port 2152, it will send it to the source port of the GTP-U packets coming from the GTP dialer. Since the GTP dialer uses UDP port 2152 as source port for all uplink GTP-U packets, the NAT translation will guarantee that all downlink packets sent by the GGSN to the source port previously received in the uplink GTP-U packets, will be delivered to the GTP dialer. Since this traffic is UDP, the translation timeout is usually small (5 minutes). So we have the risk that this translation expires, and a new source port is assigned later on. To overcome this, the GGSN needs to always compare the GTP-U source port of the uplink packets, and if it’s different, it should update it in the session table.


The control plane flow for each session establishing is the following:




Next picture, shows the user plane flow of an uplink packet:




You can check this trace that I took in the Raspberry Pi of a session where you can see all these flows.

10/17/17

Untrusted Non-3GPP IP Access - SWu IKEv2 UE Emulator

This is an application that I’ve done in 2015 to emulate the UE in SWu interface for non-3GPP accesses.

SWu is the interface between UE and the ePDG as defined by the 3GPP, and is an IKEv2 based protocol with some minor modifications that can be found in 3GPP 33.402. The IKEv2 control plane is used to perform authentication, session creation and also to negotiate the IPSec sessions parameters to be used at the user plane level.

This application is focused on the IKEv2 control plane, just like the MME/SGW Emulator. The IPSec packets carrying user plane packets coming from the ePDG towards the UE are just ignored.

This application can use any network type (Wifi, Fixed, Mobile) to establish an IKEv2 Tunnel towards the ePDG and can be used in a more broader way than just the VoWifi scenario, since any APN can be requested. I personally think that corporate APNs can also be a big driver for these non-3GPP accesses, and not only VoWifi/VoLTE/IMS. The current obstacle for this to happen is the lack of non-3GPP IKEv2/IPSec dialers or applications for smartphones/laptops. Currently, If SWu is supported by a smartphone, it is built only for VoWifi/VoLTE/IMS, and the end-user is not aware or able to easily modify any setting.

This application takes the untrusted non-3GPP access to next level in a very deep way. We can check every single KEY used in the IKEv2 process, which allow us to decode any IKEv2 trace in wireshark. 
Since this application is built inside the MME/SGW emulator application that I’ve done previously, it can also emulate very easily handovers from non-3GPP to 3GPP and from 3GPP to non-3GPP.


The next picture shows a resume of the IKEv2 implementation, which parameters are sent in each message, and how the different keys are generated and used.

Only the first two messages are unencrypted. After the Diffie-Helman exchange, everything is encrypted and integrity protected with keys derived from this exchange.

We need to send the NAI and APN information to the ePDG, so that it can request authentication and subscription information from the AAA/HSS for the correct IMSI. This uses the IDi and IDr IKEv2 payload types that are sent in the third message.

Some IKEv2 configuration payload were also defined by IANA for 3GPP usage like the P-CSCF IPv4 or P-CSCF IPv6 used in VoWifi.

This applications supports the following RFCs and options:
  •       IKEv2 RFC 5996
  •      EAP-AKA Authentication RFC 4187
  •      IKEv2 Encryption: AES-CBC-128
  •      IKEv2 Pseudo Random Function: PRF-HMAC-SHA1
  •      IKEv2 Integrity: HMAC-SHA1-96
  •      Diffie-Helman Group 1, 2, 5 and 14 (group 2 by default)
  •      No Certificates
  •      NAT-T
  •      IKEv2 over UDP port 500 or 4500


So, how can this application handle the IKEv2 authentication phase defined in 3GPP 33.402?

It needs to run the AKA algorithm in the USIM when receiving a RAND/AUTN from the ePDG in the EAP payload to get the CK and IK from the USIM, since they are needed to calculate the Master Sesssion Key. To accomplish this we need a USB modem that supports the AT Commands +CRSM and +CSIM. 

[In my LTE security post I already explained how to run the AKA procedure in the USIM using these commands].

In the application we just need to set the COM port. The application can also retrieve the IMSI from the SIM Card using the AT+CIMI command, and build the NAI automatically.

In the test phase, while building this application, and before I have implemented the USIM card interaction through the modem, I needed to have some dummy SIM card information to be retrieved from the ePDG from the AAA/HSS. Since this was not possible with the real AAA/HLR I was using, I had to build a very simple SWm and S6b Diameter Server in my OCS/PCRF/EIR Diameter Server application, that would have a dummy data base with the APNs I was going to use and some authentication information that could match the one being sent from this application. This allowed me to complete the IKEv2 flows with the ePDG more easily.
The last phase of development was just the modem integration to get the IMSI and to run the AKA procedure in the USIM.

So let’s see the application in use.

We will perform the following actions:
  • Activate a session in ePDG
  • Simulate an handover to LTE
  • Simulate an handover back to the ePDG
  • Disconnect the session to the ePDG


The next picture shows the complete flow:





So when we activate the session in the ePDG we have this result:


When we go to the SGW emulator tab and perform handover we get this output:



When we go back to the UE emulator and perform Start IKE Handover we get this output:



The traces for this session can be downloaded.

  • This one done in the ePDG and PGW, where IKEv2 packets are already decoded
  • This one done in the UE (that was also the SGW emulator and the SWm/S6b Diameter Server). IKEv2 packets are not decoded. We need to use the Wireshark ISAKMP IKEv2 Decryption Table in Edit->Preferences->Protocols->ISAKMP and fill it with the corresponding keys shown in the application screenshots below, to properly decode the IKEv2 packets.


Note: Please configure TCP port 3869 (Gx), 3870 (SWm) and 3871 (S6b) as Diameter protocol.