1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2025-01-31 11:19:18 +00:00

[TAS-248] [BUG][Kroot] Kroot and PK available, but until DSM-Kroot arrived no MACK processing

* reverted commit [TAS-247][FEAT][Kroot] enable hotstart with last known Kroot
* DSM-KROOT loaded during startup
* if new DSM verified => stored
* this improves TTFAF from 4 min to 1 minute.
This commit is contained in:
cesaaargm 2024-07-27 18:25:20 +02:00
parent 54c1a19823
commit a4cfe51515
7 changed files with 92 additions and 113 deletions

View File

@ -24,7 +24,7 @@
#include "gnss_satellite.h"
#include "osnma_dsm_reader.h" // for OSNMA_DSM_Reader
#include "osnma_helper.h"
#include "osnma_nav_data_manager.h" // TODO - all these repeated includes, is it good practice to include them in the source file?
#include "osnma_nav_data_manager.h"
#include <gnuradio/io_signature.h> // for gr::io_signature::make
#include <algorithm>
#include <cmath>
@ -37,6 +37,7 @@
#include <tuple>
#include <typeinfo> // for typeid
#include <utility>
#include <fstream> // for std::ifstream and std::ofstream
#if USE_GLOG_AND_GFLAGS
@ -60,24 +61,36 @@ namespace wht = std;
#endif
osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath)
osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath)
{
return osnma_msg_receiver_sptr(new osnma_msg_receiver(pemFilePath, merkleFilePath, rootKeyFilePath));
return osnma_msg_receiver_sptr(new osnma_msg_receiver(pemFilePath, merkleFilePath));
}
osnma_msg_receiver::osnma_msg_receiver(const std::string& crtFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath) : gr::block("osnma_msg_receiver",
osnma_msg_receiver::osnma_msg_receiver(const std::string& crtFilePath, const std::string& merkleFilePath) : gr::block("osnma_msg_receiver",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0))
{
d_dsm_reader = std::make_unique<OSNMA_DSM_Reader>();
d_crypto = std::make_unique<Gnss_Crypto>(crtFilePath, merkleFilePath, rootKeyFilePath);
d_crypto = std::make_unique<Gnss_Crypto>(crtFilePath, merkleFilePath);
d_helper = std::make_unique<Osnma_Helper>();
d_nav_data_manager = std::make_unique<OSNMA_nav_data_Manager>();
if(d_crypto->have_root_key()){
d_kroot = d_crypto->get_root_key();
d_kroot_verified = true;
if(d_crypto->have_public_key()){ // Hot start is enabled
auto dsm_nmah = parse_dsm_kroot();
if (!dsm_nmah.first.empty()){
LOG(WARNING) << "OSNMA DSM-KROOT and NMA Header successfully read from file " << KROOTFILE_DEFAULT;
std::cout << "OSNMA DSM-KROOT and NMA Header successfully read from file " << KROOTFILE_DEFAULT << std::endl;
d_flag_hot_start = true;
process_dsm_message(dsm_nmah.first, dsm_nmah.second);
LOG(WARNING) << "OSNMA DSM-KROOT available :: HOT START";
std::cout << "OSNMA DSM-KROOT available :: HOT START" << std::endl;
}
else
{
LOG(WARNING) << "OSNMA DSM-KROOT not available :: WARM START";
std::cout << "OSNMA DSM-KROOT not available :: WARM START" << std::endl;
}
}
// register OSNMA input message port from telemetry blocks
@ -369,7 +382,7 @@ void osnma_msg_receiver::process_dsm_block(const std::shared_ptr<OSNMA_msg>& osn
}
d_dsm_message[d_osnma_data.d_dsm_header.dsm_id] = std::array<uint8_t, 256>{};
d_dsm_id_received[d_osnma_data.d_dsm_header.dsm_id] = std::array<uint8_t, 16>{};
process_dsm_message(dsm_msg, osnma_msg);
process_dsm_message(dsm_msg, osnma_msg->hkroot[0]);
}
}
@ -381,10 +394,10 @@ void osnma_msg_receiver::process_dsm_block(const std::shared_ptr<OSNMA_msg>& osn
* case DSM-PKR:
* - calls verify_dsm_pkr to verify the public key
* */
void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg, const std::shared_ptr<OSNMA_msg>& osnma_msg)
void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg, const uint8_t& nma_header)
{
// DSM-KROOT message
if (d_osnma_data.d_dsm_header.dsm_id < 12)
if (d_osnma_data.d_dsm_header.dsm_id < 12 || d_flag_hot_start)
{
// Parse Kroot message
LOG(INFO) << "Galileo OSNMA: DSM-KROOT message received.";
@ -438,13 +451,13 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
// validation of padding
const uint16_t size_m = 13 + l_lk_bytes;
std::vector<uint8_t> MSG;
MSG.reserve(size_m + l_ds_bytes + 1); // C: message will get too many zeroes? ((12+1)+16) + 64 + 1? => in theory not, allocating is not assigning
MSG.push_back(osnma_msg->hkroot[0]); // C: NMA header
MSG.reserve(size_m + l_ds_bytes + 1);
MSG.push_back(nma_header); // NMA header
for (uint16_t i = 1; i < size_m; i++)
{
MSG.push_back(dsm_msg[i]);
}
std::vector<uint8_t> message = MSG; // MSG = (M | DS) from ICD. Eq. 7
std::vector<uint8_t> message = MSG; // MSG = (M | DS) from ICD. Eq. 7
for (uint16_t k = 0; k < l_ds_bytes; k++)
{
MSG.push_back(d_osnma_data.d_dsm_kroot_message.ds[k]);
@ -478,7 +491,7 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
<< ", PKID=" << static_cast<uint32_t>(d_osnma_data.d_dsm_kroot_message.pkid)
<< ", WN=" << static_cast<uint32_t>(d_osnma_data.d_dsm_kroot_message.wn_k)
<< ", TOW=" << static_cast<uint32_t>(d_osnma_data.d_dsm_kroot_message.towh_k) * 3600;
local_time_verification(osnma_msg);
// local_time_verification(osnma_msg); // FIXME TODO: real time verification needed
if (l_ds_bits == 512)
{
d_kroot_verified = d_crypto->verify_signature_ecdsa_p256(message, d_osnma_data.d_dsm_kroot_message.ds);
@ -494,10 +507,12 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
LOG(INFO) << "Galileo OSNMA: NMA Status is " << d_dsm_reader->get_nmas_status(d_osnma_data.d_nma_header.nmas) << ", "
<< "Chain in force is " << static_cast<uint32_t>(d_osnma_data.d_nma_header.cid) << ", "
<< "Chain and Public Key Status is " << d_dsm_reader->get_cpks_status(d_osnma_data.d_nma_header.cpks);
// Save Kroot into a permanent storage
d_crypto->store_root_key(ROOTKEYFILE_DEFAULT);
d_kroot = d_osnma_data.d_dsm_kroot_message.kroot;
d_crypto->set_root_key(d_kroot);
// Save DSM-Kroot and NMA header into a permanent storage
if(d_flag_hot_start){
d_flag_hot_start = false;
return;
}
store_dsm_kroot(dsm_msg, nma_header);
}
else
{
@ -576,7 +591,7 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
{
d_public_key_verified = true;
d_crypto->set_public_key(d_osnma_data.d_dsm_pkr_message.npk);
d_crypto->store_public_key(PEMFILE_STORED);
d_crypto->store_public_key(PEMFILE_DEFAULT);
}
}
}
@ -1812,6 +1827,7 @@ std::vector<MACK_tag_and_info> osnma_msg_receiver::verify_macseq_new(const MACK_
return verified_tags;
}
}
void osnma_msg_receiver::send_data_to_pvt(std::vector<OSNMA_NavData> data)
{
if (!data.empty())
@ -1823,3 +1839,43 @@ void osnma_msg_receiver::send_data_to_pvt(std::vector<OSNMA_NavData> data)
}
}
}
bool osnma_msg_receiver::store_dsm_kroot(const std::vector<uint8_t>& dsm, const uint8_t nma_header) const
{
std::ofstream file(KROOTFILE_DEFAULT, std::ios::binary | std::ios::out);
if (!file.is_open()) {
return false;
}
// NMA header
file.write(reinterpret_cast<const char*>(&nma_header), 1);
// Then writing the entire dsm_msg vector to the file
file.write(reinterpret_cast<const char*>(dsm.data()), dsm.size());
return file.good();
}
std::pair<std::vector<uint8_t>, uint8_t> osnma_msg_receiver::parse_dsm_kroot() const
{
std::ifstream file(KROOTFILE_DEFAULT, std::ios::binary | std::ios::in);
if (!file) {
return {std::vector<uint8_t>(), 0};
}
// Read the first byte into hkroot[0]
uint8_t nma_header;
file.read(reinterpret_cast<char*>(&nma_header), 1);
// Read the remaining file content into dsm_msg
std::vector<uint8_t> dsm_msg((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
file.close();
if (file.bad()) {
return {std::vector<uint8_t>(), 0};
}
return {dsm_msg, nma_header};
}

View File

@ -50,7 +50,7 @@ class osnma_msg_receiver;
using osnma_msg_receiver_sptr = gnss_shared_ptr<osnma_msg_receiver>;
osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath);
osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath);
/*!
* \brief GNU Radio block that receives asynchronous OSNMA messages
@ -63,8 +63,8 @@ class osnma_msg_receiver : public gr::block
public:
~osnma_msg_receiver() = default; //!< Default destructor
private:
friend osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath);
osnma_msg_receiver(const std::string& crtFilePath, const std::string& merkleFilePath, const std::string& rootKeyFilePath);
friend osnma_msg_receiver_sptr osnma_msg_receiver_make(const std::string& pemFilePath, const std::string& merkleFilePath);
osnma_msg_receiver(const std::string& crtFilePath, const std::string& merkleFilePath);
void msg_handler_osnma(const pmt::pmt_t& msg);
void process_osnma_message(const std::shared_ptr<OSNMA_msg>& osnma_msg);
@ -73,7 +73,7 @@ private:
void read_dsm_block(const std::shared_ptr<OSNMA_msg>& osnma_msg);
void local_time_verification(const std::shared_ptr<OSNMA_msg>& osnma_msg);
void process_dsm_block(const std::shared_ptr<OSNMA_msg>& osnma_msg);
void process_dsm_message(const std::vector<uint8_t>& dsm_msg, const std::shared_ptr<OSNMA_msg>& osnma_msg);
void process_dsm_message(const std::vector<uint8_t>& dsm_msg, const uint8_t& nma_header);
void read_and_process_mack_block(const std::shared_ptr<OSNMA_msg>& osnma_msg);
void read_mack_header();
void read_mack_body();
@ -89,7 +89,9 @@ private:
bool tag_has_key_available(const Tag& t) const;
bool verify_macseq(const MACK_message& mack);
bool verify_dsm_pkr(const DSM_PKR_message& message) const;
bool store_dsm_kroot(const std::vector<uint8_t>& dsm, const uint8_t nma_header) const;
std::pair<std::vector<uint8_t>, uint8_t> parse_dsm_kroot() const;
std::vector<uint8_t> get_merkle_tree_leaves(const DSM_PKR_message& dsm_pkr_message) const;
std::vector<uint8_t> compute_merkle_root(const DSM_PKR_message& dsm_pkr_message, const std::vector<uint8_t>& m_i) const;
std::vector<uint8_t> build_message(Tag& tag) const;
@ -100,7 +102,6 @@ private:
std::map<uint32_t, std::vector<uint8_t>> d_tesla_keys; // tesla keys over time, sorted by TOW
std::multimap<uint32_t, Tag> d_tags_awaiting_verify; // container with tags to verify from arbitrary SVIDs, sorted by TOW
std::vector<uint8_t> d_kroot; // last available stored root key
std::vector<uint8_t> d_tags_to_verify{0, 4, 12};
std::vector<MACK_message> d_macks_awaiting_MACSEQ_verification;
@ -143,6 +144,7 @@ private:
bool d_kroot_verified{false};
bool d_tesla_key_verified{false};
bool d_flag_debug{false};
bool d_flag_hot_start{false};
// Provide access to inner functions to Gtest
FRIEND_TEST(OsnmaMsgReceiverTest, TeslaKeyVerification);

View File

@ -126,8 +126,7 @@ void GNSSFlowgraph::init()
enable_osnma_rx_ = true;
const auto certFilePath = configuration_->property("GNSS-SDR.osnma_public_key", CRTFILE_DEFAULT);
const auto merKleTreePath = configuration_->property("GNSS-SDR.osnma_merkletree", MERKLEFILE_DEFAULT);
const auto rootKeyPath = configuration_->property("GNSS-SDR.osnma_root_key", ROOTKEYFILE_DEFAULT);
osnma_rx_ = osnma_msg_receiver_make(certFilePath, merKleTreePath, rootKeyPath);
osnma_rx_ = osnma_msg_receiver_make(certFilePath, merKleTreePath);
}
else
{

View File

@ -160,10 +160,10 @@ const std::unordered_map<std::string, uint16_t> OSNMA_TABLE_15 = {
{std::string("SHA-256"), 512},
{std::string("SHA-512"), 1056}}; // key: ECDSA Curve and hash function, value: {l_ds_bits}
const std::string PEMFILE_STORED("./OSNMA_PublicKey.pem");
const std::string CRTFILE_DEFAULT("../data/OSNMA_PublicKey_20240115100000_newPKID_1.crt");
const std::string MERKLEFILE_DEFAULT("../data/OSNMA_MerkleTree_20240115100000_newPKID_1.xml");
const std::string ROOTKEYFILE_DEFAULT("../data/OSNMA_RootKey.bin");
const std::string PEMFILE_DEFAULT("./data/OSNMA_PublicKey.pem");
const std::string CRTFILE_DEFAULT("./data/OSNMA_PublicKey_20240115100000_newPKID_1.crt");
const std::string MERKLEFILE_DEFAULT("./data/OSNMA_MerkleTree_20240115100000_newPKID_1.xml");
const std::string KROOTFILE_DEFAULT("./data/OSNMA_DSM_KROOT_NMAHeader.bin");
class Mack_lookup
{

View File

@ -75,7 +75,7 @@ Gnss_Crypto::Gnss_Crypto()
}
Gnss_Crypto::Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath, const std::string& rootKeyFilePath)
Gnss_Crypto::Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath)
{
#if USE_GNUTLS_FALLBACK
gnutls_global_init();
@ -96,11 +96,10 @@ Gnss_Crypto::Gnss_Crypto(const std::string& certFilePath, const std::string& mer
readPublicKeyFromPEM(certFilePath);
if (!have_public_key())
{
readPublicKeyFromPEM(PEMFILE_STORED);
readPublicKeyFromPEM(PEMFILE_DEFAULT);
}
}
read_merkle_xml(merkleTreePath);
read_root_key(rootKeyFilePath);
}
@ -123,10 +122,6 @@ Gnss_Crypto::~Gnss_Crypto()
#endif
}
bool Gnss_Crypto::have_root_key() const
{
return !d_kroot.empty();
}
bool Gnss_Crypto::have_public_key() const
{
#if USE_GNUTLS_FALLBACK
@ -200,23 +195,6 @@ bool Gnss_Crypto::store_public_key(const std::string& pubKeyFilePath) const
return true;
}
bool Gnss_Crypto::store_root_key(const std::string& rootKeyFilePath) const
{
if (!have_root_key())
{
return false;
}
std::ofstream file(rootKeyFilePath, std::ios::binary | std::ios::out);
if (!file) {
return false;
}
file.write(reinterpret_cast<const char*>(d_kroot.data()), d_kroot.size());
return file.good();
}
bool Gnss_Crypto::verify_signature_ecdsa_p256(const std::vector<uint8_t>& message, const std::vector<uint8_t>& signature) const
{
std::vector<uint8_t> digest = this->compute_SHA_256(message);
@ -865,11 +843,6 @@ std::vector<uint8_t> Gnss_Crypto::get_merkle_root() const
return d_x_4_0;
}
std::vector<uint8_t> Gnss_Crypto::get_root_key() const
{
return d_kroot;
}
void Gnss_Crypto::set_public_key(const std::vector<uint8_t>& publicKey)
{
#if USE_GNUTLS_FALLBACK
@ -928,11 +901,6 @@ void Gnss_Crypto::set_merkle_root(const std::vector<uint8_t>& v)
d_x_4_0 = v;
}
void Gnss_Crypto::set_root_key(const std::vector<uint8_t>& root_key)
{
d_kroot = root_key;
}
void Gnss_Crypto::read_merkle_xml(const std::string& merkleFilePath)
{
pugi::xml_document doc;
@ -1172,41 +1140,6 @@ bool Gnss_Crypto::readPublicKeyFromCRT(const std::string& crtFilePath)
return true;
}
/**
* \brief Reads the TESLA root key from a file and stores it.
* \param rootKeyFilePath The file path of the TESLA root key.
* \return True if the root key was successfully read and stored, false otherwise.
*/
bool Gnss_Crypto::read_root_key(const std::string& rootKeyFilePath)
{
std::ifstream file(rootKeyFilePath, std::ios::binary | std::ios::in);
if (!file) {
LOG(WARNING) << "Unable to open file: " << rootKeyFilePath;
return false;
}
// Determine file size
file.seekg(0, std::ios::end);
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
if (size == 0) {
LOG(WARNING) << "File is empty: " << rootKeyFilePath;
return false;
}
// Resize the vector and read file
d_kroot.resize(size);
if (!file.read(reinterpret_cast<char*>(d_kroot.data()), size)) {
LOG(WARNING) << "Failed to read the file: " << rootKeyFilePath;
return false;
}
std::cout << "OSNMA TESLA Root Key successfully read from file " << rootKeyFilePath << std::endl;
LOG(INFO) << "OSNMA TESLA Root Key successfully read from file " << rootKeyFilePath;
return true;
}
bool Gnss_Crypto::convert_raw_to_der_ecdsa(const std::vector<uint8_t>& raw_signature, std::vector<uint8_t>& der_signature) const
{
if (raw_signature.size() % 2 != 0)

View File

@ -48,22 +48,15 @@ public:
* and a XML file for the Merkle Tree root.
* Files can be downloaded by registering at https://www.gsc-europa.eu/
*/
Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath, const std::string& rootKeyFilePath);
Gnss_Crypto(const std::string& certFilePath, const std::string& merkleTreePath);
~Gnss_Crypto(); //!< Default destructor
bool have_root_key() const; //!< Returns true if the TESLA root key is already loaded
bool have_public_key() const; //!< Returns true if the ECDSA Public Key is already loaded
/*!
* Stores the ECDSA Public Key in a .pem file, which is read in a following run if the .crt file is not found
*/
bool store_public_key(const std::string& pubKeyFilePath) const;
/*!
* Stores the TESLA root key in a plaintext file, which is read in a following run for a faster TTFAF.
* @param kroot TESLA root key
* @return true if successful
*/
bool store_root_key(const std::string& rootKeyFilePath) const;
bool verify_signature_ecdsa_p256(const std::vector<uint8_t>& message, const std::vector<uint8_t>& signature) const; //!< Verify ECDSA-P256 signature (message in plain hex, signature in raw format)
bool verify_signature_ecdsa_p521(const std::vector<uint8_t>& message, const std::vector<uint8_t>& signature) const; //!< Verify ECDSA-P521 signature (message in plain hex, signature in raw format)
@ -75,15 +68,12 @@ public:
std::vector<uint8_t> get_public_key() const; //!< Gets the ECDSA Public Key in PEM format
std::vector<uint8_t> get_merkle_root() const; //!< Gets the Merkle Tree root node (\f$ x_{4,0} \f$)
std::vector<uint8_t> get_root_key() const; //!< Gets the TESLA root key in binary format
void set_public_key(const std::vector<uint8_t>& publickey); //!< Sets the ECDSA Public Key (publickey in PEM format)
void set_merkle_root(const std::vector<uint8_t>& v); //!< Sets the Merkle Tree root node x(\f$ x_{4,0} \f$)
void set_root_key(const std::vector<uint8_t>& root_key); //!< Sets the TESLA root key
private:
void read_merkle_xml(const std::string& merkleFilePath);
void readPublicKeyFromPEM(const std::string& pemFilePath);
bool read_root_key(const std::string& rootKeyFilePath);
bool readPublicKeyFromCRT(const std::string& crtFilePath);
bool convert_raw_to_der_ecdsa(const std::vector<uint8_t>& raw_signature, std::vector<uint8_t>& der_signature) const;
std::vector<uint8_t> convert_from_hex_str(const std::string& input) const;
@ -100,7 +90,6 @@ private:
#endif
#endif
std::vector<uint8_t> d_x_4_0;
std::vector<uint8_t> d_kroot;
};
/** \} */

View File

@ -51,7 +51,7 @@ protected:
// std::tm input_time = {0, 0, 5, 16, 8 - 1, 2023 - 1900, 0}; // conf. 1
std::tm input_time = {0, 0, 0, 27, 7 - 1, 2023 - 1900, 0}; // conf. 2
set_time(input_time);
osnma = osnma_msg_receiver_make("", "", "");
osnma = osnma_msg_receiver_make(CRTFILE_DEFAULT, MERKLEFILE_DEFAULT);
}
};