Skip to content

Commit

Permalink
add validation of Alert Message against Merkle tree
Browse files Browse the repository at this point in the history
  • Loading branch information
daniestevez committed Jan 24, 2024
1 parent d7c3f73 commit a0457ec
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 7 deletions.
33 changes: 31 additions & 2 deletions src/merkle_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl MerkleTree {
MerkleTree { root }
}

/// Validates a DSM-PKR against this Merkle tree.
/// Validates a DSM-PKR containing a public key against this Merkle tree.
///
/// This function checks that the public key in the DSM-PKR message belongs
/// to the Merkle tree by using the intermediate tree nodes in the DSM-PKR
Expand All @@ -41,6 +41,32 @@ impl MerkleTree {
if !matches!(dsm_pkr.new_public_key_type(), NewPublicKeyType::EcdsaKey(_)) {
return Err(PkrError::NoPublicKey);
}
self.validate(dsm_pkr)?;
Self::pubkey_from_pkr(dsm_pkr)
}

/// Validates a DSM-PKR containing an Alert Message against this Merkle tree.
///
/// This function checks that the public key in the DSM-PKR message belongs
/// to the Merkle tree by using the intermediate tree nodes in the DSM-PKR
/// and checking against the tree root stored in `self`.
///
/// The validation algorithm is described in Section 6.2 of the
/// [OSNMA SIS ICD v1.1](https://www.gsc-europa.eu/sites/default/files/sites/all/files/Galileo_OSNMA_SIS_ICD_v1.1.pdf).
///
/// If validation is successful, the function returns `Ok(())`. Otherwise,
/// an error is returned.
pub fn validate_alert_message(&self, dsm_pkr: DsmPkr) -> Result<(), PkrError> {
if !matches!(
dsm_pkr.new_public_key_type(),
NewPublicKeyType::OsnmaAlertMessage
) {
return Err(PkrError::NoPublicKey);
}
self.validate(dsm_pkr)
}

fn validate(&self, dsm_pkr: DsmPkr) -> Result<(), PkrError> {
let Some(leaf) = dsm_pkr.merkle_tree_leaf() else {
return Err(PkrError::ReservedField);
};
Expand All @@ -58,7 +84,7 @@ impl MerkleTree {
id >>= 1;
}
if node == self.root {
Self::pubkey_from_pkr(dsm_pkr)
Ok(())
} else {
Err(PkrError::Invalid)
}
Expand Down Expand Up @@ -117,6 +143,8 @@ pub enum PkrError {
Invalid,
/// The DSM-PKR does not contain a public key.
NoPublicKey,
/// The DSM-PKR is not an Alert Message.
NotAlert,
/// The DSM-PRK key is P-521, but P-521 support has not been enabled.
#[cfg(not(feature = "p521"))]
P521NotSupported,
Expand All @@ -128,6 +156,7 @@ impl fmt::Display for PkrError {
PkrError::ReservedField => "reserved value present in some field".fmt(f),
PkrError::Invalid => "wrong calculated Merkle tree root".fmt(f),
PkrError::NoPublicKey => "no public key in DSM-PKR".fmt(f),
PkrError::NotAlert => "the DSM-PKR is not an alert message".fmt(f),
#[cfg(not(feature = "p521"))]
PkrError::P521NotSupported => "P-521 support disabled".fmt(f),
}
Expand Down
40 changes: 35 additions & 5 deletions src/osnma.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::bitfields::{
ChainAndPubkeyStatus, DsmHeader, DsmKroot, DsmPkr, DsmType, Mack, NmaHeader, NmaStatus,
ChainAndPubkeyStatus, DsmHeader, DsmKroot, DsmPkr, DsmType, Mack, NewPublicKeyType, NmaHeader,
NmaStatus,
};
use crate::dsm::{CollectDsm, Dsm};
use crate::mack::MackStorage;
Expand Down Expand Up @@ -335,20 +336,35 @@ impl<S: StaticStorage> OsnmaData<S> {
log::warn!("CPKS is new Merkle tree");
}
ChainAndPubkeyStatus::AlertMessage => {
log::warn!("CPKS is alert message; deleting all cryptographic material");
self.merkle_tree = None;
self.pubkey = PubkeyStore::empty();
self.key = KeyStore::empty();
log::warn!("CPKS is alert message");
self.alert_message_received();
}
}
}

fn alert_message_received(&mut self) {
log::warn!("received OSNMA Alert Message; deleting all cryptographic material");
self.merkle_tree = None;
self.pubkey = PubkeyStore::empty();
self.key = KeyStore::empty();
}

fn set_dont_use(&mut self) {
self.dont_use = true;
self.navmessage.reset_authbits();
}

fn process_dsm_pkr(&mut self, dsm_pkr: DsmPkr) {
match dsm_pkr.new_public_key_type() {
NewPublicKeyType::EcdsaKey(_) => self.process_dsm_pkr_npk(dsm_pkr),
NewPublicKeyType::OsnmaAlertMessage => self.process_dsm_pkr_alert_message(dsm_pkr),
NewPublicKeyType::Reserved => {
log::error!("reserved NPKT in DSM-PKR: {:?}", dsm_pkr);
}
}
}

fn process_dsm_pkr_npk(&mut self, dsm_pkr: DsmPkr) {
let Some(merkle_tree) = &self.merkle_tree else {
log::error!("could not verify public key because Merkle tree is not loaded");
return;
Expand All @@ -362,6 +378,20 @@ impl<S: StaticStorage> OsnmaData<S> {
}
}

fn process_dsm_pkr_alert_message(&mut self, dsm_pkr: DsmPkr) {
let Some(merkle_tree) = &self.merkle_tree else {
log::error!("could not verify OSNMA Alert Message because Merkle tree is not loaded");
return;
};
match merkle_tree.validate_alert_message(dsm_pkr) {
Ok(()) => {
log::warn!("received valid OSNMA Alert Message in DSM-PKR: {dsm_pkr:?}");
self.alert_message_received();
}
Err(e) => log::error!("could not verify OSNMA Alert Message: {e:?}"),
}
}

fn validate_key(&mut self, mack: &MackMessage, gst: Gst, nma_status: NmaStatus) {
let Some(current_key) = self.key.current_key() else {
log::info!("no valid TESLA key for the chain in force. unable to validate MACK key");
Expand Down

0 comments on commit a0457ec

Please sign in to comment.