mirror of
https://github.com/chirpstack/chirpstack.git
synced 2025-04-13 22:23:07 +00:00
Add passive_roaming_validate_mic option.
This option does two things: 1) In case the passive-roaming agreement is not state-less, it will trigger the validation of MIC (this was already implemented at the roaming-session retrieval, but never used). 2) On PRStartAns, it will return the NwkSKey / FNwkSIntKey to the requester (such that the MIC validation can be performed). For state-less passive-roaming, it is recommended to leave this option set to false, such that no session-keys are exposed.
This commit is contained in:
parent
04ffcf88a1
commit
52a08acf81
@ -293,6 +293,7 @@ async fn _handle_pr_start_req_data(
|
||||
let region_common_name = CommonName::from_str(&pl.ul_meta_data.rf_region)?;
|
||||
let region_config_id = region::get_region_config_id(region_common_name)?;
|
||||
let dr = pl.ul_meta_data.data_rate.unwrap_or_default();
|
||||
let validate_mic = roaming::get_passive_roaming_validate_mic(sender_id)?;
|
||||
|
||||
let mut ufs = UplinkFrameSet {
|
||||
uplink_set_id: Uuid::new_v4(),
|
||||
@ -318,7 +319,7 @@ async fn _handle_pr_start_req_data(
|
||||
let kek_label = roaming::get_passive_roaming_kek_label(sender_id)?;
|
||||
let ds = d.get_device_session()?;
|
||||
|
||||
let nwk_s_key = if ds.mac_version().to_string().starts_with("1.0") {
|
||||
let nwk_s_key = if validate_mic && ds.mac_version().to_string().starts_with("1.0") {
|
||||
Some(keywrap::wrap(
|
||||
&kek_label,
|
||||
AES128Key::from_slice(&ds.nwk_s_enc_key)?,
|
||||
@ -327,7 +328,7 @@ async fn _handle_pr_start_req_data(
|
||||
None
|
||||
};
|
||||
|
||||
let f_nwk_s_int_key = if ds.mac_version().to_string().starts_with("1.0") {
|
||||
let f_nwk_s_int_key = if validate_mic && ds.mac_version().to_string().starts_with("1.0") {
|
||||
None
|
||||
} else {
|
||||
Some(keywrap::wrap(
|
||||
|
@ -155,6 +155,25 @@ pub fn get_passive_roaming_kek_label(net_id: NetID) -> Result<String> {
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_passive_roaming_validate_mic(net_id: NetID) -> Result<bool> {
|
||||
let conf = config::get();
|
||||
|
||||
for s in &conf.roaming.servers {
|
||||
if s.net_id == net_id {
|
||||
return Ok(s.passive_roaming_validate_mic);
|
||||
}
|
||||
}
|
||||
|
||||
if conf.roaming.default.enabled {
|
||||
return Ok(conf.roaming.default.passive_roaming_validate_mic);
|
||||
}
|
||||
|
||||
Err(anyhow!(
|
||||
"Passive-roaming mic-check for net_id {} does not exist",
|
||||
net_id
|
||||
))
|
||||
}
|
||||
|
||||
pub fn is_enabled() -> bool {
|
||||
let conf = config::get();
|
||||
conf.roaming.default.enabled || !conf.roaming.servers.is_empty()
|
||||
|
@ -802,6 +802,13 @@ pub fn run() {
|
||||
#
|
||||
# If set, the session-keys will be encrypted using the given KEK.
|
||||
passive_roaming_kek_label="{{roaming.default.passive_roaming_kek_label}}"
|
||||
|
||||
# Passive-roaming validate MIC.
|
||||
#
|
||||
# If set ChirpStack will validate the MIC (for non-stateless roaming
|
||||
# agreements). As well it means it will expose the NwkSKey / FNwkSIntKey
|
||||
# on PRStartAns.
|
||||
passive_roaming_validate_mic={{roaming.default.passive_roaming_validate_mic}}
|
||||
|
||||
# Server.
|
||||
#
|
||||
@ -846,6 +853,13 @@ pub fn run() {
|
||||
# #
|
||||
# # If set, the session-keys will be encrypted using the given KEK.
|
||||
# passive_roaming_kek_label=""
|
||||
|
||||
# # Passive-roaming validate MIC.
|
||||
# #
|
||||
# # If set ChirpStack will validate the MIC (for non-stateless roaming
|
||||
# # agreements). As well it means it will expose the NwkSKey / FNwkSIntKey
|
||||
# # on PRStartAns.
|
||||
# passive_roaming_validate_mic=false
|
||||
#
|
||||
# # Server.
|
||||
# #
|
||||
@ -878,6 +892,7 @@ pub fn run() {
|
||||
async_timeout="{{ this.async_timeout }}"
|
||||
passive_roaming_lifetime="{{ this.passive_roaming_lifetime }}"
|
||||
passive_roaming_kek_label="{{ this.passive_roaming_kek_label }}"
|
||||
passive_roaming_validate_mic={{ this.passive_roaming_validate_mic }}
|
||||
server="{{ this.server }}"
|
||||
use_target_role_suffix="{{ this.use_target_role_suffix }}"
|
||||
ca_cert="{{ this.ca_cert }}"
|
||||
|
@ -487,6 +487,7 @@ pub struct RoamingServer {
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub passive_roaming_lifetime: Duration,
|
||||
pub passive_roaming_kek_label: String,
|
||||
pub passive_roaming_validate_mic: bool,
|
||||
pub server: String,
|
||||
pub use_target_role_suffix: bool,
|
||||
pub ca_cert: String,
|
||||
@ -504,6 +505,7 @@ pub struct RoamingServerDefault {
|
||||
#[serde(with = "humantime_serde")]
|
||||
pub passive_roaming_lifetime: Duration,
|
||||
pub passive_roaming_kek_label: String,
|
||||
pub passive_roaming_validate_mic: bool,
|
||||
pub server: String,
|
||||
pub use_target_role_suffix: bool,
|
||||
pub ca_cert: String,
|
||||
|
@ -182,6 +182,7 @@ async fn test_sns_uplink() {
|
||||
// Set roaming agreement.
|
||||
conf.roaming.servers.push(config::RoamingServer {
|
||||
net_id: NetID::from_str("000202").unwrap(),
|
||||
passive_roaming_validate_mic: true,
|
||||
server: fns_mock.url("/"),
|
||||
..Default::default()
|
||||
});
|
||||
|
@ -200,6 +200,7 @@ impl Data {
|
||||
session_id: sess_id.as_bytes().to_vec(),
|
||||
net_id: net_id.to_vec(),
|
||||
dev_addr: self.mac_payload.fhdr.devaddr.to_vec(),
|
||||
validate_mic: roaming::get_passive_roaming_validate_mic(net_id)?,
|
||||
lifetime: {
|
||||
let lt = pr_start_ans.lifetime.unwrap_or_default() as i64;
|
||||
if lt == 0 {
|
||||
|
@ -172,6 +172,7 @@ impl JoinRequest {
|
||||
let sess = internal::PassiveRoamingDeviceSession {
|
||||
session_id: sess_id.as_bytes().to_vec(),
|
||||
net_id: self.home_net_id.unwrap().to_vec(),
|
||||
validate_mic: roaming::get_passive_roaming_validate_mic(self.home_net_id.unwrap())?,
|
||||
dev_addr: pr_start_ans.dev_addr.clone(),
|
||||
dev_eui: self.join_request.dev_eui.to_vec(),
|
||||
lifetime: {
|
||||
|
Loading…
x
Reference in New Issue
Block a user