Compiling the nrUE with -fsanitize=undefined leads to a crash
because the function nas_stream_encrypt_eia1() accesses the data
as uint32_t but the data is not aligned.
Actually this function wants 64 bits (big-endian) of data, so let's
introduce a simple function to read a big-endian 64 bits value.
It may seem bad (unoptimized), but looking at the output of gcc
without -fsanitize=undefined this will be translated as just one
movbeq instruction (on the machine where I did the test), which is
actually less instructions than the previous version which was doing
too movbel plus orq.
Moreover, after the main loop we need to process the last remaining
bytes (so less than 8). The code was wrong, reading past the input
data (and also not caring about alignment). It may work, it may fail,
it depends on many things. But it's plain wrong. So this was replaced
for something better, simpler. No need for mask32bit() anymore, only
one call to U64() is needed.
And now -fsanitize=undefined is happy, so all is good.
We also restrict the input length to be multiple of 8 bits. I don't
think it's a problem. To be refined if I'm wrong. (I think RRC and
NAS messages are 8-bits aligned, so it shouldn't be a problem.)
It should not be > 0xffffff.
Normally the core network would prevent this case from happening.
Plus to reach this value means to send a lot of NAS messages, which
is very doubtful in practice.
Anyway, it's better to deal with the case.
We exit if that happens. To be refined if needed (very doubtful).
This function was very wrong, now it's just wrong.
(It does not sanitize the input properly. Some bad input may have
unexpected effects on the nrUE.)
It needs a serious rewrite.
I just wanted to "fix" it so that it processes correctly some
correct input data.
OAI core network is sending Deregistation Accept in clear, even
after security context activation.
The standards is not clear to me, so let's accept it.
To be removed if incorrect with respect to the standards.
(The core network will need to be fixed then.)
The version before this commit is wrong, taking wrong byte for msg_type
in some cases.
I don't have much time to implement a proper NAS PDU parser, so let's go
with hardcoded values.
Some later work will be done to have a cleaner solution. As of now, it's
too much work.
Get the security algorithms from SecurityModeCommand.
Restructure the code a bit: add the function handle_security_mode_command()
and adapt the code to use it.
Derive also knas_enc (not used yet).
Do the derivation for knas_enc and knas_int only after reception
of SecurityModeCommand. We need the algorithms configured by core
network to derive those keys.
Taking the example of the AMF Set ID, the previous version of the macro
used another macro to encode the number, then marked the last 6 bits in
the bit string (of length 16/2 bytes) as unused. This is wrong, because,
assuming AMF Set ID=3, it basically truncates the number by encoding all
16 bits and cutting off the last 6, like this:
------------------- encode 16 bits
0000 0000 0000 0011
------------ take these 10 bits
So what remains is these bit positions of the number:
(16,15,14,13,12,11,10,9,8,7) -- the last 6 bits are cut (and the first 6
are either 0 or garbage, because they don't exist).
Instead, manually encode bits (10,9,8,7,6,5,4,3) in the first byte, and
(2,1) in the second byte of the bit string, like so:
------------ encode 10 bits
0000 0000 1100 0000
------------ take these 10 bits
Do the same with the pointer.
The AMF Region ID above is correct, as it is exactly 8 bits.
By using a struct we may have warnings at compilation time in some
cases if the type is not used properly.
The type definition becomes a bit weird, so we need to properly
comment.
If too strange, we can revert to the classical "void" definition.
- rename "lib_ctx" to "mac_implementation" which is what is stored because
of previous commit
- rename "mac" to "mac_context" to be clearer with what is stored in this
variable too
The main work of this commit is to rewrite cipher_aes_128_cbc_cmac()
which was too slow and created issues when running the 4G eNB on some
machines (LLL when using a b210).
Instead of creating the security context, use it, and delete it, all in
cipher_aes_128_cbc_cmac(), we introduce the function init_aes_128_cbc_cmac()
to create it, and we change cipher_aes_128_cbc_cmac() to just use it,
and we also adapt free_aes_128_cbc_cmac(). The function aes_128_cbc_cmac()
is removed, it does not follow the new init/use/free API.
Doing so, on the primary test machine used to do this work (a powerful
and fast machine where it is possible to set the CPUs' frequencies)
it was possible to limit the CPUs' frequencies to 500 MHz (it was not
possible to go lower) without any LLL (using a b210, quectel UE in
idle, no data traffic, only periodic measurement reports). Before this
commit, it was necessary to set a frequency of at least 1.8 GHz
otherwise some LLL would appear.
Then we need to adapt the rest of the code to take into account those
changes.
A notion of security container (stream_security_container_t) containing
ciphering and integrity contexts is introduced.
And the code is harmonized for, hopefully, better readability.
The (opaque) type stream_security_context_t is introduced, used by
both integrity and ciphering contexts. The idea is to use a
init()/use()/free() API for all the security procedures, identical for
all the ciphering and integrity procedures.
Rewrite sctp_create_new_listener() to use getaddrinfo() to look up
IPv4/IPv6 addresses via a common interface. This allows to use e.g. DNS,
and enables the SCTP library to listen on IPv6 ports.
To support the above, the corresponding message is changed to not take
an IPv4 address on 4 bytes (and separate IPv6), but a single name that
will be resolved via getaddrinfo()
The previous init message used to allow multiple IPv4/IPv6 addresses;
since this is not used anywhere, remove the corresponding functionality.
Modify all uses of this ITTI message to copy te SCTP init bind addr into
ITTI message.
Use getaddrinfo() to look up IP (v4/v6) addresses. The old
interface of separately specifying IPv4 and IPv6 addresses is
preserved, but both fields allow to specify types of addresses, and we
will remove one field in the future.
We did not give a cause value, and therefore NGAP's "PDU sessions
failed" encoding failed. Give a cause value in all cases, so that this
cannot happen anymore.
To prevent against this, put an AssertFatal() into the encoder that
stops if an internally sent message does not have a correct cause value
(we can at least select a default).
Print an info message for all failed PDU sessions.
In rrc_gNB_send_NGAP_INITIAL_CONTEXT_SETUP_RESP(): if a PDU session is
already established, don't count it as failed.
While testing the OAI 5G RAN in SA mode, it appears that the PDU session
establishment request is using the wrong security header type.
The current used header is: "Integrity protected and ciphered with new
5G NAS security context".
However, according to TS 24.501, Table 9.3.1, this header can only be used
by the "Security mode complete" procedure.
Changes:
- Hence, the new security header has been changed to "Integrity protected
and ciphered", which will use the security context generated during the
"Security mode complete".
- Sequences numbers and NAS counters have been updated to reflect that
change.