diff --git a/epan/dissectors/asn1/lte-rrc/lte-rrc.cnf b/epan/dissectors/asn1/lte-rrc/lte-rrc.cnf index f6a65fbd55..898b5172b8 100644 --- a/epan/dissectors/asn1/lte-rrc/lte-rrc.cnf +++ b/epan/dissectors/asn1/lte-rrc/lte-rrc.cnf @@ -2512,7 +2512,7 @@ DRX-Config/setup/shortDRX/drxShortCycleTimer DISPLAY=BASE_DEC|BASE_UNIT_STRING S #.FN_BODY SecurityAlgorithmConfig pdcp_lte_info *p_pdcp_lte_info; - pdcp_security_info_t *p_security_algorithms; + pdcp_lte_security_info_t *p_security_algorithms; %(DEFAULT_BODY)s p_security_algorithms = private_data_pdcp_security_algorithms(actx); p_security_algorithms->configuration_frame = actx->pinfo->num; @@ -2529,17 +2529,17 @@ DRX-Config/setup/shortDRX/drxShortCycleTimer DISPLAY=BASE_DEC|BASE_UNIT_STRING S #.FN_BODY CipheringAlgorithm-r12 VAL_PTR=&value guint32 value; - pdcp_security_info_t *p_security_algorithms; + pdcp_lte_security_info_t *p_security_algorithms; %(DEFAULT_BODY)s p_security_algorithms = private_data_pdcp_security_algorithms(actx); - p_security_algorithms->ciphering = (enum security_ciphering_algorithm_e)value; + p_security_algorithms->ciphering = (enum lte_security_ciphering_algorithm_e)value; #.FN_BODY SecurityAlgorithmConfig/integrityProtAlgorithm VAL_PTR=&value guint32 value; - pdcp_security_info_t *p_security_algorithms; + pdcp_lte_security_info_t *p_security_algorithms; %(DEFAULT_BODY)s p_security_algorithms = private_data_pdcp_security_algorithms(actx); - p_security_algorithms->integrity = (enum security_integrity_algorithm_e)value; + p_security_algorithms->integrity = (enum lte_security_integrity_algorithm_e)value; #.FN_BODY SchedulingRequestConfig/setup/sr-ConfigIndex VAL_PTR=&value guint32 value; diff --git a/epan/dissectors/asn1/lte-rrc/packet-lte-rrc-template.c b/epan/dissectors/asn1/lte-rrc/packet-lte-rrc-template.c index 57c3e4de95..99cd507b32 100644 --- a/epan/dissectors/asn1/lte-rrc/packet-lte-rrc-template.c +++ b/epan/dissectors/asn1/lte-rrc/packet-lte-rrc-template.c @@ -2737,7 +2737,7 @@ typedef struct lte_rrc_private_data_t guint8 warning_message_segment_number; drb_mapping_t drb_mapping; drx_config_t drx_config; - pdcp_security_info_t pdcp_security; + pdcp_lte_security_info_t pdcp_security; meas_capabilities_item_band_mappings_t meas_capabilities_item_band_mappings; simult_pucch_pusch_cell_type cell_type; gboolean bcch_dl_sch_msg; @@ -2873,7 +2873,7 @@ static void private_data_set_ra_preambles(asn1_ctx_t *actx, guint8 ra_preambles) /* PDCP Security info */ -static pdcp_security_info_t* private_data_pdcp_security_algorithms(asn1_ctx_t *actx) +static pdcp_lte_security_info_t* private_data_pdcp_security_algorithms(asn1_ctx_t *actx) { lte_rrc_private_data_t *private_data = (lte_rrc_private_data_t*)lte_rrc_get_private_data(actx); return &private_data->pdcp_security; diff --git a/epan/dissectors/asn1/nr-rrc/nr-rrc.cnf b/epan/dissectors/asn1/nr-rrc/nr-rrc.cnf index efdb80599d..d3dbf67654 100644 --- a/epan/dissectors/asn1/nr-rrc/nr-rrc.cnf +++ b/epan/dissectors/asn1/nr-rrc/nr-rrc.cnf @@ -470,7 +470,17 @@ RAT-Type TYPE_PREFIX #.FN_HDR SecurityModeComplete col_append_sep_str(actx->pinfo->cinfo, COL_INFO, NULL, "Security Mode Complete"); -#.FN_HDR SecurityModeFailure +#.FN_BODY SecurityModeFailure + mac_nr_info *p_mac_nr_info; +%(DEFAULT_BODY)s + /* Look for UE identifier */ + p_mac_nr_info = (mac_nr_info *)p_get_proto_data(wmem_file_scope(), actx->pinfo, proto_mac_nr, 0); + + if (p_mac_nr_info != NULL) { + /* Inform PDCP that the UE failed to execute the securityModeCommand */ + set_pdcp_nr_security_algorithms_failed(p_mac_nr_info->ueid); + } + col_append_sep_str(actx->pinfo->cinfo, COL_INFO, NULL, "Security Mode Failure"); #.FN_HDR ULInformationTransfer @@ -1549,6 +1559,23 @@ VisitedCellInfo-r16/timeSpent-r16 DISPLAY=BASE_DEC|BASE_UNIT_STRING STRINGS=&uni mapping->rlcDlSnLength = (value=0) ? 12 : 18; } +#.FN_BODY SDAP-Config/sdap-HeaderDL VAL_PTR=&value + guint32 value; +%(DEFAULT_BODY)s + nr_drb_mapping_t *mapping = &nr_rrc_get_private_data(actx)->drb_mapping; + mapping->pdcpDlSdap = !value; + +#.FN_BODY SDAP-Config/sdap-HeaderUL VAL_PTR=&value + guint32 value; +%(DEFAULT_BODY)s + nr_drb_mapping_t *mapping = &nr_rrc_get_private_data(actx)->drb_mapping; + mapping->pdcpUlSdap = !value; + +#.FN_BODY PDCP-Config/drb/integrityProtection +%(DEFAULT_BODY)s + nr_drb_mapping_t *mapping = &nr_rrc_get_private_data(actx)->drb_mapping; + mapping->pdcpIntegrityProtection = TRUE; + #.FN_BODY DRB-ToAddMod nr_drb_mapping_t *mapping = &nr_rrc_get_private_data(actx)->drb_mapping; memset(mapping, 0, sizeof(*mapping)); @@ -1559,7 +1586,9 @@ VisitedCellInfo-r16/timeSpent-r16 DISPLAY=BASE_DEC|BASE_UNIT_STRING STRINGS=&uni /* Configure PDCP SN length(s) for this DRB */ if (mapping->pdcpUlSnLength_present || mapping->pdcpDlSnLength_present) { set_rlc_nr_drb_pdcp_seqnum_length(actx->pinfo, p_mac_nr_info->ueid, mapping->drbid, - mapping->pdcpUlSnLength, mapping->pdcpDlSnLength); + mapping->pdcpUlSnLength, mapping->pdcpDlSnLength, + mapping->pdcpUlSdap, mapping->pdcpDlSdap, + mapping->pdcpIntegrityProtection); } } @@ -1679,6 +1708,38 @@ CA-ParametersEUTRA-v1570/dl-1024QAM-TotalWeightedLayers DISPLAY=BASE_CUSTOM STRI dissect_lte_rrc_MeasResultSCG_FailureMRDC_r15_PDU(meas_result_scg_fail_mrdc_tvb, actx->pinfo, subtree, NULL); } +#.FN_BODY SecurityAlgorithmConfig + pdcp_nr_info *p_pdcp_nr_info; + pdcp_nr_security_info_t *p_security_algorithms; +%(DEFAULT_BODY)s + p_security_algorithms = &(nr_rrc_get_private_data(actx)->pdcp_security); + p_security_algorithms->configuration_frame = actx->pinfo->num; + p_security_algorithms->previous_configuration_frame = 0; + p_security_algorithms->previous_integrity = nia0; + p_security_algorithms->previous_ciphering = nea0; + + /* Look for UE identifier */ + p_pdcp_nr_info = (pdcp_nr_info *)p_get_proto_data(wmem_file_scope(), actx->pinfo, proto_pdcp_nr, 0); + if (p_pdcp_nr_info != NULL) { + /* Configure algorithms */ + set_pdcp_nr_security_algorithms(p_pdcp_nr_info->ueid, p_security_algorithms); + } + +#.FN_BODY CipheringAlgorithm VAL_PTR=&value + guint32 value; + pdcp_nr_security_info_t *p_security_algorithms; +%(DEFAULT_BODY)s + p_security_algorithms = &(nr_rrc_get_private_data(actx)->pdcp_security); + p_security_algorithms->ciphering = (enum nr_security_ciphering_algorithm_e)value; + +#.FN_BODY IntegrityProtAlgorithm VAL_PTR=&value + guint32 value; + pdcp_nr_security_info_t *p_security_algorithms; +%(DEFAULT_BODY)s + p_security_algorithms = &(nr_rrc_get_private_data(actx)->pdcp_security); + p_security_algorithms->integrity = (enum nr_security_integrity_algorithm_e)value; + + #.FN_HDR SBCCH-SL-BCH-Message proto_item *ti; @@ -1717,3 +1778,6 @@ CA-ParametersEUTRA-v1570/dl-1024QAM-TotalWeightedLayers DISPLAY=BASE_CUSTOM STRI #.FN_HDR UECapabilityInformationSidelink col_append_sep_str(actx->pinfo->cinfo, COL_INFO, NULL, "UE Capability Information Sidelink"); + + + diff --git a/epan/dissectors/asn1/nr-rrc/packet-nr-rrc-template.c b/epan/dissectors/asn1/nr-rrc/packet-nr-rrc-template.c index e0615e3e01..724b769ee6 100644 --- a/epan/dissectors/asn1/nr-rrc/packet-nr-rrc-template.c +++ b/epan/dissectors/asn1/nr-rrc/packet-nr-rrc-template.c @@ -31,6 +31,7 @@ #include "packet-cell_broadcast.h" #include "packet-mac-nr.h" #include "packet-rlc-nr.h" +#include "packet-pdcp-nr.h" #include "packet-rrc.h" #include "packet-lte-rrc.h" #include "packet-nr-rrc.h" @@ -56,6 +57,7 @@ static reassembly_table nr_rrc_sib7_reassembly_table; static reassembly_table nr_rrc_sib8_reassembly_table; extern int proto_mac_nr; +extern int proto_pdcp_nr; /* Include constants */ #include "packet-nr-rrc-val.h" @@ -166,6 +168,7 @@ typedef struct { guint8 warning_message_segment_number; nr_drb_mapping_t drb_mapping; lpp_pos_sib_type_t pos_sib_type; + pdcp_nr_security_info_t pdcp_security; } nr_rrc_private_data_t; /* Helper function to get or create a struct that will be actx->private_data */ diff --git a/epan/dissectors/packet-lte-rrc.c b/epan/dissectors/packet-lte-rrc.c index 4492ce06c1..9c677df6c4 100644 --- a/epan/dissectors/packet-lte-rrc.c +++ b/epan/dissectors/packet-lte-rrc.c @@ -15046,7 +15046,7 @@ typedef struct lte_rrc_private_data_t guint8 warning_message_segment_number; drb_mapping_t drb_mapping; drx_config_t drx_config; - pdcp_security_info_t pdcp_security; + pdcp_lte_security_info_t pdcp_security; meas_capabilities_item_band_mappings_t meas_capabilities_item_band_mappings; simult_pucch_pusch_cell_type cell_type; gboolean bcch_dl_sch_msg; @@ -15182,7 +15182,7 @@ static void private_data_set_ra_preambles(asn1_ctx_t *actx, guint8 ra_preambles) /* PDCP Security info */ -static pdcp_security_info_t* private_data_pdcp_security_algorithms(asn1_ctx_t *actx) +static pdcp_lte_security_info_t* private_data_pdcp_security_algorithms(asn1_ctx_t *actx) { lte_rrc_private_data_t *private_data = (lte_rrc_private_data_t*)lte_rrc_get_private_data(actx); return &private_data->pdcp_security; @@ -40763,12 +40763,12 @@ static const value_string lte_rrc_CipheringAlgorithm_r12_vals[] = { static int dissect_lte_rrc_CipheringAlgorithm_r12(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { guint32 value; - pdcp_security_info_t *p_security_algorithms; + pdcp_lte_security_info_t *p_security_algorithms; offset = dissect_per_enumerated(tvb, offset, actx, tree, hf_index, 8, &value, TRUE, 0, NULL); p_security_algorithms = private_data_pdcp_security_algorithms(actx); - p_security_algorithms->ciphering = (enum security_ciphering_algorithm_e)value; + p_security_algorithms->ciphering = (enum lte_security_ciphering_algorithm_e)value; return offset; @@ -40791,12 +40791,12 @@ static const value_string lte_rrc_T_integrityProtAlgorithm_vals[] = { static int dissect_lte_rrc_T_integrityProtAlgorithm(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { guint32 value; - pdcp_security_info_t *p_security_algorithms; + pdcp_lte_security_info_t *p_security_algorithms; offset = dissect_per_enumerated(tvb, offset, actx, tree, hf_index, 8, &value, TRUE, 0, NULL); p_security_algorithms = private_data_pdcp_security_algorithms(actx); - p_security_algorithms->integrity = (enum security_integrity_algorithm_e)value; + p_security_algorithms->integrity = (enum lte_security_integrity_algorithm_e)value; return offset; @@ -40812,7 +40812,7 @@ static const per_sequence_t SecurityAlgorithmConfig_sequence[] = { static int dissect_lte_rrc_SecurityAlgorithmConfig(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { pdcp_lte_info *p_pdcp_lte_info; - pdcp_security_info_t *p_security_algorithms; + pdcp_lte_security_info_t *p_security_algorithms; offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index, ett_lte_rrc_SecurityAlgorithmConfig, SecurityAlgorithmConfig_sequence); diff --git a/epan/dissectors/packet-mac-nr.h b/epan/dissectors/packet-mac-nr.h index 5e48bee9fc..800132082e 100644 --- a/epan/dissectors/packet-mac-nr.h +++ b/epan/dissectors/packet-mac-nr.h @@ -134,6 +134,9 @@ typedef struct nr_drb_mapping_t guint8 pdcpUlSnLength; /* Part of PDCP config - optional */ gboolean pdcpDlSnLength_present; guint8 pdcpDlSnLength; /* Part of PDCP config - optional */ + gboolean pdcpUlSdap; + gboolean pdcpDlSdap; + gboolean pdcpIntegrityProtection; } nr_drb_mapping_t; diff --git a/epan/dissectors/packet-nr-rrc.c b/epan/dissectors/packet-nr-rrc.c index f2818a6535..c136c3c32e 100644 --- a/epan/dissectors/packet-nr-rrc.c +++ b/epan/dissectors/packet-nr-rrc.c @@ -39,6 +39,7 @@ #include "packet-cell_broadcast.h" #include "packet-mac-nr.h" #include "packet-rlc-nr.h" +#include "packet-pdcp-nr.h" #include "packet-rrc.h" #include "packet-lte-rrc.h" #include "packet-nr-rrc.h" @@ -64,6 +65,7 @@ static reassembly_table nr_rrc_sib7_reassembly_table; static reassembly_table nr_rrc_sib8_reassembly_table; extern int proto_mac_nr; +extern int proto_pdcp_nr; /* Include constants */ @@ -361,7 +363,7 @@ typedef enum _T_targetRAT_Type_enum { } T_targetRAT_Type_enum; /*--- End of included file: packet-nr-rrc-val.h ---*/ -#line 62 "./asn1/nr-rrc/packet-nr-rrc-template.c" +#line 64 "./asn1/nr-rrc/packet-nr-rrc-template.c" /* Initialize the protocol and registered fields */ static int proto_nr_rrc = -1; @@ -6207,7 +6209,7 @@ static int hf_nr_rrc_lowSE_64QAM_MCS_TableSidelink_r16_01 = -1; /* T_lowSE_64QA static int dummy_hf_nr_rrc_eag_field = -1; /* never registered */ /*--- End of included file: packet-nr-rrc-hf.c ---*/ -#line 66 "./asn1/nr-rrc/packet-nr-rrc-template.c" +#line 68 "./asn1/nr-rrc/packet-nr-rrc-template.c" static int hf_nr_rrc_serialNumber_gs = -1; static int hf_nr_rrc_serialNumber_msg_code = -1; static int hf_nr_rrc_serialNumber_upd_nb = -1; @@ -8594,7 +8596,7 @@ static gint ett_nr_rrc_T_fr1_r16_02 = -1; static gint ett_nr_rrc_T_fr2_r16_02 = -1; /*--- End of included file: packet-nr-rrc-ett.c ---*/ -#line 103 "./asn1/nr-rrc/packet-nr-rrc-template.c" +#line 105 "./asn1/nr-rrc/packet-nr-rrc-template.c" static gint ett_nr_rrc_DedicatedNAS_Message = -1; static gint ett_nr_rrc_targetRAT_MessageContainer = -1; static gint ett_nr_rrc_nas_Container = -1; @@ -8661,6 +8663,7 @@ typedef struct { guint8 warning_message_segment_number; nr_drb_mapping_t drb_mapping; lpp_pos_sib_type_t pos_sib_type; + pdcp_nr_security_info_t pdcp_security; } nr_rrc_private_data_t; /* Helper function to get or create a struct that will be actx->private_data */ @@ -24612,6 +24615,10 @@ dissect_nr_rrc_T_integrityProtection(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx offset = dissect_per_enumerated(tvb, offset, actx, tree, hf_index, 1, NULL, FALSE, 0, NULL); + nr_drb_mapping_t *mapping = &nr_rrc_get_private_data(actx)->drb_mapping; + mapping->pdcpIntegrityProtection = TRUE; + + return offset; } @@ -25187,8 +25194,13 @@ static const value_string nr_rrc_T_sdap_HeaderDL_vals[] = { static int dissect_nr_rrc_T_sdap_HeaderDL(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + guint32 value; offset = dissect_per_enumerated(tvb, offset, actx, tree, hf_index, - 2, NULL, FALSE, 0, NULL); + 2, &value, FALSE, 0, NULL); + + nr_drb_mapping_t *mapping = &nr_rrc_get_private_data(actx)->drb_mapping; + mapping->pdcpDlSdap = !value; + return offset; } @@ -25203,8 +25215,13 @@ static const value_string nr_rrc_T_sdap_HeaderUL_vals[] = { static int dissect_nr_rrc_T_sdap_HeaderUL(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + guint32 value; offset = dissect_per_enumerated(tvb, offset, actx, tree, hf_index, - 2, NULL, FALSE, 0, NULL); + 2, &value, FALSE, 0, NULL); + + nr_drb_mapping_t *mapping = &nr_rrc_get_private_data(actx)->drb_mapping; + mapping->pdcpUlSdap = !value; + return offset; } @@ -25371,7 +25388,9 @@ dissect_nr_rrc_DRB_ToAddMod(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx /* Configure PDCP SN length(s) for this DRB */ if (mapping->pdcpUlSnLength_present || mapping->pdcpDlSnLength_present) { set_rlc_nr_drb_pdcp_seqnum_length(actx->pinfo, p_mac_nr_info->ueid, mapping->drbid, - mapping->pdcpUlSnLength, mapping->pdcpDlSnLength); + mapping->pdcpUlSnLength, mapping->pdcpDlSnLength, + mapping->pdcpUlSdap, mapping->pdcpDlSdap, + mapping->pdcpIntegrityProtection); } } @@ -25423,8 +25442,14 @@ static const value_string nr_rrc_CipheringAlgorithm_vals[] = { static int dissect_nr_rrc_CipheringAlgorithm(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + guint32 value; + pdcp_nr_security_info_t *p_security_algorithms; offset = dissect_per_enumerated(tvb, offset, actx, tree, hf_index, - 8, NULL, TRUE, 0, NULL); + 8, &value, TRUE, 0, NULL); + + p_security_algorithms = &(nr_rrc_get_private_data(actx)->pdcp_security); + p_security_algorithms->ciphering = (enum nr_security_ciphering_algorithm_e)value; + return offset; } @@ -25445,8 +25470,15 @@ static const value_string nr_rrc_IntegrityProtAlgorithm_vals[] = { static int dissect_nr_rrc_IntegrityProtAlgorithm(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + guint32 value; + pdcp_nr_security_info_t *p_security_algorithms; offset = dissect_per_enumerated(tvb, offset, actx, tree, hf_index, - 8, NULL, TRUE, 0, NULL); + 8, &value, TRUE, 0, NULL); + + p_security_algorithms = &(nr_rrc_get_private_data(actx)->pdcp_security); + p_security_algorithms->integrity = (enum nr_security_integrity_algorithm_e)value; + + return offset; } @@ -25460,9 +25492,25 @@ static const per_sequence_t SecurityAlgorithmConfig_sequence[] = { static int dissect_nr_rrc_SecurityAlgorithmConfig(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { + pdcp_nr_info *p_pdcp_nr_info; + pdcp_nr_security_info_t *p_security_algorithms; offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index, ett_nr_rrc_SecurityAlgorithmConfig, SecurityAlgorithmConfig_sequence); + p_security_algorithms = &(nr_rrc_get_private_data(actx)->pdcp_security); + p_security_algorithms->configuration_frame = actx->pinfo->num; + p_security_algorithms->previous_configuration_frame = 0; + p_security_algorithms->previous_integrity = nia0; + p_security_algorithms->previous_ciphering = nea0; + + /* Look for UE identifier */ + p_pdcp_nr_info = (pdcp_nr_info *)p_get_proto_data(wmem_file_scope(), actx->pinfo, proto_pdcp_nr, 0); + if (p_pdcp_nr_info != NULL) { + /* Configure algorithms */ + set_pdcp_nr_security_algorithms(p_pdcp_nr_info->ueid, p_security_algorithms); + } + + return offset; } @@ -41136,11 +41184,21 @@ static const per_sequence_t SecurityModeFailure_sequence[] = { static int dissect_nr_rrc_SecurityModeFailure(tvbuff_t *tvb _U_, int offset _U_, asn1_ctx_t *actx _U_, proto_tree *tree _U_, int hf_index _U_) { - col_append_sep_str(actx->pinfo->cinfo, COL_INFO, NULL, "Security Mode Failure"); - + mac_nr_info *p_mac_nr_info; offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index, ett_nr_rrc_SecurityModeFailure, SecurityModeFailure_sequence); + /* Look for UE identifier */ + p_mac_nr_info = (mac_nr_info *)p_get_proto_data(wmem_file_scope(), actx->pinfo, proto_mac_nr, 0); + + if (p_mac_nr_info != NULL) { + /* Inform PDCP that the UE failed to execute the securityModeCommand */ + set_pdcp_nr_security_algorithms_failed(p_mac_nr_info->ueid); + } + + col_append_sep_str(actx->pinfo->cinfo, COL_INFO, NULL, "Security Mode Failure"); + + return offset; } @@ -85852,6 +85910,9 @@ dissect_nr_rrc_UECapabilityInformationSidelink(tvbuff_t *tvb _U_, int offset _U_ proto_item *prot_ti = proto_tree_add_item(tree, proto_nr_rrc, tvb, 0, -1, ENC_NA); proto_item_set_hidden(prot_ti); col_append_sep_str(actx->pinfo->cinfo, COL_INFO, NULL, "UE Capability Information Sidelink"); + + + offset = dissect_per_sequence(tvb, offset, actx, tree, hf_index, ett_nr_rrc_UECapabilityInformationSidelink, UECapabilityInformationSidelink_sequence); @@ -86694,7 +86755,7 @@ static int dissect_UECapabilityEnquiry_v1560_IEs_PDU(tvbuff_t *tvb _U_, packet_i /*--- End of included file: packet-nr-rrc-fn.c ---*/ -#line 515 "./asn1/nr-rrc/packet-nr-rrc-template.c" +#line 518 "./asn1/nr-rrc/packet-nr-rrc-template.c" int dissect_nr_rrc_nr_RLF_Report_r16_PDU(tvbuff_t *tvb _U_, packet_info *pinfo _U_, proto_tree *tree _U_, void *data _U_) { @@ -110075,7 +110136,7 @@ proto_register_nr_rrc(void) { "T_lowSE_64QAM_MCS_TableSidelink_r16_01", HFILL }}, /*--- End of included file: packet-nr-rrc-hfarr.c ---*/ -#line 547 "./asn1/nr-rrc/packet-nr-rrc-template.c" +#line 550 "./asn1/nr-rrc/packet-nr-rrc-template.c" { &hf_nr_rrc_serialNumber_gs, { "Geographical Scope", "nr-rrc.serialNumber.gs", @@ -112563,7 +112624,7 @@ proto_register_nr_rrc(void) { &ett_nr_rrc_T_fr2_r16_02, /*--- End of included file: packet-nr-rrc-ettarr.c ---*/ -#line 685 "./asn1/nr-rrc/packet-nr-rrc-template.c" +#line 688 "./asn1/nr-rrc/packet-nr-rrc-template.c" &ett_nr_rrc_DedicatedNAS_Message, &ett_nr_rrc_targetRAT_MessageContainer, &ett_nr_rrc_nas_Container, @@ -112650,7 +112711,7 @@ proto_register_nr_rrc(void) { /*--- End of included file: packet-nr-rrc-dis-reg.c ---*/ -#line 751 "./asn1/nr-rrc/packet-nr-rrc-template.c" +#line 754 "./asn1/nr-rrc/packet-nr-rrc-template.c" nr_rrc_etws_cmas_dcs_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal); diff --git a/epan/dissectors/packet-pdcp-lte.c b/epan/dissectors/packet-pdcp-lte.c index a2370cf56a..188b0a33c8 100644 --- a/epan/dissectors/packet-pdcp-lte.c +++ b/epan/dissectors/packet-pdcp-lte.c @@ -12,7 +12,6 @@ #include "config.h" - #include #include #include @@ -358,8 +357,8 @@ static gboolean global_pdcp_ignore_sec = FALSE; /* Ignore Set Security /* Use these values where we know the keys but may have missed the algorithm, e.g. when handing over and RRCReconfigurationRequest goes to target cell only */ -static enum security_ciphering_algorithm_e global_default_ciphering_algorithm = eea0; -static enum security_integrity_algorithm_e global_default_integrity_algorithm = eia0; +static enum lte_security_ciphering_algorithm_e global_default_ciphering_algorithm = eea0; +static enum lte_security_integrity_algorithm_e global_default_integrity_algorithm = eia0; static const value_string direction_vals[] = @@ -605,8 +604,8 @@ static wmem_map_t *pdcp_lte_sequence_analysis_report_hash = NULL; /* Gather together security settings in order to be able to do deciphering */ typedef struct pdu_security_settings_t { - enum security_ciphering_algorithm_e ciphering; - enum security_integrity_algorithm_e integrity; + enum lte_security_ciphering_algorithm_e ciphering; + enum lte_security_integrity_algorithm_e integrity; guint8* cipherKey; guint8* integrityKey; gboolean cipherKeyValid; @@ -704,6 +703,7 @@ static void addChannelSequenceInfo(pdcp_sequence_report_in_frame *p, /* May also be able to add key inputs to security tree here */ if ((pdu_security->ciphering != eea0) || (pdu_security->integrity != eia0)) { + guint32 hfn_multiplier; guint32 count; gchar *cipher_key = NULL; @@ -713,6 +713,7 @@ static void addChannelSequenceInfo(pdcp_sequence_report_in_frame *p, ti = proto_tree_add_uint(security_tree, hf_pdcp_lte_security_bearer, tvb, 0, 0, p_pdcp_lte_info->channelId-1); proto_item_set_generated(ti); + pdu_security->bearer = p_pdcp_lte_info->channelId-1; /* DIRECTION */ @@ -869,7 +870,7 @@ static void checkChannelSequenceInfo(packet_info *pinfo, tvbuff_t *tvb, guint32 snLimit = 0; /* If find stat_report_in_frame already, use that and get out */ - if (pinfo->fd->visited) { + if (PINFO_FD_VISITED(pinfo)) { p_report_in_frame = (pdcp_sequence_report_in_frame*)wmem_map_lookup(pdcp_lte_sequence_analysis_report_hash, get_report_hash_key(sequenceNumber, @@ -1022,14 +1023,6 @@ static void checkChannelSequenceInfo(packet_info *pinfo, tvbuff_t *tvb, /* Hash table for security state for a UE Maps UEId -> pdcp_security_info_t* */ -static gint pdcp_lte_ueid_hash_equal(gconstpointer v, gconstpointer v2) -{ - return (v == v2); -} -static guint pdcp_lte_ueid_hash_func(gconstpointer v) -{ - return GPOINTER_TO_UINT(v); -} static wmem_map_t *pdcp_security_hash = NULL; /* Result is (ueid, framenum) -> pdcp_security_info_t* */ @@ -1453,11 +1446,11 @@ static gboolean dissect_pdcp_lte_heur(tvbuff_t *tvb, packet_info *pinfo, } /* Called from control protocol to configure security algorithms for the given UE */ -void set_pdcp_lte_security_algorithms(guint16 ueid, pdcp_security_info_t *security_info) +void set_pdcp_lte_security_algorithms(guint16 ueid, pdcp_lte_security_info_t *security_info) { /* Use for this frame so can check integrity on SecurityCommandRequest frame */ /* N.B. won't work for internal, non-RRC signalling methods... */ - pdcp_security_info_t *p_frame_security; + pdcp_lte_security_info_t *p_frame_security; /* Disable this entire sub-routine with the Preference */ /* Used when the capture is already deciphered */ @@ -1466,12 +1459,12 @@ void set_pdcp_lte_security_algorithms(guint16 ueid, pdcp_security_info_t *securi } /* Create or update current settings, by UEID */ - pdcp_security_info_t* ue_security = - (pdcp_security_info_t*)wmem_map_lookup(pdcp_security_hash, - GUINT_TO_POINTER((guint)ueid)); + pdcp_lte_security_info_t* ue_security = + (pdcp_lte_security_info_t*)wmem_map_lookup(pdcp_security_hash, + GUINT_TO_POINTER((guint)ueid)); if (ue_security == NULL) { /* Copy whole security struct */ - ue_security = wmem_new(wmem_file_scope(), pdcp_security_info_t); + ue_security = wmem_new(wmem_file_scope(), pdcp_lte_security_info_t); *ue_security = *security_info; /* And add into security table */ @@ -1491,7 +1484,7 @@ void set_pdcp_lte_security_algorithms(guint16 ueid, pdcp_security_info_t *securi /* Also add an entry for this PDU already to use these settings, as otherwise it won't be present when we query it on the first pass. */ - p_frame_security = wmem_new(wmem_file_scope(), pdcp_security_info_t); + p_frame_security = wmem_new(wmem_file_scope(), pdcp_lte_security_info_t); *p_frame_security = *ue_security; wmem_map_insert(pdcp_security_result_hash, get_ueid_frame_hash_key(ueid, ue_security->configuration_frame, TRUE), @@ -1502,9 +1495,9 @@ void set_pdcp_lte_security_algorithms(guint16 ueid, pdcp_security_info_t *securi void set_pdcp_lte_security_algorithms_failed(guint16 ueid) { /* Look up current state by UEID */ - pdcp_security_info_t* ue_security = - (pdcp_security_info_t*)wmem_map_lookup(pdcp_security_hash, - GUINT_TO_POINTER((guint)ueid)); + pdcp_lte_security_info_t* ue_security = + (pdcp_lte_security_info_t*)wmem_map_lookup(pdcp_security_hash, + GUINT_TO_POINTER((guint)ueid)); if (ue_security != NULL) { /* TODO: could remove from table if previous_configuration_frame is 0 */ /* Go back to previous state */ @@ -1785,8 +1778,8 @@ static int dissect_pdcp_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint32 reserved_value; guint32 seqnum = 0; - pdcp_security_info_t *current_security = NULL; /* current security for this UE */ - pdcp_security_info_t *pdu_security; /* security in place for this PDU */ + pdcp_lte_security_info_t *current_security = NULL; /* current security for this UE */ + pdcp_lte_security_info_t *pdu_security; /* security in place for this PDU */ proto_tree *security_tree = NULL; proto_item *security_ti; tvbuff_t *payload_tvb; @@ -1840,13 +1833,13 @@ static int dissect_pdcp_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, /***************************************/ /* UE security algorithms */ - if (!pinfo->fd->visited) { + if (!PINFO_FD_VISITED(pinfo)) { /* Look up current state by UEID */ - current_security = (pdcp_security_info_t*)wmem_map_lookup(pdcp_security_hash, - GUINT_TO_POINTER((guint)p_pdcp_info->ueid)); + current_security = (pdcp_lte_security_info_t*)wmem_map_lookup(pdcp_security_hash, + GUINT_TO_POINTER((guint)p_pdcp_info->ueid)); if (current_security != NULL) { /* Store any result for this frame in the result table */ - pdcp_security_info_t *security_to_store = wmem_new(wmem_file_scope(), pdcp_security_info_t); + pdcp_lte_security_info_t *security_to_store = wmem_new(wmem_file_scope(), pdcp_lte_security_info_t); /* Take a deep copy of the settings */ *security_to_store = *current_security; wmem_map_insert(pdcp_security_result_hash, @@ -1858,7 +1851,7 @@ static int dissect_pdcp_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, if ((global_default_ciphering_algorithm != eea0) || (global_default_integrity_algorithm != eia0)) { /* Copy algorithms from preference defaults */ - pdcp_security_info_t *security_to_store = wmem_new0(wmem_file_scope(), pdcp_security_info_t); + pdcp_lte_security_info_t *security_to_store = wmem_new0(wmem_file_scope(), pdcp_lte_security_info_t); security_to_store->ciphering = global_default_ciphering_algorithm; security_to_store->integrity = global_default_integrity_algorithm; security_to_store->seen_next_ul_pdu = TRUE; @@ -1870,8 +1863,8 @@ static int dissect_pdcp_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, } /* Show security settings for this PDU */ - pdu_security = (pdcp_security_info_t*)wmem_map_lookup(pdcp_security_result_hash, - get_ueid_frame_hash_key(p_pdcp_info->ueid, pinfo->num, FALSE)); + pdu_security = (pdcp_lte_security_info_t*)wmem_map_lookup(pdcp_security_result_hash, + get_ueid_frame_hash_key(p_pdcp_info->ueid, pinfo->num, FALSE)); if (pdu_security != NULL) { /* Create subtree */ security_ti = proto_tree_add_string_format(pdcp_tree, @@ -2312,8 +2305,6 @@ static int dissect_pdcp_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, /* Now deal with the payload */ /*******************************************************/ - /* Check pdu_security_settings - may need to do deciphering before calling - further dissectors on payload */ payload_tvb = decipher_payload(tvb, pinfo, &offset, &pdu_security_settings, p_pdcp_info, pdu_security ? pdu_security->seen_next_ul_pdu: FALSE, &payload_deciphered); @@ -2337,6 +2328,7 @@ static int dissect_pdcp_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, Call lte-rrc dissector (according to direction and channel type) if we have valid data */ if ((global_pdcp_dissect_signalling_plane_as_rrc) && ((pdu_security == NULL) || (pdu_security->ciphering == eea0) || payload_deciphered || !pdu_security->seen_next_ul_pdu)) { + /* Get appropriate dissector handle */ dissector_handle_t rrc_handle = lookup_rrc_dissector_handle(p_pdcp_info); @@ -2355,11 +2347,11 @@ static int dissect_pdcp_lte(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, } else { /* Just show data */ - proto_tree_add_item(pdcp_tree, hf_pdcp_lte_signalling_data, payload_tvb, offset, - data_length, ENC_NA); + proto_tree_add_item(pdcp_tree, hf_pdcp_lte_signalling_data, payload_tvb, offset, + data_length, ENC_NA); } - if (!pinfo->fd->visited && + if (!PINFO_FD_VISITED(pinfo) && (current_security != NULL) && !current_security->seen_next_ul_pdu && p_pdcp_info->direction == DIRECTION_UPLINK) { @@ -2824,6 +2816,7 @@ void proto_register_pdcp(void) } }, + /* Security fields */ { &hf_pdcp_lte_security, { "Security Config", "pdcp-lte.security-config", FT_STRING, BASE_NONE, 0, 0x0, @@ -2877,7 +2870,7 @@ void proto_register_pdcp(void) "pdcp-lte.security-config.integrity-key", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } - }, + } }; static gint *ett[] = @@ -3043,9 +3036,9 @@ void proto_register_pdcp(void) pdcp_sequence_analysis_channel_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal); pdcp_lte_sequence_analysis_report_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), pdcp_result_hash_func, pdcp_result_hash_equal); - pdcp_security_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), pdcp_lte_ueid_hash_func, pdcp_lte_ueid_hash_equal); + pdcp_security_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal); pdcp_security_result_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), pdcp_lte_ueid_frame_hash_func, pdcp_lte_ueid_frame_hash_equal); - pdcp_security_key_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), pdcp_lte_ueid_hash_func, pdcp_lte_ueid_hash_equal); + pdcp_security_key_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal); } void proto_reg_handoff_pdcp_lte(void) diff --git a/epan/dissectors/packet-pdcp-lte.h b/epan/dissectors/packet-pdcp-lte.h index 2d43cdadf3..bffc26f404 100644 --- a/epan/dissectors/packet-pdcp-lte.h +++ b/epan/dissectors/packet-pdcp-lte.h @@ -45,21 +45,21 @@ typedef enum #define PDCP_SN_LENGTH_15_BITS 15 #define PDCP_SN_LENGTH_18_BITS 18 -enum security_integrity_algorithm_e { eia0, eia1, eia2, eia3 }; -enum security_ciphering_algorithm_e { eea0, eea1, eea2, eea3 }; +enum lte_security_integrity_algorithm_e { eia0, eia1, eia2, eia3 }; +enum lte_security_ciphering_algorithm_e { eea0, eea1, eea2, eea3 }; -typedef struct pdcp_security_info_t +typedef struct pdcp_lte_security_info_t { - guint32 configuration_frame; - gboolean seen_next_ul_pdu; /* i.e. have we seen SecurityModeResponse */ - enum security_integrity_algorithm_e integrity; - enum security_ciphering_algorithm_e ciphering; + guint32 configuration_frame; + gboolean seen_next_ul_pdu; /* i.e. have we seen SecurityModeResponse */ + enum lte_security_integrity_algorithm_e integrity; + enum lte_security_ciphering_algorithm_e ciphering; /* Store previous settings so can revert if get SecurityModeFailure */ - guint32 previous_configuration_frame; - enum security_integrity_algorithm_e previous_integrity; - enum security_ciphering_algorithm_e previous_ciphering; -} pdcp_security_info_t; + guint32 previous_configuration_frame; + enum lte_security_integrity_algorithm_e previous_integrity; + enum lte_security_ciphering_algorithm_e previous_ciphering; +} pdcp_lte_security_info_t; /* Info attached to each LTE PDCP/RoHC packet */ @@ -174,7 +174,7 @@ typedef struct pdcp_lte_info /* Called by RRC, or other configuration protocols */ /* Function to configure ciphering & integrity algorithms */ -void set_pdcp_lte_security_algorithms(guint16 ueid, pdcp_security_info_t *security_info); +void set_pdcp_lte_security_algorithms(guint16 ueid, pdcp_lte_security_info_t *security_info); /* Function to indicate securityModeCommand did not complete */ void set_pdcp_lte_security_algorithms_failed(guint16 ueid); diff --git a/epan/dissectors/packet-pdcp-nr.c b/epan/dissectors/packet-pdcp-nr.c index 88a8cb3934..93e005aa15 100644 --- a/epan/dissectors/packet-pdcp-nr.c +++ b/epan/dissectors/packet-pdcp-nr.c @@ -12,7 +12,6 @@ #include "config.h" - #include #include #include @@ -21,6 +20,11 @@ #include +/* Define this symbol if you have a working implementation of SNOW3G f8() and f9() available. + Note that the use of this algorithm is restricted, and that an administrative charge + may be applicable if you use it (see e.g. http://www.gsma.com/technicalprojects/fraud-security/security-algorithms). + A version of Wireshark with this enabled would not be distributable. */ +/* #define HAVE_SNOW3G */ #include "packet-rlc-nr.h" #include "packet-pdcp-nr.h" @@ -31,12 +35,15 @@ void proto_reg_handoff_pdcp_nr(void); /* Described in: * 3GPP TS 38.323 Technical Specification Group Radio Access Netowrk; NR; * Packet Data Convergence Protocol (PDCP) specification (Release 15.1.0) + * 3GPP TS 37.324 Technical Specification Group Radio Access Network; E-UTRA and NR; + * Service Data Adaptation Protocol (SDAP) specification (Release 15) */ /* TODO: - - Deciphering, but should refactor and share LTE implementation -*/ + - look into refactoring/sharing parts of deciphering/integrity with LTE implementation + - call e.g. report_failure() if fail to configure security keys + */ /* Initialize the protocol and registered fields. */ @@ -52,6 +59,7 @@ static int hf_pdcp_nr_bearer_type = -1; static int hf_pdcp_nr_bearer_id = -1; static int hf_pdcp_nr_plane = -1; static int hf_pdcp_nr_seqnum_length = -1; +static int hf_pdcp_nr_maci_present = -1; static int hf_pdcp_nr_sdap = -1; static int hf_pdcp_nr_rohc_compression = -1; @@ -87,6 +95,18 @@ static int hf_pdcp_nr_sequence_analysis_expected_sn = -1; static int hf_pdcp_nr_sequence_analysis_repeated = -1; static int hf_pdcp_nr_sequence_analysis_skipped = -1; +/* Security Settings */ +static int hf_pdcp_nr_security = -1; +static int hf_pdcp_nr_security_setup_frame = -1; +static int hf_pdcp_nr_security_integrity_algorithm = -1; +static int hf_pdcp_nr_security_ciphering_algorithm = -1; + +static int hf_pdcp_nr_security_bearer = -1; +static int hf_pdcp_nr_security_direction = -1; +static int hf_pdcp_nr_security_count = -1; +static int hf_pdcp_nr_security_cipher_key = -1; +static int hf_pdcp_nr_security_integrity_key = -1; + /* Protocol subtree. */ static int ett_pdcp = -1; @@ -94,14 +114,238 @@ static int ett_pdcp_configuration = -1; static int ett_pdcp_packet = -1; static int ett_pdcp_nr_sequence_analysis = -1; static int ett_pdcp_report_bitmap = -1; +static int ett_pdcp_security = -1; static expert_field ei_pdcp_nr_sequence_analysis_wrong_sequence_number = EI_INIT; static expert_field ei_pdcp_nr_reserved_bits_not_zero = EI_INIT; static expert_field ei_pdcp_nr_sequence_analysis_sn_repeated = EI_INIT; static expert_field ei_pdcp_nr_sequence_analysis_sn_missing = EI_INIT; +static expert_field ei_pdcp_nr_digest_wrong = EI_INIT; static expert_field ei_pdcp_nr_unknown_udp_framing_tag = EI_INIT; static expert_field ei_pdcp_nr_missing_udp_framing_tag = EI_INIT; +/*------------------------------------- + * UAT for UE Keys + *------------------------------------- + */ +/* UAT entry structure. */ +typedef struct { + guint32 ueid; + gchar *rrcCipherKeyString; + gchar *upCipherKeyString; + gchar *rrcIntegrityKeyString; + gchar *upIntegrityKeyString; + + guint8 rrcCipherBinaryKey[16]; + gboolean rrcCipherKeyOK; + guint8 upCipherBinaryKey[16]; + gboolean upCipherKeyOK; + guint8 rrcIntegrityBinaryKey[16]; + gboolean rrcIntegrityKeyOK; + guint8 upIntegrityBinaryKey[16]; + gboolean upIntegrityKeyOK; + +} uat_ue_keys_record_t; + +/* N.B. this is an array/table of the struct above, where ueid is the key */ +static uat_ue_keys_record_t *uat_ue_keys_records = NULL; + +/* Entries added by UAT */ +static uat_t * ue_keys_uat = NULL; +static guint num_ue_keys_uat = 0; + +/* Convert an ascii hex character into a digit. Should only be given valid + hex ascii characters */ +static guchar hex_ascii_to_binary(gchar c) +{ + if ((c >= '0') && (c <= '9')) { + return c - '0'; + } + else if ((c >= 'a') && (c <= 'f')) { + return 10 + c - 'a'; + } + else if ((c >= 'A') && (c <= 'F')) { + return 10 + c - 'A'; + } + else + return 0; +} + +static void* uat_ue_keys_record_copy_cb(void* n, const void* o, size_t siz _U_) { + uat_ue_keys_record_t* new_rec = (uat_ue_keys_record_t *)n; + const uat_ue_keys_record_t* old_rec = (const uat_ue_keys_record_t *)o; + + new_rec->ueid = old_rec->ueid; + new_rec->rrcCipherKeyString = g_strdup(old_rec->rrcCipherKeyString); + new_rec->upCipherKeyString = g_strdup(old_rec->upCipherKeyString); + new_rec->rrcIntegrityKeyString = g_strdup(old_rec->rrcIntegrityKeyString); + new_rec->upIntegrityKeyString = g_strdup(old_rec->upIntegrityKeyString); + + return new_rec; +} + +/* If raw_string is a valid key, set check_string & return TRUE */ +static gboolean check_valid_key_string(const char* raw_string, char* checked_string) +{ + guint n; + guint written = 0; + guint length = (gint)strlen(raw_string); + + /* Can't be valid if not long enough. */ + if (length < 32) { + return FALSE; + } + + for (n=0; (n < length) && (written < 32); n++) { + char c = raw_string[n]; + + /* Skipping past allowed 'padding' characters */ + if ((c == ' ') || (c == '-')) { + continue; + } + + /* Other characters must be hex digits, otherwise string is invalid */ + if (((c >= '0') && (c <= '9')) || + ((c >= 'a') && (c <= 'f')) || + ((c >= 'A') && (c <= 'F'))) { + checked_string[written++] = c; + } + else { + return FALSE; + } + } + + /* Must have found exactly 32 hex ascii chars for 16-byte key */ + return (written == 32); +} + +/* Write binary key by converting each nibble from the string version */ +static void update_key_from_string(const char *stringKey, guint8 *binaryKey, gboolean *pKeyOK) +{ + int n; + char cleanString[32]; + + if (!check_valid_key_string(stringKey, cleanString)) { + *pKeyOK = FALSE; + } + else { + for (n=0; n < 32; n += 2) { + binaryKey[n/2] = (hex_ascii_to_binary(cleanString[n]) << 4) + + hex_ascii_to_binary(cleanString[n+1]); + } + *pKeyOK = TRUE; + } +} + +/* Update by checking whether the 3 key strings are valid or not, and storing result */ +static gboolean uat_ue_keys_record_update_cb(void* record, char** error _U_) { + uat_ue_keys_record_t* rec = (uat_ue_keys_record_t *)record; + + /* Check and convert RRC cipher key */ + update_key_from_string(rec->rrcCipherKeyString, rec->rrcCipherBinaryKey, &rec->rrcCipherKeyOK); + + /* Check and convert User-plane cipher key */ + update_key_from_string(rec->upCipherKeyString, rec->upCipherBinaryKey, &rec->upCipherKeyOK); + + /* Check and convert RRC Integrity key */ + update_key_from_string(rec->rrcIntegrityKeyString, rec->rrcIntegrityBinaryKey, &rec->rrcIntegrityKeyOK); + + /* Check and convert User-plane Integrity key */ + update_key_from_string(rec->upIntegrityKeyString, rec->upIntegrityBinaryKey, &rec->upIntegrityKeyOK); + + /* Return TRUE regardless, as user might only specify one, or get it wrong and want to edit it later */ + return TRUE; +} + +/* Free heap parts of record */ +static void uat_ue_keys_record_free_cb(void*r) { + uat_ue_keys_record_t* rec = (uat_ue_keys_record_t*)r; + + g_free(rec->rrcCipherKeyString); + g_free(rec->upCipherKeyString); + g_free(rec->rrcIntegrityKeyString); + g_free(rec->upIntegrityKeyString); +} + +UAT_DEC_CB_DEF(uat_ue_keys_records, ueid, uat_ue_keys_record_t) +UAT_CSTRING_CB_DEF(uat_ue_keys_records, rrcCipherKeyString, uat_ue_keys_record_t) +UAT_CSTRING_CB_DEF(uat_ue_keys_records, upCipherKeyString, uat_ue_keys_record_t) +UAT_CSTRING_CB_DEF(uat_ue_keys_records, rrcIntegrityKeyString, uat_ue_keys_record_t) +UAT_CSTRING_CB_DEF(uat_ue_keys_records, upIntegrityKeyString, uat_ue_keys_record_t) + +/* Also supporting a hash table with entries from these functions */ + +/* Table from ueid -> uat_ue_keys_record_t* */ +static wmem_map_t *pdcp_security_key_hash = NULL; + + +void set_pdcp_nr_rrc_ciphering_key(guint16 ueid, const char *key) +{ + /* Get or create struct for this UE */ + uat_ue_keys_record_t *key_record = (uat_ue_keys_record_t*)wmem_map_lookup(pdcp_security_key_hash, + GUINT_TO_POINTER((guint)ueid)); + if (key_record == NULL) { + /* Create and add to table */ + key_record = wmem_new0(wmem_file_scope(), uat_ue_keys_record_t); + key_record->ueid = ueid; + wmem_map_insert(pdcp_security_key_hash, GUINT_TO_POINTER((guint)ueid), key_record); + } + + /* Check and convert RRC key */ + key_record->rrcCipherKeyString = g_strdup(key); + update_key_from_string(key_record->rrcCipherKeyString, key_record->rrcCipherBinaryKey, &key_record->rrcCipherKeyOK); +} + +void set_pdcp_nr_rrc_integrity_key(guint16 ueid, const char *key) +{ + /* Get or create struct for this UE */ + uat_ue_keys_record_t *key_record = (uat_ue_keys_record_t*)wmem_map_lookup(pdcp_security_key_hash, + GUINT_TO_POINTER((guint)ueid)); + if (key_record == NULL) { + /* Create and add to table */ + key_record = wmem_new0(wmem_file_scope(), uat_ue_keys_record_t); + key_record->ueid = ueid; + wmem_map_insert(pdcp_security_key_hash, GUINT_TO_POINTER((guint)ueid), key_record); + } + + /* Check and convert RRC integrity key */ + key_record->rrcIntegrityKeyString = g_strdup(key); + update_key_from_string(key_record->rrcIntegrityKeyString, key_record->rrcIntegrityBinaryKey, &key_record->rrcIntegrityKeyOK); +} + +void set_pdcp_nr_up_ciphering_key(guint16 ueid, const char *key) +{ + /* Get or create struct for this UE */ + uat_ue_keys_record_t *key_record = (uat_ue_keys_record_t*)wmem_map_lookup(pdcp_security_key_hash, + GUINT_TO_POINTER((guint)ueid)); + if (key_record == NULL) { + /* Create and add to table */ + key_record = wmem_new0(wmem_file_scope(), uat_ue_keys_record_t); + key_record->ueid = ueid; + wmem_map_insert(pdcp_security_key_hash, GUINT_TO_POINTER((guint)ueid), key_record); + } + + /* Check and convert UP key */ + key_record->upCipherKeyString = g_strdup(key); + update_key_from_string(key_record->upCipherKeyString, key_record->upCipherBinaryKey, &key_record->upCipherKeyOK); +} + +void set_pdcp_nr_up_integrity_key(guint16 ueid, const char *key) +{ + /* Get or create struct for this UE */ + uat_ue_keys_record_t *key_record = (uat_ue_keys_record_t*)wmem_map_lookup(pdcp_security_key_hash, + GUINT_TO_POINTER((guint)ueid)); + if (key_record == NULL) { + /* Create and add to table */ + key_record = wmem_new0(wmem_file_scope(), uat_ue_keys_record_t); + key_record->ueid = ueid; + wmem_map_insert(pdcp_security_key_hash, GUINT_TO_POINTER((guint)ueid), key_record); + } + + /* Check and convert RRC integrity key */ + key_record->upIntegrityKeyString = g_strdup(key); + update_key_from_string(key_record->upIntegrityKeyString, key_record->upIntegrityBinaryKey, &key_record->upIntegrityKeyOK); +} static const value_string direction_vals[] = @@ -164,23 +408,21 @@ static const value_string control_pdu_type_vals[] = { { 0, NULL } }; -#if 0 static const value_string integrity_algorithm_vals[] = { - { 0, "NIA0" }, - { 1, "NIA1" }, - { 2, "NIA2" }, - { 3, "NIA3" }, + { 0, "NIA0 (NULL)" }, + { 1, "NIA1 (SNOW3G)" }, + { 2, "NIA2 (AES)" }, + { 3, "NIA3 (ZUC)" }, { 0, NULL } }; static const value_string ciphering_algorithm_vals[] = { - { 0, "NEA0" }, - { 1, "NEA1" }, - { 2, "NEA2" }, - { 3, "NEA3" }, + { 0, "NEA0 (NULL)" }, + { 1, "NEA1 (SNOW3G)" }, + { 2, "NEA2 (AES)" }, + { 3, "NEA3 (ZUC)" }, { 0, NULL } }; -#endif /* SDAP header fields and tree */ @@ -225,6 +467,18 @@ static gboolean global_pdcp_dissect_signalling_plane_as_rrc = TRUE; static gint global_pdcp_check_sequence_numbers = TRUE; static gboolean global_pdcp_dissect_rohc = FALSE; + +/* Preference settings for deciphering and integrity checking. */ +static gboolean global_pdcp_decipher_signalling = TRUE; +static gboolean global_pdcp_decipher_userplane = FALSE; /* Can be slow, so default to FALSE */ +static gboolean global_pdcp_check_integrity = TRUE; +static gboolean global_pdcp_ignore_sec = FALSE; /* Ignore Set Security Algo calls */ + +/* Use these values where we know the keys but may have missed the algorithm, + e.g. when handing over and RRCReconfigurationRequest goes to target cell only */ +static enum nr_security_ciphering_algorithm_e global_default_ciphering_algorithm = nea0; +static enum nr_security_integrity_algorithm_e global_default_integrity_algorithm = nia0; + /* Which layer info to show in the info column */ enum layer_to_show { ShowRLCLayer, ShowPDCPLayer, ShowTrafficLayer @@ -368,17 +622,55 @@ typedef struct Maps pdcp_result_hash_key* -> pdcp_sequence_report_in_frame* */ static wmem_map_t *pdcp_nr_sequence_analysis_report_hash = NULL; +/* Gather together security settings in order to be able to do deciphering */ +typedef struct pdu_security_settings_t +{ + enum nr_security_ciphering_algorithm_e ciphering; + enum nr_security_integrity_algorithm_e integrity; + guint8* cipherKey; + guint8* integrityKey; + gboolean cipherKeyValid; + gboolean integrityKeyValid; + guint32 count; + guint8 bearer; + guint8 direction; +} pdu_security_settings_t; + +static uat_ue_keys_record_t* look_up_keys_record(guint16 ueid) +{ + unsigned int record_id; + + /* Try hash table first (among entries added by set_pdcp_nr_xxx_key() functions) */ + uat_ue_keys_record_t* key_record = (uat_ue_keys_record_t*)wmem_map_lookup(pdcp_security_key_hash, + GUINT_TO_POINTER((guint)ueid)); + if (key_record != NULL) { + return key_record; + } + + /* Else look up UAT entries. N.B. linear search... */ + for (record_id=0; record_id < num_ue_keys_uat; record_id++) { + if (uat_ue_keys_records[record_id].ueid == ueid) { + return &uat_ue_keys_records[record_id]; + } + } + + /* No match at all - return NULL */ + return NULL; +} /* Add to the tree values associated with sequence analysis for this frame */ static void addBearerSequenceInfo(pdcp_sequence_report_in_frame *p, - pdcp_nr_info *p_pdcp_nr_info, - guint32 sequenceNumber, - packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb) + pdcp_nr_info *p_pdcp_nr_info, + guint32 sequenceNumber, + packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, + proto_tree *security_tree, + pdu_security_settings_t *pdu_security) { proto_tree *seqnum_tree; proto_item *seqnum_ti; proto_item *ti_expected_sn; proto_item *ti; + uat_ue_keys_record_t *keys_record; /* Create subtree */ seqnum_ti = proto_tree_add_string_format(tree, @@ -425,6 +717,90 @@ static void addBearerSequenceInfo(pdcp_sequence_report_in_frame *p, tvb, 0, 0, p->nextFrameNum); } + /* May also be able to add key inputs to security tree here */ + if ((pdu_security->ciphering != nea0) || + (pdu_security->integrity != nia0)) { + guint32 hfn_multiplier; + guint32 count; + gchar *cipher_key = NULL; + gchar *integrity_key = NULL; + + /* BEARER */ + ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_bearer, + tvb, 0, 0, p_pdcp_nr_info->bearerId-1); + proto_item_set_generated(ti); + pdu_security->bearer = p_pdcp_nr_info->bearerId-1; + + /* DIRECTION */ + ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_direction, + tvb, 0, 0, p_pdcp_nr_info->direction); + proto_item_set_generated(ti); + + /* COUNT (HFN * snLength^2 + SN) */ + switch (p_pdcp_nr_info->seqnum_length) { + case PDCP_NR_SN_LENGTH_12_BITS: + hfn_multiplier = 4096; + break; + case PDCP_NR_SN_LENGTH_18_BITS: + hfn_multiplier = 262144; + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + break; + } + count = (p->hfn * hfn_multiplier) + sequenceNumber; + ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_count, + tvb, 0, 0, count); + proto_item_set_generated(ti); + pdu_security->count = count; + + /* KEY. Look this UE up among UEs that have keys configured */ + keys_record = look_up_keys_record(p_pdcp_nr_info->ueid); + if (keys_record != NULL) { + if (p_pdcp_nr_info->plane == NR_SIGNALING_PLANE) { + /* Get RRC ciphering key */ + if (keys_record->rrcCipherKeyOK) { + cipher_key = keys_record->rrcCipherKeyString; + pdu_security->cipherKey = &(keys_record->rrcCipherBinaryKey[0]); + pdu_security->cipherKeyValid = TRUE; + } + /* Get RRC integrity key */ + if (keys_record->rrcIntegrityKeyOK) { + integrity_key = keys_record->rrcIntegrityKeyString; + pdu_security->integrityKey = &(keys_record->rrcIntegrityBinaryKey[0]); + pdu_security->integrityKeyValid = TRUE; + } + } + else { + /* Get userplane ciphering key */ + if (keys_record->upCipherKeyOK) { + cipher_key = keys_record->upCipherKeyString; + pdu_security->cipherKey = &(keys_record->upCipherBinaryKey[0]); + pdu_security->cipherKeyValid = TRUE; + } + /* Get userplane integrity key */ + if (keys_record->upIntegrityKeyOK) { + integrity_key = keys_record->upIntegrityKeyString; + pdu_security->integrityKey = &(keys_record->upIntegrityBinaryKey[0]); + pdu_security->integrityKeyValid = TRUE; + } + } + + /* Show keys where known and valid */ + if (cipher_key != NULL) { + ti = proto_tree_add_string(security_tree, hf_pdcp_nr_security_cipher_key, + tvb, 0, 0, cipher_key); + proto_item_set_generated(ti); + } + if (integrity_key != NULL) { + ti = proto_tree_add_string(security_tree, hf_pdcp_nr_security_integrity_key, + tvb, 0, 0, integrity_key); + proto_item_set_generated(ti); + } + + pdu_security->direction = p_pdcp_nr_info->direction; + } + } break; case SN_Missing: @@ -492,9 +868,11 @@ static void addBearerSequenceInfo(pdcp_sequence_report_in_frame *p, /* Update the bearer status and set report for this frame */ static void checkBearerSequenceInfo(packet_info *pinfo, tvbuff_t *tvb, - pdcp_nr_info *p_pdcp_nr_info, - guint32 sequenceNumber, - proto_tree *tree) + pdcp_nr_info *p_pdcp_nr_info, + guint32 sequenceNumber, + proto_tree *tree, + proto_tree *security_tree, + pdu_security_settings_t *pdu_security) { pdcp_bearer_hash_key bearer_key; pdcp_bearer_status *p_bearer_status; @@ -504,7 +882,7 @@ static void checkBearerSequenceInfo(packet_info *pinfo, tvbuff_t *tvb, guint32 snLimit = 0; /* If find stat_report_in_frame already, use that and get out */ - if (pinfo->fd->visited) { + if (PINFO_FD_VISITED(pinfo)) { p_report_in_frame = (pdcp_sequence_report_in_frame*)wmem_map_lookup(pdcp_nr_sequence_analysis_report_hash, get_report_hash_key(sequenceNumber, @@ -513,7 +891,7 @@ static void checkBearerSequenceInfo(packet_info *pinfo, tvbuff_t *tvb, if (p_report_in_frame != NULL) { addBearerSequenceInfo(p_report_in_frame, p_pdcp_nr_info, sequenceNumber, - pinfo, tree, tvb); + pinfo, tree, tvb, security_tree, pdu_security); return; } else { @@ -641,11 +1019,13 @@ static void checkBearerSequenceInfo(packet_info *pinfo, tvbuff_t *tvb, /* Add state report for this frame into tree */ addBearerSequenceInfo(p_report_in_frame, p_pdcp_nr_info, sequenceNumber, - pinfo, tree, tvb); + pinfo, tree, tvb, security_tree, pdu_security); } - +/* Hash table for security state for a UE + Maps UEId -> pdcp_security_info_t* */ +static wmem_map_t *pdcp_security_hash = NULL; /* Result is (ueid, framenum) -> pdcp_security_info_t* */ typedef struct ueid_frame_t { @@ -653,6 +1033,45 @@ typedef struct ueid_frame_t { guint16 ueid; } ueid_frame_t; +/* Convenience function to get a pointer for the hash_func to work with */ +static gpointer get_ueid_frame_hash_key(guint16 ueid, guint32 frameNumber, + gboolean do_persist) +{ + static ueid_frame_t key; + ueid_frame_t *p_key; + + /* Only allocate a struct when will be adding entry */ + if (do_persist) { + p_key = wmem_new(wmem_file_scope(), ueid_frame_t); + } + else { + /* Only looking up, so just use static */ + memset(&key, 0, sizeof(ueid_frame_t)); + p_key = &key; + } + + /* Fill in details, and return pointer */ + p_key->framenum = frameNumber; + p_key->ueid = ueid; + + return p_key; +} + +static gint pdcp_nr_ueid_frame_hash_equal(gconstpointer v, gconstpointer v2) +{ + const ueid_frame_t *ueid_frame_1 = (const ueid_frame_t *)v; + const ueid_frame_t *ueid_frame_2 = (const ueid_frame_t *)v2; + return ((ueid_frame_1->framenum == ueid_frame_2->framenum) && + (ueid_frame_1->ueid == ueid_frame_2->ueid)); +} +static guint pdcp_nr_ueid_frame_hash_func(gconstpointer v) +{ + const ueid_frame_t *ueid_frame = (const ueid_frame_t *)v; + return ueid_frame->framenum + 100*ueid_frame->ueid; +} +static wmem_map_t *pdcp_security_result_hash = NULL; + + /* Write the given formatted text to: @@ -683,6 +1102,7 @@ static void write_pdu_label_and_info(proto_item *pdu_ti, /* Show in the tree the config info attached to this frame, as generated fields */ +/* TODO: add imac_present field */ static void show_pdcp_config(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree, pdcp_nr_info *p_pdcp_info) { @@ -733,11 +1153,18 @@ static void show_pdcp_config(packet_info *pinfo, tvbuff_t *tvb, proto_tree *tree val_to_str_const(p_pdcp_info->bearerType, bearer_type_vals, "Unknown")); } + /* Seqnum length */ + ti = proto_tree_add_uint(configuration_tree, hf_pdcp_nr_seqnum_length, tvb, 0, 0, + p_pdcp_info->seqnum_length); + proto_item_set_generated(ti); + + /* MAC-I Present */ + ti = proto_tree_add_boolean(configuration_tree, hf_pdcp_nr_maci_present, tvb, 0, 0, + p_pdcp_info->maci_present); + proto_item_set_generated(ti); + + if (p_pdcp_info->plane == NR_USER_PLANE) { - /* Seqnum length */ - ti = proto_tree_add_uint(configuration_tree, hf_pdcp_nr_seqnum_length, tvb, 0, 0, - p_pdcp_info->seqnum_length); - proto_item_set_generated(ti); /* SDAP */ ti = proto_tree_add_boolean(configuration_tree, hf_pdcp_nr_sdap, tvb, 0, 0, @@ -847,7 +1274,332 @@ static dissector_handle_t lookup_rrc_dissector_handle(struct pdcp_nr_info *p_pd } -/* Forwad declarations */ +/* Called from control protocol to configure security algorithms for the given UE */ +void set_pdcp_nr_security_algorithms(guint16 ueid, pdcp_nr_security_info_t *security_info) +{ + /* Use for this frame so can check integrity on SecurityCommandRequest frame */ + /* N.B. won't work for internal, non-RRC signalling methods... */ + pdcp_nr_security_info_t *p_frame_security; + + /* Disable this entire sub-routine with the Preference */ + /* Used when the capture is already deciphered */ + if (global_pdcp_ignore_sec) { + return; + } + + /* Create or update current settings, by UEID */ + pdcp_nr_security_info_t* ue_security = + (pdcp_nr_security_info_t*)wmem_map_lookup(pdcp_security_hash, + GUINT_TO_POINTER((guint)ueid)); + if (ue_security == NULL) { + /* Copy whole security struct */ + ue_security = wmem_new(wmem_file_scope(), pdcp_nr_security_info_t); + *ue_security = *security_info; + + /* And add into security table */ + wmem_map_insert(pdcp_security_hash, GUINT_TO_POINTER((guint)ueid), ue_security); + } + else { + /* Just update existing entry already in table */ + ue_security->previous_configuration_frame = ue_security->configuration_frame; + ue_security->previous_integrity = ue_security->integrity; + ue_security->previous_ciphering = ue_security->ciphering; + + ue_security->configuration_frame = security_info->configuration_frame; + ue_security->integrity = security_info->integrity; + ue_security->ciphering = security_info->ciphering; + ue_security->seen_next_ul_pdu = FALSE; + } + + /* Also add an entry for this PDU already to use these settings, as otherwise it won't be present + when we query it on the first pass. */ + p_frame_security = wmem_new(wmem_file_scope(), pdcp_nr_security_info_t); + *p_frame_security = *ue_security; + wmem_map_insert(pdcp_security_result_hash, + get_ueid_frame_hash_key(ueid, ue_security->configuration_frame, TRUE), + p_frame_security); +} + + +/* UE failed to process SecurityModeCommand so go back to previous security settings */ +void set_pdcp_nr_security_algorithms_failed(guint16 ueid) +{ + /* Look up current state by UEID */ + pdcp_nr_security_info_t* ue_security = + (pdcp_nr_security_info_t*)wmem_map_lookup(pdcp_security_hash, + GUINT_TO_POINTER((guint)ueid)); + if (ue_security != NULL) { + /* TODO: could remove from table if previous_configuration_frame is 0 */ + /* Go back to previous state */ + ue_security->configuration_frame = ue_security->previous_configuration_frame; + ue_security->integrity = ue_security->previous_integrity; + ue_security->ciphering = ue_security->previous_ciphering; + } +} + +/* Decipher payload if algorithm is supported and plausible inputs are available */ +static tvbuff_t *decipher_payload(tvbuff_t *tvb, packet_info *pinfo, int *offset, + pdu_security_settings_t *pdu_security_settings, + struct pdcp_nr_info *p_pdcp_info, guint sdap_length, + gboolean will_be_deciphered, gboolean *deciphered) +{ + guint8* decrypted_data = NULL; + gint payload_length = 0; + tvbuff_t *decrypted_tvb; + + /* Nothing to do if NULL ciphering */ + if (pdu_security_settings->ciphering == nea0) { + return tvb; + } + + /* Nothing to do if don't have valid cipher key */ + if (!pdu_security_settings->cipherKeyValid) { + return tvb; + } + + /* Check whether algorithm supported (only drop through and process if we do) */ + if (pdu_security_settings->ciphering == nea1) { +#ifndef HAVE_SNOW3G + return tvb; +#endif + } + else + if (pdu_security_settings->ciphering != nea2) { + /* An algorithm we don't support at all! */ + return tvb; + } + + /* Don't decipher if turned off in preferences */ + if (((p_pdcp_info->plane == NR_SIGNALING_PLANE) && !global_pdcp_decipher_signalling) || + ((p_pdcp_info->plane == NR_USER_PLANE) && !global_pdcp_decipher_userplane)) { + return tvb; + } + + /* Don't decipher user-plane control messages */ + if ((p_pdcp_info->plane == NR_USER_PLANE) && ((tvb_get_guint8(tvb, 0) & 0x80) == 0x00)) { + return tvb; + } + + /* Don't decipher common control messages */ + if ((p_pdcp_info->plane == NR_SIGNALING_PLANE) && (p_pdcp_info->bearerType != Bearer_DCCH)) { + return tvb; + } + + /* Don't decipher if not yet past SecurityModeResponse */ + if (!will_be_deciphered) { + return tvb; + } + + /* AES */ + if (pdu_security_settings->ciphering == nea2) { + unsigned char ctr_block[16]; + gcry_cipher_hd_t cypher_hd; + int gcrypt_err; + + /* TS 33.501 D.4.4 defers to TS 33.401 B.1.3 */ + + /* Set CTR */ + memset(ctr_block, 0, 16); + /* Only first 5 bytes set */ + ctr_block[0] = (pdu_security_settings->count & 0xff000000) >> 24; + ctr_block[1] = (pdu_security_settings->count & 0x00ff0000) >> 16; + ctr_block[2] = (pdu_security_settings->count & 0x0000ff00) >> 8; + ctr_block[3] = (pdu_security_settings->count & 0x000000ff); + ctr_block[4] = (pdu_security_settings->bearer << 3) + (pdu_security_settings->direction << 2); + + /* Open gcrypt handle */ + gcrypt_err = gcry_cipher_open(&cypher_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_CTR, 0); + if (gcrypt_err != 0) { + return tvb; + } + + /* Set the key */ + gcrypt_err = gcry_cipher_setkey(cypher_hd, pdu_security_settings->cipherKey, 16); + if (gcrypt_err != 0) { + gcry_cipher_close(cypher_hd); + return tvb; + } + + /* Set the CTR */ + gcrypt_err = gcry_cipher_setctr(cypher_hd, ctr_block, 16); + if (gcrypt_err != 0) { + gcry_cipher_close(cypher_hd); + return tvb; + } + + /* Extract the encrypted data into a buffer */ + payload_length = tvb_captured_length_remaining(tvb, *offset+sdap_length); + decrypted_data = (guint8 *)tvb_memdup(pinfo->pool, tvb, *offset+sdap_length, payload_length); + + /* Decrypt the actual data */ + gcrypt_err = gcry_cipher_decrypt(cypher_hd, + decrypted_data, payload_length, + NULL, 0); + if (gcrypt_err != 0) { + gcry_cipher_close(cypher_hd); + return tvb; + } + + /* Close gcrypt handle */ + gcry_cipher_close(cypher_hd); + } + +#ifdef HAVE_SNOW3G + /* SNOW-3G */ + if (pdu_security_settings->ciphering == nea1) { + /* TS 33.501 D.4.3 defers to RS 33.401 */ + + /* Extract the encrypted data into a buffer */ + payload_length = tvb_captured_length_remaining(tvb, *offset+sdap_length); + decrypted_data = (guint8 *)tvb_memdup(pinfo->pool, tvb, *offset+sdap_length, payload_length); + + /* Do the algorithm */ + snow3g_f8(pdu_security_settings->cipherKey, + pdu_security_settings->count, + pdu_security_settings->bearer, + pdu_security_settings->direction, + decrypted_data, payload_length*8); + } +#endif + + /* Create tvb for resulting deciphered sdu */ + decrypted_tvb = tvb_new_child_real_data(tvb, decrypted_data, payload_length, payload_length); + add_new_data_source(pinfo, decrypted_tvb, "Deciphered Payload"); + + /* Return deciphered data, i.e. beginning of new tvb */ + *offset = 0; + *deciphered = TRUE; + return decrypted_tvb; +} + +/* Try to calculate digest to compare with that found in frame. */ +static guint32 calculate_digest(pdu_security_settings_t *pdu_security_settings, tvbuff_t *header_tvb _U_, + tvbuff_t *tvb _U_, gint offset _U_, guint sdap_length _U_, gboolean *calculated) +{ + *calculated = FALSE; + + if (pdu_security_settings->integrity == nia0) { + /* Should be zero in this case */ + *calculated = TRUE; + return 0; + } + + /* Can't calculate if don't have valid integrity key */ + if (!pdu_security_settings->integrityKeyValid) { + return 0; + } + + /* Can only do if indicated in preferences */ + if (!global_pdcp_check_integrity) { + return 0; + } + + guint header_length = tvb_reported_length(header_tvb); + + switch (pdu_security_settings->integrity) { + +#ifdef HAVE_SNOW3G + case nia1: + { + /* SNOW3G */ + guint8 *mac; + gint message_length = tvb_captured_length_remaining(tvb, offset) - 4; + guint8 *message_data = (guint8 *)wmem_alloc0(wmem_packet_scope(), header_length+message_length-sdap_length+4); + + /* TS 33.401 B.2.2 */ + + /* Data is header bytes */ + tvb_memcpy(header_tvb, message_data, 0, header_length); + /* Followed by the decrypted message (but not the digest bytes) */ + tvb_memcpy(tvb, message_data+header_length, offset+sdap_length, message_length-sdap_length); + + mac = (u8*)snow3g_f9(pdu_security_settings->integrityKey, + pdu_security_settings->count, + /* 'Fresh' is the bearer bits then zeros */ + pdu_security_settings->bearer << 27, + pdu_security_settings->direction, + message_data, + (message_length+1)*8); + + *calculated = TRUE; + return ((mac[0] << 24) | (mac[1] << 16) | (mac[2] << 8) | mac[3]); + } +#endif + +#if GCRYPT_VERSION_NUMBER >= 0x010600 /* 1.6.0 */ + case nia2: + { + /* AES */ + gcry_mac_hd_t mac_hd; + int gcrypt_err; + gint message_length; + guint8 *message_data; + guint8 mac[4]; + size_t read_digest_length = 4; + + /* Open gcrypt handle */ + gcrypt_err = gcry_mac_open(&mac_hd, GCRY_MAC_CMAC_AES, 0, NULL); + if (gcrypt_err != 0) { + return 0; + } + + /* Set the key */ + gcrypt_err = gcry_mac_setkey(mac_hd, pdu_security_settings->integrityKey, 16); + if (gcrypt_err != 0) { + gcry_mac_close(mac_hd); + return 0; + } + + /* TS 33.501 D.4.3 defers to TS 33.401 B.2.3 */ + + /* Extract the encrypted data into a buffer */ + message_length = tvb_captured_length_remaining(tvb, offset) - 4; + message_data = (guint8 *)wmem_alloc0(wmem_packet_scope(), 8+header_length+message_length-sdap_length); + message_data[0] = (pdu_security_settings->count & 0xff000000) >> 24; + message_data[1] = (pdu_security_settings->count & 0x00ff0000) >> 16; + message_data[2] = (pdu_security_settings->count & 0x0000ff00) >> 8; + message_data[3] = (pdu_security_settings->count & 0x000000ff); + message_data[4] = (pdu_security_settings->bearer << 3) + (pdu_security_settings->direction << 2); + /* rest of first 8 bytes are left as zeroes... */ + + /* Now the header bytes */ + tvb_memcpy(header_tvb, message_data+8, 0, header_length); + /* Followed by the decrypted message (but not the digest bytes or any SDAP bytes) */ + tvb_memcpy(tvb, message_data+8+header_length, offset+sdap_length, message_length-sdap_length); + + /* Pass in the message */ + gcrypt_err = gcry_mac_write(mac_hd, message_data, 8+header_length+message_length-sdap_length); + if (gcrypt_err != 0) { + gcry_mac_close(mac_hd); + return 0; + } + + /* Read out the digest */ + gcrypt_err = gcry_mac_read(mac_hd, mac, &read_digest_length); + if (gcrypt_err != 0) { + gcry_mac_close(mac_hd); + return 0; + } + + /* Now close the mac handle */ + gcry_mac_close(mac_hd); + + *calculated = TRUE; + return ((mac[0] << 24) | (mac[1] << 16) | (mac[2] << 8) | mac[3]); + } +#endif + + default: + /* Can't calculate (e.g. Zuc) */ + *calculated = FALSE; + return 0; + } +} + + + + +/* Forward declarations */ static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data); static void report_heur_error(proto_tree *tree, packet_info *pinfo, expert_field *eiindex, @@ -1015,7 +1767,16 @@ static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, struct pdcp_nr_info *p_pdcp_info; tvbuff_t *rohc_tvb = NULL; + pdcp_nr_security_info_t *current_security = NULL; /* current security for this UE */ + pdcp_nr_security_info_t *pdu_security; /* security in place for this PDU */ + proto_tree *security_tree = NULL; + proto_item *security_ti; tvbuff_t *payload_tvb; + pdu_security_settings_t pdu_security_settings; + gboolean payload_deciphered = FALSE; + + /* Initialise security settings */ + memset(&pdu_security_settings, 0, sizeof(pdu_security_settings)); /* Set protocol name. */ col_set_str(pinfo->cinfo, COL_PROTOCOL, "PDCP-NR"); @@ -1042,6 +1803,12 @@ static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, col_set_writable(pinfo->cinfo, COL_INFO, TRUE); } + /* MACI always present for SRBs */ + /* TODO: should get configured from RRC for DRBs */ + if (p_pdcp_info->plane == NR_SIGNALING_PLANE) { + p_pdcp_info->maci_present = TRUE; + } + /* Create pdcp tree. */ if (tree) { root_ti = proto_tree_add_item(tree, proto_pdcp_nr, tvb, offset, -1, ENC_NA); @@ -1062,6 +1829,75 @@ static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, col_append_fstr(pinfo->cinfo, COL_INFO, " (mode=%c)", mode[0]); } + /***************************************/ + /* UE security algorithms */ + if (!PINFO_FD_VISITED(pinfo)) { + /* Look up current state by UEID */ + current_security = (pdcp_nr_security_info_t*)wmem_map_lookup(pdcp_security_hash, + GUINT_TO_POINTER((guint)p_pdcp_info->ueid)); + if (current_security != NULL) { + /* Store any result for this frame in the result table */ + pdcp_nr_security_info_t *security_to_store = wmem_new(wmem_file_scope(), pdcp_nr_security_info_t); + /* Take a deep copy of the settings */ + *security_to_store = *current_security; + wmem_map_insert(pdcp_security_result_hash, + get_ueid_frame_hash_key(p_pdcp_info->ueid, pinfo->num, TRUE), + security_to_store); + } + else { + /* No entry added from RRC, but still use configured defaults */ + if ((global_default_ciphering_algorithm != nea0) || + (global_default_integrity_algorithm != nia0)) { + /* Copy algorithms from preference defaults */ + pdcp_nr_security_info_t *security_to_store = wmem_new0(wmem_file_scope(), pdcp_nr_security_info_t); + security_to_store->ciphering = global_default_ciphering_algorithm; + security_to_store->integrity = global_default_integrity_algorithm; + security_to_store->seen_next_ul_pdu = TRUE; + wmem_map_insert(pdcp_security_result_hash, + get_ueid_frame_hash_key(p_pdcp_info->ueid, pinfo->num, TRUE), + security_to_store); + } + } + } + + /* Show security settings for this PDU */ + pdu_security = (pdcp_nr_security_info_t*)wmem_map_lookup(pdcp_security_result_hash, + get_ueid_frame_hash_key(p_pdcp_info->ueid, pinfo->num, FALSE)); + if (pdu_security != NULL) { + /* Create subtree */ + security_ti = proto_tree_add_string_format(pdcp_tree, + hf_pdcp_nr_security, + tvb, 0, 0, + "", "UE Security"); + security_tree = proto_item_add_subtree(security_ti, ett_pdcp_security); + proto_item_set_generated(security_ti); + + /* Setup frame */ + if (pinfo->num > pdu_security->configuration_frame) { + ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_setup_frame, + tvb, 0, 0, pdu_security->configuration_frame); + proto_item_set_generated(ti); + } + + /* Ciphering */ + ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_ciphering_algorithm, + tvb, 0, 0, pdu_security->ciphering); + proto_item_set_generated(ti); + + /* Integrity */ + ti = proto_tree_add_uint(security_tree, hf_pdcp_nr_security_integrity_algorithm, + tvb, 0, 0, pdu_security->integrity); + proto_item_set_generated(ti); + + proto_item_append_text(security_ti, " (ciphering=%s, integrity=%s)", + val_to_str_const(pdu_security->ciphering, ciphering_algorithm_vals, "Unknown"), + val_to_str_const(pdu_security->integrity, integrity_algorithm_vals, "Unknown")); + + pdu_security_settings.ciphering = pdu_security->ciphering; + pdu_security_settings.integrity = pdu_security->integrity; + } + + /***********************************/ /* Handle PDCP header */ @@ -1239,6 +2075,9 @@ static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, return 1; } + /* Have reached the end of the header (for data frames) */ + gint header_length = offset; + /* Do sequence analysis if configured to. */ if (seqnum_set) { gboolean do_analysis = FALSE; @@ -1261,7 +2100,8 @@ static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, if (do_analysis) { checkBearerSequenceInfo(pinfo, tvb, p_pdcp_info, - seqnum, pdcp_tree); + seqnum, pdcp_tree, security_tree, + &pdu_security_settings); } } @@ -1270,18 +2110,41 @@ static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, /* Now deal with the payload */ /*******************************************************/ - payload_tvb = tvb; + /* Any SDAP bytes (between header and payload) are ignored for integrity/encryption */ + guint sdap_length = 0; + if (p_pdcp_info->plane == NR_USER_PLANE) { + if ((p_pdcp_info->direction == PDCP_NR_DIRECTION_UPLINK && (p_pdcp_info->sdap_header & PDCP_NR_UL_SDAP_HEADER_PRESENT)) || + (p_pdcp_info->direction == PDCP_NR_DIRECTION_DOWNLINK && (p_pdcp_info->sdap_header & PDCP_NR_DL_SDAP_HEADER_PRESENT))) { + /* Currently, all SDAP message bytes are 1 byte long */ + sdap_length = 1; + } + } + + /* Decipher payload if necessary */ + payload_tvb = decipher_payload(tvb, pinfo, &offset, &pdu_security_settings, p_pdcp_info, sdap_length, + pdu_security ? pdu_security->seen_next_ul_pdu: FALSE, &payload_deciphered); + + proto_item *mac_ti = NULL; + guint32 calculated_digest = 0; + gboolean digest_was_calculated = FALSE; + + /* Try to calculate digest so we can check it */ + if (global_pdcp_check_integrity && p_pdcp_info->maci_present) { + calculated_digest = calculate_digest(&pdu_security_settings, + tvb_new_subset_length(tvb, 0, header_length), + payload_tvb, + offset, sdap_length, &digest_was_calculated); + } if (p_pdcp_info->plane == NR_SIGNALING_PLANE) { - guint32 data_length; - /* Compute payload length (no MAC on common control Bearers) */ - data_length = tvb_reported_length_remaining(payload_tvb, offset)-4; - + guint32 data_length = tvb_reported_length_remaining(payload_tvb, offset)-4; /* RRC data is all but last 4 bytes. Call nr-rrc dissector (according to direction and Bearer type) if we have valid data */ - if (global_pdcp_dissect_signalling_plane_as_rrc) { + if ((global_pdcp_dissect_signalling_plane_as_rrc) && + ((pdu_security == NULL) || (pdu_security->ciphering == nea0) || payload_deciphered || !pdu_security->seen_next_ul_pdu)) { + /* Get appropriate dissector handle */ dissector_handle_t rrc_handle = lookup_rrc_dissector_handle(p_pdcp_info, data_length); @@ -1300,8 +2163,16 @@ static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, } else { /* Just show data */ - proto_tree_add_item(pdcp_tree, hf_pdcp_nr_signalling_data, payload_tvb, offset, - data_length, ENC_NA); + proto_tree_add_item(pdcp_tree, hf_pdcp_nr_signalling_data, payload_tvb, offset, + data_length, ENC_NA); + } + + if (!PINFO_FD_VISITED(pinfo) && + (current_security != NULL) && !current_security->seen_next_ul_pdu && + p_pdcp_info->direction == PDCP_NR_DIRECTION_UPLINK) + { + /* i.e. we have already seen SecurityModeResponse! */ + current_security->seen_next_ul_pdu = TRUE; } } else { @@ -1309,21 +2180,12 @@ static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_tree_add_item(pdcp_tree, hf_pdcp_nr_signalling_data, payload_tvb, offset, data_length, ENC_NA); } - - if (p_pdcp_info->bearerType == Bearer_DCCH) { - p_pdcp_info->maci_present = TRUE; - } else { - col_append_fstr(pinfo->cinfo, COL_INFO, " (%u bytes data)", data_length); - } } else if (tvb_captured_length_remaining(payload_tvb, offset)) { /* User-plane payload here. */ gint payload_length = tvb_reported_length_remaining(payload_tvb, offset) - ((p_pdcp_info->maci_present) ? 4 : 0); - if ((p_pdcp_info->direction == PDCP_NR_DIRECTION_UPLINK && - p_pdcp_info->sdap_header & PDCP_NR_UL_SDAP_HEADER_PRESENT) || - (p_pdcp_info->direction == PDCP_NR_DIRECTION_DOWNLINK && - p_pdcp_info->sdap_header & PDCP_NR_DL_SDAP_HEADER_PRESENT)) { + if (sdap_length) { /* SDAP */ proto_item *sdap_ti; @@ -1418,9 +2280,23 @@ static int dissect_pdcp_nr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, /* MAC */ if (p_pdcp_info->maci_present) { /* Last 4 bytes are MAC */ - gint mac_offset = tvb_reported_length(tvb)-4; + gint mac_offset = tvb_reported_length(payload_tvb)-4; guint32 mac = tvb_get_ntohl(payload_tvb, mac_offset); - proto_tree_add_item(pdcp_tree, hf_pdcp_nr_mac, payload_tvb, mac_offset, 4, ENC_BIG_ENDIAN); + mac_ti = proto_tree_add_item(pdcp_tree, hf_pdcp_nr_mac, payload_tvb, mac_offset, 4, ENC_BIG_ENDIAN); + offset += 4; + + if (digest_was_calculated) { + /* Compare what was found with calculated value! */ + if (mac != calculated_digest) { + expert_add_info_format(pinfo, mac_ti, &ei_pdcp_nr_digest_wrong, + "MAC-I Digest wrong - calculated %08x but found %08x", + calculated_digest, mac); + proto_item_append_text(mac_ti, " (but calculated %08x !)", calculated_digest); + } + else { + proto_item_append_text(mac_ti, " [Matches calculated result]"); + } + } col_append_fstr(pinfo->cinfo, COL_INFO, " MAC=0x%08x", mac); } @@ -1478,6 +2354,12 @@ void proto_register_pdcp_nr(void) "Sequence Number Length", HFILL } }, + { &hf_pdcp_nr_maci_present, + { "MAC-I Present", + "pdcp-nr.maci_present", FT_BOOLEAN, BASE_NONE, NULL, 0x0, + "Indicates whether MAC-I digest bytes are expected", HFILL + } + }, { &hf_pdcp_nr_sdap, { "SDAP header", "pdcp-nr.sdap", FT_BOOLEAN, 8, TFS(&tfs_present_not_present), 0x0, @@ -1655,6 +2537,62 @@ void proto_register_pdcp_nr(void) NULL, HFILL } }, + + /* Security fields */ + { &hf_pdcp_nr_security, + { "Security Config", + "pdcp-nr.security-config", FT_STRING, BASE_NONE, 0, 0x0, + NULL, HFILL + } + }, + { &hf_pdcp_nr_security_setup_frame, + { "Configuration frame", + "pdcp-nr.security-config.setup-frame", FT_FRAMENUM, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_pdcp_nr_security_integrity_algorithm, + { "Integrity Algorithm", + "pdcp-nr.security-config.integrity", FT_UINT16, BASE_DEC, VALS(integrity_algorithm_vals), 0x0, + NULL, HFILL + } + }, + { &hf_pdcp_nr_security_ciphering_algorithm, + { "Ciphering Algorithm", + "pdcp-nr.security-config.ciphering", FT_UINT16, BASE_DEC, VALS(ciphering_algorithm_vals), 0x0, + NULL, HFILL + } + }, + { &hf_pdcp_nr_security_bearer, + { "BEARER", + "pdcp-nr.security-config.bearer", FT_UINT8, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_pdcp_nr_security_direction, + { "DIRECTION", + "pdcp-nr.security-config.direction", FT_UINT8, BASE_DEC, VALS(direction_vals), 0x0, + NULL, HFILL + } + }, + { &hf_pdcp_nr_security_count, + { "COUNT", + "pdcp-nr.security-config.count", FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_pdcp_nr_security_cipher_key, + { "CIPHER KEY", + "pdcp-nr.security-config.cipher-key", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + }, + { &hf_pdcp_nr_security_integrity_key, + { "INTEGRITY KEY", + "pdcp-nr.security-config.integrity-key", FT_STRING, BASE_NONE, NULL, 0x0, + NULL, HFILL + } + } }; static hf_register_info hf_sdap[] = @@ -1698,7 +2636,8 @@ void proto_register_pdcp_nr(void) &ett_pdcp_packet, &ett_pdcp_nr_sequence_analysis, &ett_pdcp_report_bitmap, - &ett_sdap + &ett_sdap, + &ett_pdcp_security }; static ei_register_info ei[] = { @@ -1706,6 +2645,7 @@ void proto_register_pdcp_nr(void) { &ei_pdcp_nr_sequence_analysis_sn_repeated, { "pdcp-nr.sequence-analysis.sn-repeated", PI_SEQUENCE, PI_WARN, "PDCP SN repeated", EXPFILL }}, { &ei_pdcp_nr_sequence_analysis_wrong_sequence_number, { "pdcp-nr.sequence-analysis.wrong-sequence-number", PI_SEQUENCE, PI_WARN, "Wrong Sequence Number", EXPFILL }}, { &ei_pdcp_nr_reserved_bits_not_zero, { "pdcp-nr.reserved-bits-not-zero", PI_MALFORMED, PI_ERROR, "Reserved bits not zero", EXPFILL }}, + { &ei_pdcp_nr_digest_wrong, { "pdcp-nr.maci-wrong", PI_SEQUENCE, PI_ERROR, "MAC-I doesn't match expected value", EXPFILL }}, { &ei_pdcp_nr_unknown_udp_framing_tag, { "pdcp-nr.unknown-udp-framing-tag", PI_UNDECODED, PI_WARN, "Unknown UDP framing tag, aborting dissection", EXPFILL }}, { &ei_pdcp_nr_missing_udp_framing_tag, { "pdcp-nr.missing-udp-framing-tag", PI_UNDECODED, PI_WARN, "Missing UDP framing conditional tag, aborting dissection", EXPFILL }} }; @@ -1724,6 +2664,32 @@ void proto_register_pdcp_nr(void) {NULL, NULL, -1} }; + static const enum_val_t default_ciphering_algorithm_vals[] = { + {"nea0", "NEA0 (NULL)", nea0}, + {"nea1", "NEA1 (SNOW3G)", nea1}, + {"nea2", "NEA2 (AES)", nea2}, + {"nea3", "NEA3 (ZUC)", nea3}, + {NULL, NULL, -1} + }; + + static const enum_val_t default_integrity_algorithm_vals[] = { + {"nia0", "NIA0 (NULL)", nia0}, + {"nia1", "NIA1 (SNOW3G)", nia1}, + {"nia2", "NIA2 (AES)", nia2}, + {"nia3", "NIA3 (ZUC)", nia3}, + {NULL, NULL, -1} + }; + + static uat_field_t ue_keys_uat_flds[] = { + UAT_FLD_DEC(uat_ue_keys_records, ueid, "UEId", "UE Identifier of UE associated with keys"), + UAT_FLD_CSTRING(uat_ue_keys_records, rrcCipherKeyString, "RRC Cipher Key", "Key for deciphering signalling messages"), + UAT_FLD_CSTRING(uat_ue_keys_records, upCipherKeyString, "User-Plane Cipher Key", "Key for deciphering user-plane messages"), + UAT_FLD_CSTRING(uat_ue_keys_records, rrcIntegrityKeyString, "RRC Integrity Key", "Key for calculating signalling integrity MAC"), + UAT_FLD_CSTRING(uat_ue_keys_records, upIntegrityKeyString, "RRC Integrity Key", "Key for calculating user-plane integrity MAC"), + UAT_END_FIELDS + }; + + module_t *pdcp_nr_module; expert_module_t* expert_pdcp_nr; @@ -1772,9 +2738,67 @@ void proto_register_pdcp_nr(void) "Can show RLC, PDCP or Traffic layer info in Info column", &global_pdcp_nr_layer_to_show, show_info_col_vals, FALSE); + ue_keys_uat = uat_new("PDCP UE security keys", + sizeof(uat_ue_keys_record_t), /* record size */ + "pdcp_nr_ue_keys", /* filename */ + TRUE, /* from_profile */ + &uat_ue_keys_records, /* data_ptr */ + &num_ue_keys_uat, /* numitems_ptr */ + UAT_AFFECTS_DISSECTION, /* affects dissection of packets, but not set of named fields */ + NULL, /* help */ + uat_ue_keys_record_copy_cb, /* copy callback */ + uat_ue_keys_record_update_cb, /* update callback */ + uat_ue_keys_record_free_cb, /* free callback */ + NULL, /* post update callback */ + NULL, /* reset callback */ + ue_keys_uat_flds); /* UAT field definitions */ + + prefs_register_uat_preference(pdcp_nr_module, + "ue_keys_table", + "PDCP UE Keys", + "Preconfigured PDCP keys", + ue_keys_uat); + + prefs_register_enum_preference(pdcp_nr_module, "default_ciphering_algorithm", + "Ciphering algorithm to use if not signalled", + "If RRC Security Info not seen, e.g. in Handover", + (gint*)&global_default_ciphering_algorithm, default_ciphering_algorithm_vals, FALSE); + + prefs_register_enum_preference(pdcp_nr_module, "default_integrity_algorithm", + "Integrity algorithm to use if not signalled", + "If RRC Security Info not seen, e.g. in Handover", + (gint*)&global_default_integrity_algorithm, default_integrity_algorithm_vals, FALSE); + + /* Attempt to decipher RRC messages */ + prefs_register_bool_preference(pdcp_nr_module, "decipher_signalling", + "Attempt to decipher Signalling (RRC) SDUs", + "N.B. only possible if build with algorithm support, and have key available and configured", + &global_pdcp_decipher_signalling); + + /* Attempt to decipher user-plane messages */ + prefs_register_bool_preference(pdcp_nr_module, "decipher_userplane", + "Attempt to decipher User-plane (IP) SDUs", + "N.B. only possible if build with algorithm support, and have key available and configured", + &global_pdcp_decipher_userplane); + + /* Attempt to verify RRC integrity/authentication digest */ + prefs_register_bool_preference(pdcp_nr_module, "verify_integrity", + "Attempt to check integrity calculation", + "N.B. only possible if build with algorithm support, and have key available and configured", + &global_pdcp_check_integrity); + + + prefs_register_bool_preference(pdcp_nr_module, "ignore_rrc_sec_params", + "Ignore RRC security parameters", + "Ignore the NR RRC security algorithm configuration, to be used when PDCP is already deciphered in the capture", + &global_pdcp_ignore_sec); + pdcp_sequence_analysis_bearer_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal); pdcp_nr_sequence_analysis_report_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), pdcp_result_hash_func, pdcp_result_hash_equal); + pdcp_security_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal); + pdcp_security_result_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), pdcp_nr_ueid_frame_hash_func, pdcp_nr_ueid_frame_hash_equal); + pdcp_security_key_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), g_direct_hash, g_direct_equal); } void proto_reg_handoff_pdcp_nr(void) diff --git a/epan/dissectors/packet-pdcp-nr.h b/epan/dissectors/packet-pdcp-nr.h index e20b1b09f6..578f3bf089 100644 --- a/epan/dissectors/packet-pdcp-nr.h +++ b/epan/dissectors/packet-pdcp-nr.h @@ -37,6 +37,23 @@ typedef enum NRBearerType #define PDCP_NR_UL_SDAP_HEADER_PRESENT 0x01 #define PDCP_NR_DL_SDAP_HEADER_PRESENT 0x02 +enum nr_security_integrity_algorithm_e { nia0, nia1, nia2, nia3 }; +enum nr_security_ciphering_algorithm_e { nea0, nea1, nea2, nea3 }; + +typedef struct pdcp_nr_security_info_t +{ + guint32 configuration_frame; + gboolean seen_next_ul_pdu; /* i.e. have we seen SecurityModeResponse */ + enum nr_security_integrity_algorithm_e integrity; + enum nr_security_ciphering_algorithm_e ciphering; + + /* Store previous settings so can revert if get SecurityModeFailure */ + guint32 previous_configuration_frame; + enum nr_security_integrity_algorithm_e previous_integrity; + enum nr_security_ciphering_algorithm_e previous_ciphering; +} pdcp_nr_security_info_t; + + /* Info attached to each nr PDCP/RoHC packet */ typedef struct pdcp_nr_info { @@ -78,7 +95,7 @@ void set_pdcp_nr_proto_data(packet_info *pinfo, pdcp_nr_info *p_pdcp_nr_info); /* and implemented by this dissector, using the definitions */ /* below. */ /* */ -/* A heuristic dissecter (enabled by a preference) will */ +/* A heuristic dissector (enabled by a preference) will */ /* recognise a signature at the beginning of these frames. */ /* Until someone is using this format, suggestions for changes */ /* are welcome. */ @@ -154,6 +171,21 @@ void set_pdcp_nr_proto_data(packet_info *pinfo, pdcp_nr_info *p_pdcp_nr_info); #define PDCP_NR_PAYLOAD_TAG 0x01 +/* Called by RRC, or other configuration protocols */ + +/* Function to configure ciphering & integrity algorithms */ +void set_pdcp_nr_security_algorithms(guint16 ueid, pdcp_nr_security_info_t *security_info); + +/* Function to indicate securityModeCommand did not complete */ +void set_pdcp_nr_security_algorithms_failed(guint16 ueid); + + +/* Called by external dissectors */ +void set_pdcp_nr_rrc_ciphering_key(guint16 ueid, const char *key); +void set_pdcp_nr_rrc_integrity_key(guint16 ueid, const char *key); +void set_pdcp_nr_up_ciphering_key(guint16 ueid, const char *key); +void set_pdcp_nr_up_integrity_key(guint16 ueid, const char *key); + /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * diff --git a/epan/dissectors/packet-rlc-nr.c b/epan/dissectors/packet-rlc-nr.c index 457a3e0799..14461a6d1a 100644 --- a/epan/dissectors/packet-rlc-nr.c +++ b/epan/dissectors/packet-rlc-nr.c @@ -66,9 +66,12 @@ static gboolean global_rlc_nr_reassemble_am_pdus = TRUE; /* Tree storing UE related parameters */ typedef struct rlc_ue_parameters { - guint32 id; - guint8 pdcp_sn_bits_ul; - guint8 pdcp_sn_bits_dl; + guint32 id; + guint8 pdcp_sn_bits_ul; + guint8 pdcp_sn_bits_dl; + gboolean pdcp_sdap_ul; + gboolean pdcp_sdap_dl; + gboolean pdcp_integrity; } rlc_ue_parameters; static wmem_tree_t *ue_parameters_tree; @@ -471,10 +474,17 @@ static void show_PDU_in_tree(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb if (params) { if (p_pdcp_nr_info->direction == DIRECTION_UPLINK) { p_pdcp_nr_info->seqnum_length = params->pdcp_sn_bits_ul; + if (params->pdcp_sdap_ul) { + p_pdcp_nr_info->sdap_header &= PDCP_NR_UL_SDAP_HEADER_PRESENT; + } } else { p_pdcp_nr_info->seqnum_length = params->pdcp_sn_bits_dl; + if (params->pdcp_sdap_dl) { + p_pdcp_nr_info->sdap_header &= PDCP_NR_DL_SDAP_HEADER_PRESENT; + } } + p_pdcp_nr_info->maci_present = params->pdcp_integrity; } break; @@ -1294,7 +1304,10 @@ static void dissect_rlc_nr_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree /* Configure number of PDCP SN bits to use for DRB channels */ void set_rlc_nr_drb_pdcp_seqnum_length(packet_info *pinfo, guint16 ueid, guint8 drbid, guint8 userplane_seqnum_length_ul, - guint8 userplane_seqnum_length_dl) + guint8 userplane_seqnum_length_dl, + gboolean sdap_ul, + gboolean sdap_dl, + gboolean integrity) { wmem_tree_key_t key[3]; guint32 id; @@ -1312,17 +1325,24 @@ void set_rlc_nr_drb_pdcp_seqnum_length(packet_info *pinfo, guint16 ueid, guint8 key[2].length = 0; key[2].key = NULL; + /* Look up entry for this UEId/drbid */ params = (rlc_ue_parameters *)wmem_tree_lookup32_array_le(ue_parameters_tree, key); if (params && (params->id != id)) { params = NULL; } if (params == NULL) { + /* Not found so create new entry */ params = (rlc_ue_parameters *)wmem_new(wmem_file_scope(), rlc_ue_parameters); params->id = id; wmem_tree_insert32_array(ue_parameters_tree, key, (void *)params); } + + /* Populate params */ params->pdcp_sn_bits_ul = userplane_seqnum_length_ul; params->pdcp_sn_bits_dl = userplane_seqnum_length_dl; + params->pdcp_sdap_ul = sdap_ul; + params->pdcp_sdap_dl = sdap_dl; + params->pdcp_integrity = integrity; } diff --git a/epan/dissectors/packet-rlc-nr.h b/epan/dissectors/packet-rlc-nr.h index 77a4760b4d..6a7fedba2c 100644 --- a/epan/dissectors/packet-rlc-nr.h +++ b/epan/dissectors/packet-rlc-nr.h @@ -50,7 +50,9 @@ typedef struct rlc_nr_info /* Configure number of PDCP SN bits to use for DRB channels. */ void set_rlc_nr_drb_pdcp_seqnum_length(packet_info *pinfo, guint16 ueid, guint8 drbid, guint8 userplane_seqnum_length_ul, - guint8 userplane_seqnum_length_dl); + guint8 userplane_seqnum_length_dl, + gboolean sdap_ul, gboolean sdap_dl, + gboolean integrity); /*****************************************************************/ /* UDP framing format */