Closed dziugas-wg2 closed 1 year ago
How have you set the values in your messages' structures ? In FGMMSecProtNASMessage
and EMMSecProtNASMessage
, the NASMessage
field is a Buf
, hence supposed to be assigned with a bytes buffer.
Maybe this happens because you decode a message with parse_NAS5G(buf, inner=True)
: in this case, the NASMessage
part gets substituted with the structured inner NAS message. But, that will make cryptographic transforms fail on it.
Maybe I should switch the default value for inner
to False in parse_NAS()
and parse_NAS5G()
, to avoid that kind of confusion ?
Thanks for reply, @p1-bmu. We are using parse_NAS_MO() to parse NAS messages and MAC verification seem to be working just fine for other MO NAS messages.
Here is an attempt to parse and verify the MAC of the the same 5GMMRegistrationRequest from the above.
>>> nas_bytes = b'~\x01\xc5/\x18&\x1a~\x00A\t\x00\x0b\xf2\x00\xf1\x10\x01\x00A^\xf0\x820.\x04\xf0p\xf0pq\x008~\x00A\t\x00\x0b\xf2\x00\xf1\x10\x01\x00A^\xf0\x820\x10\x01\x07.\x04\xf0p\xf0p/\x02\x01\x01R\x00\xf1\x10\x00\x00\x01\x17\x07\xf0p\xc0@\x19\x80\xb0\x18\x01\x00t\x00\x00\x90S\x01\x01'
>>> nas, err = NAS.parse_NAS_MO(nas_bytes)
>>> err
0
>>> nas
<5GMMSecProtNASMessage : <5GMMHeaderSec : <EPD : 126 (5GMM)><spare : 0x0><SecHdr : 1 (Integrity protected)>><MAC : 0xc52f1826><Seqn : 26><5GMMRegistrationRequest : <5GMMHeader : <EPD : 126 (5GMM)><spare : 0x0><SecHdr : 0 (No security)><Type : 65 (Registration request)>><NAS_KSI : <NAS_KSI : <TSC : 0 (native security context)><Value : 0>>><5GSRegType : <5GSRegType : <FOR : 1 (Follow-on request pending)><Value : 1 (initial registration)>>><5GSID : <L : 11><5GSID : <ind : 0xf><spare : 0><Type : 2 (5G-GUTI)><PLMN : 00101><AMFRegionID : 1><AMFSetID : 1><AMFPtr : 1><5GTMSI : 0x5ef08230>>><NonCurrentNativeNAS_KSI [transparent] : <T : 12><V : 0>><5GMMCap [transparent] : <T : 16><L : 1><V : 0x00>><UESecCap : <T : 46><L : 4><UESecCap : <5G-EA0 : 1><5G-EA1_128 : 1><5G-EA2_128 : 1><5G-EA3_128 : 1><5G-EA4 : 0><5G-EA5 : 0><5G-EA6 : 0><5G-EA7 : 0><5G-IA0 : 0><5G-IA1_128 : 1><5G-IA2_128 : 1><5G-IA3_128 : 1><5G-IA4 : 0><5G-IA5 : 0><5G-IA6 : 0><5G-IA7 : 0><EEA0 : 1><EEA1_128 : 1><EEA2_128 : 1><EEA3_128 : 1><EEA4 : 0><EEA5 : 0><EEA6 : 0><EEA7 : 0><EIA0 : 0><EIA1_128 : 1><EIA2_128 : 1><EIA3_128 : 1><EIA4 : 0><EIA5 : 0><EIA6 : 0><EIA7 : 0><spare [transparent] : 0x>>><NSSAI [transparent] : <T : 47><L : 2><V : 0x0101>><TAI [transparent] : <T : 82><V : 0x000000000000>><EPSUENetCap [transparent] : <T : 23><L : 2><V : 0x0000>><ULDataStat [transparent] : <T : 64><L : 2><V : 0x0000>><PDUSessStat [transparent] : <T : 80><L : 2><V : 0x0000>><MICOInd [transparent] : <T : 11><V : 0>><UEStatus [transparent] : <T : 43><L : 1><V : 0x00>><AddGUTI [transparent] : <T : 119><L : 11><V : 0xf200000000000000000000>><AllowedPDUSessStat [transparent] : <T : 37><L : 2><V : 0x0000>><UEUsage [transparent] : <T : 24><L : 1><V : 0x00>><5GSDRXParam [transparent] : <T : 81><L : 1><V : 0x00>><EPSNASContainer [transparent] : <T : 112><L : 2><V : 0x0700>><LADNInd [transparent] : <T : 116><L : 0><V : 0x>><PayloadContainerType [transparent] : <T : 8><V : 1 (N1 SM information)>><PayloadContainer [transparent] : <T : 123><L : 1><V : 0x00>><NetSlicingInd [transparent] : <T : 9><V : 0>><5GSUpdateType [transparent] : <T : 83><L : 1><V : 0x00>><MSCm2 [transparent] : <T : 65><L : 3><V : 0x400000>><SuppCodecs [transparent] : <T : 66><L : 3><V : 0x000100>><NASContainer : <T : 113><L : 56><5GMMRegistrationRequest : <5GMMHeader : <EPD : 126 (5GMM)><spare : 0x0><SecHdr : 0 (No security)><Type : 65 (Registration request)>><NAS_KSI : <NAS_KSI : <TSC : 0 (native security context)><Value : 0>>><5GSRegType : <5GSRegType : <FOR : 1 (Follow-on request pending)><Value : 1 (initial registration)>>><5GSID : <L : 11><5GSID : <ind : 0xf><spare : 0><Type : 2 (5G-GUTI)><PLMN : 00101><AMFRegionID : 1><AMFSetID : 1><AMFPtr : 1><5GTMSI : 0x5ef08230>>><NonCurrentNativeNAS_KSI [transparent] : <T : 12><V : 0>><5GMMCap : <T : 16><L : 1><5GMMCap : <SGC : 0><5G-HC-CP-CIoT : 0><N3Data : 0><5G-CP-CIoT : 0><RestrictEC : 0><LPP : 1><HOAttach : 1><S1Mode : 1><RACS [transparent] : 0><NSSAA [transparent] : 0><5G-LCS [transparent] : 0><V2XCNPC5 [transparent] : 0><V2XCEPC5 [transparent] : 0><V2X [transparent] : 0><5G-UP-CIoT [transparent] : 0><5GSRVCC [transparent] : 0><spare [transparent] : 0><5G-EHC-CP-CIoT [transparent] : 0><MultipleUP [transparent] : 0><WUSA [transparent] : 0><CAG [transparent] : 0><spare [transparent] : 0x>>><UESecCap : <T : 46><L : 4><UESecCap : <5G-EA0 : 1><5G-EA1_128 : 1><5G-EA2_128 : 1><5G-EA3_128 : 1><5G-EA4 : 0><5G-EA5 : 0><5G-EA6 : 0><5G-EA7 : 0><5G-IA0 : 0><5G-IA1_128 : 1><5G-IA2_128 : 1><5G-IA3_128 : 1><5G-IA4 : 0><5G-IA5 : 0><5G-IA6 : 0><5G-IA7 : 0><EEA0 : 1><EEA1_128 : 1><EEA2_128 : 1><EEA3_128 : 1><EEA4 : 0><EEA5 : 0><EEA6 : 0><EEA7 : 0><EIA0 : 0><EIA1_128 : 1><EIA2_128 : 1><EIA3_128 : 1><EIA4 : 0><EIA5 : 0><EIA6 : 0><EIA7 : 0><spare [transparent] : 0x>>><NSSAI : <T : 47><L : 2><NSSAI : <SNSSAI : <Len : 1><SNSSAI : <SST : 1 (eMBB)><SD [transparent] : 0x><MappedHPLMNSST [transparent] : 0><MappedHPLMNSD [transparent] : 0x>>>>><TAI : <T : 82><5GSTAI : <PLMN : 00101><TAC : 0x000001>>><EPSUENetCap : <T : 23><L : 7><UENetCap : <EEA0 : 1><EEA1_128 : 1><EEA2_128 : 1><EEA3_128 : 1><EEA4 : 0><EEA5 : 0><EEA6 : 0><EEA7 : 0><EIA0 : 0><EIA1_128 : 1><EIA2_128 : 1><EIA3_128 : 1><EIA4 : 0><EIA5 : 0><EIA6 : 0><EIA7 : 0><UEA0 : 1><UEA1 : 1><UEA2 : 0><UEA3 : 0><UEA4 : 0><UEA5 : 0><UEA6 : 0><UEA7 : 0><UCS2 : 0><UIA1 : 1><UIA2 : 0><UIA3 : 0><UIA4 : 0><UIA5 : 0><UIA6 : 0><UIA7 : 0><ProSe_dd : 0><ProSe : 0><H245_ASH : 0><ACC_CSFB : 1><LPP : 1><LCS : 0><X1_SRVCC : 0><NF : 1><ePCO : 1><HC_CP_CIoT : 0><ERw_oPDN : 0><S1U_data : 0><UP_CIoT : 0><CP_CIoT : 0><ProSe_relay : 0><ProSe_dc : 0><spare : 1><spare : 0><spare : 1><spare : 1><spare : 0><spare : 0><spare : 0><MultiDRB : 0><spare [transparent] : 0x>>><ULDataStat [transparent] : <T : 64><L : 2><V : 0x0000>><PDUSessStat [transparent] : <T : 80><L : 2><V : 0x0000>><MICOInd [transparent] : <T : 11><V : 0>><UEStatus [transparent] : <T : 43><L : 1><V : 0x00>><AddGUTI [transparent] : <T : 119><L : 11><V : 0xf200000000000000000000>><AllowedPDUSessStat [transparent] : <T : 37><L : 2><V : 0x0000>><UEUsage : <T : 24><L : 1><UEUsage : <spare : 0x00><Value : 0 (voice centric)>>><5GSDRXParam [transparent] : <T : 81><L : 1><V : 0x00>><EPSNASContainer [transparent] : <T : 112><L : 2><V : 0x0700>><LADNInd : <T : 116><L : 0><LADNInd : >><PayloadContainerType [transparent] : <T : 8><V : 1 (N1 SM information)>><PayloadContainer [transparent] : <T : 123><L : 1><V : 0x00>><NetSlicingInd : <T : 9><NetSlicingInd : <spare : 0><DCNI : 0 (requested NSSAI not created from default configured NSSAI)><NSSCI : 0 (network slicing subscription not changed)>>><5GSUpdateType : <T : 83><L : 1><5GSUpdateType : <spare : 0><EPS-PNB-CIoT : 0><5GS-PNB-CIoT : 0><NG-RAN-RCU : 0><SMSRequested : 1>>><MSCm2 [transparent] : <T : 65><L : 3><V : 0x400000>><SuppCodecs [transparent] : <T : 66><L : 3><V : 0x000100>><NASContainer [transparent] : <T : 113><L : 2><V : 0x0000>><EPSBearerCtxtStat [transparent] : <T : 96><L : 2><V : 0x0000>><ExtDRXParam [transparent] : <T : 110><L : 1><V : 0x00>><T3324 [transparent] : <T : 106><L : 1><V : 0x00>><UERadioCapID [transparent] : <T : 103><L : 1><V : 0x00>><MappedNSSAI [transparent] : <T : 53><L : 1><V : 0x00>><AddInfoReq [transparent] : <T : 72><L : 1><V : 0x00>><WUSAssistInfo [transparent] : <T : 26><L : 1><V : 0x00>><N5GCInd [transparent] : <T : 10>><NBN1ModeDRXParam [transparent] : <T : 48><L : 1><V : 0x00>>>><EPSBearerCtxtStat [transparent] : <T : 96><L : 2><V : 0x0000>><ExtDRXParam [transparent] : <T : 110><L : 1><V : 0x00>><T3324 [transparent] : <T : 106><L : 1><V : 0x00>><UERadioCapID [transparent] : <T : 103><L : 1><V : 0x00>><MappedNSSAI [transparent] : <T : 53><L : 1><V : 0x00>><AddInfoReq [transparent] : <T : 72><L : 1><V : 0x00>><WUSAssistInfo [transparent] : <T : 26><L : 1><V : 0x00>><N5GCInd [transparent] : <T : 10>><NBN1ModeDRXParam [transparent] : <T : 48><L : 1><V : 0x00>>>>
>>> knasint = b'\x86B\n\x11\xa9^\xdd\xa6\x08S\x9b\x9b\x91\x06\r\x17'
>>> nas.mac_verify(knasint, 0, 2, 0)
Traceback (most recent call last):
File "/Users/dziugas/Library/Application Support/JetBrains/IntelliJIdea2022.2/plugins/python/helpers/pydev/_pydevd_bundle/pydevd_exec2.py", line 3, in Exec
exec(exp, global_vars, local_vars)
File "<input>", line 1, in <module>
File "/private/var/tmp/_bazel_dziugas/41664892ed9f0826ac144a7b33f74680/execroot/__main__/bazel-out/darwin_arm64-fastbuild/bin/terminators/radiostar/vulcan/vulcan_unit_tests.runfiles/pycrate/pycrate_mobile/TS24501_FGMM.py", line 757, in mac_verify
mac = FGIA(key, seqnoff + self[2].get_val(), bearer, dir, self[2].to_bytes() + self[3].get_val())
TypeError: can't concat list to bytes
Can confirm that when calling NAS5G.parse_NAS5G()
directly with inner=False
, MAC verification is successful. So yes, defaulting inner
to False
would make sense in this case, but I am not yet sure about other implications of such change. Note that just modifying the function signatures won't help as NAS.parse_NAS_MO()
wrapper function call parse_NASLTE_MO()
/ parse_NAS5G()
functions explicitly with inner=True
:
>>> from pycrate_mobile import NAS5G
>>> nas_bytes = b'~\x01\xc5/\x18&\x1a~\x00A\t\x00\x0b\xf2\x00\xf1\x10\x01\x00A^\xf0\x820.\x04\xf0p\xf0pq\x008~\x00A\t\x00\x0b\xf2\x00\xf1\x10\x01\x00A^\xf0\x820\x10\x01\x07.\x04\xf0p\xf0p/\x02\x01\x01R\x00\xf1\x10\x00\x00\x01\x17\x07\xf0p\xc0@\x19\x80\xb0\x18\x01\x00t\x00\x00\x90S\x01\x01'
>>> knasint = b'\x0e\xe3j\x0f\x90~\x86\x87\xd9\xddfX\xf7\x0f.4'
>>> nas5g, err = NAS5G.parse_NAS5G(nas_bytes, False)
>>>err
0
>>> nas5g
<5GMMSecProtNASMessage : <5GMMHeaderSec : <EPD : 126 (5GMM)><spare : 0x0><SecHdr : 1 (Integrity protected)>><MAC : 0xc52f1826><Seqn : 26><NASMessage : 0x7e004109000bf200f1100100415ef082302e04f070f0707100387e004109000bf200f1100100415ef082301001072e04f070f0702f0201015200f1100000011707f070c0401980b018010074000090530101>>
>>> nas5g.mac_verify(knasint, 0, 2)
True
Yep, I am currently adding the inner
arg in parse_NAS_MO()
and _MT()
from NAS.py.
And I am effectively hesitating to change the default of inner=True, as it's like this since years...
@p1-bmu Found at least one consequence of that possible change: we are iterating over IEs of a NAS container and with inner=False
, I get the following:
> for element in container:
E TypeError: 'Buf' object is not iterable
Yep, that's how your application work, I guess. And you'll have to change it if you want to apply the proper cryptographic transforms on one side, and access all inner structures on the other side.
But here, you are on your own. Moreover, I hope wg2 is compliant with both the licensing of pycrate (LGPL library) and CryptoMobile (which is GPL for its C-Python binding and Python part).
The last commit https://github.com/P1sec/pycrate/commit/c7c6acb1b431518cd128ead7dd5e1ddc2cc1f996 should support the way you want to call cryptographic operations on 4G and 5G NAS messages. This is however sub-optimal, because in your case, you decode the full message, and then re-encode it to compute the MAC over the re-encoded buffer.
Thank you for coming up with a solution, I really appreciate that!
Regarding the licensing, to the best of my knowledge, we at WG2 are compliant. Some of my colleagues are maintaining open-source projects, so we know how hard and sometimes non-rewarding that might be, so we at WG2 like to contribute when we can, and please be assured that if we ever need to make a change to either of the projects, we'll make it in a public fork and send a pull request.
Thanks for your kind feedback
mac_verify()
function of theFGMMSecProtNASMessage
class verifies the message authentication code of byte stream that is a concatenation of the NAS sequence number and the NAS payload. Bytes for the latter is obtained by calling aget_val()
function of the NAS payload (that in case of protected message will by theself[3]
).https://github.com/P1sec/pycrate/blob/bedeaf30ad5762b3e67b5c66a08d3840b5ad8b3b/pycrate_mobile/TS24501_FGMM.py#L758
While this works for most of the NAS messages, it does result in a
TypeError: can't concat list to bytes exception
for the5GMMRegistrationRequest
NAS message since in this caseget_val()
returns not bytes, but a list of list that contains representation of each NAS IE in the form of integers or bytes. One possible fix that seems to work is to useto_bytes()
instead ofget_val()
to get the bytes of the NAS payload.Below are the contents of different variables when having a breakpoint set inside FGMMSecProtNASMessage::mac_verify():
Note that exactly the same problem is with the EMMAuthenticationResponse (that is an LTE message, handled by EMMSecProtNASMessage in TS24301_EMM.py):