mbed-os/features/nanostack/sal-stack-nanostack/source/Security/protocols/sec_prot_keys.c

855 lines
23 KiB
C

/*
* Copyright (c) 2016-2019, Arm Limited and affiliates.
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "nsconfig.h"
#include <string.h>
#include "ns_types.h"
#include "ns_list.h"
#include "ns_trace.h"
#include "nsdynmemLIB.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "Common_Protocols/ipv6_constants.h"
#include "socket_api.h"
#include "6LoWPAN/ws/ws_config.h"
#include "Security/kmp/kmp_addr.h"
#include "Security/kmp/kmp_api.h"
#include "Security/PANA/pana_eap_header.h"
#include "Security/eapol/eapol_helper.h"
#include "Security/protocols/sec_prot_certs.h"
#include "Security/protocols/sec_prot_keys.h"
#include "Security/protocols/sec_prot.h"
#include "Security/protocols/sec_prot_lib.h"
#ifdef HAVE_WS
#define TRACE_GROUP "spke"
static const uint8_t empty_hash[GTK_HASH_LEN] = {0};
sec_prot_keys_t *sec_prot_keys_create(sec_prot_gtk_keys_t *gtks, const sec_prot_certs_t *certs)
{
sec_prot_keys_t *sec_keys = ns_dyn_mem_alloc(sizeof(sec_prot_keys_t));
if (!sec_keys) {
return NULL;
}
sec_prot_keys_init(sec_keys, gtks, certs);
return sec_keys;
}
void sec_prot_keys_init(sec_prot_keys_t *sec_keys, sec_prot_gtk_keys_t *gtks, const sec_prot_certs_t *certs)
{
memset(sec_keys, 0, sizeof(sec_prot_keys_t));
sec_keys->pmk_lifetime = PMK_LIFETIME_INSTALL;
sec_keys->ptk_lifetime = PTK_LIFETIME_INSTALL;
sec_keys->pmk_key_replay_cnt = 0;
sec_keys->gtks = gtks;
sec_keys->certs = certs;
sec_keys->gtkl = 0;
sec_keys->gtk_set_index = -1;
sec_keys->pmk_set = false;
sec_keys->ptk_set = false;
sec_keys->pmk_key_replay_cnt_set = false;
sec_keys->updated = false;
sec_keys->ptk_eui_64_set = false;
sec_keys->pmk_mismatch = false;
sec_keys->ptk_mismatch = false;
sec_prot_keys_ptk_installed_gtk_hash_clear_all(sec_keys);
}
void sec_prot_keys_delete(sec_prot_keys_t *sec_keys)
{
ns_dyn_mem_free(sec_keys);
}
sec_prot_gtk_keys_t *sec_prot_keys_gtks_create(void)
{
sec_prot_gtk_keys_t *gtks = ns_dyn_mem_alloc(sizeof(sec_prot_gtk_keys_t));
if (!gtks) {
return NULL;
}
sec_prot_keys_gtks_init(gtks);
return gtks;
}
void sec_prot_keys_gtks_init(sec_prot_gtk_keys_t *gtks)
{
memset(gtks, 0, sizeof(sec_prot_gtk_keys_t));
gtks->updated = false;
}
void sec_prot_keys_gtks_delete(sec_prot_gtk_keys_t *gtks)
{
ns_dyn_mem_free(gtks);
}
void sec_prot_keys_pmk_write(sec_prot_keys_t *sec_keys, uint8_t *pmk)
{
memcpy(sec_keys->pmk, pmk, PMK_LEN);
sec_keys->pmk_key_replay_cnt = 0;
sec_keys->pmk_key_replay_cnt_set = false;
sec_keys->pmk_lifetime = PMK_LIFETIME_INSTALL;
sec_keys->pmk_set = true;
sec_keys->updated = true;
}
void sec_prot_keys_pmk_delete(sec_prot_keys_t *sec_keys)
{
memset(sec_keys->pmk, 0, PMK_LEN);
sec_keys->pmk_key_replay_cnt = 0;
sec_keys->pmk_key_replay_cnt_set = false;
sec_keys->pmk_lifetime = PMK_LIFETIME_INSTALL;
sec_keys->pmk_set = false;
sec_keys->updated = true;
}
uint8_t *sec_prot_keys_pmk_get(sec_prot_keys_t *sec_keys)
{
if (!sec_keys->pmk_set) {
return NULL;
}
return sec_keys->pmk;
}
uint64_t sec_prot_keys_pmk_replay_cnt_get(sec_prot_keys_t *sec_keys)
{
return sec_keys->pmk_key_replay_cnt;
}
void sec_prot_keys_pmk_replay_cnt_set(sec_prot_keys_t *sec_keys, uint64_t counter)
{
sec_keys->pmk_key_replay_cnt_set = true;
sec_keys->pmk_key_replay_cnt = counter;
}
void sec_prot_keys_pmk_replay_cnt_increment(sec_prot_keys_t *sec_keys)
{
// Start from zero i.e. does not increment on first call
if (!sec_keys->pmk_key_replay_cnt_set) {
sec_keys->pmk_key_replay_cnt_set = true;
return;
}
sec_keys->pmk_key_replay_cnt++;
}
bool sec_prot_keys_pmk_replay_cnt_compare(uint64_t received_counter, sec_prot_keys_t *sec_keys)
{
// If previous value is set must be greater
if (sec_keys->pmk_key_replay_cnt_set && received_counter > sec_keys->pmk_key_replay_cnt) {
return true;
} else if (!sec_keys->pmk_key_replay_cnt_set && received_counter >= sec_keys->pmk_key_replay_cnt) {
// Otherwise allows also same value e.g. zero
return true;
}
return false;
}
void sec_prot_keys_pmk_mismatch_set(sec_prot_keys_t *sec_keys)
{
sec_keys->pmk_mismatch = true;
}
void sec_prot_keys_pmk_mismatch_reset(sec_prot_keys_t *sec_keys)
{
sec_keys->pmk_mismatch = false;
}
bool sec_prot_keys_pmk_mismatch_is_set(sec_prot_keys_t *sec_keys)
{
return sec_keys->pmk_mismatch;
}
bool sec_prot_keys_pmk_lifetime_decrement(sec_prot_keys_t *sec_keys, uint32_t default_lifetime, uint8_t seconds)
{
if (!sec_keys->pmk_set) {
return false;
}
if (sec_keys->pmk_lifetime == PMK_LIFETIME_INSTALL) {
sec_keys->pmk_lifetime = default_lifetime;
}
if (sec_keys->pmk_lifetime > seconds) {
sec_keys->pmk_lifetime -= seconds;
} else {
if (sec_keys->pmk_lifetime > 0) {
sec_keys->pmk_lifetime = 0;
sec_prot_keys_ptk_delete(sec_keys);
sec_prot_keys_pmk_delete(sec_keys);
return true;
}
}
return false;
}
void sec_prot_keys_ptk_write(sec_prot_keys_t *sec_keys, uint8_t *ptk)
{
memcpy(sec_keys->ptk, ptk, PTK_LEN);
sec_keys->ptk_lifetime = PTK_LIFETIME_INSTALL;
sec_keys->ptk_set = true;
sec_keys->updated = true;
}
void sec_prot_keys_ptk_delete(sec_prot_keys_t *sec_keys)
{
memset(sec_keys->ptk, 0, PTK_LEN);
sec_keys->ptk_lifetime = PTK_LIFETIME_INSTALL;
sec_keys->ptk_set = false;
sec_keys->updated = true;
}
uint8_t *sec_prot_keys_ptk_get(sec_prot_keys_t *sec_keys)
{
if (!sec_keys->ptk_set) {
return NULL;
}
return sec_keys->ptk;
}
void sec_prot_keys_ptk_mismatch_set(sec_prot_keys_t *sec_keys)
{
sec_keys->ptk_mismatch = true;
}
void sec_prot_keys_ptk_mismatch_reset(sec_prot_keys_t *sec_keys)
{
sec_keys->ptk_mismatch = false;
}
bool sec_prot_keys_ptk_mismatch_is_set(sec_prot_keys_t *sec_keys)
{
return sec_keys->ptk_mismatch;
}
void sec_prot_keys_ptk_eui_64_write(sec_prot_keys_t *sec_keys, const uint8_t *eui_64)
{
memcpy(sec_keys->ptk_eui_64, eui_64, 8);
sec_keys->ptk_eui_64_set = true;
sec_keys->updated = true;
}
uint8_t *sec_prot_keys_ptk_eui_64_get(sec_prot_keys_t *sec_keys)
{
if (!sec_keys->ptk_eui_64_set) {
return NULL;
}
return sec_keys->ptk_eui_64;
}
void sec_prot_keys_ptk_eui_64_delete(sec_prot_keys_t *sec_keys)
{
memset(sec_keys->ptk_eui_64, 0, 8);
sec_keys->ptk_eui_64_set = false;
sec_keys->updated = true;
}
bool sec_prot_keys_ptk_lifetime_decrement(sec_prot_keys_t *sec_keys, uint32_t default_lifetime, uint8_t seconds)
{
if (!sec_keys->ptk_set) {
return false;
}
if (sec_keys->ptk_lifetime == PTK_LIFETIME_INSTALL) {
sec_keys->ptk_lifetime = default_lifetime;
}
if (sec_keys->ptk_lifetime > seconds) {
sec_keys->ptk_lifetime -= seconds;
} else {
if (sec_keys->ptk_lifetime > 0) {
sec_prot_keys_ptk_delete(sec_keys);
sec_keys->ptk_lifetime = 0;
return true;
}
}
return false;
}
bool sec_prot_keys_are_updated(sec_prot_keys_t *sec_keys)
{
return sec_keys->updated;
}
void sec_prot_keys_updated_reset(sec_prot_keys_t *sec_keys)
{
sec_keys->updated = false;
}
uint8_t sec_prot_keys_fresh_gtkl_get(sec_prot_gtk_keys_t *gtks)
{
uint8_t gtkl = 0;
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (sec_prot_keys_gtk_status_is_live(gtks, i)) {
gtkl |= 1 << i;
}
}
return gtkl;
}
void sec_prot_keys_gtkl_set(sec_prot_keys_t *sec_keys, uint8_t gtkl)
{
sec_keys->gtkl = gtkl;
}
bool sec_prot_keys_gtkl_gtk_is_live(sec_prot_keys_t *sec_keys, uint8_t index)
{
if (index >= GTK_NUM) {
return false;
}
if (sec_keys->gtkl & (1 << index)) {
return true;
}
return false;
}
int8_t sec_prot_keys_gtkl_gtk_live_set(sec_prot_keys_t *sec_keys, uint8_t index)
{
if (index >= GTK_NUM) {
return -1;
}
sec_keys->gtkl |= (1 << index);
return 0;
}
int8_t sec_prot_keys_gtk_insert_index_set(sec_prot_keys_t *sec_keys, uint8_t index)
{
if (index >= GTK_NUM || !sec_keys->gtks->gtk[index].set) {
return -1;
}
sec_keys->gtk_set_index = index;
return 0;
}
int8_t sec_prot_keys_gtk_insert_index_get(sec_prot_keys_t *sec_keys)
{
return sec_keys->gtk_set_index;
}
void sec_prot_keys_gtk_insert_index_clear(sec_prot_keys_t *sec_keys)
{
sec_keys->gtk_set_index = -1;
}
void sec_prot_keys_gtkl_from_gtk_insert_index_set(sec_prot_keys_t *sec_keys)
{
if (sec_keys->gtk_set_index >= 0) {
sec_prot_keys_gtkl_gtk_live_set(sec_keys, sec_keys->gtk_set_index);
sec_prot_keys_gtk_insert_index_clear(sec_keys);
}
}
int8_t sec_prot_keys_gtk_insert_index_from_gtkl_get(sec_prot_keys_t *sec_keys)
{
// Get currently active key index
int8_t active_index = sec_prot_keys_gtk_status_active_get(sec_keys->gtks);
if (active_index >= 0 && !sec_prot_keys_gtkl_gtk_is_live(sec_keys, active_index)) {
// If currently active key is not live on remote, inserts it
sec_prot_keys_gtk_insert_index_set(sec_keys, active_index);
return active_index;
}
// Checks all keys
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (sec_prot_keys_gtk_status_is_live(sec_keys->gtks, i)) {
// If key is live, but not indicated on GTKL inserts it
if (!sec_prot_keys_gtkl_gtk_is_live(sec_keys, i)) {
sec_prot_keys_gtk_insert_index_set(sec_keys, i);
return i;
}
}
}
return -1;
}
uint8_t *sec_prot_keys_get_gtk_to_insert(sec_prot_keys_t *sec_keys, uint8_t *index)
{
if (sec_keys->gtk_set_index >= 0 && sec_keys->gtks->gtk[sec_keys->gtk_set_index].set) {
*index = sec_keys->gtk_set_index;
return sec_keys->gtks->gtk[sec_keys->gtk_set_index].key;
} else {
return NULL;
}
}
int8_t sec_prot_keys_gtk_set(sec_prot_gtk_keys_t *gtks, uint8_t index, uint8_t *gtk, uint32_t lifetime)
{
if (!gtk || index >= GTK_NUM) {
return -1;
}
// If same GTK is given again, do not update
if (gtks->gtk[index].set && memcmp(gtks->gtk[index].key, gtk, GTK_LEN) == 0) {
return -1;
}
sec_prot_keys_gtk_clear(gtks, index);
uint8_t install_order = sec_prot_keys_gtk_install_order_last_get(gtks);
gtks->gtk[index].set = true;
gtks->gtk[index].lifetime = lifetime;
gtks->gtk[index].status = GTK_STATUS_NEW;
gtks->gtk[index].install_order = install_order;
memcpy(gtks->gtk[index].key, gtk, GTK_LEN);
gtks->updated = true;
return 0;
}
int8_t sec_prot_keys_gtk_clear(sec_prot_gtk_keys_t *gtks, uint8_t index)
{
if (!gtks || index >= GTK_NUM) {
return -1;
}
gtks->gtk[index].set = false;
gtks->gtk[index].lifetime = 0; // Should be provided by authenticator
gtks->gtk[index].status = GTK_STATUS_NEW;
memset(gtks->gtk[index].key, 0, GTK_LEN);
sec_prot_keys_gtk_install_order_update(gtks);
return 0;
}
bool sec_prot_keys_gtk_is_set(sec_prot_gtk_keys_t *gtks, uint8_t index)
{
if (index >= GTK_NUM || !gtks->gtk[index].set) {
return false;
}
return true;
}
uint8_t *sec_prot_keys_gtk_get(sec_prot_gtk_keys_t *gtks, uint8_t index)
{
if (index >= GTK_NUM || !gtks->gtk[index].set) {
return NULL;
}
return gtks->gtk[index].key;
}
uint32_t sec_prot_keys_gtk_lifetime_get(sec_prot_gtk_keys_t *gtks, uint8_t index)
{
if (index >= GTK_NUM || !gtks->gtk[index].set) {
return 0;
}
return gtks->gtk[index].lifetime;
}
uint32_t sec_prot_keys_gtk_lifetime_decrement(sec_prot_gtk_keys_t *gtks, uint8_t index, uint16_t seconds)
{
if (gtks->gtk[index].lifetime > seconds) {
gtks->gtk[index].lifetime -= seconds;
} else {
gtks->gtk[index].lifetime = 0;
}
return gtks->gtk[index].lifetime;
}
bool sec_prot_keys_gtks_are_updated(sec_prot_gtk_keys_t *gtks)
{
return gtks->updated;
}
void sec_prot_keys_gtks_updated_set(sec_prot_gtk_keys_t *gtks)
{
gtks->updated = true;
}
void sec_prot_keys_gtks_updated_reset(sec_prot_gtk_keys_t *gtks)
{
gtks->updated = false;
}
void sec_prot_keys_gtk_status_fresh_set(sec_prot_gtk_keys_t *gtks, uint8_t index)
{
if (index >= GTK_NUM || !gtks->gtk[index].set) {
return;
}
// Active key remains as active, old keys are never reused
if (gtks->gtk[index].status < GTK_STATUS_FRESH) {
gtks->gtk[index].status = GTK_STATUS_FRESH;
}
}
void sec_prot_keys_gtk_status_all_fresh_set(sec_prot_gtk_keys_t *gtks)
{
for (uint8_t i = 0; i < GTK_NUM; i++) {
sec_prot_keys_gtk_status_fresh_set(gtks, i);
}
}
int8_t sec_prot_keys_gtk_status_active_set(sec_prot_gtk_keys_t *gtks, uint8_t index)
{
if (index >= GTK_NUM || !gtks->gtk[index].set) {
return -1;
}
// If key is valid to be taken into use sets it active
if (gtks->gtk[index].status == GTK_STATUS_FRESH) {
// Sets previously active key old
for (uint8_t i = 0; i < GTK_NUM; i++) {
// Sets previously active key old
if (gtks->gtk[i].status == GTK_STATUS_ACTIVE) {
gtks->gtk[i].status = GTK_STATUS_OLD;
}
}
gtks->gtk[index].status = GTK_STATUS_ACTIVE;
return 0;
}
return -1;
}
int8_t sec_prot_keys_gtk_status_active_get(sec_prot_gtk_keys_t *gtks)
{
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (gtks->gtk[i].status == GTK_STATUS_ACTIVE) {
return i;
}
}
return -1;
}
bool sec_prot_keys_gtk_status_is_live(sec_prot_gtk_keys_t *gtks, uint8_t index)
{
if (index >= GTK_NUM || !gtks->gtk[index].set) {
return false;
}
if (gtks->gtk[index].status == GTK_STATUS_FRESH || gtks->gtk[index].status == GTK_STATUS_ACTIVE) {
return true;
}
return false;
}
void sec_prot_keys_gtk_status_new_set(sec_prot_gtk_keys_t *gtks, uint8_t index)
{
if (index >= GTK_NUM || !gtks->gtk[index].set) {
return;
}
gtks->gtk[index].status = GTK_STATUS_NEW;
}
void sec_prot_keys_gtks_hash_generate(sec_prot_gtk_keys_t *gtks, uint8_t *gtkhash)
{
memset(gtkhash, 0, GTK_ALL_HASHES_LEN);
uint8_t *gtk_hash_ptr = gtkhash;
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (sec_prot_keys_gtk_is_set(gtks, i)) {
uint8_t *gtk = sec_prot_keys_gtk_get(gtks, i);
sec_prot_lib_gtkhash_generate(gtk, gtk_hash_ptr);
}
gtk_hash_ptr += GTK_HASH_LEN;
}
}
int8_t sec_prot_keys_gtk_hash_generate(uint8_t *gtk, uint8_t *gtk_hash)
{
return sec_prot_lib_gtkhash_generate(gtk, gtk_hash);
}
int8_t sec_prot_keys_gtk_valid_check(uint8_t *gtk)
{
uint8_t gtk_hash[8];
sec_prot_lib_gtkhash_generate(gtk, gtk_hash);
// Checks if GTK hash for the GTK would be all zero
if (memcmp(gtk_hash, empty_hash, GTK_HASH_LEN) == 0) {
return -1;
}
return 0;
}
gtk_mismatch_e sec_prot_keys_gtks_hash_update(sec_prot_gtk_keys_t *gtks, uint8_t *gtkhash)
{
uint8_t *gtk_hash_ptr = gtkhash;
gtk_mismatch_e mismatch = GTK_NO_MISMATCH;
for (uint8_t i = 0; i < GTK_NUM; i++, gtk_hash_ptr += 8) {
// If hash is not set, stop using the key
if (sec_prot_keys_gtk_hash_empty(gtk_hash_ptr)) {
if (sec_prot_keys_gtk_is_set(gtks, i)) {
uint32_t lifetime = sec_prot_keys_gtk_lifetime_get(gtks, i);
if (lifetime > GTK_EXPIRE_MISMATCH_TIME) {
tr_info("GTK mismatch %i expired time, lifetime: %"PRIu32"", i, lifetime);
if (mismatch < GTK_LIFETIME_MISMATCH) {
mismatch = GTK_LIFETIME_MISMATCH;
}
}
sec_prot_keys_gtk_clear(gtks, i);
}
} else {
// Check is hash matches to existing key
uint8_t gtk_hash[8];
uint8_t *gtk = sec_prot_keys_gtk_get(gtks, i);
if (!gtk) {
// Hash set but GTK is not known, set mismatch
tr_info("GTK mismatch: %i", i);
if (mismatch < GTK_HASH_MISMATCH) {
mismatch = GTK_HASH_MISMATCH;
}
continue;
}
sec_prot_lib_gtkhash_generate(gtk, gtk_hash);
if (memcmp(gtk_hash, gtk_hash_ptr, 8) == 0) {
// Key is fresh (or active, if old do not change state)
sec_prot_keys_gtk_status_fresh_set(gtks, i);
} else {
// Hash does not match, set mismatch and delete key
tr_info("GTK mismatch: %i", i);
if (mismatch < GTK_HASH_MISMATCH) {
mismatch = GTK_HASH_MISMATCH;
}
sec_prot_keys_gtk_clear(gtks, i);
}
}
}
return mismatch;
}
bool sec_prot_keys_gtk_hash_empty(uint8_t *gtkhash)
{
if (memcmp(gtkhash, empty_hash, GTK_HASH_LEN) == 0) {
return true;
} else {
return false;
}
}
int8_t sec_prot_keys_gtk_install_order_last_get(sec_prot_gtk_keys_t *gtks)
{
int8_t install_order = -1;
// Gets the last key index
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (sec_prot_keys_gtk_is_set(gtks, i)) {
if (gtks->gtk[i].install_order > install_order) {
install_order = gtks->gtk[i].install_order;
}
}
}
return install_order + 1;
}
int8_t sec_prot_keys_gtk_install_order_last_index_get(sec_prot_gtk_keys_t *gtks)
{
int8_t install_order = -1;
int8_t index = -1;
// Gets the last key index
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (sec_prot_keys_gtk_is_set(gtks, i)) {
if (gtks->gtk[i].install_order > install_order) {
install_order = gtks->gtk[i].install_order;
index = i;
}
}
}
return index;
}
uint32_t sec_prot_keys_gtk_install_order_last_lifetime_get(sec_prot_gtk_keys_t *gtks)
{
uint32_t lifetime = 0;
int8_t install_order = -1;
// Gets the last key index
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (sec_prot_keys_gtk_is_set(gtks, i)) {
if (gtks->gtk[i].install_order > install_order) {
install_order = gtks->gtk[i].install_order;
lifetime = gtks->gtk[i].lifetime;
}
}
}
return lifetime;
}
int8_t sec_prot_keys_gtk_install_order_first_index_get(sec_prot_gtk_keys_t *gtks)
{
// Gets the first key index
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (sec_prot_keys_gtk_is_set(gtks, i)) {
if (gtks->gtk[i].install_order == GTK_INSTALL_ORDER_FIRST) {
return i;
}
}
}
return -1;
}
int8_t sec_prot_keys_gtk_install_order_second_index_get(sec_prot_gtk_keys_t *gtks)
{
// Gets the first key index
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (sec_prot_keys_gtk_is_set(gtks, i)) {
if (gtks->gtk[i].install_order == GTK_INSTALL_ORDER_SECOND) {
return i;
}
}
}
return -1;
}
void sec_prot_keys_gtk_install_order_update(sec_prot_gtk_keys_t *gtks)
{
int8_t ordered_indexes[4] = {-1, -1, -1, -1};
// Creates table of ordered indexes
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (sec_prot_keys_gtk_is_set(gtks, i)) {
ordered_indexes[gtks->gtk[i].install_order] = i;
}
}
// Updates indexes of the GTKs
uint8_t new_install_order = 0;
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (ordered_indexes[i] >= 0) {
gtks->gtk[ordered_indexes[i]].install_order = new_install_order++;
}
}
}
int8_t sec_prot_keys_gtk_install_index_get(sec_prot_gtk_keys_t *gtks)
{
// Gets the index of the last key to be installed
int8_t install_index = sec_prot_keys_gtk_install_order_last_index_get(gtks);
if (install_index < 0) {
install_index = 0;
}
// Checks if there is free index, and available uses that for new GTK
for (uint8_t ctr = 0, i = install_index; ctr < GTK_NUM; ctr++) {
if (!sec_prot_keys_gtk_is_set(gtks, i)) {
install_index = i;
break;
}
i++;
if (i >= GTK_NUM) {
i = 0;
}
}
return install_index;
}
uint8_t sec_prot_keys_gtk_count(sec_prot_gtk_keys_t *gtks)
{
uint8_t count = 0;
for (uint8_t i = 0; i < GTK_NUM; i++) {
if (sec_prot_keys_gtk_is_set(gtks, i)) {
count++;
}
}
return count;
}
void sec_prot_keys_ptk_installed_gtk_hash_clear_all(sec_prot_keys_t *sec_keys)
{
for (uint8_t index = 0; index < GTK_NUM; index++) {
memset(sec_keys->ins_gtk_hash[sec_keys->gtk_set_index].hash, 0, INS_GTK_HASH_LEN);
}
sec_keys->ins_gtk_hash_set = 0;
}
void sec_prot_keys_ptk_installed_gtk_hash_set(sec_prot_keys_t *sec_keys)
{
if (sec_keys->gtk_set_index >= 0) {
uint8_t *gtk = sec_prot_keys_gtk_get(sec_keys->gtks, sec_keys->gtk_set_index);
if (!gtk) {
return;
}
uint8_t gtk_hash[GTK_HASH_LEN];
if (sec_prot_keys_gtk_hash_generate(gtk, gtk_hash) < 0) {
return;
}
/* Store two byte hash. This is long enough for the GTK installed check, since
* possible conflict between hashes causes only that 4WH is initiated/is not
* initiated instead of GKH.
*/
memcpy(sec_keys->ins_gtk_hash[sec_keys->gtk_set_index].hash, gtk_hash, INS_GTK_HASH_LEN);
sec_keys->ins_gtk_hash_set |= (1 << sec_keys->gtk_set_index);
}
}
bool sec_prot_keys_ptk_installed_gtk_hash_mismatch_check(sec_prot_keys_t *sec_keys, uint8_t gtk_index)
{
if ((sec_keys->ins_gtk_hash_set & (1 << sec_keys->gtk_set_index)) == 0) {
return false;
}
uint8_t *gtk = sec_prot_keys_gtk_get(sec_keys->gtks, gtk_index);
if (!gtk) {
return false;
}
// Calculated GTK hash for the current GTK on the defined index
uint8_t gtk_hash[GTK_HASH_LEN];
if (sec_prot_keys_gtk_hash_generate(gtk, gtk_hash) < 0) {
return false;
}
// If PTK has been used to install different GTK to index than the current one, trigger mismatch
if (memcmp(sec_keys->ins_gtk_hash[sec_keys->gtk_set_index].hash, gtk_hash, INS_GTK_HASH_LEN) != 0) {
return true;
}
return false;
}
#endif /* HAVE_WS */