1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2025-01-31 03:14:56 +00:00

[TAS-250] (x2) [FEAT] Implement PK renewal and revocation.

* Revocation implemented and tested. DSM-KROOT appears to be corrupted because length check fails after start of step 1, during steps 2 and 3.
This commit is contained in:
cesaaargm 2024-07-30 12:25:22 +02:00
parent 440dc582b5
commit 26f77a3c42
3 changed files with 101 additions and 29 deletions

View File

@ -77,6 +77,10 @@ osnma_msg_receiver::osnma_msg_receiver(const std::string& crtFilePath, const std
d_nav_data_manager = std::make_unique<OSNMA_nav_data_Manager>();
if(d_crypto->have_public_key()){ // Hot start is enabled
LOG(WARNING) << "OSNMA Public Key available, trying to find DSM-KROOT saved";
std::cout << "OSNMA Public Key available, trying to find DSM-KROOT saved" << std::endl;
d_public_key_verified = true;
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;
@ -170,22 +174,47 @@ void osnma_msg_receiver::msg_handler_osnma(const pmt::pmt_t& msg)
void osnma_msg_receiver::process_osnma_message(const std::shared_ptr<OSNMA_msg>& osnma_msg)
{
read_nma_header(osnma_msg->hkroot[0]);
if (d_osnma_data.d_nma_header.nmas == 0 || d_osnma_data.d_nma_header.nmas == 3)
{
LOG(WARNING) << "Galileo OSNMA: NMAS invalid, skipping osnma message";
// Check for corner cases: renewal, revocation
if (d_osnma_data.d_nma_header.nmas == 0 /* RES */){
LOG(WARNING) << "Galileo OSNMA: NMAS invalid (RES), skipping osnma message";
return;
}
if (d_osnma_data.d_nma_header.nmas == 2 /*OP*/ && d_osnma_data.d_nma_header.cpks == 4 /*NPK*/ && d_GST_PKR_start == 0){
if (d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 4 /* NPK */ && d_GST_PKR_PKREV_start == 0){
d_flag_PK_renewal = true;
d_GST_PKR_start = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0);
LOG(INFO) << "Galileo OSNMA: Public Key Renewal :: Start at GST=" << d_GST_PKR_start;
std::cout << "Galileo OSNMA: Public Key Renewal :: Start at GST=" << d_GST_PKR_start << std::endl;
d_GST_PKR_PKREV_start = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0);
LOG(INFO) << "Galileo OSNMA: Public Key Renewal :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]";
std::cout << "Galileo OSNMA: Public Key Renewal :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << std::endl;
}
if (d_flag_PK_renewal && d_osnma_data.d_nma_header.nmas == 2 /*OP*/ && d_osnma_data.d_nma_header.cpks == 1 /*Nominal*/ ){
if (d_flag_PK_renewal && d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 1 /* Nominal */ ){
d_flag_PK_renewal = false;
LOG(INFO) << "Galileo OSNMA: Public Key Renewal :: Finished at GST=" << d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0);
std::cout << "Galileo OSNMA: Public Key Renewal :: Finished at GST=" << d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0) << std::endl;
uint32_t final_GST = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0);
double duration_hours = (final_GST - d_GST_PKR_PKREV_start) / 3600;
LOG(INFO) << "Galileo OSNMA: Public Key Renewal :: Finished at GST=" << duration_hours << ", Duration=" << duration_hours << " h";
std::cout << "Galileo OSNMA: Public Key Renewal :: Finished at GST=" << duration_hours << ", Duration=" << duration_hours << " h" << std::endl;
}
if(d_osnma_data.d_nma_header.nmas == 3 /* DU */ && d_osnma_data.d_nma_header.cpks == 5 /* PKREV */ && d_GST_PKR_PKREV_start == 0){
d_flag_PK_revocation = true;
d_number_of_blocks[d_osnma_data.d_dsm_header.dsm_id] = 0;
d_public_key_verified = false;
d_kroot_verified = false;
d_tesla_key_verified = false;
d_GST_PKR_PKREV_start = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0);
LOG(INFO) << "Galileo OSNMA: Public Key Revocation :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]";
std::cout << "Galileo OSNMA: Public Key Revocation :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << std::endl;
}
if (d_flag_PK_revocation && d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 1 /* Nominal */ ){
// step 2 , start using new chain
d_flag_PK_revocation = false;
uint32_t final_GST = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0);
double duration_hours = (final_GST - d_GST_PKR_PKREV_start) / 3600;
LOG(INFO) << "Galileo OSNMA: Public Key Revocation :: Finished at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << ", Duration=" << duration_hours << "h";
std::cout << "Galileo OSNMA: Public Key Revocation :: Finished at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << ", Duration=" << duration_hours << "h" << std::endl;
}
read_dsm_header(osnma_msg->hkroot[1]);
read_dsm_block(osnma_msg);
process_dsm_block(osnma_msg); // will process dsm block if received a complete one, then will call mack processing upon re-setting the dsm block to 0
@ -393,6 +422,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>{};
LOG(INFO) << "Galileo OSNMA: DSM message completed :: start processing, GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]";
process_dsm_message(dsm_msg, osnma_msg->hkroot[0]);
}
}
@ -408,7 +438,7 @@ void osnma_msg_receiver::process_dsm_block(const std::shared_ptr<OSNMA_msg>& osn
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 || d_flag_hot_start)
if ((d_osnma_data.d_dsm_header.dsm_id < 12 || d_flag_hot_start) && d_public_key_verified)
{
// Parse Kroot message
LOG(INFO) << "Galileo OSNMA: DSM-KROOT message received.";
@ -456,6 +486,7 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
if (l_dk_bits != check_l_dk)
{
LOG(WARNING) << "Galileo OSNMA: Failed length reading of DSM-KROOT message";
d_count_failed_Kroot++;
}
else
{
@ -498,7 +529,7 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
if (d_osnma_data.d_dsm_kroot_message.p_dk == p_dk_truncated)
{
LOG(INFO) << "Galileo OSNMA: DSM-KROOT message received ok.";
LOG(INFO) << "Galileo OSNMA: KROOT with CID=" << static_cast<uint32_t>(d_osnma_data.d_nma_header.cid)
LOG(INFO) << "Galileo OSNMA: DSM-KROOT with CID=" << static_cast<uint32_t>(d_osnma_data.d_nma_header.cid)
<< ", 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;
@ -519,22 +550,22 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
}
if (d_kroot_verified)
{
std::cout << "Galileo OSNMA: KROOT authentication successful!" << std::endl;
LOG(INFO) << "Galileo OSNMA: KROOT authentication successful!";
std::cout << "Galileo OSNMA: DSM-KROOT authentication successful!" << std::endl;
LOG(INFO) << "Galileo OSNMA: DSM-KROOT authentication successful!";
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 DSM-Kroot and NMA header into a permanent storage
if(d_flag_hot_start){
if (d_flag_hot_start){
d_flag_hot_start = false;
return;
}
store_dsm_kroot(dsm_msg, nma_header);
store_dsm_kroot(dsm_msg, nma_header); // TODO - store it only if DSM-KROOT is new
}
else
{
LOG(WARNING) << "Galileo OSNMA: KROOT authentication failed.";
std::cerr << "Galileo OSNMA: KROOT authentication failed." << std::endl;
LOG(WARNING) << "Galileo OSNMA: DSM-KROOT authentication failed.";
std::cerr << "Galileo OSNMA: DSM-KROOT authentication failed." << std::endl;
d_count_failed_Kroot ++;
}
}
@ -546,9 +577,10 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
}
}
}
// DSM-PKR message
else if (d_osnma_data.d_dsm_header.dsm_id >= 12 && d_osnma_data.d_dsm_header.dsm_id < 16)
{
LOG(INFO) << "Galileo OSNMA: DSM-PKR message received.";
LOG(INFO) << "Galileo OSNMA: DSM-PKR message received";
// Save DSM-PKR message
d_osnma_data.d_dsm_pkr_message.nb_dp = d_dsm_reader->get_number_blocks_index(dsm_msg[0]);
d_osnma_data.d_dsm_pkr_message.mid = d_dsm_reader->get_mid(dsm_msg);
@ -606,7 +638,7 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
/*<< ", 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*/
<< " received";
// C: NPK verification against Merkle tree root.
// Public key verification against Merkle tree root.
bool verification = verify_dsm_pkr(d_osnma_data.d_dsm_pkr_message);
if (verification)
{
@ -634,13 +666,14 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
LOG(WARNING) << "Galileo OSNMA: Reserved message received";
std::cerr << "Galileo OSNMA: Reserved message received" << std::endl;
}
d_number_of_blocks[d_osnma_data.d_dsm_header.dsm_id] = 0;
d_number_of_blocks[d_osnma_data.d_dsm_header.dsm_id] = 0; // TODO - reset during header parsing in PKREV?
}
/**
* @brief Reads the Mack message from the given OSNMA_msg object.
* @brief Reads the Mack message from the given OSNMA_msg object
*
* @details Conditions for MACK processing:
* @param osnma_msg The OSNMA_msg object containing the Mack message.
*/
void osnma_msg_receiver::read_and_process_mack_block(const std::shared_ptr<OSNMA_msg>& osnma_msg)
@ -657,8 +690,10 @@ void osnma_msg_receiver::read_and_process_mack_block(const std::shared_ptr<OSNMA
}
d_osnma_data.d_nav_data.set_tow_sf0(osnma_msg->TOW_sf0);
if (d_kroot_verified || d_tesla_key_verified || d_osnma_data.d_dsm_kroot_message.ts != 0 /*mack parser needs to know the tag size, otherwise cannot parse mack messages*/) // C: 4 ts < ts < 10
bool can_process_mack_block = d_osnma_data.d_nma_header.nmas != 3; // Don't Use
bool can_verify_tesla_key = d_kroot_verified || d_tesla_key_verified; // Either of those suffices for verifying the incoming TESLA key
bool can_parse_tag_fields = d_osnma_data.d_dsm_kroot_message.ts != 0; // calculating the number of tags is based on the TS of the DSM-KROOT.
if (can_verify_tesla_key && can_parse_tag_fields && can_process_mack_block)
{ // TODO - correct? with this, MACK would not be processed unless a Kroot is available -- no, if TK available MACK sould go on, this has to change in future
read_mack_header();
d_osnma_data.d_mack_message.PRNa = osnma_msg->PRN; // FIXME this is ugly.
@ -669,6 +704,12 @@ void osnma_msg_receiver::read_and_process_mack_block(const std::shared_ptr<OSNMA
// TODO - shorten the MACK processing for the cases where no TK verified or no Kroot verified (warm and cold start)
// still, for instance the OSNMA_NavData and Mack storage (within process_mack_message) makes sense.
}
else
{
// TODO - MACKs should be saved because once Kroot available, they could be verified.
LOG(WARNING) << "Galileo OSNMA: Cannot process MACK block. Skipping it.";
std::cout << "Galileo OSNMA: Cannot process MACK block. Skipping it." << std::endl;
}
}
@ -1084,7 +1125,6 @@ void osnma_msg_receiver::process_mack_message()
<< ". Key available (" << tag_has_key_available(it.second) << "), navData (" << tag_has_nav_data_available(it.second) << "). ";
}
}
uint8_t tag_size = 0;
const auto it = OSNMA_TABLE_11.find(d_osnma_data.d_dsm_kroot_message.ts);
if (it != OSNMA_TABLE_11.cend())
@ -1107,7 +1147,7 @@ void osnma_msg_receiver::process_mack_message()
*
* \details This method provides the functionality to verify the DSM-PKR message. The verification includes generating the base leaf
* and intermediate leafs, and comparing the computed merkle root leaf with the received one.
* \pre DSM_PKR_message correctly filled in especially the 1024 intermediate tree nodes
* \pre DSM_PKR_message correctly filled in, especially the 1024-bit intermediate tree nodes fields
* \returns true if computed merkle root matches received one, false otherwise
*/
bool osnma_msg_receiver::verify_dsm_pkr(const DSM_PKR_message& message) const

View File

@ -133,7 +133,7 @@ private:
uint32_t d_last_verified_key_GST{0};
uint32_t d_GST_0{};
uint32_t d_GST_SIS{};
uint32_t d_GST_PKR_start{};
uint32_t d_GST_PKR_PKREV_start{};
uint8_t d_Lt_min{}; // minimum equivalent tag length
uint8_t d_Lt_verified_eph{0}; // verified tag bits - ephemeris
@ -168,6 +168,7 @@ private:
FRIEND_TEST(OsnmaTestVectors, NominalTestConf1);
FRIEND_TEST(OsnmaTestVectors, NominalTestConf2);
FRIEND_TEST(OsnmaTestVectors, PublicKeyRenewal);
FRIEND_TEST(OsnmaTestVectors, PublicKeyRevocation);
};

View File

@ -60,7 +60,7 @@ protected:
const int SIZE_SUBFRAME_PAGES{15}; // number of pages of a subframe
const int DURATION_SUBFRAME{30}; // duration of a subframe, in seconds// 13 + 5;
bool d_flag_NPK{false}; // flag for NPK, new MT will be set when the new Kroot is received.
bool d_flag_NPK{false}; // flag for NPK, new MT will be set when the new Kroot is received.
};
TEST_F(OsnmaTestVectors, NominalTestConf1)
@ -150,6 +150,37 @@ TEST_F(OsnmaTestVectors, PublicKeyRenewal)
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
}
TEST_F(OsnmaTestVectors, PublicKeyRevocation)
{
// Arrange
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20231007081500_PKID_8.crt";
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007081500_PKID_8.xml";
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
std::tm input_time_step1 = {0, 45, 7, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step2 = {0, 30, 9, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step3 = {0, 30, 10, 7, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::vector<std::tm> input_times = { input_time_step1, input_time_step2, input_time_step3 };
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/pkrev_step1/07_OCT_2023_GST_07_45_01.csv");
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/pkrev_step2/07_OCT_2023_GST_09_30_01.csv");
std::vector<TestVector> testVectors_step3 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/pkrev_step3/07_OCT_2023_GST_10_30_01.csv");
if (testVectors_step1.empty() || testVectors_step2.empty() || testVectors_step3.empty())
{
ASSERT_TRUE(false);
}
std::vector<std::vector<TestVector>> testVectors = { testVectors_step1, testVectors_step2, testVectors_step3};
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);
// Assert
ASSERT_EQ(osnma->d_count_failed_tags, 0);
ASSERT_EQ(osnma->d_count_failed_Kroot, 0);
ASSERT_EQ(osnma->d_count_failed_pubKey, 0);
ASSERT_EQ(osnma->d_count_failed_macseq, 0);
}
// 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.
bool OsnmaTestVectors::feedOsnmaWithTestVectors(osnma_msg_receiver_sptr osnma_object, std::vector<std::vector<TestVector>> testVectors, std::vector<std::tm> startTimesFiles){
@ -367,7 +398,7 @@ bool OsnmaTestVectors::feedOsnmaWithTestVectors(osnma_msg_receiver_sptr osnma_ob
}
}
if (end_of_hex_stream)
break;
continue;
}
return true;
}