From 6beb92278f562466e270c65981a5c14fdf7c7722 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Fri, 2 Aug 2024 09:38:37 +0200 Subject: [PATCH] Read type of public key (#16) * Clang Tidy fixes * Improve reading public key type * Update osnma_nav_data_manager.cc hotfix --------- Co-authored-by: cesaaargm --- src/core/libs/osnma_msg_receiver.cc | 133 ++++++---- src/core/system_parameters/gnss_crypto.cc | 247 +++++++++++++----- src/core/system_parameters/gnss_crypto.h | 11 +- .../osnma_nav_data_manager.cc | 32 ++- .../osnma/gnss_crypto_test.cc | 30 ++- 5 files changed, 307 insertions(+), 146 deletions(-) diff --git a/src/core/libs/osnma_msg_receiver.cc b/src/core/libs/osnma_msg_receiver.cc index 380d917f4..d5cb1fcaa 100644 --- a/src/core/libs/osnma_msg_receiver.cc +++ b/src/core/libs/osnma_msg_receiver.cc @@ -29,6 +29,7 @@ #include #include #include +#include // for std::ifstream and std::ofstream #include // for std::setfill #include // for std::hex, std::uppercase #include @@ -37,7 +38,6 @@ #include #include // for typeid #include -#include // for std::ifstream and std::ofstream #if USE_GLOG_AND_GFLAGS @@ -68,21 +68,23 @@ osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, osnma_msg_receiver::osnma_msg_receiver(const std::string& crtFilePath, const std::string& merkleFilePath) : gr::block("osnma_msg_receiver", - gr::io_signature::make(0, 0, 0), - gr::io_signature::make(0, 0, 0)) + gr::io_signature::make(0, 0, 0), + gr::io_signature::make(0, 0, 0)) { d_dsm_reader = std::make_unique(); d_crypto = std::make_unique(crtFilePath, merkleFilePath); d_helper = std::make_unique(); d_nav_data_manager = std::make_unique(); - if(d_crypto->have_public_key()){ // Hot start is enabled + if (d_crypto->have_public_key()) + { // Hot start is enabled LOG(WARNING) << "OSNMA Public Key available, trying to find DSM-KROOT saved"; std::cout << "OSNMA Public Key available, trying to find DSM-KROOT saved" << std::endl; d_public_key_verified = true; auto dsm_nmah = parse_dsm_kroot(); - if (!dsm_nmah.first.empty()){ + if (!dsm_nmah.first.empty()) + { LOG(WARNING) << "OSNMA DSM-KROOT and NMA Header successfully read from file " << KROOTFILE_DEFAULT; std::cout << "OSNMA DSM-KROOT and NMA Header successfully read from file " << KROOTFILE_DEFAULT << std::endl; d_flag_hot_start = true; @@ -113,20 +115,22 @@ osnma_msg_receiver::osnma_msg_receiver(const std::string& crtFilePath, const std #endif #endif std::chrono::time_point now; - if (d_flag_debug){ + if (d_flag_debug) + { // d_GST_Rx = d_helper->compute_gst(d_initial_debug_time); LOG(WARNING) << "Galileo OSNMA: Debug mode, time artificially set up."; std::cout << "Galileo OSNMA: Debug mode, time artificially set up." << std::endl; // TODO - need to synchronize time lapse with Gnss_Synchro? } - else{ + else + { d_GST_Rx = d_helper->compute_gst_now(); } d_WN = d_helper->get_WN(d_GST_Rx); d_TOW = d_helper->get_TOW(d_GST_Rx); - LOG(WARNING) << "Galileo OSNMA: initial receiver time GST=["<< d_WN << " " << d_TOW <<"]"; - std::cout << "Galileo OSNMA: initial receiver time GST=["<< d_WN << " " << d_TOW <<"]" << std::endl; + LOG(WARNING) << "Galileo OSNMA: initial receiver time GST=[" << d_WN << " " << d_TOW << "]"; + std::cout << "Galileo OSNMA: initial receiver time GST=[" << d_WN << " " << d_TOW << "]" << std::endl; } @@ -155,16 +159,20 @@ void osnma_msg_receiver::msg_handler_osnma(const pmt::pmt_t& msg) // Receiver time update d_GST_SIS = d_helper->compute_gst(nma_msg->WN_sf0, nma_msg->TOW_sf0); - if (d_last_verified_key_GST == 0){ + if (d_last_verified_key_GST == 0) + { d_last_received_GST = d_GST_SIS; } - else if (d_GST_SIS > d_last_received_GST){ + else if (d_GST_SIS > d_last_received_GST) + { d_last_received_GST = d_GST_SIS; } - if (d_flag_debug){ + if (d_flag_debug) + { d_GST_Rx = d_last_received_GST; } - else{ + else + { d_GST_Rx = d_helper->compute_gst_now(); } LOG(INFO) << "Galileo OSNMA: Receiver Time GST=[" << d_helper->get_WN(d_GST_Rx) << " " << d_helper->get_TOW(d_GST_Rx) << "]"; @@ -198,7 +206,7 @@ void osnma_msg_receiver::msg_handler_osnma(const pmt::pmt_t& msg) } process_osnma_message(nma_msg); - } // OSNMA frame received + } // OSNMA frame received else if (msg_type_hash_code == typeid(std::shared_ptr>).hash_code()) // Navigation data bits for OSNMA received { const auto inav_data = wht::any_cast>>(pmt::any_ref(msg)); @@ -231,26 +239,30 @@ void osnma_msg_receiver::msg_handler_osnma(const pmt::pmt_t& msg) void osnma_msg_receiver::process_osnma_message(const std::shared_ptr& osnma_msg) { - if (d_flag_alert_message && (d_public_key_verified || d_kroot_verified)){ + if (d_flag_alert_message && (d_public_key_verified || d_kroot_verified)) + { return; } read_nma_header(osnma_msg->hkroot[0]); // Check for corner cases: renewal, revocation, alert message - if (d_osnma_data.d_nma_header.nmas == 0 /* RES */){ + if (d_osnma_data.d_nma_header.nmas == 0 /* RES */) + { LOG(WARNING) << "Galileo OSNMA: NMAS invalid (RES), skipping osnma message"; return; } // TODO - trusting the NMAS and CPKS shall be done upon PKR verification or Tag verification. // It's ok to activate the flags, but the final decision should happen after verifying it. // For OAM is solved, but NPK and PKREV I think not yet - if (d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 4 /* NPK */ && d_GST_PKR_PKREV_start == 0){ + if (d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 4 /* NPK */ && d_GST_PKR_PKREV_start == 0) + { d_flag_PK_renewal = true; d_GST_PKR_PKREV_start = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0); LOG(INFO) << "Galileo OSNMA: Public Key Renewal :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]"; std::cout << "Galileo OSNMA: Public Key Renewal :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << std::endl; } - if (d_flag_PK_renewal && d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 1 /* Nominal */ ){ + if (d_flag_PK_renewal && d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 1 /* Nominal */) + { d_flag_PK_renewal = false; uint32_t final_GST = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0); double duration_hours = (final_GST - d_GST_PKR_PKREV_start) / 3600; @@ -258,7 +270,8 @@ void osnma_msg_receiver::process_osnma_message(const std::shared_ptr& std::cout << "Galileo OSNMA: Public Key Renewal :: Finished at GST=" << duration_hours << ", Duration=" << duration_hours << " h" << std::endl; } - if (d_osnma_data.d_nma_header.nmas == 3 /* DU */ && d_osnma_data.d_nma_header.cpks == 5 /* PKREV */ && d_GST_PKR_PKREV_start == 0){ + if (d_osnma_data.d_nma_header.nmas == 3 /* DU */ && d_osnma_data.d_nma_header.cpks == 5 /* PKREV */ && d_GST_PKR_PKREV_start == 0) + { d_flag_PK_revocation = true; d_number_of_blocks[d_osnma_data.d_dsm_header.dsm_id] = 0; d_public_key_verified = false; @@ -268,16 +281,20 @@ void osnma_msg_receiver::process_osnma_message(const std::shared_ptr& LOG(INFO) << "Galileo OSNMA: Public Key Revocation :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]"; std::cout << "Galileo OSNMA: Public Key Revocation :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << std::endl; } - if (d_flag_PK_revocation && d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 1 /* Nominal */ ){ + if (d_flag_PK_revocation && d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 1 /* Nominal */) + { // step 2 , start using new chain d_flag_PK_revocation = false; uint32_t final_GST = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0); double duration_hours = (final_GST - d_GST_PKR_PKREV_start) / 3600; - LOG(INFO) << "Galileo OSNMA: Public Key Revocation :: Finished at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << ", Duration=" << duration_hours << "h"; - std::cout << "Galileo OSNMA: Public Key Revocation :: Finished at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << ", Duration=" << duration_hours << "h" << std::endl; + LOG(INFO) << "Galileo OSNMA: Public Key Revocation :: Finished at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" + << ", Duration=" << duration_hours << "h"; + std::cout << "Galileo OSNMA: Public Key Revocation :: Finished at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" + << ", Duration=" << duration_hours << "h" << std::endl; } - if (d_osnma_data.d_nma_header.nmas == 3 /* DU */ && d_osnma_data.d_nma_header.cpks == 7 /* AM */ && d_GST_PKR_AM_start == 0){ + if (d_osnma_data.d_nma_header.nmas == 3 /* DU */ && d_osnma_data.d_nma_header.cpks == 7 /* AM */ && d_GST_PKR_AM_start == 0) + { d_flag_alert_message = true; d_GST_PKR_AM_start = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0); d_public_key_verified = false; @@ -289,7 +306,8 @@ void osnma_msg_receiver::process_osnma_message(const std::shared_ptr& read_dsm_header(osnma_msg->hkroot[1]); read_dsm_block(osnma_msg); process_dsm_block(osnma_msg); // will process dsm block if received a complete one, then will call mack processing upon re-setting the dsm block to 0 - if (d_osnma_data.d_dsm_kroot_message.towh_k != 0){ + if (d_osnma_data.d_dsm_kroot_message.towh_k != 0) + { d_GST_0 = d_helper->compute_gst(d_osnma_data.d_dsm_kroot_message.wn_k, d_osnma_data.d_dsm_kroot_message.towh_k * 3600); d_GST_Sf = d_GST_0 + 30 * std::floor((d_GST_SIS - d_GST_0) / 30); // Eq. 3 R.G. } @@ -474,7 +492,7 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg d_osnma_data.d_dsm_kroot_message.kroot = d_dsm_reader->get_kroot(dsm_msg, l_lk_bytes); // DS field uint16_t l_ds_bits = 0; - const auto it = OSNMA_TABLE_15.find(d_crypto->d_PublicKeyType); + const auto it = OSNMA_TABLE_15.find(d_crypto->get_public_key_type()); if (it != OSNMA_TABLE_15.cend()) { l_ds_bits = it->second; @@ -507,12 +525,12 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg const uint16_t size_m = 13 + l_lk_bytes; std::vector MSG; MSG.reserve(size_m + l_ds_bytes + 1); - MSG.push_back(nma_header); // NMA header + MSG.push_back(nma_header); // NMA header for (uint16_t i = 1; i < size_m; i++) { MSG.push_back(dsm_msg[i]); } - std::vector message = MSG; // MSG = (M | DS) from ICD. Eq. 7 + std::vector message = MSG; // MSG = (M | DS) from ICD. Eq. 7 for (uint16_t k = 0; k < l_ds_bytes; k++) { MSG.push_back(d_osnma_data.d_dsm_kroot_message.ds[k]); @@ -549,7 +567,8 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg // local_time_verification(osnma_msg); // FIXME TODO: real time verification needed // If new PK verified and the new KROOT arrived, set the new PK before attempting verification - if(d_flag_PK_renewal && d_osnma_data.d_dsm_kroot_message.pkid == d_new_public_key_id && d_flag_NPK_set == false){ + if (d_flag_PK_renewal && d_osnma_data.d_dsm_kroot_message.pkid == d_new_public_key_id && d_flag_NPK_set == false) + { d_crypto->set_public_key(d_new_public_key); d_crypto->store_public_key(PEMFILE_DEFAULT); d_flag_NPK_set = true; @@ -566,7 +585,8 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg { std::cout << "Galileo OSNMA: DSM-KROOT authentication successful!" << std::endl; LOG(INFO) << "Galileo OSNMA: DSM-KROOT authentication successful!"; - if (d_flag_alert_message){ + if (d_flag_alert_message) + { LOG(WARNING) << "Galileo OSNMA: DSM-KROOT :: Alert message verification :: SUCCESS. "; } else @@ -576,7 +596,8 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg << "Chain and Public Key Status is " << d_dsm_reader->get_cpks_status(d_osnma_data.d_nma_header.cpks); } // Save DSM-Kroot and NMA header into a permanent storage - if (d_flag_hot_start){ + if (d_flag_hot_start) + { d_flag_hot_start = false; return; } @@ -586,10 +607,11 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg { LOG(WARNING) << "Galileo OSNMA: DSM-KROOT authentication failed."; std::cerr << "Galileo OSNMA: DSM-KROOT authentication failed." << std::endl; - if (d_flag_alert_message){ + if (d_flag_alert_message) + { d_flag_alert_message = false; } - d_count_failed_Kroot ++; + d_count_failed_Kroot++; } } else @@ -613,7 +635,8 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg } d_osnma_data.d_dsm_pkr_message.npkt = d_dsm_reader->get_npkt(dsm_msg); uint8_t npktid = d_dsm_reader->get_npktid(dsm_msg); - if (d_flag_PK_renewal && npktid > d_osnma_data.d_dsm_pkr_message.npktid){ + if (d_flag_PK_renewal && npktid > d_osnma_data.d_dsm_pkr_message.npktid) + { d_new_public_key_id = npktid; } d_osnma_data.d_dsm_pkr_message.npktid = npktid; @@ -668,15 +691,18 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg { LOG(INFO) << "Galileo OSNMA: DSM-PKR verification :: SUCCESS"; d_public_key_verified = true; - if (d_flag_PK_renewal){ + if (d_flag_PK_renewal) + { d_new_public_key = d_osnma_data.d_dsm_pkr_message.npk; } - else if (d_flag_alert_message){ + else if (d_flag_alert_message) + { LOG(WARNING) << "Galileo OSNMA: DSM-PKR verification :: Alert message verification :: SUCCESS. OSNMA disabled. Contact Galileo Service Centre"; std::cout << "Galileo OSNMA: DSM-PKR verification :: Alert message verification :: SUCCESS. OSNMA disabled. Contact Galileo Service Centre" << std::endl; } - else{ - d_crypto->d_PublicKeyType = PKT; + else + { + d_crypto->set_public_key_type(PKT); d_crypto->set_public_key(d_osnma_data.d_dsm_pkr_message.npk); d_crypto->store_public_key(PEMFILE_DEFAULT); } @@ -685,8 +711,9 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg { LOG(ERROR) << "Galileo OSNMA: DSM-PKR verification :: FAILURE"; d_public_key_verified = false; - d_count_failed_pubKey ++; - if (d_flag_alert_message){ + d_count_failed_pubKey++; + if (d_flag_alert_message) + { d_flag_alert_message = false; // disregard message as its authenticity could not be verified. } } @@ -724,9 +751,10 @@ void osnma_msg_receiver::read_and_process_mack_block(const std::shared_ptrTOW_sf0); bool can_process_mack_block = (d_osnma_data.d_nma_header.nmas != 3 && d_kroot_verified) || // NMAS different than DU (d_osnma_data.d_nma_header.nmas == 3 && !d_kroot_verified); // NMAS is DU, but must be disregarded - bool can_verify_tesla_key = d_kroot_verified || d_tesla_key_verified; // Either of those suffices for verifying the incoming TESLA key - bool can_parse_tag_fields = d_osnma_data.d_dsm_kroot_message.ts != 0; // calculating the number of tags is based on the TS of the DSM-KROOT. - if (can_verify_tesla_key && can_parse_tag_fields && can_process_mack_block){ + bool can_verify_tesla_key = d_kroot_verified || d_tesla_key_verified; // Either of those suffices for verifying the incoming TESLA key + bool can_parse_tag_fields = d_osnma_data.d_dsm_kroot_message.ts != 0; // calculating the number of tags is based on the TS of the DSM-KROOT. + if (can_verify_tesla_key && can_parse_tag_fields && can_process_mack_block) + { read_mack_header(); d_osnma_data.d_mack_message.PRNa = osnma_msg->PRN; // FIXME this is ugly. d_osnma_data.d_mack_message.TOW = osnma_msg->TOW_sf0; @@ -1617,8 +1645,8 @@ bool osnma_msg_receiver::verify_macseq(const MACK_message& mack) return true; } // Fixed as well as FLX Tags share first part - Eq. 22 ICD - std::vector m(5 + 2 * flxTags.size()); // each flx tag brings two bytes - m[0] = static_cast(mack.PRNa); // PRN_A - SVID of the satellite transmiting the tag + std::vector m(5 + 2 * flxTags.size()); // each flx tag brings two bytes + m[0] = static_cast(mack.PRNa); // PRN_A - SVID of the satellite transmiting the tag m[1] = static_cast((GST_SFi & 0xFF000000) >> 24); m[2] = static_cast((GST_SFi & 0x00FF0000) >> 16); m[3] = static_cast((GST_SFi & 0x0000FF00) >> 8); @@ -1863,7 +1891,7 @@ std::vector osnma_msg_receiver::verify_macseq_new(const MACK_ LOG(WARNING) << "Galileo OSNMA: MACSEQ verification :: FAILURE :: ADKD mismatch against MAC Look-up table for Tag=0x" << std::setfill('0') << std::setw(10) << std::hex << std::uppercase << mack.tag_and_info[i].tag << std::dec; - d_count_failed_macseq ++; + d_count_failed_macseq++; } } @@ -1873,8 +1901,8 @@ std::vector osnma_msg_receiver::verify_macseq_new(const MACK_ return verified_tags; } // Fixed as well as FLX Tags share first part - Eq. 22 ICD - std::vector m(5 + 2 * flxTags.size()); // each flx tag brings two bytes - m[0] = static_cast(mack.PRNa); // PRN_A - SVID of the satellite transmiting the tag + std::vector m(5 + 2 * flxTags.size()); // each flx tag brings two bytes + m[0] = static_cast(mack.PRNa); // PRN_A - SVID of the satellite transmiting the tag m[1] = static_cast((GST_Sfi & 0xFF000000) >> 24); m[2] = static_cast((GST_Sfi & 0x00FF0000) >> 16); m[3] = static_cast((GST_Sfi & 0x0000FF00) >> 8); @@ -1924,7 +1952,7 @@ void osnma_msg_receiver::send_data_to_pvt(const std::vector& data { if (!data.empty()) { - for (auto & i : data) + for (auto& i : data) { const auto tmp_obj = std::make_shared(i); this->message_port_pub(pmt::mp("OSNMA_to_PVT"), pmt::make_any(tmp_obj)); @@ -1936,7 +1964,8 @@ bool osnma_msg_receiver::store_dsm_kroot(const std::vector& dsm, const { std::ofstream file(KROOTFILE_DEFAULT, std::ios::binary | std::ios::out); - if (!file.is_open()) { + if (!file.is_open()) + { return false; } @@ -1952,7 +1981,8 @@ bool osnma_msg_receiver::store_dsm_kroot(const std::vector& dsm, const std::pair, uint8_t> osnma_msg_receiver::parse_dsm_kroot() const { std::ifstream file(KROOTFILE_DEFAULT, std::ios::binary | std::ios::in); - if (!file) { + if (!file) + { return {std::vector(), 0}; } @@ -1965,7 +1995,8 @@ std::pair, uint8_t> osnma_msg_receiver::parse_dsm_kroot() c file.close(); - if (file.bad()) { + if (file.bad()) + { return {std::vector(), 0}; } diff --git a/src/core/system_parameters/gnss_crypto.cc b/src/core/system_parameters/gnss_crypto.cc index e2c88faa4..ac8723ab5 100644 --- a/src/core/system_parameters/gnss_crypto.cc +++ b/src/core/system_parameters/gnss_crypto.cc @@ -124,6 +124,7 @@ Gnss_Crypto::~Gnss_Crypto() #endif } + bool Gnss_Crypto::have_public_key() const { #if USE_GNUTLS_FALLBACK @@ -133,6 +134,7 @@ bool Gnss_Crypto::have_public_key() const #endif } + bool Gnss_Crypto::store_public_key(const std::string& pubKeyFilePath) const { if (!have_public_key()) @@ -782,63 +784,6 @@ std::vector Gnss_Crypto::compute_CMAC_AES(const std::vector& k return output; } -// TODO - deprecate: change return type to respective key type, PEM is not needed. -std::vector Gnss_Crypto::get_public_key() const -{ - if (!have_public_key()) - { - return {}; - } -#if USE_GNUTLS_FALLBACK - gnutls_datum_t pem_data = {nullptr, 0}; -#if HAVE_GNUTLS_PUBKEY_EXPORT2 - int ret = gnutls_pubkey_export2(d_PublicKey, GNUTLS_X509_FMT_PEM, &pem_data); -#else - size_t output_stata_size; - int ret = gnutls_pubkey_export(d_PublicKey, GNUTLS_X509_FMT_PEM, &pem_data, &output_stata_size); -#endif - if (ret != GNUTLS_E_SUCCESS) - { - LOG(WARNING) << "GnuTLS: Failed to export public key to PEM format."; - return {}; - } - std::vector output(pem_data.data, pem_data.data + pem_data.size); - - // Free the allocated memory by gnutls_pubkey_export2 - gnutls_free(pem_data.data); -#else // OpenSSL - // Create a BIO for the memory buffer - BIO* mem = BIO_new(BIO_s_mem()); - if (!mem) - { - LOG(WARNING) << "OpenSSL: Failed to create BIO."; - return {}; - } -#if USE_OPENSSL_3 - if (!PEM_write_bio_PUBKEY(mem, d_PublicKey)) -#else // OpenSSL 1.x - if (!PEM_write_bio_EC_PUBKEY(mem, d_PublicKey)) -#endif - { - BIO_free(mem); - LOG(WARNING) << "OpenSSL: Failed to write public key to PEM format."; - return {}; - } - - // Get the length of the data in the BIO - BUF_MEM* mem_ptr; - BIO_get_mem_ptr(mem, &mem_ptr); - - // Copy the data from the BIO to a std::vector - std::vector output(mem_ptr->length); - memcpy(output.data(), mem_ptr->data, mem_ptr->length); - - // Clean up the BIO - BIO_free(mem); -#endif - return output; -} - std::vector Gnss_Crypto::get_merkle_root() const { @@ -846,8 +791,15 @@ std::vector Gnss_Crypto::get_merkle_root() const } +std::string Gnss_Crypto::get_public_key_type() const +{ + return d_PublicKeyType; +} + + void Gnss_Crypto::set_public_key(const std::vector& publicKey) { + d_PublicKeyType = "Unknown"; #if USE_GNUTLS_FALLBACK gnutls_pubkey_t pubkey{}; gnutls_ecc_curve_t curve; @@ -859,16 +811,19 @@ void Gnss_Crypto::set_public_key(const std::vector& publicKey) if (size_pk == 33) { curve = GNUTLS_ECC_CURVE_SECP256R1; + d_PublicKeyType = "ECDSA P-256"; decompress_public_key_secp256r1(publicKey, x, y); } else if (size_pk == 67) { curve = GNUTLS_ECC_CURVE_SECP521R1; + d_PublicKeyType = "ECDSA P-521"; decompress_public_key_secp521r1(publicKey, x, y); } else { LOG(WARNING) << "GnuTLS: Invalid public key size"; + gnutls_pubkey_deinit(pubkey); return; } @@ -893,22 +848,41 @@ void Gnss_Crypto::set_public_key(const std::vector& publicKey) EVP_PKEY_CTX* ctx = nullptr; OSSL_PARAM_BLD* param_bld; OSSL_PARAM* params = nullptr; + const size_t public_key_size = publicKey.size(); param_bld = OSSL_PARAM_BLD_new(); if (param_bld != nullptr && - OSSL_PARAM_BLD_push_utf8_string(param_bld, "group", (publicKey.size() == 33) ? "prime256v1" : "secp521r1", 0) && - OSSL_PARAM_BLD_push_octet_string(param_bld, "pub", publicKey.data(), publicKey.size())) { - params = OSSL_PARAM_BLD_to_param(param_bld); + OSSL_PARAM_BLD_push_utf8_string(param_bld, "group", (public_key_size == 33) ? "prime256v1" : "secp521r1", 0) && + OSSL_PARAM_BLD_push_octet_string(param_bld, "pub", publicKey.data(), public_key_size)) + { + params = OSSL_PARAM_BLD_to_param(param_bld); + } + + if (public_key_size == 33) + { + d_PublicKeyType = "ECDSA P-256"; + } + else if (public_key_size == 67) + { + d_PublicKeyType = "ECDSA P-521"; } ctx = EVP_PKEY_CTX_new_from_name(nullptr, "EC", nullptr); if (ctx == nullptr || params == nullptr || EVP_PKEY_fromdata_init(ctx) <= 0 || EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_PUBLIC_KEY, params) <= 0) { + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(param_bld); return; } if (!pubkey_copy(pkey, &d_PublicKey)) { + EVP_PKEY_free(pkey); + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(param_bld); return; } @@ -923,10 +897,12 @@ void Gnss_Crypto::set_public_key(const std::vector& publicKey) if (publicKey.size() == 33) // ECDSA-P-256 { group = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1); + d_PublicKeyType = "ECDSA P-256"; } else // ECDSA-P-521 { group = EC_GROUP_new_by_curve_name(NID_secp521r1); + d_PublicKeyType = "ECDSA P-256"; } if (!group) { @@ -974,6 +950,15 @@ void Gnss_Crypto::set_public_key(const std::vector& publicKey) } +void Gnss_Crypto::set_public_key_type(const std::string& public_key_type) +{ + if (public_key_type == "ECDSA P-256" || public_key_type == "ECDSA P-521") + { + d_PublicKeyType = public_key_type; + } +} + + void Gnss_Crypto::set_merkle_root(const std::vector& v) { d_x_4_0 = v; @@ -1038,6 +1023,14 @@ void Gnss_Crypto::read_merkle_xml(const std::string& merkleFilePath) LOG(INFO) << "OSNMA Merkletree - Length in Bits: " << lengthInBits; LOG(INFO) << "OSNMA Merkletree - Point: " << point; LOG(INFO) << "OSNMA Merkletree - PK Type: " << pkType; + if (pkType == "ECDSA P-256/SHA-256") + { + d_PublicKeyType = "ECDSA P-256"; + } + else if (pkType == "ECDSA P-521/SHA-512") + { + d_PublicKeyType = "ECDSA P-521"; + } } for (pugi::xml_node treeNode : merkleTree.children("TreeNode")) { @@ -1122,6 +1115,7 @@ void Gnss_Crypto::readPublicKeyFromPEM(const std::string& pemFilePath) bool Gnss_Crypto::readPublicKeyFromCRT(const std::string& crtFilePath) { + d_PublicKeyType = "Unknown"; #if USE_GNUTLS_FALLBACK // Open the .crt file std::ifstream crtFile(crtFilePath, std::ios::binary); @@ -1160,6 +1154,72 @@ bool Gnss_Crypto::readPublicKeyFromCRT(const std::string& crtFilePath) gnutls_x509_crt_deinit(cert); return false; } + + // store the key type - needed for the Kroot in case no DSM-PKR available + gnutls_pk_algorithm_t pk_algorithm; + unsigned int bits; + + ret = gnutls_pubkey_get_pk_algorithm(pubkey, &bits); + if (ret < 0) + { + LOG(WARNING) << "GnuTLS: Failed to get public key algorithm: " << gnutls_strerror(ret); + gnutls_pubkey_deinit(pubkey); + gnutls_x509_crt_deinit(cert); + return false; + } + + pk_algorithm = static_cast(ret); + + if (pk_algorithm == GNUTLS_PK_ECDSA) + { + gnutls_datum_t params; + ret = gnutls_pubkey_export_ecc_raw(pubkey, nullptr, ¶ms, nullptr); + if (ret < 0) + { + LOG(WARNING) << "GnuTLS: Failed to export EC parameters: " << gnutls_strerror(ret); + gnutls_pubkey_deinit(pubkey); + gnutls_x509_crt_deinit(cert); + return false; + } + + gnutls_ecc_curve_t curve; + ret = gnutls_ecc_curve_get_id(reinterpret_cast(params.data)); + gnutls_free(params.data); + + if (ret < 0) + { + LOG(WARNING) << "GnuTLS: Failed to get EC curve: " << gnutls_strerror(ret); + gnutls_pubkey_deinit(pubkey); + gnutls_x509_crt_deinit(cert); + return false; + } + + curve = static_cast(ret); + + if (curve == GNUTLS_ECC_CURVE_SECP256R1) + { + d_PublicKeyType = "ECDSA P-256"; + } + else if (curve == GNUTLS_ECC_CURVE_SECP521R1) + { + d_PublicKeyType = "ECDSA P-521"; + } + else + { + LOG(WARNING) << "GnuTLS: Trying to read unknown EC curve"; + gnutls_x509_crt_deinit(cert); + gnutls_pubkey_deinit(pubkey); + return false; + } + } + else + { + LOG(WARNING) << "GnuTLS: Trying to read unknown key type"; + gnutls_x509_crt_deinit(cert); + gnutls_pubkey_deinit(pubkey); + return false; + } + pubkey_copy(pubkey, &d_PublicKey); gnutls_x509_crt_deinit(cert); gnutls_pubkey_deinit(pubkey); @@ -1190,27 +1250,69 @@ bool Gnss_Crypto::readPublicKeyFromCRT(const std::string& crtFilePath) // Read the public key from the certificate EVP_PKEY* pubkey = X509_get_pubkey(cert); - - // store the key type - needed for the Kroot in case no DSM-PKR available - // TODO - only way I have found to find the curve type - const auto *const ec_key = EVP_PKEY_get0_EC_KEY(pubkey); - const EC_GROUP *group = EC_KEY_get0_group(ec_key); - int nid = EC_GROUP_get_curve_name(group); - if (nid == NID_X9_62_prime256v1) { - d_PublicKeyType = "ECDSA P-256"; - } else if (nid == NID_secp521r1) { - d_PublicKeyType = "ECDSA P-521"; - } -#if USE_OPENSSL_3 if (!pubkey) { LOG(WARNING) << "OpenSSL: Failed to extract the public key"; X509_free(cert); return false; } +#if USE_OPENSSL_3 + // store the key type - needed for the Kroot in case no DSM-PKR available + // Get the key type + int key_type = EVP_PKEY_base_id(pubkey); + if (key_type == EVP_PKEY_EC) + { + // It's an EC key, now we need to determine the curve + char curve_name[256]; + size_t curve_name_len = sizeof(curve_name); + + if (EVP_PKEY_get_utf8_string_param(pubkey, OSSL_PKEY_PARAM_GROUP_NAME, curve_name, curve_name_len, &curve_name_len) == 1) + { + if (strcmp(curve_name, "prime256v1") == 0 || strcmp(curve_name, "P-256") == 0) + { + d_PublicKeyType = "ECDSA P-256"; + } + else if (strcmp(curve_name, "secp521r1") == 0 || strcmp(curve_name, "P-521") == 0) + { + d_PublicKeyType = "ECDSA P-521"; + } + else + { + LOG(WARNING) << "OpenSSL: Trying to read an unknown EC curve"; + X509_free(cert); + return false; + } + } + else + { + d_PublicKeyType = "Unknown EC curve"; + LOG(WARNING) << "OpenSSL: Trying to read an unknown EC curve"; + X509_free(cert); + return false; + } + } + else + { + LOG(WARNING) << "OpenSSL: Trying to read an unknown key type"; + X509_free(cert); + return false; + } pubkey_copy(pubkey, &d_PublicKey); EVP_PKEY_free(pubkey); #else // OpenSSL 1.x + // store the key type - needed for the Kroot in case no DSM-PKR available + const auto ec_key = EVP_PKEY_get0_EC_KEY(pubkey); + const EC_GROUP* group = EC_KEY_get0_group(ec_key); + const int nid = EC_GROUP_get_curve_name(group); + if (nid == NID_X9_62_prime256v1) + { + d_PublicKeyType = "ECDSA P-256"; + } + else if (nid == NID_secp521r1) + { + d_PublicKeyType = "ECDSA P-521"; + } + EC_KEY* ec_pubkey = EVP_PKEY_get1_EC_KEY(pubkey); EVP_PKEY_free(pubkey); if (!ec_pubkey) @@ -1299,6 +1401,7 @@ std::vector Gnss_Crypto::convert_from_hex_str(const std::string& input) return result; } + #if USE_GNUTLS_FALLBACK // GnuTLS-specific functions bool Gnss_Crypto::pubkey_copy(gnutls_pubkey_t src, gnutls_pubkey_t* dest) { diff --git a/src/core/system_parameters/gnss_crypto.h b/src/core/system_parameters/gnss_crypto.h index 6aa347bb0..e197d61d0 100644 --- a/src/core/system_parameters/gnss_crypto.h +++ b/src/core/system_parameters/gnss_crypto.h @@ -66,13 +66,13 @@ public: std::vector compute_HMAC_SHA_256(const std::vector& key, const std::vector& input) const; //!< Computes HMAC-SHA-256 message authentication code std::vector compute_CMAC_AES(const std::vector& key, const std::vector& input) const; //!< Computes CMAC-AES message authentication code - std::vector get_public_key() const; //!< Gets the ECDSA Public Key in PEM format std::vector get_merkle_root() const; //!< Gets the Merkle Tree root node (\f$ x_{4,0} \f$) + std::string get_public_key_type() const; //!< Gets the ECDSA Public Key type (ECDSA P-256 / ECDSA P-521 / Unknown) - void set_public_key(const std::vector& publickey); //!< Sets the ECDSA Public Key (publickey compressed format) - void set_merkle_root(const std::vector& v); //!< Sets the Merkle Tree root node x(\f$ x_{4,0} \f$) - void read_merkle_xml(const std::string& merkleFilePath); - std::string d_PublicKeyType; + void set_public_key(const std::vector& publickey); //!< Sets the ECDSA Public Key (publickey compressed format) + void set_public_key_type(const std::string& public_key_type); //!< Sets the ECDSA Public Key type (ECDSA P-256 / ECDSA P-521) + void set_merkle_root(const std::vector& v); //!< Sets the Merkle Tree root node x(\f$ x_{4,0} \f$) + void read_merkle_xml(const std::string& merkleFilePath); //!> Reads the XML file provided from the GSC OSNMA server private: void readPublicKeyFromPEM(const std::string& pemFilePath); @@ -94,6 +94,7 @@ private: #endif #endif std::vector d_x_4_0; + std::string d_PublicKeyType; }; /** \} */ diff --git a/src/core/system_parameters/osnma_nav_data_manager.cc b/src/core/system_parameters/osnma_nav_data_manager.cc index 8fdd9ee6e..9d5017aec 100644 --- a/src/core/system_parameters/osnma_nav_data_manager.cc +++ b/src/core/system_parameters/osnma_nav_data_manager.cc @@ -38,6 +38,8 @@ void OSNMA_nav_data_Manager::add_navigation_data(const std::string& nav_bits, ui _satellite_nav_data[PRNd][TOW].set_tow_sf0(TOW); } } + + /** * @brief loops over the verified tags and updates the navigation data tag length */ @@ -55,7 +57,8 @@ void OSNMA_nav_data_Manager::update_nav_data(const std::multimap& for (auto & tow_it : tow_map) // note: starts with smallest (i.e. oldest) navigation dataset { std::string nav_data; - if (tag.second.ADKD == 0 || tag.second.ADKD == 12){ + if (tag.second.ADKD == 0 || tag.second.ADKD == 12) + { nav_data = tow_it.second.get_ephemeris_data(); } else if (tag.second.ADKD == 4){ @@ -70,11 +73,15 @@ void OSNMA_nav_data_Manager::update_nav_data(const std::multimap& } } } + + bool OSNMA_nav_data_Manager::have_PRNd_nav_data(uint32_t PRNd) { // check if have data from PRNd in _satellite_nav_data return _satellite_nav_data.find(PRNd) != _satellite_nav_data.end(); } + + std::vector OSNMA_nav_data_Manager::get_verified_data() { std::vector result; @@ -91,6 +98,8 @@ std::vector OSNMA_nav_data_Manager::get_verified_data() } return result; } + + bool OSNMA_nav_data_Manager::have_nav_data(uint32_t PRNd, uint32_t TOW, uint8_t ADKD) { if (ADKD == 0 || ADKD == 12) @@ -119,6 +128,8 @@ bool OSNMA_nav_data_Manager::have_nav_data(uint32_t PRNd, uint32_t TOW, uint8_t } return false; } + + /** * @brief returns OSNMA_NavData object. * @remarks assumes it exists (called have_nav_data before), otherwise undefined behavior @@ -140,13 +151,16 @@ std::string OSNMA_nav_data_Manager::get_navigation_data(const Tag& tag) { if (tag.ADKD == 0 || tag.ADKD == 12) { - if (!tow_it.second.get_ephemeris_data().empty()){ + if (!tow_it.second.get_ephemeris_data().empty()) + { return tow_it.second.get_ephemeris_data(); } } else if(tag.ADKD == 4) { - if (!tow_it.second.get_utc_data().empty()){ + + if (!tow_it.second.get_utc_data().empty()) + { return tow_it.second.get_utc_data(); } } @@ -154,6 +168,8 @@ std::string OSNMA_nav_data_Manager::get_navigation_data(const Tag& tag) } return ""; } + + /** * @brief Checks if the OSNMA_NavData bits are already present. In case affirmative, it updates the OSNMA_NavData 'last received' timestamp * @remarks e.g.: a SV may repeat the bits over several subframes. In that case, need to save them only once. @@ -182,6 +198,8 @@ bool OSNMA_nav_data_Manager::have_nav_data(const std::string& nav_bits, uint32_t } return false; } + + /** * @brief Checks if there is a OSNMA_NavData element within the COP time interval for a Tag t * @param t Tag object @@ -202,13 +220,15 @@ bool OSNMA_nav_data_Manager::have_nav_data(const Tag& t) const { if (t.ADKD == 0 || t.ADKD == 12) { - if (!tow_it.second.get_ephemeris_data().empty()){ + if (!tow_it.second.get_ephemeris_data().empty()) + { return true; } } else if (t.ADKD == 4) { - if (!tow_it.second.get_utc_data().empty()){ + if (!tow_it.second.get_utc_data().empty()) + { return true; } } @@ -216,6 +236,8 @@ bool OSNMA_nav_data_Manager::have_nav_data(const Tag& t) const } return false; } + + void OSNMA_nav_data_Manager::print_status() { for (const auto& satellite : _satellite_nav_data){ diff --git a/src/tests/unit-tests/signal-processing-blocks/osnma/gnss_crypto_test.cc b/src/tests/unit-tests/signal-processing-blocks/osnma/gnss_crypto_test.cc index 6d645edb1..6b4813975 100644 --- a/src/tests/unit-tests/signal-processing-blocks/osnma/gnss_crypto_test.cc +++ b/src/tests/unit-tests/signal-processing-blocks/osnma/gnss_crypto_test.cc @@ -34,8 +34,10 @@ TEST(GnssCryptoTest, VerifyPubKeyImport) // Input taken from RG 1.3 A7.1 // compressed ECDSA P-256 format std::vector publicKey = { - 0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B, 0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, - 0x32, 0x0D, 0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7, 0x79, 0x80, 0xEA}; + 0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B, + 0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D, + 0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7, + 0x79, 0x80, 0xEA}; ASSERT_FALSE(d_crypto->have_public_key()); @@ -55,8 +57,10 @@ TEST(GnssCryptoTest, VerifyPublicKeyStorage) // Input taken from RG 1.3 A7.1 // compressed ECDSA P-256 format std::vector publicKey = { - 0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B, 0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, - 0x32, 0x0D, 0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7, 0x79, 0x80, 0xEA}; + 0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B, + 0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D, + 0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7, + 0x79, 0x80, 0xEA}; d_crypto->set_public_key(publicKey); bool result = d_crypto->store_public_key(f1); @@ -75,10 +79,6 @@ TEST(GnssCryptoTest, VerifyPublicKeyStorage) ASSERT_EQ(content_file, content_file2); - // TODO - this cannot be tested right now - // std::vector readkey = d_crypto2->get_public_key(); - // ASSERT_EQ(publicKey, readkey); - errorlib::error_code ec; ASSERT_TRUE(fs::remove(fs::path(f1), ec)); ASSERT_TRUE(fs::remove(fs::path(f2), ec)); @@ -93,8 +93,9 @@ TEST(GnssCryptoTest, TestComputeSHA_256) std::vector expected_output = { 0x18, 0x94, 0xA1, 0x9C, 0x85, 0xBA, 0x15, 0x3A, 0xCB, 0xF7, - 0x43, 0xAC, 0x4E, 0x43, 0xFC, 0x00, 0x4C, 0x89, 0x16, 0x04, 0xB2, - 0x6F, 0x8C, 0x69, 0xE1, 0xE8, 0x3E, 0xA2, 0xAF, 0xC7, 0xC4, 0x8F}; + 0x43, 0xAC, 0x4E, 0x43, 0xFC, 0x00, 0x4C, 0x89, 0x16, 0x04, + 0xB2, 0x6F, 0x8C, 0x69, 0xE1, 0xE8, 0x3E, 0xA2, 0xAF, 0xC7, + 0xC4, 0x8F}; std::vector output = d_crypto->compute_SHA_256(message); @@ -111,7 +112,8 @@ TEST(GnssCryptoTest, TestComputeSHA3_256) std::vector expected_output = { 0xCC, 0xB8, 0xF9, 0x23, 0x5F, 0x4A, 0x93, 0x2C, 0xA0, 0xAB, 0xBB, 0x2C, 0x24, 0x36, 0x72, 0x5E, 0x2E, 0x8D, 0xC7, 0x5B, - 0x99, 0xE7, 0xF6, 0xC4, 0x50, 0x5B, 0x2A, 0x93, 0x6E, 0xB6, 0x3B, 0x3F}; + 0x99, 0xE7, 0xF6, 0xC4, 0x50, 0x5B, 0x2A, 0x93, 0x6E, 0xB6, + 0x3B, 0x3F}; std::vector output = d_crypto->compute_SHA3_256(message); @@ -254,8 +256,10 @@ TEST(GnssCryptoTest, VerifySignatureP256) // Input taken from RG 1.3 A7.1 // compressed ECDSA P-256 format std::vector publicKey = { - 0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B, 0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, - 0x32, 0x0D, 0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7, 0x79, 0x80, 0xEA}; + 0x03, 0x03, 0xB2, 0xCE, 0x64, 0xBC, 0x20, 0x7B, 0xDD, 0x8B, + 0xC4, 0xDF, 0x85, 0x91, 0x87, 0xFC, 0xB6, 0x86, 0x32, 0x0D, + 0x63, 0xFF, 0xA0, 0x91, 0x41, 0x0F, 0xC1, 0x58, 0xFB, 0xB7, + 0x79, 0x80, 0xEA}; d_crypto->set_public_key(publicKey); bool result = d_crypto->verify_signature_ecdsa_p256(message, signature);