1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2024-12-13 11:40:33 +00:00

[TAS-240][FEAT] Implement TESLA Chain Renewal and Revocation I

* Implemented a new chain renewal mechanism for OSNMA data structure updates and key management.
* Added related flags and data fields.
* tested successfully with test vectors.
This commit is contained in:
cesaaargm 2024-08-14 13:14:23 +02:00
parent e4f4c335bc
commit 2e867f2dac
4 changed files with 105 additions and 32 deletions

View File

@ -258,7 +258,7 @@ void osnma_msg_receiver::process_osnma_message(const std::shared_ptr<OSNMA_msg>&
return;
}
// TODO - trusting the NMAS and CPKS shall be done upon PKR verification or Tag verification.
// It's ok to activate the flags, but the final decision should happen after verifying it.
// It's ok to activate the flags, but the final decision should happen after verifying it.
// For OAM is solved, but NPK and PKREV I think not yet
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)
{
@ -309,6 +309,27 @@ void osnma_msg_receiver::process_osnma_message(const std::shared_ptr<OSNMA_msg>&
std::cout << "Galileo OSNMA: Alert message :: Start at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << std::endl;
}
if (d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 2 /* EOC */ && d_GST_chain_renewal_start == 0)
{
d_flag_chain_renewal = true;
d_GST_chain_renewal_start = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0);
LOG(INFO) << "Galileo OSNMA: Chain renewal :: Start at at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]";
std::cout << "Galileo OSNMA: Chain renewal :: Start at at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]" << std::endl;
}
if (d_flag_chain_renewal && d_osnma_data.d_nma_header.nmas == 2 /* OP */ && d_osnma_data.d_nma_header.cpks == 1 /* Nominal */)
{
// Step 2, start using the new kroot
d_flag_chain_renewal = false;
uint32_t final_GST = d_helper->compute_gst(osnma_msg->WN_sf0, osnma_msg->TOW_sf0);
double duration_hours = (final_GST - d_GST_chain_renewal_start) / 3600.0;
LOG(INFO) << "Galileo OSNMA: Chain renewal :: Finished at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]"
<< ", Duration=" << duration_hours << "h";
std::cout << "Galileo OSNMA: Chain renewal :: Finished at GST=[" << osnma_msg->WN_sf0 << " " << osnma_msg->TOW_sf0 << "]"
<< ", Duration=" << duration_hours << "h" << std::endl;
d_osnma_data.d_dsm_kroot_message = d_osnma_data.d_dsm_kroot_new_message; // set new kroot as the one to use from now on
d_tesla_key_verified = false; // force the verification up to the Kroot due to chain change
}
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
@ -478,24 +499,27 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
// DSM-KROOT message
if ((d_osnma_data.d_dsm_header.dsm_id < 12 || d_flag_hot_start) && d_public_key_verified)
{
bool new_chain = (d_dsm_reader->get_cidkr(dsm_msg) != d_osnma_data.d_nma_header.cid) && d_flag_chain_renewal;
DSM_KROOT_message& applicable_kroot_msg = new_chain ? d_osnma_data.d_dsm_kroot_new_message : d_osnma_data.d_dsm_kroot_message;
// Parse Kroot message
LOG(INFO) << "Galileo OSNMA: DSM-KROOT message received.";
d_osnma_data.d_dsm_kroot_message.nb_dk = d_dsm_reader->get_number_blocks_index(dsm_msg[0]);
d_osnma_data.d_dsm_kroot_message.pkid = d_dsm_reader->get_pkid(dsm_msg);
d_osnma_data.d_dsm_kroot_message.cidkr = d_dsm_reader->get_cidkr(dsm_msg);
d_osnma_data.d_dsm_kroot_message.reserved1 = d_dsm_reader->get_dsm_reserved1(dsm_msg);
d_osnma_data.d_dsm_kroot_message.hf = d_dsm_reader->get_hf(dsm_msg);
d_osnma_data.d_dsm_kroot_message.mf = d_dsm_reader->get_mf(dsm_msg);
d_osnma_data.d_dsm_kroot_message.ks = d_dsm_reader->get_ks(dsm_msg);
d_osnma_data.d_dsm_kroot_message.ts = d_dsm_reader->get_ts(dsm_msg);
d_osnma_data.d_dsm_kroot_message.maclt = d_dsm_reader->get_maclt(dsm_msg);
d_osnma_data.d_dsm_kroot_message.reserved = d_dsm_reader->get_dsm_reserved(dsm_msg);
d_osnma_data.d_dsm_kroot_message.wn_k = d_dsm_reader->get_wn_k(dsm_msg);
d_osnma_data.d_dsm_kroot_message.towh_k = d_dsm_reader->get_towh_k(dsm_msg);
d_osnma_data.d_dsm_kroot_message.alpha = d_dsm_reader->get_alpha(dsm_msg);
applicable_kroot_msg.nb_dk = d_dsm_reader->get_number_blocks_index(dsm_msg[0]);
applicable_kroot_msg.pkid = d_dsm_reader->get_pkid(dsm_msg);
applicable_kroot_msg.cidkr = d_dsm_reader->get_cidkr(dsm_msg);
applicable_kroot_msg.reserved1 = d_dsm_reader->get_dsm_reserved1(dsm_msg);
applicable_kroot_msg.hf = d_dsm_reader->get_hf(dsm_msg);
applicable_kroot_msg.mf = d_dsm_reader->get_mf(dsm_msg);
applicable_kroot_msg.ks = d_dsm_reader->get_ks(dsm_msg);
applicable_kroot_msg.ts = d_dsm_reader->get_ts(dsm_msg);
applicable_kroot_msg.maclt = d_dsm_reader->get_maclt(dsm_msg);
applicable_kroot_msg.reserved = d_dsm_reader->get_dsm_reserved(dsm_msg);
applicable_kroot_msg.wn_k = d_dsm_reader->get_wn_k(dsm_msg);
applicable_kroot_msg.towh_k = d_dsm_reader->get_towh_k(dsm_msg);
applicable_kroot_msg.alpha = d_dsm_reader->get_alpha(dsm_msg);
// Kroot field
const uint16_t l_lk_bytes = d_dsm_reader->get_lk_bits(d_osnma_data.d_dsm_kroot_message.ks) / 8;
d_osnma_data.d_dsm_kroot_message.kroot = d_dsm_reader->get_kroot(dsm_msg, l_lk_bytes);
const uint16_t l_lk_bytes = d_dsm_reader->get_lk_bits(applicable_kroot_msg.ks) / 8;
applicable_kroot_msg.kroot = d_dsm_reader->get_kroot(dsm_msg, l_lk_bytes);
// DS field
uint16_t l_ds_bits = 0;
const auto it = OSNMA_TABLE_15.find(d_crypto->get_public_key_type());
@ -504,19 +528,19 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
l_ds_bits = it->second;
}
const uint16_t l_ds_bytes = l_ds_bits / 8;
d_osnma_data.d_dsm_kroot_message.ds = std::vector<uint8_t>(l_ds_bytes, 0); // C: this accounts for padding in case needed.
applicable_kroot_msg.ds = std::vector<uint8_t>(l_ds_bytes, 0); // C: this accounts for padding in case needed.
for (uint16_t k = 0; k < l_ds_bytes; k++)
{
d_osnma_data.d_dsm_kroot_message.ds[k] = dsm_msg[13 + l_lk_bytes + k];
applicable_kroot_msg.ds[k] = dsm_msg[13 + l_lk_bytes + k];
}
// Padding
const uint16_t l_dk_bits = d_dsm_reader->get_l_dk_bits(d_osnma_data.d_dsm_kroot_message.nb_dk);
const uint16_t l_dk_bits = d_dsm_reader->get_l_dk_bits(applicable_kroot_msg.nb_dk);
const uint16_t l_dk_bytes = l_dk_bits / 8;
const uint16_t l_pdk_bytes = (l_dk_bytes - 13 - l_lk_bytes - l_ds_bytes);
d_osnma_data.d_dsm_kroot_message.p_dk = std::vector<uint8_t>(l_pdk_bytes, 0);
applicable_kroot_msg.p_dk = std::vector<uint8_t>(l_pdk_bytes, 0);
for (uint16_t k = 0; k < l_pdk_bytes; k++)
{
d_osnma_data.d_dsm_kroot_message.p_dk[k] = dsm_msg[13 + l_lk_bytes + l_ds_bytes + k];
applicable_kroot_msg.p_dk[k] = dsm_msg[13 + l_lk_bytes + l_ds_bytes + k];
}
const uint16_t check_l_dk = 104 * std::ceil(1.0 + static_cast<float>((l_lk_bytes * 8.0) + l_ds_bits) / 104.0);
@ -539,15 +563,15 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
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]);
MSG.push_back(applicable_kroot_msg.ds[k]);
}
std::vector<uint8_t> hash;
if (d_osnma_data.d_dsm_kroot_message.hf == 0) // Table 8.
if (applicable_kroot_msg.hf == 0) // Table 8.
{
hash = d_crypto->compute_SHA_256(MSG);
}
else if (d_osnma_data.d_dsm_kroot_message.hf == 2)
else if (applicable_kroot_msg.hf == 2)
{
hash = d_crypto->compute_SHA3_256(MSG);
}
@ -563,17 +587,16 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
p_dk_truncated.push_back(hash[i]);
}
// Check that the padding bits received match the computed values
if (d_osnma_data.d_dsm_kroot_message.p_dk == p_dk_truncated)
if (applicable_kroot_msg.p_dk == p_dk_truncated)
{
LOG(INFO) << "Galileo OSNMA: DSM-KROOT message received ok.";
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;
// local_time_verification(osnma_msg); // FIXME TODO: real time verification needed
<< ", PKID=" << static_cast<uint32_t>(applicable_kroot_msg.pkid)
<< ", WN=" << static_cast<uint32_t>(applicable_kroot_msg.wn_k)
<< ", TOW=" << static_cast<uint32_t>(applicable_kroot_msg.towh_k) * 3600;
// If new PK verified and the new KROOT arrived, set the new PK before attempting verification
if (d_flag_PK_renewal && d_osnma_data.d_dsm_kroot_message.pkid == d_new_public_key_id && d_flag_NPK_set == false)
if (d_flag_PK_renewal && applicable_kroot_msg.pkid == d_new_public_key_id && d_flag_NPK_set == false)
{
d_crypto->set_public_key(d_new_public_key);
d_crypto->store_public_key(PEMFILE_DEFAULT);
@ -581,14 +604,15 @@ void osnma_msg_receiver::process_dsm_message(const std::vector<uint8_t>& dsm_msg
}
if (l_ds_bits == 512)
{
d_kroot_verified = d_crypto->verify_signature_ecdsa_p256(message, d_osnma_data.d_dsm_kroot_message.ds);
d_kroot_verified = d_crypto->verify_signature_ecdsa_p256(message, applicable_kroot_msg.ds);
}
else if (l_ds_bits == 1056)
{
d_kroot_verified = d_crypto->verify_signature_ecdsa_p521(message, d_osnma_data.d_dsm_kroot_message.ds);
d_kroot_verified = d_crypto->verify_signature_ecdsa_p521(message, applicable_kroot_msg.ds);
}
if (d_kroot_verified)
{
applicable_kroot_msg.verified = true;
std::cout << "Galileo OSNMA: DSM-KROOT authentication successful!" << std::endl;
LOG(INFO) << "Galileo OSNMA: DSM-KROOT authentication successful!";
if (d_flag_alert_message)
@ -1271,6 +1295,12 @@ std::vector<uint8_t> osnma_msg_receiver::compute_merkle_root(const DSM_PKR_messa
}
/**
* @brief Get the Merkle tree base leave from a DSM_PKR_message.
*
* @param dsm_pkr_message The DSM_PKR_message object from which to retrieve the Merkle tree leave.
* @return std::vector<uint8_t> The Merkle tree base leave from the DSM_PKR_message object.
*/
std::vector<uint8_t> osnma_msg_receiver::get_merkle_tree_leaves(const DSM_PKR_message& dsm_pkr_message) const
{
// build base leaf m_i according to OSNMA SIS ICD v1.1, section 6.2 DSM-PKR Verification

View File

@ -129,6 +129,7 @@ private:
uint32_t d_GST_SIS{}; // GST coming from W6 and W5 of SIS
uint32_t d_GST_PKR_PKREV_start{};
uint32_t d_GST_PKR_AM_start{};
uint32_t d_GST_chain_renewal_start{};
uint32_t d_count_successful_tags{0};
uint32_t d_count_failed_tags{0};
@ -149,6 +150,7 @@ private:
bool d_flag_PK_revocation{false};
bool d_flag_NPK_set{false};
bool d_flag_alert_message{false};
bool d_flag_chain_renewal{false};
// Provide access to inner functions to Gtest
FRIEND_TEST(OsnmaMsgReceiverTest, TeslaKeyVerification);
@ -161,6 +163,7 @@ private:
FRIEND_TEST(OsnmaTestVectors, NominalTestConf2);
FRIEND_TEST(OsnmaTestVectors, PublicKeyRenewal);
FRIEND_TEST(OsnmaTestVectors, PublicKeyRevocation);
FRIEND_TEST(OsnmaTestVectors, ChainRenewal);
FRIEND_TEST(OsnmaTestVectors, AlertMessage);
};

View File

@ -116,6 +116,7 @@ public:
uint8_t maclt{};
uint8_t reserved{};
uint8_t towh_k{};
bool verified{false};
};
@ -182,6 +183,7 @@ public:
DSM_dsm_header d_dsm_header;
DSM_PKR_message d_dsm_pkr_message;
DSM_KROOT_message d_dsm_kroot_message;
DSM_KROOT_message d_dsm_kroot_new_message;
MACK_message d_mack_message;
OSNMA_NavData d_nav_data;
};

View File

@ -152,6 +152,7 @@ TEST_F(OsnmaTestVectors, PublicKeyRenewal)
}
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2, testVectors_step3};
// Act
d_flag_NPK = true;
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);
@ -190,6 +191,43 @@ TEST_F(OsnmaTestVectors, PublicKeyRevocation)
}
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2, testVectors_step3};
// Act
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);
// Assert
LOG(INFO) << "Successful tags count= " << osnma->d_count_successful_tags;
LOG(INFO) << "Failed tags count= " << osnma->d_count_failed_tags;
LOG(INFO) << "Unverified tags count= " << osnma->d_tags_awaiting_verify.size();
LOG(INFO) << "Failed Kroot count= " << osnma->d_count_failed_Kroot;
LOG(INFO) << "Failed PK count= " << osnma->d_count_failed_pubKey;
LOG(INFO) << "Failed MACSEQ count= " << osnma->d_count_failed_macseq;
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);
}
TEST_F(OsnmaTestVectors, ChainRenewal)
{
// Arrange
std::string crtFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/PublicKey/OSNMA_PublicKey_20231007041500_PKID_7.crt";
std::string merkleFilePath = std::string(BASE_OSNMA_TEST_VECTORS) + "cryptographic_material/Merkle_tree_2/MerkleTree/OSNMA_MerkleTree_20231007041500_PKID_7.xml";
osnma_msg_receiver_sptr osnma = osnma_msg_receiver_make(crtFilePath, merkleFilePath);
std::tm input_time_step1 = {0, 45, 16, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::tm input_time_step2 = {0, 30, 18, 6, 10 - 1, 2023 - 1900, 0, 0, 0, 0, 0};
std::vector<std::tm> input_times = {input_time_step1, input_time_step2};
std::vector<TestVector> testVectors_step1 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/eoc_step1/06_OCT_2023_GST_16_45_01.csv");
std::vector<TestVector> testVectors_step2 = readTestVectorsFromFile(std::string(BASE_OSNMA_TEST_VECTORS) + "osnma_test_vectors/eoc_step2/06_OCT_2023_GST_18_30_01.csv");
if (testVectors_step1.empty() || testVectors_step2.empty())
{
ASSERT_TRUE(false);
}
std::vector<std::vector<TestVector>> testVectors = {testVectors_step1, testVectors_step2};
// Act
bool result = feedOsnmaWithTestVectors(osnma, testVectors, input_times);
ASSERT_TRUE(result);