From 6512a7a9ca71e71dd438f8d33b4d056e623882e6 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 18 Nov 2025 18:17:56 +0100 Subject: [PATCH 1/3] Notify security keys change to CUUP during re-establishment During re-establishment, new KgNB* is derived, requiring new UP keys (KUPint, KUPenc) to be sent to CU-UP. When do_drb_integrity = true, CU-UP needs the updated integrity keys to verify DRB PDUs. Without this update, CU-UP uses old keys while the UE uses new keys, causing integrity failures. The fix ensures security info is always sent during re-establishment, which is critical for DRB integrity. --- openair2/RRC/NR/rrc_gNB.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index b18e820e91..d067428a9b 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -1063,15 +1063,10 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p) } } -#if 0 - /* According to current understanding of E1 specifications, it is not needed - * to send security information because this does not change. - * But let's keep the code here in case it's needed. - */ - // Always send security information + /* During reestablishment, for DRB integrity protection security keys change (KgNB* is derived), + * so security information MUST be sent to CU-UP to update DRB security keys. */ req.secInfo = malloc_or_fail(sizeof(*req.secInfo)); fill_security_info(rrc, ue_p, req.secInfo); -#endif /* Send E1 Bearer Context Modification Request (3GPP TS 38.463) */ sctp_assoc_t assoc_id = get_existing_cuup_for_ue(rrc, ue_p); From e1dd62df5371bc60fde814cf36510fef135f8d89 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 18 Nov 2025 23:13:50 +0100 Subject: [PATCH 2/3] Fix N2 handover integrity failures by enabling ciphering during handover setup Problem: During N2 handover with integrity protection (NIA1/NIA2), the target gNB was disabling ciphering when configuring SRB1 security, while the UE continued using the old security context from the source gNB until it received and processed the RRC Reconfiguration with masterKeyUpdate. The first PDUs sent after CFRA were still ciphered with the old keys, but the target gNB had ciphering disabled, causing it to skip deciphering and attempt integrity verification on ciphered data, leading to MAC-I mismatches and integrity failures. Specification Reference: According to TS 33.501 section 6.11, "the UE shall keep the K gNB used in the source cell until the handover or a connection re-establishment has been completed successfully". This means the UE continues using the old security context (old KRRCint, KRRCenc) until it receives and processes the RRC Reconfiguration with masterKeyUpdate. The target gNB must therefore enable ciphering during handover setup to correctly decipher and verify integrity of PDUs sent with the old security context before the UE switches to the new keys. Root Cause: The code was calling nr_rrc_pdcp_config_security(UE, false) during handover setup, which disabled ciphering. This was incorrect because: 1. The UE already has security active from the source gNB 2. The UE continues using old ciphered keys until masterKeyUpdate 3. The UE sends ciphered PDUs immediately after CFRA succeeds 4. The target gNB must be able to decipher these PDUs to verify integrity Why it only manifested when drb_integrity = "yes": The bug was always present, but only caused visible failures when integrity verification was enabled. When drb_integrity = "no", the config typically also sets integrity_algorithms to only NIA0, resulting in UE->integrity_algorithm = 0, which causes has_integrity = 0 for SRB1, skipping integrity verification and hiding the underlying ciphering bug. When drb_integrity = "yes", integrity_algorithms typically includes NIA1/NIA2, resulting in has_integrity = 1 for SRB1, causing integrity checks to run on ciphered data and fail. Fix: Change nr_rrc_pdcp_config_security(UE, false) to nr_rrc_pdcp_config_security(UE, true) during handover setup to enable ciphering immediately, allowing the target gNB to correctly decipher and verify integrity of PDUs sent with the old security context. Note: This fix is specific to handover. During initial context setup, ciphering should remain disabled until SecurityModeComplete is received, as the UE hasn't activated ciphering yet at that point. --- openair2/RRC/NR/rrc_gNB_NGAP.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index 8ba7b3e950..979fe0ac5e 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1192,7 +1192,11 @@ int rrc_gNB_process_Handover_Request(gNB_RRC_INST *rrc, instance_t instance, nga // Activate SRBs activate_srb(UE, SRB1); activate_srb(UE, SRB2); - nr_rrc_pdcp_config_security(UE, false); + // During N2 handover, the UE continues using the old security context from the source gNB + // until it receives and processes the RRC Reconfiguration with masterKeyUpdate. The first + // PDUs sent after CFRA are still ciphered with the old keys. The target gNB must enable + // ciphering during handover setup to correctly decipher and verify integrity of these PDUs. + nr_rrc_pdcp_config_security(UE, true); // Process all PDU Session Resource Setup items from handover request DevAssert(msg->nb_of_pdusessions <= NGAP_MAX_PDU_SESSION); From b7454cf5d43acae8301498bd645f21a072f49736 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 20 Nov 2025 14:57:54 +0100 Subject: [PATCH 3/3] Enable DRB integrity and ciphering in F1 and N2 HO pipelines with Quectel --- ci-scripts/conf_files/gnb-cu.sa.f1.ho.conf | 4 ++-- ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ci-scripts/conf_files/gnb-cu.sa.f1.ho.conf b/ci-scripts/conf_files/gnb-cu.sa.f1.ho.conf index f16a94b472..ca1d9de0d7 100644 --- a/ci-scripts/conf_files/gnb-cu.sa.f1.ho.conf +++ b/ci-scripts/conf_files/gnb-cu.sa.f1.ho.conf @@ -53,7 +53,7 @@ security = { # preferred ciphering algorithms # the first one of the list that an UE supports in chosen # valid values: nea0, nea1, nea2, nea3 - ciphering_algorithms = ( "nea0" ); + ciphering_algorithms = ( "nea2", "nea0" ); # preferred integrity algorithms # the first one of the list that an UE supports in chosen @@ -63,7 +63,7 @@ security = { # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' drb_ciphering = "yes"; - drb_integrity = "no"; + drb_integrity = "yes"; }; log_config : diff --git a/ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf b/ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf index dc24284c18..577e23a87d 100644 --- a/ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf +++ b/ci-scripts/conf_files/gnb.sa.band77.51prb.usrpb200.n2-ho.conf @@ -209,7 +209,7 @@ security = { # preferred ciphering algorithms # the first one of the list that an UE supports in chosen # valid values: nea0, nea1, nea2, nea3 - ciphering_algorithms = ( "nea0" ); + ciphering_algorithms = ( "nea2", "nea0" ); # preferred integrity algorithms # the first one of the list that an UE supports in chosen @@ -219,7 +219,7 @@ security = { # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' drb_ciphering = "yes"; - drb_integrity = "no"; + drb_integrity = "yes"; }; log_config :