From a4cfe5151517442ea42f7cf1b1d1f549b014b7b4 Mon Sep 17 00:00:00 2001 From: cesaaargm Date: Sat, 27 Jul 2024 18:25:20 +0200 Subject: [PATCH] [TAS-248] [BUG][Kroot] Kroot and PK available, but until DSM-Kroot arrived no MACK processing * reverted commit [TAS-247][FEAT][Kroot] enable hotstart with last known Kroot * DSM-KROOT loaded during startup * if new DSM verified => stored * this improves TTFAF from 4 min to 1 minute. --- src/core/libs/osnma_msg_receiver.cc | 96 +++++++++++++++---- src/core/libs/osnma_msg_receiver.h | 12 ++- src/core/receiver/gnss_flowgraph.cc | 3 +- src/core/system_parameters/Galileo_OSNMA.h | 8 +- src/core/system_parameters/gnss_crypto.cc | 71 +------------- src/core/system_parameters/gnss_crypto.h | 13 +-- .../osnma/osnma_msg_receiver_test.cc | 2 +- 7 files changed, 92 insertions(+), 113 deletions(-) diff --git a/src/core/libs/osnma_msg_receiver.cc b/src/core/libs/osnma_msg_receiver.cc index 27130600f..80613ca3d 100644 --- a/src/core/libs/osnma_msg_receiver.cc +++ b/src/core/libs/osnma_msg_receiver.cc @@ -24,7 +24,7 @@ #include "gnss_satellite.h" #include "osnma_dsm_reader.h" // for OSNMA_DSM_Reader #include "osnma_helper.h" -#include "osnma_nav_data_manager.h" // TODO - all these repeated includes, is it good practice to include them in the source file? +#include "osnma_nav_data_manager.h" #include // for gr::io_signature::make #include #include @@ -37,6 +37,7 @@ #include #include // for typeid #include +#include // for std::ifstream and std::ofstream #if USE_GLOG_AND_GFLAGS @@ -60,24 +61,36 @@ namespace wht = std; #endif -osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath) +osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath) { - return osnma_msg_receiver_sptr(new osnma_msg_receiver(pemFilePath, merkleFilePath, rootKeyFilePath)); + return osnma_msg_receiver_sptr(new osnma_msg_receiver(pemFilePath, merkleFilePath)); } -osnma_msg_receiver::osnma_msg_receiver(const std::string& crtFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath) : gr::block("osnma_msg_receiver", +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)) { d_dsm_reader = std::make_unique(); - d_crypto = std::make_unique(crtFilePath, merkleFilePath, rootKeyFilePath); + d_crypto = std::make_unique(crtFilePath, merkleFilePath); d_helper = std::make_unique(); d_nav_data_manager = std::make_unique(); - if(d_crypto->have_root_key()){ - d_kroot = d_crypto->get_root_key(); - d_kroot_verified = true; + if(d_crypto->have_public_key()){ // Hot start is enabled + auto dsm_nmah = parse_dsm_kroot(); + 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; + process_dsm_message(dsm_nmah.first, dsm_nmah.second); + LOG(WARNING) << "OSNMA DSM-KROOT available :: HOT START"; + std::cout << "OSNMA DSM-KROOT available :: HOT START" << std::endl; + } + else + { + LOG(WARNING) << "OSNMA DSM-KROOT not available :: WARM START"; + std::cout << "OSNMA DSM-KROOT not available :: WARM START" << std::endl; + } } // register OSNMA input message port from telemetry blocks @@ -369,7 +382,7 @@ void osnma_msg_receiver::process_dsm_block(const std::shared_ptr& osn } d_dsm_message[d_osnma_data.d_dsm_header.dsm_id] = std::array{}; d_dsm_id_received[d_osnma_data.d_dsm_header.dsm_id] = std::array{}; - process_dsm_message(dsm_msg, osnma_msg); + process_dsm_message(dsm_msg, osnma_msg->hkroot[0]); } } @@ -381,10 +394,10 @@ void osnma_msg_receiver::process_dsm_block(const std::shared_ptr& osn * case DSM-PKR: * - calls verify_dsm_pkr to verify the public key * */ -void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg, const std::shared_ptr& osnma_msg) +void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg, const uint8_t& nma_header) { // DSM-KROOT message - if (d_osnma_data.d_dsm_header.dsm_id < 12) + if (d_osnma_data.d_dsm_header.dsm_id < 12 || d_flag_hot_start) { // Parse Kroot message LOG(INFO) << "Galileo OSNMA: DSM-KROOT message received."; @@ -438,13 +451,13 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg // validation of padding const uint16_t size_m = 13 + l_lk_bytes; std::vector MSG; - MSG.reserve(size_m + l_ds_bytes + 1); // C: message will get too many zeroes? ((12+1)+16) + 64 + 1? => in theory not, allocating is not assigning - MSG.push_back(osnma_msg->hkroot[0]); // C: NMA header + MSG.reserve(size_m + l_ds_bytes + 1); + 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]); @@ -478,7 +491,7 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg << ", 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); + // local_time_verification(osnma_msg); // FIXME TODO: real time verification needed if (l_ds_bits == 512) { d_kroot_verified = d_crypto->verify_signature_ecdsa_p256(message, d_osnma_data.d_dsm_kroot_message.ds); @@ -494,10 +507,12 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg LOG(INFO) << "Galileo OSNMA: NMA Status is " << d_dsm_reader->get_nmas_status(d_osnma_data.d_nma_header.nmas) << ", " << "Chain in force is " << static_cast(d_osnma_data.d_nma_header.cid) << ", " << "Chain and Public Key Status is " << d_dsm_reader->get_cpks_status(d_osnma_data.d_nma_header.cpks); - // Save Kroot into a permanent storage - d_crypto->store_root_key(ROOTKEYFILE_DEFAULT); - d_kroot = d_osnma_data.d_dsm_kroot_message.kroot; - d_crypto->set_root_key(d_kroot); + // Save DSM-Kroot and NMA header into a permanent storage + if(d_flag_hot_start){ + d_flag_hot_start = false; + return; + } + store_dsm_kroot(dsm_msg, nma_header); } else { @@ -576,7 +591,7 @@ void osnma_msg_receiver::process_dsm_message(const std::vector& dsm_msg { d_public_key_verified = true; d_crypto->set_public_key(d_osnma_data.d_dsm_pkr_message.npk); - d_crypto->store_public_key(PEMFILE_STORED); + d_crypto->store_public_key(PEMFILE_DEFAULT); } } } @@ -1812,6 +1827,7 @@ std::vector osnma_msg_receiver::verify_macseq_new(const MACK_ return verified_tags; } } + void osnma_msg_receiver::send_data_to_pvt(std::vector data) { if (!data.empty()) @@ -1823,3 +1839,43 @@ void osnma_msg_receiver::send_data_to_pvt(std::vector data) } } } + +bool osnma_msg_receiver::store_dsm_kroot(const std::vector& dsm, const uint8_t nma_header) const +{ + std::ofstream file(KROOTFILE_DEFAULT, std::ios::binary | std::ios::out); + + if (!file.is_open()) { + return false; + } + + // NMA header + file.write(reinterpret_cast(&nma_header), 1); + + // Then writing the entire dsm_msg vector to the file + file.write(reinterpret_cast(dsm.data()), dsm.size()); + + return file.good(); +} + +std::pair, uint8_t> osnma_msg_receiver::parse_dsm_kroot() const +{ + std::ifstream file(KROOTFILE_DEFAULT, std::ios::binary | std::ios::in); + if (!file) { + return {std::vector(), 0}; + } + + // Read the first byte into hkroot[0] + uint8_t nma_header; + file.read(reinterpret_cast(&nma_header), 1); + + // Read the remaining file content into dsm_msg + std::vector dsm_msg((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + + file.close(); + + if (file.bad()) { + return {std::vector(), 0}; + } + + return {dsm_msg, nma_header}; +} diff --git a/src/core/libs/osnma_msg_receiver.h b/src/core/libs/osnma_msg_receiver.h index cda571321..15abc632d 100644 --- a/src/core/libs/osnma_msg_receiver.h +++ b/src/core/libs/osnma_msg_receiver.h @@ -50,7 +50,7 @@ class osnma_msg_receiver; using osnma_msg_receiver_sptr = gnss_shared_ptr; -osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath); +osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath); /*! * \brief GNU Radio block that receives asynchronous OSNMA messages @@ -63,8 +63,8 @@ class osnma_msg_receiver : public gr::block public: ~osnma_msg_receiver() = default; //!< Default destructor private: - friend osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath); - osnma_msg_receiver(const std::string& crtFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath); + friend osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath); + osnma_msg_receiver(const std::string& crtFilePath, const std::string& merkleFilePath); void msg_handler_osnma(const pmt::pmt_t& msg); void process_osnma_message(const std::shared_ptr& osnma_msg); @@ -73,7 +73,7 @@ private: void read_dsm_block(const std::shared_ptr& osnma_msg); 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); + void process_dsm_message(const std::vector& dsm_msg, const uint8_t& nma_header); void read_and_process_mack_block(const std::shared_ptr& osnma_msg); void read_mack_header(); void read_mack_body(); @@ -89,7 +89,9 @@ private: bool tag_has_key_available(const Tag& t) const; bool verify_macseq(const MACK_message& mack); bool verify_dsm_pkr(const DSM_PKR_message& message) const; + bool store_dsm_kroot(const std::vector& dsm, const uint8_t nma_header) const; + std::pair, uint8_t> parse_dsm_kroot() const; std::vector get_merkle_tree_leaves(const DSM_PKR_message& dsm_pkr_message) const; std::vector compute_merkle_root(const DSM_PKR_message& dsm_pkr_message, const std::vector& m_i) const; std::vector build_message(Tag& tag) const; @@ -100,7 +102,6 @@ private: std::map> d_tesla_keys; // tesla keys over time, sorted by TOW std::multimap d_tags_awaiting_verify; // container with tags to verify from arbitrary SVIDs, sorted by TOW - std::vector d_kroot; // last available stored root key std::vector d_tags_to_verify{0, 4, 12}; std::vector d_macks_awaiting_MACSEQ_verification; @@ -143,6 +144,7 @@ private: bool d_kroot_verified{false}; bool d_tesla_key_verified{false}; bool d_flag_debug{false}; + bool d_flag_hot_start{false}; // Provide access to inner functions to Gtest FRIEND_TEST(OsnmaMsgReceiverTest, TeslaKeyVerification); diff --git a/src/core/receiver/gnss_flowgraph.cc b/src/core/receiver/gnss_flowgraph.cc index 035d690dc..af1795ee0 100644 --- a/src/core/receiver/gnss_flowgraph.cc +++ b/src/core/receiver/gnss_flowgraph.cc @@ -126,8 +126,7 @@ void GNSSFlowgraph::init() enable_osnma_rx_ = true; const auto certFilePath = configuration_->property("GNSS-SDR.osnma_public_key", CRTFILE_DEFAULT); const auto merKleTreePath = configuration_->property("GNSS-SDR.osnma_merkletree", MERKLEFILE_DEFAULT); - const auto rootKeyPath = configuration_->property("GNSS-SDR.osnma_root_key", ROOTKEYFILE_DEFAULT); - osnma_rx_ = osnma_msg_receiver_make(certFilePath, merKleTreePath, rootKeyPath); + osnma_rx_ = osnma_msg_receiver_make(certFilePath, merKleTreePath); } else { diff --git a/src/core/system_parameters/Galileo_OSNMA.h b/src/core/system_parameters/Galileo_OSNMA.h index 08a14c93e..94274cc36 100644 --- a/src/core/system_parameters/Galileo_OSNMA.h +++ b/src/core/system_parameters/Galileo_OSNMA.h @@ -160,10 +160,10 @@ const std::unordered_map OSNMA_TABLE_15 = { {std::string("SHA-256"), 512}, {std::string("SHA-512"), 1056}}; // key: ECDSA Curve and hash function, value: {l_ds_bits} -const std::string PEMFILE_STORED("./OSNMA_PublicKey.pem"); -const std::string CRTFILE_DEFAULT("../data/OSNMA_PublicKey_20240115100000_newPKID_1.crt"); -const std::string MERKLEFILE_DEFAULT("../data/OSNMA_MerkleTree_20240115100000_newPKID_1.xml"); -const std::string ROOTKEYFILE_DEFAULT("../data/OSNMA_RootKey.bin"); +const std::string PEMFILE_DEFAULT("./data/OSNMA_PublicKey.pem"); +const std::string CRTFILE_DEFAULT("./data/OSNMA_PublicKey_20240115100000_newPKID_1.crt"); +const std::string MERKLEFILE_DEFAULT("./data/OSNMA_MerkleTree_20240115100000_newPKID_1.xml"); +const std::string KROOTFILE_DEFAULT("./data/OSNMA_DSM_KROOT_NMAHeader.bin"); class Mack_lookup { diff --git a/src/core/system_parameters/gnss_crypto.cc b/src/core/system_parameters/gnss_crypto.cc index e31a76acc..2a819c60c 100644 --- a/src/core/system_parameters/gnss_crypto.cc +++ b/src/core/system_parameters/gnss_crypto.cc @@ -75,7 +75,7 @@ Gnss_Crypto::Gnss_Crypto() } -Gnss_Crypto::Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath, const std::string& rootKeyFilePath) +Gnss_Crypto::Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath) { #if USE_GNUTLS_FALLBACK gnutls_global_init(); @@ -96,11 +96,10 @@ Gnss_Crypto::Gnss_Crypto(const std::string& certFilePath, const std::string& mer readPublicKeyFromPEM(certFilePath); if (!have_public_key()) { - readPublicKeyFromPEM(PEMFILE_STORED); + readPublicKeyFromPEM(PEMFILE_DEFAULT); } } read_merkle_xml(merkleTreePath); - read_root_key(rootKeyFilePath); } @@ -123,10 +122,6 @@ Gnss_Crypto::~Gnss_Crypto() #endif } -bool Gnss_Crypto::have_root_key() const -{ - return !d_kroot.empty(); -} bool Gnss_Crypto::have_public_key() const { #if USE_GNUTLS_FALLBACK @@ -200,23 +195,6 @@ bool Gnss_Crypto::store_public_key(const std::string& pubKeyFilePath) const return true; } -bool Gnss_Crypto::store_root_key(const std::string& rootKeyFilePath) const -{ - if (!have_root_key()) - { - return false; - } - std::ofstream file(rootKeyFilePath, std::ios::binary | std::ios::out); - - if (!file) { - return false; - } - - file.write(reinterpret_cast(d_kroot.data()), d_kroot.size()); - - return file.good(); -} - bool Gnss_Crypto::verify_signature_ecdsa_p256(const std::vector& message, const std::vector& signature) const { std::vector digest = this->compute_SHA_256(message); @@ -865,11 +843,6 @@ std::vector Gnss_Crypto::get_merkle_root() const return d_x_4_0; } -std::vector Gnss_Crypto::get_root_key() const -{ - return d_kroot; -} - void Gnss_Crypto::set_public_key(const std::vector& publicKey) { #if USE_GNUTLS_FALLBACK @@ -928,11 +901,6 @@ void Gnss_Crypto::set_merkle_root(const std::vector& v) d_x_4_0 = v; } -void Gnss_Crypto::set_root_key(const std::vector& root_key) -{ - d_kroot = root_key; -} - void Gnss_Crypto::read_merkle_xml(const std::string& merkleFilePath) { pugi::xml_document doc; @@ -1172,41 +1140,6 @@ bool Gnss_Crypto::readPublicKeyFromCRT(const std::string& crtFilePath) return true; } -/** - * \brief Reads the TESLA root key from a file and stores it. - * \param rootKeyFilePath The file path of the TESLA root key. - * \return True if the root key was successfully read and stored, false otherwise. - */ -bool Gnss_Crypto::read_root_key(const std::string& rootKeyFilePath) -{ - std::ifstream file(rootKeyFilePath, std::ios::binary | std::ios::in); - - if (!file) { - LOG(WARNING) << "Unable to open file: " << rootKeyFilePath; - return false; - } - - // Determine file size - file.seekg(0, std::ios::end); - std::streamsize size = file.tellg(); - file.seekg(0, std::ios::beg); - - if (size == 0) { - LOG(WARNING) << "File is empty: " << rootKeyFilePath; - return false; - } - - // Resize the vector and read file - d_kroot.resize(size); - if (!file.read(reinterpret_cast(d_kroot.data()), size)) { - LOG(WARNING) << "Failed to read the file: " << rootKeyFilePath; - return false; - } - std::cout << "OSNMA TESLA Root Key successfully read from file " << rootKeyFilePath << std::endl; - LOG(INFO) << "OSNMA TESLA Root Key successfully read from file " << rootKeyFilePath; - return true; -} - bool Gnss_Crypto::convert_raw_to_der_ecdsa(const std::vector& raw_signature, std::vector& der_signature) const { if (raw_signature.size() % 2 != 0) diff --git a/src/core/system_parameters/gnss_crypto.h b/src/core/system_parameters/gnss_crypto.h index 6ff60650c..a331c78d0 100644 --- a/src/core/system_parameters/gnss_crypto.h +++ b/src/core/system_parameters/gnss_crypto.h @@ -48,22 +48,15 @@ public: * and a XML file for the Merkle Tree root. * Files can be downloaded by registering at https://www.gsc-europa.eu/ */ - Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath, const std::string& rootKeyFilePath); + Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath); ~Gnss_Crypto(); //!< Default destructor - bool have_root_key() const; //!< Returns true if the TESLA root key is already loaded bool have_public_key() const; //!< Returns true if the ECDSA Public Key is already loaded /*! * Stores the ECDSA Public Key in a .pem file, which is read in a following run if the .crt file is not found */ bool store_public_key(const std::string& pubKeyFilePath) const; - /*! - * Stores the TESLA root key in a plaintext file, which is read in a following run for a faster TTFAF. - * @param kroot TESLA root key - * @return true if successful - */ - bool store_root_key(const std::string& rootKeyFilePath) const; bool verify_signature_ecdsa_p256(const std::vector& message, const std::vector& signature) const; //!< Verify ECDSA-P256 signature (message in plain hex, signature in raw format) bool verify_signature_ecdsa_p521(const std::vector& message, const std::vector& signature) const; //!< Verify ECDSA-P521 signature (message in plain hex, signature in raw format) @@ -75,15 +68,12 @@ public: 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::vector get_root_key() const; //!< Gets the TESLA root key in binary format void set_public_key(const std::vector& publickey); //!< Sets the ECDSA Public Key (publickey in PEM format) void set_merkle_root(const std::vector& v); //!< Sets the Merkle Tree root node x(\f$ x_{4,0} \f$) - void set_root_key(const std::vector& root_key); //!< Sets the TESLA root key private: void read_merkle_xml(const std::string& merkleFilePath); void readPublicKeyFromPEM(const std::string& pemFilePath); - bool read_root_key(const std::string& rootKeyFilePath); bool readPublicKeyFromCRT(const std::string& crtFilePath); bool convert_raw_to_der_ecdsa(const std::vector& raw_signature, std::vector& der_signature) const; std::vector convert_from_hex_str(const std::string& input) const; @@ -100,7 +90,6 @@ private: #endif #endif std::vector d_x_4_0; - std::vector d_kroot; }; /** \} */ 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 519f78a22..a9d6e60c5 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 @@ -51,7 +51,7 @@ protected: // std::tm input_time = {0, 0, 5, 16, 8 - 1, 2023 - 1900, 0}; // conf. 1 std::tm input_time = {0, 0, 0, 27, 7 - 1, 2023 - 1900, 0}; // conf. 2 set_time(input_time); - osnma = osnma_msg_receiver_make("", "", ""); + osnma = osnma_msg_receiver_make(CRTFILE_DEFAULT, MERKLEFILE_DEFAULT); } };