srsue/extnas: implement encryption and signing of NAS messages

This commit is contained in:
Vadim Yanitskiy 2020-11-07 19:47:54 +07:00
parent 19ed00e9bb
commit fbb2ed3aa9
2 changed files with 84 additions and 1 deletions

View File

@ -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);

View File

@ -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<const struct rrctl::proto::msg_sec_mode_req*> (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<srslte::CIPHERING_ALGORITHM_ID_ENUM> (req->eea);
ctxt.integ_algo = static_cast<srslte::INTEGRITY_ALGORITHM_ID_ENUM> (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);
}