vlan_file: true,
wpa_psk_file: true,
sae_password_file: true,
+ rxkh_file: true,
accept_mac_file: true,
deny_mac_file: true,
eap_user_file: true,
hostapd.printf(`Reload WPA PSK file for bss ${config.ifname}: ${ret}`);
}
+function normalize_rxkhs(txt)
+{
+ const pat = {
+ sep: "\x20",
+ mac: "([[:xdigit:]]{2}:?){5}[[:xdigit:]]{2}",
+ r0kh_id: "[\x21-\x7e]{1,48}",
+ r1kh_id: "([[:xdigit:]]{2}:?){5}[[:xdigit:]]{2}",
+ key: "[[:xdigit:]]{32,}",
+ r0kh: function() {
+ return "r0kh=" + this.mac + this.sep + this.r0kh_id;
+ },
+ r1kh: function() {
+ return "r1kh=" + this.mac + this.sep + this.r1kh_id;
+ },
+ rxkh: function() {
+ return "(" + this.r0kh() + "|" + this.r1kh() + ")" + this.sep + this.key;
+ },
+ };
+
+ let rxkhs = filter(
+ split(txt, "\n"), (line) => match(line, regexp("^" + pat.rxkh() + "$"))
+ ) ?? [];
+
+ rxkhs = map(rxkhs, function(k) {
+ k = split(k, " ", 3);
+ k[0] = lc(k[0]);
+ if(match(k[0], /^r1kh/)) {
+ k[1] = lc(k[1]);
+ }
+ if(!k[2] = hostapd.rkh_derive_key(k[2])) {
+ return;
+ }
+ return join(" ", k);
+ });
+
+ return join("\n", sort(filter(rxkhs, length)));
+}
+
+function bss_reload_rxkhs(bss, config, old_config)
+{
+ let bss_rxkhs = join("\n", sort(split(bss.ctrl("GET_RXKHS"), "\n")));
+ let bss_rxkhs_hash = hostapd.sha1(bss_rxkhs);
+
+ if (is_equal(config.hash.rxkh_file, bss_rxkhs_hash)) {
+ if (is_equal(old_config.hash.rxkh_file, config.hash.rxkh_file))
+ return;
+ }
+
+ old_config.hash.rxkh_file = config.hash.rxkh_file;
+ if (!is_equal(old_config, config))
+ return;
+
+ let ret = bss.ctrl("RELOAD_RXKHS");
+ ret ??= "failed";
+
+ hostapd.printf(`Reload RxKH file for bss ${config.ifname}: ${ret}`);
+}
+
function remove_file_fields(config)
{
return filter(config, (line) => !hostapd.data.file_fields[split(line, "=")[0]]);
}
bss_reload_psk(bss, config.bss[i], bss_list_cfg[i]);
+ bss_reload_rxkhs(bss, config.bss[i], bss_list_cfg[i]);
if (is_equal(config.bss[i], bss_list_cfg[i]))
continue;
continue;
}
- if (hostapd.data.file_fields[val[0]])
- bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
+ if (hostapd.data.file_fields[val[0]]) {
+ if (val[0] == "rxkh_file") {
+ bss.hash[val[0]] = hostapd.sha1(normalize_rxkhs(readfile(val[1])));
+ } else {
+ bss.hash[val[0]] = hostapd.sha1(readfile(val[1]));
+ }
+ }
push(bss.data, line);
}
#include "utils/eloop.h"
#include "crypto/crypto.h"
#include "crypto/sha1.h"
+#include "crypto/sha256.h"
#include "common/ieee802_11_common.h"
#include <linux/netlink.h>
#include <linux/genetlink.h>
return ucv_string_new_length(hash_hex, 2 * ARRAY_SIZE(hash));
}
+uc_value_t *uc_wpa_rkh_derive_key(uc_vm_t *vm, size_t nargs)
+{
+ u8 oldkey[16];
+ char *oldkey_hex;
+ u8 key[SHA256_MAC_LEN];
+ size_t key_len = sizeof(key);
+ char key_hex[2 * ARRAY_SIZE(key) + 1];
+ uc_value_t *val = uc_fn_arg(0);
+ int i;
+
+ if (ucv_type(val) != UC_STRING)
+ return NULL;
+
+ oldkey_hex = ucv_string_get(val);
+
+ if (!hexstr2bin(oldkey_hex, key, key_len))
+ return ucv_string_new_length(oldkey_hex, 2 * ARRAY_SIZE(key));
+
+ if (hexstr2bin(oldkey_hex, oldkey, sizeof(oldkey))) {
+ wpa_printf(MSG_ERROR, "Invalid RxKH key: '%s'", oldkey_hex);
+ return NULL;
+ }
+
+ if (hmac_sha256_kdf(oldkey, sizeof(oldkey), "FT OLDKEY", NULL, 0, key, key_len) < 0) {
+ wpa_printf(MSG_ERROR, "Invalid RxKH key: '%s'", oldkey_hex);
+ return NULL;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(key); i++)
+ sprintf(key_hex + 2 * i, "%02x", key[i]);
+
+ return ucv_string_new_length(key_hex, 2 * ARRAY_SIZE(key));
+}
+
uc_vm_t *wpa_ucode_create_vm(void)
{
static uc_parse_config_t config = {