From 2e867f2dac0c2a398cbe331dc88685ec1e29e7d5 Mon Sep 17 00:00:00 2001 From: cesaaargm Date: Wed, 14 Aug 2024 13:14:23 +0200 Subject: [PATCH] [TAS-240][FEAT] Implement TESLA Chain Renewal and Revocation I * Implemented a new chain renewal mechanism for OSNMA data structure updates and key management. * Added related flags and data fields. * tested successfully with test vectors. --- src/core/libs/osnma_msg_receiver.cc | 94 ++++++++++++------- src/core/libs/osnma_msg_receiver.h | 3 + src/core/system_parameters/osnma_data.h | 2 + .../osnma/osnma_test_vectors.cc | 38 ++++++++ 4 files changed, 105 insertions(+), 32 deletions(-) diff --git a/src/core/libs/osnma_msg_receiver.cc b/src/core/libs/osnma_msg_receiver.cc index f5f8d4c0a..b4fb6a29d 100644 --- a/src/core/libs/osnma_msg_receiver.cc +++ b/src/core/libs/osnma_msg_receiver.cc @@ -258,7 +258,7 @@ void osnma_msg_receiver::process_osnma_message(const std::shared_ptr& 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. + // 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) { @@ -309,6 +309,27 @@ void osnma_msg_receiver::process_osnma_message(const std::shared_ptr& std::cout << "Galileo OSNMA: Alert message :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << std::endl; } + if (d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 2 /* EOC */ && d_GST_chain_renewal_start == 0) + { + d_flag_chain_renewal = true; + d_GST_chain_renewal_start = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0); + LOG(INFO) << "Galileo OSNMA: Chain renewal :: Start at at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]"; + std::cout << "Galileo OSNMA: Chain renewal :: Start at at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << std::endl; + } + if (d_flag_chain_renewal && d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 1 /* Nominal */) + { + // Step 2, start using the new kroot + d_flag_chain_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_chain_renewal_start) / 3600.0; + LOG(INFO) << "Galileo OSNMA: Chain renewal :: Finished at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" + << ", Duration=" << duration_hours << "h"; + std::cout << "Galileo OSNMA: Chain renewal :: Finished at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" + << ", Duration=" << duration_hours << "h" << std::endl; + d_osnma_data.d_dsm_kroot_message = d_osnma_data.d_dsm_kroot_new_message; // set new kroot as the one to use from now on + d_tesla_key_verified = false; // force the verification up to the Kroot due to chain change + } + 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 @@ -478,24 +499,27 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg // DSM-KROOT message if ((d_osnma_data.d_dsm_header.dsm_id < 12 || d_flag_hot_start) && d_public_key_verified) { + bool new_chain = (d_dsm_reader->get_cidkr(dsm_msg) != d_osnma_data.d_nma_header.cid) && d_flag_chain_renewal; + DSM_KROOT_message& applicable_kroot_msg = new_chain ? d_osnma_data.d_dsm_kroot_new_message : d_osnma_data.d_dsm_kroot_message; + // Parse Kroot message LOG(INFO) << "Galileo OSNMA: DSM-KROOT message received."; - d_osnma_data.d_dsm_kroot_message.nb_dk = d_dsm_reader->get_number_blocks_index(dsm_msg[0]); - d_osnma_data.d_dsm_kroot_message.pkid = d_dsm_reader->get_pkid(dsm_msg); - d_osnma_data.d_dsm_kroot_message.cidkr = d_dsm_reader->get_cidkr(dsm_msg); - d_osnma_data.d_dsm_kroot_message.reserved1 = d_dsm_reader->get_dsm_reserved1(dsm_msg); - d_osnma_data.d_dsm_kroot_message.hf = d_dsm_reader->get_hf(dsm_msg); - d_osnma_data.d_dsm_kroot_message.mf = d_dsm_reader->get_mf(dsm_msg); - d_osnma_data.d_dsm_kroot_message.ks = d_dsm_reader->get_ks(dsm_msg); - d_osnma_data.d_dsm_kroot_message.ts = d_dsm_reader->get_ts(dsm_msg); - d_osnma_data.d_dsm_kroot_message.maclt = d_dsm_reader->get_maclt(dsm_msg); - d_osnma_data.d_dsm_kroot_message.reserved = d_dsm_reader->get_dsm_reserved(dsm_msg); - d_osnma_data.d_dsm_kroot_message.wn_k = d_dsm_reader->get_wn_k(dsm_msg); - d_osnma_data.d_dsm_kroot_message.towh_k = d_dsm_reader->get_towh_k(dsm_msg); - d_osnma_data.d_dsm_kroot_message.alpha = d_dsm_reader->get_alpha(dsm_msg); + applicable_kroot_msg.nb_dk = d_dsm_reader->get_number_blocks_index(dsm_msg[0]); + applicable_kroot_msg.pkid = d_dsm_reader->get_pkid(dsm_msg); + applicable_kroot_msg.cidkr = d_dsm_reader->get_cidkr(dsm_msg); + applicable_kroot_msg.reserved1 = d_dsm_reader->get_dsm_reserved1(dsm_msg); + applicable_kroot_msg.hf = d_dsm_reader->get_hf(dsm_msg); + applicable_kroot_msg.mf = d_dsm_reader->get_mf(dsm_msg); + applicable_kroot_msg.ks = d_dsm_reader->get_ks(dsm_msg); + applicable_kroot_msg.ts = d_dsm_reader->get_ts(dsm_msg); + applicable_kroot_msg.maclt = d_dsm_reader->get_maclt(dsm_msg); + applicable_kroot_msg.reserved = d_dsm_reader->get_dsm_reserved(dsm_msg); + applicable_kroot_msg.wn_k = d_dsm_reader->get_wn_k(dsm_msg); + applicable_kroot_msg.towh_k = d_dsm_reader->get_towh_k(dsm_msg); + applicable_kroot_msg.alpha = d_dsm_reader->get_alpha(dsm_msg); // Kroot field - const uint16_t l_lk_bytes = d_dsm_reader->get_lk_bits(d_osnma_data.d_dsm_kroot_message.ks) / 8; - d_osnma_data.d_dsm_kroot_message.kroot = d_dsm_reader->get_kroot(dsm_msg, l_lk_bytes); + const uint16_t l_lk_bytes = d_dsm_reader->get_lk_bits(applicable_kroot_msg.ks) / 8; + applicable_kroot_msg.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->get_public_key_type()); @@ -504,19 +528,19 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg l_ds_bits = it->second; } const uint16_t l_ds_bytes = l_ds_bits / 8; - d_osnma_data.d_dsm_kroot_message.ds = std::vector(l_ds_bytes, 0); // C: this accounts for padding in case needed. + applicable_kroot_msg.ds = std::vector(l_ds_bytes, 0); // C: this accounts for padding in case needed. for (uint16_t k = 0; k < l_ds_bytes; k++) { - d_osnma_data.d_dsm_kroot_message.ds[k] = dsm_msg[13 + l_lk_bytes + k]; + applicable_kroot_msg.ds[k] = dsm_msg[13 + l_lk_bytes + k]; } // Padding - const uint16_t l_dk_bits = d_dsm_reader->get_l_dk_bits(d_osnma_data.d_dsm_kroot_message.nb_dk); + const uint16_t l_dk_bits = d_dsm_reader->get_l_dk_bits(applicable_kroot_msg.nb_dk); const uint16_t l_dk_bytes = l_dk_bits / 8; const uint16_t l_pdk_bytes = (l_dk_bytes - 13 - l_lk_bytes - l_ds_bytes); - d_osnma_data.d_dsm_kroot_message.p_dk = std::vector(l_pdk_bytes, 0); + applicable_kroot_msg.p_dk = std::vector(l_pdk_bytes, 0); for (uint16_t k = 0; k < l_pdk_bytes; k++) { - d_osnma_data.d_dsm_kroot_message.p_dk[k] = dsm_msg[13 + l_lk_bytes + l_ds_bytes + k]; + applicable_kroot_msg.p_dk[k] = dsm_msg[13 + l_lk_bytes + l_ds_bytes + k]; } const uint16_t check_l_dk = 104 * std::ceil(1.0 + static_cast((l_lk_bytes * 8.0) + l_ds_bits) / 104.0); @@ -539,15 +563,15 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg 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]); + MSG.push_back(applicable_kroot_msg.ds[k]); } std::vector hash; - if (d_osnma_data.d_dsm_kroot_message.hf == 0) // Table 8. + if (applicable_kroot_msg.hf == 0) // Table 8. { hash = d_crypto->compute_SHA_256(MSG); } - else if (d_osnma_data.d_dsm_kroot_message.hf == 2) + else if (applicable_kroot_msg.hf == 2) { hash = d_crypto->compute_SHA3_256(MSG); } @@ -563,17 +587,16 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg p_dk_truncated.push_back(hash[i]); } // Check that the padding bits received match the computed values - if (d_osnma_data.d_dsm_kroot_message.p_dk == p_dk_truncated) + if (applicable_kroot_msg.p_dk == p_dk_truncated) { LOG(INFO) << "Galileo OSNMA: DSM-KROOT message received ok."; LOG(INFO) << "Galileo OSNMA: DSM-KROOT with CID=" << static_cast(d_osnma_data.d_nma_header.cid) - << ", PKID=" << static_cast(d_osnma_data.d_dsm_kroot_message.pkid) - << ", WN=" << static_cast(d_osnma_data.d_dsm_kroot_message.wn_k) - << ", TOW=" << static_cast(d_osnma_data.d_dsm_kroot_message.towh_k) * 3600; - // local_time_verification(osnma_msg); // FIXME TODO: real time verification needed + << ", PKID=" << static_cast(applicable_kroot_msg.pkid) + << ", WN=" << static_cast(applicable_kroot_msg.wn_k) + << ", TOW=" << static_cast(applicable_kroot_msg.towh_k) * 3600; // 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 && applicable_kroot_msg.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); @@ -581,14 +604,15 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg } if (l_ds_bits == 512) { - d_kroot_verified = d_crypto->verify_signature_ecdsa_p256(message, d_osnma_data.d_dsm_kroot_message.ds); + d_kroot_verified = d_crypto->verify_signature_ecdsa_p256(message, applicable_kroot_msg.ds); } else if (l_ds_bits == 1056) { - d_kroot_verified = d_crypto->verify_signature_ecdsa_p521(message, d_osnma_data.d_dsm_kroot_message.ds); + d_kroot_verified = d_crypto->verify_signature_ecdsa_p521(message, applicable_kroot_msg.ds); } if (d_kroot_verified) { + applicable_kroot_msg.verified = true; std::cout << "Galileo OSNMA: DSM-KROOT authentication successful!" << std::endl; LOG(INFO) << "Galileo OSNMA: DSM-KROOT authentication successful!"; if (d_flag_alert_message) @@ -1271,6 +1295,12 @@ std::vector osnma_msg_receiver::compute_merkle_root(const DSM_PKR_messa } +/** + * @brief Get the Merkle tree base leave from a DSM_PKR_message. + * + * @param dsm_pkr_message The DSM_PKR_message object from which to retrieve the Merkle tree leave. + * @return std::vector The Merkle tree base leave from the DSM_PKR_message object. + */ std::vector osnma_msg_receiver::get_merkle_tree_leaves(const DSM_PKR_message& dsm_pkr_message) const { // build base leaf m_i according to OSNMA SIS ICD v1.1, section 6.2 DSM-PKR Verification diff --git a/src/core/libs/osnma_msg_receiver.h b/src/core/libs/osnma_msg_receiver.h index 8327709da..56d104cf4 100644 --- a/src/core/libs/osnma_msg_receiver.h +++ b/src/core/libs/osnma_msg_receiver.h @@ -129,6 +129,7 @@ private: uint32_t d_GST_SIS{}; // GST coming from W6 and W5 of SIS uint32_t d_GST_PKR_PKREV_start{}; uint32_t d_GST_PKR_AM_start{}; + uint32_t d_GST_chain_renewal_start{}; uint32_t d_count_successful_tags{0}; uint32_t d_count_failed_tags{0}; @@ -149,6 +150,7 @@ private: bool d_flag_PK_revocation{false}; bool d_flag_NPK_set{false}; bool d_flag_alert_message{false}; + bool d_flag_chain_renewal{false}; // Provide access to inner functions to Gtest FRIEND_TEST(OsnmaMsgReceiverTest, TeslaKeyVerification); @@ -161,6 +163,7 @@ private: FRIEND_TEST(OsnmaTestVectors, NominalTestConf2); FRIEND_TEST(OsnmaTestVectors, PublicKeyRenewal); FRIEND_TEST(OsnmaTestVectors, PublicKeyRevocation); + FRIEND_TEST(OsnmaTestVectors, ChainRenewal); FRIEND_TEST(OsnmaTestVectors, AlertMessage); }; diff --git a/src/core/system_parameters/osnma_data.h b/src/core/system_parameters/osnma_data.h index 0816abda6..7e6d03b42 100644 --- a/src/core/system_parameters/osnma_data.h +++ b/src/core/system_parameters/osnma_data.h @@ -116,6 +116,7 @@ public: uint8_t maclt{}; uint8_t reserved{}; uint8_t towh_k{}; + bool verified{false}; }; @@ -182,6 +183,7 @@ public: DSM_dsm_header d_dsm_header; DSM_PKR_message d_dsm_pkr_message; DSM_KROOT_message d_dsm_kroot_message; + DSM_KROOT_message d_dsm_kroot_new_message; MACK_message d_mack_message; OSNMA_NavData d_nav_data; }; diff --git a/src/tests/unit-tests/signal-processing-blocks/osnma/osnma_test_vectors.cc b/src/tests/unit-tests/signal-processing-blocks/osnma/osnma_test_vectors.cc index 755725d98..494103499 100644 --- a/src/tests/unit-tests/signal-processing-blocks/osnma/osnma_test_vectors.cc +++ b/src/tests/unit-tests/signal-processing-blocks/osnma/osnma_test_vectors.cc @@ -152,6 +152,7 @@ TEST_F(OsnmaTestVectors, PublicKeyRenewal) } std::vector> testVectors = {testVectors_step1, testVectors_step2, testVectors_step3}; + // Act d_flag_NPK = true; bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times); ASSERT_TRUE(result); @@ -190,6 +191,43 @@ TEST_F(OsnmaTestVectors, PublicKeyRevocation) } std::vector> testVectors = {testVectors_step1, testVectors_step2, testVectors_step3}; + // Act + bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times); + ASSERT_TRUE(result); + + // Assert + LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags; + LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags; + LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size(); + LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot; + LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey; + LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq; + ASSERT_EQ(osnma->d_count_failed_tags, 0); + ASSERT_EQ(osnma->d_count_failed_Kroot, 0); + ASSERT_EQ(osnma->d_count_failed_pubKey, 0); + ASSERT_EQ(osnma->d_count_failed_macseq, 0); +} + +TEST_F(OsnmaTestVectors, ChainRenewal) +{ + // Arrange + std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20231007041500_PKID_7.crt"; + std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007041500_PKID_7.xml"; + osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath); + + std::tm input_time_step1 = {0, 45, 16, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0}; + std::tm input_time_step2 = {0, 30, 18, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0}; + std::vector input_times = {input_time_step1, input_time_step2}; + + std::vector testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/eoc_step1/06_OCT_2023_GST_16_45_01.csv"); + std::vector testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/eoc_step2/06_OCT_2023_GST_18_30_01.csv"); + if (testVectors_step1.empty() || testVectors_step2.empty()) + { + ASSERT_TRUE(false); + } + std::vector> testVectors = {testVectors_step1, testVectors_step2}; + + // Act bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times); ASSERT_TRUE(result);