Skip to content

Commit

Permalink
implement dummy tag validation
Browse files Browse the repository at this point in the history
This implements validation of tags with COP = 0.
  • Loading branch information
daniestevez committed Jan 24, 2024
1 parent a0457ec commit 7719fb1
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 36 deletions.
113 changes: 98 additions & 15 deletions src/navmessage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,17 @@ impl<S: StaticStorage> CollectNavMessage<S> {
log::info!("{} tag0 at {:?} COP = {}", prna, gst_mack, mack.cop());
let gst_navmessage = gst_mack.add_seconds(-30);
if mack.cop() == 0 {
log::warn!("COP = 0 validation not implemented");
Self::validate_dummy_tag(
key,
mack.tag0(),
Adkd::InavCed,
gst_mack,
u8::from(prna),
prna,
0,
nma_status,
CED_AND_STATUS_BITS,
);
} else if let Some(&navdata) = self.find_ced_and_status(prna, gst_navmessage) {
if navdata.max_age().saturating_add(1) <= mack.cop() {
// Try to validate tag0
Expand Down Expand Up @@ -365,10 +375,6 @@ impl<S: StaticStorage> CollectNavMessage<S> {
tag.cop(),
tag.prnd()
);
if tag.cop() == 0 {
log::warn!("COP = 0 validation not implemented");
continue;
}
let prnd = match u8::try_from(tag.prnd()) {
Ok(p) => p,
Err(_) => {
Expand All @@ -379,7 +385,21 @@ impl<S: StaticStorage> CollectNavMessage<S> {
match tag.adkd() {
Adkd::InavCed => match Svn::try_from(prnd) {
Ok(prnd_svn) => {
if let Some(&navdata) = self.find_ced_and_status(prnd_svn, gst_navmessage) {
if tag.cop() == 0 {
Self::validate_dummy_tag(
key,
tag.tag(),
tag.adkd(),
gst_mack,
prnd,
prna,
j,
nma_status,
CED_AND_STATUS_BITS,
);
} else if let Some(&navdata) =
self.find_ced_and_status(prnd_svn, gst_navmessage)
{
if navdata.max_age().saturating_add(1) <= tag.cop() {
Self::validate_tag(
key,
Expand All @@ -402,7 +422,19 @@ impl<S: StaticStorage> CollectNavMessage<S> {
},
Adkd::InavTiming => match Svn::try_from(prnd) {
Ok(prnd_svn) => {
if let Some(&navdata) =
if tag.cop() == 0 {
Self::validate_dummy_tag(
key,
tag.tag(),
tag.adkd(),
gst_mack,
prnd,
prna,
j,
nma_status,
TIMING_PARAMETERS_BITS,
);
} else if let Some(&navdata) =
self.find_timing_parameters(prnd_svn, gst_navmessage)
{
if navdata.max_age().saturating_add(1) <= tag.cop() {
Expand Down Expand Up @@ -466,9 +498,6 @@ impl<S: StaticStorage> CollectNavMessage<S> {
if tag.adkd() != Adkd::SlowMac {
continue;
}
if tag.cop() == 0 {
log::warn!("COP = 0 validation not implemented");
}
let prnd = match u8::try_from(tag.prnd()) {
Ok(p) => p,
Err(_) => {
Expand All @@ -483,7 +512,19 @@ impl<S: StaticStorage> CollectNavMessage<S> {
continue;
}
};
if let Some(&navdata) = self.find_ced_and_status(prnd_svn, gst_navmessage) {
if tag.cop() == 0 {
Self::validate_dummy_tag(
key,
tag.tag(),
tag.adkd(),
gst_mack,
prnd,
prna,
j,
nma_status,
CED_AND_STATUS_BITS,
);
} else if let Some(&navdata) = self.find_ced_and_status(prnd_svn, gst_navmessage) {
if navdata.max_age().saturating_add(1) <= tag.cop() {
Self::validate_tag(
key,
Expand Down Expand Up @@ -556,6 +597,46 @@ impl<S: StaticStorage> CollectNavMessage<S> {
ret
}

fn validate_dummy_tag<'a>(
key: &Key<Validated>,
tag: &BitSlice,
adkd: Adkd,
gst_tag: Gst,
prnd: u8,
prna: Svn,
tag_idx: usize,
nma_status: NmaStatus,
navdata_len_bits: usize,
) -> bool {
let ctr = (tag_idx + 1).try_into().unwrap();
let ret = match tag_idx {
0 => key.validate_tag0_dummy(tag, gst_tag, prna, nma_status, navdata_len_bits),
_ => {
key.validate_tag_dummy(tag, gst_tag, prnd, prna, ctr, nma_status, navdata_len_bits)
}
};
if ret {
log::info!(
"E{:02} {:?} at {:?} dummy tag{} correct (auth by {})",
prnd,
adkd,
gst_tag,
tag_idx,
prna
);
} else {
log::error!(
"E{:02} {:?} at {:?} dummy tag{} wrong (auth by {})",
prnd,
adkd,
gst_tag,
tag_idx,
prna
);
}
ret
}

/// Resets all the authentication bits to zero.
///
/// This function can be called when the NMA status is set to don't use in
Expand All @@ -576,8 +657,9 @@ impl<S: StaticStorage> Default for CollectNavMessage<S> {
}
}

const CED_AND_STATUS_BYTES: usize = 69;
const CED_AND_STATUS_WORDS: usize = 5;
const CED_AND_STATUS_BITS: usize = 549;
const CED_AND_STATUS_BYTES: usize = (CED_AND_STATUS_BITS + 7) / 8;

#[doc(hidden)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
Expand All @@ -589,8 +671,9 @@ pub struct CedAndStatus {
authbits: u16,
}

const TIMING_PARAMETERS_BYTES: usize = 18;
const TIMING_PARAMETERS_WORDS: usize = 2;
const TIMING_PARAMETERS_BITS: usize = 141;
const TIMING_PARAMETERS_BYTES: usize = (TIMING_PARAMETERS_BITS + 7) / 8;

#[doc(hidden)]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
Expand Down Expand Up @@ -704,13 +787,13 @@ impl_common!(
CedAndStatus,
CED_AND_STATUS_BYTES,
CED_AND_STATUS_WORDS,
549
CED_AND_STATUS_BITS
);
impl_common!(
TimingParameters,
TIMING_PARAMETERS_BYTES,
TIMING_PARAMETERS_WORDS,
141
TIMING_PARAMETERS_BITS
);

impl CedAndStatus {
Expand Down
123 changes: 102 additions & 21 deletions src/tesla.rs
Original file line number Diff line number Diff line change
Expand Up @@ -762,8 +762,8 @@ impl Key<Validated> {
/// Tries to validate a tag and its corresponding navigation data.
///
/// The algorithm in Section 6.7 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).
/// Is used to attempt to validate a tag and its corresponding navigation data.
/// [OSNMA SIS ICD v1.1](https://www.gsc-europa.eu/sites/default/files/sites/all/files/Galileo_OSNMA_SIS_ICD_v1.1.pdf)
/// is used to attempt to validate a tag and its corresponding navigation data.
///
/// The `tag_gst` parameter should give the GST at the start of the subframe
/// when the `tag` was transmitted. The `prnd` and `prna` parameters are
Expand All @@ -781,7 +781,6 @@ impl Key<Validated> {
/// difference between the GSTs of the key and the tag should be 11
/// subframes).
///
///
/// This returns `true` if the validation was succesful. Otherwise, it
/// returns `false`.
#[allow(clippy::too_many_arguments)]
Expand All @@ -797,15 +796,41 @@ impl Key<Validated> {
) -> bool {
let mut mac = self.mac_digest();
mac.update(&[prnd]);
self.update_common_tag_message(&mut mac, tag_gst, prna, ctr, nma_status, navdata);
Self::update_mac_with_navdata(&mut mac, tag_gst, prna, ctr, nma_status, navdata);
self.check_common(mac, tag)
}

/// Tries to validate a dummy tag.
///
/// The algorithm in Section 6.7 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)
/// is used to attempt to validate a dummy tag.
///
/// The length of the corresponding navigation data in bits is supplied in
/// the `navdata_len_bits` parameter. See [`Key::validate_tag`] for a
/// description of the remaining parameters and the return value.
#[allow(clippy::too_many_arguments)]
pub fn validate_tag_dummy(
&self,
tag: &BitSlice,
tag_gst: Gst,
prnd: u8,
prna: Svn,
ctr: u8,
nma_status: NmaStatus,
navdata_len_bits: usize,
) -> bool {
let mut mac = self.mac_digest();
mac.update(&[prnd]);
Self::update_mac_with_dummy(&mut mac, tag_gst, prna, ctr, nma_status, navdata_len_bits);
self.check_common(mac, tag)
}

/// Tries to validate a tag0 and its corresponding navigation data.
///
/// The algorithm in Section 6.7 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).
/// Is used to attempt to validate a tag and its corresponding navigation data.
/// [OSNMA SIS ICD v1.1](https://www.gsc-europa.eu/sites/default/files/sites/all/files/Galileo_OSNMA_SIS_ICD_v1.1.pdf)
/// is used to attempt to validate a tag and its corresponding navigation data.
///
/// The `tag_gst` parameter should give the GST at the start of the subframe
/// when the `tag` was transmitted. The `prna` parameter corresponds to the
Expand All @@ -830,7 +855,29 @@ impl Key<Validated> {
navdata: &BitSlice,
) -> bool {
let mut mac = self.mac_digest();
self.update_common_tag_message(&mut mac, tag_gst, prna, 1, nma_status, navdata);
Self::update_mac_with_navdata(&mut mac, tag_gst, prna, 1, nma_status, navdata);
self.check_common(mac, tag0)
}

/// Tries to validate a dummy tag0.
///
/// The algorithm in Section 6.7 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)
/// is used to attempt to validate a dummy tag.
///
/// The length of the corresponding navigation data in bits is supplied in
/// the `navdata_len_bits` parameter. See [`Key::validate_tag`] for a
/// description of the remaining parameters and the return value.
pub fn validate_tag0_dummy(
&self,
tag0: &BitSlice,
tag_gst: Gst,
prna: Svn,
nma_status: NmaStatus,
navdata_len_bits: usize,
) -> bool {
let mut mac = self.mac_digest();
Self::update_mac_with_dummy(&mut mac, tag_gst, prna, 1, nma_status, navdata_len_bits);
self.check_common(mac, tag0)
}

Expand All @@ -839,34 +886,68 @@ impl Key<Validated> {
MacDigest::new_from_slice(self.chain.mac_function, key).unwrap()
}

fn update_common_tag_message(
&self,
mac: &mut MacDigest,
// This is large enough to fit all the message for ADKD=0 and 12
// (which have the largest navdata size, equal to 549 bits)
const MAX_NAVDATA_SIZE: usize = 69;
const TAG_FIXED_SIZE: usize = 6;
const TAG_BUFF_SIZE: usize = Self::TAG_FIXED_SIZE + Self::MAX_NAVDATA_SIZE;
const STATUS_BITS: usize = 2;

fn new_tag_buffer() -> [u8; Self::TAG_BUFF_SIZE] {
[0u8; Self::TAG_BUFF_SIZE]
}

fn fill_buffer_header(
buffer: &mut [u8; Self::TAG_BUFF_SIZE],
gst: Gst,
prna: Svn,
ctr: u8,
nma_status: NmaStatus,
navdata: &BitSlice,
) {
// This is large enough to fit all the message for ADKD=0 and 12
// (which have the largest navdata size, equal to 549 bits)
const MAX_NAVDATA_SIZE: usize = 69;
const FIXED_SIZE: usize = 6;
const BUFF_SIZE: usize = FIXED_SIZE + MAX_NAVDATA_SIZE;
let mut buffer = [0u8; BUFF_SIZE];
buffer[0] = u8::from(prna);
Self::store_gst(&mut buffer[1..5], gst);
buffer[5] = ctr;
let remaining_bits = BitSlice::from_slice_mut(&mut buffer[6..]);
const STATUS_BITS: usize = 2;
remaining_bits[..STATUS_BITS].store_be(match nma_status {
remaining_bits[..Self::STATUS_BITS].store_be(match nma_status {
NmaStatus::Reserved => 0,
NmaStatus::Test => 1,
NmaStatus::Operational => 2,
NmaStatus::DontUse => 3,
});
remaining_bits[STATUS_BITS..STATUS_BITS + navdata.len()].copy_from_bitslice(navdata);
let message_bytes = FIXED_SIZE + (STATUS_BITS + navdata.len() + 7) / 8;
}

fn fill_buffer_navdata(buffer: &mut [u8; Self::TAG_BUFF_SIZE], navdata: &BitSlice) {
let remaining_bits = BitSlice::from_slice_mut(&mut buffer[6..]);
remaining_bits[Self::STATUS_BITS..Self::STATUS_BITS + navdata.len()]
.copy_from_bitslice(navdata);
}

fn update_mac_with_navdata(
mac: &mut MacDigest,
gst: Gst,
prna: Svn,
ctr: u8,
nma_status: NmaStatus,
navdata: &BitSlice,
) {
let mut buffer = Self::new_tag_buffer();
Self::fill_buffer_header(&mut buffer, gst, prna, ctr, nma_status);
Self::fill_buffer_navdata(&mut buffer, navdata);
let message_bytes = Self::TAG_FIXED_SIZE + (Self::STATUS_BITS + navdata.len() + 7) / 8;
mac.update(&buffer[..message_bytes]);
}

fn update_mac_with_dummy(
mac: &mut MacDigest,
gst: Gst,
prna: Svn,
ctr: u8,
nma_status: NmaStatus,
navdata_len_bits: usize,
) {
let mut buffer = Self::new_tag_buffer();
Self::fill_buffer_header(&mut buffer, gst, prna, ctr, nma_status);
let message_bytes = Self::TAG_FIXED_SIZE + (Self::STATUS_BITS + navdata_len_bits + 7) / 8;
mac.update(&buffer[..message_bytes]);
}

Expand Down

0 comments on commit 7719fb1

Please sign in to comment.