From f534ef859c4cd3ffe0dd01a9077f9cd8c19c87fc Mon Sep 17 00:00:00 2001 From: cesaaargm Date: Tue, 9 Jul 2024 17:13:40 +0200 Subject: [PATCH] [TAS-212] [TEST] implement tests for DSM-PKR Verification * VerifyPublicKey, ComputeBaseLeaf, ComputeMerkleRoot * Refactored verify_dsm_pkr to allow for the new tests * add convert_from_hex function in the helper --- src/core/libs/osnma_msg_receiver.cc | 62 +++++++++------ src/core/libs/osnma_msg_receiver.h | 36 +++++---- src/core/system_parameters/gnss_crypto.h | 3 + src/core/system_parameters/osnma_helper.cc | 24 +++++- src/core/system_parameters/osnma_helper.h | 7 +- .../osnma/osnma_msg_receiver_test.cc | 78 ++++++++++++++++++- 6 files changed, 164 insertions(+), 46 deletions(-) diff --git a/src/core/libs/osnma_msg_receiver.cc b/src/core/libs/osnma_msg_receiver.cc index 7b5fbe51b..1d1414c45 100644 --- a/src/core/libs/osnma_msg_receiver.cc +++ b/src/core/libs/osnma_msg_receiver.cc @@ -1027,56 +1027,70 @@ void osnma_msg_receiver::process_mack_message() } +/** + * @brief Verify received DSM-PKR message + * + * \details This method provides the functionality to verify the DSM-PKR message. The verification includes generating the base leaf + * and intermediate leafs, and comparing the computed merkle root leaf with the received one. + * \pre DSM_PKR_message correctly filled in especially the 1024 intermediate tree nodes + * \returns true if computed merkle root matches received one, false otherwise + */ bool osnma_msg_receiver::verify_dsm_pkr(DSM_PKR_message message) { - // TODO create function for recursively apply hash + std::vector computed_merkle_root; // x_4_0 + std::vector base_leaf = compute_base_leaf(message); // m_i - // build base leaf m_i - std::vector m_i; - m_i.reserve(2 + message.npk.size()); - m_i.push_back((message.npkt << 4) + message.npktid); - for (uint8_t i = 0; i < message.npk.size(); i++) - { - m_i.push_back(message.npk[i]); - } - - // compute intermediate leafs' values - // std::vector x_0,x_1,x_2,x_3,x_4; LOG(INFO) << "Galileo OSNMA: DSM-PKR :: leaf provided: m_" << static_cast(message.mid); + computed_merkle_root = compute_merke_root(message, base_leaf); + + if (computed_merkle_root == d_crypto->getMerkleRoot()) + { + LOG(INFO) << "Galileo OSNMA: DSM-PKR verification :: SUCCESS!." << std::endl; + return true; + } + else + { + LOG(INFO) << "Galileo OSNMA: DSM-PKR verification :: FAILURE." << std::endl; + return false; + } +} +std::vector osnma_msg_receiver::compute_merke_root(const DSM_PKR_message& dsm_pkr_message, const std::vector& m_i) const +{ std::vector x_next, x_current = d_crypto->computeSHA256(m_i); for (size_t i = 0; i < 4; i++) { x_next.clear(); - bool leaf_is_on_right = ((message.mid / (1 << (i))) % 2) == 1; + bool leaf_is_on_right = ((dsm_pkr_message.mid / (1 << (i))) % 2) == 1; if (leaf_is_on_right) { // Leaf is on the right -> first the itn, then concatenate the leaf - x_next.insert(x_next.end(), &message.itn[32 * i], &message.itn[32 * i + 32]); + x_next.insert(x_next.end(), &dsm_pkr_message.itn[32 * i], &dsm_pkr_message.itn[32 * i + 32]); x_next.insert(x_next.end(), x_current.begin(), x_current.end()); } else { // Leaf is on the left -> first the leaf, then concatenate the itn x_next.insert(x_next.end(), x_current.begin(), x_current.end()); - x_next.insert(x_next.end(), &message.itn[32 * i], &message.itn[32 * i + 32]); + x_next.insert(x_next.end(), &dsm_pkr_message.itn[32 * i], &dsm_pkr_message.itn[32 * i + 32]); } // Compute the next node. x_current = d_crypto->computeSHA256(x_next); } - - if (x_current == d_crypto->getMerkleRoot()) + return x_current; +} +std::vector osnma_msg_receiver::compute_base_leaf(const DSM_PKR_message& dsm_pkr_message) const +{ // build base leaf m_i + std::vector m_i; + m_i.reserve(2 + dsm_pkr_message.npk.size()); + m_i.push_back((dsm_pkr_message.npkt << 4) + dsm_pkr_message.npktid); + for (uint8_t i = 0; i < dsm_pkr_message.npk.size(); i++) { - LOG(INFO) << "Galileo OSNMA: DSM-PKR verified successfully! " << std::endl; - return true; - } - else - { - LOG(INFO) << "Galileo OSNMA: DSM-PKR verification unsuccessful !" << std::endl; - return false; + m_i.push_back(dsm_pkr_message.npk[i]); } + return m_i; } diff --git a/src/core/libs/osnma_msg_receiver.h b/src/core/libs/osnma_msg_receiver.h index aef9fd91e..8bb8eb452 100644 --- a/src/core/libs/osnma_msg_receiver.h +++ b/src/core/libs/osnma_msg_receiver.h @@ -69,26 +69,35 @@ private: void local_time_verification(const std::shared_ptr& osnma_msg); void process_dsm_block(const std::shared_ptr& osnma_msg); void process_dsm_message(const std::vector& dsm_msg, const std::shared_ptr& osnma_msg); - bool verify_dsm_pkr(DSM_PKR_message message); void read_and_process_mack_block(const std::shared_ptr& osnma_msg); void read_mack_header(); void read_mack_body(); void process_mack_message(); void add_satellite_data(uint32_t SV_ID, uint32_t TOW, const NavData &data); - bool verify_tesla_key(std::vector& key, uint32_t TOW); + void remove_verified_tags(); + void control_tags_awaiting_verify_size(); + std::vector build_message(const Tag& tag); std::vector hash_chain(uint32_t num_of_hashes_needed, std::vector key, uint32_t GST_SFi, const uint8_t lk_bytes); - void display_data();bool verify_tag(MACK_tag_and_info tag_and_info, OSNMA_data applicable_OSNMA, uint8_t tag_position, const std::vector& applicable_key, NavData applicable_NavData); + std::vector compute_base_leaf(const DSM_PKR_message& dsm_pkr_message) const; + std::vector compute_merke_root(const DSM_PKR_message& dsm_pkr_message, const std::vector& m_i) const; + void display_data(); + bool verify_tag(MACK_tag_and_info tag_and_info, OSNMA_data applicable_OSNMA, uint8_t tag_position, const std::vector& applicable_key, NavData applicable_NavData); + bool verify_tesla_key(std::vector& key, uint32_t TOW); bool verify_tag(Tag& tag); bool is_next_subframe(); bool tag_has_nav_data_available(Tag& t); bool tag_has_key_available(Tag& t); + bool verify_macseq(const MACK_message& mack); + bool verify_dsm_pkr(DSM_PKR_message message); + enum tags_to_verify{all,utc,slow_eph, eph, none}; + tags_to_verify d_tags_allowed{tags_to_verify::all}; std::map> d_satellite_nav_data; // map holding NavData sorted by SVID (first key) and TOW (second key). std::map> d_tesla_keys; // tesla keys over time, sorted by TOW std::vector d_macks_awaiting_MACSEQ_verification; std::multimap d_tags_awaiting_verify; // container with tags to verify from arbitrary SVIDs, sorted by TOW - std::unique_ptr d_dsm_reader; - std::unique_ptr d_crypto; + std::unique_ptr d_dsm_reader; // osnma parameters parser + std::unique_ptr d_crypto; // access to cryptographic functions std::unique_ptr d_helper; std::array, 16> d_dsm_message{}; // structure for recording DSM blocks, when filled it sends them to parse and resets itself. @@ -104,27 +113,26 @@ private: bool d_flag_debug{false}; uint32_t d_GST_Sf {}; // C: used for MACSEQ and Tesla Key verification TODO need really to be global var? uint32_t d_last_verified_key_GST{0}; + uint32_t d_GST_0 {}; + uint32_t d_GST_SIS {}; + std::time_t d_receiver_time {0}; uint8_t d_Lt_min {}; // minimum equivalent tag length uint8_t d_Lt_verified_eph {0}; // verified tag bits - ephemeris uint8_t d_Lt_verified_utc {0}; // verified tag bits - timing uint8_t const d_T_L{30}; // s RG Section 2.1 uint8_t const d_delta_COP{30}; // s SIS ICD Table 14 - uint32_t d_GST_0 {}; - uint32_t d_GST_SIS {}; - std::time_t d_receiver_time {0}; - enum tags_to_verify{all,utc,slow_eph, eph, none}; // TODO is this safe? I hope so - tags_to_verify d_tags_allowed{tags_to_verify::all}; + std::vector d_tags_to_verify{0,4,12}; std::vector d_validated_key{}; - void remove_verified_tags(); - void control_tags_awaiting_verify_size(); - bool verify_macseq(const MACK_message& mack); + // Provide access to inner functions to Gtest FRIEND_TEST(OsnmaMsgReceiverTest, TeslaKeyVerification); FRIEND_TEST(OsnmaMsgReceiverTest, OsnmaTestVectorsSimulation); FRIEND_TEST(OsnmaMsgReceiverTest, TagVerification); FRIEND_TEST(OsnmaMsgReceiverTest, BuildTagMessageM0); - std::vector build_message(const Tag& tag); + FRIEND_TEST(OsnmaMsgReceiverTest, VerifyPublicKey); + FRIEND_TEST(OsnmaMsgReceiverTest, ComputeBaseLeaf); + FRIEND_TEST(OsnmaMsgReceiverTest, ComputeMerkleRoot); }; diff --git a/src/core/system_parameters/gnss_crypto.h b/src/core/system_parameters/gnss_crypto.h index 69df8d45d..c0de86e24 100644 --- a/src/core/system_parameters/gnss_crypto.h +++ b/src/core/system_parameters/gnss_crypto.h @@ -54,6 +54,9 @@ public: { return d_x_4_0; } + inline void setMerkleRoot(std::vector v){ + d_x_4_0 = v; + } private: void read_merkle_xml(const std::string& merkleFilePath); diff --git a/src/core/system_parameters/osnma_helper.cc b/src/core/system_parameters/osnma_helper.cc index bd469a079..2002c72ca 100644 --- a/src/core/system_parameters/osnma_helper.cc +++ b/src/core/system_parameters/osnma_helper.cc @@ -47,7 +47,7 @@ std::vector Osnma_Helper::gst_to_uint8(uint32_t GST) const * @param binaryString The binary string to be converted. * @return The vector of bytes converted from the binary string. */ -std::vector Osnma_Helper::bytes(const std::string& binaryString) { +std::vector Osnma_Helper::bytes(const std::string& binaryString) const { std::vector bytes; // Determine the size of the padding needed. @@ -68,7 +68,7 @@ std::vector Osnma_Helper::bytes(const std::string& binaryString) { return bytes; } -std::string Osnma_Helper::verification_status_str(int status) +std::string Osnma_Helper::verification_status_str(const int& status) const { switch (status) { case 0: return "SUCCESS"; @@ -77,7 +77,7 @@ std::string Osnma_Helper::verification_status_str(int status) default: return "UNKNOWN"; } } -std::string Osnma_Helper::convert_to_hex_string(const std::vector& vector) +std::string Osnma_Helper::convert_to_hex_string(const std::vector& vector) const { std::stringstream ss; ss << std::hex << std::setfill('0'); @@ -86,3 +86,21 @@ std::string Osnma_Helper::convert_to_hex_string(const std::vector& vect } return ss.str(); } + +std::vector Osnma_Helper::convert_from_hex_string(const std::string& hex_string) const +{ + std::vector result; + + std::string adjusted_hex_string = hex_string; + if (hex_string.length() % 2 != 0) { + adjusted_hex_string = "0" + hex_string; + } + + for (std::size_t i = 0; i < adjusted_hex_string.length(); i += 2) { + std::string byte_string = adjusted_hex_string.substr(i, 2); + uint8_t byte = static_cast(std::stoul(byte_string, nullptr, 16)); + result.push_back(byte); + } + + return result; +} diff --git a/src/core/system_parameters/osnma_helper.h b/src/core/system_parameters/osnma_helper.h index e78fdba0f..761306efc 100644 --- a/src/core/system_parameters/osnma_helper.h +++ b/src/core/system_parameters/osnma_helper.h @@ -28,9 +28,10 @@ public: ~Osnma_Helper() = default; uint32_t compute_gst(uint32_t WN, uint32_t TOW) const; std::vector gst_to_uint8(uint32_t GST) const; - std::vector bytes(const std::string& binaryString); - std::string verification_status_str(int status); - std::string convert_to_hex_string(const std::vector& vector); + std::vector bytes(const std::string& binaryString) const; + std::string verification_status_str(const int& status) const; + std::string convert_to_hex_string(const std::vector& vector) const ; + std::vector convert_from_hex_string(const std::string& hex_string) const; // TODO remove similar function in gnss_crypto }; #endif // GNSS_SDR_OSNMA_HELPER_H diff --git a/src/tests/unit-tests/signal-processing-blocks/osnma/osnma_msg_receiver_test.cc b/src/tests/unit-tests/signal-processing-blocks/osnma/osnma_msg_receiver_test.cc index f7ff50fe7..6b7f280a4 100644 --- a/src/tests/unit-tests/signal-processing-blocks/osnma/osnma_msg_receiver_test.cc +++ b/src/tests/unit-tests/signal-processing-blocks/osnma/osnma_msg_receiver_test.cc @@ -6,6 +6,8 @@ #include #if USE_GLOG_AND_GFLAGS +#include "osnma_helper.h" +#include "gnss_crypto.h" #include // for LOG #include #else @@ -25,6 +27,7 @@ struct TestVector class OsnmaMsgReceiverTest : public ::testing::Test { protected: + Osnma_Helper helper; osnma_msg_receiver_sptr osnma; OSNMA_msg osnma_msg{}; std::array nma_position_filled; @@ -59,6 +62,77 @@ public: std::vector extract_page_bytes(const TestVector& tv, const int byte_index, const int num_bytes); }; +TEST_F(OsnmaMsgReceiverTest, ComputeMerkleRoot) +{ + // Arrange + // ---------- + std::vector computed_merkle_root; + std::vector expected_merkle_root = helper.convert_from_hex_string("A10C440F3AA62453526DB4AF76DF8D9410D35D8277397D7053C700D192702B0D"); + DSM_PKR_message dsm_pkr_message; + dsm_pkr_message.npkt = 0x01; + dsm_pkr_message.npktid = 0x2; + dsm_pkr_message.mid = 0x01; + std::vector base_leaf = helper.convert_from_hex_string("120303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA"); + + std::vector vec = helper.convert_from_hex_string("7CBE05D9970CFC9E22D0A43A340EF557624453A2E821AADEAC989C405D78BA06" + "956380BAB0D2C939EC6208151040CCFFCF1FB7156178FD1255BA0AECAAA253F7" + "407B6C5DD4DF059FF8789474061301E1C34881DB7A367A913A3674300E21EAB1" + "24EF508389B7D446C3E2ECE8D459FBBD3239A794906F5B1F92469C640164FD87"); + std::copy(vec.begin(), vec.end(), dsm_pkr_message.itn.begin()); + dsm_pkr_message.npk = helper.convert_from_hex_string("0303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA"); + + // Act + // ---------- + computed_merkle_root = osnma->compute_merke_root(dsm_pkr_message,base_leaf); + + // Assert + // ---------- + ASSERT_EQ(computed_merkle_root, expected_merkle_root); +} + +TEST_F(OsnmaMsgReceiverTest, ComputeBaseLeaf) +{ + // Arrange + // ---------- + std::vector expected_base_leaf = helper.convert_from_hex_string("120303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA"); + DSM_PKR_message dsm_pkr_message; + dsm_pkr_message.npkt = 0x01; + dsm_pkr_message.npktid = 0x2; + dsm_pkr_message.npk = helper.convert_from_hex_string("0303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA"); + + // Act + // ---------- + std::vector computed_base_leaf = osnma->compute_base_leaf(dsm_pkr_message); + + // Assert + // ---------- + ASSERT_EQ(computed_base_leaf,expected_base_leaf); +} + +TEST_F(OsnmaMsgReceiverTest, VerifyPublicKey){ // values taken from RG A.7 + // Arrange + // ---------- + osnma->d_crypto->setMerkleRoot(helper.convert_from_hex_string("A10C440F3AA62453526DB4AF76DF8D9410D35D8277397D7053C700D192702B0D")); + DSM_PKR_message dsm_pkr_message; + dsm_pkr_message.npkt = 0x01; + dsm_pkr_message.npktid = 0x2; + dsm_pkr_message.mid = 0x01; + std::vector vec = helper.convert_from_hex_string("7CBE05D9970CFC9E22D0A43A340EF557624453A2E821AADEAC989C405D78BA06" + "956380BAB0D2C939EC6208151040CCFFCF1FB7156178FD1255BA0AECAAA253F7" + "407B6C5DD4DF059FF8789474061301E1C34881DB7A367A913A3674300E21EAB1" + "24EF508389B7D446C3E2ECE8D459FBBD3239A794906F5B1F92469C640164FD87"); + std::copy(vec.begin(), vec.end(), dsm_pkr_message.itn.begin()); + dsm_pkr_message.npk = helper.convert_from_hex_string("0303B2CE64BC207BDD8BC4DF859187FCB686320D63FFA091410FC158FBB77980EA"); + + // Act + // ---------- + bool result = osnma->verify_dsm_pkr(dsm_pkr_message); + + // Assert + // ---------- + ASSERT_TRUE(result); + +} TEST_F(OsnmaMsgReceiverTest, BuildTagMessageM0) { @@ -552,10 +626,10 @@ void OsnmaMsgReceiverTest::set_time(std::tm& input) } void OsnmaMsgReceiverTest::initializeGoogleLog() { - google::InitGoogleLogging(log_name.c_str()); + google::InitGoogleLogging(log_name.c_str()); // TODO - running all tests causes conflict due to being called twice FLAGS_minloglevel = 0; // INFO FLAGS_logtostderr = 0; // add this line - FLAGS_log_dir = "/home/cgm/CLionProjects/osnma/build/src/tests/logs"; + FLAGS_log_dir = "/home/cgm/CLionProjects/osnma/data/build/src/tests/logs"; if (FLAGS_log_dir.empty()) {