linux-usb.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v2 0/4] Support for usb authentication
@ 2025-07-11  8:41 nicolas.bouchinet
  2025-07-11  8:41 ` [RFC PATCH v2 1/4] usb: core: Introduce netlink usb authentication policy engine nicolas.bouchinet
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: nicolas.bouchinet @ 2025-07-11  8:41 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Luc Bonnafoux, Alan Stern, Kannappan R, Sabyrzhan Tasbolatov,
	Krzysztof Kozlowski, Stefan Eichenberger, Thomas Gleixner,
	Pawel Laszczak, Ma Ke, Jeff Johnson, Luc Bonnafoux,
	Nicolas Bouchinet, linux-kernel, linux-usb

We have been working on the implementation of the USB authentication
protocol in the kernel.

You can find our work here https://github.com/ANSSI-FR/usb_authentication.

It is still work in progress but we would like to start discussions
about the implementation design and its possible integration to the
Linux kernel.

Best regards,

Nicolas and Luc

---
USB peripheral authentication
=============================

USB peripherals are an important attack vector in personal computers and
pose a risk to the cyber security of companies and organizations.

The USB foundation has published a standard to allow the authentication
of USB peripherals ([1] and [2]). It defines a mechanism for the host to
request credentials and issue an authentication challenge to USB-2 or
USB-3 peripherals, either upon connection or later during the use of the
peripheral.

We currently envision the following use cases for USB authentication:

- company networks where computers and peripherals can be privately
  controlled and administered;
- USB cleaning or decontamination stations;
- individuals who want to prevent unauthorized device plug-in into their
  machine.

The implementation of this feature will obviously necessitate efforts
from both the kernel community and peripherals vendors. We believe that
providing an implementation of the host side of the protocol in the
Linux kernel will encourage constructors to include this feature in
their devices. On the other hand, we are working on implementing
reference code for embedded devices, notably for Zephyr OS.

Design
======

The USB authentication protocol is based on a simple signature
challenge. Devices hold between 1 and 8 pairs of private signing key and
x509 certificate. Hosts must possess a store of root Certificate
Authority certificates provided by device vendors.

The protocol exchange is driven by the host and can be decomposed into
three, mostly independent, phases:

- The Host can request a digest of each certificate owned by the
  peripheral.
- If the Host does not recognize the peripheral from one of its digests,
  it can read one or more certificates from the device until a valid one
  is found.
- The Host can issue an authentication challenge to the peripheral.

On the host side, this requires the following functions:

- handling the protocol exchange with the peripheral;
- X509 certificates validation and administration (root CA loading,
  certificate revocation…);
- cryptographic functions for the challenge (random number generation
  and ECDSA with the NIST P256 -secp256r1- curve);
- security policy management;
- authorization decision enforcement.

We chose to implement the authentication protocol exchange directly in
the kernel USB stack during the device enumeration. This is done by
first requesting the device BOS to detect its capacity at handling
authentication, then if supported starting the authentication sequence
with a digest request.

The implementation of the other functions is open to several design
alternatives, mainly based on their distribution between kernel and user
space. In this first implementation, we chose to implement most (all) of
the cryptographic functions, certificate management and security policy
management in user space in order to limit impact on the kernel side.
This allows for more personalization later on. The communication between
the kernel USB stack authentication function and user space is done via
a generic netlink socket.

Once a device has been authenticated, a per-device authenticated field
is update with the result of the authentication. The authenticated field
can be used to track the result of the authentication process in
userspace thanks to a sysfs exposed file.

The device enforcement point is done in the usb_probe_interface()
function. This allows for more complex security policy in userspace: the
user could manually authorize a device that failed the authentication or
manually deauthorize a device that was previously authenticated.

+----------+------------+-------------+
|          | authorized | !authorized |
+----------+------------+-------------+
| authent  |     OK     |     NOK     |
+----------+------------+-------------+
| !authent |     OK     |     NOK     |
+----------+------------+-------------+

If set to true, the authentication decision is enforced, the following
decision is made:

+----------+------------+-------------+
|          | authorized | !authorized |
+----------+------------+-------------+
| authent  |     OK     |     NOK     |
+----------+------------+-------------+
| !authent |    NOK     |     NOK     |
+----------+------------+-------------+

Note that combined with the CONFIG_USB_DEFAULT_AUTHORIZATION_MODE=2:
 - internal devices should be authorized and !authenticated => OK
 - external qemu dev-auth is !authorized and authenticated  => NOK at
   first but then authorization can be granted via sysfs.
 - external qemu non auth dev is !authorized and !authenticated => NOK
   and authorization can be granted via sysfs

The default enforcement decision can be configured thanks to the new
USB_AUTHENTICATION_ENFORCE configuration option and can be overridden
using the usbcore.enforce_authentication command line or module
parameter.

Limitations
===========

The USB authentication protocol come with some inherent limitations, [3]
does a good job at describing most of them. During the implementation,
we also found that the value encoding of the Validity field in the x509
certificate differs from the RFC5280 [4]. This has prevented us from
using the x509 parser included in the Linux kernel or OpenSSL, we chose
to use the mbedtls library instead [5]. This obviously does not prevent
others to replace it with their preferred implementation. It will also
open discussions on the protocol enhancement.

The architectural choice to place most of the cryptographic and security
management functions in user space comes with its own limitations.

First it introduces a dependency on the user space program availability.
It will probably be necessary to introduce a fail-safe mechanism if the
authentication can not be completed. Also, during early boot stages the
user space service will be needed in one form or another in the
initramfs.

The second limitation is that the device initialization process is
paused multiple times. Each time, the hub lock is released in order not
to block the rest of the stack; and then reacquired when a response has
been received from user space. The resuming of the operation on the
device must be done with great care.

Last, we do not yet interface properly with the rest of the usb stack
and thus do not enforce a strict control of the two authenticated and
authorized fields. Other sections of the kernel or userspace are able to
overwrite those fields using the sysfs exposed files for example.

Status
======

The current kernel implementation of the USB authentication protocol is
experimental and has the following limitations:

- It does not yet handle all possible protocol errors.
- It has been tested with a QEMU mock device, but tests with real
  hardware are still in progress. As such, the over-the-wire protocol
  has not yet been fully validated.
- The kernel/user space communication has not yet been completely
  validated, including the interruption of the worker thread and its
  resuming.
- Device authorization and deauthorization has not been completely
  implemented.
- It lacks an overall documentation and test suite.

Upstreaming plans
=================

Our current kernel patch is obviously a work-in-progress and not yet
ready for merging. We feel it is best to start a discussion on the
architectural choices and gather early comments that could be used to
improve the design.

Concerning the user space functions, they are currently implemented in a
small independent executable as a proof-of-concept. In the future,
integrating it in existing larger projects, like USBGuard [6], would
allow presenting a homogeneous USB administration interface to the user.

Reviewing this RFC
==================

We would like to get comments on the proposed architectural choices
regarding the repartition of functions between kernel and user space and
on the implementation in the USB stack, mostly concerning the releasing
and reacquiring the hub lock multiple times during the authentication
process.

Testing this RFC
================

You can find in the following repository [7] the necessary code for
creating a test environment:

- the Linux kernel patches;
- a python utility to generate a small PKI for device enrollment;
- a C minimalist service to implement the USB policy engine;
- patches for QEMU to implement a mock USB device with the
  authentication capability;
- a testbed to compile and test the project.

References
==========

- [1] “Universal Serial Bus Security Foundation Specification”, Revision
  1.0 with ECN and Errata through January 7, 2019
- [2] “Universal Serial Bus Type-C Authentication Specification”,
  Revision 1.0 with ECN and Errata through January 7, 2019
- [3] J. Tian, N. Scaife, D. Kumar, M. Bailey, A. Bates and K. Butler,
  "SoK: "Plug & Pray" Today – Understanding USB Insecurity in Versions 1
  Through C," 2018 IEEE Symposium on Security and Privacy (SP), San
  Francisco, CA, USA, 2018, pp. 1032-1047, doi: 10.1109/SP.2018.00037
- [4] RFC 5280, Internet X.509 Public Key Infrastructure Certificate and
  Certificate Revocation List (CRL) Profile, May 2008
- [5] https://www.trustedfirmware.org/projects/mbed-tls/
- [6] https://usbguard.github.io/
- [7] https://github.com/ANSSI-FR/usb_authentication

Signed-off-by: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Alan Stern <stern@rowland.harvard.edu>
Cc: Kannappan R <r.kannappan@intel.com>
Cc: Sabyrzhan Tasbolatov <snovitoll@gmail.com>
Cc: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Cc: Stefan Eichenberger <stefan.eichenberger@toradex.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Pawel Laszczak <pawell@cadence.com>
Cc: Ma Ke <make_ruc2021@163.com>
Cc: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
Cc: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
Cc: Luc Bonnafoux <luc.bonnafoux@oss.cyber.gouv.fr>
Cc: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>
Cc: linux-kernel@vger.kernel.org
Cc: linux-usb@vger.kernel.org
Signed-off-by: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>

---
Changes in v2:
- Fix code documentation to match kernel-doc format
- General coding style fixes as suggested in v1
- Added netlink socket configuration checks when receiving messages from
  userspace.
- Moved usb_authenticate_device() after device enumeration
- Changed the way authentication enforcement is handled.
  - If the device is authenticated, the device authenticated field is
    set to 1.
  - Added an authenticated file in sysfs that reflects the device
    authenticated field.
  - Enforcement Kconfig and cmdline configurations has been added.
  - Sysctls has been added to permit timeouts values.
- Removed USBAUTH_CMD_RESP_REMOVE_DEV netlink command
- The Kconfig commit has been squashed in the commit that plugs of the
  authentication in the usb stack.
- Link to v1: https://lore.kernel.org/r/20250620-usb_authentication-v1-0-0d92261a5779@ssi.gouv.fr

---
Nicolas Bouchinet (4):
      usb: core: Introduce netlink usb authentication policy engine
      usb: core: Introduce usb authentication feature
      usb: core: Plug the usb authentication capability
      usb: core: Add sysctl to configure authentication timeouts

 Documentation/usb/authentication.rst      |   15 +
 Documentation/usb/index.rst               |    1 +
 drivers/usb/core/Kconfig                  |   26 +
 drivers/usb/core/Makefile                 |    5 +
 drivers/usb/core/authent.c                |  586 ++++++++++++++++
 drivers/usb/core/authent.h                |  192 ++++++
 drivers/usb/core/authent_netlink.c        | 1039 +++++++++++++++++++++++++++++
 drivers/usb/core/authent_netlink.h        |   33 +
 drivers/usb/core/config.c                 |   22 +-
 drivers/usb/core/driver.c                 |   31 +
 drivers/usb/core/hub.c                    |    5 +
 drivers/usb/core/sysctl.c                 |   55 ++
 drivers/usb/core/sysfs.c                  |   16 +
 drivers/usb/core/usb.c                    |   16 +
 include/linux/usb.h                       |   11 +
 include/uapi/linux/usb/usb_auth_netlink.h |   70 ++
 16 files changed, 2121 insertions(+), 2 deletions(-)
---
base-commit: fc85704c3dae5ac1cb3c94045727241cd72871ff
change-id: 20250620-usb_authentication-333bda6f33fe

Best regards,
-- 
Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [RFC PATCH v2 1/4] usb: core: Introduce netlink usb authentication policy engine
  2025-07-11  8:41 [RFC PATCH v2 0/4] Support for usb authentication nicolas.bouchinet
@ 2025-07-11  8:41 ` nicolas.bouchinet
  2025-07-11  8:41 ` [RFC PATCH v2 2/4] usb: core: Introduce usb authentication feature nicolas.bouchinet
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: nicolas.bouchinet @ 2025-07-11  8:41 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Luc Bonnafoux, Alan Stern, Kannappan R, Sabyrzhan Tasbolatov,
	Krzysztof Kozlowski, Stefan Eichenberger, Thomas Gleixner,
	Pawel Laszczak, Ma Ke, Jeff Johnson, Luc Bonnafoux,
	Nicolas Bouchinet, linux-kernel, linux-usb

From: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>

The usb authentication feature needs a policy engine in order to
authorize or deny usb devices based on a user defined policy.

In order to reduce the attack surface and in-kernel complexity, policy
management, crypto operations and complex parsing have been implemented
in userspace using the generic netlink API. The full authentication
protocol is kernel driven.

The following unicast netlink commands have been defined in order to
fulfill device authentication :

- USBAUTH_CMD_REGISTER

This is the beginning of any authentication. The kernel first wait for
the userspace service to connect to the socket using the
`USBAUTH_CMD_REGISTER` netlink command.
Upon connection, the kernel check that the userspace service has the
`CAP_SYS_ADMIN` capability beforing enrolling the service. Only one
userspace service can be registered.

- USBAUTH_CMD_CHECK_DIGEST

The kernel then sends a `USBAUTH_CMD_CHECK_DIGEST` netlink command to
the policy engine to be verified. The policy engine checks if the device
ceritificates has already been encountered.

- USBAUTH_CMD_RESP_DIGEST

After the policy engine has received an usb device certificate digest
list from kernel, it needs to reply if it knows one of them using the
`USBAUTH_CMD_RESP_DIGEST` netlink command.

- USBAUTH_CMD_CHECK_CERTIFICATE

The kernel then sends a `USBAUTH_CMD_CHECK_CERTIFICATE` netlink command
to the policy engine. Each command contains one certificate chain. The
policy engine verifies if the device certificate chain is trusted.

- USBAUTH_CMD_RESP_CERTIFICATE

After checking the certificate chain, the policy engine sends a
`USBAUTH_CMD_RESP_CERTIFICATE` response. It tells the kernel if the
device certificate chain is trusted and thus if the device
authentication should continue.

Once device has been validated either through the digest or certificate
chain validation, an authentication session is started and a device ID
is associated for this session. The ID will then be used in all the
following commands.

- USBAUTH_CMD_GEN_NONCE

Kernel then asks for a nonce generation in order to challenge the device
using the `USBAUTH_GEN_NONCE` netlink command.

- USBAUTH_CMD_RESP_GEN_NONCE

When the nonce has been generated by the policy engine it is sent back
to the kernel using the `USBAUTH_CMD_RESP_GEN_NONCE` netlink command.

- USBAUTH_CMD_CHECK_CHALL

Once the kernel has received a device challenge response, it forwards
the response to the policy engine for validation using the
`USBAUTH_CMD_CHECK_CHALL` netlink command.

- USBAUTH_CMD_RESP_CHECK_CHALL

The policy engine then verifies the challenge and replies its decision
to the kernel using the `USBAUTH_CMD_RESP_CHECK_CHALL` netlink command.

Co-developed-by: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
Signed-off-by: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
Signed-off-by: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>
---
 drivers/usb/core/authent_netlink.c        | 1031 +++++++++++++++++++++++++++++
 drivers/usb/core/authent_netlink.h        |   33 +
 include/uapi/linux/usb/usb_auth_netlink.h |   70 ++
 3 files changed, 1134 insertions(+)

diff --git a/drivers/usb/core/authent_netlink.c b/drivers/usb/core/authent_netlink.c
new file mode 100644
index 0000000000000000000000000000000000000000..9848f219e0e4807563f0f0432a0f1108cd6a0454
--- /dev/null
+++ b/drivers/usb/core/authent_netlink.c
@@ -0,0 +1,1031 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SPDX-FileCopyrightText: (C) 2025 ANSSI
+ *
+ * USB Authentication netlink interface
+ *
+ * Author: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
+ * Author: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>
+ *
+ */
+
+#include <linux/usb/ch11.h>
+#include <linux/sched.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/capability.h>
+#include <net/genetlink.h>
+#include <uapi/linux/usb/usb_auth_netlink.h>
+#include "authent.h"
+#include "authent_netlink.h"
+
+#define WAIT_USERSPACE_TIMEOUT 30
+#define WAIT_RESPONSE_TIMEOUT 300
+#define USBAUTH_MAX_RESP_SIZE 128
+
+/**
+ * struct usb_auth_req - Define an outstanding request between the kernel and userspace
+ *
+ * @used:	1 if the slot is currently used, access must be protected.
+ * @done:	1 if the response has been received, used as wait condition.
+ * @error:	userspace response error code.
+ * @resp:	arbitrary response buffer.
+ */
+struct usb_auth_req {
+	u8 used;
+	u8 done;
+	u8 error;
+	u8 resp[USBAUTH_MAX_RESP_SIZE];
+};
+
+static struct genl_family usbauth_genl_fam;
+
+/* TODO: add mutex for PID access */
+static u32 pol_eng_pid;
+static struct net *pol_eng_net;
+
+static wait_queue_head_t usb_req_wq;
+
+#define USBAUTH_MAX_OUTSTANDING_REQS USB_MAXCHILDREN
+/* Mutex is used to protect access to the `used` field */
+DEFINE_MUTEX(usb_auth_reqs_mutex);
+static struct usb_auth_req usb_auth_outstanding_reqs[USBAUTH_MAX_OUTSTANDING_REQS];
+
+////////////////////////////////////////////////////////////////////////////////
+// USB request utilities
+
+/**
+ * usb_auth_get_reqs_slot() - Find the first available slot in the outstanding requests array and
+ * reserve it.
+ *
+ * @index: [out] reserved slot index, valid if return equals 0
+ *
+ * Context: this function will block on the request list mutex
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EXFULL	- too many outstanding requests
+ *
+ */
+static int usb_auth_get_reqs_slot(u32 *index)
+{
+	int ret = -EXFULL;
+	u32 i = 0;
+
+	mutex_lock(&usb_auth_reqs_mutex);
+
+	/* Take the first available slot */
+	for (i = 0; i < USBAUTH_MAX_OUTSTANDING_REQS; i++) {
+		if (usb_auth_outstanding_reqs[i].used == 0) {
+			usb_auth_outstanding_reqs[i].used = 1;
+			usb_auth_outstanding_reqs[i].done = 0;
+			usb_auth_outstanding_reqs[i].error = USBAUTH_OK;
+			memset(usb_auth_outstanding_reqs[i].resp, 0,
+			       USBAUTH_MAX_RESP_SIZE);
+			*index = i;
+			ret = 0;
+			break;
+		}
+	}
+
+	mutex_unlock(&usb_auth_reqs_mutex);
+
+	return ret;
+}
+
+/**
+ * usb_auth_release_reqs_slot() - release a request slot
+ *
+ * @index : [in] slot index to be released
+ *
+ * context: this function will block on the request list mutex
+ */
+static void usb_auth_release_reqs_slot(const u32 index)
+{
+	mutex_lock(&usb_auth_reqs_mutex);
+
+	usb_auth_outstanding_reqs[index].used = 0;
+
+	mutex_unlock(&usb_auth_reqs_mutex);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Generic netlink socket utilities
+
+/**
+ * usb_auth_register_req_doit() - Handle a registration request from userspace
+ *
+ * @skb:	[in] Netlink socket buffer
+ * @info:	[in] Generic Netlink receiving information
+ *
+ * It will overwrite the current userspace registered PID with the one provided
+ * in the request.
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EPERM	- Permission denied for netlink usage
+ * * %-ENOMEM	- if message creation failed
+ * * %-EMSGSIZE	- if message creation failed
+ */
+static int usb_auth_register_req_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	int ret = 0;
+	void *hdr = NULL;
+	struct sk_buff *msg = NULL;
+
+	if (!netlink_capable(skb, CAP_SYS_ADMIN)) {
+		pr_err("%s: invalid permissions\n", __func__);
+		return -EPERM;
+	}
+
+	pol_eng_pid = info->snd_portid;
+	pol_eng_net = genl_info_net(info);
+
+	wake_up_all(&usb_req_wq);
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (msg == NULL) {
+		pr_err("%s: failed to allocate message buffer\n", __func__);
+		return -ENOMEM;
+	}
+
+	hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
+			  &usbauth_genl_fam, 0, USBAUTH_CMD_REGISTER);
+	if (hdr == NULL) {
+		pr_err("%s: failed to create genetlink header\n", __func__);
+		nlmsg_free(msg);
+		return -EMSGSIZE;
+	}
+
+	genlmsg_end(msg, hdr);
+
+	ret = genlmsg_reply(msg, info);
+
+	return ret;
+}
+
+/**
+ * usb_auth_digest_resp_doit() - Handle a CHECK_DIGEST response from userspace
+ *
+ * @skb:	[in] Netlink socket buffer
+ * @info:	[in] Generic Netlink receiving information
+ *
+ * The response must contain:
+ *  - USBAUTH_A_REQ_ID
+ *  - USBAUTH_A_ERROR_CODE
+ *  - USBAUTH_A_DEV_ID
+ *  - USBAUTH_A_KNOWN
+ *  - USBAUTH_A_BLOCKED
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EPERM	- Permission denied for netlink usage.
+ * * %-ECOMM	- Netlink communication failure
+ * * %-EINVAL	- Invalid value in messages
+ */
+static int usb_auth_digest_resp_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	u32 index = 0;
+
+	if (!netlink_capable(skb, CAP_SYS_ADMIN)) {
+		pr_err("%s: invalid permissions\n", __func__);
+		return -EPERM;
+	}
+
+	if (!pol_eng_pid || !pol_eng_net) {
+		pr_err("%s: No policy engine registered\n", __func__);
+		return -ECOMM;
+	}
+
+	if (info->snd_portid != pol_eng_pid) {
+		pr_err("Sender id differ from registered policy engine\n");
+		return -EPERM;
+	}
+
+	if (!info->attrs[USBAUTH_A_REQ_ID]) {
+		pr_err("%s: invalid response: no req ID\n", __func__);
+		return -EINVAL;
+	}
+
+	index = nla_get_u32(info->attrs[USBAUTH_A_REQ_ID]);
+
+	if (!info->attrs[USBAUTH_A_ERROR_CODE]) {
+		pr_err("%s: invalid response: missing attributes\n", __func__);
+		usb_auth_outstanding_reqs[index].error = USBAUTH_INVRESP;
+		usb_auth_outstanding_reqs[index].done = 1;
+		return -EINVAL;
+	}
+
+	usb_auth_outstanding_reqs[index].error =
+		nla_get_u8(info->attrs[USBAUTH_A_ERROR_CODE]);
+
+	if (usb_auth_outstanding_reqs[index].error != USBAUTH_OK) {
+		pr_err("%s: response error\n", __func__);
+		usb_auth_outstanding_reqs[index].done = 1;
+		return -EINVAL;
+	}
+
+	if (!info->attrs[USBAUTH_A_DEV_ID] || !info->attrs[USBAUTH_A_KNOWN] ||
+	    !info->attrs[USBAUTH_A_BLOCKED]) {
+		pr_err("%s: invalid response: missing attributes\n", __func__);
+		usb_auth_outstanding_reqs[index].error = USBAUTH_INVRESP;
+		usb_auth_outstanding_reqs[index].done = 1;
+		return -EINVAL;
+	}
+
+	usb_auth_outstanding_reqs[index].resp[0] =
+		nla_get_u8(info->attrs[USBAUTH_A_KNOWN]);
+	usb_auth_outstanding_reqs[index].resp[1] =
+		nla_get_u8(info->attrs[USBAUTH_A_BLOCKED]);
+	((u32 *)usb_auth_outstanding_reqs[index].resp + 2)[0] =
+		nla_get_u32(info->attrs[USBAUTH_A_DEV_ID]);
+
+	usb_auth_outstanding_reqs[index].done = 1;
+
+	wake_up_all(&usb_req_wq);
+
+	return 0;
+}
+
+/**
+ * usb_auth_cert_resp_doit() - Handle a CHECK_CERTIFICATE response from userspace
+ *
+ * @skb:	[in] Netlink socket buffer
+ * @info:	[in] Generic Netlink receiving information
+ *
+ * The response must contain:
+ *  - USBAUTH_A_REQ_ID
+ *  - USBAUTH_A_ERROR_CODE
+ *  - USBAUTH_A_VALID
+ *  - USBAUTH_A_BLOCKED
+ *  - USBAUTH_A_DEV_ID
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EPERM	- Permission denied for netlink usage
+ * * %-ECOMM	- Netlink communication failure
+ * * %-EINVAL	- Invalid value in messages
+ *
+ */
+static int usb_auth_cert_resp_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	u32 index = 0;
+
+	if (!netlink_capable(skb, CAP_SYS_ADMIN)) {
+		pr_err("%s: invalid permissions\n", __func__);
+		return -EPERM;
+	}
+
+	if (!pol_eng_pid || !pol_eng_net) {
+		pr_err("%s: No policy engine registered", __func__);
+		return -ECOMM;
+	}
+
+	if (info->snd_portid != pol_eng_pid) {
+		pr_err("Sender id differ from registered policy engine\n");
+		return -EPERM;
+	}
+
+	if (!info->attrs[USBAUTH_A_REQ_ID]) {
+		pr_err("%s: invalid response: no req ID\n", __func__);
+		return -EINVAL;
+	}
+
+	index = nla_get_u32(info->attrs[USBAUTH_A_REQ_ID]);
+
+	if (!info->attrs[USBAUTH_A_ERROR_CODE]) {
+		pr_err("%s: invalid response: missing attributes\n", __func__);
+		usb_auth_outstanding_reqs[index].error = USBAUTH_INVRESP;
+		usb_auth_outstanding_reqs[index].done = 1;
+		return -EINVAL;
+	}
+
+	usb_auth_outstanding_reqs[index].error =
+		nla_get_u8(info->attrs[USBAUTH_A_ERROR_CODE]);
+
+	if (usb_auth_outstanding_reqs[index].error != USBAUTH_OK) {
+		pr_err("%s: response error\n", __func__);
+		usb_auth_outstanding_reqs[index].done = 1;
+		return -EINVAL;
+	}
+
+	if (!info->attrs[USBAUTH_A_DEV_ID] || !info->attrs[USBAUTH_A_VALID] ||
+	    !info->attrs[USBAUTH_A_BLOCKED]) {
+		pr_err("%s: invalid response: missing attributes\n", __func__);
+		usb_auth_outstanding_reqs[index].done = 1;
+		usb_auth_outstanding_reqs[index].error = USBAUTH_INVRESP;
+		return -EINVAL;
+	}
+
+	usb_auth_outstanding_reqs[index].resp[0] =
+		nla_get_u8(info->attrs[USBAUTH_A_VALID]);
+	usb_auth_outstanding_reqs[index].resp[1] =
+		nla_get_u8(info->attrs[USBAUTH_A_BLOCKED]);
+	((u32 *)usb_auth_outstanding_reqs[index].resp + 2)[0] =
+		nla_get_u32(info->attrs[USBAUTH_A_DEV_ID]);
+
+	usb_auth_outstanding_reqs[index].done = 1;
+
+	wake_up_all(&usb_req_wq);
+
+	return 0;
+}
+
+/**
+ * usb_auth_gen_nonce_doit() - Handle a GEN_NONCE response from userspace
+ *
+ * @skb:	[in] Netlink socket buffer
+ * @info:	[in] Generic Netlink receiving information
+ *
+ * The response must contain:
+ *  - USBAUTH_A_REQ_ID
+ *  - USBAUTH_A_ERROR_CODE
+ *  - USBAUTH_A_NONCE
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EPERM	- Permission denied for netlink usage
+ * * %-ECOMM	- Netlink communication failure
+ * * %-EINVAL	- Invalid value in messages
+ */
+static int usb_auth_gen_nonce_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	u32 index = 0;
+
+	if (!netlink_capable(skb, CAP_SYS_ADMIN)) {
+		pr_err("%s: invalid permissions\n", __func__);
+		return -EPERM;
+	}
+
+	if (!pol_eng_pid || !pol_eng_net) {
+		pr_err("%s: No policy engine registered", __func__);
+		return -ECOMM;
+	}
+
+	if (info->snd_portid != pol_eng_pid) {
+		pr_err("Sender id differ from registered policy engine\n");
+		return -EPERM;
+	}
+
+	if (!info->attrs[USBAUTH_A_REQ_ID]) {
+		pr_err("%s: invalid response: no req ID\n", __func__);
+		return -EINVAL;
+	}
+
+	index = nla_get_u32(info->attrs[USBAUTH_A_REQ_ID]);
+
+	if (!info->attrs[USBAUTH_A_ERROR_CODE]) {
+		pr_err("%s: invalid response: missing attributes\n", __func__);
+		usb_auth_outstanding_reqs[index].error = USBAUTH_INVRESP;
+		usb_auth_outstanding_reqs[index].done = 1;
+		return -EINVAL;
+	}
+
+	usb_auth_outstanding_reqs[index].error =
+		nla_get_u8(info->attrs[USBAUTH_A_ERROR_CODE]);
+
+	if (usb_auth_outstanding_reqs[index].error != USBAUTH_OK) {
+		pr_err("%s: response error\n", __func__);
+		usb_auth_outstanding_reqs[index].done = 1;
+		return -EINVAL;
+	}
+
+	if (!info->attrs[USBAUTH_A_NONCE]) {
+		pr_err("%s: invalid response: missing attributes\n", __func__);
+		usb_auth_outstanding_reqs[index].error = USBAUTH_INVRESP;
+		usb_auth_outstanding_reqs[index].done = 1;
+		return -EINVAL;
+	}
+
+	nla_memcpy(usb_auth_outstanding_reqs[index].resp,
+				info->attrs[USBAUTH_A_NONCE],
+				USBAUTH_NONCE_SIZE);
+	usb_auth_outstanding_reqs[index].done = 1;
+	wake_up_all(&usb_req_wq);
+
+	return 0;
+}
+
+/**
+ * usb_auth_check_chall_doit() - Handle a CHECK_CHALL response from userspace
+ *
+ * @skb:	[in] Netlink socket buffer
+ * @info:	[in] Generic Netlink receiving information
+ *
+ * The response must contain:
+ *  - USBAUTH_A_REQ_ID
+ *  - USBAUTH_A_ERROR_CODE
+ *  - USBAUTH_A_VALID
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EPERM	- Permission denied for netlink usage
+ * * %-ECOMM	- Netlink communication failure
+ * * %-EINVAL	- Invalid value in messages
+ */
+static int usb_auth_check_chall_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	u32 index = 0;
+
+	if (!netlink_capable(skb, CAP_SYS_ADMIN)) {
+		pr_err("%s: invalid permissions\n", __func__);
+		return -EPERM;
+	}
+
+	if (!pol_eng_pid || !pol_eng_net) {
+		pr_err("%s: No policy engine registered", __func__);
+		return -ECOMM;
+	}
+
+	if (info->snd_portid != pol_eng_pid) {
+		pr_err("Sender id differ from registered policy engine\n");
+		return -EPERM;
+	}
+
+	if (!info->attrs[USBAUTH_A_REQ_ID]) {
+		pr_err("%s: invalid response: no req ID\n", __func__);
+		return -EINVAL;
+	}
+
+	index = nla_get_u32(info->attrs[USBAUTH_A_REQ_ID]);
+
+	if (!info->attrs[USBAUTH_A_ERROR_CODE]) {
+		pr_err("%s: invalid response: missing attributes\n", __func__);
+		usb_auth_outstanding_reqs[index].error = USBAUTH_INVRESP;
+		usb_auth_outstanding_reqs[index].done = 1;
+		return -EINVAL;
+	}
+
+	usb_auth_outstanding_reqs[index].error =
+		nla_get_u8(info->attrs[USBAUTH_A_ERROR_CODE]);
+
+	if (usb_auth_outstanding_reqs[index].error != USBAUTH_OK) {
+		pr_err("%s: response error\n", __func__);
+		usb_auth_outstanding_reqs[index].done = 1;
+		return -EINVAL;
+	}
+
+	if (!info->attrs[USBAUTH_A_VALID]) {
+		pr_err("%s: invalid response: missing attributes\n", __func__);
+		usb_auth_outstanding_reqs[index].error = USBAUTH_INVRESP;
+		usb_auth_outstanding_reqs[index].done = 1;
+		return -EINVAL;
+	}
+
+	usb_auth_outstanding_reqs[index].resp[0] =
+		nla_get_u8(info->attrs[USBAUTH_A_VALID]);
+	usb_auth_outstanding_reqs[index].done = 1;
+	wake_up_all(&usb_req_wq);
+
+	return 0;
+}
+
+static struct nla_policy usbauth_attr_pol[USBAUTH_A_MAX + 1] = {
+	[USBAUTH_A_REQ_ID]  = {.type = NLA_U32,},
+	[USBAUTH_A_DEV_ID]  = {.type = NLA_U32,},
+	[USBAUTH_A_DIGEST] = {.type = NLA_UNSPEC, .len = USBAUTH_DIGEST_SIZE,},
+	[USBAUTH_A_DIGESTS] = {.type = NLA_UNSPEC, .len = USBAUTH_DIGESTS_SIZE,},
+	[USBAUTH_A_SLOT_MASK]  = {.type = NLA_U8,},
+	[USBAUTH_A_KNOWN]  = {.type = NLA_U8,},
+	[USBAUTH_A_BLOCKED]  = {.type = NLA_U8,},
+	[USBAUTH_A_VALID]  = {.type = NLA_U8,},
+	[USBAUTH_A_CERTIFICATE] = {.type = NLA_UNSPEC, .max = USBAUTH_MAX_CERT_SIZE,},
+	[USBAUTH_A_CERT_LEN] = {.type = NLA_U32},
+	[USBAUTH_A_ROUTE] = {.type = NLA_U32},
+	[USBAUTH_A_NONCE] = {.type = NLA_BINARY, .len = USBAUTH_NONCE_SIZE,},
+	[USBAUTH_A_CHALL] = {.type = NLA_UNSPEC, .len = USBAUTH_CHALL_SIZE,},
+	[USBAUTH_A_DESCRIPTOR] = {.type = NLA_UNSPEC, .len = USBAUTH_MAX_DESC_SIZE},
+	[USBAUTH_A_BOS] = {.type = NLA_UNSPEC, .len = USBAUTH_MAX_BOS_SIZE},
+	[USBAUTH_A_ERROR_CODE] = {.type = NLA_U8},
+};
+
+static struct genl_ops usbauth_genl_ops[] = {
+	{
+		.cmd = USBAUTH_CMD_REGISTER,
+		.policy = usbauth_attr_pol,
+		.doit = usb_auth_register_req_doit,
+	},
+	{
+		.cmd = USBAUTH_CMD_RESP_DIGEST,
+		.policy = usbauth_attr_pol,
+		.doit = usb_auth_digest_resp_doit,
+	},
+	{
+		.cmd = USBAUTH_CMD_RESP_CERTIFICATE,
+		.policy = usbauth_attr_pol,
+		.doit = usb_auth_cert_resp_doit,
+	},
+	{
+		.cmd = USBAUTH_CMD_RESP_GEN_NONCE,
+		.policy = usbauth_attr_pol,
+		.doit = usb_auth_gen_nonce_doit,
+	},
+	{
+		.cmd = USBAUTH_CMD_RESP_CHECK_CHALL,
+		.policy = usbauth_attr_pol,
+		.doit = usb_auth_check_chall_doit,
+	}
+};
+
+static struct genl_family usbauth_genl_fam = {
+	.name = USBAUTH_GENL_NAME,
+	.version = USBAUTH_GENL_VERSION,
+	.maxattr = USBAUTH_A_MAX,
+	.ops = usbauth_genl_ops,
+	.n_ops = ARRAY_SIZE(usbauth_genl_ops),
+	.mcgrps = NULL,
+	.n_mcgrps = 0,
+};
+
+/**
+ * usb_auth_init_netlink() - Initialises the netlink socket for userspace usb
+ * authentication policy engine to register.
+ *
+ * Returns:
+ * * %0 - OK
+ */
+
+int usb_auth_init_netlink(void)
+{
+	int ret = 0;
+	u8 i = 0;
+
+	for (i = 0; i < USBAUTH_MAX_OUTSTANDING_REQS; i++)
+		usb_auth_outstanding_reqs[i].used = 0;
+
+	init_waitqueue_head(&usb_req_wq);
+	ret = genl_register_family(&usbauth_genl_fam);
+	if (ret) {
+		pr_err("%s: failed to init netlink: %d\n",
+		       __func__, ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Policy engine API
+
+/**
+ * usb_policy_engine_check_digest - Check if a digest match a device
+ *
+ * @route:	[in]	Information on the device to construct the ID
+ * @digests:	[in]	USB Authentication digest, must be 256 B
+ * @mask:	[in]	USB Authentication slot mask
+ * @is_known:	[out]	1 at each index with a known digest, 0 otherwise
+ * @is_blocked: [out]	1 if the device is known and banned, 0 otherwise
+ * @id:		[out]	if is_known and !is_blocked then contains the device handle
+ *
+ * This function blocks until a response has been received from userspace or in
+ * case of timeout.
+ * The function blocks if no policy engine is registered with a timeout.
+ *
+ * Context: task context, might sleep.
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EINVAL	- if digest is NULL
+ * * %-ECOMM	- if userspace policy engine is not available or busy
+ *		  or message transmission failed
+ * * %-ENOMEM	- if message creation failed
+ * * %-EMSGSIZE	- if message creation failed
+ *
+ *
+ */
+int usb_policy_engine_check_digest(const u32 route, const u8 *const digests,
+	const u8 mask, u8 *is_known, u8 *is_blocked, u32 *id)
+{
+	int ret = -1;
+	void *hdr = NULL;
+	struct sk_buff *skb = NULL;
+	u32 index = 0;
+
+	if (digests == NULL) {
+		pr_err("%s: invalid inputs\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0, HZ * WAIT_USERSPACE_TIMEOUT)) {
+		pr_err("%s: userspace not available\n", __func__);
+		return -ECOMM;
+	}
+
+	if (usb_auth_get_reqs_slot(&index)) {
+		pr_err("%s: failed to get request slot\n", __func__);
+		return -ECOMM;
+	}
+	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (skb == NULL) {
+		pr_err("%s: failed to allocated buffer\n", __func__);
+		return -ENOMEM;
+	}
+
+	hdr = genlmsg_put(skb, 0, 0, &usbauth_genl_fam, 0,
+			  USBAUTH_CMD_CHECK_DIGEST);
+	if (hdr == NULL) {
+		pr_err("%s: failed to place header\n", __func__);
+		nlmsg_free(skb);
+		return -ENOMEM;
+	}
+
+	ret = nla_put_u32(skb, USBAUTH_A_REQ_ID, index);
+	if (ret) {
+		pr_err("%s: failed to place req ID\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	ret = nla_put_u32(skb, USBAUTH_A_ROUTE, route);
+	if (ret) {
+		pr_err("%s: failed to place route\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	ret = nla_put(skb, USBAUTH_A_DIGESTS, USBAUTH_DIGESTS_SIZE, digests);
+	if (ret) {
+		pr_err("%s: failed to place digests\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	ret = nla_put_u8(skb, USBAUTH_A_SLOT_MASK, mask);
+	if (ret) {
+		pr_err("%s: failed to place slot mask\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	genlmsg_end(skb, hdr);
+
+	ret = genlmsg_unicast(pol_eng_net, skb, pol_eng_pid);
+	if (ret != 0) {
+		pr_err("%s: failed to send message: %d\n",
+		       __func__, ret);
+		return -ECOMM;
+	}
+
+	if (!wait_event_timeout(usb_req_wq,
+				usb_auth_outstanding_reqs[index].done == 1,
+				HZ * WAIT_RESPONSE_TIMEOUT)) {
+		pr_err("%s: userspace response not available\n", __func__);
+		usb_auth_release_reqs_slot(index);
+		return -ECOMM;
+	}
+
+
+	if (usb_auth_outstanding_reqs[index].error == USBAUTH_INVRESP) {
+		pr_err("%s: userspace response error: %d\n",
+			__func__, usb_auth_outstanding_reqs[index].error);
+		usb_auth_release_reqs_slot(index);
+		return -ECOMM;
+	}
+
+	*is_known = usb_auth_outstanding_reqs[index].resp[0];
+	*is_blocked = usb_auth_outstanding_reqs[index].resp[1];
+	*id = ((u32 *)usb_auth_outstanding_reqs[index].resp + 2)[0];
+
+	usb_auth_release_reqs_slot(index);
+
+	return 0;
+}
+
+/**
+ * usb_policy_engine_check_cert_chain() - Check if a certificate chain is valid and authorized
+ *
+ * @route:	[in]	Information on the device to construct the ID
+ * @digest:	[in]	Digest corresponding to the certificate chain
+ * @chain:	[in]	Certificate chain to check, at most 4096 bytes
+ * @chain_len:	[in]	Certificate chain length
+ * @is_valid:	[out]	1 if the certificate chain can be validated
+ * @is_blocked:	[out]	1 if the chain is valid but one of the certificate is blocked
+ * @id:		[out]	If is_known and !is_blocked then contains the device handle
+ *
+ * A certificate chain is valid if it can be successfully verified with one of the
+ *  root CA in store.
+ * A certificate chain is blocked if one of the certificate of chain is blocked,
+ *  due to revocation, blacklist...
+ *
+ * Context: task context, might sleep.
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EINVAL	- if digest is NULL
+ * * %-ECOMM	- if userspace policy engine is not available or busy
+ *		  or message transmission failed
+ * * %-ENOMEM	- if message creation failed
+ * * %-EMSGSIZE	- if message creation failed
+ *
+ */
+
+int usb_policy_engine_check_cert_chain(const u32 route,
+	const u8 *const digest, const u8 *const chain,
+	const size_t chain_len, u8 *is_valid, u8 *is_blocked, u32 *id)
+{
+	int ret = -1;
+	void *hdr = NULL;
+	struct sk_buff *skb = NULL;
+	u32 index = 0;
+
+	if (chain == NULL || chain_len > USBAUTH_MAX_CERT_SIZE || digest == NULL) {
+		pr_err("%s: invalid inputs\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0, HZ * WAIT_USERSPACE_TIMEOUT)) {
+		pr_err("%s: userspace not available\n", __func__);
+		return -ECOMM;
+	}
+
+	if (usb_auth_get_reqs_slot(&index)) {
+		pr_err("%s: failed to get request slot\n", __func__);
+		return -ECOMM;
+	}
+	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (skb == NULL) {
+		pr_err("%s: failed to allocated buffer\n", __func__);
+		return -ENOMEM;
+	}
+
+	hdr = genlmsg_put(skb, 0, 0, &usbauth_genl_fam, 0,
+		USBAUTH_CMD_CHECK_CERTIFICATE);
+	if (hdr == NULL) {
+		pr_err("%s: failed to place header\n", __func__);
+		nlmsg_free(skb);
+		return -ENOMEM;
+	}
+
+	ret = nla_put_u32(skb, USBAUTH_A_REQ_ID, index);
+	if (ret) {
+		pr_err("%s: failed to place req ID\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	ret = nla_put_u32(skb, USBAUTH_A_ROUTE, route);
+	if (ret) {
+		pr_err("%s: failed to place route\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	ret = nla_put(skb, USBAUTH_A_DIGEST, USBAUTH_DIGEST_SIZE, digest);
+	if (ret) {
+		pr_err("%s: failed to place digest\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	ret = nla_put(skb, USBAUTH_A_CERTIFICATE, chain_len, chain);
+	if (ret) {
+		pr_err("%s: failed to place certificate\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	ret = nla_put_u32(skb, USBAUTH_A_CERT_LEN, chain_len);
+	if (ret) {
+		pr_err("%s: failed to place chain length\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	genlmsg_end(skb, hdr);
+
+	ret = genlmsg_unicast(pol_eng_net, skb, pol_eng_pid);
+	if (ret != 0) {
+		pr_err("%s: failed to send message: %d\n",
+		       __func__, ret);
+		return -ECOMM;
+	}
+
+	if (!wait_event_timeout(usb_req_wq,
+				usb_auth_outstanding_reqs[index].done == 1,
+				HZ * WAIT_RESPONSE_TIMEOUT)) {
+		pr_err("%s: userspace response not available\n", __func__);
+		usb_auth_release_reqs_slot(index);
+		return -ECOMM;
+	}
+
+	*is_valid = usb_auth_outstanding_reqs[index].resp[0];
+	*is_blocked = usb_auth_outstanding_reqs[index].resp[1];
+	*id = ((u32 *)usb_auth_outstanding_reqs[index].resp + 2)[0];
+
+	usb_auth_release_reqs_slot(index);
+
+	return 0;
+}
+
+/**
+ * usb_policy_engine_generate_challenge() - Generate a nonce for the authentication challenge
+ *
+ * @id:		[in]	Device ID
+ * @nonce:	[out]	32 bytes nonce buffer, caller allocated
+ *
+ * Context: task context, might sleep.
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EINVAL	- if digest is NULL
+ * * %-ECOMM	- if userspace policy engine is not available or busy
+ *                or message transmission failed
+ * * %-ENOMEM	- if message creation failed
+ * * %-EMSGSIZE	- if message creation failed
+ */
+int usb_policy_engine_generate_challenge(const u32 id, u8 *nonce)
+{
+	int ret = -1;
+	void *hdr = NULL;
+	struct sk_buff *skb = NULL;
+	u32 index = 0;
+
+	/* Arbitrary 30s wait before giving up */
+	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0, HZ * WAIT_USERSPACE_TIMEOUT)) {
+		pr_err("%s: userspace not available\n", __func__);
+		return -ECOMM;
+	}
+
+	if (usb_auth_get_reqs_slot(&index)) {
+		pr_err("%s: failed to get request slot\n", __func__);
+		return -ECOMM;
+	}
+	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (skb == NULL) {
+		pr_err("%s: failed to allocated buffer\n", __func__);
+		return -ENOMEM;
+	}
+
+	hdr = genlmsg_put(skb, 0, 0, &usbauth_genl_fam, 0,
+		USBAUTH_CMD_GEN_NONCE);
+	if (hdr == NULL) {
+		pr_err("%s: failed to place header\n", __func__);
+		nlmsg_free(skb);
+		return -ENOMEM;
+	}
+
+	ret = nla_put_u32(skb, USBAUTH_A_REQ_ID, index);
+	if (ret) {
+		pr_err("%s: failed to place req ID\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	ret = nla_put_u32(skb, USBAUTH_A_DEV_ID, id);
+	if (ret) {
+		pr_err("%s: failed to place dev ID\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	genlmsg_end(skb, hdr);
+
+	ret = genlmsg_unicast(pol_eng_net, skb, pol_eng_pid);
+	if (ret != 0) {
+		pr_err("%s: failed to send message: %d\n", __func__, ret);
+		return -ECOMM;
+	}
+
+	if (!wait_event_timeout(usb_req_wq,
+				usb_auth_outstanding_reqs[index].done == 1,
+				HZ * WAIT_RESPONSE_TIMEOUT)) {
+		pr_err("%s: userspace response not available\n", __func__);
+		usb_auth_release_reqs_slot(index);
+		return -ECOMM;
+	}
+
+	memcpy(nonce, usb_auth_outstanding_reqs[index].resp, USBAUTH_NONCE_SIZE);
+	usb_auth_release_reqs_slot(index);
+
+	return 0;
+}
+
+/**
+ * usb_policy_engine_check_challenge() - Validate the authentication challenge
+ *
+ * @id:			[in]	device handle
+ * @challenge:		[in]	challenge response, must be 204 bytes.
+ *				@challenge is the concatenation of :
+ *				message (140B) | signature (64B)
+ * @context:		[in]	usb device context
+ * @context_size:	[in]	usb device context size
+ * @is_valid:		[out]	1 if the signature is valid, 0 otherwise
+ *
+ * Check that the response challenge contains the right nonce
+ * Check that the device signature is valid
+ *
+ * Context: task context, might sleep.
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EINVAL	- if challenge, desc or bos is NULL or invalid parameter size
+ * * %-ECOMM	- if userspace policy engine is not available or busy
+ *		  or message transmission failed
+ * * %-ENOMEM	- if message creation failed
+ * * %-EMSGSIZE	- if message creation failed
+ */
+int usb_policy_engine_check_challenge(const u32 id,
+	const u8 *const challenge, const u8 *const context,
+	const size_t context_size, u8 *is_valid)
+{
+	int ret = -1;
+	void *hdr = NULL;
+	struct sk_buff *skb = NULL;
+	u32 index = 0;
+
+	if (challenge == NULL || context == NULL ||
+	      context_size > USBAUTH_MAX_BOS_SIZE) {
+		pr_err("%s: invalid inputs\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0, HZ * WAIT_USERSPACE_TIMEOUT)) {
+		pr_err("%s: userspace not available\n", __func__);
+		return -ECOMM;
+	}
+
+	if (usb_auth_get_reqs_slot(&index)) {
+		pr_err("%s: failed to get request slot\n", __func__);
+		return -ECOMM;
+	}
+	skb = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (skb == NULL) {
+		pr_err("%s: failed to allocated buffer\n", __func__);
+		return -ENOMEM;
+	}
+
+	hdr = genlmsg_put(skb, 0, 0, &usbauth_genl_fam, 0,
+		USBAUTH_CMD_CHECK_CHALL);
+	if (hdr == NULL) {
+		pr_err("%s: failed to place header\n", __func__);
+		nlmsg_free(skb);
+		return -ENOMEM;
+	}
+
+	ret = nla_put_u32(skb, USBAUTH_A_REQ_ID, index);
+	if (ret) {
+		pr_err("%s: failed to place req ID\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	ret = nla_put(skb, USBAUTH_A_CHALL, USBAUTH_CHALL_SIZE, challenge);
+	if (ret) {
+		pr_err("%s: failed to place challenge\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	ret = nla_put(skb, USBAUTH_A_DESCRIPTOR, context_size, context);
+	if (ret) {
+		pr_err("%s: failed to place descriptor\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	ret = nla_put_u32(skb, USBAUTH_A_DEV_ID, id);
+	if (ret) {
+		pr_err("%s: failed to place dev ID\n", __func__);
+		genlmsg_cancel(skb, hdr);
+		nlmsg_free(skb);
+		return ret;
+	}
+
+	genlmsg_end(skb, hdr);
+
+	ret = genlmsg_unicast(pol_eng_net, skb, pol_eng_pid);
+	if (ret != 0) {
+		pr_err("%s: failed to send message: %d\n",
+		       __func__, ret);
+		return -ECOMM;
+	}
+	if (!wait_event_timeout(usb_req_wq,
+				usb_auth_outstanding_reqs[index].done == 1,
+				HZ * WAIT_RESPONSE_TIMEOUT)) {
+		pr_err("%s: userspace response not available\n", __func__);
+		usb_auth_release_reqs_slot(index);
+		return -ECOMM;
+	}
+
+	*is_valid = usb_auth_outstanding_reqs[index].resp[0];
+	usb_auth_release_reqs_slot(index);
+
+	return 0;
+}
diff --git a/drivers/usb/core/authent_netlink.h b/drivers/usb/core/authent_netlink.h
new file mode 100644
index 0000000000000000000000000000000000000000..d458fcf620f2de9a00896d2c09babcdcc843d3f0
--- /dev/null
+++ b/drivers/usb/core/authent_netlink.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SPDX-FileCopyrightText: (C) 2025 ANSSI
+ *
+ * USB Authentication netlink interface
+ *
+ * Author: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
+ * Author: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>
+ *
+ */
+
+#ifndef __USB_CORE_AUTHENT_NETLINK_H_
+#define __USB_CORE_AUTHENT_NETLINK_H_
+
+int usb_auth_init_netlink(void);
+int usb_policy_engine_check_digest(const u32 route,
+				   const u8 *const digests,
+				   const u8 mask, u8 *is_known,
+				   u8 *is_blocked, u32 *id);
+int usb_policy_engine_check_cert_chain(const u32 route,
+				       const u8 *const digest,
+				       const u8 *const chain,
+				       const size_t chain_len,
+				       u8 *is_valid, u8 *is_blocked,
+				       u32 *id);
+int usb_policy_engine_generate_challenge(const u32 id, u8 *nonce);
+int usb_policy_engine_check_challenge(const u32 id,
+				      const u8 *const challenge,
+				      const u8 *const context,
+				      const size_t context_size,
+				      u8 *is_valid);
+
+#endif /* __USB_CORE_AUTHENT_NETLINK_H_ */
diff --git a/include/uapi/linux/usb/usb_auth_netlink.h b/include/uapi/linux/usb/usb_auth_netlink.h
new file mode 100644
index 0000000000000000000000000000000000000000..7ca49ebf6612f99742947c38903039d74a770e79
--- /dev/null
+++ b/include/uapi/linux/usb/usb_auth_netlink.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SPDX-FileCopyrightText: (C) 2025 ANSSI
+ *
+ * USB Authentication netlink interface definitions shared with userspace
+ *
+ * Author: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
+ * Author: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>
+ *
+ */
+
+#ifndef __USB_AUTHENT_NETLINK_H_
+#define __USB_AUTHENT_NETLINK_H_
+
+#define USBAUTH_GENL_NAME "usb_auth_genl"
+#define USBAUTH_GENL_VERSION 1
+
+/* Attributes */
+enum usbauth_genl_attrs {
+	USBAUTH_A_REQ_ID = 1,
+	USBAUTH_A_DEV_ID,
+	USBAUTH_A_DIGEST,
+	USBAUTH_A_DIGESTS,
+	USBAUTH_A_SLOT_MASK,
+	USBAUTH_A_KNOWN,
+	USBAUTH_A_BLOCKED,
+	USBAUTH_A_VALID,
+	USBAUTH_A_CERTIFICATE,
+	USBAUTH_A_CERT_LEN,
+	USBAUTH_A_ROUTE,
+	USBAUTH_A_NONCE,
+	USBAUTH_A_CHALL,
+	USBAUTH_A_DESCRIPTOR,
+	USBAUTH_A_BOS,
+	USBAUTH_A_ERROR_CODE,
+	__USBAUTH_A_MAX,
+};
+
+#define USBAUTH_MAX_DESC_SIZE 1024
+#define USBAUTH_MAX_BOS_SIZE  1024
+#define USBAUTH_DIGEST_SIZE   32
+#define USBAUTH_NONCE_SIZE    32
+#define USBAUTH_CHALL_SIZE    204
+#define USBAUTH_DIGESTS_SIZE  256
+#define USBAUTH_MAX_CERT_SIZE 4096
+
+#define USBAUTH_A_MAX (__USBAUTH_A_MAX - 1)
+
+/* Commands */
+enum usbauth_genl_cmds {
+	USBAUTH_CMD_REGISTER,
+	USBAUTH_CMD_CHECK_DIGEST,
+	USBAUTH_CMD_CHECK_CERTIFICATE,
+	USBAUTH_CMD_GEN_NONCE,
+	USBAUTH_CMD_CHECK_CHALL,
+	USBAUTH_CMD_RESP_DIGEST,
+	USBAUTH_CMD_RESP_CERTIFICATE,
+	USBAUTH_CMD_RESP_CREATE_DEV,
+	USBAUTH_CMD_RESP_GEN_NONCE,
+	USBAUTH_CMD_RESP_CHECK_CHALL,
+	__USBAUTH_CMD_MAX,
+};
+
+#define USBAUTH_CMD_MAX (__USBAUTH_CMD_MAX - 1)
+
+/* Error codes */
+#define USBAUTH_OK 0
+#define USBAUTH_INVRESP 1
+
+#endif /* __USB_AUTHENT_NETLINK_H_ */

-- 
2.50.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH v2 2/4] usb: core: Introduce usb authentication feature
  2025-07-11  8:41 [RFC PATCH v2 0/4] Support for usb authentication nicolas.bouchinet
  2025-07-11  8:41 ` [RFC PATCH v2 1/4] usb: core: Introduce netlink usb authentication policy engine nicolas.bouchinet
@ 2025-07-11  8:41 ` nicolas.bouchinet
  2025-07-11  8:41 ` [RFC PATCH v2 3/4] usb: core: Plug the usb authentication capability nicolas.bouchinet
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: nicolas.bouchinet @ 2025-07-11  8:41 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Luc Bonnafoux, Alan Stern, Kannappan R, Sabyrzhan Tasbolatov,
	Krzysztof Kozlowski, Stefan Eichenberger, Thomas Gleixner,
	Pawel Laszczak, Ma Ke, Jeff Johnson, Luc Bonnafoux,
	Nicolas Bouchinet, linux-kernel, linux-usb

From: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>

This includes the usb authentication protocol implementation bulk
exposed by the public usb_authenticate_device function.

The protocol exchange is driven by the host and can be decomposed into
three, mostly independent, phases:

- The Host can request a digest of each certificate own by the
  peripheral.
- If the Host does not recognize the peripheral from one of its digests,
  it can read one or more certificates from the device until a valid one
  is found.
- The Host can issue an authentication challenge to the peripheral.

The usb_authenticate_device function implements the usb authentication
protocol.

It implements the three phases of the protocol :

First, it needs to communicate with the usb device in order to fetch its
certificate digests (usb_authent_req_digest).
Then if the device is unknown, the host fetches the device certificate
chains (usb_authent_read_cert_part, usb_authent_read_certificate).
Once at least a digest has been recognized or a certificate chain has
been validated the host challenges the device in order to authenticate
it (usb_authent_challenge_dev).

It also needs to communicate with a policy engine using the following
functions :

usb_policy_engine_check_digest
usb_policy_engine_check_cert_chain
usb_policy_engine_generate_challenge
usb_policy_engine_check_challenge

Co-developed-by: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
Signed-off-by: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
Signed-off-by: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>
---
 drivers/usb/core/authent.c | 586 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/core/authent.h | 178 ++++++++++++++
 2 files changed, 764 insertions(+)

diff --git a/drivers/usb/core/authent.c b/drivers/usb/core/authent.c
new file mode 100644
index 0000000000000000000000000000000000000000..9a2d4ef27ad26d802f2ba65cb8fe72474eb93464
--- /dev/null
+++ b/drivers/usb/core/authent.c
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SPDX-FileCopyrightText: (C) 2025 ANSSI
+ *
+ * USB Authentication protocol implementation
+ *
+ * Author: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
+ * Author: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/quirks.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <asm/byteorder.h>
+#include "authent_netlink.h"
+#include "authent.h"
+
+/**
+ * usb_authent_req_digest() - Check if device is known via its digest
+ *
+ * @dev:	[in]		pointer to the usb device to query.
+ * @buffer:	[in, out]	buffer to hold request data.
+ * @digest:	[out]		device digest.
+ * @mask:	[out]		USB Authentication slot mask
+ *
+ * Context: task context, might sleep.
+ *
+ * This function sends a digest request to the usb device.
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-ECOMM	- Failed to send or received a message to the device
+ * * %-EINVAL	- If buffer or mask is NULL
+ */
+
+static int usb_authent_req_digest(struct usb_device *dev, u8 *const buffer,
+				  u8 digest[USBAUTH_DIGESTS_SIZE], u8 *mask)
+{
+	int ret = 0;
+	struct usb_authent_digest_resp *digest_resp = NULL;
+
+	if (buffer == NULL || mask == NULL) {
+		dev_err(&dev->dev, "invalid arguments\n");
+		return -EINVAL;
+	}
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), AUTH_IN, USB_DIR_IN,
+			      (USB_SECURITY_PROTOCOL_VERSION << 8) +
+				      USB_AUTHENT_DIGEST_REQ_TYPE,
+			      0, buffer, USBAUTH_DIGEST_RSP_SIZE, USB_CTRL_GET_TIMEOUT);
+	if (ret != USBAUTH_DIGEST_RSP_SIZE) {
+		dev_err(&dev->dev, "Failed to get digest: %d\n", ret);
+		ret = -ECOMM;
+		goto exit;
+	}
+
+	digest_resp = (struct usb_authent_digest_resp *)buffer;
+	*mask = digest_resp->slotMask;
+	memcpy(digest, digest_resp->digests, USBAUTH_DIGESTS_SIZE);
+
+	ret = 0;
+
+exit:
+
+	return ret;
+}
+
+/*
+ * This structure is sent as is on USB BUS and thus needs to be packed.
+ */
+struct usb_auth_cert_req {
+	u16 offset;
+	u16 length;
+} __packed;
+
+/**
+ * usb_auth_read_cert_part() -  Request a specific part of a certificate chain from the device
+ *
+ * @dev:	[in]		handle to the USB device
+ * @buffer:	[in,out]	buffer used for communication, caller allocated
+ * @slot:	[in]		slot in which to read the certificate
+ * @offset:	[in]		offset at which the certificate fragment must be read
+ * @length:	[in]		length of the certificate fragment to read
+ * @cert_part:	[out]		buffer to hold the fragment, caller allocated
+ *
+ * Context: task context, might sleep
+ *
+ * Returns:
+ * * %x00	- OK
+ * * %-ECOMM	- failed to send or receive a message to the device
+ * * %-EINVAL	- if buffer or cert_part is NULL
+ */
+static int usb_auth_read_cert_part(struct usb_device *dev, u8 *const buffer,
+				   const u8 slot, const u16 offset,
+				   const u16 length, u8 *cert_part)
+{
+	struct usb_auth_cert_req cert_req = {0};
+	int ret = -1;
+
+	if (buffer == NULL || cert_part == NULL) {
+		dev_err(&dev->dev, "invalid argument\n");
+		return -EINVAL;
+	}
+
+	cert_req.offset = cpu_to_le16(offset);
+	cert_req.length = cpu_to_le16(length);
+
+	memcpy(buffer, &cert_req, sizeof(struct usb_auth_cert_req));
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), AUTH_OUT,
+			      USB_DIR_OUT,
+			      (USB_SECURITY_PROTOCOL_VERSION << 8) +
+				      USB_AUTHENT_CERTIFICATE_REQ_TYPE,
+			      (slot << 8), buffer,
+			      sizeof(struct usb_auth_cert_req),
+			      USB_CTRL_GET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(&dev->dev, "Failed to send certificate request: %d\n", ret);
+		ret = -ECOMM;
+		goto cleanup;
+	}
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), AUTH_IN, USB_DIR_IN,
+			      (USB_SECURITY_PROTOCOL_VERSION << 8) +
+				      USB_AUTHENT_CERTIFICATE_RESP_TYPE,
+			      (slot << 8), buffer, length + 4,
+			      USB_CTRL_GET_TIMEOUT);
+	if (ret != (length + 4)) {
+		dev_err(&dev->dev, "Failed to get certificate from peripheral: %d\n", ret);
+		ret = -ECOMM;
+		goto cleanup;
+	}
+
+	/* TODO: parse received header */
+	memcpy(cert_part, buffer + 4, length);
+
+	ret = 0;
+
+cleanup:
+
+	return ret;
+}
+
+/**
+ * usb_authent_read_certificate() - Read a device certificate
+ *
+ * @dev:	[in]		pointer to the usb device to query
+ * @buffer:	[in, out]	buffer to hold request data, caller allocated
+ * @slot:	[in]		certificate chain to be read
+ * @cert_der:	[out]		buffer to hold received certificate chain
+ * @cert_len:	[out]		length of received certificate
+ *
+ * Context: task context, might sleep.
+ *
+ * Returns:
+ * * %0	- OK
+ * * %-EINVAL - NULL pointer or invalid slot value
+ * * %-ECOMM  - failed to send request to device
+ * * %-ENOMEM - failed to allocate memory for certificate
+ *
+ */
+static int usb_authent_read_certificate(struct usb_device *dev, u8 *const buffer,
+					u8 slot, u8 **cert_der, size_t *cert_len)
+{
+	u16 read_offset = 0;
+	u16 read_length = 0;
+	u8 chain_part[64] = {0};
+
+	if (slot >= 8 || buffer == NULL || cert_der == NULL || cert_len == NULL) {
+		dev_err(&dev->dev, "invalid arguments\n");
+		return -EINVAL;
+	}
+
+	if (usb_auth_read_cert_part(dev, buffer, slot, 0,
+				    USBAUTH_CHAIN_HEADER_SIZE,
+				    chain_part) != 0) {
+		dev_err(&dev->dev, "Failed to get first certificate part\n");
+		return -ECOMM;
+	}
+
+	*cert_len = le16_to_cpu(((u16 *)chain_part)[0]);
+
+	*cert_der = kzalloc(*cert_len, GFP_KERNEL);
+	if (!(*cert_der))
+		return -ENOMEM;
+
+	memcpy(*cert_der, chain_part, USBAUTH_CHAIN_HEADER_SIZE);
+	read_offset = USBAUTH_CHAIN_HEADER_SIZE;
+
+	while (read_offset < *cert_len) {
+		read_length = (*cert_len - read_offset) >= 64 ? 64 : (*cert_len - read_offset);
+
+		if (usb_auth_read_cert_part(dev, buffer, slot, read_offset,
+					    read_length, chain_part) != 0) {
+			dev_err(&dev->dev, "USB AUTH: Failed to get certificate part\n");
+			return -ECOMM;
+		}
+
+		memcpy(*cert_der + read_offset, chain_part, read_length);
+		read_offset += read_length;
+	}
+
+	return 0;
+}
+
+/**
+ * usb_authent_challenge_dev() - Challenge a device
+ *
+ * @dev:	[in]	pointer to the usb device to query
+ * @buffer:	[in]	pointer to the buffer allocated for USB query
+ * @slot:	[in]	certificate chain to be used
+ * @slot_mask:	[in]	slot mask of the device
+ * @nonce:	[in]	nonce to use for the challenge, 32 bytes long
+ * @chall:	[out]	buffer for chall response, 204 bytes long, caller allocated
+ *
+ * Context: task context, might sleep.
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EINVAL	- NULL input pointer or invalid slot value
+ * * %-ECOMM	- failed to send or receive message from the device
+ */
+static int usb_authent_challenge_dev(struct usb_device *dev, u8 *buffer,
+	const u8 slot, const u8 slot_mask, const u8 *const nonce,
+	u8 *const chall)
+{
+	int ret = -1;
+
+	if (buffer == NULL || slot >= 8 || nonce == NULL) {
+		dev_err(&dev->dev, "invalid arguments\n");
+		return -EINVAL;
+	}
+
+	memcpy(buffer, nonce, USBAUTH_NONCE_SIZE);
+	ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), AUTH_OUT,
+			      USB_DIR_OUT,
+			      (USB_SECURITY_PROTOCOL_VERSION << 8) +
+				      USB_AUTHENT_CHALLENGE_REQ_TYPE,
+			      (slot << 8), buffer, USBAUTH_NONCE_SIZE, USB_CTRL_GET_TIMEOUT);
+	if (ret < 0) {
+		dev_err(&dev->dev, "Failed to send challenge request: %d\n", ret);
+		ret = -ECOMM;
+		goto cleanup;
+	}
+
+	((struct usb_chall_req_hd *) chall)->protocolVersion = USB_SECURITY_PROTOCOL_VERSION;
+	((struct usb_chall_req_hd *) chall)->messageType = USB_AUTHENT_CHALLENGE_REQ_TYPE;
+	((struct usb_chall_req_hd *) chall)->slotNumber = slot;
+	((struct usb_chall_req_hd *) chall)->reserved = 0x00;
+	memcpy(chall+4, nonce, USBAUTH_NONCE_SIZE);
+
+	ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), AUTH_IN, USB_DIR_IN,
+			      (USB_SECURITY_PROTOCOL_VERSION << 8) +
+				      USB_AUTHENT_CHALLENGE_RESP_TYPE,
+			      (slot << 8) + slot_mask, buffer, USBAUTH_CHALL_RSP_SIZE,
+			      USB_CTRL_GET_TIMEOUT);
+	if (ret != USBAUTH_CHALL_RSP_SIZE) {
+		dev_err(&dev->dev, "Failed to get challenge response: %d\n", ret);
+		ret = -ECOMM;
+		goto cleanup;
+	}
+
+	memcpy(chall + USBAUTH_CHAIN_HEADER_SIZE, buffer, USBAUTH_CHALL_RSP_SIZE);
+	ret = 0;
+
+cleanup:
+
+	return ret;
+}
+
+/**
+ * usb_auth_create_dev_ctx() - Create a device context according to USB Type-C Authentication Specification, chapter 5.5
+ *
+ * @dev:	[in]		handle to the USB device
+ * @ctx:	[in, out]	buffer to hold the device context, caller allocated
+ * @buf_size:	[in]		available size in the context buffer
+ * @ctx_size:	[out]		total size of the context if return equals 0
+ *
+ * The device context is composed of :
+ *	1. Device Descriptor
+ *	2. Complete BOS Descriptor (if present)
+ *	3. Complete Configuration 1 Descriptor
+ *	4. Complete Configuration 2 Descriptor (if present)
+ *	5. ...
+ *	6. Complete Configuration n Descriptor (if present)
+ *
+ * FIXME: Ensure the validity of the device context is complete:
+ *	- Will the config order consistent ?
+ *	- Do we need to also get the sub configuration strings ?
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-EINVAL	- invalid dev, ctx or size
+ *
+ */
+static int usb_auth_create_dev_ctx(struct usb_device *dev, u8 *ctx,
+							const size_t buf_size, size_t *ctx_size)
+{
+	int cfgno = 0;
+	int desc_size = 0;
+
+	if (dev == NULL || ctx == NULL || ctx_size == NULL) {
+		dev_err(&dev->dev, "invalid inputs\n");
+		return -EINVAL;
+	}
+
+	*ctx_size = 0;
+
+	if (buf_size < (size_t)dev->descriptor.bLength) {
+		dev_err(&dev->dev, "buffer too small\n");
+		return -EINVAL;
+	}
+
+	memcpy(ctx, (void *) &dev->descriptor, (size_t) dev->descriptor.bLength);
+	*ctx_size += (size_t) dev->descriptor.bLength;
+
+	if (dev->bos == NULL || dev->bos->desc == NULL) {
+		dev_err(&dev->dev, "invalid BOS\n");
+		return -EINVAL;
+	}
+
+	desc_size = le16_to_cpu(dev->bos->desc->wTotalLength);
+	if (buf_size < (*ctx_size + desc_size)) {
+		dev_err(&dev->dev, "buffer too small\n");
+		return -EINVAL;
+	}
+
+	memcpy(ctx + (*ctx_size), (void *) dev->bos->desc, desc_size);
+	*ctx_size += desc_size;
+
+	if (dev->config == NULL) {
+		dev_err(&dev->dev, "invalid configuration\n");
+		return -EINVAL;
+	}
+
+	for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) {
+		desc_size = le16_to_cpu(dev->config[cfgno].desc.wTotalLength);
+
+		if (buf_size < (*ctx_size + desc_size)) {
+			dev_err(&dev->dev, "buffer too small\n");
+			return -EINVAL;
+		}
+
+		memcpy(ctx + (*ctx_size), (void *) &dev->config[cfgno].desc, USB_DT_CONFIG_SIZE);
+		*ctx_size += USB_DT_CONFIG_SIZE;
+	}
+
+	return 0;
+}
+
+/**
+ * usb_auth_try_resume() - Check that the authentication can resume after a sleep
+ *
+ * @dev: [in] the usb device
+ * @hub: [in] the parent hub
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-ENODEV	- hub has been disconnected
+ *
+ */
+static int usb_auth_try_resume(struct usb_device *dev, struct usb_device *hub)
+{
+	if (hub == NULL || dev == NULL ||
+		     dev->port_is_suspended == 1 ||
+		     dev->reset_in_progress == 1) {
+		return -ENODEV;
+	}
+
+	/*
+	 * TODO: test if the device has not been disconnected
+	 * TODO: test if the device has not been disconnected then replaced with another one
+	 */
+
+	return 0;
+}
+
+static bool usb_has_authentication_capability(const struct usb_device *const dev)
+{
+	return dev->bos && dev->bos->authent_cap;
+}
+
+/**
+ * usb_authenticate_device() - Challenge a device
+ *
+ * @dev: [in, out] pointer to device
+ *
+ * Authentication is done in the following steps:
+ *  1. Get device certificates digest to determine if it is already known
+ *       if yes, go to 3.
+ *  2. Get device certificates
+ *  3. Challenge device
+ *  4. Based on previous result, determine if device is allowed under local
+ *     security policy.
+ *
+ * Context: task context, might sleep.
+ * TODO: complete all possible error case.
+ * TODO: handle root hub device.
+ *
+ * Returns:
+ * * %0		- OK
+ * * %-ENOMEM	- failed to allocate memory for exchange
+ *
+ */
+int usb_authenticate_device(struct usb_device *dev)
+{
+	int ret = 0;
+	u8 is_valid = 0;
+	u8 is_known = 0;
+	u8 is_blocked = 0;
+	u8 chain_nb = 0;
+	u8 slot_mask = 0;
+	u8 slot = 0;
+	u8 digests[USBAUTH_DIGESTS_SIZE] = {0};
+	u8 nonce[USBAUTH_NONCE_SIZE] = {0};
+	u8 chall[USBAUTH_CHALL_SIZE] = {0};
+	u32 dev_id = 0;
+	size_t ctx_size = 0;
+	int i = 0;
+
+	u8 *cert_der = NULL;
+	u8 *buffer = NULL;
+	size_t cert_len = 0;
+
+	if (dev == NULL)
+		return -ENODEV;
+
+	dev->authenticated = 0;
+	if (!usb_has_authentication_capability(dev)) {
+		dev_notice(&dev->dev, "No authentication capability\n");
+		goto cleanup;
+	}
+
+	if (dev->parent == NULL)
+		return -ENODEV;
+
+	struct usb_device *hub = dev->parent;
+
+	buffer = kzalloc(512, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	ret = usb_authent_req_digest(dev, buffer, digests, &slot_mask);
+	if (ret != 0) {
+		dev_err(&dev->dev, "failed to get digest: %d\n", ret);
+		goto cleanup;
+	}
+
+	usb_unlock_device(hub);
+	ret = usb_policy_engine_check_digest(dev->route, digests, slot_mask,
+					     &is_known, &is_blocked, &dev_id);
+	if (ret != 0) {
+		dev_err(&dev->dev, "failed to check digest: %d\n", ret);
+		usb_lock_device(hub);
+		goto cleanup;
+	}
+	dev_info(&dev->dev, "waking up\n");
+	usb_lock_device(hub);
+	ret = usb_auth_try_resume(dev, hub);
+	if (ret != 0) {
+		dev_err(&dev->dev, "failed to resume: %d\n", ret);
+		goto cleanup;
+	}
+
+	if (is_known)
+		goto device_known;
+
+	/*
+	 * If device is not already known try to obtain a valid certificate
+	 * Iterate over every device certificate slots, it gets them one by one
+	 * in order to avoid spamming the device.
+	 */
+	if (!is_known) {
+		for (i = 0; i < 8; i++) {
+			if (1 == ((slot_mask >> i) & 1)) {
+				ret = usb_authent_read_certificate(dev, buffer,
+								   chain_nb,
+								   &cert_der,
+								   &cert_len);
+				if (ret != 0) {
+					goto cleanup;
+				}
+
+				usb_unlock_device(hub);
+				ret = usb_policy_engine_check_cert_chain(
+					dev->route, digests + i * USBAUTH_DIGEST_SIZE, cert_der,
+					cert_len, &is_valid, &is_blocked,
+					&dev_id);
+				if (ret != 0) {
+					dev_err(&dev->dev,
+						"failed to validate certificate: %d\n",
+						ret);
+					usb_lock_device(hub);
+					goto cleanup;
+				}
+				usb_lock_device(hub);
+
+				ret = usb_auth_try_resume(dev, hub);
+				if (ret != 0) {
+					dev_err(&dev->dev, "failed to resume: %d\n", ret);
+					goto cleanup;
+				}
+
+				if (is_valid) {
+					slot = i;
+					goto device_known;
+				}
+			}
+		}
+		goto done;
+	} else {
+		for (i = 0; i < 8; i++) {
+			if (1 == ((is_known >> i) & 1)) {
+				slot = i;
+				break;
+			}
+		}
+	}
+
+device_known:
+	/*
+	 * Device is known, authenticate the device with a challenge request
+	 */
+	usb_unlock_device(hub);
+	ret = usb_policy_engine_generate_challenge(dev_id, nonce);
+	if (ret != 0) {
+		dev_err(&dev->dev, "failed to generate challenge: %d\n", ret);
+		usb_lock_device(hub);
+		goto cleanup;
+	}
+	usb_lock_device(hub);
+
+	ret = usb_auth_try_resume(dev, hub);
+	if (ret != 0) {
+		dev_err(&dev->dev, "failed to resume: %d\n", ret);
+		goto cleanup;
+	}
+
+	ret = usb_authent_challenge_dev(dev, buffer, slot, slot_mask, nonce,
+					chall);
+	if (ret != 0) {
+		dev_err(&dev->dev, "failed to challenge device: %d\n", ret);
+		goto cleanup;
+	}
+
+	ret = usb_auth_create_dev_ctx(dev, buffer, 512, &ctx_size);
+	if (ret != 0) {
+		dev_err(&dev->dev, "failed to create context: %d\n", ret);
+		goto cleanup;
+	}
+
+	usb_unlock_device(hub);
+	ret = usb_policy_engine_check_challenge(dev_id, chall, buffer, ctx_size,
+						&is_valid);
+	if (ret != 0) {
+		dev_err(&dev->dev, "failed to check challenge: %d\n", ret);
+		usb_lock_device(hub);
+		goto cleanup;
+	}
+	usb_lock_device(hub);
+
+	ret = usb_auth_try_resume(dev, hub);
+	if (ret != 0) {
+		dev_err(&dev->dev, "failed to resume: %d\n", ret);
+		goto cleanup;
+	}
+
+done:
+	ret = 0;
+	if (is_valid) {
+		dev->authenticated = 1;
+	} else {
+		dev->authenticated = 0;
+		dev_err(&dev->dev, "Device authentication failure\n");
+	}
+cleanup:
+	kfree(buffer);
+	kfree(cert_der);
+
+	return ret;
+}
diff --git a/drivers/usb/core/authent.h b/drivers/usb/core/authent.h
new file mode 100644
index 0000000000000000000000000000000000000000..2cf6d577131084a97f5c30fadaace1eac7e83c11
--- /dev/null
+++ b/drivers/usb/core/authent.h
@@ -0,0 +1,178 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * SPDX-FileCopyrightText: (C) 2025 ANSSI
+ *
+ * USB Authentication protocol definition
+ *
+ * Author: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
+ * Author: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>
+ *
+ */
+
+#ifndef __USB_CORE_AUTHENT_H_
+#define __USB_CORE_AUTHENT_H_
+
+#include <linux/types.h>
+#include <linux/usb.h>
+#include <linux/usb/ch11.h>
+#include <linux/usb/hcd.h>
+#include <uapi/linux/usb/usb_auth_netlink.h>
+
+/* From USB Type-C Authentication spec, Table 5-2 */
+#define USB_AUTHENT_CAP_TYPE 0x0e
+
+/* From USB Security Foundation spec, Table 5-2 */
+#define USB_SECURITY_PROTOCOL_VERSION 0x10
+
+#define AUTH_IN 0x18
+#define AUTH_OUT 0x19
+
+/* USB_DT_AUTHENTICATION_CAP */
+struct usb_authent_cap_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u8  bDevCapabilityType; /* Shall be set to USB_AUTHENT_CAP_TYPE */
+	/*
+	 * bit 0: set to 1 if firmware can be updated
+	 * bit 1: set to 1 to indicate the Device changes interface when updated
+	 * bits 2-7: reserved, set to 0
+	 */
+	__u8  bmAttributes;
+	__u8  bcdProtocolVersion; /* Shall be set to USB_SECURITY_PROTOCOL_VERSION */
+	__u8  bcdCapability; /* Set to 0x01 */
+
+} __packed;
+
+/* Certificate chain header, Table 3-1 */
+struct usb_cert_chain_hd {
+	__u16 length; /* Chain total length including header, little endian */
+	__u16 reserved; /* Shall be set to zero */
+	__u8 rootHash[32]; /* Hash of root certificate, big endian */
+} __packed;
+
+/* Challenge request header, Table 5-7 */
+struct usb_chall_req_hd {
+	__u8 protocolVersion;
+	__u8 messageType;
+	__u8 slotNumber;
+	__u8 reserved;
+};
+
+/* From USB Security Foundation spec, Table 5-3 and Table 5-9 */
+#define USB_AUTHENT_DIGEST_RESP_TYPE 0x01
+#define USB_AUTHENT_CERTIFICATE_RESP_TYPE 0x02
+#define USB_AUTHENT_CHALLENGE_RESP_TYPE 0x03
+#define USB_AUTHENT_ERROR_TYPE 0x7f
+#define USB_AUTHENT_DIGEST_REQ_TYPE 0x81
+#define USB_AUTHENT_CERTIFICATE_REQ_TYPE 0x82
+#define USB_AUTHENT_CHALLENGE_REQ_TYPE 0x83
+
+#define USBAUTH_DIGEST_RSP_SIZE 260
+#define USBAUTH_CHALL_RSP_SIZE 168
+#define USBAUTH_CHAIN_HEADER_SIZE 36
+
+/* USB Authentication GET_DIGEST Request Header */
+struct usb_authent_digest_req_hd {
+	__u8 protocolVersion; /* Shall be set to USB_SECURITY_PROTOCOL_VERSION */
+	__u8 messageType; /* Shall be set to USB_AUTHENT_DIGEST_REQ_TYPE */
+	__u8 param1; /* Reserved */
+	__u8 param2; /* Reserved */
+} __packed;
+
+/* USB Authentication GET_CERTIFICATE Request Header */
+struct usb_authent_certificate_req_hd {
+	__u8 protocolVersion; /* Shall be set to USB_SECURITY_PROTOCOL_VERSION */
+	__u8 messageType; /* Shall be set to USB_AUTHENT_CERTIFICATE_REQ_TYPE */
+	__u8 certChainSlotNumber; /* Must be between 0 and 7 inclusive */
+	__u8 param2; /* Reserved */
+} __packed;
+
+/* USB Authentication GET_CERTIFICATE Request */
+struct usb_authent_certificate_req {
+	__u8 protocolVersion; /* Shall be set to USB_SECURITY_PROTOCOL_VERSION */
+	__u8 messageType; /* Shall be set to USB_AUTHENT_CERTIFICATE_REQ_TYPE */
+	__u8 certChainSlotNumber; /* Must be between 0 and 7 inclusive */
+	__u8 param2; /* Reserved */
+	__u16 offset; /* Read index of Certificate Chain in bytes and little endian*/
+	__u16 length; /* Length of read request, little endian */
+} __packed;
+
+/* USB Authentication CHALLENGE Request Header */
+struct usb_authent_challenge_req_hd {
+	__u8 protocolVersion; /* Shall be set to USB_SECURITY_PROTOCOL_VERSION */
+	__u8 messageType; /* Shall be set to USB_AUTHENT_CHALLENGE_REQ_TYPE */
+	__u8 certChainSlotNumber; /* Must be between 0 and 7 inclusive */
+	__u8 param2; /* Reserved */
+} __packed;
+
+/* USB Authentication CHALLENGE Request Header */
+struct usb_authent_challenge_req {
+	__u8 protocolVersion; /* Shall be set to USB_SECURITY_PROTOCOL_VERSION */
+	__u8 messageType; /* Shall be set to USB_AUTHENT_CHALLENGE_REQ_TYPE */
+	__u8 certChainSlotNumber; /* Must be between 0 and 7 inclusive */
+	__u8 param2; /* Reserved */
+	__u32 nonce; /* Random Nonce chosen for the challenge */
+} __packed;
+
+/* USB Authentication DIGEST response Header */
+struct usb_authent_digest_resp {
+	__u8 protocolVersion; /* Shall be set to USB_SECURITY_PROTOCOL_VERSION */
+	__u8 messageType; /* Shall be set to USB_AUTHENT_DIGEST_RESP_TYPE */
+	__u8 capability; /* Shall be set to 0x01 */
+	__u8 slotMask; /* Bit set to 1 if slot is set, indicates number of digests */
+	__u8 digests[8][32]; /* List of digests */
+} __packed;
+
+/* USB Authentication CERTIFICATE response Header */
+struct usb_authent_certificate_resp_hd {
+	__u8 protocolVersion; /* Shall be set to USB_SECURITY_PROTOCOL_VERSION */
+	__u8 messageType; /* Shall be set to USB_AUTHENT_CERTIFICATE_RESP_TYPE */
+	__u8 slotNumber; /* Slot number of certificate chain returned */
+	__u8 param2; /* Reserved */
+} __packed;
+
+/* USB Authentication CHALLENGE response Header */
+struct usb_authent_challenge_resp_hd {
+	__u8 protocolVersion; /* Shall be set to USB_SECURITY_PROTOCOL_VERSION */
+	__u8 messageType; /* Shall be set to USB_AUTHENT_CHALLENGE_RESP_TYPE */
+	__u8 slotNumber; /* Slot number of certificate chain returned */
+	__u8 slotMask; /* Bit set to 1 if slot is set */
+} __packed;
+
+/* USB Authentication CHALLENGE response */
+struct usb_authent_challenge_resp {
+	__u8 protocolVersion; /* Shall be set to USB_SECURITY_PROTOCOL_VERSION */
+	__u8 messageType; /* Shall be set to USB_AUTHENT_CHALLENGE_RESP_TYPE */
+	__u8 slotNumber; /* Slot number of certificate chain returned */
+	__u8 slotMask; /* Bit set to 1 if slot is set */
+	__u8 minProtocolVersion;
+	__u8 maxProtocolVersion;
+	__u8 capabilities; /* Shall be set to 0x01 */
+	__u8 orgName; /* Organisation Name, USB-IF: 0 */
+	__u32 certChainHash; /* SHA256 digest of certificate chain, big endian */
+	__u32 salt; /* Chosen by responder */
+	__u32 contextHash; /* SHA256 digest of product information, big endian */
+	__u64 signature; /* ECDSA signature of request and response */
+} __packed;
+
+/* USB Authentication error codes, Foundation Table 5-18 */
+#define USB_AUTHENT_INVALID_REQUEST_ERROR 0x01
+#define USB_AUTHENT_UNSUPPORTED_PROTOCOL_ERROR 0x02
+#define USB_AUTHENT_BUSY_ERROR 0x03
+#define USB_AUTHENT_UNSPECIFIED_ERROR 0x04
+
+/* USB Authentication response header */
+struct usb_authent_error_resp_hd {
+	__u8 protocolVersion; /* Shall be set to USB_SECURITY_PROTOCOL_VERSION */
+	__u8 messageType; /* Shall be set to USB_AUTHENT_ERROR_TYPE */
+	__u8 errorCode;
+	__u8 errorData;
+} __packed;
+
+#ifdef CONFIG_USB_AUTHENTICATION
+int usb_authenticate_device(struct usb_device *dev);
+#else
+static inline int usb_authenticate_device(struct usb_device *dev) { return 0; }
+#endif
+
+#endif /* __USB_CORE_AUTHENT_H_ */

-- 
2.50.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH v2 3/4] usb: core: Plug the usb authentication capability
  2025-07-11  8:41 [RFC PATCH v2 0/4] Support for usb authentication nicolas.bouchinet
  2025-07-11  8:41 ` [RFC PATCH v2 1/4] usb: core: Introduce netlink usb authentication policy engine nicolas.bouchinet
  2025-07-11  8:41 ` [RFC PATCH v2 2/4] usb: core: Introduce usb authentication feature nicolas.bouchinet
@ 2025-07-11  8:41 ` nicolas.bouchinet
  2025-07-11  8:41 ` [RFC PATCH v2 4/4] usb: core: Add sysctl to configure authentication timeouts nicolas.bouchinet
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: nicolas.bouchinet @ 2025-07-11  8:41 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Luc Bonnafoux, Alan Stern, Kannappan R, Sabyrzhan Tasbolatov,
	Krzysztof Kozlowski, Stefan Eichenberger, Thomas Gleixner,
	Pawel Laszczak, Ma Ke, Jeff Johnson, Luc Bonnafoux,
	Nicolas Bouchinet, linux-kernel, linux-usb

From: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>

The authentication bulk is implemented by the usb_authenticate_device
function.

The usb_authenticate_device returns 0 or an error code. If 0 is
returned, a per-device authenticated field is updated with the result of
the authentication.

The authenticated field can be used to track the result of the
authentication process in userspace thanks to a sysfs exposed file.

The device enforcement point is done in the usb_probe_interface()
function. This allows for more complex security policy in userspace: the
user could manually authorize a device that failed the authentication or
manually deauthorize a device that was previously authenticated.

If set to false (default), the authentication decision is not enforced,
the device authentication field is set for userspace information. The
legacy kernel authorization decision is used:

+----------+------------+-------------+
|          | authorized | !authorized |
+----------+------------+-------------+
| authent  |     OK     |     NOK     |
+----------+------------+-------------+
| !authent |     OK     |     NOK     |
+----------+------------+-------------+

If set to true, the authentication decision is enforced, the following
decision is made:

+----------+------------+-------------+
|          | authorized | !authorized |
+----------+------------+-------------+
| authent  |     OK     |     NOK     |
+----------+------------+-------------+
| !authent |    NOK     |     NOK     |
+----------+------------+-------------+

Note that combined with the CONFIG_USB_DEFAULT_AUTHORIZATION_MODE=2:
 - internal devices should be authorized and !authenticated => OK
 - external qemu dev-auth is !authorized and authenticated  => NOK at
   first but then authorization can be granted via sysfs.
 - external qemu non auth dev is !authorized and !authenticated => NOK
   and authorization can be granted via sysfs

The default enforcement decision can be configured thanks to the new
USB_AUTHENTICATION_ENFORCE configuration option and can be overridden
using the usbcore.enforce_authentication command line or module
parameter.

Co-developed-by: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
Signed-off-by: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
Signed-off-by: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>
---
 Documentation/usb/authentication.rst | 15 +++++++++++++++
 Documentation/usb/index.rst          |  1 +
 drivers/usb/core/Kconfig             | 26 ++++++++++++++++++++++++++
 drivers/usb/core/Makefile            |  4 ++++
 drivers/usb/core/config.c            | 22 ++++++++++++++++++++--
 drivers/usb/core/driver.c            | 31 +++++++++++++++++++++++++++++++
 drivers/usb/core/hub.c               |  5 +++++
 drivers/usb/core/sysfs.c             | 16 ++++++++++++++++
 drivers/usb/core/usb.c               |  8 ++++++++
 include/linux/usb.h                  |  2 ++
 10 files changed, 128 insertions(+), 2 deletions(-)

diff --git a/Documentation/usb/authentication.rst b/Documentation/usb/authentication.rst
new file mode 100644
index 0000000000000000000000000000000000000000..828a7f7b07017a684fa039224fdefd6d07a78ec1
--- /dev/null
+++ b/Documentation/usb/authentication.rst
@@ -0,0 +1,15 @@
+======================================
+Linux usb authentication documentation
+======================================
+
+usb authentication
+------------------
+
+.. kernel-doc:: drivers/usb/core/authent.c
+   :internal:
+
+usb authentication netlink API
+------------------------------
+
+.. kernel-doc:: drivers/usb/core/authent_netlink.c
+   :internal:
diff --git a/Documentation/usb/index.rst b/Documentation/usb/index.rst
index 826492c813acd6ec5162c97312ff9a2a4b27633a..9ebf7ef52c3dadd0c2e099ed3570d67109e94e43 100644
--- a/Documentation/usb/index.rst
+++ b/Documentation/usb/index.rst
@@ -7,6 +7,7 @@ USB support
 
     acm
     authorization
+    authentication
     chipidea
     dwc3
     ehci
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index 58e3ca7e479392112f656384c664efc36bb1151a..31dace7e2e794f0f31f77d53bc3c7f3c5339b4ca 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -143,3 +143,29 @@ config USB_DEFAULT_AUTHORIZATION_MODE
 	  ACPI selecting value 2 is analogous to selecting value 0.
 
 	  If unsure, keep the default value.
+
+config USB_AUTHENTICATION
+	bool "Enable USB authentication function"
+	depends on USB
+	help
+		Enables the USB Authentication function. This activates the
+		hook points in the USB stack.
+
+config USB_AUTHENTICATION_ENFORCE
+	bool "Default authentication mode for USB devices"
+	depends on USB && USB_AUTHENTICATION
+	help
+	  Select the default USB device authentication mode. Can be overridden
+	  with usbcore.enforce_authentication command line or module parameter.
+
+	  This option allows you to choose whether USB devices that are
+	  connected to the system needs to be authenticated, or if they are
+	  locked down.
+
+	  With value 1 all connected USB devices with the exception of root hub
+	  require device authentication before they can be used.
+
+	  With value 0 (default) no device authentication is required to use
+	  connected USB devices.
+
+	  If unsure, keep the default value.
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index ac006abd13b3ad8362dc7baa115124c11eaafc84..7ba1a89cf3de7a398889eee1820f2bfbbc4280f5 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -8,6 +8,10 @@ usbcore-y += config.o file.o buffer.o sysfs.o endpoint.o
 usbcore-y += devio.o notify.o generic.o quirks.o devices.o
 usbcore-y += phy.o port.o
 
+ifdef CONFIG_USB_AUTHENTICATION
+usbcore-y += authent.o authent_netlink.o
+endif
+
 usbcore-$(CONFIG_OF)		+= of.o
 usbcore-$(CONFIG_USB_PCI)		+= hcd-pci.o
 usbcore-$(CONFIG_ACPI)		+= usb-acpi.o
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 13bd4ec4ea5f7a6fef615b6f50b1acb3bbe44ba4..bbb835043a0d452c0e8305ac90b5e99cfb94ae24 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -13,7 +13,7 @@
 #include <linux/device.h>
 #include <asm/byteorder.h>
 #include "usb.h"
-
+#include "authent.h"
 
 #define USB_MAXALTSETTING		128	/* Hard limit */
 
@@ -824,7 +824,21 @@ static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
 		kref_init(&intfc->ref);
 	}
 
-	/* FIXME: parse the BOS descriptor */
+	/* If device USB version is above 2.0, get BOS descriptor
+	 *
+	 * Requirement for bcdUSB >= 2.10 is defined in USB 3.2 §9.2.6.6
+	 * "Devices with a value of at least 0210H in the bcdUSB field of their
+	 * device descriptor shall support GetDescriptor (BOS Descriptor) requests."
+	 *
+	 * To discuss, BOS request could be also sent for bcdUSB >= 0x0201
+	 */
+
+	if (le16_to_cpu(dev->descriptor.bcdUSB) >= 0x0201) {
+		dev_notice(ddev, "bcdUSB >= 0x0201\n");
+		retval = usb_get_bos_descriptor(dev);
+		if (retval < 0)
+			dev_notice(ddev, "Device does not have a BOS descriptor\n");
+	}
 
 	/* Skip over any Class Specific or Vendor Specific descriptors;
 	 * find the first interface descriptor */
@@ -1122,6 +1136,10 @@ int usb_get_bos_descriptor(struct usb_device *dev)
 			dev->bos->ptm_cap =
 				(struct usb_ptm_cap_descriptor *)buffer;
 			break;
+		case USB_AUTHENT_CAP_TYPE:
+			dev->bos->authent_cap =
+				(struct usb_authent_cap_descriptor *)buffer;
+			break;
 		default:
 			break;
 		}
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 460d4dde5994e783bdcad08b2abb6bb85ab3258f..d393691aced3cedd23bd359a79e7158b41f2729c 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -34,6 +34,14 @@
 
 #include "usb.h"
 
+#ifdef CONFIG_USB_AUTHENTICATION_ENFORCE
+static int enforce_authentication = CONFIG_USB_AUTHENTICATION_ENFORCE;
+module_param(enforce_authentication, int, S_IRUGO);
+MODULE_PARM_DESC(enforce_authentication,
+		"Default USB device authentication enforcement mode: 0 unauthenticated devices can be authorized, 1 enforces authentication for all devices");
+#else
+static int enforce_authentication = 0;
+#endif
 
 /*
  * Adds a new dynamic USBdevice ID to this driver,
@@ -331,15 +339,38 @@ static int usb_probe_interface(struct device *dev)
 	if (usb_device_is_owned(udev))
 		return error;
 
+	/* Simple security policy
+	 *
+	 * +----------+------------+-------------+
+	 * |          | authorized | !authorized |
+	 * +----------+------------+-------------+
+	 * | authent  |     OK     |     NOK     |
+	 * +----------+------------+-------------+
+	 * | !authent |    NOK     |     NOK     |
+	 * +----------+------------+-------------+
+	 *
+	 * with CONFIG_USB_DEFAULT_AUTHORIZATION_MODE=2
+	 *  - internal devices should be authorized and !authenticated => OK
+	 *  - external qemu dev-auth is !authorized and authenticated  => NOK at first
+	 *     but then authorization can be granted via sysfs.
+	 *  - external qemu non auth dev is !authorized and !authenticated => NOK and
+	 *     authorization can be granted via sysfs
+	 *
+	 */
 	if (udev->authorized == 0) {
 		dev_err(&intf->dev, "Device is not authorized for usage\n");
 		return error;
+	} else if (udev->authenticated == 0 && enforce_authentication == 1) {
+		dev_err(&intf->dev, "Device is not autenticated for usage\n");
+		return error;
 	} else if (intf->authorized == 0) {
 		dev_err(&intf->dev, "Interface %d is not authorized for usage\n",
 				intf->altsetting->desc.bInterfaceNumber);
 		return error;
 	}
 
+	dev_info(&intf->dev, "Device has been authorized for usage\n");
+
 	id = usb_match_dynamic_id(intf, driver);
 	if (!id)
 		id = usb_match_id(intf, driver->id_table);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 0e1dd6ef60a719f59a22d86caeb20f86991b5b29..088dba8355f850b4cb28863116e3654100bb86e5 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -40,6 +40,7 @@
 #include "hub.h"
 #include "phy.h"
 #include "otg_productlist.h"
+#include "authent.h"
 
 #define USB_VENDOR_GENESYS_LOGIC		0x05e3
 #define USB_VENDOR_SMSC				0x0424
@@ -2631,6 +2632,10 @@ int usb_new_device(struct usb_device *udev)
 	usb_disable_autosuspend(udev);
 
 	err = usb_enumerate_device(udev);	/* Read descriptors */
+	if (err < 0)
+		goto fail;
+
+	err = usb_authenticate_device(udev);
 	if (err < 0)
 		goto fail;
 	dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 23f3cb1989f408ecb6b679e6cbe4857384188ae4..53ad9ffdbc4498b1501677fd2ec336dc22c7ce6a 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -529,6 +529,7 @@ static ssize_t usb2_lpm_l1_timeout_show(struct device *dev,
 					char *buf)
 {
 	struct usb_device *udev = to_usb_device(dev);
+
 	return sysfs_emit(buf, "%d\n", udev->l1_params.timeout);
 }
 
@@ -552,6 +553,7 @@ static ssize_t usb2_lpm_besl_show(struct device *dev,
 				  struct device_attribute *attr, char *buf)
 {
 	struct usb_device *udev = to_usb_device(dev);
+
 	return sysfs_emit(buf, "%d\n", udev->l1_params.besl);
 }
 
@@ -731,6 +733,15 @@ static ssize_t authorized_show(struct device *dev,
 	return sysfs_emit(buf, "%u\n", usb_dev->authorized);
 }
 
+/* show if the device is authenticated (1) or not (0) */
+static ssize_t authenticated_show(struct device *dev,
+			       struct device_attribute *attr, char *buf)
+{
+	struct usb_device *usb_dev = to_usb_device(dev);
+
+	return sysfs_emit(buf, "%u\n", usb_dev->authenticated);
+}
+
 /*
  * Authorize a device to be used in the system
  *
@@ -755,6 +766,10 @@ static ssize_t authorized_store(struct device *dev,
 static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR,
 				  authorized_show, authorized_store);
 
+
+static DEVICE_ATTR_IGNORE_LOCKDEP(authenticated, S_IRUGO,
+				  authenticated_show, NULL);
+
 /* "Safely remove a device" */
 static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
 			    const char *buf, size_t count)
@@ -805,6 +820,7 @@ static struct attribute *dev_attrs[] = {
 	&dev_attr_quirks.attr,
 	&dev_attr_avoid_reset_quirk.attr,
 	&dev_attr_authorized.attr,
+	&dev_attr_authenticated.attr,
 	&dev_attr_remove.attr,
 	&dev_attr_ltm_capable.attr,
 #ifdef CONFIG_OF
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 0b4685aad2d50337f3cacb2198c95a68ae8eff86..421cec9966912ccc62ce163733f46cab05503bd6 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -46,6 +46,7 @@
 #include <linux/dma-mapping.h>
 
 #include "hub.h"
+#include "authent_netlink.h"
 
 const char *usbcore_name = "usbcore";
 
@@ -1080,6 +1081,13 @@ static int __init usb_init(void)
 	usb_debugfs_init();
 
 	usb_acpi_register();
+
+#ifdef CONFIG_USB_AUTHENTICATION
+	retval = usb_auth_init_netlink();
+	if (retval)
+		goto hub_init_failed;
+#endif
+
 	retval = bus_register(&usb_bus_type);
 	if (retval)
 		goto bus_register_failed;
diff --git a/include/linux/usb.h b/include/linux/usb.h
index b46738701f8dc46085f251379873b6a8a008d99d..e9037c8120b43556f8610f9acb3ad4129e847b98 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -431,6 +431,8 @@ struct usb_host_bos {
 	struct usb_ssp_cap_descriptor	*ssp_cap;
 	struct usb_ss_container_id_descriptor	*ss_id;
 	struct usb_ptm_cap_descriptor	*ptm_cap;
+	/* Authentication capability */
+	struct usb_authent_cap_descriptor *authent_cap;
 };
 
 int __usb_get_extra_descriptor(char *buffer, unsigned size,

-- 
2.50.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [RFC PATCH v2 4/4] usb: core: Add sysctl to configure authentication timeouts
  2025-07-11  8:41 [RFC PATCH v2 0/4] Support for usb authentication nicolas.bouchinet
                   ` (2 preceding siblings ...)
  2025-07-11  8:41 ` [RFC PATCH v2 3/4] usb: core: Plug the usb authentication capability nicolas.bouchinet
@ 2025-07-11  8:41 ` nicolas.bouchinet
  2025-07-11 11:08 ` [RFC PATCH v2 0/4] Support for usb authentication Greg Kroah-Hartman
  2025-07-13 15:25 ` Greg Kroah-Hartman
  5 siblings, 0 replies; 9+ messages in thread
From: nicolas.bouchinet @ 2025-07-11  8:41 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Luc Bonnafoux, Alan Stern, Kannappan R, Sabyrzhan Tasbolatov,
	Krzysztof Kozlowski, Stefan Eichenberger, Thomas Gleixner,
	Pawel Laszczak, Ma Ke, Jeff Johnson, Luc Bonnafoux,
	Nicolas Bouchinet, linux-kernel, linux-usb

From: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>

The kernel.usb.authent_engine_register_timeout let a user configure in
seconds the time the kernel will wait for a userspace usb authentication
policy engine to register itself.

The kernel.usb.authent_engine_response_timeout let a user configure in
seconds the time the kernel will wait for the registered userspace usb
authentication policy engine to reply to messages.

Co-developed-by: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
Signed-off-by: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
Signed-off-by: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>
---
 drivers/usb/core/Makefile          |  1 +
 drivers/usb/core/authent.h         | 14 ++++++++++
 drivers/usb/core/authent_netlink.c | 26 +++++++++++-------
 drivers/usb/core/sysctl.c          | 55 ++++++++++++++++++++++++++++++++++++++
 drivers/usb/core/usb.c             |  8 ++++++
 include/linux/usb.h                |  9 +++++++
 6 files changed, 104 insertions(+), 9 deletions(-)

diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index 7ba1a89cf3de7a398889eee1820f2bfbbc4280f5..2ec59764fe5ade682368890b4cd30bb3cdad7746 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -15,6 +15,7 @@ endif
 usbcore-$(CONFIG_OF)		+= of.o
 usbcore-$(CONFIG_USB_PCI)		+= hcd-pci.o
 usbcore-$(CONFIG_ACPI)		+= usb-acpi.o
+usbcore-$(CONFIG_SYSCTL)	+= sysctl.o
 
 ifdef CONFIG_USB_ONBOARD_DEV
 usbcore-y			+= ../misc/onboard_usb_dev_pdevs.o
diff --git a/drivers/usb/core/authent.h b/drivers/usb/core/authent.h
index 2cf6d577131084a97f5c30fadaace1eac7e83c11..7c3264793c6ab5d44fa453b3b70c41882c96ff0d 100644
--- a/drivers/usb/core/authent.h
+++ b/drivers/usb/core/authent.h
@@ -169,6 +169,20 @@ struct usb_authent_error_resp_hd {
 	__u8 errorData;
 } __packed;
 
+extern uint usb_auth_wait_userspace_timeout;
+extern uint usb_auth_wait_response_timeout;
+
+#define DEFAULT_USB_AUTHENT_WAIT_USERSPACE_TIMEOUT 30
+#define DEFAULT_USB_AUTHENT_WAIT_RESPONSE_TIMEOUT 300
+
+#ifdef CONFIG_SYSCTL
+extern int usb_register_sysctl(void);
+extern void usb_unregister_sysctl(void);
+#else
+# define usb_auth_init_sysctl() (0)
+# define usb_auth_exit_sysctl() do { } while (0)
+#endif
+
 #ifdef CONFIG_USB_AUTHENTICATION
 int usb_authenticate_device(struct usb_device *dev);
 #else
diff --git a/drivers/usb/core/authent_netlink.c b/drivers/usb/core/authent_netlink.c
index 9848f219e0e4807563f0f0432a0f1108cd6a0454..731ecadee934ae712735dae5932c0e595720245d 100644
--- a/drivers/usb/core/authent_netlink.c
+++ b/drivers/usb/core/authent_netlink.c
@@ -21,11 +21,15 @@
 #include <uapi/linux/usb/usb_auth_netlink.h>
 #include "authent.h"
 #include "authent_netlink.h"
+#include "authent.h"
 
 #define WAIT_USERSPACE_TIMEOUT 30
 #define WAIT_RESPONSE_TIMEOUT 300
 #define USBAUTH_MAX_RESP_SIZE 128
 
+uint usb_auth_wait_userspace_timeout = DEFAULT_USB_AUTHENT_WAIT_USERSPACE_TIMEOUT;
+uint usb_auth_wait_response_timeout = DEFAULT_USB_AUTHENT_WAIT_RESPONSE_TIMEOUT;
+
 /**
  * struct usb_auth_req - Define an outstanding request between the kernel and userspace
  *
@@ -609,7 +613,8 @@ int usb_policy_engine_check_digest(const u32 route, const u8 *const digests,
 		return -EINVAL;
 	}
 
-	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0, HZ * WAIT_USERSPACE_TIMEOUT)) {
+	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0,
+				HZ * usb_auth_wait_userspace_timeout)) {
 		pr_err("%s: userspace not available\n", __func__);
 		return -ECOMM;
 	}
@@ -675,7 +680,7 @@ int usb_policy_engine_check_digest(const u32 route, const u8 *const digests,
 
 	if (!wait_event_timeout(usb_req_wq,
 				usb_auth_outstanding_reqs[index].done == 1,
-				HZ * WAIT_RESPONSE_TIMEOUT)) {
+				HZ * usb_auth_wait_response_timeout)) {
 		pr_err("%s: userspace response not available\n", __func__);
 		usb_auth_release_reqs_slot(index);
 		return -ECOMM;
@@ -740,7 +745,8 @@ int usb_policy_engine_check_cert_chain(const u32 route,
 		return -EINVAL;
 	}
 
-	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0, HZ * WAIT_USERSPACE_TIMEOUT)) {
+	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0,
+				HZ * usb_auth_wait_userspace_timeout)) {
 		pr_err("%s: userspace not available\n", __func__);
 		return -ECOMM;
 	}
@@ -814,7 +820,7 @@ int usb_policy_engine_check_cert_chain(const u32 route,
 
 	if (!wait_event_timeout(usb_req_wq,
 				usb_auth_outstanding_reqs[index].done == 1,
-				HZ * WAIT_RESPONSE_TIMEOUT)) {
+				HZ * usb_auth_wait_response_timeout)) {
 		pr_err("%s: userspace response not available\n", __func__);
 		usb_auth_release_reqs_slot(index);
 		return -ECOMM;
@@ -852,8 +858,8 @@ int usb_policy_engine_generate_challenge(const u32 id, u8 *nonce)
 	struct sk_buff *skb = NULL;
 	u32 index = 0;
 
-	/* Arbitrary 30s wait before giving up */
-	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0, HZ * WAIT_USERSPACE_TIMEOUT)) {
+	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0,
+				HZ * usb_auth_wait_userspace_timeout)) {
 		pr_err("%s: userspace not available\n", __func__);
 		return -ECOMM;
 	}
@@ -902,7 +908,7 @@ int usb_policy_engine_generate_challenge(const u32 id, u8 *nonce)
 
 	if (!wait_event_timeout(usb_req_wq,
 				usb_auth_outstanding_reqs[index].done == 1,
-				HZ * WAIT_RESPONSE_TIMEOUT)) {
+				HZ * usb_auth_wait_response_timeout)) {
 		pr_err("%s: userspace response not available\n", __func__);
 		usb_auth_release_reqs_slot(index);
 		return -ECOMM;
@@ -953,7 +959,8 @@ int usb_policy_engine_check_challenge(const u32 id,
 		return -EINVAL;
 	}
 
-	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0, HZ * WAIT_USERSPACE_TIMEOUT)) {
+	if (!wait_event_timeout(usb_req_wq, pol_eng_pid != 0,
+				HZ * usb_auth_wait_userspace_timeout)) {
 		pr_err("%s: userspace not available\n", __func__);
 		return -ECOMM;
 	}
@@ -1016,9 +1023,10 @@ int usb_policy_engine_check_challenge(const u32 id,
 		       __func__, ret);
 		return -ECOMM;
 	}
+
 	if (!wait_event_timeout(usb_req_wq,
 				usb_auth_outstanding_reqs[index].done == 1,
-				HZ * WAIT_RESPONSE_TIMEOUT)) {
+				HZ * usb_auth_wait_response_timeout)) {
 		pr_err("%s: userspace response not available\n", __func__);
 		usb_auth_release_reqs_slot(index);
 		return -ECOMM;
diff --git a/drivers/usb/core/sysctl.c b/drivers/usb/core/sysctl.c
new file mode 100644
index 0000000000000000000000000000000000000000..a9f917e34e8e914cb60653a56fa90a4790bf6011
--- /dev/null
+++ b/drivers/usb/core/sysctl.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SPDX-FileCopyrightText: (C) 2025 ANSSI
+ *
+ * USB Authentication netlink interface
+ *
+ * Author: Luc Bonnafoux <luc.bonnafoux@ssi.gouv.fr>
+ * Author: Nicolas Bouchinet <nicolas.bouchinet@ssi.gouv.fr>
+ *
+ */
+
+#include <linux/sysctl.h>
+#include <linux/usb.h>
+#include "authent.h"
+
+static const unsigned long max_ms = 3600;
+
+static const struct ctl_table usb_sysctls[] = {
+#ifdef	CONFIG_USB_AUTHENTICATION
+	{
+		.procname	= "authent_engine_register_timeout",
+		.data		= &usb_auth_wait_userspace_timeout,
+		.maxlen		= sizeof(usb_auth_wait_userspace_timeout),
+		.mode		= 0644,
+		.proc_handler	= proc_douintvec_minmax,
+		.extra1		= SYSCTL_ZERO,
+		.extra2		= (void*)&max_ms,
+	},
+	{
+		.procname	= "authent_engine_response_timeout",
+		.data		= &usb_auth_wait_response_timeout,
+		.maxlen		= sizeof(usb_auth_wait_response_timeout),
+		.mode		= 0644,
+		.proc_handler	= proc_douintvec_minmax,
+		.extra1		= SYSCTL_ZERO,
+		.extra2		= (void*)&max_ms,
+	},
+#endif
+};
+
+static struct ctl_table_header *usb_sysctl_table;
+
+int __init usb_register_sysctl(void)
+{
+	usb_sysctl_table = register_sysctl("kernel/usb", usb_sysctls);
+	if (!usb_sysctl_table)
+		return -ENOMEM;
+	return 0;
+}
+
+void usb_unregister_sysctl(void)
+{
+	unregister_sysctl_table(usb_sysctl_table);
+	usb_sysctl_table = NULL;
+}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 421cec9966912ccc62ce163733f46cab05503bd6..0d88a072146dc0ec88314733ae92c835585f722d 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -46,6 +46,7 @@
 #include <linux/dma-mapping.h>
 
 #include "hub.h"
+#include "authent.h"
 #include "authent_netlink.h"
 
 const char *usbcore_name = "usbcore";
@@ -1082,6 +1083,10 @@ static int __init usb_init(void)
 
 	usb_acpi_register();
 
+	retval = usb_register_sysctl();
+	if (retval)
+		goto sysctl_init_failed;
+
 #ifdef CONFIG_USB_AUTHENTICATION
 	retval = usb_auth_init_netlink();
 	if (retval)
@@ -1127,6 +1132,8 @@ static int __init usb_init(void)
 bus_notifier_failed:
 	bus_unregister(&usb_bus_type);
 bus_register_failed:
+	usb_unregister_sysctl();
+sysctl_init_failed:
 	usb_acpi_unregister();
 	usb_debugfs_cleanup();
 out:
@@ -1151,6 +1158,7 @@ static void __exit usb_exit(void)
 	class_unregister(&usbmisc_class);
 	bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
 	bus_unregister(&usb_bus_type);
+	usb_unregister_sysctl();
 	usb_acpi_unregister();
 	usb_debugfs_cleanup();
 	idr_destroy(&usb_bus_idr);
diff --git a/include/linux/usb.h b/include/linux/usb.h
index e9037c8120b43556f8610f9acb3ad4129e847b98..b616c0fb79be33aace2c052ea3ecd1dd0641a024 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -2062,6 +2062,15 @@ extern void usb_led_activity(enum usb_led_event ev);
 static inline void usb_led_activity(enum usb_led_event ev) {}
 #endif
 
+/* sysctl.c */
+#ifdef CONFIG_SYSCTL
+extern int usb_register_sysctl(void);
+extern void usb_unregister_sysctl(void);
+#else
+static inline int usb_register_sysctl(void) { return 0; }
+static inline void usb_unregister_sysctl(void) { }
+#endif /* CONFIG_SYSCTL */
+
 #endif  /* __KERNEL__ */
 
 #endif

-- 
2.50.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH v2 0/4] Support for usb authentication
  2025-07-11  8:41 [RFC PATCH v2 0/4] Support for usb authentication nicolas.bouchinet
                   ` (3 preceding siblings ...)
  2025-07-11  8:41 ` [RFC PATCH v2 4/4] usb: core: Add sysctl to configure authentication timeouts nicolas.bouchinet
@ 2025-07-11 11:08 ` Greg Kroah-Hartman
  2025-07-13 15:25 ` Greg Kroah-Hartman
  5 siblings, 0 replies; 9+ messages in thread
From: Greg Kroah-Hartman @ 2025-07-11 11:08 UTC (permalink / raw)
  To: nicolas.bouchinet
  Cc: Luc Bonnafoux, Alan Stern, Kannappan R, Sabyrzhan Tasbolatov,
	Krzysztof Kozlowski, Stefan Eichenberger, Thomas Gleixner,
	Pawel Laszczak, Ma Ke, Jeff Johnson, Luc Bonnafoux,
	Nicolas Bouchinet, linux-kernel, linux-usb

On Fri, Jul 11, 2025 at 10:41:21AM +0200, nicolas.bouchinet@oss.cyber.gouv.fr wrote:
> We have been working on the implementation of the USB authentication
> protocol in the kernel.
> 
> You can find our work here https://github.com/ANSSI-FR/usb_authentication.
> 
> It is still work in progress but we would like to start discussions
> about the implementation design and its possible integration to the
> Linux kernel.
> 
> Best regards,
> 
> Nicolas and Luc
> 
> ---
> USB peripheral authentication
> =============================
> 
> USB peripherals are an important attack vector in personal computers and
> pose a risk to the cyber security of companies and organizations.
> 
> The USB foundation has published a standard to allow the authentication
> of USB peripherals ([1] and [2]). It defines a mechanism for the host to
> request credentials and issue an authentication challenge to USB-2 or
> USB-3 peripherals, either upon connection or later during the use of the
> peripheral.
> 
> We currently envision the following use cases for USB authentication:
> 
> - company networks where computers and peripherals can be privately
>   controlled and administered;
> - USB cleaning or decontamination stations;
> - individuals who want to prevent unauthorized device plug-in into their
>   machine.
> 
> The implementation of this feature will obviously necessitate efforts
> from both the kernel community and peripherals vendors. We believe that
> providing an implementation of the host side of the protocol in the
> Linux kernel will encourage constructors to include this feature in
> their devices. On the other hand, we are working on implementing
> reference code for embedded devices, notably for Zephyr OS.

What about Linux as a device (i.e. the USB gadget system?)

If we have support for that here, then we can test both sides at the
same time on the same machine, making all of this much easier to
validate it works.  Have you considered doing that work first instead of
doing it in zephyr in a totally different source tree where it makes it
very hard, if not impossible, for us to test this code ourselves?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH v2 0/4] Support for usb authentication
  2025-07-11  8:41 [RFC PATCH v2 0/4] Support for usb authentication nicolas.bouchinet
                   ` (4 preceding siblings ...)
  2025-07-11 11:08 ` [RFC PATCH v2 0/4] Support for usb authentication Greg Kroah-Hartman
@ 2025-07-13 15:25 ` Greg Kroah-Hartman
  2025-07-21 14:51   ` Nicolas Bouchinet
  5 siblings, 1 reply; 9+ messages in thread
From: Greg Kroah-Hartman @ 2025-07-13 15:25 UTC (permalink / raw)
  To: nicolas.bouchinet
  Cc: Luc Bonnafoux, Alan Stern, Kannappan R, Sabyrzhan Tasbolatov,
	Krzysztof Kozlowski, Stefan Eichenberger, Thomas Gleixner,
	Pawel Laszczak, Ma Ke, Jeff Johnson, Luc Bonnafoux,
	Nicolas Bouchinet, linux-kernel, linux-usb

On Fri, Jul 11, 2025 at 10:41:21AM +0200, nicolas.bouchinet@oss.cyber.gouv.fr wrote:
> We have been working on the implementation of the USB authentication
> protocol in the kernel.
> 
> You can find our work here https://github.com/ANSSI-FR/usb_authentication.
> 
> It is still work in progress but we would like to start discussions
> about the implementation design and its possible integration to the
> Linux kernel.
> 
> Best regards,
> 
> Nicolas and Luc
> 
> ---
> USB peripheral authentication
> =============================
> 
> USB peripherals are an important attack vector in personal computers and
> pose a risk to the cyber security of companies and organizations.
> 
> The USB foundation has published a standard to allow the authentication
> of USB peripherals ([1] and [2]). It defines a mechanism for the host to
> request credentials and issue an authentication challenge to USB-2 or
> USB-3 peripherals, either upon connection or later during the use of the
> peripheral.
> 
> We currently envision the following use cases for USB authentication:
> 
> - company networks where computers and peripherals can be privately
>   controlled and administered;
> - USB cleaning or decontamination stations;
> - individuals who want to prevent unauthorized device plug-in into their
>   machine.
> 
> The implementation of this feature will obviously necessitate efforts
> from both the kernel community and peripherals vendors. We believe that
> providing an implementation of the host side of the protocol in the
> Linux kernel will encourage constructors to include this feature in
> their devices. On the other hand, we are working on implementing
> reference code for embedded devices, notably for Zephyr OS.
> 
> Design
> ======
> 
> The USB authentication protocol is based on a simple signature
> challenge. Devices hold between 1 and 8 pairs of private signing key and
> x509 certificate. Hosts must possess a store of root Certificate
> Authority certificates provided by device vendors.

Who is going to distribute these host keys?  Who is going to be
responsible for revocations and the like?

> The protocol exchange is driven by the host and can be decomposed into
> three, mostly independent, phases:
> 
> - The Host can request a digest of each certificate owned by the
>   peripheral.
> - If the Host does not recognize the peripheral from one of its digests,
>   it can read one or more certificates from the device until a valid one
>   is found.
> - The Host can issue an authentication challenge to the peripheral.

Is this the same type of protocol that PCI has implemented for its use
of "trusted devices"?  If so, why not emulate what the kernel patches
for PCI do here as well to keep one common kernel/userspace api?  If
not, what are the major differences?

> On the host side, this requires the following functions:
> 
> - handling the protocol exchange with the peripheral;
> - X509 certificates validation and administration (root CA loading,
>   certificate revocation…);

Is the validation going to be done by the kernel?  Userspace?  And how?
Is a chain-of-trust defined somewhere?

> - cryptographic functions for the challenge (random number generation
>   and ECDSA with the NIST P256 -secp256r1- curve);
> - security policy management;
> - authorization decision enforcement.
> 
> We chose to implement the authentication protocol exchange directly in
> the kernel USB stack during the device enumeration. This is done by
> first requesting the device BOS to detect its capacity at handling
> authentication, then if supported starting the authentication sequence
> with a digest request.
> 
> The implementation of the other functions is open to several design
> alternatives, mainly based on their distribution between kernel and user
> space. In this first implementation, we chose to implement most (all) of
> the cryptographic functions, certificate management and security policy
> management in user space in order to limit impact on the kernel side.
> This allows for more personalization later on. The communication between
> the kernel USB stack authentication function and user space is done via
> a generic netlink socket.

Again, is this what PCI has decided to do?  Surely they didn't implement
a different way of authenticating devices, did they?  (I can dream...)

> Once a device has been authenticated, a per-device authenticated field
> is update with the result of the authentication. The authenticated field
> can be used to track the result of the authentication process in
> userspace thanks to a sysfs exposed file.

This is _VERY_ close to what the old wireless USB protocol required,
right?  And I think you've tied into much the same places we did for
that protocol, but you might want to look into the past history for when
we removed that to be sure.

> The device enforcement point is done in the usb_probe_interface()
> function. This allows for more complex security policy in userspace: the
> user could manually authorize a device that failed the authentication or
> manually deauthorize a device that was previously authenticated.
> 
> +----------+------------+-------------+
> |          | authorized | !authorized |
> +----------+------------+-------------+
> | authent  |     OK     |     NOK     |
> +----------+------------+-------------+
> | !authent |     OK     |     NOK     |
> +----------+------------+-------------+

What is "NOK"?  And what are the two axis?  The sysfs file values?  Or
an internal variable?

> If set to true, the authentication decision is enforced, the following
> decision is made:

If what is "set to true"?

> +----------+------------+-------------+
> |          | authorized | !authorized |
> +----------+------------+-------------+
> | authent  |     OK     |     NOK     |
> +----------+------------+-------------+
> | !authent |    NOK     |     NOK     |
> +----------+------------+-------------+
> 
> Note that combined with the CONFIG_USB_DEFAULT_AUTHORIZATION_MODE=2:

What does "2" mean?

>  - internal devices should be authorized and !authenticated => OK
>  - external qemu dev-auth is !authorized and authenticated  => NOK at
>    first but then authorization can be granted via sysfs.
>  - external qemu non auth dev is !authorized and !authenticated => NOK
>    and authorization can be granted via sysfs

Wait, what does qemu have to do with anything here?

And this is getting to be a complex set of interactions, testing the
combinations is going to be rough without regression tests...

> The default enforcement decision can be configured thanks to the new
> USB_AUTHENTICATION_ENFORCE configuration option and can be overridden
> using the usbcore.enforce_authentication command line or module
> parameter.
> 
> Limitations
> ===========
> 
> The USB authentication protocol come with some inherent limitations, [3]

That's a 2018 reference!  Are you saying that the newly designed
protocol was already discussed in 2018 and found lacking?

> does a good job at describing most of them. During the implementation,
> we also found that the value encoding of the Validity field in the x509
> certificate differs from the RFC5280 [4].

Can this be fixed?

> This has prevented us from
> using the x509 parser included in the Linux kernel or OpenSSL, we chose
> to use the mbedtls library instead [5].

Ugh, why?  Why not fix up the kernel parser to work with this broken
implementation instead?  How is any other operating system handling this
if the implementation is broken?  How was it ever tested?

> This obviously does not prevent
> others to replace it with their preferred implementation. It will also
> open discussions on the protocol enhancement.

Have you done this with the usb-if?

Note, Linux was explicitly kicked out of particapating in usb-if
discussions in the past by some of the core members a decade or so ago
because they did not want us to implement specifications before other
operating systems had the chance to.  So, given that this is a new spec,
has Windows added support for this yet to prove it actually can work?

What's the odds that usb-if is willing to talk to us again?  :)

> The architectural choice to place most of the cryptographic and security
> management functions in user space comes with its own limitations.
> 
> First it introduces a dependency on the user space program availability.
> It will probably be necessary to introduce a fail-safe mechanism if the
> authentication can not be completed. Also, during early boot stages the
> user space service will be needed in one form or another in the
> initramfs.

Ugh, that's going to get messy fast, so putting this in the kernel might
be the simplest way if at all possible.  At least for "boot devices", or
just punt and don't care about USB devices at boot time?

> The second limitation is that the device initialization process is
> paused multiple times. Each time, the hub lock is released in order not
> to block the rest of the stack; and then reacquired when a response has
> been received from user space. The resuming of the operation on the
> device must be done with great care.

USB probe sequences are slow based on the slow devices, so I doubt this
is going to even be noticable.  Have you seen any real slow-down in your
testing?

> Last, we do not yet interface properly with the rest of the usb stack
> and thus do not enforce a strict control of the two authenticated and
> authorized fields. Other sections of the kernel or userspace are able to
> overwrite those fields using the sysfs exposed files for example.

Why would the kernel be writing these values?  And if userspace isn't
supposed to be writing to these anymore, just change the permissions on
the sysfs files, OR just fail the write with the logic in the kernel
itself.

> Status
> ======
> 
> The current kernel implementation of the USB authentication protocol is
> experimental and has the following limitations:
> 
> - It does not yet handle all possible protocol errors.
> - It has been tested with a QEMU mock device, but tests with real
>   hardware are still in progress. As such, the over-the-wire protocol
>   has not yet been fully validated.

Are there any real devices out there?  Is there a test-device from
usb-if that we can use?  Surely usb-if published some example code to
prove that the specification works before ratification happened, right?

> - The kernel/user space communication has not yet been completely
>   validated, including the interruption of the worker thread and its
>   resuming.

What worker thread?  The hub?

> - Device authorization and deauthorization has not been completely
>   implemented.
> - It lacks an overall documentation and test suite.

testing is going to be key here :)

> Upstreaming plans
> =================
> 
> Our current kernel patch is obviously a work-in-progress and not yet
> ready for merging. We feel it is best to start a discussion on the
> architectural choices and gather early comments that could be used to
> improve the design.
> 
> Concerning the user space functions, they are currently implemented in a
> small independent executable as a proof-of-concept. In the future,
> integrating it in existing larger projects, like USBGuard [6], would
> allow presenting a homogeneous USB administration interface to the user.

Yes, usbguard interaction would be good as that's what Linux userspace
has standardized on.  Have you looked into that yet?

> Reviewing this RFC
> ==================
> 
> We would like to get comments on the proposed architectural choices
> regarding the repartition of functions between kernel and user space and
> on the implementation in the USB stack, mostly concerning the releasing
> and reacquiring the hub lock multiple times during the authentication
> process.
> 
> Testing this RFC
> ================
> 
> You can find in the following repository [7] the necessary code for
> creating a test environment:
> 
> - the Linux kernel patches;
> - a python utility to generate a small PKI for device enrollment;
> - a C minimalist service to implement the USB policy engine;
> - patches for QEMU to implement a mock USB device with the
>   authentication capability;
> - a testbed to compile and test the project.

As mentioned before, a usb gadget patch might be simpler than dealing
with qemu, right?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH v2 0/4] Support for usb authentication
  2025-07-13 15:25 ` Greg Kroah-Hartman
@ 2025-07-21 14:51   ` Nicolas Bouchinet
  2025-07-25  8:54     ` Greg Kroah-Hartman
  0 siblings, 1 reply; 9+ messages in thread
From: Nicolas Bouchinet @ 2025-07-21 14:51 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Luc Bonnafoux, Alan Stern, Kannappan R, Sabyrzhan Tasbolatov,
	Krzysztof Kozlowski, Stefan Eichenberger, Thomas Gleixner,
	Pawel Laszczak, Ma Ke, Jeff Johnson, Luc Bonnafoux,
	Nicolas Bouchinet, linux-kernel, linux-usb, Lukas Wunner,
	Alistair Francis, Oliver Neukum

On Sun, Jul 13, 2025 at 05:25:03PM +0200, Greg Kroah-Hartman wrote:
> On Fri, Jul 11, 2025 at 10:41:21AM +0200, nicolas.bouchinet@oss.cyber.gouv.fr wrote:
> > We have been working on the implementation of the USB authentication
> > protocol in the kernel.
> > 
> > You can find our work here https://github.com/ANSSI-FR/usb_authentication.
> > 
> > It is still work in progress but we would like to start discussions
> > about the implementation design and its possible integration to the
> > Linux kernel.
> > 
> > Best regards,
> > 
> > Nicolas and Luc
> > 
> > ---
> > USB peripheral authentication
> > =============================
> > 
> > USB peripherals are an important attack vector in personal computers and
> > pose a risk to the cyber security of companies and organizations.
> > 
> > The USB foundation has published a standard to allow the authentication
> > of USB peripherals ([1] and [2]). It defines a mechanism for the host to
> > request credentials and issue an authentication challenge to USB-2 or
> > USB-3 peripherals, either upon connection or later during the use of the
> > peripheral.
> > 
> > We currently envision the following use cases for USB authentication:
> > 
> > - company networks where computers and peripherals can be privately
> >   controlled and administered;
> > - USB cleaning or decontamination stations;
> > - individuals who want to prevent unauthorized device plug-in into their
> >   machine.
> > 
> > The implementation of this feature will obviously necessitate efforts
> > from both the kernel community and peripherals vendors. We believe that
> > providing an implementation of the host side of the protocol in the
> > Linux kernel will encourage constructors to include this feature in
> > their devices. On the other hand, we are working on implementing
> > reference code for embedded devices, notably for Zephyr OS.
> > 
> > Design
> > ======
> > 
> > The USB authentication protocol is based on a simple signature
> > challenge. Devices hold between 1 and 8 pairs of private signing key and
> > x509 certificate. Hosts must possess a store of root Certificate
> > Authority certificates provided by device vendors.
> 
> Who is going to distribute these host keys?  Who is going to be
> responsible for revocations and the like?
> 
The the first 4 slots must be used by certificate authenticated with
USB-IF root CA. The last 4 slots can be used freely by usb device
vendors or system administrators. It will be the role of the userspace
policy engine to allow the configuration of the trusted root CAs and the
revocation lists.
We are currently on the mind that its out of scope of this kernel
patchset.

> > The protocol exchange is driven by the host and can be decomposed into
> > three, mostly independent, phases:
> > 
> > - The Host can request a digest of each certificate owned by the
> >   peripheral.
> > - If the Host does not recognize the peripheral from one of its digests,
> >   it can read one or more certificates from the device until a valid one
> >   is found.
> > - The Host can issue an authentication challenge to the peripheral.
> 
> Is this the same type of protocol that PCI has implemented for its use
> of "trusted devices"?  If so, why not emulate what the kernel patches
> for PCI do here as well to keep one common kernel/userspace api?  If
> not, what are the major differences?
> 
Currently, as far as we can see in the kernel code, trust for PCI
devices is solely based on whether they are placed on a external facing
PCI port marked as untrusted or not. It is similar to the concept of
internal devices for USB. But in both cases it does not involve any
cryptographic authentication of the device.

We however have been recently aware of the effort to port the SPDM
protocol for PCI in the kernel [1]. We think it could be a great
opportunitie to study if its possible to join our efforts. @Lukas,
@Alistair.

[1]: https://lore.kernel.org/all/cover.1719771133.git.lukas@wunner.de/

> > On the host side, this requires the following functions:
> > 
> > - handling the protocol exchange with the peripheral;
> > - X509 certificates validation and administration (root CA loading,
> >   certificate revocation…);
> 
> Is the validation going to be done by the kernel?  Userspace?  And how?
> Is a chain-of-trust defined somewhere?
> 
As said below, we think its best to left the complexity of the
certificates validation to the userspace.

> > - cryptographic functions for the challenge (random number generation
> >   and ECDSA with the NIST P256 -secp256r1- curve);
> > - security policy management;
> > - authorization decision enforcement.
> > 
> > We chose to implement the authentication protocol exchange directly in
> > the kernel USB stack during the device enumeration. This is done by
> > first requesting the device BOS to detect its capacity at handling
> > authentication, then if supported starting the authentication sequence
> > with a digest request.
> > 
> > The implementation of the other functions is open to several design
> > alternatives, mainly based on their distribution between kernel and user
> > space. In this first implementation, we chose to implement most (all) of
> > the cryptographic functions, certificate management and security policy
> > management in user space in order to limit impact on the kernel side.
> > This allows for more personalization later on. The communication between
> > the kernel USB stack authentication function and user space is done via
> > a generic netlink socket.
> 
> Again, is this what PCI has decided to do?  Surely they didn't implement
> a different way of authenticating devices, did they?  (I can dream...)
> 
We are not completely sure yet if the SPDM protocol is compatible with
the USB authentication.

> > Once a device has been authenticated, a per-device authenticated field
> > is update with the result of the authentication. The authenticated field
> > can be used to track the result of the authentication process in
> > userspace thanks to a sysfs exposed file.
> 
> This is _VERY_ close to what the old wireless USB protocol required,
> right?  And I think you've tied into much the same places we did for
> that protocol, but you might want to look into the past history for when
> we removed that to be sure.
> 
We will do some git archeology. Thanks for the hint.

> > The device enforcement point is done in the usb_probe_interface()
> > function. This allows for more complex security policy in userspace: the
> > user could manually authorize a device that failed the authentication or
> > manually deauthorize a device that was previously authenticated.
> > 
> > +----------+------------+-------------+
> > |          | authorized | !authorized |
> > +----------+------------+-------------+
> > | authent  |     OK     |     NOK     |
> > +----------+------------+-------------+
> > | !authent |     OK     |     NOK     |
> > +----------+------------+-------------+
> 
> What is "NOK"?  And what are the two axis?  The sysfs file values?  Or
> an internal variable?
> 
NOK means Not OK. Authorized and authenticated represent the two
bitfields in the usb_device struct. They are reflected in the sysfs in
their respective files.

> > If set to true, the authentication decision is enforced, the following
> > decision is made:
> 
> If what is "set to true"?
> 
Sorry for that, we meant if the new `USB_AUTHENTICATION_ENFORCE` kernel
parameter is set to true.

> > +----------+------------+-------------+
> > |          | authorized | !authorized |
> > +----------+------------+-------------+
> > | authent  |     OK     |     NOK     |
> > +----------+------------+-------------+
> > | !authent |    NOK     |     NOK     |
> > +----------+------------+-------------+
> > 
> > Note that combined with the CONFIG_USB_DEFAULT_AUTHORIZATION_MODE=2:
> 
> What does "2" mean?
> 
We are refering to the usb authorized_default mode that authorizes by
default internal devices only. The idea here is that, paired with
`USB_AUTHENTICATION_ENFORCE` we can more finely control the enforcement
point.

> >  - internal devices should be authorized and !authenticated => OK
> >  - external qemu dev-auth is !authorized and authenticated  => NOK at
> >    first but then authorization can be granted via sysfs.
> >  - external qemu non auth dev is !authorized and !authenticated => NOK
> >    and authorization can be granted via sysfs
> 
> Wait, what does qemu have to do with anything here?
> 
> And this is getting to be a complex set of interactions, testing the
> combinations is going to be rough without regression tests...
> 
Sorry for that, we were refering to our current test device implemented
in qemu. But was supposed to be any external usb devices.

> > The default enforcement decision can be configured thanks to the new
> > USB_AUTHENTICATION_ENFORCE configuration option and can be overridden
> > using the usbcore.enforce_authentication command line or module
> > parameter.
> > 
> > Limitations
> > ===========
> > 
> > The USB authentication protocol come with some inherent limitations, [3]
> 
> That's a 2018 reference!  Are you saying that the newly designed
> protocol was already discussed in 2018 and found lacking?
> 
As far as we are aware, the last version of the specification [1] is the
last is the last version that includes errata from 2019. [3] is a
research paper that explored the limitation of the protocol. We are not
aware of any evolution since. We saw that SPDM reuses some of the USB
specification fields but we are not entirely sure yet if can be used in
place of the USB specification and if it fixes the limitations.

> > does a good job at describing most of them. During the implementation,
> > we also found that the value encoding of the Validity field in the x509
> > certificate differs from the RFC5280 [4].
> 
> Can this be fixed?
> 
> > This has prevented us from
> > using the x509 parser included in the Linux kernel or OpenSSL, we chose
> > to use the mbedtls library instead [5].
> 
> Ugh, why?  Why not fix up the kernel parser to work with this broken
> implementation instead?  How is any other operating system handling this
> if the implementation is broken?  How was it ever tested?
> 
We think its up to the USB-IF to fix their specification and not up to
the kernel to adapt its behavior.

> > This obviously does not prevent
> > others to replace it with their preferred implementation. It will also
> > open discussions on the protocol enhancement.
> 
> Have you done this with the usb-if?
> 
 We have not yet contacted the USB-IF to report those issues.

> Note, Linux was explicitly kicked out of particapating in usb-if
> discussions in the past by some of the core members a decade or so ago
> because they did not want us to implement specifications before other
> operating systems had the chance to.  So, given that this is a new spec,
> has Windows added support for this yet to prove it actually can work?
> 
> What's the odds that usb-if is willing to talk to us again?  :)
> 
> > The architectural choice to place most of the cryptographic and security
> > management functions in user space comes with its own limitations.
> > 
> > First it introduces a dependency on the user space program availability.
> > It will probably be necessary to introduce a fail-safe mechanism if the
> > authentication can not be completed. Also, during early boot stages the
> > user space service will be needed in one form or another in the
> > initramfs.
> 
> Ugh, that's going to get messy fast, so putting this in the kernel might
> be the simplest way if at all possible.  At least for "boot devices", or
> just punt and don't care about USB devices at boot time?
> 
As we see it we could indeed do everything in the kernel but it will
mean that either every vendor certificates have to be loaded at compile
time as well as the security policy. Or we would need a way to load the
policy and certificates later through sysfs for example.

For the former it would be pretty unflexible. For the later as far as we
understand it there needs to be an userspace available and thus it is
possible to have an userspace policy engine.

> > The second limitation is that the device initialization process is
> > paused multiple times. Each time, the hub lock is released in order not
> > to block the rest of the stack; and then reacquired when a response has
> > been received from user space. The resuming of the operation on the
> > device must be done with great care.
> 
> USB probe sequences are slow based on the slow devices, so I doubt this
> is going to even be noticable.  Have you seen any real slow-down in your
> testing?
> 
We have not seen any slowdown, our concern is that the usb_device struct
pointer is no longer valid if for example the device has been ejected
during the authentication sequence. We still need to harden our current
implementation.

> > Last, we do not yet interface properly with the rest of the usb stack
> > and thus do not enforce a strict control of the two authenticated and
> > authorized fields. Other sections of the kernel or userspace are able to
> > overwrite those fields using the sysfs exposed files for example.
> 
> Why would the kernel be writing these values?  And if userspace isn't
> supposed to be writing to these anymore, just change the permissions on
> the sysfs files, OR just fail the write with the logic in the kernel
> itself.
> 
Since the second patchset, we have been working on the enforcement of
the two authenticated and authorized bitfields. The authenticated sysfs
file is exposed as read-only.

> > Status
> > ======
> > 
> > The current kernel implementation of the USB authentication protocol is
> > experimental and has the following limitations:
> > 
> > - It does not yet handle all possible protocol errors.
> > - It has been tested with a QEMU mock device, but tests with real
> >   hardware are still in progress. As such, the over-the-wire protocol
> >   has not yet been fully validated.
> 
> Are there any real devices out there?  Is there a test-device from
> usb-if that we can use?  Surely usb-if published some example code to
> prove that the specification works before ratification happened, right?
> 
Not yet but we hope that having a host implementation will encourage the
vendors to publish authenticated devices.

> > - The kernel/user space communication has not yet been completely
> >   validated, including the interruption of the worker thread and its
> >   resuming.
> 
> What worker thread?  The hub?
> 
Yes, the hub one.

> > - Device authorization and deauthorization has not been completely
> >   implemented.
> > - It lacks an overall documentation and test suite.
> 
> testing is going to be key here :)
> 
Agreed.

> > Upstreaming plans
> > =================
> > 
> > Our current kernel patch is obviously a work-in-progress and not yet
> > ready for merging. We feel it is best to start a discussion on the
> > architectural choices and gather early comments that could be used to
> > improve the design.
> > 
> > Concerning the user space functions, they are currently implemented in a
> > small independent executable as a proof-of-concept. In the future,
> > integrating it in existing larger projects, like USBGuard [6], would
> > allow presenting a homogeneous USB administration interface to the user.
> 
> Yes, usbguard interaction would be good as that's what Linux userspace
> has standardized on.  Have you looked into that yet?
> 
Yes but we chose to implement our minimal test userspace policy engine
for now. We will happily look into it later.

> > Reviewing this RFC
> > ==================
> > 
> > We would like to get comments on the proposed architectural choices
> > regarding the repartition of functions between kernel and user space and
> > on the implementation in the USB stack, mostly concerning the releasing
> > and reacquiring the hub lock multiple times during the authentication
> > process.
> > 
> > Testing this RFC
> > ================
> > 
> > You can find in the following repository [7] the necessary code for
> > creating a test environment:
> > 
> > - the Linux kernel patches;
> > - a python utility to generate a small PKI for device enrollment;
> > - a C minimalist service to implement the USB policy engine;
> > - patches for QEMU to implement a mock USB device with the
> >   authentication capability;
> > - a testbed to compile and test the project.
> 
> As mentioned before, a usb gadget patch might be simpler than dealing
> with qemu, right?
> 
It's feasible, for our current use we think it is easier to test with
qemu and virtme-ng. We currently use the testbed we published [7].

If it were to be implemented in a Linux gadget, it will raise the
question of a secure storage for the authentication key.

Thanks,

Nicolas and Luc

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC PATCH v2 0/4] Support for usb authentication
  2025-07-21 14:51   ` Nicolas Bouchinet
@ 2025-07-25  8:54     ` Greg Kroah-Hartman
  0 siblings, 0 replies; 9+ messages in thread
From: Greg Kroah-Hartman @ 2025-07-25  8:54 UTC (permalink / raw)
  To: Nicolas Bouchinet
  Cc: Luc Bonnafoux, Alan Stern, Kannappan R, Sabyrzhan Tasbolatov,
	Krzysztof Kozlowski, Stefan Eichenberger, Thomas Gleixner,
	Pawel Laszczak, Ma Ke, Jeff Johnson, Luc Bonnafoux,
	Nicolas Bouchinet, linux-kernel, linux-usb, Lukas Wunner,
	Alistair Francis, Oliver Neukum

Hi,

Let me focus right now on only one thing here:

On Mon, Jul 21, 2025 at 04:51:52PM +0200, Nicolas Bouchinet wrote:
> > > Limitations
> > > ===========
> > > 
> > > The USB authentication protocol come with some inherent limitations, [3]
> > 
> > That's a 2018 reference!  Are you saying that the newly designed
> > protocol was already discussed in 2018 and found lacking?
> > 
> As far as we are aware, the last version of the specification [1] is the
> last is the last version that includes errata from 2019. [3] is a
> research paper that explored the limitation of the protocol. We are not
> aware of any evolution since. We saw that SPDM reuses some of the USB
> specification fields but we are not entirely sure yet if can be used in
> place of the USB specification and if it fixes the limitations.

So, this spec has been out since 2019, and it's "broken", AND there are
no actual devices using this?

If there is no hardware out there, I really do not want to be adding new
features to the kernel for this.  We ripped out wireless usb because the
thing never actually shipped and was dropped by the vendors.  Let's not
do that again here.

So if there is no hardware, there's no need for this at all, right?
Where are the vendors that wrote this spec, and why did they not
actually add support for this to their devices already?

And, most importantly, is Windows going to support this?  Because if it
is not, then there is no need for us to support it either as no vendor
is going to only be building a USB device just for Linux systems, right?

So, where are the vendors?  Without that, this is not going to actually
go very far at all, and just bit-rot in our tree :(

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2025-07-25  8:54 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-11  8:41 [RFC PATCH v2 0/4] Support for usb authentication nicolas.bouchinet
2025-07-11  8:41 ` [RFC PATCH v2 1/4] usb: core: Introduce netlink usb authentication policy engine nicolas.bouchinet
2025-07-11  8:41 ` [RFC PATCH v2 2/4] usb: core: Introduce usb authentication feature nicolas.bouchinet
2025-07-11  8:41 ` [RFC PATCH v2 3/4] usb: core: Plug the usb authentication capability nicolas.bouchinet
2025-07-11  8:41 ` [RFC PATCH v2 4/4] usb: core: Add sysctl to configure authentication timeouts nicolas.bouchinet
2025-07-11 11:08 ` [RFC PATCH v2 0/4] Support for usb authentication Greg Kroah-Hartman
2025-07-13 15:25 ` Greg Kroah-Hartman
2025-07-21 14:51   ` Nicolas Bouchinet
2025-07-25  8:54     ` Greg Kroah-Hartman

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).