[PATCH] ap: add vendor quirk for reused nonce in handshake
berend <[email protected]> Wed, 24 Dec 2025 10:28:40 +0000
| Newsgroups | dev.linux.lists.iwd |
|---|---|
| Message-ID | <[email protected]> |
commit 486cd7b1cd20bfa81437d89cdd7c8f8df7e23bfa Author: Berend De Schouwer <[email protected]> Date: Wed Dec 24 11:28:17 2025 +0200 ap: add vendor quirk for reused nonce in handshake Add a vendor quirk when IWD is an AP, to allow some Garmin IoT devices to connect. handshake_reuse_nonce: When a supplicant does a handshake, the nonce in message 4 should be set to zero. On some devices it's left identical to message 2. diff --git a/src/ap.c b/src/ap.c index af6a1709..1f805a81 100644 --- a/src/ap.c +++ b/src/ap.c @@ -59,6 +59,7 @@ #include "src/diagnostic.h" #include "src/band.h" #include "src/common.h" +#include "src/vendor_quirks.h" struct ap_state { struct netdev *netdev; @@ -143,6 +144,7 @@ struct sta_state { struct l_dhcp_lease *ip_alloc_lease; bool ip_alloc_sent; uint64_t rekey_time; + struct vendor_quirk vendor_quirks; bool ht_support : 1; bool ht_greenfield : 1; @@ -1384,6 +1386,7 @@ static void ap_start_handshake(struct sta_state *sta, bool use_eapol_start, handshake_state_set_ssid(sta->hs, (void *) ap->ssid, strlen(ap- >ssid)); handshake_state_set_authenticator_address(sta->hs, own_addr); handshake_state_set_supplicant_address(sta->hs, sta->addr); + handshake_state_set_vendor_quirks(sta->hs, sta->vendor_quirks); ap_set_rsn_info(ap, &rsn); /* @@ -2974,6 +2977,7 @@ static void ap_handle_new_station(struct ap_state *ap, struct l_genl_msg *msg) const void *data; const uint8_t *mac = NULL; uint8_t *assoc_rsne = NULL; + const char *vendor_quirks; if (!l_genl_attr_init(&attr, msg)) return; @@ -3014,6 +3018,13 @@ static void ap_handle_new_station(struct ap_state *ap, struct l_genl_msg *msg) sta->assoc_rsne = assoc_rsne; sta->aid = ++ap->last_aid; + vendor_quirks_append_for_mac(sta->addr, &sta->vendor_quirks); + + vendor_quirks = vendor_quirks_to_string(sta->vendor_quirks); + if (vendor_quirks) + l_debug("vendor quirks for "MAC": %s", + MAC_STR(sta->addr), vendor_quirks); + sta->associated = true; if (!ap->sta_states) diff --git a/src/eapol.c b/src/eapol.c index ab77746f..b603ae39 100644 --- a/src/eapol.c +++ b/src/eapol.c @@ -605,7 +605,8 @@ bool eapol_verify_ptk_3_of_4(const struct eapol_key *ek, bool is_wpa, return true; } -bool eapol_verify_ptk_4_of_4(const struct eapol_key *ek, bool is_wpa) +bool eapol_verify_ptk_4_of_4(const struct eapol_key *ek, bool is_wpa, + const uint8_t *quirk_nonce) { uint16_t key_len; @@ -636,7 +637,9 @@ bool eapol_verify_ptk_4_of_4(const struct eapol_key *ek, bool is_wpa) key_len = L_BE16_TO_CPU(ek->key_length); L_WARN_ON(key_len != 0); - VERIFY_IS_ZERO(ek->key_nonce); + if ((!quirk_nonce) || memcmp(ek->key_nonce, quirk_nonce, + sizeof(ek->key_nonce))) + VERIFY_IS_ZERO(ek->key_nonce); VERIFY_IS_ZERO(ek->eapol_key_iv); VERIFY_IS_ZERO(ek->key_rsc); VERIFY_IS_ZERO(ek->reserved); @@ -2122,10 +2125,15 @@ static void eapol_handle_ptk_4_of_4(struct eapol_sm *sm, const struct eapol_key *ek) { const uint8_t *kck; + const uint8_t *quirk_nonce = NULL; l_debug("ifindex=%u", sm->handshake->ifindex); - if (!eapol_verify_ptk_4_of_4(ek, false)) + if (sm->handshake->vendor_quirks.handshake_nonce_reuse && + sm->handshake->have_snonce) + quirk_nonce = sm->handshake->snonce; + + if (!eapol_verify_ptk_4_of_4(ek, false, quirk_nonce)) return; if (L_BE64_TO_CPU(ek->key_replay_counter) != sm- >replay_counter) diff --git a/src/eapol.h b/src/eapol.h index 43dc224d..36946017 100644 --- a/src/eapol.h +++ b/src/eapol.h @@ -70,7 +70,8 @@ bool eapol_verify_ptk_1_of_4(const struct eapol_key *ek, size_t mic_len, bool eapol_verify_ptk_2_of_4(const struct eapol_key *ek, bool ptk_complete); bool eapol_verify_ptk_3_of_4(const struct eapol_key *ek, bool is_wpa, size_t mic_len); -bool eapol_verify_ptk_4_of_4(const struct eapol_key *ek, bool is_wpa); +bool eapol_verify_ptk_4_of_4(const struct eapol_key *ek, bool is_wpa, + const uint8_t *quirk_nonce); bool eapol_verify_gtk_1_of_2(const struct eapol_key *ek, bool is_wpa, size_t mic_len); bool eapol_verify_gtk_2_of_2(const struct eapol_key *ek, bool is_wpa); diff --git a/src/vendor_quirks.c b/src/vendor_quirks.c index 4fba0c33..001fcb13 100644 --- a/src/vendor_quirks.c +++ b/src/vendor_quirks.c @@ -47,6 +47,17 @@ static const struct { }, }; +static const struct { + uint8_t mac[3]; + struct vendor_quirk quirks; +} mac_quirk_db[] = { + { + /* Garmin International */ + { 0x90, 0xf1, 0x57 }, + { .handshake_nonce_reuse = true }, + }, +}; + void vendor_quirks_append_for_oui(const uint8_t *oui, struct vendor_quirk *quirks) { @@ -65,6 +76,22 @@ void vendor_quirks_append_for_oui(const uint8_t *oui, } } +void vendor_quirks_append_for_mac(const uint8_t *mac, + struct vendor_quirk *quirks) +{ + size_t i; + + for (i = 0; i < L_ARRAY_SIZE(mac_quirk_db); i++) { + const struct vendor_quirk *quirk = &mac_quirk_db[i].quirks; + + if (memcmp(mac_quirk_db[i].mac, mac, 3)) + continue; + + quirks->handshake_nonce_reuse |= + quirk->handshake_nonce_reuse; + } +} + const char *vendor_quirks_to_string(struct vendor_quirk quirks) { static char out[1024]; @@ -77,6 +104,9 @@ const char *vendor_quirks_to_string(struct vendor_quirk quirks) if (quirks.replay_counter_mismatch) s += snprintf(pos, sizeof(out) - s, "ReplayCounterMismatch"); + if (quirks.handshake_nonce_reuse) + s += snprintf(pos, sizeof(out) - s, "HandshakeNonceReuse"); + if (!s) return NULL; diff --git a/src/vendor_quirks.h b/src/vendor_quirks.h index b5d726ca..0e199c3a 100644 --- a/src/vendor_quirks.h +++ b/src/vendor_quirks.h @@ -29,11 +29,15 @@ struct vendor_quirk { bool ignore_bss_tm_candidates : 1; bool replay_counter_mismatch : 1; + bool handshake_nonce_reuse : 1; }; void vendor_quirks_append_for_oui(const uint8_t *oui, struct vendor_quirk *quirks); +void vendor_quirks_append_for_mac(const uint8_t *oui, + struct vendor_quirk *quirks); + const char *vendor_quirks_to_string(struct vendor_quirk quirks); #endif /* __IWD_VENDOR_QUIRKS_H */
publickey - Berend De Schouwer - 0x128CD671.asc
(application/pgp-keys, 3.1 KB)
-----BEGIN PGP PUBLIC KEY BLOCK----- Comment: https://gopenpgp.org Version: GopenPGP 2.8.2 xsFNBFWKkUEBEADXuiEYW1YIgNnrazEo/0zelTzxbHCG/wWxn7ASud8xHL1jqZK5 qxQS/6WRbnQv5c5DPvk1Of6E/2XVsFUQJgR1YFxGtNt1QAl5R0vj7rvr8nBbeQhc mHsvwGpT7abkevWFvmcHbNKfXbc/sfDrCMotRFQBohc7oyo+NzOJOQpJqCbzwk+k VmVhVHpw8YzvnD2sVDqbQOxtWmrcofm9Db28NE0NcN2P9ABbNJut1QUZWtZ1lmW6 ryhgKrC4uxSz/YNcc5zs2Z761SGKOSY8PVzvVkLbK9fbdY7jcgGCFd6GRYOB2I3B 7CO9uafuToCDyhlqlugWXpGoA5B7o/a4Z1b7qlNsEmqlGh+eD94H8M/LW8dwYNRI oUNYIayRF1OQprc5Pz3iCxaonoIiGfxGZqNKT9QWoIg8rOC08tcr8uj4m1RjJrsz Sjtkwsom0UwpBygHe/3Bp4xZSxgafMw6dGSKRsDTAHj2FJFWctNluY4jvxRX7CuW 59Q35JIE8m+OgYbgLkGQfEdUyhcRqB0+VR2WRlX5sKUZkWg1AhgcL7FimNll3f06 ye/uzbraUYHX7xc3v5CJ2Z6wx6MphZzhhxuBqLcYJEbN9fNsevTOYkEQIsGC7NWo T2hy3ujeYBPj3co2Nd4iWO9tpysLZ8gK15/dNjMBTsprS+9RPqgz5BtFmwARAQAB zSxCZXJlbmQgRGUgU2Nob3V3ZXIgPGJlcmVuZEBkZXNjaG91d2VyLmNvLnphPsLB eAQTAQIAIgUCVYqRQQIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ227g cZOAoOnXdg/9EFdRr/XtOYbBeltb0sNHaSP7S4vklqcAtOntTYV3yMFNv9XBXXvF SKaGl0m5LheNAkIu/YbAdGXySzNUd7Wsfyb+23iq5UKGt+wD+RocLZY094uAKcQQ TZwJKTw4tKIgN4qBtOaNiJwLursNZYAP7JIJFPQY55pa+yec3jxfTX0a7ruiecos mzavQ182V+Ldev2korGvY1cz43rP8xiL1DjtcBUneigbj5+N0Z8UljnR6QkSRuaj dsGBAXHmkOsBV/o8M8vFWa8N83us1Gky5K54xQOaVyNRVrR4EISv8y0i33rwGYvP BD+RKaLacuKb8+8KNVuQ5M0v+4pGr/FeXpP+nPzVDfbZZhX1iweQ069LKa9ref8q Ew4VVt7AuYFSweHVMICA0ZGyKJ5hZz7LdI0vAFANyUt0KoWeeK69xik8rvF75eo4 fz/WJtf/AdnCuYp60obox/sJA7KrZrWQNbDclJefiSZV7tuHh7jLJqQufkm5D5go wAAo5grM/6WANHVw5iojM/UG6f0+tFZXgFMyk2JZy9c2s8VQgI2UzrbU8JsCtdDj BUSTC64R7klvV5bku31Am3RPC29l1hfY+3ULg4w8mmpJ28pq+/z0oKH8CwyicuFP FBG95ln/7tte4ifWkfzML8Oh2bpuKEb2PWR44sQ1Zq8+IAjS2XwL457OwU0EVYqR QQEQAKXaL/hHnywsFyUQUG5WqboqHtV83S4G9hJLCnL9hgSBvgkpm4c6/92kz/8y goE4cR3GJflfn48tT4Y/X6sNFbIv9VHiYZ8ioXUG4lpa0R27zd1VYOZWQ+oKtcFz oWcVUb8t+OPh3kn0NVAlEFyrkI9agTsJu8RtjSi0EzZ/bCLZM+E1OTBzYS1nzVer u5UbwQVmp3mIsKbRU4QnzYHIVTcEx1yHlB3VddWz3JgzulZQxH7QDEFi5Ah8cqEC 2Rgl6+bduPjbrA+dpWeZLkoPhGwLYqS4brq7FAnkL21RLeFC9/cm7u3Z2z3wZ9Ay ekvnkO6xqtijp0vcDCWy1nWbWKi7f9+SPYnqM3JTPjZyhnOBCjVLXTryXCpaDaBb gXfQJZL9l1m4jW0hB3DvU9xd6f/3umo833KPg2nWzzggKQ84DQGw/HXvARt5mGFS d/DKd2YbLw9B2JJZjoi3seFZSzaVJgAVfOgyI44tg7zIJUTd96W5ixA7HEONExpm CBkgp+2k0dH5IO8sYWwZ4syWBNdgW1xgvC75voxNfSdbfpda0ouaNqaTtb7sryUd lLVwlLYfEx5ODxQUcUr4tJP8lGR8vA0zhFHBaLGBr0ZIMzQNSq/cKsk6sDIr4Zk5 VLOEU7twHTkf47mzVLLBBfzjRIshWFro6pc41Ahi9cp+CxKbABEBAAHCwV8EGAEC AAkFAlWKkUECGwwACgkQ227gcZOAoOmy3RAAtjM0kOB3u96tKJbxjDFyTN6aNOV/ 9Yy6WghF2HNWo7HsnNyvsQNAupxV2wALatPecPX+/N/ueFQKrA3/wqUQ902L4UxV rLkWykdYoOOpgxMLdQpzgcQOTSWPCvP0V/cVsXcWJiVEDh9TjuK0KsYhFmWGa6ne 4uR1V6VByIKZk/oDkaIXBjrZSVTOS6g6VF/WihG+/M/C2eOxxBY37jZnqDJQn3qX hY2cTN0m9kLQPv2jPOKr03Pr/lWHWTisQ3FCVlmYQ0IIG2FZJJcRDcD/8N8jh1ea zEBj0+YR2+LQLsUczl0PJC4LexxDSQX82TeWmgRIWss/Q36UCH9Sr8YzJOAJLVXF 5BLAx2lYUAi8nctr8H2gL/DP8yNOxAgNPIXrB3gBOWa1x2cQ8sNx3/UuXdR25t2b 7xjlxWeZc9z2gO9reEJEkzkmGV8QJMPHP/z6Qu6HQYjPYr/r4OQRHai7TLJFlLPM BKA2FUiY71ZMLYXtWRk5Xo9JuF/Atvw0SrqpZ2DNOyKKfbs14M9/8Ey3QAUNISIt P422RMAD1w8ZXg1ZPjHJ8Lj7Bk7E2F7F3VU0IqBIUlsRyyBo1K7H7QOZpvbExDQJ g1lxbXaKYkDQ0DGaoagOzUaZ9rLBvJE4z7C0SgpcXZZ0rnOydvnF0hBXowv7WbF+ i4ZPyDBc3n68rqs= =9SXl -----END PGP PUBLIC KEY BLOCK-----
signature.asc
(application/pgp-signature, 928 B)
-----BEGIN PGP SIGNATURE----- Version: ProtonMail wsGpBAEBCABdBYJpS8BXCZDbbuBxk4Cg6TUUAAAAAAAcABBzYWx0QG5vdGF0 aW9ucy5vcGVucGdwanMub3JnClxNvSea8Ztxt9NaU71bwhYhBBKM1nGLSON6 q6UFAttu4HGTgKDpAADaQRAAtwGWgQiGlE8PoJKnR/GKoRorEXMXDspZgPQs TFmGqS+ndFaVNxPtOsGglhaOv8bKcpzuxCLvx4GhxpZIcbUvdL/33yomyESJ 8puzExVIyUPStxz5xEUL9OB0DN1ah5Ssxus8O33Xm3Wrz+8S3jblonoLMHAF WVpISxh1fvrVe0wj9vBGMYMmAHrxzN5pz9vhMf4xlfYeR0F+yR7+TYMqWJCF JqbDWaUMjkh9xSNskVK/9Fo0kR3ylLAWdpdypXBUo8LkKE7z04Fyu+DHugkK REV/+BbscT8uH1KeBVkLzg7DyHPqVcpUgU8qm8dl1ymYvrBFeziY0JzKU4v1 Bg6bT2JfewE+giCoeiT+YVuICAGyyOg09rIE43GZRlW22Ve1cn/jNwIWcaJD oa5rnDVGwgGhA6jcY+K3uy00n+NCZKU/qWClpxUbEoCrbZ0eM2oS+4yfhPyT 1I0VgvY+8HZ5FS/U1kG/N+vmTvKfqFn2kH7Ezd9sClr1wJiV5xzGLCXIzGk0 s0yQ+ek1tQ7Ks67BSuRLQDqC5boyERh4GtghJM1LRAbYBmf4okuYH3ByCG6A 7P1+cV31nhZFl1BmT10H/AFI/jvOFZHA93A7fy6VP8vU97bTtmJYG/t7P0Cu nh69pFDsvkX9MqD2fuVESv5sUAHb8+ibmBZ8tiB1bS3XFxk= =1mnd -----END PGP SIGNATURE-----