diff --git a/src/core/libs/osnma_msg_receiver.cc b/src/core/libs/osnma_msg_receiver.cc index 49ee685bf..27130600f 100644 --- a/src/core/libs/osnma_msg_receiver.cc +++ b/src/core/libs/osnma_msg_receiver.cc @@ -60,22 +60,26 @@ namespace wht = std; #endif -osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath) +osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath) { - return osnma_msg_receiver_sptr(new osnma_msg_receiver(pemFilePath, merkleFilePath)); + return osnma_msg_receiver_sptr(new osnma_msg_receiver(pemFilePath, merkleFilePath, rootKeyFilePath)); } -osnma_msg_receiver::osnma_msg_receiver( - const std::string& crtFilePath, - const std::string& merkleFilePath) : gr::block("osnma_msg_receiver", +osnma_msg_receiver::osnma_msg_receiver(const std::string& crtFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath) : 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); + d_crypto = std::make_unique(crtFilePath, merkleFilePath, rootKeyFilePath); 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; + } + // register OSNMA input message port from telemetry blocks this->message_port_register_in(pmt::mp("OSNMA_from_TLM")); // register OSNMA output message port to PVT block @@ -126,7 +130,7 @@ void osnma_msg_receiver::msg_handler_osnma(const pmt::pmt_t& msg) uint32_t PRNa = std::get<0>(*inav_data); std::string nav_data = std::get<1>(*inav_data); uint32_t TOW = std::get<2>(*inav_data); - d_nav_data_manager->add_navigation_data(nav_data, PRNa,TOW); + d_nav_data_manager->add_navigation_data(nav_data, PRNa, TOW); } else { @@ -490,6 +494,10 @@ 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); } else { diff --git a/src/core/libs/osnma_msg_receiver.h b/src/core/libs/osnma_msg_receiver.h index 53931d2cc..412296f12 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); +osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath); /*! * \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); - osnma_msg_receiver(const std::string& crtFilePath, const std::string& merkleFilePath); + 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); void msg_handler_osnma(const pmt::pmt_t& msg); void process_osnma_message(const std::shared_ptr& osnma_msg); @@ -100,6 +100,7 @@ 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; @@ -110,7 +111,7 @@ private: std::unique_ptr d_dsm_reader; // osnma parameters parser std::unique_ptr d_crypto; // access to cryptographic functions - std::unique_ptr d_helper; + std::unique_ptr d_helper; // helper class with auxiliary functions std::unique_ptr d_nav_data_manager; // refactor for holding and processing navigation data OSNMA_data d_osnma_data{}; diff --git a/src/core/receiver/gnss_flowgraph.cc b/src/core/receiver/gnss_flowgraph.cc index af1795ee0..035d690dc 100644 --- a/src/core/receiver/gnss_flowgraph.cc +++ b/src/core/receiver/gnss_flowgraph.cc @@ -126,7 +126,8 @@ 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); - osnma_rx_ = osnma_msg_receiver_make(certFilePath, merKleTreePath); + const auto rootKeyPath = configuration_->property("GNSS-SDR.osnma_root_key", ROOTKEYFILE_DEFAULT); + osnma_rx_ = osnma_msg_receiver_make(certFilePath, merKleTreePath, rootKeyPath); } else { diff --git a/src/core/system_parameters/Galileo_OSNMA.h b/src/core/system_parameters/Galileo_OSNMA.h index 09f615e08..08a14c93e 100644 --- a/src/core/system_parameters/Galileo_OSNMA.h +++ b/src/core/system_parameters/Galileo_OSNMA.h @@ -163,6 +163,7 @@ const std::unordered_map OSNMA_TABLE_15 = { 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"); class Mack_lookup { diff --git a/src/core/system_parameters/gnss_crypto.cc b/src/core/system_parameters/gnss_crypto.cc index fa47720b1..e31a76acc 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) +Gnss_Crypto::Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath, const std::string& rootKeyFilePath) { #if USE_GNUTLS_FALLBACK gnutls_global_init(); @@ -100,6 +100,7 @@ Gnss_Crypto::Gnss_Crypto(const std::string& certFilePath, const std::string& mer } } read_merkle_xml(merkleTreePath); + read_root_key(rootKeyFilePath); } @@ -122,7 +123,10 @@ 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 @@ -196,6 +200,22 @@ 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 { @@ -845,6 +865,10 @@ 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) { @@ -899,12 +923,15 @@ void Gnss_Crypto::set_public_key(const std::vector& publicKey) DLOG(INFO) << "OSNMA Public Key successfully set up."; } - 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) { @@ -1145,6 +1172,40 @@ 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 { diff --git a/src/core/system_parameters/gnss_crypto.h b/src/core/system_parameters/gnss_crypto.h index 8e89f1525..6ff60650c 100644 --- a/src/core/system_parameters/gnss_crypto.h +++ b/src/core/system_parameters/gnss_crypto.h @@ -48,15 +48,22 @@ 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); + Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath, const std::string& rootKeyFilePath); ~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) @@ -68,13 +75,15 @@ 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; @@ -91,6 +100,7 @@ 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 c67b2b208..1d2c628ae 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 @@ -24,6 +24,7 @@ #include #include #include +#include "Galileo_OSNMA.h" #if USE_GLOG_AND_GFLAGS #include // for LOG @@ -76,7 +77,8 @@ protected: // std::string merkleFilePath = "/home/cgm/CLionProjects/osnma/data/OSNMA_MerkleTree_20230803105953_newPKID_1.xml"; std::string crtFilePath = "/home/cgm/CLionProjects/osnma/data/OSNMA_PublicKey_20230720113300_newPKID_2.crt"; // conf. 2 std::string merkleFilePath = "/home/cgm/CLionProjects/osnma/data/OSNMA_MerkleTree_20230720113300_newPKID_2.xml"; - osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath); + std::string rootKeyFilePath = ROOTKEYFILE_DEFAULT; + osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath, ROOTKEYFILE_DEFAULT); } };