From 1d815f0bbf1ab797857a997b59764621dfdf25c2 Mon Sep 17 00:00:00 2001
From: cesaaargm <cesare.martinez@proton.me>
Date: Wed, 26 Jun 2024 15:01:05 +0200
Subject: [PATCH 01/14] [TAS-227] [BUG] Tag verification fails for .dat files
 (WIP)

WIP
---
 .../galileo_telemetry_decoder_gs.cc           |  56 +-
 src/core/libs/osnma_msg_receiver.cc           |  78 ++-
 src/core/libs/osnma_msg_receiver.h            |   2 +-
 .../system_parameters/galileo_inav_message.cc |   2 +-
 src/core/system_parameters/osnma_helper.cc    |  11 +
 src/core/system_parameters/osnma_helper.h     |   1 +
 .../osnma/osnma_msg_receiver_test.cc          | 513 ++++++++++--------
 7 files changed, 369 insertions(+), 294 deletions(-)

diff --git a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
index a404a100a..3b3d4b06a 100644
--- a/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
+++ b/src/algorithms/telemetry_decoder/gnuradio_blocks/galileo_telemetry_decoder_gs.cc
@@ -439,6 +439,35 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
         }
 
     // 4. Push the new navigation data to the queues
+    // extract OSNMA bits, reset container.
+    bool check_size_is_ok = d_inav_nav.get_osnma_adkd_0_12_nav_bits().size() == 549;
+    if(check_size_is_ok)
+        {
+            std::cout << "Galileo OSNMA: new ADKD=0/12 navData from " << d_satellite << " at TOW_sf=" << d_inav_nav.get_TOW5() - 25 <<std::endl;
+            const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string,uint32_t>>( // < PRNd , navDataBits, TOW_Sosf>
+                d_satellite.get_PRN(),
+                d_inav_nav.get_osnma_adkd_0_12_nav_bits(),
+                d_inav_nav.get_TOW5() - 25);
+            this->message_port_pub(pmt::mp("OSNMA_from_TLM"), pmt::make_any(tmp_obj_osnma));
+            LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d="<< static_cast<int>(d_satellite.get_PRN()) << ", TOW=" << static_cast<int>(d_inav_nav.get_TOW5() - 25) <<")";//: 0b" << d_inav_nav.get_osnma_adkd_0_12_nav_bits();
+            d_inav_nav.reset_osnma_nav_bits_adkd0_12();
+        }
+
+    check_size_is_ok = d_inav_nav.get_osnma_adkd_4_nav_bits().size() == 141;
+    if(check_size_is_ok)
+        {
+            std::cout << "Galileo OSNMA: new ADKD=4 navData from " << d_satellite <<" at TOW_sf=" << d_inav_nav.get_TOW6() - 5 <<std::endl;
+
+            const auto tmp_obj = std::make_shared<std::tuple<uint32_t, std::string,uint32_t>>( // < PRNd , navDataBits, TOW_Sosf> // TODO conversion from W6 to W_Start_of_subframe
+                d_satellite.get_PRN(),
+                d_inav_nav.get_osnma_adkd_4_nav_bits(),
+                d_inav_nav.get_TOW6() - 5);
+            this->message_port_pub(pmt::mp("OSNMA_from_TLM"), pmt::make_any(tmp_obj));
+            LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d="<< static_cast<int>(d_satellite.get_PRN()) << ", TOW=" << static_cast<int>(d_inav_nav.get_TOW6() - 5) <<")";//: 0b" << d_inav_nav.get_osnma_adkd_4_nav_bits();
+            d_inav_nav.reset_osnma_nav_bits_adkd4();
+        }
+
+
     if (d_inav_nav.have_new_ephemeris() == true) // C: tells if W1-->W4 available from same blcok (and W5!)
         {
             // get object for this SV (mandatory)
@@ -472,20 +501,6 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
             d_first_eph_sent = true;  // do not send reduced CED anymore, since we have the full ephemeris set
 
 //            d_flag_osnma_adkd_0_12 = true; // W1-> W5 available
-            // extract bits, reset container.
-            bool check_size_is_ok = d_inav_nav.get_osnma_adkd_0_12_nav_bits().size() == 549;
-            if(check_size_is_ok)
-                {
-                    std::cout << "Galileo OSNMA: sending ADKD=0/12 navData, PRN_d (" << d_satellite.get_PRN() << ") " << "TOW_sf=" << d_inav_nav.get_TOW5() - 24 <<std::endl;
-                    const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string,uint32_t>>( // < PRNd , navDataBits, TOW_Sosf>
-                        d_satellite.get_PRN(),
-                        d_inav_nav.get_osnma_adkd_0_12_nav_bits(),
-                        d_inav_nav.get_TOW5() - 24);
-                    this->message_port_pub(pmt::mp("OSNMA_from_TLM"), pmt::make_any(tmp_obj_osnma));
-                    d_inav_nav.reset_osnma_nav_bits_adkd0_12();
-                }
-
-
         }
     else
         {
@@ -615,19 +630,6 @@ void galileo_telemetry_decoder_gs::decode_INAV_word(float *page_part_symbols, in
     //    bool adkd_4_nav_data_available = d_inav_nav.get_osnma_adkd_4_nav_bits().size() == 141; // newApproach: let decoder decide when block starts and let it fill the data, and just check for length
     if(adkd_4_nav_data_available /*&& d_inav_nav.is_TOW5_set() not needed cause W6 has TOW also.*/)
         {
-            bool check_size_is_ok = d_inav_nav.get_osnma_adkd_4_nav_bits().size() == 141;
-            if(check_size_is_ok)
-                {
-                    std::cout << "Galileo OSNMA: sending ADKD=4 navData, PRN_d (" << d_satellite.get_PRN() << ") " << "TOW_sf=" << d_inav_nav.get_TOW6() - 4 <<std::endl;
-
-                    const auto tmp_obj = std::make_shared<std::tuple<uint32_t, std::string,uint32_t>>( // < PRNd , navDataBits, TOW_Sosf> // TODO conversion from W6 to W_Start_of_subframe
-                        d_satellite.get_PRN(),
-                        d_inav_nav.get_osnma_adkd_4_nav_bits(),
-                        d_inav_nav.get_TOW6() - 4);
-                    this->message_port_pub(pmt::mp("OSNMA_from_TLM"), pmt::make_any(tmp_obj));
-                    d_inav_nav.reset_osnma_nav_bits_adkd4();
-                }
-
         }
     auto newOSNMA = d_inav_nav.have_new_nma();
     if (d_band == '1' && newOSNMA)
diff --git a/src/core/libs/osnma_msg_receiver.cc b/src/core/libs/osnma_msg_receiver.cc
index 4a5e95ac9..45ad958ee 100644
--- a/src/core/libs/osnma_msg_receiver.cc
+++ b/src/core/libs/osnma_msg_receiver.cc
@@ -62,13 +62,13 @@ osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath,
 
 
 osnma_msg_receiver::osnma_msg_receiver(
-    const std::string& pemFilePath,
+    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<OSNMA_DSM_Reader>();
-    d_crypto = std::make_unique<Gnss_Crypto>(pemFilePath, merkleFilePath);
+    d_crypto = std::make_unique<Gnss_Crypto>(crtFilePath, merkleFilePath);
     d_helper = std::make_unique<Osnma_Helper>();
     //  register OSNMA input message port from telemetry blocks
     this->message_port_register_in(pmt::mp("OSNMA_from_TLM"));
@@ -101,13 +101,13 @@ void osnma_msg_receiver::msg_handler_osnma(const pmt::pmt_t& msg)
                     const auto sat = Gnss_Satellite(std::string("Galileo"), nma_msg->PRN);  // TODO remove if unneeded
 
                     std::ostringstream output_message;
-                    output_message << "Galileo OSNMA: Subframe received starting at "
-                                   << "WN="
-                                   << nma_msg->WN_sf0
-                                   << ", TOW="
-                                   << nma_msg->TOW_sf0
-                                   << ", from satellite "
-                                   << sat;
+                    output_message << "Galileo OSNMA: complete OSNMA message received starting at "
+                        << "WN="
+                        << nma_msg->WN_sf0
+                        << ", TOW="
+                        << nma_msg->TOW_sf0
+                        << ", from satellite "
+                        << sat;
                     LOG(WARNING) << output_message.str();
                     std::cout << output_message.str() << std::endl;
 
@@ -125,14 +125,14 @@ void osnma_msg_receiver::msg_handler_osnma(const pmt::pmt_t& msg)
                     // iono data => 549 bits, utc data, 141 bits.
                     if (nav_data.size() == 549)
                         {
-                            LOG(INFO) << "Galileo OSNMA: received ADKD=0/12 navData, PRN_d (" << PRNa << ") "
-                                      << "TOW_sf=" << TOW;
+//                            LOG(INFO) << "Galileo OSNMA: received ADKD=0/12 navData, PRN_d (" << PRNa << ") "
+//                                      << "TOW_sf=" << TOW;
                             d_satellite_nav_data[PRNa][TOW].ephemeris_iono_vector_2 = nav_data;
                         }
                     else if (nav_data.size() == 141)
                         {
-                            LOG(INFO) << "Galileo OSNMA: received ADKD=4 navData, PRN_d (" << PRNa << ") "
-                                      << "TOW_sf=" << TOW;
+//                            LOG(INFO) << "Galileo OSNMA: received ADKD=4 navData, PRN_d (" << PRNa << ") "
+//                                      << "TOW_sf=" << TOW;
                             d_satellite_nav_data[PRNa][TOW].utc_vector_2 = nav_data;
                         }
                     else
@@ -322,8 +322,8 @@ void osnma_msg_receiver::local_time_verification(const std::shared_ptr<OSNMA_msg
         {
             d_tags_allowed = tags_to_verify::all;
             d_tags_to_verify = {0, 4, 12};
-            LOG(INFO) << "Galileo OSNMA: time constraint OK (" << delta_T;
-            LOG(INFO) << "Galileo OSNMA: d_receiver_time: " << d_receiver_time << " d_GST_SIS: " << d_GST_SIS;
+            LOG(INFO) << "Galileo OSNMA: time constraint OK ( delta_T=" << delta_T << " s)";
+//            LOG(INFO) << "Galileo OSNMA: d_receiver_time: " << d_receiver_time << " d_GST_SIS: " << d_GST_SIS;
             // std::cout << "( |local_t - GST_SIS| < T_L ) [ |" << static_cast<int>(d_receiver_time - d_GST_SIS)<< " | < " << static_cast<int>(d_T_L) << " ]" << std::endl;
 
             // TODO set flag to false to avoid processing dsm and MACK messages
@@ -914,13 +914,25 @@ void osnma_msg_receiver::process_mack_message()
                     bool ret = verify_macseq(*mack);
                     if (ret || d_flag_debug)
                         {
+                            LOG(WARNING) << "Galileo OSNMA: MACSEQ verification :: SUCCESS for Mack at TOW=" << mack->TOW << ", PRN" << mack->PRNa;
                             for (std::size_t i = 0; i < mack->tag_and_info.size(); ++i)
                                 {
                                     // add tags of current mack to the verification queue
                                     auto& tag = mack->tag_and_info[i];
                                     Tag t(tag, mack->TOW, mack->WN, mack->PRNa, i + 2);  // tag0 (mack header) has CTR1, so first tag of MTI has CTR = 2.
                                     d_tags_awaiting_verify.insert(std::pair<uint32_t, Tag>(mack->TOW, t));
-                                    LOG(WARNING) << "Galileo OSNMA: MACSEQ verification :: SUCCESS for Mack at TOW=" << mack->TOW << ", PRN" << mack->PRNa;
+                                    LOG(INFO) << "Galileo OSNMA: Add Tag Id= "
+                                              << t.tag_id
+                                              << ", value=0x" << std::setfill('0') << std::setw(10) << std::hex << std::uppercase
+                                              << t.received_tag << std::dec
+                                              << ", TOW="
+                                              << t.TOW
+                                              << ", ADKD="
+                                              << static_cast<unsigned>(t.ADKD)
+                                              << ", PRNa="
+                                              << static_cast<unsigned>(t.PRNa)
+                                              << ", PRNd="
+                                              << static_cast<unsigned>(t.PRN_d);
                                 }
                             std::cout << "Galileo OSNMA: d_tags_awaiting_verify :: size: " << d_tags_awaiting_verify.size() << std::endl;
                             mack = d_macks_awaiting_MACSEQ_verification.erase(mack);
@@ -1070,6 +1082,11 @@ bool osnma_msg_receiver::verify_dsm_pkr(DSM_PKR_message message)
 
 bool osnma_msg_receiver::verify_tag(Tag& tag)
 {
+    // Debug
+    LOG(INFO) << "Galileo OSNMA: Tag verification :: Start for tag Id= "
+              << tag.tag_id
+              << ", value=0x" << std::setfill('0') << std::setw(10) << std::hex << std::uppercase
+              << tag.received_tag << std::dec;
     // TODO case tag0, to be verified here?, PRNd not needed for it
     // build message
     std::vector<uint8_t> m = build_message(tag);
@@ -1077,9 +1094,16 @@ bool osnma_msg_receiver::verify_tag(Tag& tag)
     std::vector<uint8_t> mac;
     std::vector<uint8_t> applicable_key;
     if (tag.ADKD == 0 || tag.ADKD == 4)
-        applicable_key = d_tesla_keys[tag.TOW + 30];
-    else  // ADKD 12
-        applicable_key = d_tesla_keys[tag.TOW + 330];
+        {
+            applicable_key = d_tesla_keys[tag.TOW + 30];
+            LOG(INFO) << "|---> Galileo OSNMA :: applicable key: 0x" << d_helper->convert_to_hex_string(applicable_key) << "TOW="<<static_cast<int>(tag.TOW + 30);
+        }
+        else  // ADKD 12
+            {
+                applicable_key = d_tesla_keys[tag.TOW + 330];
+                LOG(INFO) << "|---> Galileo OSNMA :: applicable key: 0x" << d_helper->convert_to_hex_string(applicable_key) << "TOW="<<static_cast<int>(tag.TOW + 330);
+            }
+
 
     if (d_osnma_data.d_dsm_kroot_message.mf == 0)  // C: HMAC-SHA-256
         {
@@ -1142,7 +1166,7 @@ bool osnma_msg_receiver::verify_tag(Tag& tag)
                       << ", PRNa="
                       << static_cast<unsigned>(tag.PRNa)
                       << ", PRNd="
-                      << static_cast<unsigned>(tag.PRN_d);
+                      << static_cast<unsigned>(tag.PRN_d) << std::endl;
             return true;
         }
     return false;
@@ -1171,10 +1195,12 @@ std::vector<uint8_t> osnma_msg_receiver::build_message(const Tag& tag)
     if (tag.ADKD == 0 || tag.ADKD == 12)  // note: for ADKD=12 still the same logic applies. Only the Key selection is shifted 10 Subframes into the future.
         {
             applicable_nav_data = d_satellite_nav_data[tag.PRN_d][tag.TOW - 30].ephemeris_iono_vector_2;
+            LOG(INFO) << "|---> Galileo OSNMA :: applicable NavData (PRN_d="<< static_cast<int>(tag.PRN_d) << ", TOW=" << tag.TOW - 30 <<"): 0b" << applicable_nav_data;
         }
     else if (tag.ADKD == 4)
         {
             applicable_nav_data = d_satellite_nav_data[tag.PRN_d][tag.TOW - 30].utc_vector_2;
+            LOG(INFO) << "|---> Galileo OSNMA :: applicable NavData (PRN_d="<< static_cast<int>(tag.PRN_d)  << ", TOW=" << tag.TOW - 30 <<"): 0b" << applicable_nav_data;
         }
     else
         LOG(WARNING) << "Galileo OSNMA :: Tag verification :: unknown ADKD";
@@ -1484,7 +1510,7 @@ bool osnma_msg_receiver::tag_has_nav_data_available(Tag& t)
     if (prn_it != d_satellite_nav_data.end())
         {
             // PRN was found, check if TOW exists in inner map
-            LOG(INFO) << "Galileo OSNMA: hasData = true " << std::endl;
+            //LOG(INFO) << "Galileo OSNMA: hasData = true " << std::endl;
             std::map<uint32_t, NavData>& tow_map = prn_it->second;
             auto tow_it = tow_map.find(t.TOW - 30);
             if (tow_it != tow_map.end())
@@ -1500,7 +1526,7 @@ bool osnma_msg_receiver::tag_has_nav_data_available(Tag& t)
     else
         {
             // PRN was not found
-            LOG(INFO) << "Galileo OSNMA: hasData = false " << std::endl;
+            //LOG(INFO) << "Galileo OSNMA: hasData = false " << std::endl;
             return false;
         }
     return false;
@@ -1519,7 +1545,7 @@ bool osnma_msg_receiver::tag_has_key_available(Tag& t)
             auto it = d_tesla_keys.find(t.TOW + 30);
             if (it != d_tesla_keys.end())
                 {
-                    LOG(INFO) << "Galileo OSNMA: hasKey = true " << std::endl;
+                    //LOG(INFO) << "Galileo OSNMA: hasKey = true " << std::endl;
                     return true;
                 }
         }
@@ -1528,11 +1554,11 @@ bool osnma_msg_receiver::tag_has_key_available(Tag& t)
             auto it = d_tesla_keys.find(t.TOW + 330);
             if (it != d_tesla_keys.end())
                 {
-                    LOG(INFO) << "Galileo OSNMA: hasKey = true " << std::endl;
+                    //LOG(INFO) << "Galileo OSNMA: hasKey = true " << std::endl;
                     return true;
                 }
         }
-    LOG(INFO) << "Galileo OSNMA: hasKey = false ";
+    //LOG(INFO) << "Galileo OSNMA: hasKey = false ";
     return false;
 }
 
@@ -1601,6 +1627,6 @@ std::vector<uint8_t> osnma_msg_receiver::hash_chain(uint32_t num_of_hashes_neede
     // compare computed current key against received key
     auto end = std::chrono::high_resolution_clock::now();
     std::chrono::duration<double> elapsed = end - start;
-    LOG(INFO) << "Galileo OSNMA: TESLA verification (" << num_of_hashes_needed << " hashes) took " << elapsed.count() << " seconds.";
+//    LOG(INFO) << "Galileo OSNMA: TESLA verification (" << num_of_hashes_needed << " hashes) took " << elapsed.count() << " seconds.";
     return K_II;
 }
diff --git a/src/core/libs/osnma_msg_receiver.h b/src/core/libs/osnma_msg_receiver.h
index 4e662ef55..aef9fd91e 100644
--- a/src/core/libs/osnma_msg_receiver.h
+++ b/src/core/libs/osnma_msg_receiver.h
@@ -59,7 +59,7 @@ 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& 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>& osnma_msg);
diff --git a/src/core/system_parameters/galileo_inav_message.cc b/src/core/system_parameters/galileo_inav_message.cc
index baa140149..a8509db23 100644
--- a/src/core/system_parameters/galileo_inav_message.cc
+++ b/src/core/system_parameters/galileo_inav_message.cc
@@ -1417,7 +1417,7 @@ OSNMA_msg Galileo_Inav_Message::get_osnma_msg()
     nma_position_filled = std::array<int8_t, 15>{};
     // Fill TOW and WN
     nma_msg.WN_sf0 = WN_0;
-    int32_t TOW_sf0 = TOW_5 - 24; // according to OS SIS ICD, TOW of word 5 is 25 seconds after Sf start
+    int32_t TOW_sf0 = TOW_5 - 25;//- 24; // according to OS SIS ICD, TOW of word 5 is 25 seconds after Sf start TODO review
     if (TOW_sf0 < 0)
         {
             TOW_sf0 += 604800;
diff --git a/src/core/system_parameters/osnma_helper.cc b/src/core/system_parameters/osnma_helper.cc
index aa338f408..7cf3165c2 100644
--- a/src/core/system_parameters/osnma_helper.cc
+++ b/src/core/system_parameters/osnma_helper.cc
@@ -16,6 +16,8 @@
 
 #include "osnma_helper.h"
 #include <bitset>
+#include <iomanip>
+#include <ios>
 
 uint32_t Osnma_Helper::compute_gst(uint32_t WN, uint32_t TOW) const
 {
@@ -74,3 +76,12 @@ std::string Osnma_Helper::verification_status_str(int status)
             default: return "UNKNOWN";
             }
 }
+std::string Osnma_Helper::convert_to_hex_string(const std::vector<uint8_t>& vector)
+{
+    std::stringstream ss;
+    ss << std::hex << std::setfill('0');
+    for (auto byte : vector) {
+            ss << std::setw(2) << static_cast<int>(byte);
+        }
+    return ss.str();
+}
diff --git a/src/core/system_parameters/osnma_helper.h b/src/core/system_parameters/osnma_helper.h
index 2f8df078b..e78fdba0f 100644
--- a/src/core/system_parameters/osnma_helper.h
+++ b/src/core/system_parameters/osnma_helper.h
@@ -30,6 +30,7 @@ public:
     std::vector<uint8_t> gst_to_uint8(uint32_t GST) const;
     std::vector<uint8_t> bytes(const std::string& binaryString);
     std::string verification_status_str(int status);
+    std::string convert_to_hex_string(const std::vector<uint8_t>& vector);
 };
 
 #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 35cb9fd45..7b94baf0c 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
@@ -2,6 +2,7 @@
 #include <bitset>
 #include <filesystem>
 #include <fstream>
+#include <logging.h>
 #include <osnma_msg_receiver.h>
 #include <vector>
 
@@ -14,42 +15,39 @@ struct TestVector
 };
 
 // TODO - parametrize class for different configurations (config_1, config_2, etc.. potentially 5 or 6 more) an make sure wont affect current TEST_F
+// note: until the test is parametrized for configuration 1 and 2, in order to change between them you have to comment/uncomment the respective calls in this test, identified with comments // conf. 1/2
+// log_name, input_time, crtFilePath, merkleFilePath, testVectors
 class OsnmaMsgReceiverTest : public ::testing::Test
 {
 protected:
     osnma_msg_receiver_sptr osnma;
-    Galileo_Inav_Message galileo_message;
-    uint8_t page_position_in_inav_subframe;
-    bool flag_CRC_test;
-    std::string page_even;
     OSNMA_msg osnma_msg{};
     std::array<int8_t, 15> nma_position_filled;
-    uint32_t d_GST_SIS{};  // 16 AUG 2023 05 00 01
+    uint32_t d_GST_SIS{};
     uint32_t TOW{};
     uint32_t WN{};
-    std::tm GST_START_EPOCH = {0, 0, 0, 22, 8 - 1, 1999 - 1900, 0};  // months start with 0 and years since 1900 in std::tm
-    const uint32_t LEAP_SECONDS = 0;                                 // 13 + 5;
+    std::tm GST_START_EPOCH = {0, 0, 0, 22, 8 - 1, 1999 - 1900, 0}; // months start with 0 and years since 1900 in std::tm
+    const uint32_t LEAP_SECONDS = 0; //13 + 5;
     void set_time(std::tm& input);
-    //    std::string log_name {"CONFIG1-2023-08-23-PKID1-OSNMA"};
-    std::string log_name{"CONFIG2-2023-07-27-PKID2-MT2-OSNMA"};
-    // void initializeGoogleLog();
+//    std::string log_name {"CONFIG1-2023-08-16-PKID1-OSNMA"};
+    std::string log_name {"CONFIG2-2023-07-27-PKID2-MT2-OSNMA"};
+    void initializeGoogleLog();
 
     void SetUp() override
     {
-        flag_CRC_test = false;  // TODO what for?
-        page_even = "";
-
-        //        std::tm input_time = {0, 0, 5, 16, 8 - 1, 2023 - 1900, 0};
-        std::tm input_time = {0, 0, 0, 27, 7 - 1, 2023 - 1900, 0};
+        initializeGoogleLog();
+//        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);
-        //        std::string pemFilePath = "/home/cgm/CLionProjects/osnma/data/OSNMA_PublicKey_20230803105952_newPKID_1.pem";
-        //        std::string merkleFilePath = "/home/cgm/CLionProjects/osnma/data/OSNMA_MerkleTree_20230803105953_newPKID_1.xml";
-        std::string pemFilePath = "/home/cgm/CLionProjects/osnma/data/OSNMA_PublicKey_20230720113300_newPKID_2.pem";
+//        std::string crtFilePath = "/home/cgm/CLionProjects/osnma/data/OSNMA_PublicKey_20230803105952_newPKID_1.crt"; // conf. 1
+//        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(pemFilePath, merkleFilePath);
+        osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
     }
 
 public:
+
     static std::vector<uint8_t> parseNavBits(const std::string& hex);
     static std::vector<TestVector> readTestVectorsFromFile(const std::string& filename);
     std::string bytes_to_str(const std::vector<uint8_t>& bytes);
@@ -57,85 +55,199 @@ public:
 };
 
 
-TEST_F(OsnmaMsgReceiverTest, TeslaKeyVerification)
+TEST_F(OsnmaMsgReceiverTest, BuildTagMessageM0)
 {
+    // Arrange
+    // ----------
+    // m0
+    std::vector<uint8_t> expected_message =  {
+        0x02, 0x4E, 0x05, 0x46, 0x3C, 0x01, 0x83, 0xA5, 0x91, 0x05, 0x1D, 0x69, 0x25, 0x80, 0x07, 0x6B,
+        0x3E, 0xEA, 0x81, 0x41, 0xBF, 0x03, 0xAD, 0xCB, 0x5A, 0xAD, 0xB2, 0x77, 0xAF, 0x6F, 0xCF, 0x21,
+        0xFB, 0x98, 0xFF, 0x7E, 0x83, 0xAF, 0xFC, 0x37, 0x02, 0x03, 0xB0, 0xD8, 0xE1, 0x0E, 0xB1, 0x4D,
+        0x11, 0x18, 0xE6, 0xB0, 0xE8, 0x20, 0x01, 0xA0, 0x00, 0xE5, 0x91, 0x00, 0x06, 0xD3, 0x1F, 0x00,
+        0x02, 0x68, 0x05, 0x4A, 0x02, 0xC2, 0x26, 0x07, 0xF7, 0xFC, 0x00
+    };
+
+    uint32_t TOW_Tag0 = 345660;
+    uint32_t TOW_NavData = TOW_Tag0 - 30;
+    uint32_t TOW_Key_Tag0 = TOW_Tag0 + 30 ;
+    uint32_t WN = 1248;
+    uint32_t PRNa = 2;
+    uint8_t CTR = 1;
+
+    osnma->d_osnma_data.d_dsm_kroot_message.ts = 9; // 40 bit
+    osnma->d_tesla_keys[TOW_Key_Tag0] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDB, 0xBC, 0x73}; // K4
+    osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
+    osnma->d_satellite_nav_data[PRNa][TOW_NavData].ephemeris_iono_vector_2 = "000011101001011001000100000101000111010110100100100101100000000000011101101011001111101110101010000001010000011011111100000011101011011100101101011010101011011011001001110111101011110110111111001111001000011111101110011000111111110111111010000011101011111111110000110111000000100000001110110000110110001110000100001110101100010100110100010001000110001110011010110000111010000010000000000001101000000000000011100101100100010000000000000110110100110001111100000000000000100110100000000101010010100000001011000010001001100000011111110111111111000000000";
+    osnma->d_osnma_data.d_nma_header.nmas = 0b10;
+
+    MACK_tag_and_info MTI;
+    MTI.tag = static_cast<uint64_t>(0xE37BC4F858);
+    MTI.tag_info.PRN_d = 0x02;
+    MTI.tag_info.ADKD = 0x00;
+    MTI.tag_info.cop = 0x0F;
+    Tag t0(MTI, TOW_Tag0, WN, PRNa, CTR);
+
+
+
+    // Act
+    // ----------
+    auto computed_message = osnma->build_message(t0);
+
+
+    // Assert
+    // ----------
+    ASSERT_TRUE(computed_message == expected_message);
+
+}
+
+TEST_F(OsnmaMsgReceiverTest, TagVerification) {
+    // Arrange
+    // ----------
+    // Tag0
+    uint32_t TOW_Tag0 = 345660;
+    uint32_t TOW_NavData = TOW_Tag0 - 30;
+    uint32_t TOW_Key_Tag0 = TOW_Tag0 + 30 ;
+    uint32_t WN = 1248;
+    uint32_t PRNa = 2;
+    uint8_t CTR = 1;
+
+    osnma->d_osnma_data.d_dsm_kroot_message.ts = 9; // 40 bit
+    osnma->d_tesla_keys[TOW_Key_Tag0] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73}; // K4
+    osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
+    osnma->d_satellite_nav_data[PRNa][TOW_NavData].ephemeris_iono_vector_2 = "000011101001011001000100000101000111010110100100100101100000000000011101101011001111101110101010000001010000011011111100000011101011011100101101011010101011011011001001110111101011110110111111001111001000011111101110011000111111110111111010000011101011111111110000110111000000100000001110110000110110001110000100001110101100010100110100010001000110001110011010110000111010000010000000000001101000000000000011100101100100010000000000000110110100110001111100000000000000100110100000000101010010100000001011000010001001100000011111110111111111000000000";
+    osnma->d_osnma_data.d_nma_header.nmas = 0b10;
+
+    MACK_tag_and_info MTI;
+    MTI.tag = static_cast<uint64_t>(0xE37BC4F858);
+    MTI.tag_info.PRN_d = 0x02;
+    MTI.tag_info.ADKD = 0x00;
+    MTI.tag_info.cop = 0x0F;
+    Tag t0(MTI, TOW_Tag0, WN, PRNa, CTR);
+
+
+
+    // Act
+    // ----------
+    bool result_tag0 = osnma->verify_tag(t0);
+
+
+
+
+
+    // Assert
+    // ----------
+    //ASSERT_TRUE(result_tag0);
+
+    // Tag3
+    uint32_t TOW_Tag3 = 345660;
+    uint32_t TOW_NavData_Tag3 = TOW_Tag3 - 30;
+    uint32_t TOW_Key_Tag3 = TOW_Tag0 + 30 ;
+    WN = 1248;
+    PRNa = 2;
+    CTR = 3;
+
+    osnma->d_osnma_data.d_dsm_kroot_message.ts = 9; // 40 bit
+    osnma->d_tesla_keys[TOW_Key_Tag3] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73}; // K4
+    osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
+    osnma->d_satellite_nav_data[PRNa][TOW_NavData].utc_vector_2 =
+        "111111111111111111111111111111110000000000000000000000010001001001001000"
+        "111000001000100111100010010111111111011110111111111001001100000100000000";
+    osnma->d_osnma_data.d_nma_header.nmas = 0b10;
+
+    MTI.tag = static_cast<uint64_t>(0x7BB238C883);
+    MTI.tag_info.PRN_d = 0x02;
+    MTI.tag_info.ADKD = 0x04;
+    MTI.tag_info.cop = 0x0F;
+    Tag t3(MTI, TOW_Tag0, WN, PRNa, CTR);
+
+    bool result_tag3 = osnma->verify_tag(t3);
+
+    ASSERT_TRUE(result_tag0 && result_tag3);
+
+}
+
+TEST_F(OsnmaMsgReceiverTest, TeslaKeyVerification) {
     // Arrange
     // ----------
     osnma->d_tesla_key_verified = false;
-    osnma->d_osnma_data.d_dsm_kroot_message.kroot = {0x5B, 0xF8, 0xC9, 0xCB, 0xFC, 0xF7, 0x04, 0x22, 0x08, 0x14, 0x75, 0xFD, 0x44, 0x5D, 0xF0, 0xFF};  // Kroot, TOW 345570 GST_0 - 30
-    osnma->d_osnma_data.d_dsm_kroot_message.ks = 4;                                                                                                    // TABLE 10 --> 128 bits
+    osnma->d_osnma_data.d_dsm_kroot_message.kroot = {0x5B, 0xF8, 0xC9, 0xCB, 0xFC, 0xF7, 0x04, 0x22, 0x08, 0x14, 0x75, 0xFD, 0x44, 0x5D, 0xF0, 0xFF}; // Kroot, TOW 345570 GST_0 - 30
+    osnma->d_osnma_data.d_dsm_kroot_message.ks = 4; // TABLE 10 --> 128 bits
     osnma->d_osnma_data.d_dsm_kroot_message.alpha = 0x610BDF26D77B;
     // local_time_verification would do this operation. TODO - eliminate duplication.
     osnma->d_GST_SIS = (1248 & 0x00000FFF) << 20 | (345630 & 0x000FFFFF);
-    osnma->d_GST_0 = ((1248 & 0x00000FFF) << 20 | (345600 & 0x000FFFFF));                                 // applicable time (GST_Kroot + 30)
-    osnma->d_receiver_time = osnma->d_GST_0 + 30 * std::floor((osnma->d_GST_SIS - osnma->d_GST_0) / 30);  // Eq. 3 R.G.//345630;
+    osnma->d_GST_0 = ((1248  & 0x00000FFF) << 20 | (345600 & 0x000FFFFF)); // applicable time (GST_Kroot + 30)
+    osnma->d_receiver_time =  osnma->d_GST_0 + 30 * std::floor((osnma->d_GST_SIS - osnma->d_GST_0) / 30); // Eq. 3 R.G.//345630;
 
-    osnma->d_tesla_keys.insert((std::pair<uint32_t, std::vector<uint8_t>>(345600, {0xEF, 0xF9, 0x99, 0x04, 0x0E, 0x19, 0xB5, 0x70, 0x83, 0x50, 0x60, 0xBE, 0xBD, 0x23, 0xED, 0x92})));  // K1, not needed, just for reference.
-    std::vector<uint8_t> key = {0x2D, 0xC3, 0xA3, 0xCD, 0xB1, 0x17, 0xFA, 0xAD, 0xB8, 0x3B, 0x5F, 0x0B, 0x6F, 0xEA, 0x88, 0xEB};                                                        // K2
+    osnma->d_tesla_keys.insert((std::pair<uint32_t, std::vector<uint8_t>>(345600,{0xEF, 0xF9, 0x99, 0x04, 0x0E, 0x19, 0xB5, 0x70, 0x83, 0x50, 0x60, 0xBE, 0xBD, 0x23, 0xED, 0x92}))); // K1, not needed, just for reference.
+    std::vector<uint8_t> key = {0x2D, 0xC3, 0xA3, 0xCD, 0xB1, 0x17, 0xFA, 0xAD, 0xB8, 0x3B, 0x5F, 0x0B, 0x6F, 0xEA, 0x88, 0xEB}; // K2
     uint32_t TOW = 345630;
 
+
+
+
     // Act
     // ----------
     bool result = osnma->verify_tesla_key(key, TOW);
 
+
+
+
+
     // Assert
     // ----------
     ASSERT_TRUE(result);
-}
 
+}
 
 TEST_F(OsnmaMsgReceiverTest, OsnmaTestVectorsSimulation)
 {
-    // initializeGoogleLog();
     // Arrange
     // ----------
-    //    std::vector<TestVector> testVectors = readTestVectorsFromFile("/home/cgm/CLionProjects/osnma/data/16_AUG_2023_GST_05_00_01.csv");
-    std::vector<TestVector> testVectors = readTestVectorsFromFile("/home/cgm/CLionProjects/osnma/data/27_JUL_2023_GST_00_00_01.csv");
-    if (testVectors.empty())
-        {
+//    std::vector<TestVector> testVectors = readTestVectorsFromFile("/home/cgm/CLionProjects/osnma/data/16_AUG_2023_GST_05_00_01.csv"); // conf. 1
+    std::vector<TestVector> testVectors = readTestVectorsFromFile("/home/cgm/CLionProjects/osnma/data/27_JUL_2023_GST_00_00_01.csv"); // conf. 2
+    if (testVectors.empty()){
             ASSERT_TRUE(false);
         }
 
     bool end_of_hex_stream{false};
     int offset_byte{0};
-    int byte_index{0};                                                     // index containing the last byte position of the hex stream that was retrieved. Takes advantage that all TVs have same size
-    const int SIZE_PAGE_BYTES{240 / 8};                                    // total bytes of a page
-    const int SIZE_SUBFRAME_PAGES{15};                                     // number of pages of a subframe
-    const int SIZE_SUBFRAME_BYTES{SIZE_PAGE_BYTES * SIZE_SUBFRAME_PAGES};  // total bytes of a subframe
-    const int DURATION_SUBFRAME{30};                                       // duration of a subframe, in seconds
+    int byte_index{0}; // index containing the last byte position of the hex stream that was retrieved. Takes advantage that all TVs have same size
+    const int SIZE_PAGE_BYTES{240/8}; // total bytes of a page
+    const int SIZE_SUBFRAME_PAGES{15}; // number of pages of a subframe
+    const int SIZE_SUBFRAME_BYTES{SIZE_PAGE_BYTES*SIZE_SUBFRAME_PAGES}; // total bytes of a subframe
+    const int DURATION_SUBFRAME{30}; // duration of a subframe, in seconds
 
     const int DUMMY_PAGE{63};
     bool flag_dummy_page{false};
-    std::cout << "OsnmaTestVectorsSimulation:"
-              << " d_GST_SIS= " << d_GST_SIS
-              << ", TOW=" << TOW
-              << ", WN=" << WN << std::endl;
+    std::cout << "OsnmaTestVectorsSimulation:" << " d_GST_SIS= " << d_GST_SIS
+    << ", TOW=" << TOW
+    << ", WN=" << WN << std::endl;
+
+
+
 
 
     // Act
     // ----------
 
     // loop over all bytes of data. Note: all TestVectors have same amount of data.
-    while (end_of_hex_stream == false)
-        {
+    while (end_of_hex_stream == false){
             // loop over all SVs, extract a subframe
-            for (const TestVector& tv : testVectors)
-                {  // loop over all SVs, extract a subframe
-                    std::cout << "OsnmaTestVectorsSimulation: SVID (PRN_a) " << tv.svId << std::endl;
+            for(const TestVector& tv : testVectors) { // loop over all SVs, extract a subframe
+                    std::cout << "OsnmaTestVectorsSimulation: SVID (PRN_a) "<< tv.svId << std::endl;
                     auto osnmaMsg_sptr = std::make_shared<OSNMA_msg>();
                     std::array<uint8_t, 15> hkroot{};
                     std::array<uint32_t, 15> mack{};
-                    byte_index = offset_byte;                   // reset byte_index to the offset position for the next test vector. Offset is updated at the end of each Subframe (every 30 s or 450 Bytes)
-                    std::map<uint8_t, std::bitset<128>> words;  // structure containing <WORD_NUMBER> and <EXTRACTED_BITS>
+                    byte_index = offset_byte; // reset byte_index to the offset position for the next test vector. Offset is updated at the end of each Subframe (every 30 s or 450 Bytes)
+                    std::map<uint8_t, std::bitset<128>> words_for_OSNMA; // structure containing <WORD_NUMBER> and <EXTRACTED_BITS>
 
-                    for (int idx = 0; idx < SIZE_SUBFRAME_PAGES; ++idx)  // extract all pages of a subframe
+                    for (int idx = 0; idx < SIZE_SUBFRAME_PAGES; ++idx)    // extract all pages of a subframe
                         {
                             // extract bytes of complete page (odd+even) -- extract SIZE_PAGE from tv.navBits, starting from byte_index
-                            std::vector<uint8_t> page_bytes = extract_page_bytes(tv, byte_index, SIZE_PAGE_BYTES);
-                            if (page_bytes.empty())
-                                {
-                                    std::cout << "OsnmaTestVectorsSimulation: end of TestVectors \n"
-                                              << "byte_index=" << byte_index << " expected= " << 432000 / 8 << std::endl;
+                            std::vector<uint8_t> page_bytes = extract_page_bytes(tv,byte_index,SIZE_PAGE_BYTES);
+                            if(page_bytes.empty()){
+                                    std::cout<< "OsnmaTestVectorsSimulation: end of TestVectors \n" << "byte_index="<<byte_index<< " expected= " << 432000/8 << std::endl;
                                     end_of_hex_stream = true;
                                     break;
                                 }
@@ -143,37 +255,33 @@ TEST_F(OsnmaMsgReceiverTest, OsnmaTestVectorsSimulation)
                             std::string page_bits = bytes_to_str(page_bytes);
                             // Extract the 40 OSNMA bits starting from the 18th bit
                             std::string even_page = page_bits.substr(0, page_bits.size() / 2);
-                            ;
-                            std::string odd_page = page_bits.substr(page_bits.size() / 2);
-                            if (even_page.size() < 120 || odd_page.size() < 120)
-                                {
-                                    std::cout << "OsnmaTestVectorsSimulation: error parsing pages" << std::endl;
+                            std::string odd_page = page_bits.substr( page_bits.size() / 2);
+                            if(even_page.size() < 120 || odd_page.size() < 120 ){
+                                    std::cout<< "OsnmaTestVectorsSimulation: error parsing pages" << std::endl;
                                 }
                             bool even_odd_OK = even_page[0] == '0' && odd_page[0] == '1';
                             bool page_type_OK = even_page[1] == '0' && odd_page[1] == '0';
                             bool tail_bits_OK = even_page.substr(even_page.size() - 6) == "000000" && odd_page.substr(odd_page.size() - 6) == "000000";
-                            if (!even_odd_OK || !page_type_OK || !tail_bits_OK)
-                                std::cerr << "OsnmaTestVectorsSimulation: error parsing pages." << std::endl;
+                            if(!even_odd_OK || !page_type_OK || !tail_bits_OK)
+                                std::cerr<< "OsnmaTestVectorsSimulation: error parsing pages." << std::endl;
 
-                            std::bitset<112> data_k(even_page.substr(2, 112));
-                            std::bitset<16> data_j(odd_page.substr(2, 16));
+                            std::bitset<112> data_k(even_page.substr(2,112));
+                            std::bitset<16> data_j(odd_page.substr(2,16));
                             std::bitset<112> shifted_data_k = data_k;
-                            //                            uint8_t word_type = 0;
-                            //                            for(int i = 0; i < 6; ++i) {
-                            //                                    word_type |= (data_k[104 + i] << i);
-                            //                                }
-                            uint8_t word_type = static_cast<uint8_t>((shifted_data_k >>= 106).to_ulong());  // word type is the first 6 bits of the word
-                            std::cout << "OsnmaTestVectorsSimulation: received Word " << static_cast<int>(word_type) << std::endl;
-                            if ((word_type >= 1 && word_type <= 5) || word_type == 6 || word_type == 10)
+                            uint8_t word_type = static_cast<uint8_t>((shifted_data_k >>= 106).to_ulong()); // word type is the first 6 bits of the word
+                            std::cout<< "OsnmaTestVectorsSimulation: received Word "<< static_cast<int>(word_type) << std::endl;
+                            if( (word_type >= 1 && word_type <=5) || word_type == 6 || word_type == 10)
                                 {
                                     // store raw word
                                     std::bitset<128> data_combined(data_k.to_string() + data_j.to_string());
-                                    words[word_type] = data_combined;
+                                    words_for_OSNMA[word_type] = data_combined;
                                 }
-                            if (word_type == DUMMY_PAGE)
+                            if(word_type == DUMMY_PAGE)
                                 flag_dummy_page = true;
+
                             // place it into osnma object.
                             std::bitset<40> osnmaBits(odd_page.substr(18, 40));
+
                             // Extract bits for hkroot and mack
                             std::bitset<8> hkrootBits(osnmaBits.to_string().substr(0, 8));
                             std::bitset<32> mackBits(osnmaBits.to_string().substr(8, 32));
@@ -183,13 +291,12 @@ TEST_F(OsnmaMsgReceiverTest, OsnmaTestVectorsSimulation)
                             byte_index += SIZE_PAGE_BYTES;
                         }
 
-                    std::cout << "----------" << std::endl;
-                    if (end_of_hex_stream)
+                    std::cout<< "----------" << std::endl;
+                    if(end_of_hex_stream)
                         break;
-                    if (flag_dummy_page)
-                        {
+                    if(flag_dummy_page){
                             flag_dummy_page = false;
-                            continue;  // skip this SV
+                            continue; // skip this SV
                         }
 
                     // Fill osnma object
@@ -197,26 +304,26 @@ TEST_F(OsnmaMsgReceiverTest, OsnmaTestVectorsSimulation)
                     osnmaMsg_sptr->mack = mack;
 
                     osnmaMsg_sptr->TOW_sf0 = d_GST_SIS & 0x000FFFFF;
-                    osnmaMsg_sptr->WN_sf0 = (d_GST_SIS & 0xFFF00000) >> 20;
-                    osnmaMsg_sptr->PRN = tv.svId;  // PRNa
+                    osnmaMsg_sptr->WN_sf0 = (d_GST_SIS & 0xFFF00000) >> 20 ;
+                    osnmaMsg_sptr->PRN = tv.svId; // PRNa
 
                     // TODO - refactor this logic, currently it is split
 
-                    // check if words 1--> 5 words are received => fill EphClockStatus data vector
+                    // check if words_for_OSNMA 1--> 5 words_for_OSNMA are received => fill EphClockStatus data vector
                     bool ephClockStatusWordsReceived = true;
                     for (int i = 1; i <= 5; ++i)
                         {
-                            if (words.find(i) == words.end())
+                            if (words_for_OSNMA.find(i) == words_for_OSNMA.end())
                                 {
                                     ephClockStatusWordsReceived = false;
-                                    std::cerr << "OsnmaTestVectorsSimulation: error parsing words 1->5. "
-                                                 "Word "
-                                              << i << " should be received for each subframe but was not." << std::endl;
+                                    std::cerr<< "OsnmaTestVectorsSimulation: error parsing words_for_OSNMA 1->5. "
+                                                 "Word "<< i << " should be received for each subframe but was not." << std::endl;
                                 }
                         }
                     // extract bits as needed by osnma block
-                    if (ephClockStatusWordsReceived)
+                    if(ephClockStatusWordsReceived)
                         {
+
                             // Define the starting position and length of bits to extract for each word
                             std::map<uint8_t, std::pair<uint8_t, uint8_t>> extractionParams = {
                                 {1, {6, 120}},
@@ -226,41 +333,41 @@ TEST_F(OsnmaMsgReceiverTest, OsnmaTestVectorsSimulation)
                                 {5, {6, 67}},
                             };
 
-                            // Fill NavData bits -- Iterate over the extraction parameters                            // Fill NavData bits -- Iterate over the extraction parameters
+                            // Fill NavData bits -- Iterate over the extraction parameters
                             std::string nav_data_ADKD_0_12 = "";
-                            for (const auto& param : extractionParams)
-                                {
+                            for (const auto& param : extractionParams) {
                                     uint8_t wordKey = param.first;
                                     uint8_t start = param.second.first;
                                     uint8_t length = param.second.second;
 
                                     // Extract the required bits and fill osnma block
-                                    nav_data_ADKD_0_12 += words[wordKey].to_string().substr(start, length);
+                                    nav_data_ADKD_0_12 += words_for_OSNMA[wordKey].to_string().substr(start, length);
                                 }
                             // send to osnma block
                             bool check_size_is_ok = nav_data_ADKD_0_12.size() == 549;
-                            if (check_size_is_ok)
+                            if(check_size_is_ok)
                                 {
-                                    std::cout << "Galileo OSNMA: sending ADKD=0/12 navData, PRN_d (" << tv.svId << ") "
-                                              << "TOW_sf=" << osnmaMsg_sptr->TOW_sf0 << std::endl;
-                                    const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string, uint32_t>>(  // < PRNd , navDataBits, TOW_Sosf>
+                                    std::cout << "Galileo OSNMA: sending ADKD=0/12 navData, PRN_d (" << tv.svId << ") " << "TOW_sf=" << osnmaMsg_sptr->TOW_sf0 <<std::endl;
+                                    const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string,uint32_t>>( // < PRNd , navDataBits, TOW_Sosf>
                                         tv.svId,
                                         nav_data_ADKD_0_12,
                                         osnmaMsg_sptr->TOW_sf0);
+                                    LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d="<< static_cast<int>(tv.svId) << ", TOW=" << static_cast<int>(osnmaMsg_sptr->TOW_sf0) <<"): 0b" << nav_data_ADKD_0_12;
                                     osnma->msg_handler_osnma(pmt::make_any(tmp_obj_osnma));
+
                                 }
                         }
 
                     // check w6 && w10 is received => fill TimingData data vector
-                    bool timingWordsReceived = words.find(6) != words.end() &&
-                                               words.find(10) != words.end();
+                    bool timingWordsReceived = words_for_OSNMA.find(6) != words_for_OSNMA.end() &&
+                                               words_for_OSNMA.find(10) != words_for_OSNMA.end();
                     // extract bits as needed by osnma block
-                    if (timingWordsReceived)
-                        {
+                    if(timingWordsReceived){
                             // Define the starting position and length of bits to extract for each word
                             std::map<uint8_t, std::pair<uint8_t, uint8_t>> extractionParams = {
                                 {6, {6, 99}},
-                                {10, {86, 42}}};
+                                {10, {86, 42}}
+                            };
 
                             std::string nav_data_ADKD_4 = "";
                             // Fill NavData bits -- Iterate over the extraction parameters
@@ -271,42 +378,42 @@ TEST_F(OsnmaMsgReceiverTest, OsnmaTestVectorsSimulation)
                                     uint8_t length = param.second.second;
 
                                     // Extract the required bits and fill osnma block
-                                    nav_data_ADKD_4 += words[wordKey].to_string().substr(
-                                        start, length);
+                                    nav_data_ADKD_4 += words_for_OSNMA[wordKey].to_string().substr(start, length);
                                 }
-
                             // send to osnma block
                             bool check_size_is_ok = nav_data_ADKD_4.size() == 141;
-                            if (check_size_is_ok)
+                            if(check_size_is_ok)
                                 {
-                                    std::cout << "Galileo OSNMA: sending ADKD=04 navData, PRN_d (" << tv.svId << ") "
-                                              << "TOW_sf=" << osnmaMsg_sptr->TOW_sf0 << std::endl;
-                                    const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string, uint32_t>>(  // < PRNd , navDataBits, TOW_Sosf>
+                                    std::cout << "Galileo OSNMA: sending ADKD=04 navData, PRN_d (" << tv.svId << ") " << "TOW_sf=" << osnmaMsg_sptr->TOW_sf0 <<std::endl;
+                                    const auto tmp_obj_osnma = std::make_shared<std::tuple<uint32_t, std::string,uint32_t>>( // < PRNd , navDataBits, TOW_Sosf>
                                         tv.svId,
                                         nav_data_ADKD_4,
                                         osnmaMsg_sptr->TOW_sf0);
+                                    LOG(INFO) << "|---> Galileo OSNMA :: Telemetry Decoder NavData (PRN_d="<< static_cast<int>(tv.svId) << ", TOW=" << static_cast<int>(osnmaMsg_sptr->TOW_sf0) <<"): 0b" << nav_data_ADKD_4;
                                     osnma->msg_handler_osnma(pmt::make_any(tmp_obj_osnma));
+
                                 }
+
                         }
 
                     // Call the handler, as if it came from telemetry decoder block
                     auto temp_obj = pmt::make_any(osnmaMsg_sptr);
 
-                    osnma->msg_handler_osnma(temp_obj);  // osnma entry point
+                    osnma->msg_handler_osnma(temp_obj); // osnma entry point
                 }
 
 
-            if (!end_of_hex_stream)
-                {
-                    offset_byte = byte_index;  // update offset for the next subframe
+            if(!end_of_hex_stream){
+                    offset_byte = byte_index; // update offset for the next subframe
                     d_GST_SIS += DURATION_SUBFRAME;
                     TOW = d_GST_SIS & 0x000FFFFF;
-                    WN = (d_GST_SIS & 0xFFF00000) >> 20;
-                    std::cout << "OsnmaTestVectorsSimulation:"
-                              << " d_GST_SIS= " << d_GST_SIS
+                    WN = (d_GST_SIS & 0xFFF00000) >> 20 ;
+                    std::cout << "OsnmaTestVectorsSimulation:" << " d_GST_SIS= " << d_GST_SIS
                               << ", TOW=" << TOW
                               << ", WN=" << WN << std::endl;
                 }
+
+
         }
 
 
@@ -316,24 +423,22 @@ TEST_F(OsnmaMsgReceiverTest, OsnmaTestVectorsSimulation)
     // TODO - create global vars with failed tags and compare to total tags (Tag Id for example)
 }
 
-
+// Auxiliary functions for the OsnmaTestVectorsSimulation test fixture.
+// Essentially, they perform same work as the telemetry decoder block, but adapted to the osnma-test-vector files.
 std::vector<TestVector> OsnmaMsgReceiverTest::readTestVectorsFromFile(const std::string& filename)
 {
     std::ifstream file(filename);
     std::vector<TestVector> testVectors;
-    if (!file.is_open())
-        {
-            std::cerr << "Error reading the file \"" << filename << "\" \n";
+    if (!file.is_open()) {
+            std::cerr<<"Error reading the file \"" << filename <<"\" \n";
             return testVectors;
         }
 
     std::string line;
     std::getline(file, line);
-    if (line != "SVID,NumNavBits,NavBitsHEX\r")
-        {
-            std::cerr << "Error parsing first line"
-                      << "\n";
-        }
+        if (line != "SVID,NumNavBits,NavBitsHEX\r" ){
+                std::cerr<<"Error parsing first line" <<"\n";
+            }
 
     while (std::getline(file, line))
         {
@@ -356,35 +461,29 @@ std::vector<TestVector> OsnmaMsgReceiverTest::readTestVectorsFromFile(const std:
 
     return testVectors;
 }
-
-
 std::vector<uint8_t> OsnmaMsgReceiverTest::parseNavBits(const std::string& hex)
 {
     std::vector<uint8_t> bytes;
 
-    for (unsigned int i = 0; i < hex.length() - 1; i += 2)
+    for (unsigned int i = 0; i < hex.length()-1; i += 2)
         {
             std::string byteString = hex.substr(i, 2);
-            uint8_t byte = (uint8_t)strtol(byteString.c_str(), NULL, 16);
+            uint8_t byte = (uint8_t) strtol(byteString.c_str(), NULL, 16);
             bytes.push_back(byte);
         }
     return bytes;
 }
-
-
 std::string OsnmaMsgReceiverTest::bytes_to_str(const std::vector<uint8_t>& bytes)
 {
     std::string bit_string;
     bit_string.reserve(bytes.size() * 8);
-    for (const auto& byte : bytes)
+    for(const auto& byte : bytes)
         {
             std::bitset<8> bits(byte);
             bit_string += bits.to_string();
         }
     return bit_string;
 }
-
-
 /**
  * @brief Extracts a range of bytes from a TestVector's navBits vector.
  *
@@ -413,8 +512,6 @@ std::vector<uint8_t> OsnmaMsgReceiverTest::extract_page_bytes(const TestVector&
 
     return extracted_bytes;
 }
-
-
 /**
  * @brief Sets the time based on the given input.
  *
@@ -438,118 +535,56 @@ void OsnmaMsgReceiverTest::set_time(std::tm& input)
     uint32_t sec_in_week = 7 * 24 * 60 * 60;
     uint32_t week_number = duration_sec.count() / sec_in_week;
     uint32_t time_of_week = duration_sec.count() % sec_in_week;
-    this->WN = week_number;
-    this->TOW = time_of_week + LEAP_SECONDS;
+    this->WN =  week_number;
+    this->TOW =  time_of_week + LEAP_SECONDS;
     // Return the week number and time of week as a pair
 
     // TODO: d_GST_SIS or d_receiver_time? doubt
     // I am assuming that local realisation of receiver is identical to SIS GST time coming from W5 or W0
-    this->d_GST_SIS = (this->WN & 0x00000FFF) << 20 | (this->TOW & 0x000FFFFF);
+    this->d_GST_SIS =  (this->WN  & 0x00000FFF) << 20 | (this->TOW & 0x000FFFFF);
+
+
 }
-
-
-TEST_F(OsnmaMsgReceiverTest, BuildTagMessageM0)
+void OsnmaMsgReceiverTest::initializeGoogleLog()
 {
-    // Arrange
-    // ----------
-    // m0
-    std::vector<uint8_t> expected_message = {
-        0x02, 0x4E, 0x05, 0x46, 0x3C, 0x01, 0x83, 0xA5, 0x91, 0x05, 0x1D, 0x69, 0x25, 0x80, 0x07, 0x6B,
-        0x3E, 0xEA, 0x81, 0x41, 0xBF, 0x03, 0xAD, 0xCB, 0x5A, 0xAD, 0xB2, 0x77, 0xAF, 0x6F, 0xCF, 0x21,
-        0xFB, 0x98, 0xFF, 0x7E, 0x83, 0xAF, 0xFC, 0x37, 0x02, 0x03, 0xB0, 0xD8, 0xE1, 0x0E, 0xB1, 0x4D,
-        0x11, 0x18, 0xE6, 0xB0, 0xE8, 0x20, 0x01, 0xA0, 0x00, 0xE5, 0x91, 0x00, 0x06, 0xD3, 0x1F, 0x00,
-        0x02, 0x68, 0x05, 0x4A, 0x02, 0xC2, 0x26, 0x07, 0xF7, 0xFC, 0x00};
-
-    uint32_t TOW_Tag0 = 345660;
-    uint32_t TOW_NavData = TOW_Tag0 - 30;
-    uint32_t TOW_Key_Tag0 = TOW_Tag0 + 30;
-    uint32_t WN = 1248;
-    uint32_t PRNa = 2;
-    uint8_t CTR = 1;
-
-    osnma->d_osnma_data.d_dsm_kroot_message.ts = 9;                                                                                        // 40 bit
-    osnma->d_tesla_keys[TOW_Key_Tag0] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDB, 0xBC, 0x73};  // K4
-    osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
-    osnma->d_satellite_nav_data[PRNa][TOW_NavData].ephemeris_iono_vector_2 = "000011101001011001000100000101000111010110100100100101100000000000011101101011001111101110101010000001010000011011111100000011101011011100101101011010101011011011001001110111101011110110111111001111001000011111101110011000111111110111111010000011101011111111110000110111000000100000001110110000110110001110000100001110101100010100110100010001000110001110011010110000111010000010000000000001101000000000000011100101100100010000000000000110110100110001111100000000000000100110100000000101010010100000001011000010001001100000011111110111111111000000000";
-    osnma->d_osnma_data.d_nma_header.nmas = 0b10;
-
-    MACK_tag_and_info MTI;
-    MTI.tag = static_cast<uint64_t>(0xE37BC4F858);
-    MTI.tag_info.PRN_d = 0x02;
-    MTI.tag_info.ADKD = 0x00;
-    MTI.tag_info.cop = 0x0F;
-    Tag t0(MTI, TOW_Tag0, WN, PRNa, CTR);
-
-
-    // Act
-    // ----------
-    auto computed_message = osnma->build_message(t0);
-
-
-    // Assert
-    // ----------
-    ASSERT_TRUE(computed_message == expected_message);
+    google::InitGoogleLogging(log_name.c_str());
+    FLAGS_minloglevel = 0; // INFO
+    FLAGS_logtostderr = 0;  // add this line
+    FLAGS_log_dir = "/home/cgm/CLionProjects/osnma/build/src/tests/logs";
+    if (FLAGS_log_dir.empty())
+        {
+            std::cout << "Logging will be written at "
+                      << std::filesystem::temp_directory_path()
+                      << '\n'
+                      << "Use gnss-sdr --log_dir=/path/to/log to change that.\n";
+        }
+    else
+        {
+            try
+                {
+                    const std::filesystem::path p(FLAGS_log_dir);
+                    if (!std::filesystem::exists(p))
+                        {
+                            std::cout << "The path "
+                                      << FLAGS_log_dir
+                                      << " does not exist, attempting to create it.\n";
+                            std::error_code ec;
+                            if (!std::filesystem::create_directory(p, ec))
+                                {
+                                    std::cout << "Could not create the " << FLAGS_log_dir << " folder.\n";
+                                    gflags::ShutDownCommandLineFlags();
+                                    throw std::runtime_error("Could not create folder for logs");
+                                }
+                        }
+                    std::cout << "Logging will be written at " << FLAGS_log_dir << '\n';
+                }
+            catch (const std::exception& e)
+                {
+                    std::cerr << e.what() << '\n';
+                    std::cerr << "Could not create the " << FLAGS_log_dir << " folder.\n";
+                    gflags::ShutDownCommandLineFlags();
+                    throw;
+                }
+        }
 }
 
-
-TEST_F(OsnmaMsgReceiverTest, TagVerification)
-{
-    // Arrange
-    // ----------
-    // Tag0
-    uint32_t TOW_Tag0 = 345660;
-    uint32_t TOW_NavData = TOW_Tag0 - 30;
-    uint32_t TOW_Key_Tag0 = TOW_Tag0 + 30;
-    uint32_t WN = 1248;
-    uint32_t PRNa = 2;
-    uint8_t CTR = 1;
-
-    osnma->d_osnma_data.d_dsm_kroot_message.ts = 9;                                                                                        // 40 bit
-    osnma->d_tesla_keys[TOW_Key_Tag0] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73};  // K4
-    osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
-    osnma->d_satellite_nav_data[PRNa][TOW_NavData].ephemeris_iono_vector_2 = "000011101001011001000100000101000111010110100100100101100000000000011101101011001111101110101010000001010000011011111100000011101011011100101101011010101011011011001001110111101011110110111111001111001000011111101110011000111111110111111010000011101011111111110000110111000000100000001110110000110110001110000100001110101100010100110100010001000110001110011010110000111010000010000000000001101000000000000011100101100100010000000000000110110100110001111100000000000000100110100000000101010010100000001011000010001001100000011111110111111111000000000";
-    osnma->d_osnma_data.d_nma_header.nmas = 0b10;
-
-    MACK_tag_and_info MTI;
-    MTI.tag = static_cast<uint64_t>(0xE37BC4F858);
-    MTI.tag_info.PRN_d = 0x02;
-    MTI.tag_info.ADKD = 0x00;
-    MTI.tag_info.cop = 0x0F;
-    Tag t0(MTI, TOW_Tag0, WN, PRNa, CTR);
-
-
-    // Act
-    // ----------
-    bool result_tag0 = osnma->verify_tag(t0);
-
-
-    // Assert
-    // ----------
-    // ASSERT_TRUE(result_tag0);
-
-    // Tag3
-    uint32_t TOW_Tag3 = 345660;
-    uint32_t TOW_NavData_Tag3 = TOW_Tag3 - 30;
-    uint32_t TOW_Key_Tag3 = TOW_Tag0 + 30;
-    WN = 1248;
-    PRNa = 2;
-    CTR = 3;
-
-    osnma->d_osnma_data.d_dsm_kroot_message.ts = 9;                                                                                        // 40 bit
-    osnma->d_tesla_keys[TOW_Key_Tag3] = {0x69, 0xC0, 0x0A, 0xA7, 0x36, 0x42, 0x37, 0xA6, 0x5E, 0xBF, 0x00, 0x6A, 0xD8, 0xDD, 0xBC, 0x73};  // K4
-    osnma->d_osnma_data.d_dsm_kroot_message.mf = 0;
-    osnma->d_satellite_nav_data[PRNa][TOW_NavData].utc_vector_2 =
-        "111111111111111111111111111111110000000000000000000000010001001001001000"
-        "111000001000100111100010010111111111011110111111111001001100000100000000";
-    osnma->d_osnma_data.d_nma_header.nmas = 0b10;
-
-    MTI.tag = static_cast<uint64_t>(0x7BB238C883);
-    MTI.tag_info.PRN_d = 0x02;
-    MTI.tag_info.ADKD = 0x04;
-    MTI.tag_info.cop = 0x0F;
-    Tag t3(MTI, TOW_Tag0, WN, PRNa, CTR);
-
-    bool result_tag3 = osnma->verify_tag(t3);
-
-    ASSERT_TRUE(result_tag0 && result_tag3);
-}

From c38fb0fca5756884c7f3088742226b7fa9ba64ad Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Fri, 28 Jun 2024 10:51:10 +0200
Subject: [PATCH 02/14] Fix building (add missing include)

---
 src/core/system_parameters/osnma_helper.cc | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/src/core/system_parameters/osnma_helper.cc b/src/core/system_parameters/osnma_helper.cc
index 7cf3165c2..3feeb96cf 100644
--- a/src/core/system_parameters/osnma_helper.cc
+++ b/src/core/system_parameters/osnma_helper.cc
@@ -18,6 +18,7 @@
 #include <bitset>
 #include <iomanip>
 #include <ios>
+#include <sstream>
 
 uint32_t Osnma_Helper::compute_gst(uint32_t WN, uint32_t TOW) const
 {
@@ -25,6 +26,7 @@ uint32_t Osnma_Helper::compute_gst(uint32_t WN, uint32_t TOW) const
     return GST;
 }
 
+
 std::vector<uint8_t> Osnma_Helper::gst_to_uint8(uint32_t GST) const
 {
     std::vector<uint8_t> res;
@@ -36,6 +38,7 @@ std::vector<uint8_t> Osnma_Helper::gst_to_uint8(uint32_t GST) const
     return res;
 }
 
+
 /**
  * @brief Convert a binary string to a vector of bytes.
  *
@@ -67,6 +70,7 @@ std::vector<uint8_t> Osnma_Helper::bytes(const std::string& binaryString) {
     return bytes;
 }
 
+
 std::string Osnma_Helper::verification_status_str(int status)
 {
         switch (status) {
@@ -76,6 +80,8 @@ std::string Osnma_Helper::verification_status_str(int status)
             default: return "UNKNOWN";
             }
 }
+
+
 std::string Osnma_Helper::convert_to_hex_string(const std::vector<uint8_t>& vector)
 {
     std::stringstream ss;

From 359693c0e88b92b0b34443a52bb3a5f19c143276 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@gmail.com>
Date: Fri, 28 Jun 2024 13:03:14 +0200
Subject: [PATCH 03/14] Add GNSS-SDR.osnma_enable configuration parameter, by
 default set to true

---
 src/core/receiver/gnss_flowgraph.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/receiver/gnss_flowgraph.cc b/src/core/receiver/gnss_flowgraph.cc
index acdea9df2..af1795ee0 100644
--- a/src/core/receiver/gnss_flowgraph.cc
+++ b/src/core/receiver/gnss_flowgraph.cc
@@ -121,7 +121,7 @@ void GNSSFlowgraph::init()
             galileo_tow_map_ = nullptr;
         }
 
-    if (configuration_->property("Channels_1B.count", 0) > 0)
+    if (configuration_->property("Channels_1B.count", 0) > 0 && configuration_->property("GNSS-SDR.osnma_enable", true))
         {
             enable_osnma_rx_ = true;
             const auto certFilePath = configuration_->property("GNSS-SDR.osnma_public_key", CRTFILE_DEFAULT);

From db5466832cfeae205ee0f6f18611583dfa18f222 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@cttc.es>
Date: Sat, 29 Jun 2024 11:10:35 +0200
Subject: [PATCH 04/14] Look for OpenSSL in the first place, fallback to GnuTLS
 if not found

Fix test building in some environments
---
 CMakeLists.txt                                |  94 +---
 cmake/Modules/GnssSdrCrypto.cmake             | 152 ++++++
 docs/CHANGELOG.md                             |   9 +
 src/core/libs/supl/CMakeLists.txt             |  13 +-
 src/core/libs/supl/supl.h                     |  14 +-
 src/core/system_parameters/CMakeLists.txt     |  49 +-
 src/core/system_parameters/gnss_crypto.cc     | 436 +++++++++---------
 src/core/system_parameters/gnss_crypto.h      |  15 +-
 src/tests/CMakeLists.txt                      |   3 +
 .../osnma/osnma_msg_receiver_test.cc          |  54 +--
 10 files changed, 410 insertions(+), 429 deletions(-)
 create mode 100644 cmake/Modules/GnssSdrCrypto.cmake

diff --git a/CMakeLists.txt b/CMakeLists.txt
index a97dee597..4264195a6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2196,99 +2196,9 @@ endif()
 
 
 ################################################################################
-# GnuTLS - https://www.gnutls.org/
+# OpenSSL https://www.openssl.org/ or GnuTLS - https://www.gnutls.org/
 ################################################################################
-find_package(GnuTLS)
-set_package_properties(GnuTLS PROPERTIES
-    URL "https://www.gnutls.org/"
-    PURPOSE "Used for the SUPL protocol implementation."
-    TYPE REQUIRED
-)
-if(GnuTLS_FOUND AND GNUTLS_VERSION_STRING)
-    set_package_properties(GnuTLS PROPERTIES
-        DESCRIPTION "Transport Layer Security Library (found: v${GNUTLS_VERSION_STRING})"
-    )
-else()
-    set_package_properties(GnuTLS PROPERTIES
-        DESCRIPTION "Transport Layer Security Library"
-    )
-endif()
-find_library(GNUTLS_OPENSSL_LIBRARY
-    NAMES gnutls-openssl libgnutls-openssl.so.27
-    PATHS
-        /usr/lib
-        /usr/lib64
-        /usr/lib/x86_64-linux-gnu
-        /usr/lib/aarch64-linux-gnu
-        /usr/lib/arm-linux-gnueabihf
-        /usr/lib/arm-linux-gnueabi
-        /usr/lib/i386-linux-gnu
-        /usr/lib/alpha-linux-gnu
-        /usr/lib/hppa-linux-gnu
-        /usr/lib/i386-gnu
-        /usr/lib/i686-gnu
-        /usr/lib/i686-linux-gnu
-        /usr/lib/x86_64-kfreebsd-gnu
-        /usr/lib/i686-kfreebsd-gnu
-        /usr/lib/m68k-linux-gnu
-        /usr/lib/mips-linux-gnu
-        /usr/lib/mips64el-linux-gnuabi64
-        /usr/lib/mipsel-linux-gnu
-        /usr/lib/powerpc-linux-gnu
-        /usr/lib/powerpc-linux-gnuspe
-        /usr/lib/powerpc64-linux-gnu
-        /usr/lib/powerpc64le-linux-gnu
-        /usr/lib/s390x-linux-gnu
-        /usr/lib/riscv64-linux-gnu
-        /usr/lib/sparc64-linux-gnu
-        /usr/lib/x86_64-linux-gnux32
-        /usr/lib/sh4-linux-gnu
-        /usr/lib/loongarch64-linux-gnu
-        /usr/local/lib
-        /usr/local/lib64
-        /opt/local/lib
-)
-#if(NOT GNUTLS_OPENSSL_LIBRARY)
-if(GNUTLS_OPENSSL_LIBRARY)
-    if(GnuTLS_FOUND)
-        message(STATUS " But it was not built with openssl compatibility.")
-    endif()
-    message(STATUS " Looking for OpenSSL instead...")
-    if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
-        set(OPENSSL_ROOT_DIR /usr/local/opt/openssl) # Trick for Homebrew
-    endif()
-    find_package(OpenSSL)
-    set_package_properties(OpenSSL PROPERTIES
-        URL "https://www.openssl.org"
-        DESCRIPTION "Cryptography and SSL/TLS Toolkit (found: v${OPENSSL_VERSION})"
-        PURPOSE "Used for the SUPL protocol implementation."
-        TYPE REQUIRED
-    )
-    message("OPENSSL_FOUND: " ${OPENSSL_FOUND})
-    if(OPENSSL_FOUND)
-        set_package_properties(GnuTLS PROPERTIES
-            PURPOSE "Not found, but OpenSSL can replace it."
-        )
-        set(GNUTLS_INCLUDE_DIR ${OPENSSL_INCLUDE_DIR})
-        set(GNUTLS_LIBRARIES "")
-        set(GNUTLS_OPENSSL_LIBRARY ${OPENSSL_SSL_LIBRARY})
-    else()
-        message(" The GnuTLS library with openssl compatibility enabled has not been found.")
-        message(" You can try to install the required libraries by typing:")
-        if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|kFreeBSD|GNU")
-            if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat")
-                message(" sudo yum install openssl-devel")
-            else()
-                message(" sudo apt-get install libgnutls28-dev")
-            endif()
-        endif()
-        if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
-            message(" 'sudo port install gnutls', if you are using Macports, or")
-            message(" 'brew install openssl', if you are using Homebrew.")
-        endif()
-        message(FATAL_ERROR "GnuTLS libraries with openssl compatibility are required to build gnss-sdr")
-    endif()
-endif()
+include(GnssSdrCrypto)
 
 
 
diff --git a/cmake/Modules/GnssSdrCrypto.cmake b/cmake/Modules/GnssSdrCrypto.cmake
new file mode 100644
index 000000000..33e1aaab1
--- /dev/null
+++ b/cmake/Modules/GnssSdrCrypto.cmake
@@ -0,0 +1,152 @@
+# GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
+# This file is part of GNSS-SDR.
+#
+# SPDX-FileCopyrightText: 2024 C. Fernandez-Prades cfernandez(at)cttc.es
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+################################################################################
+# OpenSSL https://www.openssl.org/
+################################################################################
+if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+    set(OPENSSL_ROOT_DIR /usr/local/opt/openssl) # Trick for Homebrew
+endif()
+find_package(OpenSSL)
+set_package_properties(OpenSSL
+    PROPERTIES
+        URL "https://www.openssl.org"
+        PURPOSE "Used for the OSNMA and SUPL protocol implementations."
+        TYPE REQUIRED
+)
+if(OPENSSL_FOUND)
+    set_package_properties(OpenSSL
+        PROPERTIES 
+            DESCRIPTION "Cryptography and SSL/TLS Toolkit (found: v${OPENSSL_VERSION})"
+    )
+else()
+    set_package_properties(OpenSSL
+        PROPERTIES 
+            DESCRIPTION "OpenSSL has not been found, but GnuTLS with openssl compatibility can replace it"
+    )
+    ################################################################################
+    # GnuTLS - https://www.gnutls.org/
+    ################################################################################
+    find_package(GnuTLS)
+    set_package_properties(GnuTLS PROPERTIES
+        URL "https://www.gnutls.org/"
+        PURPOSE "Used for the OSNMA and SUPL protocol implementations."
+        TYPE REQUIRED
+    )
+    if(GnuTLS_FOUND AND GNUTLS_VERSION_STRING)
+        set_package_properties(GnuTLS PROPERTIES
+            DESCRIPTION "Transport Layer Security Library (found: v${GNUTLS_VERSION_STRING})"
+        )
+    else()
+        set_package_properties(GnuTLS PROPERTIES
+            DESCRIPTION "Transport Layer Security Library"
+        )
+    endif()
+    find_library(GNUTLS_OPENSSL_LIBRARY
+        NAMES gnutls-openssl libgnutls-openssl.so.27
+        PATHS
+            /usr/lib
+            /usr/lib64
+            /usr/lib/x86_64-linux-gnu
+            /usr/lib/aarch64-linux-gnu
+            /usr/lib/arm-linux-gnueabihf
+            /usr/lib/arm-linux-gnueabi
+            /usr/lib/i386-linux-gnu
+            /usr/lib/alpha-linux-gnu
+            /usr/lib/hppa-linux-gnu
+            /usr/lib/i386-gnu
+            /usr/lib/i686-gnu
+            /usr/lib/i686-linux-gnu
+            /usr/lib/x86_64-kfreebsd-gnu
+            /usr/lib/i686-kfreebsd-gnu
+            /usr/lib/m68k-linux-gnu
+            /usr/lib/mips-linux-gnu
+            /usr/lib/mips64el-linux-gnuabi64
+            /usr/lib/mipsel-linux-gnu
+            /usr/lib/powerpc-linux-gnu
+            /usr/lib/powerpc-linux-gnuspe
+            /usr/lib/powerpc64-linux-gnu
+            /usr/lib/powerpc64le-linux-gnu
+            /usr/lib/s390x-linux-gnu
+            /usr/lib/riscv64-linux-gnu
+            /usr/lib/sparc64-linux-gnu
+            /usr/lib/x86_64-linux-gnux32
+            /usr/lib/sh4-linux-gnu
+            /usr/lib/loongarch64-linux-gnu
+            /usr/local/lib
+            /usr/local/lib64
+            /opt/local/lib
+    )
+
+    if(NOT GNUTLS_OPENSSL_LIBRARY)
+        message(" The GnuTLS library with openssl compatibility enabled has not been found.")
+        message(" You can try to install the required libraries by typing:")
+        if(${CMAKE_SYSTEM_NAME} MATCHES "Linux|kFreeBSD|GNU")
+            if(${LINUX_DISTRIBUTION} MATCHES "Fedora" OR ${LINUX_DISTRIBUTION} MATCHES "Red Hat")
+                message(" sudo yum install openssl-devel")
+            else()
+                message(" sudo apt-get install libgnutls28-dev")
+            endif()
+        endif()
+        if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+            message(" 'sudo port install gnutls', if you are using Macports, or")
+            message(" 'brew install openssl', if you are using Homebrew.")
+        endif()
+        message(FATAL_ERROR "OpenSSL or the GnuTLS libraries with openssl compatibility are required to build gnss-sdr")
+    endif()
+endif()
+
+################################################################################
+
+function(link_to_crypto_dependencies target)
+    if(OPENSSL_FOUND)
+        if(TARGET OpenSSL::SSL)
+            target_link_libraries(core_system_parameters
+                PUBLIC
+                    OpenSSL::SSL
+            )
+            if(TARGET OpenSSL::Crypto)
+                target_link_libraries(core_system_parameters
+                    PUBLIC
+                        OpenSSL::Crypto
+                )
+            endif()  
+        else()
+            target_link_libraries(core_system_parameters
+                PUBLIC
+                    ${OPENSSL_LIBRARIES}
+                    "${OPENSSL_CRYPTO_LIBRARIES}"
+            )
+            target_include_directories(core_system_parameters
+                PUBLIC
+                    ${OPENSSL_INCLUDE_DIR}
+            )
+        endif()
+        if(OPENSSL_VERSION)
+            if(OPENSSL_VERSION VERSION_GREATER "3.0.0")
+                target_compile_definitions(${target} PUBLIC -DUSE_OPENSSL_3=1)
+            else()
+                if(NOT OPENSSL_VERSION VERSION_LESS "1.1.1")
+                    target_compile_definitions(${target} PUBLIC -DUSE_OPENSSL_111=1)   
+                else()
+                endif()
+            endif()        
+        else()
+        endif()
+    else()  # GnuTLS
+        target_link_libraries(core_system_parameters
+            PUBLIC
+                ${GNUTLS_LIBRARIES}
+                ${GNUTLS_OPENSSL_LIBRARY}
+        )
+        target_include_directories(core_system_parameters
+            PUBLIC
+                ${GNUTLS_INCLUDE_DIR}
+        )
+        target_compile_definitions(${target} PUBLIC -DUSE_GNUTLS_FALLBACK=1)
+    endif()
+endfunction()
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index e7e594fb9..1812c76b7 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -55,6 +55,15 @@ All notable changes to GNSS-SDR will be documented in this file.
   This change has a downside in maintainability, since the source code becomes
   plagued with preprocessor directives required to maintain compatibility both
   with gflags and glog, and with Abseil.
+- Historically, GNSS-SDR linked against the GnuTLS library for cryptographic
+  functions. If GnuTLS was not found, then the building system looked for and
+  linked against OpenSSL as a fallback. This was due to the OpenSSL 1.x dual
+  license scheme, which was incompatible with GPL v3.0 license, preventing it
+  from being a mandatory dependency for GNSS-SDR in most GNU/Linux
+  distributions. This issue was solved with the release of OpenSSL 3.0.0, which
+  transitioned to the Apache License 2.0, fully compatible with GPL v3.0.
+  Accordingly, the GNSS-SDR building system now looks for OpenSSL in the first
+  place and, if not found, then it looks for GnuTLS as a fallback.
 
 ### Improvements in Usability:
 
diff --git a/src/core/libs/supl/CMakeLists.txt b/src/core/libs/supl/CMakeLists.txt
index 743852047..78ab2b80e 100644
--- a/src/core/libs/supl/CMakeLists.txt
+++ b/src/core/libs/supl/CMakeLists.txt
@@ -46,16 +46,9 @@ if(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU" AND (CMAKE_VERSION VERSION_GREATER "3
     target_compile_options(core_libs_supl PUBLIC $<$<COMPILE_LANGUAGE:C>:${MY_C_FLAGS}>)
 endif()
 
-if(OPENSSL_FOUND)
-    target_compile_definitions(core_libs_supl PUBLIC -DUSE_OPENSSL_FALLBACK=1)
-endif()
-message("OPENSSL_FOUND: " ${OPENSSL_FOUND})
-message("USE_OPENSSL_FALLBACK:" ${USE_OPENSSL_FALLBACK})
-target_link_libraries(core_libs_supl
-    PUBLIC
-        ${GNUTLS_LIBRARIES}
-        ${GNUTLS_OPENSSL_LIBRARY}
-)
+# links to the appropriate library and defines
+# USE_GNUTLS_FALLBACK, USE_OPENSSL_3, or USE_OPENSSL_111 accordingly.
+link_to_crypto_dependencies(core_libs_supl)
 
 target_include_directories(core_libs_supl
     PUBLIC
diff --git a/src/core/libs/supl/supl.h b/src/core/libs/supl/supl.h
index c879112f6..8c9bef533 100644
--- a/src/core/libs/supl/supl.h
+++ b/src/core/libs/supl/supl.h
@@ -24,18 +24,18 @@
 #define EXPORT
 #endif
 // clang-format off
-#if USE_OPENSSL_FALLBACK
-#include <openssl/crypto.h>
-#include <openssl/x509.h>
-#include <openssl/pem.h>
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#else
+#if USE_GNUTLS_FALLBACK
 #include <gnutls/gnutls.h>
 #include <gnutls/compat.h>
 #include <gnutls/crypto.h>
 #include <gnutls/openssl.h>
 #include <gnutls/x509.h>
+#else
+#include <openssl/crypto.h>
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
 #endif
 // clang-format on
 #include <PDU.h>
diff --git a/src/core/system_parameters/CMakeLists.txt b/src/core/system_parameters/CMakeLists.txt
index 0ef4cf22e..055ff256e 100644
--- a/src/core/system_parameters/CMakeLists.txt
+++ b/src/core/system_parameters/CMakeLists.txt
@@ -124,7 +124,7 @@ target_link_libraries(core_system_parameters
         Boost::date_time
         Boost::serialization
     PRIVATE
-        Pugixml::pugixml    
+        Pugixml::pugixml
 )
 
 if(ENABLE_GLOG_AND_GFLAGS)
@@ -140,50 +140,9 @@ target_include_directories(core_system_parameters
         ${GNSSSDR_SOURCE_DIR}/src/algorithms/libs
 )
 
-if(OPENSSL_FOUND)
-    message("OPENSSL_FOUND: " ${OPENSSL_FOUND})
-    if(TARGET OpenSSL::SSL)
-        target_link_libraries(core_system_parameters
-            PUBLIC
-                OpenSSL::SSL
-        )
-    else()
-        target_link_libraries(core_system_parameters
-            PUBLIC
-                ${OPENSSL_LIBRARIES}
-        )
-        target_include_directories(core_system_parameters
-            PUBLIC
-                ${OPENSSL_INCLUDE_DIR}
-        )
-    endif()
-    if(OPENSSL_VERSION)
-        message("OPENSSL_VERSION: " ${OPENSSL_VERSION})
-        if(OPENSSL_VERSION VERSION_GREATER "3.0.0")
-            target_compile_definitions(core_system_parameters PUBLIC -DUSE_OPENSSL_FALLBACK=1 -DUSE_OPENSSL_3=1)
-            message("USE_OPENSSL_3: " ${DUSE_OPENSSL_3})
-            message("USE_OPENSSL_FALLBACK:" ${USE_OPENSSL_FALLBACK})
-        else()
-            if(NOT OPENSSL_VERSION VERSION_LESS "1.1.1")
-                target_compile_definitions(core_system_parameters PRIVATE -DUSE_OPENSSL_FALLBACK=1 -DUSE_OPENSSL_111=1)
-            endif()
-        endif()
-    endif()
-
-else()
-    target_link_libraries(core_system_parameters
-        PUBLIC
-            ${GNUTLS_LIBRARIES}
-            ${GNUTLS_OPENSSL_LIBRARY}
-    )
-    target_include_directories(core_system_parameters
-        PUBLIC
-        ${GNUTLS_INCLUDE_DIR}
-    )
-endif()
-if(OPENSSL_FOUND)
-    target_compile_definitions(core_system_parameters PUBLIC -DUSE_OPENSSL_FALLBACK=1)
-endif()
+# links to the appropriate library and defines
+# USE_GNUTLS_FALLBACK, USE_OPENSSL_3, or USE_OPENSSL_111 accordingly.
+link_to_crypto_dependencies(core_system_parameters)
 
 if(ENABLE_CLANG_TIDY)
     if(CLANG_TIDY_EXE)
diff --git a/src/core/system_parameters/gnss_crypto.cc b/src/core/system_parameters/gnss_crypto.cc
index 0f1774271..a50e2f00b 100644
--- a/src/core/system_parameters/gnss_crypto.cc
+++ b/src/core/system_parameters/gnss_crypto.cc
@@ -25,7 +25,12 @@
 #include <iostream>
 #include <iterator>
 
-#if USE_OPENSSL_FALLBACK
+#if USE_GNUTLS_FALLBACK
+#include <cstring>
+#include <gnutls/abstract.h>
+#include <gnutls/crypto.h>
+#include <gnutls/x509.h>
+#else  // OpenSSL
 #include <openssl/cmac.h>
 #include <openssl/ecdsa.h>
 #include <openssl/hmac.h>
@@ -40,14 +45,9 @@
 #include <openssl/param_build.h>
 #include <openssl/params.h>
 #define OPENSSL_ENGINE nullptr
-#else
+#else  // OpenSSL 1.x
 #include <openssl/sha.h>
 #endif
-#else  // GnuTLS
-#include <cstring>
-#include <gnutls/abstract.h>
-#include <gnutls/crypto.h>
-#include <gnutls/x509.h>
 #endif
 
 #if USE_GLOG_AND_GFLAGS
@@ -59,28 +59,28 @@
 
 Gnss_Crypto::Gnss_Crypto()
 {
-#if USE_OPENSSL_FALLBACK
+#if USE_GNUTLS_FALLBACK
+    gnutls_global_init();
+#else  // OpenSSL
 #if !(USE_OPENSSL_3 || USE_OPENSSL_111)
     LOG(WARNING) << "The OpenSSL library version you are linking against is too old for some OSNMA functions."
                  << " Please do not trust OSNMA ouputs or upgrade your system to a newer version of OpenSSL"
                  << " and rebuild GNSS-SDR against it.";
 #endif
-#else  // GnuTLS
-    gnutls_global_init();
 #endif
 }
 
 
 Gnss_Crypto::Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath)
 {
-#if USE_OPENSSL_FALLBACK
+#if USE_GNUTLS_FALLBACK
+    gnutls_global_init();
+#else  // OpenSSL
 #if !(USE_OPENSSL_3 || USE_OPENSSL_111)
     LOG(WARNING) << "The OpenSSL library version you are linking against is too old for some OSNMA functions."
                  << " Please do not trust OSNMA ouputs or upgrade your system to a newer version of OpenSSL"
                  << " and rebuild GNSS-SDR against it.";
 #endif
-#else  // GnuTLS
-    gnutls_global_init();
 #endif
     if (!readPublicKeyFromCRT(certFilePath))
         {
@@ -92,31 +92,30 @@ Gnss_Crypto::Gnss_Crypto(const std::string& certFilePath, const std::string& mer
 
 Gnss_Crypto::~Gnss_Crypto()
 {
-#if USE_OPENSSL_FALLBACK
-#if USE_OPENSSL_3
-#else
-    if (d_PublicKey != nullptr)
-        {
-            EC_KEY_free(d_PublicKey);
-        }
-#endif
-#else  // GnuTLS
+#if USE_GNUTLS_FALLBACK
     if (d_PublicKey != nullptr)
         {
             gnutls_pubkey_deinit(d_PublicKey);
             d_PublicKey = nullptr;
         }
     gnutls_global_deinit();
+#else  // OpenSSL
+#if !USE_OPENSSL_3
+    if (d_PublicKey != nullptr)
+        {
+            EC_KEY_free(d_PublicKey);
+        }
+#endif
 #endif
 }
 
 
 bool Gnss_Crypto::have_public_key() const
 {
-#if USE_OPENSSL_FALLBACK
-    return (d_PublicKey != nullptr);
-#else  // GnuTLS
+#if USE_GNUTLS_FALLBACK
     return (d_PublicKey != gnutls_pubkey_t{});
+#else  // OpenSSL
+    return (d_PublicKey != nullptr);
 #endif
 }
 
@@ -260,7 +259,15 @@ void Gnss_Crypto::read_merkle_xml(const std::string& merkleFilePath)
 std::vector<uint8_t> Gnss_Crypto::computeSHA256(const std::vector<uint8_t>& input) const
 {
     std::vector<uint8_t> output(32);  // SHA256 hash size
-#if USE_OPENSSL_FALLBACK
+#if USE_GNUTLS_FALLBACK
+    std::vector<uint8_t> output_aux(32);
+    gnutls_hash_hd_t hashHandle;
+    gnutls_hash_init(&hashHandle, GNUTLS_DIG_SHA256);
+    gnutls_hash(hashHandle, input.data(), input.size());
+    gnutls_hash_output(hashHandle, output_aux.data());
+    output = output_aux;
+    gnutls_hash_deinit(hashHandle, output_aux.data());
+#else  // OpenSSL
 #if USE_OPENSSL_3
     unsigned int mdLen;
     EVP_MD_CTX* mdCtx = EVP_MD_CTX_new();
@@ -283,20 +290,12 @@ std::vector<uint8_t> Gnss_Crypto::computeSHA256(const std::vector<uint8_t>& inpu
             return output;
         }
     EVP_MD_CTX_free(mdCtx);
-#else
+#else  // OpenSSL 1.x
     SHA256_CTX sha256Context;
     SHA256_Init(&sha256Context);
     SHA256_Update(&sha256Context, input.data(), input.size());
     SHA256_Final(output.data(), &sha256Context);
 #endif
-#else  // GnuTLS
-    std::vector<uint8_t> output_aux(32);
-    gnutls_hash_hd_t hashHandle;
-    gnutls_hash_init(&hashHandle, GNUTLS_DIG_SHA256);
-    gnutls_hash(hashHandle, input.data(), input.size());
-    gnutls_hash_output(hashHandle, output_aux.data());
-    output = output_aux;
-    gnutls_hash_deinit(hashHandle, output_aux.data());
 #endif
     return output;
 }
@@ -305,7 +304,15 @@ std::vector<uint8_t> Gnss_Crypto::computeSHA256(const std::vector<uint8_t>& inpu
 std::vector<uint8_t> Gnss_Crypto::computeSHA3_256(const std::vector<uint8_t>& input) const
 {
     std::vector<uint8_t> output(32);  // SHA256 hash size
-#if USE_OPENSSL_FALLBACK
+#if USE_GNUTLS_FALLBACK
+    std::vector<uint8_t> output_aux(32);
+    gnutls_hash_hd_t hashHandle;
+    gnutls_hash_init(&hashHandle, GNUTLS_DIG_SHA3_256);
+    gnutls_hash(hashHandle, input.data(), input.size());
+    gnutls_hash_output(hashHandle, output_aux.data());
+    output = output_aux;
+    gnutls_hash_deinit(hashHandle, output_aux.data());
+#else  // OpenSSL
 #if USE_OPENSSL_3 || USE_OPENSSL_111
     EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
     const EVP_MD* md = EVP_sha3_256();
@@ -314,21 +321,13 @@ std::vector<uint8_t> Gnss_Crypto::computeSHA3_256(const std::vector<uint8_t>& in
     EVP_DigestUpdate(mdctx, input.data(), input.size());
     EVP_DigestFinal_ex(mdctx, output.data(), nullptr);
     EVP_MD_CTX_free(mdctx);
-#else
+#else  // OpenSSL 1.x
     // SHA3-256 not implemented in OpenSSL 1.0, it was introduced in OpenSSL 1.1.1
     if (!input.empty())
         {
             // do nothing
         }
 #endif
-#else  // GnuTLS
-    std::vector<uint8_t> output_aux(32);
-    gnutls_hash_hd_t hashHandle;
-    gnutls_hash_init(&hashHandle, GNUTLS_DIG_SHA3_256);
-    gnutls_hash(hashHandle, input.data(), input.size());
-    gnutls_hash_output(hashHandle, output_aux.data());
-    output = output_aux;
-    gnutls_hash_deinit(hashHandle, output_aux.data());
 #endif
     return output;
 }
@@ -337,7 +336,15 @@ std::vector<uint8_t> Gnss_Crypto::computeSHA3_256(const std::vector<uint8_t>& in
 std::vector<uint8_t> Gnss_Crypto::computeHMAC_SHA_256(const std::vector<uint8_t>& key, const std::vector<uint8_t>& input) const
 {
     std::vector<uint8_t> output(32);
-#if USE_OPENSSL_FALLBACK
+#if USE_GNUTLS_FALLBACK
+    std::vector<uint8_t> output_aux(32);
+    gnutls_hmac_hd_t hmac;
+    gnutls_hmac_init(&hmac, GNUTLS_MAC_SHA256, key.data(), key.size());
+    gnutls_hmac(hmac, input.data(), input.size());
+    gnutls_hmac_output(hmac, output_aux.data());
+    output = output_aux;
+    gnutls_hmac_deinit(hmac, output_aux.data());
+#else  // OpenSSL
 #if USE_OPENSSL_3
     std::vector<uint8_t> hmac(EVP_MAX_MD_SIZE);
     size_t output_length = 0;
@@ -393,7 +400,7 @@ std::vector<uint8_t> Gnss_Crypto::computeHMAC_SHA_256(const std::vector<uint8_t>
     EVP_MAC_free(mac);
     hmac.resize(output_length);
     output = hmac;
-#else
+#else  // OpenSSL 1.x
     unsigned int outputLength = EVP_MAX_MD_SIZE;
     unsigned char* result = HMAC(EVP_sha256(), key.data(), key.size(), input.data(), input.size(), output.data(), &outputLength);
     if (result == nullptr)
@@ -405,14 +412,6 @@ std::vector<uint8_t> Gnss_Crypto::computeHMAC_SHA_256(const std::vector<uint8_t>
     // Resize the output vector to the actual length of the HMAC-SHA256 output
     output.resize(outputLength);
 #endif
-#else  // GnuTLS
-    std::vector<uint8_t> output_aux(32);
-    gnutls_hmac_hd_t hmac;
-    gnutls_hmac_init(&hmac, GNUTLS_MAC_SHA256, key.data(), key.size());
-    gnutls_hmac(hmac, input.data(), input.size());
-    gnutls_hmac_output(hmac, output_aux.data());
-    output = output_aux;
-    gnutls_hmac_deinit(hmac, output_aux.data());
 #endif
     return output;
 }
@@ -421,7 +420,18 @@ std::vector<uint8_t> Gnss_Crypto::computeHMAC_SHA_256(const std::vector<uint8_t>
 std::vector<uint8_t> Gnss_Crypto::computeCMAC_AES(const std::vector<uint8_t>& key, const std::vector<uint8_t>& input) const
 {
     std::vector<uint8_t> output(16);
-#if USE_OPENSSL_FALLBACK
+#if USE_GNUTLS_FALLBACK
+    gnutls_cipher_hd_t cipher;
+    std::vector<uint8_t> mac(16);
+    std::vector<uint8_t> message = input;
+    gnutls_datum_t key_data = {const_cast<uint8_t*>(key.data()), static_cast<unsigned int>(key.size())};
+    gnutls_cipher_init(&cipher, GNUTLS_CIPHER_AES_128_CBC, &key_data, nullptr);
+    gnutls_cipher_set_iv(cipher, nullptr, 16);                      // Set IV to zero
+    gnutls_cipher_encrypt(cipher, message.data(), message.size());  // Encrypt the message with AES-128
+    gnutls_cipher_tag(cipher, mac.data(), mac.size());              // Get the CMAC-AES tag
+    output = mac;
+    gnutls_cipher_deinit(cipher);
+#else  // OpenSSL
 #if USE_OPENSSL_3
     std::vector<uint8_t> aux(EVP_MAX_MD_SIZE);  // CMAC-AES output size
     size_t output_length = 0;
@@ -479,7 +489,7 @@ std::vector<uint8_t> Gnss_Crypto::computeCMAC_AES(const std::vector<uint8_t>& ke
 
     aux.resize(output_length);
     output = aux;
-#else
+#else  // OpenSSL 1.x
     std::vector<uint8_t> mac(16);  // CMAC-AES output size
 
     // Create CMAC context
@@ -495,17 +505,6 @@ std::vector<uint8_t> Gnss_Crypto::computeCMAC_AES(const std::vector<uint8_t>& ke
 
     output = mac;
 #endif
-#else  // GnuTLS
-    gnutls_cipher_hd_t cipher;
-    std::vector<uint8_t> mac(16);
-    std::vector<uint8_t> message = input;
-    gnutls_datum_t key_data = {const_cast<uint8_t*>(key.data()), static_cast<unsigned int>(key.size())};
-    gnutls_cipher_init(&cipher, GNUTLS_CIPHER_AES_128_CBC, &key_data, nullptr);
-    gnutls_cipher_set_iv(cipher, nullptr, 16);                      // Set IV to zero
-    gnutls_cipher_encrypt(cipher, message.data(), message.size());  // Encrypt the message with AES-128
-    gnutls_cipher_tag(cipher, mac.data(), mac.size());              // Get the CMAC-AES tag
-    output = mac;
-    gnutls_cipher_deinit(cipher);
 #endif
     return output;
 }
@@ -526,27 +525,7 @@ void Gnss_Crypto::readPublicKeyFromPEM(const std::string& pemFilePath)
             return;
         }
     std::string pemContent((std::istreambuf_iterator<char>(pemFile)), std::istreambuf_iterator<char>());
-#if USE_OPENSSL_FALLBACK
-    // Create a BIO object from the string data
-    BIO* bio = BIO_new_mem_buf(pemContent.c_str(), pemContent.length());
-    if (!bio)
-        {
-            std::cerr << "OpenSSL: error creating a BIO object with data read from file " << pemFilePath << ". Aborting import" << std::endl;
-            return;
-        }
-#if USE_OPENSSL_3
-    d_PublicKey = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr);
-#else
-    d_PublicKey = PEM_read_bio_EC_PUBKEY(bio, nullptr, nullptr, nullptr);
-#endif
-    BIO_free(bio);
-    if (d_PublicKey == nullptr)
-        {
-            std::cerr << "OpenSSL: error reading the OSNMA Public Key from file " << pemFilePath << ". Aborting import" << std::endl;
-            LOG(INFO) << "OpenSSL: error reading the OSNMA Public Key from file " << pemFilePath << ". Aborting import";
-            return;
-        }
-#else  // GnuTLS
+#if USE_GNUTLS_FALLBACK
     // Import the PEM data
     gnutls_datum_t pemDatum = {const_cast<unsigned char*>(reinterpret_cast<unsigned char*>(pemContent.data())), static_cast<unsigned int>(pemContent.size())};
     gnutls_pubkey_t pubkey;
@@ -566,6 +545,26 @@ void Gnss_Crypto::readPublicKeyFromPEM(const std::string& pemFilePath)
 
     pubkey_copy(pubkey, &d_PublicKey);
     gnutls_pubkey_deinit(pubkey);
+#else  // OpenSSL
+    // Create a BIO object from the string data
+    BIO* bio = BIO_new_mem_buf(pemContent.c_str(), pemContent.length());
+    if (!bio)
+        {
+            std::cerr << "OpenSSL: error creating a BIO object with data read from file " << pemFilePath << ". Aborting import" << std::endl;
+            return;
+        }
+#if USE_OPENSSL_3
+    d_PublicKey = PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr);
+#else  // OpenSSL 1.x
+    d_PublicKey = PEM_read_bio_EC_PUBKEY(bio, nullptr, nullptr, nullptr);
+#endif
+    BIO_free(bio);
+    if (d_PublicKey == nullptr)
+        {
+            std::cerr << "OpenSSL: error reading the OSNMA Public Key from file " << pemFilePath << ". Aborting import" << std::endl;
+            LOG(INFO) << "OpenSSL: error reading the OSNMA Public Key from file " << pemFilePath << ". Aborting import";
+            return;
+        }
 #endif
     std::cout << "OSNMA Public key successfully read from file " << pemFilePath << std::endl;
     LOG(INFO) << "OSNMA Public key successfully read from file " << pemFilePath;
@@ -574,57 +573,7 @@ void Gnss_Crypto::readPublicKeyFromPEM(const std::string& pemFilePath)
 
 bool Gnss_Crypto::readPublicKeyFromCRT(const std::string& crtFilePath)
 {
-#if USE_OPENSSL_FALLBACK
-    // Open the .crt file
-    std::ifstream crtFile(crtFilePath, std::ios::binary);
-    if (!crtFile.is_open())
-        {
-            std::cerr << "Unable to open file: " << crtFilePath << std::endl;
-            return false;
-        }
-
-    // Read certificate
-    std::vector<char> buffer((std::istreambuf_iterator<char>(crtFile)), std::istreambuf_iterator<char>());
-    BIO* bio = BIO_new_mem_buf(buffer.data(), buffer.size());
-    if (!bio)
-        {
-            std::cerr << "Unable to create BIO for file: " << crtFilePath << std::endl;
-            return false;
-        }
-    X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
-    if (!cert)
-        {
-            std::cerr << "Unable to read certificate from file: " << crtFilePath << std::endl;
-            BIO_free(bio);
-            return false;
-        }
-
-    // Read the public key from the certificate
-    EVP_PKEY* pubkey = X509_get_pubkey(cert);
-#if USE_OPENSSL_3
-    if (!pubkey)
-        {
-            std::cerr << "Failed to extract the public key" << std::endl;
-            X509_free(cert);
-            return false;
-        }
-    pubkey_copy(pubkey, &d_PublicKey);
-    EVP_PKEY_free(pubkey);
-#else
-    EC_KEY* ec_pubkey = EVP_PKEY_get1_EC_KEY(pubkey);
-    EVP_PKEY_free(pubkey);
-    if (!ec_pubkey)
-        {
-            std::cerr << "Failed to extract the public key" << std::endl;
-            X509_free(cert);
-            return false;
-        }
-    pubkey_copy(ec_pubkey, &d_PublicKey);
-    EC_KEY_free(ec_pubkey);
-#endif
-    BIO_free(bio);
-    X509_free(cert);
-#else  // GnuTLS
+#if USE_GNUTLS_FALLBACK
     // Open the .crt file
     std::ifstream crtFile(crtFilePath, std::ios::binary);
     if (!crtFile.is_open())
@@ -664,6 +613,56 @@ bool Gnss_Crypto::readPublicKeyFromCRT(const std::string& crtFilePath)
     pubkey_copy(pubkey, &d_PublicKey);
     gnutls_x509_crt_deinit(cert);
     gnutls_pubkey_deinit(pubkey);
+#else  // OpenSSL
+    // Open the .crt file
+    std::ifstream crtFile(crtFilePath, std::ios::binary);
+    if (!crtFile.is_open())
+        {
+            std::cerr << "Unable to open file: " << crtFilePath << std::endl;
+            return false;
+        }
+
+    // Read certificate
+    std::vector<char> buffer((std::istreambuf_iterator<char>(crtFile)), std::istreambuf_iterator<char>());
+    BIO* bio = BIO_new_mem_buf(buffer.data(), buffer.size());
+    if (!bio)
+        {
+            std::cerr << "Unable to create BIO for file: " << crtFilePath << std::endl;
+            return false;
+        }
+    X509* cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
+    if (!cert)
+        {
+            std::cerr << "Unable to read certificate from file: " << crtFilePath << std::endl;
+            BIO_free(bio);
+            return false;
+        }
+
+    // Read the public key from the certificate
+    EVP_PKEY* pubkey = X509_get_pubkey(cert);
+#if USE_OPENSSL_3
+    if (!pubkey)
+        {
+            std::cerr << "Failed to extract the public key" << std::endl;
+            X509_free(cert);
+            return false;
+        }
+    pubkey_copy(pubkey, &d_PublicKey);
+    EVP_PKEY_free(pubkey);
+#else  // OpenSSL 1.x
+    EC_KEY* ec_pubkey = EVP_PKEY_get1_EC_KEY(pubkey);
+    EVP_PKEY_free(pubkey);
+    if (!ec_pubkey)
+        {
+            std::cerr << "Failed to extract the public key" << std::endl;
+            X509_free(cert);
+            return false;
+        }
+    pubkey_copy(ec_pubkey, &d_PublicKey);
+    EC_KEY_free(ec_pubkey);
+#endif
+    BIO_free(bio);
+    X509_free(cert);
 #endif
     std::cout << "OSNMA Public key successfully read from file " << crtFilePath << std::endl;
     LOG(INFO) << "OSNMA Public key successfully read from file " << crtFilePath;
@@ -680,7 +679,32 @@ bool Gnss_Crypto::verify_signature(const std::vector<uint8_t>& message, const st
             return false;
         }
     bool success = false;
-#if USE_OPENSSL_FALLBACK
+#if USE_GNUTLS_FALLBACK
+    // Convert signature to DER format
+    std::vector<uint8_t> der_sig;
+    if (!convert_raw_to_der_ecdsa(signature, der_sig))
+        {
+            std::cerr << "Failed to convert raw ECDSA signature to DER format" << std::endl;
+            return false;
+        }
+
+    // Prepare the digest datum
+    gnutls_datum_t digest_data = {const_cast<unsigned char*>(digest.data()), static_cast<unsigned int>(digest.size())};
+    gnutls_datum_t der_sig_data = {der_sig.data(), static_cast<unsigned int>(der_sig.size())};
+
+    // Verify the DER-encoded signature
+    int ret = gnutls_pubkey_verify_hash2(d_PublicKey, GNUTLS_SIGN_ECDSA_SHA256, 0, &digest_data, &der_sig_data);
+    success = (ret >= 0);
+    if (success)
+        {
+            LOG(INFO) << "GnuTLS: OSNMA signature authenticated successfully";
+        }
+    else
+        {
+            std::cerr << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret) << std::endl;
+            LOG(WARNING) << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret);
+        }
+#else  // OpenSSL
 #if USE_OPENSSL_3
     EVP_PKEY_CTX* ctx;
     ctx = EVP_PKEY_CTX_new(d_PublicKey, nullptr);
@@ -753,7 +777,7 @@ bool Gnss_Crypto::verify_signature(const std::vector<uint8_t>& message, const st
             std::cerr << "OpenSSL: OSNMA message authentication failed: " << err << std::endl;
             LOG(WARNING) << "OpenSSL: OSNMA message authentication failed: " << err;
         }
-#else
+#else  // OpenSSL 1.x
     std::vector<uint8_t> der_sig;
     if (!convert_raw_to_der_ecdsa(signature, der_sig))
         {
@@ -777,31 +801,6 @@ bool Gnss_Crypto::verify_signature(const std::vector<uint8_t>& message, const st
             LOG(WARNING) << "OpenSSL: OSNMA message authentication failed";
         }
 #endif
-#else  // GnuTLS
-    // Convert signature to DER format
-    std::vector<uint8_t> der_sig;
-    if (!convert_raw_to_der_ecdsa(signature, der_sig))
-        {
-            std::cerr << "Failed to convert raw ECDSA signature to DER format" << std::endl;
-            return false;
-        }
-
-    // Prepare the digest datum
-    gnutls_datum_t digest_data = {const_cast<unsigned char*>(digest.data()), static_cast<unsigned int>(digest.size())};
-    gnutls_datum_t der_sig_data = {der_sig.data(), static_cast<unsigned int>(der_sig.size())};
-
-    // Verify the DER-encoded signature
-    int ret = gnutls_pubkey_verify_hash2(d_PublicKey, GNUTLS_SIGN_ECDSA_SHA256, 0, &digest_data, &der_sig_data);
-    success = (ret >= 0);
-    if (success)
-        {
-            LOG(INFO) << "GnuTLS: OSNMA signature authenticated successfully";
-        }
-    else
-        {
-            std::cerr << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret) << std::endl;
-            LOG(WARNING) << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret);
-        }
 #endif
     return success;
 }
@@ -845,7 +844,21 @@ std::vector<uint8_t> Gnss_Crypto::getMerkleRoot(const std::vector<std::vector<ui
 
 void Gnss_Crypto::set_public_key(const std::vector<uint8_t>& publicKey)
 {
-#if USE_OPENSSL_FALLBACK
+#if USE_GNUTLS_FALLBACK
+    gnutls_pubkey_t pubkey;
+    gnutls_datum_t pemDatum = {const_cast<unsigned char*>(publicKey.data()), static_cast<unsigned int>(publicKey.size())};
+    gnutls_pubkey_init(&pubkey);
+    int ret = gnutls_pubkey_import(pubkey, &pemDatum, GNUTLS_X509_FMT_PEM);
+    if (ret != GNUTLS_E_SUCCESS)
+        {
+            gnutls_pubkey_deinit(pubkey);
+            std::cerr << "GnuTLS: error setting the public key" << std::endl;
+            std::cerr << "GnuTLS error: " << gnutls_strerror(ret) << std::endl;
+            return;
+        }
+    pubkey_copy(pubkey, &d_PublicKey);
+    gnutls_pubkey_deinit(pubkey);
+#else  // OpenSSL
     BIO* bio = nullptr;
     EVP_PKEY* pkey = nullptr;
     bio = BIO_new_mem_buf(publicKey.data(), publicKey.size());
@@ -876,22 +889,8 @@ void Gnss_Crypto::set_public_key(const std::vector<uint8_t>& publicKey)
             return;
         }
     EC_KEY_free(ec_pkey);
-#endif
+#endif  // OpenSSL 1.x
     EVP_PKEY_free(pkey);
-#else  // GnuTLS
-    gnutls_pubkey_t pubkey;
-    gnutls_datum_t pemDatum = {const_cast<unsigned char*>(publicKey.data()), static_cast<unsigned int>(publicKey.size())};
-    gnutls_pubkey_init(&pubkey);
-    int ret = gnutls_pubkey_import(pubkey, &pemDatum, GNUTLS_X509_FMT_PEM);
-    if (ret != GNUTLS_E_SUCCESS)
-        {
-            gnutls_pubkey_deinit(pubkey);
-            std::cerr << "GnuTLS: error setting the public key" << std::endl;
-            std::cerr << "GnuTLS error: " << gnutls_strerror(ret) << std::endl;
-            return;
-        }
-    pubkey_copy(pubkey, &d_PublicKey);
-    gnutls_pubkey_deinit(pubkey);
 #endif
     LOG(INFO) << "OSNMA Public Key successfully set up.";
 }
@@ -945,7 +944,40 @@ bool Gnss_Crypto::convert_raw_to_der_ecdsa(const std::vector<uint8_t>& raw_signa
 }
 
 
-#if USE_OPENSSL_FALLBACK
+#if USE_GNUTLS_FALLBACK  // GnuTLS-specific functions
+bool Gnss_Crypto::pubkey_copy(gnutls_pubkey_t src, gnutls_pubkey_t* dest)
+{
+    gnutls_datum_t key_datum;
+
+    // Export the public key from src to memory
+    int ret = gnutls_pubkey_export2(src, GNUTLS_X509_FMT_PEM, &key_datum);
+    if (ret < 0)
+        {
+            gnutls_free(key_datum.data);
+            return false;
+        }
+
+    // Initialize dest
+    ret = gnutls_pubkey_init(dest);
+    if (ret < 0)
+        {
+            gnutls_free(key_datum.data);
+            return false;
+        }
+
+    // Import the public key data from key_datum to dest
+    ret = gnutls_pubkey_import(*dest, &key_datum, GNUTLS_X509_FMT_PEM);
+    gnutls_free(key_datum.data);
+
+    if (ret < 0)
+        {
+            gnutls_pubkey_deinit(*dest);
+            return false;
+        }
+
+    return true;
+}
+#else  // OpenSSL
 #if USE_OPENSSL_3
 bool Gnss_Crypto::pubkey_copy(EVP_PKEY* src, EVP_PKEY** dest)
 {
@@ -1036,41 +1068,5 @@ bool Gnss_Crypto::pubkey_copy(EC_KEY* src, EC_KEY** dest)
 
     return true;
 }
-
 #endif
-
-#else  // GnuTLS-specific functions
-
-bool Gnss_Crypto::pubkey_copy(gnutls_pubkey_t src, gnutls_pubkey_t* dest)
-{
-    gnutls_datum_t key_datum;
-
-    // Export the public key from src to memory
-    int ret = gnutls_pubkey_export2(src, GNUTLS_X509_FMT_PEM, &key_datum);
-    if (ret < 0)
-        {
-            gnutls_free(key_datum.data);
-            return false;
-        }
-
-    // Initialize dest
-    ret = gnutls_pubkey_init(dest);
-    if (ret < 0)
-        {
-            gnutls_free(key_datum.data);
-            return false;
-        }
-
-    // Import the public key data from key_datum to dest
-    ret = gnutls_pubkey_import(*dest, &key_datum, GNUTLS_X509_FMT_PEM);
-    gnutls_free(key_datum.data);
-
-    if (ret < 0)
-        {
-            gnutls_pubkey_deinit(*dest);
-            return false;
-        }
-
-    return true;
-}
 #endif
diff --git a/src/core/system_parameters/gnss_crypto.h b/src/core/system_parameters/gnss_crypto.h
index bbcac1afa..7e0527750 100644
--- a/src/core/system_parameters/gnss_crypto.h
+++ b/src/core/system_parameters/gnss_crypto.h
@@ -22,10 +22,10 @@
 #include <cstdint>
 #include <string>
 #include <vector>
-#if USE_OPENSSL_FALLBACK
-#include <openssl/ec.h>
-#else
+#if USE_GNUTLS_FALLBACK
 #include <gnutls/gnutls.h>
+#else  // OpenSSL
+#include <openssl/ec.h>
 #endif
 
 /** \addtogroup Core
@@ -60,7 +60,10 @@ private:
     bool readPublicKeyFromCRT(const std::string& crtFilePath);
     bool convert_raw_to_der_ecdsa(const std::vector<uint8_t>& raw_signature, std::vector<uint8_t>& der_signature) const;
     std::vector<uint8_t> convert_from_hex_str(const std::string& input) const;
-#if USE_OPENSSL_FALLBACK
+#if USE_GNUTLS_FALLBACK
+    bool pubkey_copy(gnutls_pubkey_t src, gnutls_pubkey_t* dest);
+    gnutls_pubkey_t d_PublicKey{};
+#else  // OpenSSL
 #if USE_OPENSSL_3
     bool pubkey_copy(EVP_PKEY* src, EVP_PKEY** dest);
     EVP_PKEY* d_PublicKey{};
@@ -68,9 +71,6 @@ private:
     bool pubkey_copy(EC_KEY* src, EC_KEY** dest);
     EC_KEY* d_PublicKey = nullptr;
 #endif
-#else  // GnuTLS
-    bool pubkey_copy(gnutls_pubkey_t src, gnutls_pubkey_t* dest);
-    gnutls_pubkey_t d_PublicKey{};
 #endif
     std::vector<uint8_t> d_x_4_0;
     std::vector<uint8_t> d_x_3_1;
@@ -82,4 +82,5 @@ private:
 
 /** \} */
 /** \} */
+
 #endif  // GNSS_SDR_GNSS_CRYPTO_H
\ No newline at end of file
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 8de611f5f..f1491b032 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1367,6 +1367,9 @@ if(NOT ENABLE_PACKAGING AND NOT ENABLE_FPGA)
             GTest::GTest
             GTest::Main
             core_libs
+            Gnuradio::blocks
+            Gnuradio::runtime
+            Gnuradio::filter  # workaround for old systems
     )
     if(ENABLE_GLOG_AND_GFLAGS)
         target_link_libraries(osnma_msg_receiver_test PRIVATE Gflags::gflags Glog::glog)
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 7b94baf0c..743c2b877 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
@@ -1,11 +1,14 @@
 #include <gtest/gtest.h>
 #include <bitset>
-#include <filesystem>
 #include <fstream>
-#include <logging.h>
-#include <osnma_msg_receiver.h>
 #include <vector>
+#include "osnma_msg_receiver.h"
 
+#if USE_GLOG_AND_GFLAGS
+#include <glog/logging.h>  // for LOG
+#else
+#include <absl/log/log.h>
+#endif
 
 struct TestVector
 {
@@ -31,11 +34,9 @@ protected:
     void set_time(std::tm& input);
 //    std::string log_name {"CONFIG1-2023-08-16-PKID1-OSNMA"};
     std::string log_name {"CONFIG2-2023-07-27-PKID2-MT2-OSNMA"};
-    void initializeGoogleLog();
 
     void SetUp() override
     {
-        initializeGoogleLog();
 //        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);
@@ -545,46 +546,3 @@ void OsnmaMsgReceiverTest::set_time(std::tm& input)
 
 
 }
-void OsnmaMsgReceiverTest::initializeGoogleLog()
-{
-    google::InitGoogleLogging(log_name.c_str());
-    FLAGS_minloglevel = 0; // INFO
-    FLAGS_logtostderr = 0;  // add this line
-    FLAGS_log_dir = "/home/cgm/CLionProjects/osnma/build/src/tests/logs";
-    if (FLAGS_log_dir.empty())
-        {
-            std::cout << "Logging will be written at "
-                      << std::filesystem::temp_directory_path()
-                      << '\n'
-                      << "Use gnss-sdr --log_dir=/path/to/log to change that.\n";
-        }
-    else
-        {
-            try
-                {
-                    const std::filesystem::path p(FLAGS_log_dir);
-                    if (!std::filesystem::exists(p))
-                        {
-                            std::cout << "The path "
-                                      << FLAGS_log_dir
-                                      << " does not exist, attempting to create it.\n";
-                            std::error_code ec;
-                            if (!std::filesystem::create_directory(p, ec))
-                                {
-                                    std::cout << "Could not create the " << FLAGS_log_dir << " folder.\n";
-                                    gflags::ShutDownCommandLineFlags();
-                                    throw std::runtime_error("Could not create folder for logs");
-                                }
-                        }
-                    std::cout << "Logging will be written at " << FLAGS_log_dir << '\n';
-                }
-            catch (const std::exception& e)
-                {
-                    std::cerr << e.what() << '\n';
-                    std::cerr << "Could not create the " << FLAGS_log_dir << " folder.\n";
-                    gflags::ShutDownCommandLineFlags();
-                    throw;
-                }
-        }
-}
-

From a530981d5bc7fb97b09e8f76fa28da50880fbff0 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@cttc.es>
Date: Sat, 29 Jun 2024 11:41:28 +0200
Subject: [PATCH 05/14] Update README.md with openssl

---
 README.md                         | 30 ++++++++++++++----------------
 cmake/Modules/GnssSdrCrypto.cmake |  2 +-
 2 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/README.md b/README.md
index a904764b0..5b7bc9dfd 100644
--- a/README.md
+++ b/README.md
@@ -75,7 +75,7 @@ information about this open-source, software-defined GNSS receiver.
       - [Install Armadillo, a C++ linear algebra library](#install-armadillo-a-c-linear-algebra-library)
       - [Install Gflags, a commandline flags processing module for C++](#install-gflags-a-commandline-flags-processing-module-for-c)
       - [Install Glog, a library that implements application-level logging](#install-glog-a-library-that-implements-application-level-logging)
-      - [Install the GnuTLS or OpenSSL libraries](#install-the-gnutls-or-openssl-libraries)
+      - [Install the OpenSSL libraries](#install-the-openssl-libraries)
       - [Install Matio, MATLAB MAT file I/O library](#install-matio-matlab-mat-file-io-library)
       - [Install Protocol Buffers, a portable mechanism for serialization of structured data](#install-protocol-buffers-a-portable-mechanism-for-serialization-of-structured-data)
       - [Install Pugixml, a light-weight C++ XML processing library](#install-pugixml-a-light-weight-c-xml-processing-library)
@@ -168,16 +168,19 @@ $ sudo apt-get install build-essential cmake git pkg-config libboost-dev libboos
        libboost-system-dev libboost-filesystem-dev libboost-thread-dev libboost-chrono-dev \
        libboost-serialization-dev liblog4cpp5-dev libuhd-dev gnuradio-dev gr-osmosdr \
        libblas-dev liblapack-dev libarmadillo-dev libgflags-dev libgoogle-glog-dev \
-       libgnutls-openssl-dev libpcap-dev libmatio-dev libpugixml-dev libgtest-dev \
-       libprotobuf-dev protobuf-compiler python3-mako
+       libssl-dev libpcap-dev libmatio-dev libpugixml-dev libgtest-dev \
+       libprotobuf-dev libcpu-features-dev protobuf-compiler python3-mako
 ```
 
 Please note that the required files from `libgtest-dev` were named `googletest`
 in Debian 9 "stretch" and Ubuntu 18.04 "bionic", and renamed to `libgtest-dev`
 in Debian 10 "buster" and above.
 
-Since Ubuntu 21.04 Hirsute / Debian 11, the package `libcpu-features-dev` is
-also required.
+In distributions older than Ubuntu 21.04 Hirsute / Debian 11, the package
+`libcpu-features-dev` is not required.
+
+In distributions older than Ubuntu 22.04 Jammy / Debian 12, the package
+`libssl-dev` must be replaced by `libgnutls-openssl-dev`.
 
 **Note for Ubuntu 14.04 LTS "trusty" users:** you will need to build from source
 and install GNU Radio manually, as explained below, since GNSS-SDR requires
@@ -451,20 +454,15 @@ Please note that Glog is replaced by the
 [Abseil Logging Library](https://abseil.io/docs/cpp/guides/logging) if Abseil >=
 v20240116 is available in your system.
 
-#### Install the GnuTLS or OpenSSL libraries
+#### Install the OpenSSL libraries
 
 ```
-$ sudo apt-get install libgnutls-openssl-dev    # For Debian/Ubuntu/LinuxMint
-$ sudo yum install openssl-devel                # For Fedora/CentOS/RHEL
-$ sudo zypper install openssl-devel             # For OpenSUSE
-$ sudo pacman -S openssl                        # For Arch Linux
+$ sudo apt-get install libssl-dev         # For Debian/Ubuntu/LinuxMint
+$ sudo yum install openssl-devel          # For Fedora/CentOS/RHEL
+$ sudo zypper install openssl-devel       # For OpenSUSE
+$ sudo pacman -S openssl                  # For Arch Linux
 ```
 
-In case the [GnuTLS](https://www.gnutls.org/ "GnuTLS's Homepage") library with
-openssl extensions package is not available in your GNU/Linux distribution,
-GNSS-SDR can also work well with
-[OpenSSL](https://www.openssl.org/ "OpenSSL's Homepage").
-
 #### Install [Matio](https://github.com/tbeu/matio "Matio's Homepage"), MATLAB MAT file I/O library
 
 ```
@@ -838,7 +836,7 @@ In a terminal, type:
 ```
 $ sudo port selfupdate
 $ sudo port upgrade outdated
-$ sudo port install armadillo cmake pkgconfig protobuf3-cpp pugixml gnutls
+$ sudo port install armadillo cmake pkgconfig protobuf3-cpp pugixml openssl3
 $ sudo port install gnuradio +uhd +grc +zeromq
 $ sudo port install boost matio libad9361-iio libiio
 $ sudo port install py311-mako
diff --git a/cmake/Modules/GnssSdrCrypto.cmake b/cmake/Modules/GnssSdrCrypto.cmake
index 33e1aaab1..c873c6bc3 100644
--- a/cmake/Modules/GnssSdrCrypto.cmake
+++ b/cmake/Modules/GnssSdrCrypto.cmake
@@ -93,7 +93,7 @@ else()
             endif()
         endif()
         if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
-            message(" 'sudo port install gnutls', if you are using Macports, or")
+            message(" 'sudo port install openssl3', if you are using Macports, or")
             message(" 'brew install openssl', if you are using Homebrew.")
         endif()
         message(FATAL_ERROR "OpenSSL or the GnuTLS libraries with openssl compatibility are required to build gnss-sdr")

From 609b85b8641b7603f2a2d0b5411be0504bd126b0 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@cttc.es>
Date: Sat, 29 Jun 2024 11:45:00 +0200
Subject: [PATCH 06/14] Fix cmakelint formatting

---
 cmake/Modules/GnssSdrCrypto.cmake | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/cmake/Modules/GnssSdrCrypto.cmake b/cmake/Modules/GnssSdrCrypto.cmake
index c873c6bc3..7c427bab2 100644
--- a/cmake/Modules/GnssSdrCrypto.cmake
+++ b/cmake/Modules/GnssSdrCrypto.cmake
@@ -20,12 +20,12 @@ set_package_properties(OpenSSL
 )
 if(OPENSSL_FOUND)
     set_package_properties(OpenSSL
-        PROPERTIES 
+        PROPERTIES
             DESCRIPTION "Cryptography and SSL/TLS Toolkit (found: v${OPENSSL_VERSION})"
     )
 else()
     set_package_properties(OpenSSL
-        PROPERTIES 
+        PROPERTIES
             DESCRIPTION "OpenSSL has not been found, but GnuTLS with openssl compatibility can replace it"
     )
     ################################################################################
@@ -114,7 +114,7 @@ function(link_to_crypto_dependencies target)
                     PUBLIC
                         OpenSSL::Crypto
                 )
-            endif()  
+            endif()
         else()
             target_link_libraries(core_system_parameters
                 PUBLIC
@@ -131,10 +131,9 @@ function(link_to_crypto_dependencies target)
                 target_compile_definitions(${target} PUBLIC -DUSE_OPENSSL_3=1)
             else()
                 if(NOT OPENSSL_VERSION VERSION_LESS "1.1.1")
-                    target_compile_definitions(${target} PUBLIC -DUSE_OPENSSL_111=1)   
-                else()
+                    target_compile_definitions(${target} PUBLIC -DUSE_OPENSSL_111=1)
                 endif()
-            endif()        
+            endif()
         else()
         endif()
     else()  # GnuTLS

From 668ca7a5d8d6db1aa3c57bc6920c04064239544f Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@cttc.es>
Date: Sat, 29 Jun 2024 11:52:06 +0200
Subject: [PATCH 07/14] Fix link_to_crypto_dependencies function

---
 cmake/Modules/GnssSdrCrypto.cmake | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/cmake/Modules/GnssSdrCrypto.cmake b/cmake/Modules/GnssSdrCrypto.cmake
index 7c427bab2..67648d702 100644
--- a/cmake/Modules/GnssSdrCrypto.cmake
+++ b/cmake/Modules/GnssSdrCrypto.cmake
@@ -105,23 +105,23 @@ endif()
 function(link_to_crypto_dependencies target)
     if(OPENSSL_FOUND)
         if(TARGET OpenSSL::SSL)
-            target_link_libraries(core_system_parameters
+            target_link_libraries(${target}
                 PUBLIC
                     OpenSSL::SSL
             )
             if(TARGET OpenSSL::Crypto)
-                target_link_libraries(core_system_parameters
+                target_link_libraries(${target}
                     PUBLIC
                         OpenSSL::Crypto
                 )
             endif()
         else()
-            target_link_libraries(core_system_parameters
+            target_link_libraries(${target}
                 PUBLIC
                     ${OPENSSL_LIBRARIES}
                     "${OPENSSL_CRYPTO_LIBRARIES}"
             )
-            target_include_directories(core_system_parameters
+            target_include_directories(${target}
                 PUBLIC
                     ${OPENSSL_INCLUDE_DIR}
             )
@@ -137,12 +137,12 @@ function(link_to_crypto_dependencies target)
         else()
         endif()
     else()  # GnuTLS
-        target_link_libraries(core_system_parameters
+        target_link_libraries(${target}
             PUBLIC
                 ${GNUTLS_LIBRARIES}
                 ${GNUTLS_OPENSSL_LIBRARY}
         )
-        target_include_directories(core_system_parameters
+        target_include_directories(${target}
             PUBLIC
                 ${GNUTLS_INCLUDE_DIR}
         )

From 22b6d703185b85bcb5e41213d13a4f345fbe2478 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@cttc.es>
Date: Sun, 30 Jun 2024 09:45:51 +0200
Subject: [PATCH 08/14] Fix building of benchmarks in some environments

---
 src/tests/benchmarks/CMakeLists.txt | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/src/tests/benchmarks/CMakeLists.txt b/src/tests/benchmarks/CMakeLists.txt
index 133174871..28bddd050 100644
--- a/src/tests/benchmarks/CMakeLists.txt
+++ b/src/tests/benchmarks/CMakeLists.txt
@@ -104,10 +104,15 @@ macro(add_benchmark)
     )
 endmacro()
 
+set(EXTRA_BENCHMARK_DEPENDENCIES "")
+if(ENABLE_GLOG_AND_GFLAGS)
+    set(EXTRA_BENCHMARK_DEPENDENCIES "Gflags::gflags;Glog::glog")
+endif()
+
 add_benchmark(benchmark_copy)
-add_benchmark(benchmark_preamble core_system_parameters)
-add_benchmark(benchmark_detector core_system_parameters)
-add_benchmark(benchmark_reed_solomon core_system_parameters)
+add_benchmark(benchmark_preamble core_system_parameters ${EXTRA_BENCHMARK_DEPENDENCIES})
+add_benchmark(benchmark_detector core_system_parameters ${EXTRA_BENCHMARK_DEPENDENCIES})
+add_benchmark(benchmark_reed_solomon core_system_parameters ${EXTRA_BENCHMARK_DEPENDENCIES})
 add_benchmark(benchmark_atan2 Gnuradio::runtime)
 
 if(has_std_plus_void)

From 413e5309ba9ed29772c46d06da9517953d746ee7 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@cttc.es>
Date: Sun, 30 Jun 2024 10:10:03 +0200
Subject: [PATCH 09/14] Add missing include

---
 .../signal-processing-blocks/osnma/osnma_msg_receiver_test.cc    | 1 +
 1 file changed, 1 insertion(+)

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 743c2b877..dc3e69684 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
@@ -1,5 +1,6 @@
 #include <gtest/gtest.h>
 #include <bitset>
+#include <chrono>
 #include <fstream>
 #include <vector>
 #include "osnma_msg_receiver.h"

From d984822b453d7ba3fdac7182bebead653aecd985 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@cttc.es>
Date: Sun, 30 Jun 2024 10:24:21 +0200
Subject: [PATCH 10/14] Fix for cross-compilation

---
 src/core/libs/osnma_msg_receiver.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/libs/osnma_msg_receiver.cc b/src/core/libs/osnma_msg_receiver.cc
index 45ad958ee..955712c76 100644
--- a/src/core/libs/osnma_msg_receiver.cc
+++ b/src/core/libs/osnma_msg_receiver.cc
@@ -317,7 +317,7 @@ void osnma_msg_receiver::local_time_verification(const std::shared_ptr<OSNMA_msg
             // std::cout << "Galileo OSNMA: d_receiver_time: " << d_receiver_time << std::endl;
         }
     // verify time constraint
-    std::time_t delta_T = abs(d_receiver_time - d_GST_SIS);
+    std::time_t delta_T = std::abs(static_cast<int64_t>(d_receiver_time - d_GST_SIS));
     if (delta_T <= d_T_L)
         {
             d_tags_allowed = tags_to_verify::all;

From c2bb06076af3dc20b819a0cdc75fc2c2f0a2bb4a Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@cttc.es>
Date: Sun, 30 Jun 2024 12:04:57 +0200
Subject: [PATCH 11/14] Fix for old OpenSSL

---
 src/core/system_parameters/gnss_crypto.cc | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/core/system_parameters/gnss_crypto.cc b/src/core/system_parameters/gnss_crypto.cc
index a50e2f00b..322e4acdd 100644
--- a/src/core/system_parameters/gnss_crypto.cc
+++ b/src/core/system_parameters/gnss_crypto.cc
@@ -547,7 +547,7 @@ void Gnss_Crypto::readPublicKeyFromPEM(const std::string& pemFilePath)
     gnutls_pubkey_deinit(pubkey);
 #else  // OpenSSL
     // Create a BIO object from the string data
-    BIO* bio = BIO_new_mem_buf(pemContent.c_str(), pemContent.length());
+    BIO* bio = BIO_new_mem_buf(const_cast<char*>(pemContent.c_str()), pemContent.length());
     if (!bio)
         {
             std::cerr << "OpenSSL: error creating a BIO object with data read from file " << pemFilePath << ". Aborting import" << std::endl;
@@ -861,7 +861,7 @@ void Gnss_Crypto::set_public_key(const std::vector<uint8_t>& publicKey)
 #else  // OpenSSL
     BIO* bio = nullptr;
     EVP_PKEY* pkey = nullptr;
-    bio = BIO_new_mem_buf(publicKey.data(), publicKey.size());
+    bio = BIO_new_mem_buf(const_cast<uint8_t*>(publicKey.data()), publicKey.size());
     if (!bio)
         {
             std::cerr << "Failed to create BIO for key \n";

From 95e3329f1011779628bd75baf97f99035c7ff3fb Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@cttc.es>
Date: Mon, 1 Jul 2024 01:31:09 +0200
Subject: [PATCH 12/14] Fix for old GnuTLS

---
 cmake/Modules/GnssSdrCrypto.cmake         | 37 +++++++++++++++++++
 src/core/system_parameters/gnss_crypto.cc | 45 +++++++++++++++++------
 src/core/system_parameters/gnss_crypto.h  |  1 +
 3 files changed, 71 insertions(+), 12 deletions(-)

diff --git a/cmake/Modules/GnssSdrCrypto.cmake b/cmake/Modules/GnssSdrCrypto.cmake
index 67648d702..a5e383790 100644
--- a/cmake/Modules/GnssSdrCrypto.cmake
+++ b/cmake/Modules/GnssSdrCrypto.cmake
@@ -82,6 +82,15 @@ else()
             /opt/local/lib
     )
 
+    find_path(GNUTLS_INCLUDE_DIR NAMES gnutls/gnutls.h
+        PATHS
+            /usr/include
+            /usr/local/include
+            /opt/local/include   # default location in Macports
+            /opt/homebrew/opt/gnutls/include/
+            ${GNUTLS_ROOT_DIR}/include/
+    )
+
     if(NOT GNUTLS_OPENSSL_LIBRARY)
         message(" The GnuTLS library with openssl compatibility enabled has not been found.")
         message(" You can try to install the required libraries by typing:")
@@ -98,6 +107,22 @@ else()
         endif()
         message(FATAL_ERROR "OpenSSL or the GnuTLS libraries with openssl compatibility are required to build gnss-sdr")
     endif()
+
+    # Test GnuTLS capabilities
+    file(READ "${GNUTLS_INCLUDE_DIR}/gnutls/gnutls.h" gnutls_gnutls_file_contents)
+    if("${gnutls_gnutls_file_contents}" MATCHES "GNUTLS_SIGN_ECDSA_SHA256")
+        set(GNUTLS_SIGN_ECDSA_SHA256 TRUE)
+    endif()
+    if("${gnutls_gnutls_file_contents}" MATCHES "GNUTLS_DIG_SHA3_256")
+        set(GNUTLS_DIG_SHA3_256 TRUE)
+    endif()
+    if("${gnutls_gnutls_file_contents}" MATCHES "#define GNUTLS_VERSION_MAJOR 2")
+        set(GNUTLS_HMAC_INIT_WITH_DIGEST TRUE)
+    endif()
+    file(READ "${GNUTLS_INCLUDE_DIR}/gnutls/abstract.h" gnutls_abstract_file_contents)
+    if("${gnutls_abstract_file_contents}" MATCHES "gnutls_pubkey_export2")
+        set(GNUTLS_PUBKEY_EXPORT2 TRUE)
+    endif()
 endif()
 
 ################################################################################
@@ -147,5 +172,17 @@ function(link_to_crypto_dependencies target)
                 ${GNUTLS_INCLUDE_DIR}
         )
         target_compile_definitions(${target} PUBLIC -DUSE_GNUTLS_FALLBACK=1)
+        if(GNUTLS_SIGN_ECDSA_SHA256)
+            target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_SIGN_ECDSA_SHA256=1)
+        endif()
+        if(GNUTLS_DIG_SHA3_256)
+            target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_DIG_SHA3_256=1)
+        endif()
+        if(GNUTLS_PUBKEY_EXPORT2)
+            target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_PUBKEY_EXPORT2=1)
+        endif()
+        if(GNUTLS_HMAC_INIT_WITH_DIGEST)
+            target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_HMAC_INIT_WITH_DIGEST=1)
+        endif()
     endif()
 endfunction()
diff --git a/src/core/system_parameters/gnss_crypto.cc b/src/core/system_parameters/gnss_crypto.cc
index 322e4acdd..8ad7e55bb 100644
--- a/src/core/system_parameters/gnss_crypto.cc
+++ b/src/core/system_parameters/gnss_crypto.cc
@@ -27,7 +27,6 @@
 
 #if USE_GNUTLS_FALLBACK
 #include <cstring>
-#include <gnutls/abstract.h>
 #include <gnutls/crypto.h>
 #include <gnutls/x509.h>
 #else  // OpenSSL
@@ -61,6 +60,11 @@ Gnss_Crypto::Gnss_Crypto()
 {
 #if USE_GNUTLS_FALLBACK
     gnutls_global_init();
+#if !HAVE_GNUTLS_SIGN_ECDSA_SHA256
+    LOG(WARNING) << "The GnuTLS library version you are linking against is too old for some OSNMA functions."
+                 << " Please do not trust OSNMA ouputs or upgrade your system to a newer version of GnuTLS or OpenSSL"
+                 << " and rebuild GNSS-SDR against it.";
+#endif
 #else  // OpenSSL
 #if !(USE_OPENSSL_3 || USE_OPENSSL_111)
     LOG(WARNING) << "The OpenSSL library version you are linking against is too old for some OSNMA functions."
@@ -75,6 +79,11 @@ Gnss_Crypto::Gnss_Crypto(const std::string& certFilePath, const std::string& mer
 {
 #if USE_GNUTLS_FALLBACK
     gnutls_global_init();
+#if !HAVE_GNUTLS_SIGN_ECDSA_SHA256
+    LOG(WARNING) << "The GnuTLS library version you are linking against is too old for some OSNMA functions."
+                 << " Please do not trust OSNMA ouputs or upgrade your system to a newer version of GnuTLS or OpenSSL"
+                 << " and rebuild GNSS-SDR against it.";
+#endif
 #else  // OpenSSL
 #if !(USE_OPENSSL_3 || USE_OPENSSL_111)
     LOG(WARNING) << "The OpenSSL library version you are linking against is too old for some OSNMA functions."
@@ -305,6 +314,7 @@ std::vector<uint8_t> Gnss_Crypto::computeSHA3_256(const std::vector<uint8_t>& in
 {
     std::vector<uint8_t> output(32);  // SHA256 hash size
 #if USE_GNUTLS_FALLBACK
+#if HAVE_GNUTLS_DIG_SHA3_256
     std::vector<uint8_t> output_aux(32);
     gnutls_hash_hd_t hashHandle;
     gnutls_hash_init(&hashHandle, GNUTLS_DIG_SHA3_256);
@@ -312,6 +322,7 @@ std::vector<uint8_t> Gnss_Crypto::computeSHA3_256(const std::vector<uint8_t>& in
     gnutls_hash_output(hashHandle, output_aux.data());
     output = output_aux;
     gnutls_hash_deinit(hashHandle, output_aux.data());
+#endif
 #else  // OpenSSL
 #if USE_OPENSSL_3 || USE_OPENSSL_111
     EVP_MD_CTX* mdctx = EVP_MD_CTX_new();
@@ -339,7 +350,11 @@ std::vector<uint8_t> Gnss_Crypto::computeHMAC_SHA_256(const std::vector<uint8_t>
 #if USE_GNUTLS_FALLBACK
     std::vector<uint8_t> output_aux(32);
     gnutls_hmac_hd_t hmac;
+#if HAVE_GNUTLS_HMAC_INIT_WITH_DIGEST
+    gnutls_hmac_init(&hmac, GNUTLS_DIG_SHA256, key.data(), key.size());
+#else
     gnutls_hmac_init(&hmac, GNUTLS_MAC_SHA256, key.data(), key.size());
+#endif
     gnutls_hmac(hmac, input.data(), input.size());
     gnutls_hmac_output(hmac, output_aux.data());
     output = output_aux;
@@ -421,16 +436,15 @@ std::vector<uint8_t> Gnss_Crypto::computeCMAC_AES(const std::vector<uint8_t>& ke
 {
     std::vector<uint8_t> output(16);
 #if USE_GNUTLS_FALLBACK
-    gnutls_cipher_hd_t cipher;
-    std::vector<uint8_t> mac(16);
-    std::vector<uint8_t> message = input;
-    gnutls_datum_t key_data = {const_cast<uint8_t*>(key.data()), static_cast<unsigned int>(key.size())};
-    gnutls_cipher_init(&cipher, GNUTLS_CIPHER_AES_128_CBC, &key_data, nullptr);
-    gnutls_cipher_set_iv(cipher, nullptr, 16);                      // Set IV to zero
-    gnutls_cipher_encrypt(cipher, message.data(), message.size());  // Encrypt the message with AES-128
-    gnutls_cipher_tag(cipher, mac.data(), mac.size());              // Get the CMAC-AES tag
-    output = mac;
-    gnutls_cipher_deinit(cipher);
+    // CMAC-AES not implemented in GnuTLS
+    if (!key.empty())
+        {
+            // do nothing
+        }
+    if (!input.empty())
+        {
+            // do nothing
+        }
 #else  // OpenSSL
 #if USE_OPENSSL_3
     std::vector<uint8_t> aux(EVP_MAX_MD_SIZE);  // CMAC-AES output size
@@ -527,7 +541,7 @@ void Gnss_Crypto::readPublicKeyFromPEM(const std::string& pemFilePath)
     std::string pemContent((std::istreambuf_iterator<char>(pemFile)), std::istreambuf_iterator<char>());
 #if USE_GNUTLS_FALLBACK
     // Import the PEM data
-    gnutls_datum_t pemDatum = {const_cast<unsigned char*>(reinterpret_cast<unsigned char*>(pemContent.data())), static_cast<unsigned int>(pemContent.size())};
+    gnutls_datum_t pemDatum = {const_cast<unsigned char*>(reinterpret_cast<unsigned char*>(const_cast<char*>(pemContent.data()))), static_cast<unsigned int>(pemContent.size())};
     gnutls_pubkey_t pubkey;
     gnutls_pubkey_init(&pubkey);
 
@@ -680,6 +694,7 @@ bool Gnss_Crypto::verify_signature(const std::vector<uint8_t>& message, const st
         }
     bool success = false;
 #if USE_GNUTLS_FALLBACK
+#if HAVE_GNUTLS_SIGN_ECDSA_SHA256
     // Convert signature to DER format
     std::vector<uint8_t> der_sig;
     if (!convert_raw_to_der_ecdsa(signature, der_sig))
@@ -704,6 +719,7 @@ bool Gnss_Crypto::verify_signature(const std::vector<uint8_t>& message, const st
             std::cerr << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret) << std::endl;
             LOG(WARNING) << "GnuTLS: OSNMA message authentication failed: " << gnutls_strerror(ret);
         }
+#endif
 #else  // OpenSSL
 #if USE_OPENSSL_3
     EVP_PKEY_CTX* ctx;
@@ -950,7 +966,12 @@ bool Gnss_Crypto::pubkey_copy(gnutls_pubkey_t src, gnutls_pubkey_t* dest)
     gnutls_datum_t key_datum;
 
     // Export the public key from src to memory
+#if HAVE_GNUTLS_PUBKEY_EXPORT2
     int ret = gnutls_pubkey_export2(src, GNUTLS_X509_FMT_PEM, &key_datum);
+#else
+    size_t output_stata_size;
+    int ret = gnutls_pubkey_export(src, GNUTLS_X509_FMT_PEM, &key_datum, &output_stata_size);
+#endif
     if (ret < 0)
         {
             gnutls_free(key_datum.data);
diff --git a/src/core/system_parameters/gnss_crypto.h b/src/core/system_parameters/gnss_crypto.h
index 7e0527750..69df8d45d 100644
--- a/src/core/system_parameters/gnss_crypto.h
+++ b/src/core/system_parameters/gnss_crypto.h
@@ -23,6 +23,7 @@
 #include <string>
 #include <vector>
 #if USE_GNUTLS_FALLBACK
+#include <gnutls/abstract.h>
 #include <gnutls/gnutls.h>
 #else  // OpenSSL
 #include <openssl/ec.h>

From 2f475d6aafbe01113fcb75f8e80a95036ee18b3a Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@cttc.es>
Date: Mon, 1 Jul 2024 02:44:22 +0200
Subject: [PATCH 13/14] Fix CMAC-AES algorithm for OpenSSL 1.x

---
 src/core/system_parameters/gnss_crypto.cc | 37 +++++++++++++++++++----
 1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/src/core/system_parameters/gnss_crypto.cc b/src/core/system_parameters/gnss_crypto.cc
index 8ad7e55bb..2062b0674 100644
--- a/src/core/system_parameters/gnss_crypto.cc
+++ b/src/core/system_parameters/gnss_crypto.cc
@@ -504,20 +504,45 @@ std::vector<uint8_t> Gnss_Crypto::computeCMAC_AES(const std::vector<uint8_t>& ke
     aux.resize(output_length);
     output = aux;
 #else  // OpenSSL 1.x
-    std::vector<uint8_t> mac(16);  // CMAC-AES output size
+    size_t mac_length = 0;  // to hold the length of the MAC output
 
     // Create CMAC context
     CMAC_CTX* cmacCtx = CMAC_CTX_new();
-    CMAC_Init(cmacCtx, key.data(), key.size(), EVP_aes_128_cbc(), nullptr);
+    if (!cmacCtx)
+        {
+            LOG(INFO) << "OSNMA CMAC-AES: Failed to create CMAC context";
+            return output;
+        }
 
-    // Compute CMAC-AES
-    CMAC_Update(cmacCtx, input.data(), input.size());
-    CMAC_Final(cmacCtx, mac.data(), nullptr);
+    // Initialize the CMAC context with the key and cipher
+    if (1 != CMAC_Init(cmacCtx, key.data(), key.size(), EVP_aes_128_cbc(), nullptr))
+        {
+            LOG(INFO) << "OSNMA CMAC-AES: MAC_Init failed";
+            CMAC_CTX_free(cmacCtx);
+            return output;
+        }
+
+    // Compute the CMAC
+    if (1 != CMAC_Update(cmacCtx, input.data(), input.size()))
+        {
+            LOG(INFO) << "OSNMA CMAC-AES: CMAC_Update failed";
+            CMAC_CTX_free(cmacCtx);
+            return output;
+        }
+
+    // Finalize the CMAC computation and retrieve the output
+    if (1 != CMAC_Final(cmacCtx, output.data(), &mac_length))
+        {
+            LOG(INFO) << "OSNMA CMAC-AES:CMAC_Final failed";
+            CMAC_CTX_free(cmacCtx);
+            return output;
+        }
 
     // Clean up CMAC context
     CMAC_CTX_free(cmacCtx);
 
-    output = mac;
+    // Ensure the output vector is properly sized according to the actual MAC length
+    output.resize(mac_length);
 #endif
 #endif
     return output;

From 4b4f6b9d7f9e1a255c5109979d7d34d4d2c72578 Mon Sep 17 00:00:00 2001
From: Carles Fernandez <carles.fernandez@cttc.es>
Date: Mon, 1 Jul 2024 22:43:54 +0200
Subject: [PATCH 14/14] Fix CMAC-AES with GnuTLS

---
 cmake/Modules/GnssSdrCrypto.cmake         |  6 ++++
 src/core/system_parameters/gnss_crypto.cc | 36 +++++++++++++++++++----
 2 files changed, 37 insertions(+), 5 deletions(-)

diff --git a/cmake/Modules/GnssSdrCrypto.cmake b/cmake/Modules/GnssSdrCrypto.cmake
index a5e383790..9649da9da 100644
--- a/cmake/Modules/GnssSdrCrypto.cmake
+++ b/cmake/Modules/GnssSdrCrypto.cmake
@@ -119,6 +119,9 @@ else()
     if("${gnutls_gnutls_file_contents}" MATCHES "#define GNUTLS_VERSION_MAJOR 2")
         set(GNUTLS_HMAC_INIT_WITH_DIGEST TRUE)
     endif()
+    if("${gnutls_gnutls_file_contents}" MATCHES "GNUTLS_MAC_AES_CMAC_128")
+       set(GNUTLS_MAC_AES_CMAC_128 TRUE)
+    endif()
     file(READ "${GNUTLS_INCLUDE_DIR}/gnutls/abstract.h" gnutls_abstract_file_contents)
     if("${gnutls_abstract_file_contents}" MATCHES "gnutls_pubkey_export2")
         set(GNUTLS_PUBKEY_EXPORT2 TRUE)
@@ -184,5 +187,8 @@ function(link_to_crypto_dependencies target)
         if(GNUTLS_HMAC_INIT_WITH_DIGEST)
             target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_HMAC_INIT_WITH_DIGEST=1)
         endif()
+        if(GNUTLS_MAC_AES_CMAC_128)
+            target_compile_definitions(${target} PRIVATE -DHAVE_GNUTLS_MAC_AES_CMAC_128=1)
+        endif()
     endif()
 endfunction()
diff --git a/src/core/system_parameters/gnss_crypto.cc b/src/core/system_parameters/gnss_crypto.cc
index 2062b0674..055bd0382 100644
--- a/src/core/system_parameters/gnss_crypto.cc
+++ b/src/core/system_parameters/gnss_crypto.cc
@@ -436,7 +436,32 @@ std::vector<uint8_t> Gnss_Crypto::computeCMAC_AES(const std::vector<uint8_t>& ke
 {
     std::vector<uint8_t> output(16);
 #if USE_GNUTLS_FALLBACK
-    // CMAC-AES not implemented in GnuTLS
+#if HAVE_GNUTLS_MAC_AES_CMAC_128
+    gnutls_hmac_hd_t hmac;
+
+    // Initialize the HMAC context with the CMAC algorithm and key
+    int ret = gnutls_hmac_init(&hmac, GNUTLS_MAC_AES_CMAC_128, key.data(), key.size());
+    if (ret != GNUTLS_E_SUCCESS)
+        {
+            LOG(INFO) << "OSNMA CMAC-AES: gnutls_hmac_init failed: " << gnutls_strerror(ret);
+            return output;
+        }
+
+    // Update the HMAC context with the input data
+    ret = gnutls_hmac(hmac, input.data(), input.size());
+    if (ret != GNUTLS_E_SUCCESS)
+        {
+            LOG(INFO) << "OSNMA CMAC-AES: gnutls_hmac failed: " << gnutls_strerror(ret);
+            gnutls_hmac_deinit(hmac, nullptr);
+            return output;
+        }
+
+    // Retrieve the HMAC output
+    gnutls_hmac_output(hmac, output.data());
+
+    // Clean up the HMAC context
+    gnutls_hmac_deinit(hmac, nullptr);
+#else
     if (!key.empty())
         {
             // do nothing
@@ -445,6 +470,7 @@ std::vector<uint8_t> Gnss_Crypto::computeCMAC_AES(const std::vector<uint8_t>& ke
         {
             // do nothing
         }
+#endif
 #else  // OpenSSL
 #if USE_OPENSSL_3
     std::vector<uint8_t> aux(EVP_MAX_MD_SIZE);  // CMAC-AES output size
@@ -515,7 +541,7 @@ std::vector<uint8_t> Gnss_Crypto::computeCMAC_AES(const std::vector<uint8_t>& ke
         }
 
     // Initialize the CMAC context with the key and cipher
-    if (1 != CMAC_Init(cmacCtx, key.data(), key.size(), EVP_aes_128_cbc(), nullptr))
+    if (CMAC_Init(cmacCtx, key.data(), key.size(), EVP_aes_128_cbc(), nullptr) != 1)
         {
             LOG(INFO) << "OSNMA CMAC-AES: MAC_Init failed";
             CMAC_CTX_free(cmacCtx);
@@ -523,7 +549,7 @@ std::vector<uint8_t> Gnss_Crypto::computeCMAC_AES(const std::vector<uint8_t>& ke
         }
 
     // Compute the CMAC
-    if (1 != CMAC_Update(cmacCtx, input.data(), input.size()))
+    if (CMAC_Update(cmacCtx, input.data(), input.size()) != 1)
         {
             LOG(INFO) << "OSNMA CMAC-AES: CMAC_Update failed";
             CMAC_CTX_free(cmacCtx);
@@ -531,9 +557,9 @@ std::vector<uint8_t> Gnss_Crypto::computeCMAC_AES(const std::vector<uint8_t>& ke
         }
 
     // Finalize the CMAC computation and retrieve the output
-    if (1 != CMAC_Final(cmacCtx, output.data(), &mac_length))
+    if (CMAC_Final(cmacCtx, output.data(), &mac_length) != 1)
         {
-            LOG(INFO) << "OSNMA CMAC-AES:CMAC_Final failed";
+            LOG(INFO) << "OSNMA CMAC-AES: CMAC_Final failed";
             CMAC_CTX_free(cmacCtx);
             return output;
         }