Skip to contentSkip to conversation

Add HID gamepad quirk fallback for broken SDP records

PATCH In review Pakrohk 2 participants 6 messages Jul 21

Subsystems (2)
lore
Share & tools
lore

Pakrohk

author
#0 lore raw

v2 cover Add HID gamepad quirk fallback for broken SDP records

From: Pakrohk <[email protected]>

This patch series adds a modular quirk system that provides HID report descriptor fallbacks for third-party Bluetooth gamepads with incomplete or malformed SDP records.

Problem:
Third-party Bluetooth HID gamepads (notably TG170W / DualShock 4 v2 clones, VID:PID 054c:09cc) expose incomplete HID SDP records. BlueZ's extract_hid_record() fails with -ENOENT, preventing HIDP registration. The Bluetooth link is active but no /dev/input/event* device is created. Windows and Android handle this gracefully by falling back to known HID descriptors. BlueZ does not.

Solution:
A two-tier quirk system that activates only when SDP parsing fails for a specifically matched device. Does not globally weaken HID parsing.

hidp_add_connection()

    -> extract_hid_record()           // normal SDP path
      -> FAILS (-ENOENT)
    -> gamepad_quirk_apply()          // check built-in + external quirks
      -> inject fallback HID descriptor
    -> continue HIDP setup normally

Patch [1] adds HMAC-SHA256 to the internal bt_crypto library using the kernel AF_ALG interface, following the existing cmac(aes) pattern. This replaces the previous openssl CLI dependency.

Patch [2] adds the core quirk system: built-in quirk for TG170W/DS4 v2, external quirk profile loader with HMAC signature verification, and the device.c integration point.

Patch [3] adds bluez-quirkctl, a CLI tool for managing external quirk profiles (validate, install, list, remove).

External quirk profiles are JSON files installed to /var/lib/bluez/quirks/ with HMAC-SHA256 signatures, loaded at bluetoothd startup. This enables community-maintained gamepad support without BlueZ source modifications.

Testing:
- TG170W / DualShock 4 v2 (054c:09cc): buttons, sticks, touchpad verified via evtest
- Kernel creates three input devices: Wireless Controller, Wireless Controller Motion Sensors, Wireless Controller Touchpad
- External profile validation, installation, loading tested end-to-end
- Tampered profiles rejected by signature verification

---
Patch 1: src: add HMAC-SHA256 to bt_crypto using kernel AF_ALG Patch 2: input: add HID gamepad quirk fallback for broken SDP records Patch 3: tools: add bluez-quirkctl for managing quirk profiles

Signature · 1 line
2.49.0
author
#1 lore raw

PATCH v2 1/3 src: add HMAC-SHA256 to bt_crypto using kernel AF_ALG

From: Pakrohk <[email protected]>

Add HMAC-SHA256 computation to the bt_crypto library using the Linux kernel AF_ALG interface. This follows the exact same pattern as the existing cmac(aes) implementation.

The HMAC-SHA256 function is needed for verifying external quirk profile signatures. The previous implementation used popen("openssl dgst ...") which had command injection risks and an openssl CLI dependency.

Using kernel AF_ALG:
- No external library dependencies
- Matches existing BlueZ crypto patterns
- No shell invocation or temp files
- Works in bluetoothd's process context

The hmac(sha256) algorithm is available in all modern Linux kernels via the crypto API (/proc/crypto).

src/shared/crypto.c +66 -0
index cb99116..f5883bd 100644
@@ -69,6 +69,7 @@ struct bt_crypto {
 	int ecb_aes;
 	int urandom;
 	int cmac_aes;
+	int hmac_sha256;
 };
 
 static int urandom_setup(void)
@@ -126,6 +127,28 @@ static int cmac_aes_setup(void)
 	return fd;
 }
 
+static int hmac_sha256_setup(void)
+{
+	struct sockaddr_alg salg;
+	int fd;
+
+	fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+	if (fd < 0)
+		return -1;
+
+	memset(&salg, 0, sizeof(salg));
+	salg.salg_family = AF_ALG;
+	strcpy((char *) salg.salg_type, "hash");
+	strcpy((char *) salg.salg_name, "hmac(sha256)");
+
+	if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
+		close(fd);
+		return -1;
+	}
+
+	return fd;
+}
+
 static struct bt_crypto *singleton;
 
 struct bt_crypto *bt_crypto_new(void)
@@ -159,6 +182,16 @@ struct bt_crypto *bt_crypto_new(void)
 		return NULL;
 	}
 
+	singleton->hmac_sha256 = hmac_sha256_setup();
+	if (singleton->hmac_sha256 < 0) {
+		close(singleton->cmac_aes);
+		close(singleton->urandom);
+		close(singleton->ecb_aes);
+		free(singleton);
+		singleton = NULL;
+		return NULL;
+	}
+
 	return bt_crypto_ref(singleton);
 }
 
@@ -183,6 +216,7 @@ void bt_crypto_unref(struct bt_crypto *crypto)
 	close(crypto->urandom);
 	close(crypto->ecb_aes);
 	close(crypto->cmac_aes);
+	close(crypto->hmac_sha256);
 
 	free(crypto);
 	singleton = NULL;
@@ -1005,3 +1039,35 @@ bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor,
 	/* Encrypt sirk using k as LTK with sef function */
 	return bt_crypto_sef(crypto, k, sirk_plaintext, sirk);
 }
+
+bool bt_crypto_hmac_sha256(struct bt_crypto *crypto,
+				const uint8_t *key, size_t key_len,
+				const uint8_t *msg, size_t msg_len,
+				uint8_t res[32])
+{
+	ssize_t len;
+	int fd;
+
+	if (!crypto)
+		return false;
+
+	fd = alg_new(crypto->hmac_sha256, key, key_len);
+	if (fd < 0)
+		return false;
+
+	len = send(fd, msg, msg_len, 0);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	len = read(fd, res, 32);
+	if (len < 0) {
+		close(fd);
+		return false;
+	}
+
+	close(fd);
+
+	return true;
+}
src/shared/crypto.h +5 -0
index d85f807..6b234bc 100644
@@ -62,3 +62,8 @@ bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor,
 			uint8_t sirk[16]);
 bool bt_crypto_rsi(struct bt_crypto *crypto, const uint8_t sirk[16],
 					uint8_t rsi[6]);
+
+bool bt_crypto_hmac_sha256(struct bt_crypto *crypto,
+				const uint8_t *key, size_t key_len,
+				const uint8_t *msg, size_t msg_len,
+				uint8_t res[32]);
author
#3 lore raw

PATCH v2 2/3 input: add HID gamepad quirk fallback for broken SDP records

In reply to Pakrohk

From: Pakrohk <[email protected]>

Add a modular gamepad quirk system to profiles/input/ that provides HID report descriptor fallbacks when BlueZ's SDP parser fails.

When extract_hid_record() returns -ENOENT for a known gamepad, the quirk system checks registered quirks and injects a fallback HID descriptor so the kernel's HID driver can create an input device.

Two tiers of quirk support:

Built-in quirks:
Hardcoded C entries for known broken controllers. Currently supports DualShock 4 v2 / TG170W (054c:09cc) with a minimal BT HID descriptor matching report ID 0x11 (78 bytes, as expected by hid-playstation).

External quirk profiles:
JSON files in /var/lib/bluez/quirks/ with HMAC-SHA256 signatures. Loaded at bluetoothd startup. Enables community-maintained support without BlueZ source modifications.

Architecture:
quirk.h - quirk struct definition and dispatch API
quirk.c - quirk registry and dispatch logic
quirk-profile.h/c - external JSON profile loader with HMAC verification quirks/tg170w.c - built-in quirk for DualShock 4 v2

The quirk only activates when SDP parsing fails AND the device matches a registered quirk. This does not globally weaken HID parsing.

Multi-factor matching avoids false positives:
match = (vendor_id == 0x054c && product_id == 0x09cc)

       || (device_name == "Wireless Controller"
           && SDP provider contains "Sony")
profiles/input/device.c +2 -5
index 8017e07..0089c3d 100644
@@ -1089,11 +1089,8 @@ static int hidp_add_connection(struct input_device *idev)
 
 	err = extract_hid_record(idev, req);
 	if (err < 0) {
-		/* Try gamepad quirk fallback for known broken devices */
-		if (gamepad_quirk_match(idev)) {
-			DBG("HID SDP failed, trying gamepad quirk");
-			err = gamepad_quirk_apply(idev, req);
-		}
+		DBG("HID SDP failed, trying gamepad quirk");
+		err = gamepad_quirk_apply(idev, req);
 	}
 
 	if (err < 0) {
profiles/input/quirk-profile.c +38 -78

174 lines not on this page · raw message

profiles/input/quirk.c +1 -22
index 053f45b..fa5e7e6 100644
@@ -36,23 +36,6 @@ static struct gamepad_quirk *quirks[] = {
 	NULL
 };
 
-bool gamepad_quirk_match(struct input_device *idev)
-{
-	int i;
-
-	/* Check built-in quirks first */
-	for (i = 0; quirks[i]; i++) {
-		if (quirks[i]->match(idev))
-			return true;
-	}
-
-	/* Then check external (file-based) quirks */
-	if (external_quirk_match(idev))
-		return true;
-
-	return false;
-}
-
 int gamepad_quirk_apply(struct input_device *idev,
 			struct hidp_connadd_req *req)
 {
@@ -69,9 +52,5 @@ int gamepad_quirk_apply(struct input_device *idev,
 	}
 
 	/* Try external quirks */
-	if (external_quirk_match(idev)) {
-		return external_quirk_apply(idev, req);
-	}
-
-	return -1;
+	return external_quirk_apply(idev, req);
 }
profiles/input/quirk.h +0 -3
index 08e0bce..f2fc81b 100644
@@ -24,13 +24,10 @@ struct gamepad_quirk {
 			struct hidp_connadd_req *req);
 };
 
-bool gamepad_quirk_match(struct input_device *idev);
-
 int gamepad_quirk_apply(struct input_device *idev,
 			struct hidp_connadd_req *req);
 
 /* External quirk support (quirk-profile.c) */
-bool external_quirk_match(struct input_device *idev);
 int external_quirk_apply(struct input_device *idev,
 			struct hidp_connadd_req *req);
 
profiles/input/quirks/tg170w.c +5 -19
index d3f37a8..d82c3fa 100644
@@ -38,9 +38,14 @@
 #include "bluetooth/sdp.h"
 #include "bluetooth/sdp_lib.h"
 
+#include "gdbus/gdbus.h"
+
 #include "src/log.h"
+#include "src/device.h"
+#include "src/service.h"
 
 #include "../quirk.h"
+#include "../device.h"
 
 /*
  * DualShock 4 Bluetooth HID Report Descriptor.
@@ -124,25 +129,6 @@ static const uint8_t tg170w_hid_report_descriptor[] = {
 #define TG170W_VID  0x054c  /* Sony */
 #define TG170W_PID  0x09cc  /* DualShock 4 v2 */
 
-/* Opaque structs - we don't pull in heavy headers */
-struct input_device;
-struct btd_device;
-struct btd_service;
-
-/* Declarations from src/device.h and src/service.h */
-extern struct btd_service *input_device_get_service(
-					struct input_device *idev);
-extern struct btd_device *btd_service_get_device(
-					const struct btd_service *service);
-extern uint16_t btd_device_get_vendor(struct btd_device *device);
-extern uint16_t btd_device_get_product(struct btd_device *device);
-extern const sdp_record_t *btd_device_get_record(
-					struct btd_device *device,
-					const char *uuid);
-extern bool device_name_known(struct btd_device *device);
-extern void device_get_name(struct btd_device *device,
-					char *name, size_t len);
-
 static bool tg170w_match(struct input_device *idev)
 {
 	struct btd_service *service;
author
#5 lore raw

PATCH v2 3/3 tools: add bluez-quirkctl for managing quirk profiles

In reply to Pakrohk

From: Pakrohk <[email protected]>

Add bluez-quirkctl, a CLI tool for managing external gamepad quirk profiles. This tool validates JSON profiles, manages HMAC-SHA256 keys, and installs/removes signed profiles to the system directory.

Usage:

  bluez-quirkctl install <file.json>   - validate, sign, install
  bluez-quirkctl remove <name>         - uninstall a profile
  bluez-quirkctl list                  - list installed profiles
  bluez-quirkctl validate <file.json>  - check without installing

Security model:
- Install/remove requires root (checked via getuid())
- HMAC key auto-generated in /var/lib/bluez/quirks/.hmac_key (0600)
- Profiles installed to /var/lib/bluez/quirks/ (0644)
- HMAC-SHA256 computed using bt_crypto (kernel AF_ALG)
- Symlinks rejected during loading
- Descriptor size capped at 2048 bytes

The tool links against libbluetooth-internal and libshared-mainloop for access to bt_crypto_hmac_sha256().

Makefile.tools +2 -1
index 44da0ce..ecce5b4 100644
@@ -581,5 +581,6 @@ bin_PROGRAMS += tools/bluez-quirkctl
 
 tools_bluez_quirkctl_SOURCES = tools/quirkctl.c
 tools_bluez_quirkctl_CFLAGS = $(JSONC_CFLAGS)
-tools_bluez_quirkctl_LDADD = $(JSONC_LIBS)
+tools_bluez_quirkctl_LDADD = lib/libbluetooth-internal.la \
+				src/libshared-mainloop.la $(JSONC_LIBS)
 endif
tools/quirkctl.c +181 -49

277 lines not on this page · raw message

#2 lore raw

Re: [PATCH BlueZ v2 1/3] src: add HMAC-SHA256 to bt_crypto using kernel AF_ALG

In reply to Pakrohk

Hi Pakrohk,

Quoted 11 lines · Pakrohk

From: Pakrohk <[email protected]>

Add HMAC-SHA256 computation to the bt_crypto library using the Linux kernel AF_ALG interface. This follows the exact same pattern as the existing cmac(aes) implementation.

The HMAC-SHA256 function is needed for verifying external quirk profile signatures. The previous implementation used popen("openssl dgst ...") which had command injection risks and an openssl CLI dependency.

Quoted 5 lines

Using kernel AF_ALG:
- No external library dependencies
- Matches existing BlueZ crypto patterns
- No shell invocation or temp files
- Works in bluetoothd's process context

Well I guess kernel crypto doesn't want us to add new usage like this since AF_ALG is to be considered deprecated, even though I have to agree it is much simpler and probably safer than using an external crypto library.

Quoted 120 lines

The hmac(sha256) algorithm is available in all modern Linux kernels via the crypto API (/proc/crypto).

src/shared/crypto.c +66 -0
index cb99116..f5883bd 100644
@@ -69,6 +69,7 @@ struct bt_crypto {
        int ecb_aes;
        int urandom;
        int cmac_aes;
+       int hmac_sha256;
 };
 
 static int urandom_setup(void)
@@ -126,6 +127,28 @@ static int cmac_aes_setup(void)
        return fd;
 }
 
+static int hmac_sha256_setup(void)
+{
+       struct sockaddr_alg salg;
+       int fd;
+
+       fd = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
+       if (fd < 0)
+               return -1;
+
+       memset(&salg, 0, sizeof(salg));
+       salg.salg_family = AF_ALG;
+       strcpy((char *) salg.salg_type, "hash");
+       strcpy((char *) salg.salg_name, "hmac(sha256)");
+
+       if (bind(fd, (struct sockaddr *) &salg, sizeof(salg)) < 0) {
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
 static struct bt_crypto *singleton;
 
 struct bt_crypto *bt_crypto_new(void)
@@ -159,6 +182,16 @@ struct bt_crypto *bt_crypto_new(void)
                return NULL;
        }
 
+       singleton->hmac_sha256 = hmac_sha256_setup();
+       if (singleton->hmac_sha256 < 0) {
+               close(singleton->cmac_aes);
+               close(singleton->urandom);
+               close(singleton->ecb_aes);
+               free(singleton);
+               singleton = NULL;
+               return NULL;
+       }
+
        return bt_crypto_ref(singleton);
 }
 
@@ -183,6 +216,7 @@ void bt_crypto_unref(struct bt_crypto *crypto)
        close(crypto->urandom);
        close(crypto->ecb_aes);
        close(crypto->cmac_aes);
+       close(crypto->hmac_sha256);
 
        free(crypto);
        singleton = NULL;
@@ -1005,3 +1039,35 @@ bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor,
        /* Encrypt sirk using k as LTK with sef function */
        return bt_crypto_sef(crypto, k, sirk_plaintext, sirk);
 }
+
+bool bt_crypto_hmac_sha256(struct bt_crypto *crypto,
+                               const uint8_t *key, size_t key_len,
+                               const uint8_t *msg, size_t msg_len,
+                               uint8_t res[32])
+{
+       ssize_t len;
+       int fd;
+
+       if (!crypto)
+               return false;
+
+       fd = alg_new(crypto->hmac_sha256, key, key_len);
+       if (fd < 0)
+               return false;
+
+       len = send(fd, msg, msg_len, 0);
+       if (len < 0) {
+               close(fd);
+               return false;
+       }
+
+       len = read(fd, res, 32);
+       if (len < 0) {
+               close(fd);
+               return false;
+       }
+
+       close(fd);
+
+       return true;
+}
src/shared/crypto.h +5 -0
index d85f807..6b234bc 100644
@@ -62,3 +62,8 @@ bool bt_crypto_sirk(struct bt_crypto *crypto, const char *str, uint16_t vendor,
                        uint8_t sirk[16]);
 bool bt_crypto_rsi(struct bt_crypto *crypto, const uint8_t sirk[16],
                                        uint8_t rsi[6]);
+
+bool bt_crypto_hmac_sha256(struct bt_crypto *crypto,
+                               const uint8_t *key, size_t key_len,
+                               const uint8_t *msg, size_t msg_len,
+                               uint8_t res[32]);
Signature · 1 line
Luiz Augusto von Dentz
#4 lore raw

Re: [PATCH BlueZ v2 2/3] input: add HID gamepad quirk fallback for broken SDP records

In reply to Pakrohk

Hi Pakrohk,

Quoted 99 lines · Pakrohk

From: Pakrohk <[email protected]>

Add a modular gamepad quirk system to profiles/input/ that provides HID report descriptor fallbacks when BlueZ's SDP parser fails.

When extract_hid_record() returns -ENOENT for a known gamepad, the quirk system checks registered quirks and injects a fallback HID descriptor so the kernel's HID driver can create an input device.

Two tiers of quirk support:

Built-in quirks:
Hardcoded C entries for known broken controllers. Currently supports DualShock 4 v2 / TG170W (054c:09cc) with a minimal BT HID descriptor matching report ID 0x11 (78 bytes, as expected by hid-playstation).

External quirk profiles:
JSON files in /var/lib/bluez/quirks/ with HMAC-SHA256 signatures. Loaded at bluetoothd startup. Enables community-maintained support without BlueZ source modifications.

Architecture:
quirk.h - quirk struct definition and dispatch API
quirk.c - quirk registry and dispatch logic
quirk-profile.h/c - external JSON profile loader with HMAC verification quirks/tg170w.c - built-in quirk for DualShock 4 v2

The quirk only activates when SDP parsing fails AND the device matches a registered quirk. This does not globally weaken HID parsing.

Multi-factor matching avoids false positives:
match = (vendor_id == 0x054c && product_id == 0x09cc)

       || (device_name == "Wireless Controller"
           && SDP provider contains "Sony")
profiles/input/device.c +2 -5
index 8017e07..0089c3d 100644
@@ -1089,11 +1089,8 @@ static int hidp_add_connection(struct input_device *idev)
 
        err = extract_hid_record(idev, req);
        if (err < 0) {
-               /* Try gamepad quirk fallback for known broken devices */
-               if (gamepad_quirk_match(idev)) {
-                       DBG("HID SDP failed, trying gamepad quirk");
-                       err = gamepad_quirk_apply(idev, req);
-               }
+               DBG("HID SDP failed, trying gamepad quirk");
+               err = gamepad_quirk_apply(idev, req);
        }
 
        if (err < 0) {
profiles/input/quirk-profile.c +9 -10
index 8ec3835..4240154 100644
@@ -28,22 +28,20 @@
 
 #include "bluetooth/bluetooth.h"
 #include "bluetooth/hidp.h"
+#include "bluetooth/sdp.h"
+#include "bluetooth/sdp_lib.h"
 
 #include "src/log.h"
 
 #include "quirk.h"
 #include "quirk-profile.h"
 
-/* Accessors from src/device.h and src/service.h */
-extern struct btd_service *input_device_get_service(
-                                       struct input_device *idev);
-extern struct btd_device *btd_service_get_device(
-                                       const struct btd_service *service);
-extern uint16_t btd_device_get_vendor(struct btd_device *device);
-extern uint16_t btd_device_get_product(struct btd_device *device);
-extern bool device_name_known(struct btd_device *device);
-extern void device_get_name(struct btd_device *device,
-                                       char *name, size_t len);
+#include "gdbus/gdbus.h"
+
+#include "src/device.h"
+#include "src/service.h"
+#include "src/shared/crypto.h"
+#include "device.h"
 
 #define MAX_EXTERNAL_QUIRKS 32
 #define HMAC_KEY_PATH QUIRK_PROFILE_DIR "/.hmac_key"
@@ -67,6 +65,7 @@ struct external_quirk {
 
 static struct external_quirk *ext_quirks[MAX_EXTERNAL_QUIRKS + 1];
 static int num_ext_quirks;
+static struct bt_crypto *crypto;
 
 /*
  * Parse a hex string like "0501 0905" into a byte buffer.

profiles/input/quirk-profile.c static char *read_file(const char *path, size_t *out_len) · line 145

Quoted 7 lines
profiles/input/quirk-profile.c +0 -4
@@ -139,77 +138,23 @@ static char *read_file(const char *path, size_t *out_len)
 }
 
 /*
- * Compute HMAC-SHA256 using openssl CLI.
- */
-static char *hmac_sha256(const uint8_t *key, int key_len,
-                        const void *data, size_t data_len)

This file doesn't even exist upstream, so how come you're modifying it? Looks like there is something off with this patch-set, beside it is saying that it doesn't use openssl so this seem to be a left over or something.

Quoted 236 lines
profiles/input/quirk-profile.c +29 -64
-{
-       char key_hex[HMAC_KEY_SIZE * 2 + 1];
-       char *cmd;
-       char *result = NULL;
-       FILE *p, *tmpf;
-       int i;
-       const char *tmpfile = "/tmp/.bluez_quirk_hmac_input";
-       char line[256];
-       char *eq, *end;
-
-       for (i = 0; i < key_len; i++)
-               snprintf(key_hex + i * 2, 3, "%02x", key[i]);
-       key_hex[key_len * 2] = '\0';
-
-       tmpf = fopen(tmpfile, "we");
-       if (!tmpf)
-               return NULL;
-
-       fwrite(data, 1, data_len, tmpf);
-       fclose(tmpf);
-
-       cmd = malloc(strlen(key_hex) + 128);
-       if (!cmd) {
-               unlink(tmpfile);
-               return NULL;
-       }
-
-       snprintf(cmd, strlen(key_hex) + 128,
-               "openssl dgst -sha256 -hmac '%s' -hex < %s 2>/dev/null",
-               key_hex, tmpfile);
-
-       p = popen(cmd, "re");
-       free(cmd);
-       unlink(tmpfile);
-
-       if (!p)
-               return NULL;
-
-       while (fgets(line, sizeof(line), p)) {
-               eq = strstr(line, "= ");
-               if (eq) {
-                       eq += 2;
-                       end = eq + strlen(eq) - 1;
-                       while (end > eq && (*end == '\n' || *end == '\r'
-                                               || *end == ' '))
-                               *end-- = '\0';
-                       result = strdup(eq);
-                       break;
-               }
-       }
-
-       pclose(p);
-       return result;
-}
-
-/*
- * Verify HMAC signature.
+ * Verify HMAC-SHA256 signature using bt_crypto (kernel AF_ALG).
  */
 static bool verify_signature(const char *json_path,
                             const uint8_t *hmac_key, int key_len)
 {
        char sig_path[1024];
        size_t sig_len, json_len;
-       char *sig_hex, *json_data, *expected_sig;
+       char *sig_hex, *json_data;
        char *end;
+       uint8_t computed[32];
+       char computed_hex[65];
+       int i;
        bool valid;
 
+       if (!crypto)
+               return false;
+
        snprintf(sig_path, sizeof(sig_path), "%s" SIG_EXT, json_path);
 
        sig_hex = read_file(sig_path, &sig_len);
@@ -228,21 +173,25 @@ static bool verify_signature(const char *json_path,
                return false;
        }
 
-       expected_sig = hmac_sha256(hmac_key, key_len, json_data, json_len);
-       free(json_data);
-
-       if (!expected_sig) {
+       if (!bt_crypto_hmac_sha256(crypto, hmac_key, key_len,
+                               (const uint8_t *) json_data, json_len,
+                               computed)) {
+               free(json_data);
                free(sig_hex);
                return false;
        }
+       free(json_data);
+
+       for (i = 0; i < 32; i++)
+               snprintf(computed_hex + i * 2, 3, "%02x", computed[i]);
+       computed_hex[64] = '\0';
 
-       valid = (strcmp(sig_hex, expected_sig) == 0);
+       valid = (strcmp(sig_hex, computed_hex) == 0);
 
        if (!valid)
                DBG("Signature mismatch for %s", json_path);
 
        free(sig_hex);
-       free(expected_sig);
        return valid;
 }
 
@@ -516,6 +465,12 @@ int load_external_quirks(const char *dir)
        if (!dir)
                dir = QUIRK_PROFILE_DIR;
 
+       if (!crypto) {
+               crypto = bt_crypto_new();
+               if (!crypto)
+                       DBG("quirk-profile: failed to init crypto");
+       }
+
        d = opendir(dir);
        if (!d) {
                DBG("quirk-profile: cannot open %s: %s", dir, strerror(errno));
@@ -586,4 +541,9 @@ void free_external_quirks(void)
                ext_quirks[i] = NULL;
        }
        num_ext_quirks = 0;
+
+       if (crypto) {
+               bt_crypto_unref(crypto);
+               crypto = NULL;
+       }
 }
profiles/input/quirk.c +1 -22
index 053f45b..fa5e7e6 100644
@@ -36,23 +36,6 @@ static struct gamepad_quirk *quirks[] = {
        NULL
 };
 
-bool gamepad_quirk_match(struct input_device *idev)
-{
-       int i;
-
-       /* Check built-in quirks first */
-       for (i = 0; quirks[i]; i++) {
-               if (quirks[i]->match(idev))
-                       return true;
-       }
-
-       /* Then check external (file-based) quirks */
-       if (external_quirk_match(idev))
-               return true;
-
-       return false;
-}
-
 int gamepad_quirk_apply(struct input_device *idev,
                        struct hidp_connadd_req *req)
 {
@@ -69,9 +52,5 @@ int gamepad_quirk_apply(struct input_device *idev,
        }
 
        /* Try external quirks */
-       if (external_quirk_match(idev)) {
-               return external_quirk_apply(idev, req);
-       }
-
-       return -1;
+       return external_quirk_apply(idev, req);
 }
profiles/input/quirk.h +0 -3
index 08e0bce..f2fc81b 100644
@@ -24,13 +24,10 @@ struct gamepad_quirk {
                        struct hidp_connadd_req *req);
 };
 
-bool gamepad_quirk_match(struct input_device *idev);
-
 int gamepad_quirk_apply(struct input_device *idev,
                        struct hidp_connadd_req *req);
 
 /* External quirk support (quirk-profile.c) */
-bool external_quirk_match(struct input_device *idev);
 int external_quirk_apply(struct input_device *idev,
                        struct hidp_connadd_req *req);
 
profiles/input/quirks/tg170w.c +5 -19
index d3f37a8..d82c3fa 100644
@@ -38,9 +38,14 @@
 #include "bluetooth/sdp.h"
 #include "bluetooth/sdp_lib.h"
 
+#include "gdbus/gdbus.h"
+
 #include "src/log.h"
+#include "src/device.h"
+#include "src/service.h"
 
 #include "../quirk.h"
+#include "../device.h"
 
 /*
  * DualShock 4 Bluetooth HID Report Descriptor.
@@ -124,25 +129,6 @@ static const uint8_t tg170w_hid_report_descriptor[] = {
 #define TG170W_VID  0x054c  /* Sony */
 #define TG170W_PID  0x09cc  /* DualShock 4 v2 */
 
-/* Opaque structs - we don't pull in heavy headers */
-struct input_device;
-struct btd_device;
-struct btd_service;
-
-/* Declarations from src/device.h and src/service.h */
-extern struct btd_service *input_device_get_service(
-                                       struct input_device *idev);
-extern struct btd_device *btd_service_get_device(
-                                       const struct btd_service *service);
-extern uint16_t btd_device_get_vendor(struct btd_device *device);
-extern uint16_t btd_device_get_product(struct btd_device *device);
-extern const sdp_record_t *btd_device_get_record(
-                                       struct btd_device *device,
-                                       const char *uuid);
-extern bool device_name_known(struct btd_device *device);
-extern void device_get_name(struct btd_device *device,
-                                       char *name, size_t len);
-
 static bool tg170w_match(struct input_device *idev)
 {
        struct btd_service *service;
Signature · 1 line
Luiz Augusto von Dentz