From fbb2ed3aa90130bf634c5d5bd8899afcc6287c88 Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Sat, 7 Nov 2020 19:47:54 +0700 Subject: [PATCH] srsue/extnas: implement encryption and signing of NAS messages --- srsue/hdr/stack/upper/nas_ext.h | 1 + srsue/src/stack/upper/nas_ext.cc | 84 +++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/srsue/hdr/stack/upper/nas_ext.h b/srsue/hdr/stack/upper/nas_ext.h index 78b0bbd0c..f891892f9 100644 --- a/srsue/hdr/stack/upper/nas_ext.h +++ b/srsue/hdr/stack/upper/nas_ext.h @@ -88,6 +88,7 @@ private: void handle_rrctl_conn_release(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len); void handle_rrctl_data(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len); void handle_rrctl_param(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len); + void handle_rrctl_sec_mode(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len); void handle_rrctl_ext_usim(const uint8_t* msg, size_t len); void handle_usim_gen_auth_resp_req(const struct rrctl::proto::ext_usim_msg* msg, size_t len); diff --git a/srsue/src/stack/upper/nas_ext.cc b/srsue/src/stack/upper/nas_ext.cc index 2d29aca5f..29f7f970a 100644 --- a/srsue/src/stack/upper/nas_ext.cc +++ b/srsue/src/stack/upper/nas_ext.cc @@ -84,6 +84,8 @@ void nas_ext::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interf handle_rrctl_param(disc, payload, length); break; case rrctl::proto::RRCTL_SEC_MODE: + handle_rrctl_sec_mode(disc, payload, length); + break; case rrctl::proto::RRCTL_EXT_USIM: handle_rrctl_ext_usim(payload, length); break; @@ -115,7 +117,9 @@ void nas_ext::rrctl_send_error(rrctl::proto::msg_type type) void nas_ext::handle_rrctl_reset(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len) { - // TODO: do we need to reset anything? + /* Reset security state (no EEA, no EIA) */ + memset(&ctxt, 0x00, sizeof(ctxt)); + rrctl_send_confirm(rrctl::proto::RRCTL_RESET); } @@ -162,6 +166,9 @@ void nas_ext::handle_rrctl_conn_establish(rrctl::proto::msg_disc disc, const uin return; } + set_k_enb_count(ctxt.tx_count); + ctxt.tx_count++; + // Allocate a new NAS PDU on heap unique_byte_buffer_t nas_pdu = srslte::allocate_unique_buffer(*pool, true); nas_pdu->append_bytes(pdu, pdu_len); @@ -181,7 +188,15 @@ void nas_ext::handle_rrctl_data(rrctl::proto::msg_disc disc, const uint8_t* msg, unique_byte_buffer_t nas_pdu = srslte::allocate_unique_buffer(*pool, true); nas_pdu->append_bytes(msg, len); + // Apply pre-configured EEA algorythm (if enabled) + cipher_encrypt(nas_pdu.get()); + // Apply pre-configured EIA algorythm (if enabled) + integrity_generate(&k_nas_int[16], ctxt.tx_count, SECURITY_DIRECTION_UPLINK, + &nas_pdu->msg[5], nas_pdu->N_bytes - 5, &nas_pdu->msg[1]); + rrc->write_sdu(std::move(nas_pdu)); + ctxt.tx_count++; + rrctl_send_confirm(rrctl::proto::RRCTL_DATA); } @@ -215,6 +230,53 @@ void nas_ext::handle_rrctl_param(rrctl::proto::msg_disc disc, const uint8_t* msg } } +void nas_ext::handle_rrctl_sec_mode(rrctl::proto::msg_disc disc, const uint8_t* msg, size_t len) +{ + const struct rrctl::proto::msg_sec_mode_req* req; + + req = reinterpret_cast (msg); + + if (len < sizeof(*req)) { /* XXX: mandatory fields only */ + nas_log->error("Received malformed PARAM.req (len=%zu)\n", len); + rrctl_send_error(rrctl::proto::RRCTL_SEC_MODE); + return; + } + + /* Skip the header */ + len -= sizeof(*req); + + nas_log->info("Rx SecurityMode.req (EEA%u, EIA%u)\n", req->eea, req->eia); + + ctxt.cipher_algo = static_cast (req->eea); + ctxt.integ_algo = static_cast (req->eia); + + if (req->flags & SEC_MODE_F_RESET_RX_CTR) + ctxt.rx_count = 0; + if (req->flags & SEC_MODE_F_RESET_TX_CTR) + ctxt.tx_count = 0; + + if (req->eea != 0x00 or req->eia != 0x00) { + /* Ensure that Kasme is present */ + if (len != sizeof(ctxt.k_asme)) { + nas_log->error("Kasme is expected, but not present"); + rrctl_send_error(rrctl::proto::RRCTL_SEC_MODE); + return; + } + + memcpy(&ctxt.k_asme[0], &req->k_asme[0], sizeof(ctxt.k_asme)); + + /* Derive both Knas_enc and Knas_int from Kasme */ + /* NOTE: how is this related to (U)SIM at all?!? */ + usim->generate_nas_keys(&ctxt.k_asme[0], // in + &k_nas_enc[0], // out + &k_nas_int[0], // out + ctxt.cipher_algo, + ctxt.integ_algo); + } + + rrctl_send_confirm(rrctl::proto::RRCTL_SEC_MODE); +} + void nas_ext::handle_rrctl_ext_usim(const uint8_t* _msg, size_t len) { enum rrctl::proto::ext_usim_msg_type msg_type; @@ -296,6 +358,8 @@ void nas_ext::handle_usim_gen_auth_resp_req(const struct rrctl::proto::ext_usim_ case AUTH_OK: nas_log->info("Authentication vector has been generated successfully\n"); + set_k_enb_count(0); + pdu.N_bytes += sizeof(*rsp); pdu.append_bytes(&k_asme[0], sizeof(k_asme)); pdu.append_bytes(&res[0], res_len); @@ -380,9 +444,27 @@ bool nas_ext::paging(srslte::s_tmsi_t* ue_identity) void nas_ext::write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) { srslte::byte_buffer_t msg; + uint8 pd, sec_hdr_type; nas_log->info_hex(pdu->msg, pdu->N_bytes, "Received DL %s PDU from RRC\n", rrc->get_rb_name(lcid).c_str()); + // Parse the message security header + liblte_mme_parse_msg_sec_header((LIBLTE_BYTE_MSG_STRUCT*) pdu.get(), &pd, &sec_hdr_type); + switch (sec_hdr_type) { + case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED_WITH_NEW_EPS_SECURITY_CONTEXT: + case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_WITH_NEW_EPS_SECURITY_CONTEXT: + case LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST: + case LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS: + case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY: + break; + case LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED: + // Apply pre-configured EEA algorythm (if enabled) + cipher_decrypt(pdu.get()); + break; + default: + nas_log->error("Received DL NAS PDU with unknown sec_hdr=%02x\n", sec_hdr_type); + } + rrctl::codec::enc_data_ind(msg, pdu->msg, pdu->N_bytes, lcid); iface->write(msg); }