Skip to content

Commit

Permalink
Added support for large system time changes (e.g. due to NTP) (ARMmbe…
Browse files Browse the repository at this point in the history
…d#2670)

- When loading GTKs on startup if there has been large jump on system
time do not invalidate GTKs (e.g. due to received NTP time).
- On Border Router, if there is large jump on system time after startup
do not set GTK lifetime to minimum (minimum is new activation time).
- Either of the rules above are used once and do not apply after that
(status is stored to NVM).
- When above rules are not used, GTK lifetime is decremented as defined
by the system timestamp until minimum is reached (minimum is new
activation time).
- Border Router key storage handling (storing of PMK and PTK) has not
been modified so jump in system time will invalidate the values and
on next authentication full authentication is made.
  • Loading branch information
Mika Leppänen authored Aug 26, 2021
1 parent c94b306 commit 0c5faca
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 58 deletions.
2 changes: 2 additions & 0 deletions source/6LoWPAN/ws/ws_bootstrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -1043,6 +1043,8 @@ static void ws_bootstrap_dhcp_info_notify_cb(int8_t interface, dhcp_option_notif
int ret = ns_time_system_time_write(network_time);
tr_info("Network Time %s: Era:%"PRId32" Offset:%"PRIu32" old time: %"PRIu64" time: %"PRIu64, ret == 0 ? "updated" : "update FAILED", era, offset, current_time, network_time);
}
// System time has been acquired
ns_time_system_time_acquired_set();
}
}
}
Expand Down
75 changes: 74 additions & 1 deletion source/6LoWPAN/ws/ws_pae_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@
#define FRAME_CNT_TIMER 3600

#define SECONDS_IN_DAY (3600 * 24)
#define TIME_MINIMUM_DIFFERENCE 5
#define TIME_DIFFERENCE_THRESHOLD 3600

typedef struct {
ns_list_link_t link; /**< Link */
Expand All @@ -108,6 +110,8 @@ typedef struct {
sec_prot_keys_nw_info_t *sec_keys_nw_info; /**< Security keys network information */
sec_cfg_t *sec_cfg; /**< Security configuration */
frame_counters_t *frame_counters; /**< Frame counters */
uint64_t prev_system_time; /**< Previous system time */
uint64_t system_time_diff; /**< System time diffence */
uint32_t prev_frame_cnt; /**< Previous frame counter */
uint16_t prev_frame_cnt_timer; /**< Previous frame counter timer */
uint16_t supp_max_number; /**< Max number of stored supplicants */
Expand All @@ -116,7 +120,8 @@ typedef struct {
uint8_t radius_socked_msg_if_instance_id; /**< Radius socket message interface instance identifier */
bool timer_running : 1; /**< Timer is running */
bool gtk_new_inst_req_exp : 1; /**< GTK new install required timer expired */
bool gtk_new_act_time_exp: 1; /**< GTK new activation time expired */
bool gtk_new_act_time_exp : 1; /**< GTK new activation time expired */
bool prev_system_time_set : 1; /**< Previous system time set */
bool prev_frame_cnt_set : 1; /**< Previous frame counter set */
} pae_auth_t;

Expand All @@ -129,6 +134,7 @@ static pae_auth_t *ws_pae_auth_by_kmp_service_get(kmp_service_t *service);
static int8_t ws_pae_auth_event_send(kmp_service_t *service, void *data);
static void ws_pae_auth_tasklet_handler(arm_event_s *event);
static uint32_t ws_pae_auth_lifetime_key_frame_cnt_check(pae_auth_t *pae_auth, uint8_t gtk_index, uint16_t seconds);
static uint32_t ws_pae_auth_lifetime_system_time_check(pae_auth_t *pae_auth, int8_t gtk_index, uint16_t seconds, uint32_t dec_extra_seconds);
static void ws_pae_auth_gtk_key_insert(pae_auth_t *pae_auth);
static int8_t ws_pae_auth_new_gtk_activate(pae_auth_t *pae_auth);
static int8_t ws_pae_auth_timer_if_start(kmp_service_t *service, kmp_api_t *kmp);
Expand Down Expand Up @@ -193,6 +199,8 @@ int8_t ws_pae_auth_init(protocol_interface_info_entry_t *interface_ptr, sec_prot
pae_auth->sec_keys_nw_info = sec_keys_nw_info;
pae_auth->sec_cfg = sec_cfg;
pae_auth->frame_counters = frame_counters;
pae_auth->prev_system_time = 0;
pae_auth->system_time_diff = 0;
pae_auth->prev_frame_cnt = 0;
pae_auth->prev_frame_cnt_timer = FRAME_CNT_TIMER;
pae_auth->supp_max_number = SUPPLICANT_MAX_NUMBER;
Expand All @@ -201,6 +209,7 @@ int8_t ws_pae_auth_init(protocol_interface_info_entry_t *interface_ptr, sec_prot
pae_auth->gtk_new_inst_req_exp = false;
pae_auth->gtk_new_act_time_exp = false;
pae_auth->prev_frame_cnt_set = false;
pae_auth->prev_system_time_set = false;

pae_auth->relay_socked_msg_if_instance_id = 0;
pae_auth->radius_socked_msg_if_instance_id = 0;
Expand Down Expand Up @@ -754,6 +763,7 @@ void ws_pae_auth_slow_timer(uint16_t seconds)
uint32_t gtk_lifetime_dec_extra_seconds = 0;
if (active_index == i) {
gtk_lifetime_dec_extra_seconds = ws_pae_auth_lifetime_key_frame_cnt_check(pae_auth, i, seconds);
gtk_lifetime_dec_extra_seconds = ws_pae_auth_lifetime_system_time_check(pae_auth, i, seconds, gtk_lifetime_dec_extra_seconds);
}
uint32_t timer_seconds = sec_prot_keys_gtk_lifetime_decrement(pae_auth->sec_keys_nw_info->gtks, i, current_time, seconds + gtk_lifetime_dec_extra_seconds, true);
if (active_index == i) {
Expand Down Expand Up @@ -787,6 +797,10 @@ void ws_pae_auth_slow_timer(uint16_t seconds)
pae_auth->nw_info_updated(pae_auth->interface_ptr);
}
}
if (gtk_lifetime_dec_extra_seconds != 0) {
// Update keys to NVM as needed
pae_auth->nw_info_updated(pae_auth->interface_ptr);
}
}

if (timer_seconds == 0) {
Expand Down Expand Up @@ -901,6 +915,65 @@ static uint32_t ws_pae_auth_lifetime_key_frame_cnt_check(pae_auth_t *pae_auth, u
return decrement_seconds;
}

static uint32_t ws_pae_auth_lifetime_system_time_check(pae_auth_t *pae_auth, int8_t gtk_index, uint16_t seconds, uint32_t dec_extra_seconds)
{
// Read current system time and compare it to previous time
uint64_t current_time = ws_pae_current_time_get();
if (pae_auth->prev_system_time_set) {
if (current_time > pae_auth->prev_system_time + TIME_MINIMUM_DIFFERENCE) {
pae_auth->system_time_diff += current_time - pae_auth->prev_system_time;
}
}
pae_auth->prev_system_time = current_time;
pae_auth->prev_system_time_set = true;

uint64_t time_diff = 0;
// Update lifetimes only if time difference is more than hour
if (pae_auth->system_time_diff > TIME_DIFFERENCE_THRESHOLD + seconds + dec_extra_seconds) {
time_diff = pae_auth->system_time_diff - seconds - dec_extra_seconds;
} else {
return 0;
}
pae_auth->system_time_diff = 0;

uint32_t new_dec_extra_seconds = 0;

if (time_diff > 0) {
/* If the system time has made a large jump then use the stored time to calculate the lifetime
(this implies e.g. that new time has been received from NTP and old time was not valid) */
if (!ws_pae_time_old_and_new_validate(current_time, current_time + time_diff)) {
// Allow one jump without invalidating active GTK
if (pae_auth->sec_keys_nw_info->system_time_changed == SYSTEM_TIME_NOT_CHANGED) {
pae_auth->sec_keys_nw_info->system_time_changed = SYSTEM_TIME_CHANGED;
tr_info("System time large change ignored; difference: %"PRIu64, time_diff);
time_diff = 0;
}
}

uint32_t gtk_lifetime_left = sec_prot_keys_gtk_lifetime_get(pae_auth->sec_keys_nw_info->gtks, gtk_index);
sec_timer_cfg_t *timer_cfg = &pae_auth->sec_cfg->timer_cfg;
uint32_t gtk_new_activation_time_seconds = timer_cfg->gtk_expire_offset / timer_cfg->gtk_new_act_time;

// If there is GTK lifetime left
if (gtk_lifetime_left > (seconds + dec_extra_seconds + time_diff)) {
// If GTK lifetime would be less than new activation time sets decrements time to activation time
if (gtk_lifetime_left - seconds - dec_extra_seconds - time_diff < gtk_new_activation_time_seconds) {
new_dec_extra_seconds = gtk_lifetime_left - gtk_new_activation_time_seconds;
} else {
// Decrements GTK lifetime
new_dec_extra_seconds = dec_extra_seconds + time_diff;
}
} else {
// If there is no GTK lifetime left decrements time to activation time
new_dec_extra_seconds = gtk_lifetime_left - gtk_new_activation_time_seconds;
}

tr_info("System change difference: %"PRIu64" decrement extra: %"PRIu32" (seconds: %"PRIu16" previous extra %"PRIu32")", time_diff, new_dec_extra_seconds, seconds, dec_extra_seconds);
}

return new_dec_extra_seconds;
}

static void ws_pae_auth_gtk_key_insert(pae_auth_t *pae_auth)
{
// Gets index to install the key
Expand Down
44 changes: 28 additions & 16 deletions source/6LoWPAN/ws/ws_pae_controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "ws_management_api.h"
#include "ws_bbr_api.h"
#include "Service_Libs/utils/ns_file.h"
#include "Service_Libs/utils/ns_time.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "6LoWPAN/ws/ws_config.h"
#include "6LoWPAN/ws/ws_cfg_settings.h"
Expand Down Expand Up @@ -141,8 +142,8 @@ static int8_t ws_pae_controller_frame_counter_read(pae_controller_t *controller)
static void ws_pae_controller_frame_counter_reset(frame_counters_t *frame_counters);
static void ws_pae_controller_frame_counter_index_reset(frame_counters_t *frame_counters, uint8_t index);
static int8_t ws_pae_controller_nw_info_read(pae_controller_t *controller, sec_prot_gtk_keys_t *gtks);
static int8_t ws_pae_controller_nvm_nw_info_write(protocol_interface_info_entry_t *interface_ptr, uint16_t pan_id, char *network_name, uint8_t *gtk_eui64, sec_prot_gtk_keys_t *gtks);
static int8_t ws_pae_controller_nvm_nw_info_read(protocol_interface_info_entry_t *interface_ptr, uint16_t *pan_id, char *network_name, uint8_t *gtk_eui64, sec_prot_gtk_keys_t *gtks);
static int8_t ws_pae_controller_nvm_nw_info_write(protocol_interface_info_entry_t *interface_ptr, uint16_t pan_id, char *network_name, uint8_t *gtk_eui64, sec_prot_gtk_keys_t *gtks, uint64_t stored_time, uint8_t time_changed);
static int8_t ws_pae_controller_nvm_nw_info_read(protocol_interface_info_entry_t *interface_ptr, uint16_t *pan_id, char *network_name, uint8_t *gtk_eui64, sec_prot_gtk_keys_t *gtks, uint64_t current_time, uint8_t *time_changed);


static const char *FRAME_COUNTER_FILE = FRAME_COUNTER_FILE_NAME;
Expand Down Expand Up @@ -377,7 +378,8 @@ static void ws_pae_controller_nw_info_updated_check(protocol_interface_info_entr
if (arm_nwk_mac_address_read(interface_ptr->id, &mac_params) >= 0) {
memcpy(gtk_eui64, mac_params.mac_long, 8);
}
ws_pae_controller_nvm_nw_info_write(interface_ptr, controller->sec_keys_nw_info.key_pan_id, controller->sec_keys_nw_info.network_name, gtk_eui64, controller->sec_keys_nw_info.gtks);
uint64_t system_time = ws_pae_current_time_get();
ws_pae_controller_nvm_nw_info_write(interface_ptr, controller->sec_keys_nw_info.key_pan_id, controller->sec_keys_nw_info.network_name, gtk_eui64, controller->sec_keys_nw_info.gtks, system_time, controller->sec_keys_nw_info.system_time_changed);
controller->sec_keys_nw_info.updated = false;
sec_prot_keys_gtks_updated_reset(controller->sec_keys_nw_info.gtks);
}
Expand Down Expand Up @@ -820,8 +822,8 @@ static int8_t ws_pae_controller_frame_counter_read(pae_controller_t *controller)

// Read frame counters
if (ws_pae_controller_nvm_frame_counter_read(&controller->restart_cnt, &stored_time, &controller->sec_keys_nw_info.pan_version, &controller->frame_counters) >= 0) {
// Current time is not valid
if (ws_pae_current_time_set(stored_time) < 0) {
// Check if stored time is not valid
if (ws_pae_stored_time_check_and_set(stored_time) < 0) {
ret_value = -1;
}
// This is used to ensure that PMK replay counters are fresh after each re-start.
Expand Down Expand Up @@ -872,10 +874,16 @@ static void ws_pae_controller_frame_counter_index_reset(frame_counters_t *frame_
static int8_t ws_pae_controller_nw_info_read(pae_controller_t *controller, sec_prot_gtk_keys_t *gtks)
{
uint8_t nvm_gtk_eui64[8];
if (ws_pae_controller_nvm_nw_info_read(controller->interface_ptr, &controller->sec_keys_nw_info.key_pan_id, controller->sec_keys_nw_info.network_name, nvm_gtk_eui64, gtks) < 0) {
uint64_t system_time = ws_pae_current_time_get();

uint8_t system_time_changed = controller->sec_keys_nw_info.system_time_changed;
if (ws_pae_controller_nvm_nw_info_read(controller->interface_ptr, &controller->sec_keys_nw_info.key_pan_id, controller->sec_keys_nw_info.network_name, nvm_gtk_eui64, gtks, system_time, &controller->sec_keys_nw_info.system_time_changed) < 0) {
// If no stored GTKs and network info (pan_id and network name) exits
return -1;
}
if (system_time_changed != controller->sec_keys_nw_info.system_time_changed) {
controller->sec_keys_nw_info.updated = true;
}

/* Get own EUI-64 and compare to the one read from the NVM. In case of mismatch delete GTKs and make
full authentication to update keys with new EUI-64 and in case of authenticator to update new
Expand All @@ -896,21 +904,21 @@ static int8_t ws_pae_controller_nw_info_read(pae_controller_t *controller, sec_p
return 0;
}

static int8_t ws_pae_controller_nvm_nw_info_write(protocol_interface_info_entry_t *interface_ptr, uint16_t pan_id, char *network_name, uint8_t *gtk_eui64, sec_prot_gtk_keys_t *gtks)
static int8_t ws_pae_controller_nvm_nw_info_write(protocol_interface_info_entry_t *interface_ptr, uint16_t pan_id, char *network_name, uint8_t *gtk_eui64, sec_prot_gtk_keys_t *gtks, uint64_t stored_time, uint8_t time_changed)
{
nw_info_nvm_tlv_t *tlv = (nw_info_nvm_tlv_t *) ws_pae_controller_nvm_tlv_get(interface_ptr);
if (!tlv) {
return -1;
}

ws_pae_nvm_store_nw_info_tlv_create(tlv, pan_id, network_name, gtk_eui64, gtks);
ws_pae_nvm_store_nw_info_tlv_create(tlv, pan_id, network_name, gtk_eui64, gtks, stored_time, time_changed);

ws_pae_nvm_store_tlv_file_write(NW_INFO_FILE, (nvm_tlv_t *) tlv);

return 0;
}

static int8_t ws_pae_controller_nvm_nw_info_read(protocol_interface_info_entry_t *interface_ptr, uint16_t *pan_id, char *network_name, uint8_t *gtk_eui64, sec_prot_gtk_keys_t *gtks)
static int8_t ws_pae_controller_nvm_nw_info_read(protocol_interface_info_entry_t *interface_ptr, uint16_t *pan_id, char *network_name, uint8_t *gtk_eui64, sec_prot_gtk_keys_t *gtks, uint64_t current_time, uint8_t *time_changed)
{
nw_info_nvm_tlv_t *tlv_entry = (nw_info_nvm_tlv_t *) ws_pae_controller_nvm_tlv_get(interface_ptr);
if (!tlv_entry) {
Expand All @@ -923,7 +931,7 @@ static int8_t ws_pae_controller_nvm_nw_info_read(protocol_interface_info_entry_t
return -1;
}

if (ws_pae_nvm_store_nw_info_tlv_read(tlv_entry, pan_id, network_name, gtk_eui64, gtks) < 0) {
if (ws_pae_nvm_store_nw_info_tlv_read(tlv_entry, pan_id, network_name, gtk_eui64, gtks, current_time, time_changed) < 0) {
return -1;
}

Expand Down Expand Up @@ -972,6 +980,9 @@ int8_t ws_pae_controller_auth_init(protocol_interface_info_entry_t *interface_pt
return -1;
}

// For Border Router enable the time acquired as default
ns_time_system_time_acquired_set();

controller->pae_delete = ws_pae_auth_delete;
controller->pae_fast_timer = ws_pae_auth_fast_timer;
controller->pae_slow_timer = ws_pae_auth_slow_timer;
Expand All @@ -996,21 +1007,21 @@ int8_t ws_pae_controller_auth_init(protocol_interface_info_entry_t *interface_pt
sec_prot_keys_gtks_updated_reset(&controller->gtks);
}
#endif

if (ws_pae_controller_nw_info_read(controller, read_gtks_to) >= 0) {
if (read_gtks_to && ws_pae_controller_nw_info_read(controller, read_gtks_to) >= 0) {
/* If network information i.e pan_id and network name exists updates bootstrap with it,
(in case already configured by application then no changes are made) */
if (controller->nw_info_updated) {
controller->nw_info_updated(interface_ptr, controller->sec_keys_nw_info.key_pan_id, controller->sec_keys_nw_info.pan_version, controller->sec_keys_nw_info.network_name);
}
if (!read_gtks_to || sec_prot_keys_gtk_count(read_gtks_to) == 0) {
if (sec_prot_keys_gtk_count(read_gtks_to) == 0) {
// Key material invalid or GTKs are expired, delete GTKs from NVM
uint8_t gtk_eui64[8] = {0}; // Set GTK EUI-64 to zero
ws_pae_controller_nvm_nw_info_write(controller->interface_ptr, controller->sec_keys_nw_info.key_pan_id, controller->sec_keys_nw_info.network_name, gtk_eui64, NULL);
uint64_t system_time = ws_pae_current_time_get();
ws_pae_controller_nvm_nw_info_write(controller->interface_ptr, controller->sec_keys_nw_info.key_pan_id, controller->sec_keys_nw_info.network_name, gtk_eui64, NULL, system_time, controller->sec_keys_nw_info.system_time_changed);
}
}

ws_pae_key_storage_init();

if (read_gtks_to) {
ws_pae_key_storage_read(controller->restart_cnt);
} else {
Expand Down Expand Up @@ -1793,8 +1804,9 @@ static void ws_pae_controller_frame_counter_store(pae_controller_t *entry, bool

if (update_needed || entry->frame_cnt_store_force_timer == 0) {
tr_debug("Write frame counters: system time %"PRIu32"", protocol_core_monotonic_time / 10);
uint64_t system_time = ws_pae_current_time_get();
// Writes modified frame counters
ws_pae_nvm_store_frame_counter_tlv_create((frame_cnt_nvm_tlv_t *) &entry->pae_nvm_buffer, entry->restart_cnt, entry->sec_keys_nw_info.pan_version, &entry->frame_counters);
ws_pae_nvm_store_frame_counter_tlv_create((frame_cnt_nvm_tlv_t *) &entry->pae_nvm_buffer, entry->restart_cnt, entry->sec_keys_nw_info.pan_version, &entry->frame_counters, system_time);
ws_pae_controller_nvm_frame_counter_write((frame_cnt_nvm_tlv_t *) &entry->pae_nvm_buffer);

// Reset force interval when ever values are stored
Expand Down
2 changes: 1 addition & 1 deletion source/6LoWPAN/ws/ws_pae_key_storage.c
Original file line number Diff line number Diff line change
Expand Up @@ -944,7 +944,7 @@ static int8_t ws_pae_key_storage_array_time_update_entry(uint64_t time_differenc
#endif
}

if (storage_array_entry->pmk_lifetime_set) {
if (storage_array_entry->ptk_lifetime_set) {
#ifdef EXTRA_DEBUG_INFO
tr_debug("KeyS time update diff: %"PRIi64" PTK OLD t: %i %i eui64: %s", time_difference, STIME_TIME_GET(storage_array_entry->ptk_lifetime), STIME_FORMAT_GET(storage_array_entry->ptk_lifetime), tr_array(storage_array_entry->ptk_eui_64, 8));
#endif
Expand Down
Loading

0 comments on commit 0c5faca

Please sign in to comment.