From 5ceda4569e28bf569ca294fbf50738537717e765 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Sat, 31 Jan 2026 16:01:46 +0100 Subject: [PATCH] confd: restore IETF standard keystore leaf types and NACM rules Infix key format identities now derive from the IETF bases, so the standard identityref accepts them without any need for deviations. This also preserves the nacm default rules, which were inadvertently dropped before. Also rename format identities for generality: - wifi-preshared-key-format -> renamed: 'passphrase-key-format' - wireguard-symmetric-key-format -> use IETF 'octet-string-key-format' Since cleartext-symmetric-key is now type binary (base64-encoded), the WiFi backends (station and AP) decode values before passing them to wpa_supplicant and hostapd. Signed-off-by: Joachim Wiberg --- .../1.7/20-keystore-cleartext-key-rename.sh | 25 ++++++-- src/confd/src/hardware.c | 18 ++++-- src/confd/src/if-wifi.c | 23 ++++--- src/confd/yang/confd/infix-crypto-types.yang | 37 +++++++----- src/confd/yang/confd/infix-if-wireguard.yang | 7 ++- src/confd/yang/confd/infix-keystore.yang | 60 +------------------ .../interfaces/wireguard_multipoint/test.py | 16 ++--- 7 files changed, 82 insertions(+), 104 deletions(-) diff --git a/src/confd/share/migrate/1.7/20-keystore-cleartext-key-rename.sh b/src/confd/share/migrate/1.7/20-keystore-cleartext-key-rename.sh index b55b42d7..067db0c6 100755 --- a/src/confd/share/migrate/1.7/20-keystore-cleartext-key-rename.sh +++ b/src/confd/share/migrate/1.7/20-keystore-cleartext-key-rename.sh @@ -1,5 +1,13 @@ #!/bin/sh -# Rename cleartext-key to symmetric-key +# Migrate symmetric keys from v1.6 (v25.11) to IETF standard format. +# +# v1.6 had WiFi keys stored as: +# - infix-keystore:cleartext-key (type string, plaintext) +# - infix-keystore:key-format wifi-preshared-key-format +# +# v1.7 uses the IETF standard leaf and updated format names: +# - cleartext-symmetric-key (type binary, base64-encoded) +# - key-format passphrase-key-format file=$1 temp=${file}.tmp @@ -7,12 +15,17 @@ temp=${file}.tmp jq ' if .["ietf-keystore:keystore"]?."symmetric-keys"?."symmetric-key" then .["ietf-keystore:keystore"]."symmetric-keys"."symmetric-key" |= map( + if ."infix-keystore:key-format" then + del(."infix-keystore:key-format") | + . + { "key-format": "infix-crypto-types:passphrase-key-format" } + else + . + end | + if ."infix-keystore:cleartext-key" then - # Rename cleartext-key to symmetric-key - ."infix-keystore:cleartext-key" as $key_value | - del(."infix-keystore:cleartext-key") | . + { - "infix-keystore:symmetric-key": $key_value - } + ."infix-keystore:cleartext-key" as $val | + del(."infix-keystore:cleartext-key") | + . + { "cleartext-symmetric-key": ($val | @base64) } else . end diff --git a/src/confd/src/hardware.c b/src/confd/src/hardware.c index 0ea34f7b..6d441fe7 100644 --- a/src/confd/src/hardware.c +++ b/src/confd/src/hardware.c @@ -12,6 +12,7 @@ #include "core.h" #include "interfaces.h" #include "dagger.h" +#include "base64.h" #define XPATH_BASE_ "/ietf-hardware:hardware" #define HOSTAPD_CONF "/etc/hostapd-%s.conf" @@ -235,8 +236,9 @@ static int wifi_find_radio_aps(struct lyd_node *cifs, const char *radio_name, /* Helper: Write SSID and security configuration (shared between primary and BSS) */ static void wifi_gen_ssid_config(FILE *hostapd, struct lyd_node *cif, struct lyd_node *config, bool is_bss) { - const char *ssid, *hidden, *security_mode, *secret_name, *secret; + const char *ssid, *hidden, *security_mode, *secret_name; struct lyd_node *wifi, *ap, *security, *secret_node; + unsigned char *secret = NULL; const char *ifname; char bssid[18]; @@ -282,15 +284,19 @@ static void wifi_gen_ssid_config(FILE *hostapd, struct lyd_node *cif, struct lyd security_mode = "open"; /* Get secret from keystore if needed */ - secret = NULL; if (strcmp(security_mode, "open") != 0) { secret_name = lydx_get_cattr(security, "secret"); if (secret_name) { + const char *b64; + secret_node = lydx_get_xpathf(config, - "/keystore/symmetric-keys/symmetric-key[name='%s']/symmetric-key", + "/keystore/symmetric-keys/symmetric-key[name='%s']/cleartext-symmetric-key", secret_name); - if (secret_node) - secret = lyd_get_value(secret_node); + if (secret_node) { + b64 = lyd_get_value(secret_node); + if (b64) + secret = base64_decode((const unsigned char *)b64, strlen(b64), NULL); + } } } @@ -329,6 +335,8 @@ static void wifi_gen_ssid_config(FILE *hostapd, struct lyd_node *cif, struct lyd /* ieee80211w=1: MFP capable but optional, for WPA2 client compatibility */ fprintf(hostapd, "ieee80211w=1\n"); } + + free(secret); } /* Helper: Write radio-specific configuration */ diff --git a/src/confd/src/if-wifi.c b/src/confd/src/if-wifi.c index f7d5bef5..bb35a96a 100644 --- a/src/confd/src/if-wifi.c +++ b/src/confd/src/if-wifi.c @@ -11,6 +11,7 @@ #include #include "interfaces.h" +#include "base64.h" #define WPA_SUPPLICANT_CONF "/etc/wpa_supplicant-%s.conf" @@ -59,8 +60,9 @@ int wifi_mode_changed(struct lyd_node *wifi) */ int wifi_gen_station(struct lyd_node *cif) { - const char *ifname, *ssid, *secret_name, *secret, *security_mode, *radio; + const char *ifname, *ssid, *secret_name, *security_mode, *radio; struct lyd_node *security, *secret_node, *radio_node, *station, *wifi; + unsigned char *secret = NULL; FILE *wpa_supplicant = NULL; char *security_str = NULL; const char *country; @@ -91,12 +93,14 @@ int wifi_gen_station(struct lyd_node *cif) country = lydx_get_cattr(radio_node, "country-code"); if (secret_name && strcmp(security_mode, "disabled") != 0) { + const char *b64; + secret_node = lydx_get_xpathf(cif, "../../keystore/symmetric-keys/symmetric-key[name='%s']", secret_name); - secret = lydx_get_cattr(secret_node, "symmetric-key"); - } else { - secret = NULL; + b64 = lydx_get_cattr(secret_node, "cleartext-symmetric-key"); + if (b64) + secret = base64_decode((const unsigned char *)b64, strlen(b64), NULL); } oldmask = umask(0077); @@ -121,11 +125,13 @@ int wifi_gen_station(struct lyd_node *cif) /* If SSID is present, create network block. Otherwise, scan-only mode */ if (ssid) { /* Station mode with network configured */ - if (!strcmp(security_mode, "disabled")) { + if (!strcmp(security_mode, "disabled")) asprintf(&security_str, "key_mgmt=NONE"); - } else if (secret) { - asprintf(&security_str, "key_mgmt=SAE WPA-PSK\npsk=\"%s\"", secret); - } + else if (secret) + asprintf(&security_str, + "key_mgmt=SAE WPA-PSK\n" + " psk=\"%s\"", secret); + fprintf(wpa_supplicant, "network={\n" " bgscan=\"simple: 30:-45:300\"\n" @@ -152,6 +158,7 @@ int wifi_gen_station(struct lyd_node *cif) } out: + free(secret); if (wpa_supplicant) fclose(wpa_supplicant); umask(oldmask); diff --git a/src/confd/yang/confd/infix-crypto-types.yang b/src/confd/yang/confd/infix-crypto-types.yang index f6b06e92..ab7ab37a 100644 --- a/src/confd/yang/confd/infix-crypto-types.yang +++ b/src/confd/yang/confd/infix-crypto-types.yang @@ -15,32 +15,46 @@ module infix-crypto-types { revision 2025-02-04 { description "Initial"; } + identity private-key-format { + base ct:private-key-format; description - "Base key-format identity for private keys."; + "Base for Infix private key format extensions."; } + identity public-key-format { + base ct:public-key-format; description - "Base key-format identity for public keys."; + "Base for Infix public key format extensions."; } + identity rsa-private-key-format { base private-key-format; base ct:rsa-private-key-format; + description + "RSA private key in PKCS#1 format (RFC 8017). + Used for SSH host keys."; } + identity ssh-public-key-format { base public-key-format; base ct:ssh-public-key-format; + description + "SSH public key format (RFC 4253, Section 6.6). + Used for SSH host keys."; } identity symmetric-key-format { + base ct:symmetric-key-format; description - "Base for symmetric key format"; + "Base for Infix symmetric key format extensions."; } - identity wifi-preshared-key-format { + + identity passphrase-key-format { base ct:symmetric-key-format; base symmetric-key-format; description - "WiFi secret key"; + "Human-readable passphrase, e.g., WiFi WPA2/WPA3 passwords."; } identity x25519-public-key-format { @@ -48,7 +62,7 @@ module infix-crypto-types { base ct:public-key-format; description "X25519 (Curve25519) public key format for Diffie-Hellman key exchange. - This is the format used by WireGuard."; + This format is used by network protocols such as WireGuard VPN."; } identity x25519-private-key-format { @@ -56,15 +70,6 @@ module infix-crypto-types { base ct:private-key-format; description "X25519 (Curve25519) private key format for Diffie-Hellman key exchange. - This is the format used by WireGuard."; - } - - identity wireguard-symmetric-key-format { - base ct:symmetric-key-format; - base symmetric-key-format; - description - "WireGuard pre-shared key format. - 32-byte base64-encoded key used as an optional additional layer - of symmetric encryption for post-quantum resistance."; + This format is used by network protocols such as WireGuard VPN."; } } diff --git a/src/confd/yang/confd/infix-if-wireguard.yang b/src/confd/yang/confd/infix-if-wireguard.yang index ea5e2c2d..7441ce25 100644 --- a/src/confd/yang/confd/infix-if-wireguard.yang +++ b/src/confd/yang/confd/infix-if-wireguard.yang @@ -10,6 +10,9 @@ submodule infix-if-wireguard { import ietf-inet-types { prefix inet; } + import ietf-crypto-types { + prefix ct; + } import infix-crypto-types { prefix ixct; } @@ -75,8 +78,8 @@ submodule infix-if-wireguard { This provides post-quantum resistance as an attacker would need to break both the Curve25519 key exchange and this symmetric key."; - must "derived-from-or-self(deref(.)/../infix-ks:key-format, 'ixct:wireguard-symmetric-key-format')" { - error-message "Preshared key must be in wireguard-symmetric-key-format"; + must "derived-from-or-self(deref(.)/../ks:key-format, 'ct:octet-string-key-format')" { + error-message "Preshared key must be in octet-string-key-format"; } } diff --git a/src/confd/yang/confd/infix-keystore.yang b/src/confd/yang/confd/infix-keystore.yang index 9f1f61bd..c27fbcf8 100644 --- a/src/confd/yang/confd/infix-keystore.yang +++ b/src/confd/yang/confd/infix-keystore.yang @@ -5,15 +5,12 @@ module infix-keystore { import ietf-keystore { prefix ks; } - import ietf-crypto-types { - prefix ct; - } import infix-crypto-types { prefix infix-ct; } revision 2025-12-17 { - description "Add WireGuard support"; + description "Add WireGuard support, see infix-crypto-types.yang"; } revision 2025-12-10 { description "Adapt to changes in final version of ietf-keystore"; @@ -24,59 +21,4 @@ module infix-keystore { revision 2025-02-04 { description "Initial"; } - deviation "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key/ks:public-key-format" { - deviate replace { - type identityref { - base infix-ct:public-key-format; - } - } - } - deviation "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key/ks:private-key-format" { - deviate replace { - type identityref { - base infix-ct:private-key-format; - } - } - } - deviation "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-format" { - deviate not-supported; - } - augment "/ks:keystore/ks:symmetric-keys/ks:symmetric-key" { - leaf key-format { - type identityref { - base infix-ct:symmetric-key-format; - } - description - "Identifies the symmetric key's format - - Valid symmetric key formats are: - wifi-preshared-key-format - WiFi preshared key - wireguard-symmetric-key-format - WireGuard preshared key"; - } - } - deviation "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-type/ks:cleartext-symmetric-key" { - deviate not-supported; - } - augment "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/ks:key-type" { - case cleartext-symmetric-key { - leaf symmetric-key { - type string; - must "../infix-ks:key-format != 'infix-ct:wifi-preshared-key-format' or " + - "(string-length(.) >= 8 and string-length(.) <= 63)" { - error-message "WiFi pre-shared key must be 8-63 characters long"; - } - must "../infix-ks:key-format != 'infix-ct:wireguard-symmetric-key-format' or " + - "string-length(.) = 44" { - error-message "WireGuard pre-shared key must be 44 characters (32-byte base64-encoded)"; - } - description - "Cleartext symmetric key value. - - Format depends on key-format: - - WiFi pre-shared key: 8-63 printable ASCII characters - - WireGuard pre-shared key: 32-byte base64-encoded key (44 chars with padding)"; - - } - } - } } diff --git a/test/case/interfaces/wireguard_multipoint/test.py b/test/case/interfaces/wireguard_multipoint/test.py index 53119210..c790ae52 100755 --- a/test/case/interfaces/wireguard_multipoint/test.py +++ b/test/case/interfaces/wireguard_multipoint/test.py @@ -75,12 +75,12 @@ def configure_server(dut): "symmetric-keys": { "symmetric-key": [{ "name": "psk-client1", - "infix-keystore:symmetric-key": psk_client1, - "infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format" + "cleartext-symmetric-key": psk_client1, + "key-format": "ietf-crypto-types:octet-string-key-format" }, { "name": "psk-client2", - "infix-keystore:symmetric-key": psk_client2, - "infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format" + "cleartext-symmetric-key": psk_client2, + "key-format": "ietf-crypto-types:octet-string-key-format" }] } } @@ -226,8 +226,8 @@ def configure_client1(dut): "symmetric-keys": { "symmetric-key": [{ "name": "psk-server", - "infix-keystore:symmetric-key": psk_client1, - "infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format" + "cleartext-symmetric-key": psk_client1, + "key-format": "ietf-crypto-types:octet-string-key-format" }] } } @@ -360,8 +360,8 @@ def configure_client2(dut): "symmetric-keys": { "symmetric-key": [{ "name": "psk-server", - "infix-keystore:symmetric-key": psk_client2, - "infix-keystore:key-format": "infix-crypto-types:wireguard-symmetric-key-format" + "cleartext-symmetric-key": psk_client2, + "key-format": "ietf-crypto-types:octet-string-key-format" }] } }