[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251118142025.1982263-7-neeraj.sanjaykale@nxp.com>
Date: Tue, 18 Nov 2025 19:50:19 +0530
From: Neeraj Sanjay Kale <neeraj.sanjaykale@....com>
To: marcel@...tmann.org,
luiz.dentz@...il.com
Cc: linux-bluetooth@...r.kernel.org,
linux-kernel@...r.kernel.org,
amitkumar.karwar@....com,
sherry.sun@....com,
dmitrii.lebed@....com,
neeraj.sanjaykale@....com
Subject: [PATCH v1 06/11] Bluetooth: btnxpuart: Add TLS device hello processing
This implements device hello message processing to derive handshake
traffic secrets:
- Add HKDF-SHA256 functions for TLS 1.3 traffic secret derivation
following RFC 5869/8446
- Extract device ECDH public key and compute shared secret using
KPP API with host private key and device public key
- Derive handshake traffic secret from ECDH shared secret
following TLS 1.3 key schedule
- Validate device hello message and update handshake hash state
The handshake traffic secret enables decryption of the
device_finish portion within the device_hello message.
Signed-off-by: Neeraj Sanjay Kale <neeraj.sanjaykale@....com>
---
drivers/bluetooth/btnxpuart.c | 274 +++++++++++++++++++++++++++++++++-
1 file changed, 270 insertions(+), 4 deletions(-)
diff --git a/drivers/bluetooth/btnxpuart.c b/drivers/bluetooth/btnxpuart.c
index 44073eae0df3..3bff7758fa5e 100644
--- a/drivers/bluetooth/btnxpuart.c
+++ b/drivers/bluetooth/btnxpuart.c
@@ -150,8 +150,9 @@
#define FW_METADATA_TLV_ECDSA_KEY 0x50
#define FW_METADATA_FLAG_BT 0x02
-#define NXP_FW_UUID_SIZE 16
-#define NXP_FW_ECDSA_PUBKEY_SIZE 65
+#define NXP_FW_UUID_SIZE 16
+#define NXP_FW_ECDH_PUBKEY_SIZE 64
+#define NXP_FW_ECDSA_PUBKEY_SIZE 65
struct ps_data {
u8 target_ps_mode; /* ps mode to be set */
@@ -203,8 +204,11 @@ struct btnxpuart_crypto {
struct crypto_shash *tls_handshake_hash_tfm;
struct shash_desc *tls_handshake_hash_desc;
struct crypto_kpp *kpp;
+ uint8_t ecdh_public[NXP_FW_ECDH_PUBKEY_SIZE]; /* ECDH public key, Key negotiation */
u8 ecdsa_public[NXP_FW_ECDSA_PUBKEY_SIZE]; /* ECDSA public key, Authentication*/
u8 fw_uuid[NXP_FW_UUID_SIZE];
+ u8 handshake_h2_hash[SHA256_DIGEST_SIZE];
+ u8 handshake_secret[SHA256_DIGEST_SIZE];
};
struct btnxpuart_dev {
@@ -396,6 +400,11 @@ union nxp_set_bd_addr_payload {
#define NXP_TLS_VERSION 1
#define NXP_TLS_ECDH_PUBLIC_KEY_SIZE 64
+#define NXP_DEVICE_UUID_LEN 16
+#define NXP_ENC_AUTH_TAG_SIZE 16
+
+#define NXP_TLS_LABEL(str) str, strlen(str)
+#define NXP_TLS_DEVICE_HS_TS_LABEL NXP_TLS_LABEL("D HS TS")
enum nxp_tls_signature_algorithm {
NXP_TLS_ECDSA_SECP256R1_SHA256 = 0x0403,
@@ -440,6 +449,38 @@ union nxp_tls_host_hello_payload {
u8 buf[113];
};
+struct nxp_tls_device_info {
+ __le16 chip_id;
+ __le16 device_flags;
+ u8 reserved[4];
+ u8 uuid[NXP_DEVICE_UUID_LEN];
+};
+
+struct nxp_tls_signature {
+ u8 sig[64]; /* P-256 ECDSA signature, two points */
+};
+
+struct nxp_tls_finished {
+ u8 verify_data[32];
+};
+
+struct nxp_tls_device_hello {
+ struct nxp_tls_message_hdr hdr;
+ __le32 reserved;
+ u8 random[32];
+ u8 pubkey[NXP_TLS_ECDH_PUBLIC_KEY_SIZE];
+ /* Encrypted portion */
+ struct {
+ struct nxp_tls_device_info device_info;
+ struct nxp_tls_signature device_handshake_sig; /* TLS Certificate Verify */
+ struct nxp_tls_finished device_finished;
+ } enc;
+ u8 auth_tag[NXP_ENC_AUTH_TAG_SIZE]; /* Auth tag for the encrypted portion */
+};
+
+#define DEVICE_HELLO_SIG_CUTOFF_POS \
+ offsetof(struct nxp_tls_device_hello, enc)
+
/* FW Meta Data */
struct fw_metadata_hdr {
__le32 cmd;
@@ -1698,7 +1739,7 @@ static int nxp_generate_ecdh_public_key(struct crypto_kpp *tfm, u8 public_key[64
}
static inline void nxp_tls_hdr_init(struct nxp_tls_message_hdr *hdr, size_t len,
- enum nxp_tls_message_id id)
+ enum nxp_tls_message_id id)
{
hdr->magic = cpu_to_le32(NXP_TLS_MAGIC);
hdr->len = cpu_to_le16((u16)len);
@@ -1785,11 +1826,222 @@ static struct sk_buff *nxp_host_do_hello(struct hci_dev *hdev)
return skb;
}
+static int nxp_crypto_shash_final(struct shash_desc *desc, u8 *out)
+{
+ struct shash_desc *desc_tmp = kzalloc(sizeof(struct shash_desc) +
+ crypto_shash_descsize(desc->tfm),
+ GFP_KERNEL);
+
+ if (!desc_tmp)
+ return -ENOMEM;
+
+ crypto_shash_export(desc, desc_tmp);
+ crypto_shash_final(desc, out);
+ crypto_shash_import(desc, desc_tmp);
+ kfree(desc_tmp);
+
+ return 0;
+}
+
+static int nxp_compute_shared_secret(struct crypto_kpp *tfm, const u8 public_key[64], u8 secret[32])
+{
+ DECLARE_CRYPTO_WAIT(result);
+ struct kpp_request *req;
+ struct scatterlist src, dst;
+ int err;
+
+ req = kpp_request_alloc(tfm, GFP_KERNEL);
+ if (!req) {
+ pr_err("Failed to allocate memory for KPP request\n");
+ return -ENOMEM;
+ }
+
+ sg_init_one(&src, public_key, 64);
+ sg_init_one(&dst, secret, 32);
+ kpp_request_set_input(req, &src, 64);
+ kpp_request_set_output(req, &dst, 32);
+ kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &result);
+ err = crypto_kpp_compute_shared_secret(req);
+ err = crypto_wait_req(err, &result);
+ if (err < 0) {
+ pr_err("alg: ecdh: compute shared secret failed. err %d\n", err);
+ goto free_all;
+ }
+
+free_all:
+ kpp_request_free(req);
+ return err;
+}
+
+static int nxp_hkdf_sha256_extract(const void *salt, size_t salt_len,
+ const void *ikm, size_t ikm_len,
+ u8 result[SHA256_DIGEST_SIZE])
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ u8 zeroes[SHA256_DIGEST_SIZE] = {0};
+ int ret = 0;
+
+ tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
+ if (IS_ERR(tfm))
+ return PTR_ERR(tfm);
+
+ desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm), GFP_KERNEL);
+ if (!desc) {
+ crypto_free_shash(tfm);
+ return -ENOMEM;
+ }
+
+ desc->tfm = tfm;
+
+ /* RFC 5869: If salt is empty, use HashLen zero octets */
+ if (salt_len == 0)
+ ret = crypto_shash_setkey(tfm, zeroes, SHA256_DIGEST_SIZE);
+ else
+ ret = crypto_shash_setkey(tfm, salt, salt_len);
+
+ if (ret)
+ goto cleanup;
+
+ ret = crypto_shash_init(desc);
+ if (ret)
+ goto cleanup;
+
+ ret = crypto_shash_update(desc, ikm, ikm_len);
+ if (ret)
+ goto cleanup;
+
+ ret = crypto_shash_final(desc, result);
+
+cleanup:
+ kfree(desc);
+ crypto_free_shash(tfm);
+ return ret;
+}
+
+static int nxp_hkdf_expand_label(const u8 secret[SHA256_DIGEST_SIZE],
+ const char *label, size_t label_size,
+ u8 *context, size_t context_size,
+ void *output, size_t output_size)
+{
+ struct crypto_shash *tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
+ struct shash_desc *desc = kzalloc(sizeof(*desc) + crypto_shash_descsize(tfm),
+ GFP_KERNEL);
+ u8 hmac_out[SHA256_DIGEST_SIZE];
+ u16 length = output_size;
+ u8 one = 0x01;
+
+ if (IS_ERR(tfm)) {
+ pr_err("Failed to alloc shash for HMAC\n");
+ return -ENOMEM;
+ }
+
+ if (!desc) {
+ crypto_free_shash(tfm);
+ return -ENOMEM;
+ }
+
+ crypto_shash_setkey(tfm, secret, SHA256_DIGEST_SIZE);
+ desc->tfm = tfm;
+
+ crypto_shash_init(desc);
+ crypto_shash_update(desc, (u8 *)&length, sizeof(length));
+ crypto_shash_update(desc, label, label_size);
+
+ if (context && context_size > 0)
+ crypto_shash_update(desc, context, context_size);
+
+ /* RFC 5869: HKDF-Expand counter starts at 0x01 */
+ crypto_shash_update(desc, &one, sizeof(one));
+ crypto_shash_final(desc, hmac_out);
+
+ memcpy(output, hmac_out, output_size);
+
+ kfree(desc);
+ crypto_free_shash(tfm);
+ return 0;
+}
+
+static int nxp_hkdf_derive_secret(u8 secret[32], const char *label, size_t label_size,
+ u8 context[SHA256_DIGEST_SIZE],
+ u8 output[SHA256_DIGEST_SIZE])
+{
+ return nxp_hkdf_expand_label(secret, label, label_size, context, SHA256_DIGEST_SIZE,
+ output, SHA256_DIGEST_SIZE);
+}
+
+static int nxp_process_device_hello(struct hci_dev *hdev, struct nxp_tls_device_hello *msg)
+{
+ struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct nxp_tls_message_hdr *hdr;
+ u8 hs_traffic_secret[SHA256_DIGEST_SIZE];
+ u8 *shared_secret = NULL;
+ int ret;
+
+ if (!msg)
+ return -EINVAL;
+
+ hdr = &msg->hdr;
+
+ if (le32_to_cpu(hdr->magic) != NXP_TLS_MAGIC ||
+ le16_to_cpu(hdr->len) != sizeof(*msg) ||
+ hdr->message_id != NXP_TLS_DEVICE_HELLO ||
+ hdr->protocol_version != NXP_TLS_VERSION) {
+ bt_dev_err(hdev, "Invalid device hello header");
+ return -EINVAL;
+ }
+
+ shared_secret = kzalloc(32, GFP_KERNEL);
+ if (!shared_secret)
+ return -ENOMEM;
+
+ ret = crypto_shash_update(nxpdev->crypto.tls_handshake_hash_desc, (u8 *)msg,
+ DEVICE_HELLO_SIG_CUTOFF_POS);
+ if (ret)
+ goto fail;
+
+ ret = nxp_crypto_shash_final(nxpdev->crypto.tls_handshake_hash_desc,
+ nxpdev->crypto.handshake_h2_hash);
+ if (ret)
+ goto fail;
+
+ memcpy(nxpdev->crypto.ecdh_public, msg->pubkey, NXP_FW_ECDH_PUBKEY_SIZE);
+
+ ret = nxp_compute_shared_secret(nxpdev->crypto.kpp, nxpdev->crypto.ecdh_public,
+ shared_secret);
+ if (ret)
+ goto fail;
+
+ ret = nxp_hkdf_sha256_extract(NULL, 0, shared_secret, 32,
+ nxpdev->crypto.handshake_secret);
+ if (ret)
+ goto fail;
+
+ ret = nxp_hkdf_derive_secret(nxpdev->crypto.handshake_secret,
+ NXP_TLS_DEVICE_HS_TS_LABEL,
+ nxpdev->crypto.handshake_h2_hash,
+ hs_traffic_secret);
+ if (ret)
+ goto fail;
+
+ /* TODO: Verify Signature in Device Hello using ECDSA Public Key
+ * extracted from the FW metadata.
+ */
+
+fail:
+ memset(shared_secret, 0, 32);
+ kfree(shared_secret);
+ return ret;
+}
+
static int nxp_authenticate_device(struct hci_dev *hdev)
{
struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+ struct nxp_tls_device_hello *device_hello;
size_t desc_size = 0;
struct sk_buff *skb;
+ u8 *status;
int ret = 0;
nxpdev->crypto.tls_handshake_hash_tfm = crypto_alloc_shash("sha256", 0, 0);
@@ -1819,9 +2071,23 @@ static int nxp_authenticate_device(struct hci_dev *hdev)
goto free_kpp;
}
+ status = skb_pull_data(skb, 1);
+ if (*status)
+ goto free_skb;
+
+ if (skb->len != sizeof(struct nxp_tls_device_hello)) {
+ bt_dev_err(hdev, "Invalid Device Hello Length: %d", skb->len);
+ goto free_skb;
+ }
+
+ device_hello = skb_pull_data(skb, sizeof(*device_hello));
+ ret = nxp_process_device_hello(hdev, device_hello);
+ if (ret)
+ goto free_skb;
+
/* TODO: Implement actual TLS handshake protocol
* This will include:
- * 1. Handle Device hello message exchange
+ * 1. Send Host Finish TLS message
* 2. Master secret and traffic key derivation
*/
--
2.43.0
Powered by blists - more mailing lists