diff --git a/doc/reference/auth/auth_api_ref.rst b/doc/reference/auth/auth_api_ref.rst new file mode 100644 index 000000000000..6c6126c67398 --- /dev/null +++ b/doc/reference/auth/auth_api_ref.rst @@ -0,0 +1,200 @@ +Authentication +############################################## + +The Zephyr Authentication Library is a library that provides +authentication services between two devices independent of the lower +transport layer. The library enables firmware applications to authenticate +with a Bluetooth or serial connected device using a simple +Challenge-Response or DTLS authentication method. Authentication +means proving the peer device’s identity. + +Use the authentication samples as a template for adding authentication into your +firmware application. + + + +Kconfig Options +----------------------------- + +:option:`CONFIG_AUTH_LIB`: This option enables the Authentication library. + +:option:`CONFIG_AUTH_CHALLENGE_RESPONSE`: Selects the Challenge Response authentication method + +:option:`CONFIG_AUTH_DTLS`: Selects the DTLS authentication method. + +:option:`CONFIG_AUTH_LOG_LEVEL`: Authentication log level, 0-4 + +:option:`CONFIG_BT_XPORT`: Use Bluetooth as lower transport. + +:option:`CONFIG_BT_XPORT`: Use Bluetooth as lower transport. + +:option:`CONFIG_BT_ALT_AUTH_BT_UUIDS`: Use alternate Bluetooth Auth service UUIDs. + +:option:`CONFIG_NUM_AUTH_INSTANCES`: Each authentication instance uses a thread to authenticate with +a peer over the lower transport. It is possible to have multiple +authentication instances where one instance authenticates a peer +over Bluetooth and another authenticates over a serial link. + +:option:`CONFIG_AUTH_THREAD_PRIORITY`: Authentication thread priority. + +Examples of API usage: +----------------------------- + +The Authentication API is designed to abstract away the authentication method and +transport. The calling application configures the ZAUTH library, starts the authentication +process and monitors results via a status callback. The API is also designed to handle +multiple concurrent authentication processes, for example If device is acting as a +Bluetooth Central and Peripheral. An example of the API used is shown in the following +code snippet. + +.. code-block:: none + + void auth_status(struct authenticate_conn *auth_conn, enum auth_instance_id instance, auth_status_t status, void *context) + { + if(status == AUTH_STATUS_SUCCESSFUL) { + printk(“Authentication Successful.\n”); + } else { + printk(“Authentication status: %s\n”, auth_lib_getstatus_str(status)); + } + } + + /* BLE connection callback */ + void connected(struct bt_conn *conn, uint8_t err) + { + /* start authentication */ + auth_lib_start(¢ral_auth_conn); + } + + void main(void) + { + int err = auth_lib_init(¢ral_auth_conn, AUTH_INST_1, auth_status, NULL, + opt_parms, flags); + + err = bt_enabled(NULL); + + while(true) { + k_yield(); + } + } + +Client Server Model +---------------------- +ZAUTH is designed as a client server model for the authentication message flow. The client initiates the +authentication messaging sequence where the server responds. Depending on the authentication method chosen +(Challenge-Response, DTLS, other), mutual authentication can be used to authenticate both sides of the +connection. For some transports, this model maps nicely to the native transport model. Bluetooth +is an example of this, a peripheral is in the server role and the central is in the client role. For Serial +transports, the choice of which endpoint acts as the client or server is up to the application firmware. + +Authentication Instances +------------------------- +Multiple authentication instances are possible concurrently authenticating connections over different +communication links. For example, a Bluetooth central device could use different instances to authenticate +different peripherals. Another example could be a HVAC controller with Bluetooth to communicate with mobile +devices and a serial interface to control HVAC equipment. One instance would authenticate the mobile device, +the second instance would authenticate the HVAC equipment. + + +Under the hood, an authentication Instance is a Zephyr thread and authentication method. + +Authentication Methods +------------------------- +Two authentication methods are supported, DTLS and simple Challenge-Response. However, the authentication +architecture can support additional authentication methods in the future. + +* DTLS. The TLS protocol is the gold standard of authentication and securing network communications. DTLS + is part of the TLS protocol, but designed for IP datagrams which are lighter weight and ideal for resource + constrained devices. Identities are verified using X.509 certificates and trusted root certificates. The + DTLS handshake steps are used for authentication, a successful handshake means each side of the connection + has been properly authenticated. A result of the DTLS handshake steps is a shared secret key which can be + used to encrypted further communications, this is up to the firmware application to implement. For the ZAUTH + this key is not used. + + +* Challenge-Response. A simple Challenge-Response authentication method is an alternative lighter weight + approach to authentication. This method uses a shared key and a random nonce. Each side exchanges SHA256 + hash of Nonce and shared key, authentication is proven by each side knowing shared key. A Challenge-Response + is not as secure and DTLS, however for some applications it is sufficient. For example, if a vendor wishes + to restrict certain features of an IoT device to paid applications. + + +The authentication is done at the application layer after connecting over the lower transport. This +requires the firmware application to ignore or reject any messages until the authentication process has +completed. This complicates the application firmware but does enable authentication independent of a +vendor’s stack such as Bluetooth, TCP/IP, or serial. In addition, most embedded engineers have no +desire to modify a vendor’s stack. + + +Detailed Design +------------------------- +The high-level diagram below shows the main ZAUTH components. + +.. image:: high_level_design.png + + +Authentication is performed in a separate thread started by the application. Each authentication method uses a +dedicated thread to exchange authentication message with their peer. Adding additional authentication methods is +done by creating a authentication instance. Messages are passed between the authentication thread and lower +transport using an abstracted transport handle which maps to a Tx or Rx queue. The authentication threads are +unaware of how messages are transferred. Optionally the lower transport can be configured to bypass the Tx queue +and send the message directly to the lower transport, by passing the Tx queue. This is ideal for lower transports +that handle their own message queueing. + + +An Authentication method is a defined message protocol between two peers. The message protocol contains details +of the contents and the order of messages. The DTLS protocol is an example of a detailed authentication +protocol. Messages are different sizes and depending on the lower transport, may not fit into a transports MTU +size. For example, the default MTU for Bluetooth is 23 bytes versus the 512 byte minimum possible for DTLS record. + + +Authentication messages larger than the underlying transport MTU are fragmented; ZAUTH disassembles and +re-assembles messages over the transport layer. For example, if a 267 byte message is send over a Bluetooth link +with an MTU of 150, ZAUTH will break up the message into one 150 byte message and a second 117 byte fragments when +sending. The receiving side will reassemble the fragments into the original 267 byte message before +forwarding to the Rx queue. An important caveat is ZAUTH does not handle reordering of fragments, if fragment 2 +arrives before fragment 1, the message is corrupted. + + +The diagram below shows how the Tx and Rx queues are used along with message fragmentation. + +.. image:: tx_rx_queues.png + +The Bluetooth Central Authentication sample (see samples/authentication/bluetooth/central_auth) provides a +good example to drill deeper into the transport layer interface and how Bluetooth is “hooked up” to ZAUTH. +The GREEN boxes are Bluetooth transport specific. + +.. image:: xport_layer.png + +In *auth_xp_bt_init()* the Bluetooth connection (*struct bt_conn*) is added, along with the transport, +to a connection using the *struct auth_xport_connection_map* + +Transport Layer Interface +------------------------------ +Transport layer details vary greatly, it does not make sense to create a one-size-fits-all transport +API. ZAUTH separates the transport into transport independent and transport specific. For example, the details +of the Bluetooth transport are in the *auth_xport_bt.c* file. This includes direct calls into the Zephyr +Bluetooth stack. The transport common function, *auth_xport_init()*, calls the transport specific i +nitialization function, passing the opaque transport handle (*auth_xport_hdl_t*) as an argument and transport +specific parameters. The lower transport is responsible for mapping any transport specific variables to the +transport handle. For example, the Bluetooth transport internally maps the transport handle to a Bluetooth +connection handle, *struct bt_conn*. + +The organization of the transport layers are show in the following diagram, the blue boxs are the Authentication +library code. + +.. image:: xport_interface.png + +API Reference +-------------------------- + +.. doxygengroup:: zauth_api + :project: Zephyr + + + + + + + + + diff --git a/doc/reference/auth/high_level_design.png b/doc/reference/auth/high_level_design.png new file mode 100644 index 000000000000..b5eb85ea5f5b Binary files /dev/null and b/doc/reference/auth/high_level_design.png differ diff --git a/doc/reference/auth/tx_rx_queues.png b/doc/reference/auth/tx_rx_queues.png new file mode 100644 index 000000000000..4d5b1af0ba0e Binary files /dev/null and b/doc/reference/auth/tx_rx_queues.png differ diff --git a/doc/reference/auth/xport_interface.png b/doc/reference/auth/xport_interface.png new file mode 100644 index 000000000000..c37300f93305 Binary files /dev/null and b/doc/reference/auth/xport_interface.png differ diff --git a/doc/reference/auth/xport_layer.png b/doc/reference/auth/xport_layer.png new file mode 100644 index 000000000000..06ffe1d68c6a Binary files /dev/null and b/doc/reference/auth/xport_layer.png differ diff --git a/doc/reference/index.rst b/doc/reference/index.rst index 29bb60fc9738..8f82cedd54a7 100644 --- a/doc/reference/index.rst +++ b/doc/reference/index.rst @@ -9,6 +9,7 @@ API Reference overview.rst terminology.rst audio/index.rst + auth/auth_api_ref.rst misc/notify.rst bluetooth/index.rst kconfig/index.rst @@ -35,3 +36,4 @@ API Reference settings/index.rst timing_functions/index.rst virtualization/index.rst + diff --git a/include/auth/auth_lib.h b/include/auth/auth_lib.h new file mode 100644 index 000000000000..a319e3e9135c --- /dev/null +++ b/include/auth/auth_lib.h @@ -0,0 +1,335 @@ +/** + * @file auth_lib.h + * + * @brief Authentication library functions + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_AUTH_LIB_H_ +#define ZEPHYR_INCLUDE_AUTH_LIB_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Authentication API + * @defgroup zauth_api Authentication API + * @ingroup zauth + * @{ + */ + +#include + +/* + * Determine number of auth instances. + */ +#if (CONFIG_NUM_AUTH_INSTANCES == 0) +#error Error at least one Authentication instance must be defined. +#endif + +#if (CONFIG_NUM_AUTH_INSTANCES > 0) +#define AUTH_INSTANCE_1 +#endif + +#if (CONFIG_NUM_AUTH_INSTANCES > 1) +#define AUTH_INSTANCE_2 +#endif + + +/** + * Auth instance id. Either AUTH_INST_1_ID or AUTH_INST_2_ID + * Each instance performs authentication over a given hardware transport + * such as Bluetooth or serial. It is possible to configure the authentication + * library to authenticate over BLE and Serial. Or if the device + * is a Bluetooth central, then two instances can be used to authenticate + * two different peripherals. + */ +enum auth_instance_id { + +#if defined(AUTH_INSTANCE_1) + /** Auth instance 1 */ + AUTH_INST_1_ID = 0, +#endif + +#if defined(AUTH_INSTANCE_2) + /** Auth instance 2 */ + AUTH_INST_2_ID = 1, +#endif + + AUTH_MAX_INSTANCES +}; + + +#include + +/** Authentication success */ +#define AUTH_SUCCESS 0 +/** Auth error base value */ +#define AUTH_ERROR_BASE (-200) +/** Invalid param passed to function */ +#define AUTH_ERROR_INVALID_PARAM (AUTH_ERROR_BASE - 1) +/** Out of memory */ +#define AUTH_ERROR_NO_MEMORY (AUTH_ERROR_BASE - 2) +/** Operation timeout */ +#define AUTH_ERROR_TIMEOUT (AUTH_ERROR_BASE - 3) +/** No resource such as buffers or DTLS context buffer */ +#define AUTH_ERROR_NO_RESOURCE (AUTH_ERROR_BASE - 4) +/** DTLS initialization failed. */ +#define AUTH_ERROR_DTLS_INIT_FAILED (AUTH_ERROR_BASE - 5) +/** One of the Rx or Tx buffers are full */ +#define AUTH_ERROR_IOBUFF_FULL (AUTH_ERROR_BASE - 6) +/** An unexpected error, such as a Bluetooth handle unexpectedly missing. */ +#define AUTH_ERROR_INTERNAL (AUTH_ERROR_BASE - 7) +/** The lower transport failed to send */ +#define AUTH_ERROR_XPORT_SEND (AUTH_ERROR_BASE - 8) +/** A framing error occurred when re-assembling a message. */ +#define AUTH_ERROR_XPORT_FRAME (AUTH_ERROR_BASE - 9) +/** An error occurred when performming a crypto operation */ +#define AUTH_ERROR_CRYPTO (AUTH_ERROR_BASE - 10) +/** The authentication check failed. */ +#define AUTH_ERROR_FAILED (AUTH_ERROR_BASE - 11) +/** The authentication was canceled*/ +#define AUTH_ERROR_CANCELED (AUTH_ERROR_BASE - 12) + + +/* + * Flags used when initializing authentication connection + */ +enum auth_flags { + /** Server role */ + AUTH_CONN_SERVER = BIT(0), + /** Client role */ + AUTH_CONN_CLIENT = BIT(1), + /** Use DTLS for authentication */ + AUTH_CONN_DTLS_AUTH_METHOD = BIT(2), + /** Use Challenge-Response for authentication */ + AUTH_CONN_CHALLENGE_AUTH_METHOD = BIT(3) +}; + + +/** + * Authentication status enums + */ +enum auth_status { + /** Authentication started */ + AUTH_STATUS_STARTED, + /** Authentication in process */ + AUTH_STATUS_IN_PROCESS, + /** Authentication has been canceled */ + AUTH_STATUS_CANCELED, + /** An internal failure of some type */ + AUTH_STATUS_FAILED, + /** Authentication failed */ + AUTH_STATUS_AUTHENTICATION_FAILED, + /** Authentication successful */ + AUTH_STATUS_SUCCESSFUL +}; + + + +/* Forward declaration */ +struct authenticate_conn; + +/** + * Authentication function prototype + */ +typedef void (*auth_instance_func_t)(struct authenticate_conn *); + +/** + * Authentication callback status function + */ +typedef void (*auth_status_cb_t)(struct authenticate_conn *auth_conn, enum auth_instance_id instance, + enum auth_status status, void *context); + + +/** + * @brief Used to manage one authentication instance with a peer. It is possible + * to have multiple concurrent authentication instances. For example if + * a device is acting as a Central and Peripheral concurrently. + */ +struct authenticate_conn { + bool is_client; /* True if client */ + + /* lower transport opaque handle */ + auth_xport_hdl_t xport_hdl; + + /* current status of the authentication process */ + enum auth_status curr_status; + + /* The auth instance ID for this connection */ + enum auth_instance_id instance; + + /* status callback func */ + auth_status_cb_t status_cb; + void *callback_context; + + /* Work queue used to return status. Important if authentication + * status changes/fails in an ISR context */ + struct k_work auth_status_work; + + + /* authentication function, performs the actual authentication */ + auth_instance_func_t auth_func; + + /* cancel the authentication */ + volatile bool cancel_auth; + + /* Pointer to internal details, do not touch!!! */ + void *internal_obj; +}; + + +/** + * Optional authentication parameters specific to a particular authentication method. + * This is a "catch-all" for parameters such as certificates or device salts when + * using an authentication method. For example, a user password or PIN code would + * be passed to a password based authentication method such as J-PAKE. + */ + +/* + * Optional parameter id + */ +enum auth_opt_param_id { + /** Optional DTLS param */ + AUTH_DTLS_PARAM = 1, + /** Optional Challenge-Response param */ + AUTH_CHALRESP_PARAM, + /** Placeholder for future use */ + AUTH_PLACHOLDER_PARAM +}; + + +/** + * Structs specific to DTLS authentication + */ +struct auth_certificate_info { + const uint8_t *cert; /* Cert or cert chain in PEM format. */ + size_t cert_size; /* Size of cert or chain */ + + /* Optional private key for this cert, set to NULL if + * not used. */ + const uint8_t *priv_key; + size_t priv_key_size; +}; + +/** + * DTLS cert chain plus key info + */ +struct auth_dtls_certs { + struct auth_certificate_info server_ca_chain_pem; + struct auth_certificate_info device_cert_pem; +}; + +/** + * Optional shared key vs. hard-coded key. This shared key could come + * from a secure element such as a Microchip ATECC608A or NXP SE050 device. + */ +struct auth_challenge_resp { + const uint8_t *shared_key; /* 32 byte random nonce */ +}; + +/** + * Placeholder for future params + */ +struct auth_placeholder_opt { + void *placeholder; +}; + + +/** + * Optional parameters specific to the authentication method used. + */ +struct auth_optional_param { + enum auth_opt_param_id param_id; + union opt_params { + struct auth_dtls_certs dtls_certs; + struct auth_challenge_resp chal_resp; + + /* For future use, additional optional params are added + * added here. */ + struct auth_placeholder_opt placeholder_opt; + } param_body; +}; + + +/** + * Initializes authentication library + * + * @param auth_conn Authentication connection struct, initialized by this call. + * @param instance The instance ID. + * @param status_func Status function callback. + * @param context Optional context used in status callback. NULL if not used. + * @param opt_params Optional params specific to the authentication method selected. NULL if not used. + * @param auth_flags Authentication flags. + * + * @return AUTH_SUCCESS on success else one of AUTH_ERROR_* values. + */ +int auth_lib_init(struct authenticate_conn *auth_conn, enum auth_instance_id instance, + auth_status_cb_t status_func, void *context, struct auth_optional_param *opt_params, + enum auth_flags auth_flags); + + +/** + * Frees up any previously allocated resources. + * + * @param auth_conn Pointer to Authentication connection struct. + * + * @return AUTH_SUCCESS on success else one of AUTH_ERROR_* values. + */ +int auth_lib_deinit(struct authenticate_conn *auth_conn); + + +/** + * Starts the authentication process + * + * @param auth_conn Authentication connection struct. + * + * @return AUTH_SUCCESS on success else one of AUTH_ERROR_* values. + */ +int auth_lib_start(struct authenticate_conn *auth_conn); + +/** + * Returns the current status of the authentication process. + * + * @param auth_conn Authentication connection struct. + * + * @return One of AUTH_STATUS_* values + */ +enum auth_status auth_lib_get_status(struct authenticate_conn *auth_conn); + +/** + * Helper routine to return string corresponding to status + * + * @param status Authentication status value. + * + * @return Pointer to string representing the status. + */ +const char *auth_lib_getstatus_str(enum auth_status status); + + +/** + * Cancels the authentication process. Must wait until the AUTH_STATUS_CANCELED + * status is returned. + * + * @param auth_conn Authentication connection struct. + * + * @return One of AUTH_STATUS_* values + */ +int auth_lib_cancel(struct authenticate_conn *auth_conn); + + + +/** + * @} + */ + + +#ifdef __cplusplus +} +#endif + + +#endif /* ZEPHYR_INCLUDE_AUTH_LIB_H_ */ diff --git a/include/auth/auth_xport.h b/include/auth/auth_xport.h new file mode 100644 index 000000000000..d6f3b284d6cd --- /dev/null +++ b/include/auth/auth_xport.h @@ -0,0 +1,426 @@ +/** + * @file auth_xport.h + * + * @brief + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_AUTH_XPORT_H_ +#define ZEPHYR_INCLUDE_AUTH_XPORT_H_ + +#if defined(CONFIG_BT_XPORT) +#include +#endif + + +/** + * Transport functions and defines. + */ + + +/** + * Handle to lower transport, should be treated as opaque object. + * + * @note A typedef is use here because the transport handle is intended to + * be an opaque object to the lower transport layers and to the + * upper layers calling into the transport. This use satisfies + * the Linux coding standards. + */ +typedef void *auth_xport_hdl_t; + +/** + * The lower transport type. + */ +enum auth_xport_type { + AUTH_XP_TYPE_NONE = 0, + AUTH_XP_TYPE_BLUETOOTH, + AUTH_XP_TYPE_SERIAL, + AUTH_XP_TYPE_FUTURE /* new transports added here */ +}; + + +/** + * Transport event type. + */ +enum auth_xport_evt_type { + XP_EVT_NONE = 0, + XP_EVT_CONNECT, + XP_EVT_DISCONNECT, + XP_EVT_RECONNECT, + + /* transport specific events */ + XP_EVT_SERIAL_BAUDCHANGE +}; + +/** + * Transport event structure + */ +struct auth_xport_evt { + enum auth_xport_evt_type event; + + /* transport specific event information */ + void *xport_ctx; +}; + +/** + * Callback invoked when sending data asynchronously. + * + * @param err Error code, 0 == success. + * @param numbytes Number of bytes sent, can be 0. + */ +typedef void (*send_callback_t)(int err, uint16_t numbytes); + + +/** + * Function for sending data directly to the lower layer transport + * instead of putting data on an output queue. Some lower transport + * layers have the ability to queue outbound data, no need to double + * buffer. + * + * @param xport_hdl Opaque transport handle. + * @param data Data to send. + * @param len Number of bytes to send + * + * @return Number of bytes sent, on error negative error value. + */ +typedef int (*send_xport_t)(auth_xport_hdl_t xport_hdl, const uint8_t *data, + const size_t len); + + +/** + * Initializes the lower transport layer. + * + * @param xporthdl New transport handle is returned here. + * @param instance Authentication instance. + * @param xport_type Transport type + * @param xport_params Transport specific params, passed directly to lower transport. + * + * @return 0 on success, else negative error number. + */ +int auth_xport_init(auth_xport_hdl_t *xporthdl, enum auth_instance_id instance, + enum auth_xport_type xport_type, void *xport_params); + +/** + * De-initializes the transport. The lower layer transport should + * free any allocated resources. + * + * @param xporthdl + * + * @return AUTH_SUCCESS or negative error value. + */ +int auth_xport_deinit(const auth_xport_hdl_t xporthdl); + +/** + * Send event to the lower transport. + * + * @param xporthdl Transport handle. + * @param event Event + * + * @return 0 on success, else -1 + */ + +int auth_xport_event(const auth_xport_hdl_t xporthdl, struct auth_xport_evt *event); +/** + * Sends packet of data to peer. + * + * @param xporthdl Transport handle + * @param data Buffer to send. + * @param len Number of bytes to send. + * + * @return Number of bytes sent on success, can be less than requested. + * On error, negative error code. + */ +int auth_xport_send(const auth_xport_hdl_t xporthdl, const uint8_t *data, size_t len); + + +/** + * Receive data from the lower transport. + * + * @param xporthdl Transport handle + * @param buff Buffer to read bytes into. + * @param buf_len Size of buffer. + * @param timeoutMsec Wait timeout in milliseconds. If no bytes available, then + * wait timeoutMec milliseconds. If 0, then will not wait. + * + * @return Negative on error or timeout, else number of bytes received. + */ +int auth_xport_recv(const auth_xport_hdl_t xporthdl, uint8_t *buff, uint32_t buf_len, uint32_t timeoutMsec); + + +/** + * Peeks at the contents of the receive queue used by the lower transport. The + * data returned is not removed from the receive queue. + * + * @param xporthdl Transport handle + * @param buff Buffer to read bytes into. + * @param buf_len Size of buffer. + * + * @return Negative on error or timeout, else number of bytes peeked. + */ +int auth_xport_recv_peek(const auth_xport_hdl_t xporthdl, uint8_t *buff, uint32_t buf_len); + +/** + * Used by lower transport to put bytes reveived into rx queue. + * + * @param xporthdl Transport handle. + * @param buf Pointer to bytes to put. + * @param buflen Byte len of buffer. + * + * @return Number of bytes added to receive queue. + */ +int auth_xport_put_recv(const auth_xport_hdl_t xporthdl, const uint8_t *buf, size_t buflen); + + +/** + * Get the number of bytes queued for sending. + * + * @param xporthdl Transport handle. + * + * @return Number of queued bytes, negative value on error. + */ +int auth_xport_getnum_send_queued_bytes(const auth_xport_hdl_t xporthdl); + + +/** + * Get the number of bytes in the receive queue + * + * @param xporthdl Transport handle. + * + * @return Number of queued bytes, negative value on error. + */ +int auth_xport_getnum_recvqueue_bytes(const auth_xport_hdl_t xporthdl); + +/** + * Get the number of bytes in the receive queue, if no byte wait until + * bytes are received or time out. + * + * @param xporthdl Transport handle.l + * @param waitmsec Number of milliseconds to wait. + * + * @return Number of queued bytes, negative value on error. + */ +int auth_xport_getnum_recvqueue_bytes_wait(const auth_xport_hdl_t xporthdl, uint32_t waitmsec); + +/** + * Sets a direct send function to the lower transport layer instead of + * queuing bytes into an output buffer. Some lower transports can handle + * all of the necessary output queuing while others (serial UARTs for example) + * may not have the ability to queue outbound byes. + * + * @param xporthdl Transport handle. + * @param send_func Lower transport send function. + */ +void auth_xport_set_sendfunc(auth_xport_hdl_t xporthdl, send_xport_t send_func); + + +/** + * Used by the lower transport to set a context for a given transport handle. To + * clear a previously set context, use NULL as context pointer. + * + * @param xporthdl Transport handle. + * @param context Context pointer to set. + * + */ +void auth_xport_set_context(auth_xport_hdl_t xporthdl, void *context); + +/** + * Returns pointer to context. + * + * @param xporthdl Transport handle. + * + * @return Pointer to transport layer context, else NULL + */ +void *auth_xport_get_context(auth_xport_hdl_t xporthdl); + +/** + * Get the application max payload the lower transport can handle in one + * in one frame. The common transport functions will break up a larger + * application packet into multiple frames. + * + * @param xporthdl Transport handle. + * + * @return The max payload, or negative error number. + */ +int auth_xport_get_max_payload(const auth_xport_hdl_t xporthdl); + + +#if defined(CONFIG_BT_XPORT) + + +/** + * Bluetooth UUIDs for the Authentication service. + */ +#if defined(CONFIG_ALT_AUTH_BT_UUIDS) + +/** + * If desired, define differnet UUIDs in this header file to be included + * by the firmware application. + */ +#include "authlib_alt_bt_uuids.h" + +#else + +#define BT_BASE_AUTH_UUID "00000000-a3f6-4491-8b4d-b830f521243b" +#define BT_UUID_AUTH_SVC (0x3010) +#define BT_UUID_AUTH_SVC_CLIENT_CHAR (0x3015) +#define BT_UUID_AUTH_SVC_SERVER_CHAR (0x3016) + +#define AUTH_SERVICE_UUID_BYTES BT_UUID_128_ENCODE(BT_UUID_AUTH_SVC, 0xa3f6, 0x4491, 0x8b4d, 0xb830f521243b) +#define AUTH_SERVICE_UUID BT_UUID_INIT_128(AUTH_SERVICE_UUID_BYTES) + + +#define AUTH_SVC_CLIENT_CHAR_UUID BT_UUID_INIT_128( \ + BT_UUID_128_ENCODE(BT_UUID_AUTH_SVC_CLIENT_CHAR, 0xa3f6, 0x4491, 0x8b4d, 0xb830f521243b)) + +#define AUTH_SVC_SERVER_CHAR BT_UUID_INIT_128( \ + BT_UUID_128_ENCODE(BT_UUID_AUTH_SVC_SERVER_CHAR, 0xa3f6, 0x4491, 0x8b4d, 0xb830f521243b)) +#endif + + +/** + * Bluetooth params. + */ +struct auth_xp_bt_params { + struct bt_conn *conn; + bool is_central; + + /* The BT value handle used by the Central to send to the Peripheral. + * Not used by the Peripheral. */ + uint16_t server_char_hdl; + + /* Client attribute, used by peripheral to indicate data for client. + * Not used by the Central (client) */ + const struct bt_gatt_attr *client_attr; +}; + +/** + * Initialize Bluetooth transport + * + * @param xport_hdl Transport handle. + * @param flags Reserved for future use, should be set to 0. + * @param xport_param Pointer to Bluetooth transport params for use by BT layer. + * + * @return 0 on success, else negative error value + */ +int auth_xp_bt_init(const auth_xport_hdl_t xport_hdl, uint32_t flags, void *xport_param); + + +/** + * Deinit the lower BT later. + * + * @param xport_hdl The transport handle to de-initialize. + * + * @return 0 on success, else negative error number. + */ +int auth_xp_bt_deinit(const auth_xport_hdl_t xport_hdl); + +/* + * Called when the Central (client) writes to a Peripheral (server) characteristic. + * + * @param conn The BT connection. + * @param attr GATT attribute to write to. + * @param buf Data to write. + * @param len Number of bytes to write. + * @param offset Offset to start writing from. + * @param flags BT_GATT_WRITE_* flags + * + * @return Number of bytes written. + */ +ssize_t auth_xp_bt_central_write(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags); + + +/** + * Called by the Central (client) when a Peripheral (server) writes/updates a characteristic. + * This function is called by the Central BT stack when data is received by the Peripheral (server) + * + * @param conn Bluetooth connection struct. + * @param params Gatt subscription params. + * @param data Data from peripheral. + * @param length Number of bytes reived. + * + * @return BT_GATT_ITER_CONTINUE or BT_GATT_ITER_STOP + */ +uint8_t auth_xp_bt_central_notify(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, + const void *data, uint16_t length); + + +/** + * Sends Bluetooth event to lower Bluetooth transport. + * + * @param xporthdl Transport handle. + * @param event The event. + * + * @return AUTH_SUCCESS, else negative error code. + */ +int auth_xp_bt_event(const auth_xport_hdl_t xporthdl, struct auth_xport_evt *event); + +/** + * Gets the maximum payload for the Bluetooth link, which is the MTU less any + * Bluetooth link overhead. + * + * @param xporthdl Transport handle. + * + * @return Max application payload. + */ +int auth_xp_bt_get_max_payload(const auth_xport_hdl_t xporthdl); + +#endif /* CONFIG_BT_XPORT */ + + +#if defined(CONFIG_SERIAL_XPORT) + +/** + * Serial transport param. + */ +struct auth_xp_serial_params { + const struct device *uart_dev; /* pointer to Uart instance */ +}; + +/** + * Initialize Serial lower layer transport. + * + * @param xport_hdl Transport handle. + * @param flags RFU (Reserved for future use), set to 0. + * @param xport_params Serial specific transport parameters. + * + * @return 0 on success, else negative value. + */ +int auth_xp_serial_init(const auth_xport_hdl_t xport_hdl, uint32_t flags, void *xport_param); + + +/** + * Deinit lower serial transport. + * + * @param xport_hdl Transport handle + * + * @return 0 on success, else negative value. + */ +int auth_xp_serial_deinit(const auth_xport_hdl_t xport_hdl); + + +/** + * Sends an event to lower serial transport. + * + * @param xporthdl Transport handle. + * @param event The event. + * + * @return AUTH_SUCCESS, else negative error code. + */ +int auth_xp_serial_event(const auth_xport_hdl_t xporthdl, struct auth_xport_evt *event); + + +/** + * Gets the maximum payload for the serial link. + * + * @param xporthdl Transport handle. + * + * @return Max application payload. + */ +int auth_xp_serial_get_max_payload(const auth_xport_hdl_t xporthdl); + +#endif /* CONFIG_SERIAL_XPORT */ + + +#endif /* ZEPHYR_INCLUDE_AUTH_XPORT_H_ */ \ No newline at end of file diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 9ca2f968df2f..70833ca71654 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,3 +10,4 @@ add_subdirectory_ifdef(CONFIG_FNMATCH fnmatch) add_subdirectory(gui) add_subdirectory(os) add_subdirectory_ifdef(CONFIG_OPENAMP open-amp) +add_subdirectory(auth) diff --git a/lib/Kconfig b/lib/Kconfig index 04b1819ea15f..76a8bee97844 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -19,4 +19,6 @@ source "lib/posix/Kconfig" source "lib/open-amp/Kconfig" +source "lib/auth/Kconfig" + endmenu diff --git a/lib/auth/CMakeLists.txt b/lib/auth/CMakeLists.txt new file mode 100644 index 000000000000..e2c325588391 --- /dev/null +++ b/lib/auth/CMakeLists.txt @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 + +# Only use if CONFIG_AUTH_LIB is defined as True +if(CONFIG_AUTH_LIB) + zephyr_interface_library_named(authentication) + zephyr_library() + + zephyr_library_sources_ifdef(CONFIG_AUTH_LIB auth_lib.c auth_xport_common.c) + + zephyr_library_sources_ifdef(CONFIG_BT_XPORT auth_xport_bt.c) + + zephyr_library_sources_ifdef(CONFIG_SERIAL_XPORT auth_xport_serial.c) + + zephyr_library_sources_ifdef(CONFIG_AUTH_CHALLENGE_RESPONSE auth_chalresp.c) + + zephyr_library_sources_ifdef(CONFIG_AUTH_DTLS auth_dtls.c) +endif() + +# Is this the correct way to include Mbed TLS module directory? +if(CONFIG_MBEDTLS) + zephyr_include_directories($ENV{ZEPHYR_BASE}/../modules/crypto/mbedtls/include) + zephyr_include_directories($ENV{ZEPHYR_BASE}/../modules/crypto/mbedtls/configs) +endif() + diff --git a/lib/auth/Kconfig b/lib/auth/Kconfig new file mode 100644 index 000000000000..5ba980df516b --- /dev/null +++ b/lib/auth/Kconfig @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: Apache-2.0 + + + +menuconfig AUTH_LIB + bool "Authentication library" + help + This option enables the Authentication library. + +if AUTH_LIB + config AUTH_CHALLENGE_RESPONSE + bool "Challenge Response (Must use TINYCRYPT and TINYCRYPT_SHA256)" + help + Selects the Challenge Response authentication method. + Must use TINYCRYPT and TINYCRYPT_SHA256 + + config AUTH_DTLS + bool "DTLS Authentication" + help + Selects the DTLS authentication method. + + config AUTH_LOG_LEVEL + int "Authentication log level" + depends on LOG + range 0 4 + default 0 + help + Sets authentication log level + Levels are: + 0 OFF, do not write + 1 ERROR, only write LOG_ERR + 2 WARNING, write LOG_WRN in addition to previous level + 3 INFO, write LOG_INF in addition to previous levels + 4 DEBUG, write LOG_DBG in addition to previous levels + + config BT_XPORT + bool "Bluetooth Transport" + help + If using Bluetooth to authenticate peer. + + + config BT_ALT_AUTH_BT_UUIDS + bool "Authentication service alternate Bluetooth UUIDs" + depends on BT_XPORT + help + To use alternate Bluetooth Auth service UUIDs. Alternative UUIDs should be + set in authlib_alt_bt_uuids.h header and included with firmware app. See + auth_xport.h header file for details. + + + config SERIAL_XPORT + bool "Serial Transport" + help + If using serial port to authenticate peer. + + + config NUM_AUTH_INSTANCES + int "Number of authentication instances" + range 1 2 + default 1 + help + Each authentication instance uses a thread to authenticate with + a peer over the lower transport. It is possible to have multiple + authentication instances where one instance authenticates a peer + over Bluetooth and another authenticates over a serial link. Another example + would be a Bluetooth Central needing to authenticate multiple + connected Peripherals. + + config AUTH_THREAD_PRIORITY + int "Authentication thread priority." + default 0 + help + The priority of the authentication thread. + + +endif + diff --git a/lib/auth/auth_chalresp.c b/lib/auth/auth_chalresp.c new file mode 100644 index 000000000000..74ef6cb5ff05 --- /dev/null +++ b/lib/auth/auth_chalresp.c @@ -0,0 +1,637 @@ +/** + * @file auth_chalresp.c + * + * @brief Challenge-Response method for authenticating connection. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + + + +#define LOG_LEVEL CONFIG_AUTH_LOG_LEVEL +#include +LOG_MODULE_DECLARE(auth_lib, CONFIG_AUTH_LOG_LEVEL); + +#include "auth_internal.h" + +#define AUTH_SHA256_HASH (TC_SHA256_DIGEST_SIZE) +#define AUTH_SHARED_KEY_LEN (32u) +#define AUTH_CHALLENGE_LEN (32u) +#define AUTH_CHAL_RESPONSE_LEN AUTH_SHA256_HASH + +/* magic number to help identify and parse messages */ +#define CHALLENGE_RESP_SOH 0x65A2 + +/* Message IDs */ +#define AUTH_CLIENT_CHAL_MSG_ID 0x01 +#define AUTH_SERVER_CHALRESP_MSG_ID 0x02 +#define AUTH_CLIENT_CHALRESP_MSG_ID 0x03 +#define AUTH_CHALRESP_RESULT_MSG_ID 0x04 + +/* Timeout for receive */ +#define AUTH_RX_TIMEOUT_MSEC (3000u) + + +/* ensure structs are byte aligned */ +#pragma pack(push, 1) + +/** + * Header for challenge response messages + */ +struct chalresp_header { + uint16_t soh; /* start of header */ + uint8_t msg_id; +}; + +/** + * Sent by the client, contains a random challenge which the + * server will hash with the know pre-shared key. + */ +struct client_challenge { + struct chalresp_header hdr; + uint8_t client_challenge[AUTH_CHALLENGE_LEN]; +}; + +/** + * Server response to the client challenge. The server responds with + * the hash of the client challenge and a server random challenge. + */ +struct server_chal_response { + struct chalresp_header hdr; + + /* Server created hash of the client challenge and + * the shared key */ + uint8_t server_response[AUTH_CHAL_RESPONSE_LEN]; + + /* To be hashed with the shared key by the client */ + uint8_t server_challenge[AUTH_CHALLENGE_LEN]; +}; + +/** + * Response from client to server challenge. + */ +struct client_chal_resp { + struct chalresp_header hdr; + /* hash of server challenge with shared key */ + uint8_t client_response[AUTH_CHAL_RESPONSE_LEN]; +}; + +/* From Central or Peripheral indicating result of challenge-response */ +struct auth_chalresp_result { + struct chalresp_header hdr; + uint8_t result; /* 0 == success, 1 == failure */ +}; + +#pragma pack(pop) + + +/** + * Shared key. + * @brief In a production system, the shared key should be stored in a + * secure hardware store such as an ECC608A or TrustZone. + */ +static uint8_t default_shared_key[AUTH_SHARED_KEY_LEN] = { + 0xBD, 0x84, 0xDC, 0x6E, 0x5C, 0x77, 0x41, 0x58, 0xE8, 0xFB, 0x1D, 0xB9, 0x95, 0x39, 0x20, 0xE4, + 0xC5, 0x03, 0x69, 0x9D, 0xBC, 0x53, 0x08, 0x20, 0x1E, 0xF4, 0x72, 0x8E, 0x90, 0x56, 0x49, 0xA8 +}; + +/* default shared key if not set */ +static uint8_t *shared_key = default_shared_key; + +/* If caller specifies a new shared key, it is copied into this buffer. */ +static uint8_t chalresp_key[AUTH_SHARED_KEY_LEN]; + +/** + * Utility function to create the hash of the random challenge and the shared key. + * Uses Tiny Crypt hashing code. + * + * @param random_chal Pointer to 32 byte value to hash with shared key. + * @param hash Buffer where hash is returned. + * + * @return AUTH_SUCCESS on success, else error value. + */ +static int auth_chalresp_hash(const uint8_t *random_chal, uint8_t *hash) +{ + int err = 0; + struct tc_sha256_state_struct hash_state; + + tc_sha256_init(&hash_state); + + /* Update the hash with the random challenge followed by the shared key. */ + if ((err = tc_sha256_update(&hash_state, random_chal, AUTH_CHALLENGE_LEN)) != TC_CRYPTO_SUCCESS || + (err = tc_sha256_update(&hash_state, shared_key, AUTH_SHARED_KEY_LEN)) != TC_CRYPTO_SUCCESS) { + return AUTH_ERROR_CRYPTO; + } + + /* calc the final hash */ + err = tc_sha256_final(hash, &hash_state) == TC_CRYPTO_SUCCESS ? + AUTH_SUCCESS : AUTH_ERROR_CRYPTO; + + return err; +} + +/** + * Checks header and id. + * + * @param hdr Message header. + * @param msg_id Message ID to check for. + * + * @return true if message is valid, else false. + */ +static bool auth_check_msg(struct chalresp_header *hdr, const uint8_t msg_id) +{ + if ((hdr->soh != CHALLENGE_RESP_SOH) || (hdr->msg_id != msg_id)) { + return false; + } + + return true; +} + + +/** + * Sends a challenge to the server. + * + * @param auth_conn Authentication connection structure. + * @param random_chal Random 32 byte challenge to send. + * + * @return true if message send successfully, else false on error. + */ +static bool auth_client_send_challenge(struct authenticate_conn *auth_conn, const uint8_t *random_chal) +{ + int numbytes; + struct client_challenge chal; + + /* build and send challenge message to Peripheral */ + memset(&chal, 0, sizeof(chal)); + chal.hdr.soh = CHALLENGE_RESP_SOH; + chal.hdr.msg_id = AUTH_CLIENT_CHAL_MSG_ID; + + memcpy(&chal.client_challenge, random_chal, sizeof(chal.client_challenge)); + + /* send to server */ + numbytes = auth_xport_send(auth_conn->xport_hdl, (uint8_t *)&chal, sizeof(chal)); + + if ((numbytes <= 0) || (numbytes != sizeof(chal))) { + /* error */ + LOG_ERR("Error sending challenge to server, err: %d", numbytes); + return false; + } + + return true; +} + +/** + * Receives and processes the challenge response from the server. + * + * @param auth_conn Authentication connection structure. + * @param random_chal 32 byte challenge sent to the server. + * @param status Pointer to return authentication status. + * + * @return true on success, else false. + */ +static bool auth_client_recv_chal_resp(struct authenticate_conn *auth_conn, const uint8_t *random_chal, + enum auth_status *status) +{ + uint8_t hash[AUTH_CHAL_RESPONSE_LEN]; + int numbytes; + int err; + struct server_chal_response server_resp; + struct client_chal_resp client_resp; + struct auth_chalresp_result chal_result; + uint8_t *buf = (uint8_t *)&server_resp; + int len = sizeof(server_resp); + + while (len > 0) { + + numbytes = auth_xport_recv(auth_conn->xport_hdl, buf, len, AUTH_RX_TIMEOUT_MSEC); + + /* canceled ? */ + if (auth_conn->cancel_auth) { + *status = AUTH_STATUS_CANCELED; + return false; + } + + /* timed out, try to read again */ + if (numbytes == -EAGAIN) { + continue; + } + + if (numbytes <= 0) { + LOG_ERR("Failed to read server challenge response, err: %d", numbytes); + *status = AUTH_STATUS_FAILED; + return false; + } + + buf += numbytes; + len -= numbytes; + } + + /* check message */ + if (!auth_check_msg(&server_resp.hdr, AUTH_SERVER_CHALRESP_MSG_ID)) { + LOG_ERR("Invalid message received from the server."); + *status = AUTH_STATUS_FAILED; + return false; + } + + + /* Now verify response, is the response correct? Hash the random challenge + * with the shared key. */ + err = auth_chalresp_hash(random_chal, hash); + + if (err) { + LOG_ERR("Failed to calc hash, err: %d", err); + *status = AUTH_STATUS_FAILED; + return false; + } + + /* Does the response match what is expected? */ + if (memcmp(hash, server_resp.server_response, sizeof(hash))) { + /* authentication failed */ + LOG_ERR("Server authentication failed."); + *status = AUTH_STATUS_AUTHENTICATION_FAILED; + + /* send failed message to the Peripheral */ + memset(&chal_result, 0, sizeof(chal_result)); + chal_result.hdr.soh = CHALLENGE_RESP_SOH; + chal_result.hdr.msg_id = AUTH_CHALRESP_RESULT_MSG_ID; + chal_result.result = 1; + + numbytes = auth_xport_send(auth_conn->xport_hdl, (uint8_t *)&chal_result, sizeof(chal_result)); + + if ((numbytes <= 0) || (numbytes != sizeof(chal_result))) { + LOG_ERR("Failed to send authentication error result to server."); + } + + return false; + } + + /* init Client response message */ + memset(&client_resp, 0, sizeof(client_resp)); + client_resp.hdr.soh = CHALLENGE_RESP_SOH; + client_resp.hdr.msg_id = AUTH_CLIENT_CHALRESP_MSG_ID; + + /* Create response to the server's random challenge */ + err = auth_chalresp_hash(server_resp.server_challenge, client_resp.client_response); + + if (err) { + LOG_ERR("Failed to create server response to challenge, err: %d", err); + *status = AUTH_STATUS_FAILED; + return false; + } + + /* send Client's response to the Server's random challenge */ + numbytes = auth_xport_send(auth_conn->xport_hdl, (uint8_t *)&client_resp, sizeof(client_resp)); + + if ((numbytes <= 0) || (numbytes != sizeof(client_resp))) { + LOG_ERR("Failed to send Client response."); + *status = AUTH_STATUS_FAILED; + return false; + } + + /* so far so good, need to wait for Server response */ + *status = AUTH_STATUS_IN_PROCESS; + return true; +} + +/** + * Server waits and receives a message from the client. + * + * @param auth_conn Authentication connection structure. + * @param msgbuf Buffer to copy message into. + * @param msglen Buffer byte length. + * + * @return true if number of bytes were received, else false. + */ +static bool auth_server_recv_msg(struct authenticate_conn *auth_conn, uint8_t *msgbuf, size_t msglen) +{ + int numbytes; + + while ((int)msglen > 0) { + + numbytes = auth_xport_recv(auth_conn->xport_hdl, msgbuf, msglen, AUTH_RX_TIMEOUT_MSEC); + + if (auth_conn->cancel_auth) { + return false; + } + + /* timed out, retry */ + if (numbytes == -EAGAIN) { + continue; + } + + if (numbytes <= 0) { + return false; + } + + msgbuf += numbytes; + msglen -= numbytes; + } + + return true; +} + +/** + * Handles the client challenge, creates a hash of the challenge with the + * shared key. + * + * @param auth_conn Authentication connection structure. + * @param server_random_chal The server random challenge to be sent to the client. + * + * @return true on success, else false on error. + */ +static bool auth_server_recv_challenge(struct authenticate_conn *auth_conn, uint8_t *server_random_chal) +{ + struct client_challenge chal; + struct server_chal_response server_resp; + int numbytes; + + if (!auth_server_recv_msg(auth_conn, (uint8_t *)&chal, sizeof(chal))) { + LOG_ERR("Failed to receive client challenge message."); + return false; + } + + if (!auth_check_msg(&chal.hdr, AUTH_CLIENT_CHAL_MSG_ID)) { + LOG_ERR("Invalid message."); + return false; + } + + /* create response and send back to the Client */ + server_resp.hdr.soh = CHALLENGE_RESP_SOH; + server_resp.hdr.msg_id = AUTH_SERVER_CHALRESP_MSG_ID; + + /* copy the server's challenge for the client */ + memcpy(server_resp.server_challenge, server_random_chal, + sizeof(server_resp.server_challenge)); + + /* Now create the response for the Client */ + auth_chalresp_hash(chal.client_challenge, server_resp.server_response); + + /* Send response */ + numbytes = auth_xport_send(auth_conn->xport_hdl, (uint8_t *)&server_resp, sizeof(server_resp)); + + if ((numbytes <= 0) || (numbytes != sizeof(server_resp))) { + LOG_ERR("Failed to send challenge response to the Client."); + return false; + } + + return true; +} + +/** + * Handles the client response to the server challenge. + * + * @param auth_conn Authentication connection structure. + * @param server_random_chal The server random challenge sent to the client. + * @param status Status of the Challenge-Response authentication set here. + * + * @return true on success, else false. + */ +static bool auth_server_recv_chalresp(struct authenticate_conn *auth_conn, uint8_t *server_random_chal, + enum auth_status *status) +{ + struct client_chal_resp client_resp; + struct auth_chalresp_result result_resp; + uint8_t hash[AUTH_SHA256_HASH]; + int err, numbytes; + + memset(&client_resp, 0, sizeof(client_resp)); + + /* read just the header */ + if (!auth_server_recv_msg(auth_conn, (uint8_t *)&client_resp, sizeof(client_resp.hdr))) { + LOG_ERR("Failed to receive challenge response from the Client"); + *status = AUTH_STATUS_FAILED; + return false; + } + + /* This is a result message, means the Client failed to authenticate the Server. */ + if (client_resp.hdr.msg_id == AUTH_CHALRESP_RESULT_MSG_ID) { + + /* read the remainder of the message */ + auth_server_recv_msg(auth_conn, (uint8_t *)&result_resp.result, sizeof(result_resp.result)); + + /* Result should be non-zero, meaning an authentication failure. */ + if (result_resp.result != 0) { + LOG_ERR("Unexpected result value: %d", result_resp.result); + } + + LOG_ERR("Client authentication failed."); + *status = AUTH_STATUS_AUTHENTICATION_FAILED; + return false; + } + + /* The Client authenticated the Server (this code) response. Now verify the Client's + * response to the Server challenge. */ + if (!auth_server_recv_msg(auth_conn, (uint8_t *)&client_resp.hdr + sizeof(client_resp.hdr), + sizeof(client_resp) - sizeof(client_resp.hdr))) { + LOG_ERR("Failed to read Client response."); + *status = AUTH_STATUS_FAILED; + return false; + } + + err = auth_chalresp_hash(server_random_chal, hash); + if (err) { + LOG_ERR("Failed to create hash."); + *status = AUTH_STATUS_FAILED; + return false; + } + + /* init result response message */ + memset(&result_resp, 0, sizeof(result_resp)); + result_resp.hdr.soh = CHALLENGE_RESP_SOH; + result_resp.hdr.msg_id = AUTH_CHALRESP_RESULT_MSG_ID; + + /* verify Central's response */ + if (memcmp(hash, client_resp.client_response, sizeof(hash))) { + /* authentication failed, the Client did not sent the correct response */ + result_resp.result = 1; + } + + /* send result back to the Client */ + numbytes = auth_xport_send(auth_conn->xport_hdl, (uint8_t *)&result_resp, sizeof(result_resp)); + + if ((numbytes <= 0) || (numbytes != sizeof(result_resp))) { + LOG_ERR("Failed to send Client authentication result."); + *status = AUTH_STATUS_FAILED; + return false; + } + + *status = (result_resp.result == 0) ? AUTH_STATUS_SUCCESSFUL : AUTH_STATUS_AUTHENTICATION_FAILED; + + return true; +} + +/** + * Client function used to execute Challenge-Response authentication. + * + * @param auth_conn Authentication connection structure. + * + * @return AUTH_SUCCESS on success, else AUTH error code. + */ +static int auth_chalresp_client(struct authenticate_conn *auth_conn) +{ + int numbytes; + uint8_t random_chal[AUTH_CHALLENGE_LEN]; + struct auth_chalresp_result server_result; + enum auth_status status; + + /* generate random number as challenge */ + sys_rand_get(random_chal, sizeof(random_chal)); + + + if (!auth_client_send_challenge(auth_conn, random_chal)) { + auth_lib_set_status(auth_conn, AUTH_STATUS_FAILED); + return AUTH_ERROR_FAILED; + } + + /* check for cancel operation */ + if (auth_conn->cancel_auth) { + return AUTH_ERROR_CANCELED; + } + + /* read response from the sever */ + if (!auth_client_recv_chal_resp(auth_conn, random_chal, &status)) { + auth_lib_set_status(auth_conn, status); + return AUTH_ERROR_FAILED; + } + + /* Wait for the final response from the Server indicating success or failure + * of the Client's response. */ + numbytes = auth_xport_recv(auth_conn->xport_hdl, (uint8_t * ) &server_result, + sizeof(server_result), AUTH_RX_TIMEOUT_MSEC); + + /* check for cancel operation */ + if (auth_conn->cancel_auth) { + return AUTH_ERROR_CANCELED; + } + + if ((numbytes <= 0) || (numbytes != sizeof(server_result))) { + LOG_ERR("Failed to receive server authentication result."); + auth_lib_set_status(auth_conn, AUTH_STATUS_AUTHENTICATION_FAILED); + return AUTH_ERROR_FAILED; + } + + /* check message */ + if (!auth_check_msg(&server_result.hdr, AUTH_CHALRESP_RESULT_MSG_ID)) { + LOG_ERR("Server rejected Client response, authentication failed."); + auth_lib_set_status(auth_conn, AUTH_STATUS_AUTHENTICATION_FAILED); + return AUTH_ERROR_FAILED; + } + + /* check the Server result */ + if (server_result.result != 0) { + LOG_ERR("Authentication with server failed."); + auth_lib_set_status(auth_conn, AUTH_STATUS_AUTHENTICATION_FAILED); + return AUTH_ERROR_FAILED; + } + + LOG_INF("Authentication with server successful."); + auth_lib_set_status(auth_conn, AUTH_STATUS_SUCCESSFUL); + + return AUTH_SUCCESS; +} + +/** + * Server function used to execute Challenge-Response authentication. + * + * @param auth_conn Authentication connection structure. + * + * @return AUTH_SUCCESS on success, else AUTH error code. + */ +static int auth_chalresp_server(struct authenticate_conn *auth_conn) +{ + enum auth_status status; + uint8_t random_chal[AUTH_CHALLENGE_LEN]; + + /* generate random number as challenge */ + sys_rand_get(random_chal, sizeof(random_chal)); + + /* Wait for challenge from the Central */ + if (!auth_server_recv_challenge(auth_conn, random_chal)) { + auth_lib_set_status(auth_conn, AUTH_STATUS_FAILED); + return AUTH_ERROR_FAILED; + } + + /* check for cancel operation */ + if (auth_conn->cancel_auth) { + return AUTH_ERROR_CANCELED; + } + + /* Wait for challenge response from the Client */ + auth_server_recv_chalresp(auth_conn, random_chal, &status); + + auth_lib_set_status(auth_conn, status); + + if (status != AUTH_STATUS_SUCCESSFUL) { + LOG_INF("Authentication with Client failed."); + return AUTH_ERROR_FAILED; + } + + LOG_INF("Authentication with client successful."); + + return AUTH_SUCCESS; +} + + +/** + * @see auth_internal.h + */ +int auth_init_chalresp_method(struct authenticate_conn *auth_conn, struct auth_challenge_resp *chal_resp) +{ + /* verify inputs */ + if ((auth_conn == NULL) || (chal_resp == NULL)) { + return AUTH_ERROR_INVALID_PARAM; + } + + /* set new shared key */ + memcpy(chalresp_key, chal_resp->shared_key, AUTH_SHARED_KEY_LEN); + + /* set shared key pointer to new key */ + shared_key = chalresp_key; + + return AUTH_SUCCESS; +} + + +/** + * Use hash (SHA-256) with shared key to authenticate each side. + * + * @param auth_conn The authenticate_conn connection. + */ +void auth_chalresp_thread(struct authenticate_conn *auth_conn) +{ + int ret; + + auth_lib_set_status(auth_conn, AUTH_STATUS_STARTED); + + /** + * Since a device can be a client and server at the same + * time need to use is_client to determine which funcs to call. + * refactor this code. */ + if (auth_conn->is_client) { + ret = auth_chalresp_client(auth_conn); + } else { + ret = auth_chalresp_server(auth_conn); + } + + if (ret) { + LOG_ERR("Challenge-Response authentication failed, err: %d", ret); + } else { + LOG_INF("Successful Challenge-Response."); + } + + /* End of Challenge-Response authentication thread */ + LOG_DBG("Challenge-Response thread complete."); +} diff --git a/lib/auth/auth_dtls.c b/lib/auth/auth_dtls.c new file mode 100644 index 000000000000..dfcfd75a2c74 --- /dev/null +++ b/lib/auth/auth_dtls.c @@ -0,0 +1,959 @@ +/** + * @file BLE Authentication using DTLS + * + * @brief DTLS authentication code using Mbed DTLS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(CONFIG_MBEDTLS) +#if !defined(CONFIG_MBEDTLS_CFG_FILE) +#include "mbedtls/config.h" +#else +#include CONFIG_MBEDTLS_CFG_FILE +#endif +#endif /* CONFIG_MBEDTLS_CFG_FILE */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_AUTH_LOG_LEVEL +#include +LOG_MODULE_DECLARE(auth_lib, CONFIG_AUTH_LOG_LEVEL); + +#include +#include "auth_internal.h" + +#define MBED_ERROR_BUFLEN (150u) +#define MAX_MBEDTLS_CONTEXT (CONFIG_NUM_AUTH_INSTANCES) +#define AUTH_DTLS_COOKIE_LEN (32u) + +#define DTLS_PACKET_SYNC_BYTES (0x45B8) +#define DTLS_HEADER_BYTES (sizeof(struct dtls_packet_hdr)) + +#define AUTH_DTLS_MIN_TIMEOUT (10000u) +#define ATUH_DTLS_MAX_TIMEOUT (30000u) +#define AUTH_DTLS_HELLO_WAIT_MSEC (15000u) + + +/** + * Header identifying a DTLS packet (aka datagram). Unlike TLS, DTLS packets + * must be forwarded to Mbedtls as one or more complete packets. TLS is + * design to handle an incoming byte stream. + */ +#pragma pack(push, 1) +struct dtls_packet_hdr { + uint16_t sync_bytes; /* use magic number to identify header */ + uint16_t packet_len; /* size of DTLS datagram */ +}; +#pragma pack(pop) + + + +/** + * Mbed context, one context per DTLS connection. + */ +struct mbed_tls_context { + bool in_use; + + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + mbedtls_x509_crt cacert; + mbedtls_x509_crt device_cert; + mbedtls_pk_context device_private_key; + mbedtls_timing_delay_context timer; + mbedtls_ssl_cookie_ctx cookie_ctx; + + /* Temp buffer used to assemble full frame when sending. */ + uint8_t temp_dtlsbuf[CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN]; + + /* cookie used for DTLS */ + uint8_t cookie[AUTH_DTLS_COOKIE_LEN]; +}; + +/** + * List of Mbed instances + */ +static struct mbed_tls_context tlscontext[MAX_MBEDTLS_CONTEXT]; + + +/* ===================== local functions =========================== */ + +/** + * Get a free Mbed context to use with an authentication instance. + * + * @return Pointer to context, NULL if no free context available. + */ +static struct mbed_tls_context *auth_get_mbedcontext(void) +{ + // use mutex lock to protect accessing list + for (int cnt = 0; cnt < MAX_MBEDTLS_CONTEXT; cnt++) { + + if (!tlscontext[cnt].in_use) { + tlscontext[cnt].in_use = true; + return &tlscontext[cnt]; + } + } + + return NULL; +} + +/** + * Free a Mbed context. + * + * @param mbed_ctx Pointer to context. + */ +static void auth_free_mbedcontext(struct mbed_tls_context *mbed_ctx) +{ + mbed_ctx->in_use = false; + + /* Free any MBed tls resources */ + mbedtls_x509_crt_free(&mbed_ctx->cacert); + mbedtls_x509_crt_free(&mbed_ctx->device_cert); + mbedtls_pk_free(&mbed_ctx->device_private_key); + mbedtls_ssl_free(&mbed_ctx->ssl); + mbedtls_ssl_config_free(&mbed_ctx->conf); + mbedtls_ctr_drbg_free(&mbed_ctx->ctr_drbg); + mbedtls_entropy_free(&mbed_ctx->entropy); +} + +/** + * Initialize Mbed context. + * + * @param mbed_ctx Pointer to context. + */ +static void auth_init_context(struct mbed_tls_context *mbed_ctx) +{ + mbedtls_ssl_init(&mbed_ctx->ssl); + mbedtls_ssl_config_init(&mbed_ctx->conf); + mbedtls_x509_crt_init(&mbed_ctx->cacert); + mbedtls_x509_crt_init(&mbed_ctx->device_cert); + mbedtls_pk_init(&mbed_ctx->device_private_key); + mbedtls_ssl_cookie_init(&mbed_ctx->cookie_ctx); + mbedtls_ctr_drbg_init(&mbed_ctx->ctr_drbg); + mbedtls_entropy_init(&mbed_ctx->entropy); + + sys_rand_get(mbed_ctx->cookie, sizeof(mbed_ctx->cookie)); +} + +/** + * Return the handshake state name, helpful for debug purposes. + * + * @param state The state enumeration. + * + * @return Pointer to handshake string name. + */ +static const char *auth_tls_handshake_state(const mbedtls_ssl_states state) +{ + switch (state) { + + case MBEDTLS_SSL_HELLO_REQUEST: + return "MBEDTLS_SSL_HELLO_REQUEST"; + break; + + case MBEDTLS_SSL_CLIENT_HELLO: + return "MBEDTLS_SSL_CLIENT_HELLO"; + break; + + case MBEDTLS_SSL_SERVER_HELLO: + return "MBEDTLS_SSL_SERVER_HELLO"; + break; + + case MBEDTLS_SSL_SERVER_CERTIFICATE: + return "MBEDTLS_SSL_SERVER_CERTIFICATE"; + break; + + case MBEDTLS_SSL_SERVER_KEY_EXCHANGE: + return "MBEDTLS_SSL_SERVER_KEY_EXCHANGE"; + break; + + case MBEDTLS_SSL_CERTIFICATE_REQUEST: + return "MBEDTLS_SSL_CERTIFICATE_REQUEST"; + break; + + case MBEDTLS_SSL_SERVER_HELLO_DONE: + return "MBEDTLS_SSL_SERVER_HELLO_DONE"; + break; + + case MBEDTLS_SSL_CLIENT_CERTIFICATE: + return "MBEDTLS_SSL_CLIENT_CERTIFICATE"; + break; + + case MBEDTLS_SSL_CLIENT_KEY_EXCHANGE: + return "MBEDTLS_SSL_CLIENT_KEY_EXCHANGE"; + break; + + case MBEDTLS_SSL_CERTIFICATE_VERIFY: + return "MBEDTLS_SSL_CERTIFICATE_VERIFY"; + break; + + case MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC: + return "MBEDTLS_SSL_CLIENT_CHANGE_CIPHER_SPEC"; + break; + + case MBEDTLS_SSL_CLIENT_FINISHED: + return "MBEDTLS_SSL_CLIENT_FINISHED"; + break; + + case MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC: + return "MBEDTLS_SSL_SERVER_CHANGE_CIPHER_SPEC"; + break; + + case MBEDTLS_SSL_SERVER_FINISHED: + return "MBEDTLS_SSL_SERVER_FINISHED"; + break; + + case MBEDTLS_SSL_FLUSH_BUFFERS: + return "MBEDTLS_SSL_FLUSH_BUFFERS"; + break; + + case MBEDTLS_SSL_HANDSHAKE_WRAPUP: + return "MBEDTLS_SSL_HANDSHAKE_WRAPUP"; + break; + + case MBEDTLS_SSL_HANDSHAKE_OVER: + return "MBEDTLS_SSL_HANDSHAKE_OVER"; + break; + + case MBEDTLS_SSL_SERVER_NEW_SESSION_TICKET: + return "MBEDTLS_SSL_SERVER_NEW_SESSION_TICKET"; + break; + + case MBEDTLS_SSL_SERVER_HELLO_VERIFY_REQUEST_SENT: + return "MBEDTLS_SSL_SERVER_HELLO_VERIFY_REQUEST_SENT"; + break; + + default: + break; + } + + return "unknown"; +} + + +/** + * Gets the time since boot in Milliseconds, used for DTLS retry timer. The absolute + * time (wall clock time) isn't necessary, just the relative time in milliseconds. + * + * @param val Timer value + * @param reset If non-zero then set current milliseconds into the time var. + * + * @return 0 if resetting the time, else the time difference in milliseconds. + */ +static unsigned long auth_tls_timing_get_timer(struct mbedtls_timing_hr_time *val, int reset) +{ + int64_t delta; + int64_t *mssec = (int64_t *) val; + int64_t cur_msg = k_uptime_get(); + + if (reset) { + *mssec = cur_msg; + return 0; + } + + delta = cur_msg - *mssec; + + return (unsigned long )delta; +} + +/** + * Set delays to watch, final and intermediate delays. + * + * @param data Timing delay context. + * @param int_ms Intermediate delay in milliseconds. + * @param fin_ms Final delay in milliseconds. + * + */ +static void auth_tls_timing_set_delay(void *data, uint32_t int_ms, uint32_t fin_ms) +{ + mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *) data; + + ctx->int_ms = int_ms; + ctx->fin_ms = fin_ms; + + if (fin_ms != 0) { + (void) auth_tls_timing_get_timer(&ctx->timer, 1); + } +} + +/** + * Determine the delay result. + * + * @param data Pointer to delay context. + * + * @return -1 if cancelled, 0 if none of the delays is expired, + * 1 if the intermediate delay only is expired, + * 2 if the final delay is expired + */ +static int auth_tls_timing_get_delay(void *data) +{ + mbedtls_timing_delay_context *ctx = (mbedtls_timing_delay_context *)data; + int64_t elapsed_ms; + + if (ctx->fin_ms == 0) { + return -1; + } + + elapsed_ms = auth_tls_timing_get_timer(&ctx->timer, 0); + + if (elapsed_ms >= (int64_t)ctx->fin_ms) { + return 2; + } + + if (elapsed_ms >= (int64_t)ctx->int_ms) { + return 1; + } + + return 0; +} + + +/** + * Function called by Mbed stack to print debug messages. + * + * @param ctx Context + * @param level Debug level + * @param file Source filename of debug log entry. + * @param line Line number of debug log entry. + * @param str Debug/Log message. + */ +static void auth_mbed_debug(void *ctx, int level, const char *file, + int line, const char *str) +{ + const char *p, *basename; + + /** + * @brief Need to define const string here vs. const char *fmt = "[%s:%d] %s" + * because the LOG_ERR(), LOG_* macros can't handle a pointer. + */ +#define LOG_FMT "[%s:%d] %s" + + (void)ctx; + + if (!file || !str) { + return; + } + + /* Extract basename from file */ + for (p = basename = file; *p != '\0'; p++) { + if (*p == '/' || *p == '\\') { + basename = p + 1; + } + } + + + switch (level) { + + case 0: + { + LOG_ERR(LOG_FMT, log_strdup(basename), line, log_strdup(str)); + break; + } + + case 1: + { + LOG_WRN(LOG_FMT, log_strdup(basename), line, log_strdup(str)); + break; + } + + case 2: + { + LOG_INF(LOG_FMT, log_strdup(basename), line, log_strdup(str)); + break; + } + + case 3: + default: + { + LOG_DBG(LOG_FMT, log_strdup(basename), line, log_strdup(str)); + break; + } + } +} + + + +/** + * Mbed routine to send data, called by Mbed TLS library. + * + * @param ctx Context pointer, pointer to struct authenticate_conn + * @param buf Buffer to send. + * @param len Number of bytes to send. + * + * @return Number of bytes sent, else Mbed tls error. + */ +static int auth_mbedtls_tx(void *ctx, const uint8_t *buf, size_t len) +{ + int send_cnt; + struct authenticate_conn *auth_conn = (struct authenticate_conn *)ctx; + + if (auth_conn == NULL) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + struct dtls_packet_hdr *dtls_hdr; + struct mbed_tls_context *mbedctx = (struct mbed_tls_context *)auth_conn->internal_obj; + + if (mbedctx == NULL) { + LOG_ERR("Missing Mbed context."); + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + dtls_hdr = (struct dtls_packet_hdr *)mbedctx->temp_dtlsbuf; + + /** + * DTLS is targeted for the UDP datagram protocol, as such the Mbed stack + * expects a full DTLS packet (ie datagram) to be receive vs. a partial + * packet. When sending, add a header to enable the receiving side + * to determine when a full DTLS packet has been recevid. + */ + + /* Check the temp buffer is large enough */ + if ((sizeof(mbedctx->temp_dtlsbuf) - DTLS_HEADER_BYTES) < len) { + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + /* set byte order to Big Endian when sending over lower transport. */ + dtls_hdr->sync_bytes = sys_cpu_to_be16(DTLS_PACKET_SYNC_BYTES); + + /* does not include header */ + dtls_hdr->packet_len = sys_cpu_to_be16((uint16_t)len); + + /* Combine the header with the payload. This maximizes the lower + * transport throughput vs. sending the DTLS header separately then + * sending the body. */ + memcpy(&mbedctx->temp_dtlsbuf[DTLS_HEADER_BYTES], buf, len); + + /* send to peripheral */ + send_cnt = auth_xport_send(auth_conn->xport_hdl, mbedctx->temp_dtlsbuf, + len + DTLS_HEADER_BYTES); + + + if (send_cnt < 0) { + LOG_ERR("Failed to send, err: %d", send_cnt); + return MBEDTLS_ERR_NET_SEND_FAILED; + } + + LOG_INF("Send %d byes.", send_cnt); + + /* return number bytes sent, do not include the DTLS header */ + send_cnt -= DTLS_HEADER_BYTES; + + return send_cnt; +} + +/** + * Mbed receive function, called by Mbed code. + * + * @param ctx Context. + * @param buffer Buffer to copy received bytes into. + * @param len Buffer byte size. + * + * @return Number of bytes copied or negative number on error. + */ +static int auth_mbedtls_rx(void *ctx, uint8_t *buffer, size_t len) +{ + struct authenticate_conn *auth_conn = (struct authenticate_conn *)ctx; + int rx_bytes = 0; + struct dtls_packet_hdr dtls_hdr; + uint16_t packet_len = 0; + + /** + * DTLS is targeted for the UDP datagram protocol, as such the Mbed stack + * expects a full DTLS packet (ie datagram) to be receive vs. a partial + * packet. For the lower transports, a full datagram packet maybe broken + * up into multiple fragments. The receive queue may contain a partial + * DTLS frame. The code here waits until a full DTLS packet is received. + */ + + /* Will wait until a full DTLS packet is received. */ + while (true) { + + rx_bytes = auth_xport_getnum_recvqueue_bytes_wait(auth_conn->xport_hdl, 1000u); + + /* Check for canceled flag */ + if (auth_conn->cancel_auth) { + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + /* no bytes or timed out */ + if (rx_bytes == 0 || rx_bytes == -EAGAIN) { + continue; + } + + /* an error */ + if (rx_bytes < 0) { + /* an error occurred */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + if (rx_bytes < DTLS_HEADER_BYTES) { + continue; + } + + /* peek into receive queue */ + auth_xport_recv_peek(auth_conn->xport_hdl, (uint8_t *)&dtls_hdr, + sizeof(struct dtls_packet_hdr)); + + /* check for sync bytes */ + if (sys_be16_to_cpu(dtls_hdr.sync_bytes) != DTLS_PACKET_SYNC_BYTES) { + // read bytes and try to peek again + auth_xport_recv(auth_conn->xport_hdl, (uint8_t *)&dtls_hdr, + sizeof(struct dtls_packet_hdr), 1000u); + continue; + } + + // have valid DTLS packet header, check packet length + dtls_hdr.packet_len = sys_be16_to_cpu(dtls_hdr.packet_len); + + /* Is there enough room to copy into Mbedtls buffer? */ + if (dtls_hdr.packet_len > len) { + return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL; + } + + /* Zero length packet, ignore */ + if (dtls_hdr.packet_len == 0u) { + LOG_ERR("Empty DTLS packet."); + /* Read the DTLS header and return */ + auth_xport_recv(auth_conn->xport_hdl, (uint8_t *)&dtls_hdr, + sizeof(struct dtls_packet_hdr), 1000u); + return 0; + } + + /* rx_bytes must be at least DTLS_HEADER_BYTES in length here. Enough + * to fill a complete DTLS packet. */ + if ((int)dtls_hdr.packet_len <= (rx_bytes - (int)DTLS_HEADER_BYTES)) { + + packet_len = dtls_hdr.packet_len; + + /* copy packet into mbed buffers */ + /* read header, do not forward to Mbed */ + auth_xport_recv(auth_conn->xport_hdl, (uint8_t *)&dtls_hdr, + sizeof(struct dtls_packet_hdr), 1000u); + + /* read packet into mbed buffer*/ + rx_bytes = auth_xport_recv(auth_conn->xport_hdl, buffer, + packet_len, 1000u); + + if (rx_bytes <= 0) { + /* an error occurred */ + return MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } + + len -= rx_bytes; + buffer += rx_bytes; + + /* we're done with one DTLS packet, return */ + return rx_bytes; + } + + /** + * If we're here it means we have a partial DTLS packet, + * wait for more data until there is enough to fill a + * complete DTLS packet. + */ + LOG_DBG("Waiting for more bytes to fill DTLS packet."); + } + + /* Didn't send any bytes */ + return 0; +} + + +/** + * Set the DTLS cookie. + * + * @param auth_conn Pointer to auth connection + * + * @return 0 on success, else error code. + */ +static int auth_tls_set_cookie(struct authenticate_conn *auth_conn) +{ + int ret; + struct mbed_tls_context *mbed_ctx = (struct mbed_tls_context *)auth_conn->internal_obj; + + /* should not be NULL!! */ + if (!mbed_ctx) { + LOG_ERR("No MBED context."); + return AUTH_ERROR_INVALID_PARAM; + } + + ret = mbedtls_ssl_set_client_transport_id(&mbed_ctx->ssl, mbed_ctx->cookie, + sizeof(mbed_ctx->cookie)); + + return ret; +} + +/** + * Pseudo random source for Mbed + * + * @param data Optional entropy source. + * @param output Buffer to receive random numbers. + * @param len Number of bytes requested. + * @param olen Number of bytes actually returned. + * + * @return Always zero. + */ +static int auth_tls_entropy(void *data, unsigned char *output, size_t len, + size_t *olen) +{ + (void) data; + + sys_rand_get(output, len); + *olen = len; + + return 0; +} + + + +/* ================= external/internal funcs ==================== */ +/** + * @see auth_internal.h + * + */ +int auth_init_dtls_method(struct authenticate_conn *auth_conn, struct auth_dtls_certs *certs) +{ + struct mbed_tls_context *mbed_ctx; + int ret; + + LOG_DBG("Initializing Mbed DTLS"); + + /* get, init, and set context pointer */ + mbed_ctx = auth_get_mbedcontext(); + + if (mbed_ctx == NULL) { + LOG_ERR("Unable to allocate Mbed TLS context."); + return AUTH_ERROR_NO_RESOURCE; + } + + /* Init mbed context */ + auth_init_context(mbed_ctx); + + + /* Save MBED tls context as internal object. The intent of using a void + * 'internal_obj' is to provide a var in the struct authentication_conn to + * store different authentication methods. Instead of Mbed, it maybe a + * Challenge-Response.*/ + auth_conn->internal_obj = mbed_ctx; + + int endpoint = auth_conn->is_client ? MBEDTLS_SSL_IS_CLIENT : MBEDTLS_SSL_IS_SERVER; + + mbedtls_ssl_config_defaults(&mbed_ctx->conf, + endpoint, + MBEDTLS_SSL_TRANSPORT_DATAGRAM, + MBEDTLS_SSL_PRESET_DEFAULT); + + + /* set the lower layer transport functions */ + mbedtls_ssl_set_bio(&mbed_ctx->ssl, auth_conn, auth_mbedtls_tx, auth_mbedtls_rx, NULL); + + /* set max record len to 512, as small as possible */ + mbedtls_ssl_conf_max_frag_len(&mbed_ctx->conf, MBEDTLS_SSL_MAX_FRAG_LEN_512); + + /* Set the DTLS time out */ + mbedtls_ssl_conf_handshake_timeout(&mbed_ctx->conf, AUTH_DTLS_MIN_TIMEOUT, + ATUH_DTLS_MAX_TIMEOUT); + + /* Force verification. */ + mbedtls_ssl_conf_authmode(&mbed_ctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED); + + + if ((certs->device_cert_pem.priv_key == NULL) || (certs->device_cert_pem.priv_key_size == 0)) { + auth_free_mbedcontext(mbed_ctx); + LOG_ERR("Failed to get device private key"); + return AUTH_ERROR_DTLS_INIT_FAILED; + } + + + ret = mbedtls_pk_parse_key(&mbed_ctx->device_private_key, certs->device_cert_pem.priv_key, + certs->device_cert_pem.priv_key_size, NULL, 0); + + if (ret) { + auth_free_mbedcontext(mbed_ctx); + LOG_ERR("Failed to parse device private key, error: 0x%x", ret); + return AUTH_ERROR_DTLS_INIT_FAILED; + } + + /* Setup certs, the CA chain followed by the end device cert. */ + if ((certs->server_ca_chain_pem.cert == NULL) || (certs->server_ca_chain_pem.cert_size == 0)) { + auth_free_mbedcontext(mbed_ctx); + LOG_ERR("Failed to get CA cert chain"); + return AUTH_ERROR_DTLS_INIT_FAILED; + } + + /* Parse and set the CA certs */ + ret = mbedtls_x509_crt_parse(&mbed_ctx->cacert, certs->server_ca_chain_pem.cert, + certs->server_ca_chain_pem.cert_size); + + if (ret) { + auth_free_mbedcontext(mbed_ctx); + LOG_ERR("Failed to parse CA cert, error: 0x%x", ret); + return AUTH_ERROR_DTLS_INIT_FAILED; + } + + /* set CA certs into context */ + mbedtls_ssl_conf_ca_chain(&mbed_ctx->conf, &mbed_ctx->cacert, NULL); + + /* Get and parse the device cert */ + if ((certs->device_cert_pem.cert == NULL) || (certs->device_cert_pem.cert_size == 0)) { + auth_free_mbedcontext(mbed_ctx); + LOG_ERR("Failed to get device cert"); + return AUTH_ERROR_DTLS_INIT_FAILED; + } + + ret = mbedtls_x509_crt_parse(&mbed_ctx->device_cert, (const unsigned char *)certs->device_cert_pem.cert, + certs->device_cert_pem.cert_size); + + if (ret) { + auth_free_mbedcontext(mbed_ctx); + LOG_ERR("Failed to parse device cert, error: 0x%x", ret); + return AUTH_ERROR_DTLS_INIT_FAILED; + } + + /* Parse and set the device cert */ + ret = mbedtls_ssl_conf_own_cert(&mbed_ctx->conf, &mbed_ctx->device_cert, + &mbed_ctx->device_private_key); + + if (ret) { + auth_free_mbedcontext(mbed_ctx); + LOG_ERR("Failed to set device cert and key, error: 0x%x", ret); + return AUTH_ERROR_DTLS_INIT_FAILED; + } + + /* set entropy source */ + mbedtls_entropy_add_source(&mbed_ctx->entropy, auth_tls_entropy, NULL, + 32, MBEDTLS_ENTROPY_SOURCE_STRONG); + + mbedtls_ctr_drbg_seed(&mbed_ctx->ctr_drbg, mbedtls_entropy_func, + &mbed_ctx->entropy, NULL, 0); + + /* setup call to Zephyr random API */ + mbedtls_ssl_conf_rng(&mbed_ctx->conf, mbedtls_ctr_drbg_random, &mbed_ctx->ctr_drbg); + + mbedtls_ssl_conf_dbg(&mbed_ctx->conf, auth_mbed_debug, auth_conn); + +#if defined(MBEDTLS_DEBUG_C) + mbedtls_debug_set_threshold(CONFIG_MBEDTLS_DEBUG_LEVEL); +#endif + + + if (!auth_conn->is_client) { + + auth_tls_set_cookie(auth_conn); + + ret = mbedtls_ssl_cookie_setup(&mbed_ctx->cookie_ctx, + mbedtls_ctr_drbg_random, + &mbed_ctx->ctr_drbg); + + if (ret) { + auth_free_mbedcontext(mbed_ctx); + LOG_ERR("Failed to setup dtls cookies, error: 0x%x", ret); + return AUTH_ERROR_DTLS_INIT_FAILED; + } + + mbedtls_ssl_conf_dtls_cookies(&mbed_ctx->conf, + mbedtls_ssl_cookie_write, + mbedtls_ssl_cookie_check, + &mbed_ctx->cookie_ctx); + } + + ret = mbedtls_ssl_setup(&mbed_ctx->ssl, &mbed_ctx->conf); + + if (ret) { + auth_free_mbedcontext(mbed_ctx); + LOG_ERR("mbedtls_ssl_setup returned %d", ret); + return AUTH_ERROR_DTLS_INIT_FAILED; + } + + /* Setup timers */ + mbedtls_ssl_set_timer_cb(&mbed_ctx->ssl, &mbed_ctx->timer, + auth_tls_timing_set_delay, + auth_tls_timing_get_delay); + + return AUTH_SUCCESS; +} + + +/** + * If performing a DTLS handshake + * + * @param auth_conn The auth connection/instance. + * + */ +void auth_dtls_thead(struct authenticate_conn *auth_conn) +{ + char err_buf[MBED_ERROR_BUFLEN]; + int bytecount = 0; + struct mbed_tls_context *mbed_ctx = (struct mbed_tls_context *) auth_conn->internal_obj; + + /* Set status */ + auth_lib_set_status(auth_conn, AUTH_STATUS_STARTED); + + /** + * For the server we can noty start the handshake, the code will continue + * to read looking for a "Client Hello". So we'll just stay at the + * MBEDTLS_SSL_CLIENT_HELLO state until the central sends the "Client Hello" + * + * For the client, a client hello will be sent immediately. + */ + if (!auth_conn->is_client) { + + /** + * For the the DTLS server, use the auth connection handle as the cookie. + */ + int ret = auth_tls_set_cookie(auth_conn); + + if (ret) { + LOG_ERR("Failed to get connection info for DTLS cookie, auth failed, error: 0x%x", ret); + auth_lib_set_status(auth_conn, AUTH_STATUS_FAILED); + return; + } + + /* Sit in a loop waiting for the initial Client Hello message + * from the client. */ + while (bytecount == 0) { + + if (auth_conn->cancel_auth) { + LOG_INF("DTLS authentication canceled."); + return; + } + + /* Server, wait for client hello */ + bytecount = auth_xport_getnum_recvqueue_bytes_wait(auth_conn->xport_hdl, + AUTH_DTLS_HELLO_WAIT_MSEC); + + if (bytecount == -EAGAIN) { + /* simply timed out waiting for client hello, try again */ + LOG_INF("Timeout waiting for Client Hello"); + continue; + } + + if (bytecount < 0) { + LOG_ERR("Server, error when waiting for client hello, error: %d", bytecount); + auth_lib_set_status(auth_conn, AUTH_STATUS_FAILED); + return; + } + } + + LOG_INF("Server received initial Client Hello from client."); + } + + /* Set status */ + auth_lib_set_status(auth_conn, AUTH_STATUS_IN_PROCESS); + + int ret = 0; + + /* start handshake */ + do { + + while (mbed_ctx->ssl.state != MBEDTLS_SSL_HANDSHAKE_OVER && + !auth_conn->cancel_auth) { + + LOG_INF("Handshake state: %s", auth_tls_handshake_state(mbed_ctx->ssl.state)); + + /* do handshake step */ + ret = mbedtls_ssl_handshake_step(&mbed_ctx->ssl); + + if (ret != 0) { + break; + } + } + + + if (auth_conn->cancel_auth) { + LOG_INF("Authentication canceled."); + break; + } + + if (ret == MBEDTLS_ERR_SSL_HELLO_VERIFY_REQUIRED) { + + /* restart handshake to process client cookie */ + LOG_INF("Restarting handshake, need client cookie."); + + /* reset session and cookie info */ + mbedtls_ssl_session_reset(&mbed_ctx->ssl); + + ret = auth_tls_set_cookie(auth_conn); + + if (ret) { + LOG_ERR("Failed to reset cookie information, error: 0x%x", ret); + ret = MBEDTLS_ERR_SSL_INTERNAL_ERROR; + } else { + ret = MBEDTLS_ERR_SSL_WANT_READ; + } + + } + + if (auth_conn->cancel_auth) { + LOG_INF("Authentication canceled."); + break; + } + + } while (ret == MBEDTLS_ERR_SSL_WANT_READ || + ret == MBEDTLS_ERR_SSL_WANT_WRITE); + + + if (mbed_ctx->ssl.state == MBEDTLS_SSL_HANDSHAKE_OVER) { + LOG_INF("DTLS Handshake success."); + ret = AUTH_SUCCESS; + } else { + /* All of the MBed error are of the form -0xXXXX. To display + * correctly negate the error value, thus -ret */ + LOG_ERR("DTLS Handshake failed, error: -0x%x", -ret); + mbedtls_strerror(ret, err_buf, sizeof(err_buf)); + LOG_ERR("%s", log_strdup(err_buf)); + } + + + enum auth_status auth_status; + + switch (ret) { + case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE: + case MBEDTLS_ERR_SSL_BAD_HS_CERTIFICATE_VERIFY: + auth_status = AUTH_STATUS_AUTHENTICATION_FAILED; + break; + + case AUTH_SUCCESS: + auth_status = AUTH_STATUS_SUCCESSFUL; + break; + + default: + auth_status = AUTH_STATUS_FAILED; + break; + } + + /* now check if cancel occurred */ + if (auth_conn->cancel_auth) { + auth_status = AUTH_STATUS_CANCELED; + } + + /* Call status */ + auth_lib_set_status(auth_conn, auth_status); + + return; +} + + + diff --git a/lib/auth/auth_internal.h b/lib/auth/auth_internal.h new file mode 100644 index 000000000000..bb03cd0b7d9a --- /dev/null +++ b/lib/auth/auth_internal.h @@ -0,0 +1,295 @@ +/** + * @file auth_internal.h + * + * @brief Internal functions used by the authentication library. + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#ifndef ZEPHYR_INCLUDE_AUTH_INTERNAL_H_ +#define ZEPHYR_INCLUDE_AUTH_INTERNAL_H_ + +/** + * Thread params + */ +struct auth_thread_params { + struct authenticate_conn *auth_conn; + struct k_sem *thrd_sem; +}; + + + +/** + * Defines to handle message fragmentation over the different transports. + */ + +/* should be at large as the largest msg. */ +#define XPORT_MAX_MESSAGE_SIZE (650u) + +/** + * A message is broken up into multiple fragments. Each fragement has + * sync bytes, flags, and fragment length. + */ +#define XPORT_FRAG_SYNC_BYTE_HIGH (0xA5) /* bits 15-4 sync byte */ +#define XPORT_FRAG_SYNC_BYTE_LOW (0x90) +#define XPORT_FRAG_LOWBYTE_MASK (0xF0) /* bits 3-0 for flags */ + +#define XPORT_FRAG_SYNC_BITS ((XPORT_FRAG_SYNC_BYTE_HIGH << 8u) | \ + XPORT_FRAG_SYNC_BYTE_LOW) +#define XPORT_FRAG_SYNC_MASK (0xFFF0) + +/** + * Bitlfags used to indicate fragment order + */ +#define XPORT_FRAG_BEGIN (0x1) +#define XPORT_FRAG_NEXT (0x2) +#define XPORT_FRAG_END (0x4) +#define XPORT_FRAG_UNUSED (0x8) + +#define XPORT_FRAG_HDR_BYTECNT (sizeof(struct auth_message_frag_hdr)) +#define XPORT_MIN_FRAGMENT XPORT_FRAG_HDR_BYTECNT + + +#pragma pack(push, 1) +/** + * Fragment header + */ +struct auth_message_frag_hdr { + /* bits 15-4 are for fragment sync, bits 3-0 are flags */ + uint16_t sync_flags; /* bytes to insure we're at a fragment */ + uint16_t payload_len; /* number of bytes in the payload, does not + * include the header. */ +}; + +/** + * One fragment, one or more fragments make up a message. + */ +struct auth_message_fragment { + struct auth_message_frag_hdr hdr; + uint8_t frag_payload[XPORT_MAX_MESSAGE_SIZE]; +}; +#pragma pack(pop) + + + +/** + * Starts the authentication thread. + * + * @param auth_conn Pointer to Authentication connection struct. + * + * @return 0 on success else negative error number. + */ +int auth_start_thread(struct authenticate_conn *auth_conn); + + +/** + * Set the authentication status. + * + * @param auth_conn Authentication connection struct. + * @param status Authentication status. + */ +void auth_lib_set_status(struct authenticate_conn *auth_conn, enum auth_status status); + + +/** + * Initializes DTLS authentication method. + * + * @param auth_conn Pointer to Authentication connection struct. + * @param certs Pointer to certs and associated private keys. + * + * @return 0 on success else one of AUTH_ERROR_* values. + */ +int auth_init_dtls_method(struct authenticate_conn *auth_conn, + struct auth_dtls_certs *certs); + + +/** + * Initialize Challenge-Response method with additional parameters. + * + * @param auth_conn Pointer to Authentication connection struct. + * @param chal_resp Pointer to Challenge-Response params. + * + * @return 0 on success else one of AUTH_ERROR_* values. + */ +int auth_init_chalresp_method(struct authenticate_conn *auth_conn, + struct auth_challenge_resp *chal_resp); + + +/** + * Used by the client to send data bytes to the Peripheral. + * If necessary, will break up data into several sends depending + * on the MTU size. + * + * @param auth_conn Pointer to Authentication connection struct. + * @param buf Buffer to send. + * @param len Buffer size. + * + * @return Number of bytes sent or negative error value. + */ +int auth_client_tx(struct authenticate_conn *auth_conn, const unsigned char *buf, + size_t len); + +/** + * Used by the client to read data from the receive buffer. Will not + * block, if no bytes are available from the Peripheral returns 0. + * + * @param auth_conn Pointer to Authentication connection struct. + * @param buf Buffer to copy byes into. + * @param len Buffer length. + * + * @return Number of bytes returned, 0 if no bytes returned, or negative if + * an error occurred. + */ +int auth_client_recv(struct authenticate_conn *auth_conn, unsigned char *buf, + size_t len); + +/** + * Used by the Central to receive data from the Peripheral. Will block until data is + * received or a timeout has occurred. + * + * @param auth_conn Pointer to Authentication connection struct. + * @param buf Buffer to copy byes into. + * @param len Buffer length. + * @param timeout Wait time in msecs for data, K_FOREVER or K_NO_WAIT. + * + * @return Number of bytes returned, 0 if no bytes returned, or negative if + * an error occurred. + */ +int auth_client_recv_timeout(struct authenticate_conn *auth_conn, unsigned char *buf, + size_t len, uint32_t timeout); + +/** + * Used by the server to send data to the Central. Will break up buffer to max MTU + * sizes if necessary and send multiple PDUs. Uses Write Indications to get + * acknowledgement from the Central before sending additional packet. + * + * @param auth_conn Pointer to Authentication connection struct. + * @param buf Data to send + * @param len Data length. + * + * @return Number of bytes send, or negative if an error occurred. + */ +int auth_server_tx(struct authenticate_conn *auth_conn, const unsigned char *buf, + size_t len); + +/** + * Used by the server to read data from the receive buffer. Non-blocking. + * + * @param auth_conn Context, pointer to Authentication connection struct. + * @param buf Buffer to copy bytes into. + * @param len Number of bytes requested. + * + * @return Number of bytes returned, 0 if no bytes, of negative if an error occurred. + */ +int auth_server_recv(struct authenticate_conn *auth_conn, unsigned char *buf, + size_t len); + +/** + * Used by the server to read data from the receive buffer. Blocking call. + * + * @param auth_conn Context, pointer to Authentication connection struct. + * @param buf Buffer to copy bytes into. + * @param len Number of bytes requested. + * @param timeout Wait time in msecs for data, K_FOREVER or K_NO_WAIT. + * + * @return Number of bytes returned, or -EAGAIN if timed out. + */ +int auth_server_recv_timeout(struct authenticate_conn *auth_conn, unsigned char *buf, + size_t len, uint32_t timeout); + + +/** + * Used by the client to send data to Peripheral. + * + * @param conn Pointer to Authentication connection struct. + * @param data Data to send. + * @param len Byte length of data. + * + * @return Number of bytes sent, negative number on error. + */ +int auth_client_tx(struct authenticate_conn *conn, const unsigned char *data, + size_t len); + +/** + * Used by the client to receive data to Peripheral. + * + * @param conn Pointer to Authentication connection struct. + * @param buf Buffer to copy received bytes into. + * @param rxbytes Number of bytes requested. + * + * @return Number of bytes copied into the buffer. On error, negative error number. + */ +int auth_client_rx(struct authenticate_conn *conn, uint8_t *buf, size_t rxbytes); + +/** + * Used by server to send data to the client. + * + * @param conn Pointer to Authentication connection struct. + * @param data Data to send. + * @param len Byte length of data. + * + * @return Number of bytes sent, negative number on error. + */ +int auth_server_tx(struct authenticate_conn *conn, const unsigned char *data, + size_t len); + +/** + * Used by server to receive data. + * + * @param conn Pointer to Authentication connection struct. + * @param buf Buffer to copy received bytes into. + * @param len Number of bytes requested. + * + * @return Number of bytes copied (received) into the buffer. + * On error, negative error number. + */ +int auth_sever_rx(struct authenticate_conn *conn, uint8_t *buf, size_t len); + +/** + * Scans buffer to determine if a fragment is present. + * + * @param buffer Buffer to scan. + * @param buflen Buffer length. + * @param frag_beg_offset Offset from buffer begin where fragment starts. + * @param frag_byte_cnt Number of bytes in this fragment. + * + * @return true if full frame found, else false. + */ +bool auth_message_get_fragment(const uint8_t *buffer, uint16_t buflen, + uint16_t *frag_beg_offset, uint16_t *frag_byte_cnt); + +/** + * Used by lower transport to put received bytes into recv queue. Handle framing and + * puts full message into receive queue. Handles reassembly of message fragments. + * + * @param xporthdl Transport handle. + * @param buff Pointer to one frame. + * @param buflen Number of bytes in frame + * + * @return The number of bytes queued, can be less than requested. + * On error, negative value is returned. + */ +int auth_message_assemble(const auth_xport_hdl_t xporthdl, const uint8_t *buf, + size_t buflen); + +/** + * Swap the fragment header from Big Endian to the processor's byte + * ordering. + * + * @param frag_hdr Pointer to message fragment header. + */ +void auth_message_hdr_to_cpu(struct auth_message_frag_hdr *frag_hdr); + + +/** + * Swaps the fragment header bytes to Big Endian order. + * + * @param frag_hdr Pointer to message fragment header. + */ +void auth_message_hdr_to_be16(struct auth_message_frag_hdr *frag_hdr); + + + + +#endif /* ZEPHYR_INCLUDE_AUTH_INTERNAL_H_ */ \ No newline at end of file diff --git a/lib/auth/auth_lib.c b/lib/auth/auth_lib.c new file mode 100644 index 000000000000..0a0b880eb41e --- /dev/null +++ b/lib/auth/auth_lib.c @@ -0,0 +1,342 @@ +/** + * @file auth_lib.c + * + * @brief Authentication Library functions used to authenticate a + * connection between a client and server. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +LOG_MODULE_REGISTER(auth_lib, CONFIG_AUTH_LOG_LEVEL); + +#include +#include "auth_internal.h" + + +#define AUTH_THRD_STACK_SIZE (4096u) +#define AUTH_THRD_PRIORITY CONFIG_AUTH_THREAD_PRIORITY + + + +#if defined(AUTH_INSTANCE_1) +K_SEM_DEFINE(thrd_sem_1, 0, 1); +#endif + +#if defined(AUTH_INSTANCE_2) +K_SEM_DEFINE(thrd_sem_2, 0, 1); +#endif + +static void auth_thrd_entry(void *, void *, void *); + +static struct auth_thread_params thrd_params[CONFIG_NUM_AUTH_INSTANCES] = +{ +#if defined(AUTH_INSTANCE_1) + { .thrd_sem = &thrd_sem_1 }, +#endif + +#if defined(AUTH_INSTANCE_2) + { .thrd_sem = &thrd_sem_2 }, +#endif +}; + +#if defined(AUTH_INSTANCE_1) +K_THREAD_DEFINE(auth_tid_1, AUTH_THRD_STACK_SIZE, + auth_thrd_entry, &thrd_params[AUTH_INST_1_ID], NULL, NULL, + AUTH_THRD_PRIORITY, 0, 0); +#endif + + +#if defined(AUTH_INSTANCE_2) +K_THREAD_DEFINE(auth_tid_2, AUTH_THRD_STACK_SIZE, + auth_thrd_entry, &thrd_params[AUTH_INST_2_ID], NULL, NULL, + AUTH_THRD_PRIORITY, 0, 0); +#endif + + +/** + * Forward function declarations for authentication threads. + */ +void auth_dtls_thead(struct authenticate_conn *auth_conn); +void auth_chalresp_thread(struct authenticate_conn *auth_conn); + + +/* ========================== local functions ========================= */ + +/** + * Check auth flags consistency. + * + * @param flags Flags to check. + * + * @return true if flags are correct, else false. + */ +static bool auth_lib_checkflags(uint32_t flags) +{ + /* Check for invalid flag combinations */ + + /* server and client roles are mutually exclusive */ + if ((flags & (AUTH_CONN_SERVER | AUTH_CONN_CLIENT)) == + (AUTH_CONN_SERVER | AUTH_CONN_CLIENT)) { + return false; + } + + /* can only define one auth method */ + if ((flags & (AUTH_CONN_DTLS_AUTH_METHOD | AUTH_CONN_CHALLENGE_AUTH_METHOD)) + == (AUTH_CONN_DTLS_AUTH_METHOD | AUTH_CONN_CHALLENGE_AUTH_METHOD)) { + return false; + } + + return true; +} + + +/** + * Invokes the status callback from the system work queue + * + * @param work Pointer to work item. + */ +static void auth_lib_status_work(struct k_work *work) +{ + struct authenticate_conn *auth_conn = + CONTAINER_OF(work, struct authenticate_conn, auth_status_work); + + if (!auth_conn) { + LOG_ERR("Failed to get auth conn struct."); + return; + } + + /* invoke callback */ + auth_conn->status_cb(auth_conn, auth_conn->instance, auth_conn->curr_status, + auth_conn->callback_context); +} + + +/** + * Auth thread starts during boot and waits on semaphore. + * + * @param arg1 Pointer to struct auth_thread_params. + * @param arg2 Unused + * @param arg3 Unused + */ +static void auth_thrd_entry(void *arg1, void *arg2, void *arg3) +{ + int ret; + struct auth_thread_params *thrd_params = (struct auth_thread_params *)arg1; + + while (true) { + + ret = k_sem_take(thrd_params->thrd_sem, K_FOREVER); + + if (ret) { + LOG_ERR("Failed to get semaphore for auth thread, err: %d", ret); + LOG_ERR("Auth thread terminating, instance id: %d.", + thrd_params->auth_conn->instance); + return; + } + + /* call auth thread function */ + thrd_params->auth_conn->auth_func(thrd_params->auth_conn); + } +} + + + +/* ========================= external API ============================ */ + + +/** + * @see auth_lib.h + */ +int auth_lib_init(struct authenticate_conn *auth_conn, enum auth_instance_id instance, + auth_status_cb_t status_func, void *context, struct auth_optional_param *opt_params, + enum auth_flags auth_flags) +{ + int err = 0; + + /* check input params */ + if (status_func == NULL) { + LOG_ERR("Error, status function is NULL."); + return AUTH_ERROR_INVALID_PARAM; + } + + /* check auth flags */ + if (!auth_lib_checkflags(auth_flags)) { + LOG_ERR("Invalid auth flags."); + return AUTH_ERROR_INVALID_PARAM; + } + + /* init the struct to zero */ + memset(auth_conn, 0, sizeof(struct authenticate_conn)); + + /* setup the status callback */ + auth_conn->status_cb = status_func; + auth_conn->callback_context = context; + + auth_conn->cancel_auth = false; + auth_conn->instance = instance; + + /* init the work item used to post authentication status */ + k_work_init(&auth_conn->auth_status_work, auth_lib_status_work); + + auth_conn->is_client = (auth_flags & AUTH_CONN_CLIENT) ? true : false; + +#if defined(CONFIG_AUTH_DTLS) + + if (auth_flags & AUTH_CONN_DTLS_AUTH_METHOD) { + /* Set the DTLS authentication thread */ + auth_conn->auth_func = auth_dtls_thead; + { + if (opt_params == NULL || opt_params->param_id != AUTH_DTLS_PARAM) { + LOG_ERR("Missing certificates for TLS/DTLS authentication."); + return AUTH_ERROR_INVALID_PARAM; + } + + struct auth_dtls_certs *certs = &opt_params->param_body.dtls_certs; + + // init TLS layer + err = auth_init_dtls_method(auth_conn, certs); + } + + if (err) { + LOG_ERR("Failed to initialize MBed TLS, err: %d", err); + return err; + } + } +#endif + +#if defined(CONFIG_AUTH_CHALLENGE_RESPONSE) + + if (auth_flags & AUTH_CONN_CHALLENGE_AUTH_METHOD) { + /* Set the Challenge-Response authentication thread */ + auth_conn->auth_func = auth_chalresp_thread; + + if ((opt_params != NULL) && (opt_params->param_id == AUTH_CHALRESP_PARAM)) { + + struct auth_challenge_resp *chal_resp = &opt_params->param_body.chal_resp; + + err = auth_init_chalresp_method(auth_conn, chal_resp); + + if (err) { + LOG_ERR("Failed to set Challege-Response param, err: %d", err); + return err; + } + } + } +#endif + + /* + * Set auth connect into thread param instance. + * + * @note: k_sem_give() is called to start the thread, which provides + * a compile_barrier() and for SMP systems should handle any + * necessary memory barriers and/or cache syncing. + * The important point is the auth_conn pointer is set into + * a structure used by another thread (auth_thrd_entry) + */ + thrd_params[instance].auth_conn = auth_conn; + + return AUTH_SUCCESS; +} + +/** + * @see auth_lib.h + */ +int auth_lib_deinit(struct authenticate_conn *auth_conn) +{ + /* Free any resources, nothing for now, but maybe + * needed in the future */ + return AUTH_SUCCESS; +} + +/** + * @see auth_lib.h + */ +int auth_lib_start(struct authenticate_conn *auth_conn) +{ + /* Start the authentication thread */ + k_sem_give(thrd_params[auth_conn->instance].thrd_sem); + + return AUTH_SUCCESS; +} + +/** + * @see auth_lib.h + */ +int auth_lib_cancel(struct authenticate_conn *auth_conn) +{ + auth_conn->cancel_auth = true; + + auth_lib_set_status(auth_conn, AUTH_STATUS_CANCELED); + + return AUTH_SUCCESS; +} + +/** + * @see auth_lib.h + */ +const char *auth_lib_getstatus_str(enum auth_status status) +{ + switch (status) { + case AUTH_STATUS_STARTED: + return "Authentication started"; + break; + + case AUTH_STATUS_IN_PROCESS: + return "In process"; + break; + + case AUTH_STATUS_CANCELED: + return "Canceled"; + break; + + case AUTH_STATUS_FAILED: + return "Failure"; + break; + + case AUTH_STATUS_AUTHENTICATION_FAILED: + return "Authentication Failed"; + break; + + case AUTH_STATUS_SUCCESSFUL: + return "Authentication Successful"; + break; + + default: + break; + } + + return "unknown"; +} + +/** + * @see auth_lib.h + */ +enum auth_status auth_lib_get_status(struct authenticate_conn *auth_conn) +{ + return auth_conn->curr_status; +} + +/** + * @see auth_lib.h + */ +void auth_lib_set_status(struct authenticate_conn *auth_conn, enum auth_status status) +{ + auth_conn->curr_status = status; + + if (auth_conn->status_cb) { + + /* submit work item */ + k_work_submit(&auth_conn->auth_status_work); + } +} diff --git a/lib/auth/auth_xport_bt.c b/lib/auth/auth_xport_bt.c new file mode 100644 index 000000000000..daaff7b84f6c --- /dev/null +++ b/lib/auth/auth_xport_bt.c @@ -0,0 +1,537 @@ +/** + * @file auth_xport_bt.c + * + * @brief Bluetooth transport layer. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "auth_internal.h" + + +#define LOG_LEVEL CONFIG_AUTH_LOGLEVEL +#include +LOG_MODULE_REGISTER(auth_bt_xport, CONFIG_AUTH_LOG_LEVEL); + +#define AUTH_BT_WRITE_TIMEOUTMSEC (5000u) + +/* two bytes for header, not sure about extra byte */ +#define BT_LINK_HEADER_BYTES (2u + 1u) + +/** + * Maps transport handle to a BT connection along with + * additional info used to manage BT I/O. + */ +struct auth_xport_connection_map { + + /* Opaque transport handle */ + auth_xport_hdl_t xporthdl; + + /* details for bt connection */ + bool is_central; + + /* BT connection */ + struct bt_conn *conn; + + /* Peripheral (server) characteristic value handle. Used by the + * Central (client) to send data. */ + uint16_t server_char_hdl; + + /* The Central (client) attribute, used by the Peripheral (server) + * to send data to the Central. + */ + const struct bt_gatt_attr *client_attr; + + /* Used to wait for central write completion before + * sending more data */ + struct k_sem auth_central_write_sem; + + volatile uint8_t write_att_err; + + /* Semaphore used when processing peripheral indications */ + struct k_sem auth_indicate_sem; + uint32_t indicate_err; + + uint16_t payload_size; /* BT Link MTU less struct bt_att_write_req */ +}; + + +static struct auth_xport_connection_map bt_conn_map[CONFIG_BT_MAX_CONN]; + + +/** + * Forward declarations + */ +#if defined(CONFIG_BT_GATT_CLIENT) +static int auth_xp_bt_central_tx(struct auth_xport_connection_map *bt_xp_conn, + const unsigned char *buf, size_t len); +#else +static int auth_xp_bt_peripheral_tx(struct auth_xport_connection_map *bt_xp_conn, + const unsigned char *buf, size_t len); +#endif + +/** + * Given a BT connection, return the xport connection info. + * + * @param conn Bluetooth Connection struct. + * + * @return Pointer to connection map for the given BT connection struct. + */ +static struct auth_xport_connection_map *auth_xp_bt_getconn(struct bt_conn *conn) +{ + uint8_t index = bt_conn_index(conn); + + if (index > CONFIG_BT_MAX_CONN) { + return NULL; + } + + return &bt_conn_map[index]; +} + + +#if defined(CONFIG_BT_GATT_CLIENT) +/** + * Called by the Central (Client) to send bytes to the peripheral (Server) + * + * @param xport_hdl Transport handle + * @param data Data to send. + * @param len Number of bytes to send. + * + * @return Total bytes sent on success, on error negative error value. + */ +static int auth_xp_bt_central_send(auth_xport_hdl_t xport_hdl, const uint8_t *data, + const size_t len) +{ + /* get the Xport BT connection info from the xport handle */ + struct auth_xport_connection_map *bt_xp_conn = auth_xport_get_context(xport_hdl); + + /* sanity check */ + if (bt_xp_conn == NULL) { + LOG_ERR("Missing bt transport connection context."); + return AUTH_ERROR_INTERNAL; + } + + int ret = auth_xp_bt_central_tx(bt_xp_conn, data, len); + + return ret; +} +#else +/** + * Sends data to the client via BT indication. + * + * @param xport_hdl Transport handle. + * @param data Data to send. + * @param len Number of bytes to send. + * + * @return Total byte send on success, negative value on error. + */ +static int auth_xp_bt_peripheral_send(auth_xport_hdl_t xport_hdl, const uint8_t *data, + const size_t len) +{ + /* get the Xport BT connection info from the xport handle */ + struct auth_xport_connection_map *bt_xp_conn = auth_xport_get_context(xport_hdl); + + /* sanity check */ + if (bt_xp_conn == NULL) { + LOG_ERR("Missing bt transport connection context."); + return AUTH_ERROR_INTERNAL; + } + + int ret = auth_xp_bt_peripheral_tx(bt_xp_conn, data, len); + + return ret; +} +#endif /* CONFIG_BT_GATT_CLIENT */ + + +/** + * @see auth_xport.h + */ +int auth_xp_bt_init(const auth_xport_hdl_t xport_hdl, uint32_t flags, void *xport_param) +{ + struct auth_xp_bt_params *bt_params = (struct auth_xp_bt_params *)xport_param; + struct auth_xport_connection_map *bt_xp_conn; + + if (bt_params == NULL) { + return AUTH_ERROR_INVALID_PARAM; + } + +#if defined(CONFIG_BT_GATT_CLIENT) + if (!bt_params->is_central) { + LOG_ERR("Invalid config, is_central is false for GATT client"); + return AUTH_ERROR_INVALID_PARAM; + } + + /* set direct call function */ + auth_xport_set_sendfunc(xport_hdl, auth_xp_bt_central_send); +#else + if (bt_params->is_central) { + LOG_ERR("Invalid config, is_central is set for GATT server"); + return AUTH_ERROR_INVALID_PARAM; + } + + /* set direct call function */ + auth_xport_set_sendfunc(xport_hdl, auth_xp_bt_peripheral_send); +#endif + + + bt_xp_conn = auth_xp_bt_getconn(bt_params->conn); + + if (bt_xp_conn == NULL) { + LOG_ERR("Failed to get transport map entry."); + return AUTH_ERROR_INTERNAL; + } + + /* Is this entry inuse? Check conn param */ + if (bt_xp_conn->conn != NULL) { + LOG_WRN("BT transport entry in use."); + } + + bt_xp_conn->is_central = bt_params->is_central; + bt_xp_conn->conn = bt_params->conn; + bt_xp_conn->xporthdl = xport_hdl; + bt_xp_conn->payload_size = 0; + + + if (bt_params->is_central) { + bt_xp_conn->server_char_hdl = bt_params->server_char_hdl; + + /* init central write semaphore */ + k_sem_init(&bt_xp_conn->auth_central_write_sem, 0, 1); + + bt_xp_conn->write_att_err = 0; + + } else { + + bt_xp_conn->client_attr = bt_params->client_attr; + + /* init peripheral indicate semaphore */ + k_sem_init(&bt_xp_conn->auth_indicate_sem, 0, 1); + bt_xp_conn->indicate_err = 0; + } + + /* Set the BT xport connection struct into the xport handle */ + auth_xport_set_context(xport_hdl, bt_xp_conn); + + return AUTH_SUCCESS; +} + +/** + * @see auth_xport.h + */ +int auth_xp_bt_deinit(const auth_xport_hdl_t xport_hdl) +{ + if (xport_hdl == NULL) { + LOG_ERR("Transport handle is NULL."); + return AUTH_ERROR_INVALID_PARAM; + } + + /* get xport context which is where the BT connection is saved */ + struct auth_xport_connection_map *bt_xp_conn = auth_xport_get_context(xport_hdl); + + if (bt_xp_conn == NULL) { + LOG_ERR("Missing BT connection"); + return AUTH_ERROR_INTERNAL; + } + + + /* clear out auth_xport_map entry */ + bt_xp_conn->xporthdl = NULL; + bt_xp_conn->is_central = false; + bt_xp_conn->conn = NULL; + bt_xp_conn->payload_size = 0; + bt_xp_conn->server_char_hdl = 0; + bt_xp_conn->client_attr = NULL; + + /* clear direct send function */ + auth_xport_set_sendfunc(xport_hdl, NULL); + auth_xport_set_context(xport_hdl, NULL); + + return AUTH_SUCCESS; +} + +/** + * @see auth_xport.h + */ +int auth_xp_bt_event(const auth_xport_hdl_t xporthdl, struct auth_xport_evt *event) +{ + /* stub for now */ + return AUTH_SUCCESS; +} + +/** + * @see auth_xport.h + */ +int auth_xp_bt_get_max_payload(const auth_xport_hdl_t xporthdl) +{ + struct auth_xport_connection_map *bt_xp_conn = auth_xport_get_context(xporthdl); + + return (int)(bt_gatt_get_mtu(bt_xp_conn->conn) - (uint16_t)BT_LINK_HEADER_BYTES); +} + +#if defined(CONFIG_BT_GATT_CLIENT) + +/** + * @see auth_xport.h + */ +uint8_t auth_xp_bt_central_notify(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params, + const void *data, uint16_t length) +{ + int err; + struct auth_xport_connection_map *bt_xp_conn = auth_xp_bt_getconn(conn); + + if (bt_xp_conn == NULL) { + LOG_ERR("Failed to get BT connection map."); + return BT_GATT_ITER_CONTINUE; + } + + /* This happens when the connection is dropped */ + if (length == 0) { + return BT_GATT_ITER_CONTINUE; + } + + /* put the received bytes into the receive queue */ + auth_message_hdr_to_cpu((struct auth_message_frag_hdr *)data); + err = auth_message_assemble(bt_xp_conn->xporthdl, data, length); + + if (length < 0) { + LOG_ERR("Failed to set all received bytes, err: %d", length); + } + + return BT_GATT_ITER_CONTINUE; +} + + +/** + * Called by the Bluetooth stack after sending BLE data. + * + * @param conn The Bluetooth connection. + * @param err ATT error code, 0 is success. + * @param params Pointer to write params used when calling bt_gatt_write() + */ +static void auth_xp_bt_central_write_cb(struct bt_conn *conn, uint8_t err, + struct bt_gatt_write_params *params) +{ + struct auth_xport_connection_map *bt_xp_conn = auth_xp_bt_getconn(conn); + + if (err) { + LOG_ERR("gatt write failed, err: %d", err); + } else { + LOG_DBG("gatt write success, num bytes: %d", params->length); + } + + bt_xp_conn->write_att_err = err; + + k_sem_give(&bt_xp_conn->auth_central_write_sem); +} + + +/** + * Used by the central to write data to the peripheral. + * + * @param bt_xp_conn Bluetooth transport connection map. + * @param buf Data to send. + * @param len Number of bytes to send. + * + * @return Number of bytes sent, negative number on error. + */ +static int auth_xp_bt_central_tx(struct auth_xport_connection_map *bt_xp_conn, + const unsigned char *buf, size_t len) +{ + int err = 0; + uint16_t write_count; + int total_write_cnt = 0; + struct bt_gatt_write_params write_params; + + write_params.func = auth_xp_bt_central_write_cb; + write_params.handle = bt_xp_conn->server_char_hdl; + write_params.offset = 0; + + /* Set payload size if not set. This is necessary when the MTU + * size is negotiated after the BT connection has been established. */ + if (bt_xp_conn->payload_size == 0) { + bt_xp_conn->payload_size = auth_xp_bt_get_max_payload(bt_xp_conn->xporthdl); + } + + /* if necessary break up the write */ + while (len != 0) { + + write_count = MIN(bt_xp_conn->payload_size, len); + + write_params.data = buf; + write_params.length = write_count; + + err = bt_gatt_write(bt_xp_conn->conn, &write_params); + + if (err) { + LOG_ERR("Failed to write to peripheral, err: %d", err); + return err; + } + + /* wait on semaphore for write completion */ + err = k_sem_take(&bt_xp_conn->auth_central_write_sem, + K_MSEC(AUTH_BT_WRITE_TIMEOUTMSEC)); + + if (err) { + LOG_ERR("Failed to take semaphore, err: %d", err); + return err; + } + + /* Was ther an ATT error code in the call back? */ + if (bt_xp_conn->write_att_err != 0) { + LOG_ERR("ATT write error occurred, err: 0x%x", + bt_xp_conn->write_att_err); + return -1; + } + + total_write_cnt += write_count; + buf += write_count; + len -= write_count; + } + + LOG_DBG("Central - num bytes sent: %d", total_write_cnt); + + return total_write_cnt; +} + +#else /* CONFIG_BT_GATT_CLIENT */ + +/** + * Called when the Central has ACK'd receiving data + * Function is called in the System workqueue context + * + * @param conn Bluetooth connection + * @param params Indicate params + * @param err GATT error + */ +static void auth_xp_bt_peripheral_indicate(struct bt_conn *conn, + struct bt_gatt_indicate_params *params, + uint8_t err) +{ + struct auth_xport_connection_map *bt_xp_conn = auth_xp_bt_getconn(conn); + + /* set error */ + bt_xp_conn->indicate_err = err; + + // signal semaphore that chunk fo data was received from the peripheral + k_sem_give(&bt_xp_conn->auth_indicate_sem); + + /* if an error occurred */ + if (err != 0) { + LOG_DBG("Peripheral indication, err: %d", err); + } +} + +/** + * Used by the Peripheral to send data to the central. + * + * @param bt_xp_conn Bluetooth transport connection map. + * @param buf bytes to send. + * @aram len Number of bytes to send. + * + * @return On success, total number of bytes send. On error, negative error value. + */ +static int auth_xp_bt_peripheral_tx(struct auth_xport_connection_map *bt_xp_conn, + const unsigned char *buf, size_t len) +{ + int ret = 0; + int total_bytes_sent = 0; + bool done = false; + size_t send_cnt = 0; + static struct bt_uuid_128 xp_bt_auth_client_char = AUTH_SVC_CLIENT_CHAR_UUID; + + + /* Set payload size if not set. This is necessary when the MTU + * size is negotiated after the BT connection has been established. */ + if (bt_xp_conn->payload_size == 0) { + bt_xp_conn->payload_size = auth_xp_bt_get_max_payload(bt_xp_conn->xporthdl); + } + + + /* Setup the indicate params. The client will use BLE indications vs. + * notifications. This enables the client to know when the central has + * read the attribute and send another packet of data. */ + struct bt_gatt_indicate_params indicate_params; + + /* setup indicate params */ + memset(&indicate_params, 0, sizeof(indicate_params)); + + + indicate_params.uuid = (const struct bt_uuid *)&xp_bt_auth_client_char; + indicate_params.attr = bt_xp_conn->client_attr; + indicate_params.func = auth_xp_bt_peripheral_indicate; + + while (!done) { + + send_cnt = MIN(len, bt_xp_conn->payload_size); + + indicate_params.data = buf; + indicate_params.len = send_cnt; /* bytes to send */ + + ret = bt_gatt_indicate(bt_xp_conn->conn, &indicate_params); + + if (ret) { + LOG_ERR("bt_gatt_indicate failed, error: 0x%x", ret); + } + + /* wait on semaphore before sending next chunk */ + ret = k_sem_take(&bt_xp_conn->auth_indicate_sem, + K_MSEC(AUTH_BT_WRITE_TIMEOUTMSEC)); + + /* on wakeup check if error occurred */ + if (ret) { + LOG_ERR("Wait for central indicated failed, err: %d", ret); + break; + } + + /* update buffer pointer and count */ + total_bytes_sent += send_cnt; + len -= send_cnt; + buf += send_cnt; + + /* are we done sending? */ + if (len == 0) { + ret = total_bytes_sent; + break; + } + } + + return ret; +} + +#endif /* CONFIG_BT_GATT_CLIENT */ + + +/** + * @see auth_xport.h + */ +ssize_t auth_xp_bt_central_write(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, uint16_t offset, uint8_t flags) +{ + struct auth_xport_connection_map *bt_xp_conn = auth_xp_bt_getconn(conn); + + LOG_DBG("client write called, len: %d", len); + + /* put the received bytes into the receive queue */ + auth_message_hdr_to_cpu((struct auth_message_frag_hdr *)buf); + int err = auth_message_assemble(bt_xp_conn->xporthdl, buf, len); + + /* if no error, need to return num of bytes handled. */ + if (err >= 0) { + err = len; + } + + /* return number of bytes writen */ + return err; +} diff --git a/lib/auth/auth_xport_common.c b/lib/auth/auth_xport_common.c new file mode 100644 index 000000000000..3390b26df06e --- /dev/null +++ b/lib/auth/auth_xport_common.c @@ -0,0 +1,1039 @@ +/** + * @file auth_xport_common.c + * + * @brief Common transport routines. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_AUTH_LOG_LEVEL +#include +LOG_MODULE_DECLARE(auth_lib, CONFIG_AUTH_LOG_LEVEL); + +#include +#include +#include "auth_internal.h" + +/** + * Size of a buffer used for Rx. + */ +#define XPORT_IOBUF_LEN (4096u) + + +/** + * @brief Circular buffer used to save received data. + */ +struct auth_xport_io_buffer { + struct k_mutex buf_mutex; + struct k_sem buf_sem; + + uint32_t head_index; + uint32_t tail_index; + uint32_t num_valid_bytes; + + uint8_t io_buffer[XPORT_IOBUF_LEN]; +}; + + +/** + * Contains buffer used to assemble a message from multiple fragments. + */ +struct auth_message_recv { + /* pointer to buffer where message is assembled */ + uint8_t rx_buffer[XPORT_MAX_MESSAGE_SIZE]; + + /* vars used for re-assembling frames into a message */ + uint32_t rx_curr_offset; + bool rx_first_frag; +}; + + +/** + * Transport instance, contains send and recv circular queues. + */ +struct auth_xport_instance { + /* Send & Recv circular queues */ + struct auth_xport_io_buffer send_buf; + struct auth_xport_io_buffer recv_buf; + + /* Lower transport type */ + enum auth_xport_type xport_type; + + void *xport_ctx; /* transport specific context */ + + /* If the lower transport has a send function */ + send_xport_t send_func; + + /* Struct for handling assembling message from multiple fragments */ + struct auth_message_recv recv_msg; + + uint32_t payload_size; /* Max payload size for lower transport. */ +}; + +/* transport instances */ +static struct auth_xport_instance xport_inst[CONFIG_NUM_AUTH_INSTANCES]; + + +/* ================ local static funcs ================== */ + +/** + * Initializes common transport IO buffers. + * + * @param iobuf Pointer to IO buffer to initialize. + * + * @return 0 for success, else negative error value. + */ +static void auth_xport_iobuffer_init(struct auth_xport_io_buffer *iobuf) +{ + /* init mutex*/ + k_mutex_init(&iobuf->buf_mutex); + + /* init semaphore */ + k_sem_init(&iobuf->buf_sem, 0, 1); + + iobuf->head_index = 0; + iobuf->tail_index = 0; + iobuf->num_valid_bytes = 0; +} + +/** + * Reset queue counters + * + * @param iobuf Pointer to IO buffer to reset. + * + * @return 0 for success + */ +static void auth_xport_iobuffer_reset(struct auth_xport_io_buffer *iobuf) +{ + iobuf->head_index = 0; + iobuf->tail_index = 0; + iobuf->num_valid_bytes = 0; +} + + +/** + * Puts data into the transport buffer. + * + * @param iobuf IO buffer to add data. + * @param in_buf Data to put into IO buffer. + * @param num_bytes Number of bytes to put. + * + * @return On success, number of bytes put into IO buffer, can be less than requested. + * Negative number on error. + */ +static int auth_xport_buffer_put(struct auth_xport_io_buffer *iobuf, + const uint8_t *in_buf, size_t num_bytes) +{ + // Is the buffer full? + if (iobuf->num_valid_bytes == XPORT_IOBUF_LEN) { + return AUTH_ERROR_IOBUFF_FULL; + } + + /* don't put zero bytes */ + if (num_bytes == 0) { + return 0; + } + + /* lock mutex */ + int err = k_mutex_lock(&iobuf->buf_mutex, K_FOREVER); + if (err) { + return err; + } + + uint32_t free_space = XPORT_IOBUF_LEN - iobuf->num_valid_bytes; + uint32_t copy_cnt = MIN(free_space, num_bytes); + uint32_t total_copied = 0; + uint32_t byte_cnt; + + if (iobuf->head_index < iobuf->tail_index) { + // only enough room from head to tail, don't over-write + uint32_t max_copy_cnt = iobuf->tail_index - iobuf->head_index; + + copy_cnt = MIN(max_copy_cnt, copy_cnt); + + memcpy(iobuf->io_buffer + iobuf->head_index, in_buf, copy_cnt); + + total_copied += copy_cnt; + iobuf->head_index += copy_cnt; + iobuf->num_valid_bytes += copy_cnt; + + } else { + + // copy from head to end of buffer + byte_cnt = XPORT_IOBUF_LEN - iobuf->head_index; + + if (byte_cnt > copy_cnt) { + byte_cnt = copy_cnt; + } + + memcpy(iobuf->io_buffer + iobuf->head_index, in_buf, byte_cnt); + + total_copied += byte_cnt; + in_buf += byte_cnt; + copy_cnt -= byte_cnt; + iobuf->head_index += byte_cnt; + + iobuf->num_valid_bytes += byte_cnt; + + // if wrapped, then copy from beginning of buffer + if (copy_cnt > 0) { + memcpy(iobuf->io_buffer, in_buf, copy_cnt); + + total_copied += copy_cnt; + iobuf->num_valid_bytes += copy_cnt; + iobuf->head_index = copy_cnt; + } + } + + /* unlock */ + k_mutex_unlock(&iobuf->buf_mutex); + + /* after putting data into buffer, signal semaphore */ + k_sem_give(&iobuf->buf_sem); + + return (int)total_copied; +} + +/** + * Used to read bytes from an io buffer. + * + * @param iobuf IO buffer to read. + * @param out_buf Buffer to copy bytes into. + * @param num_bytes Number of bytes to read + * @param peek If true, then queue pointers are not updated. + * + * @return On success, number of bytes copied into buffer. Can be less than requested. + * Negative number on error. + */ +static int auth_xport_buffer_get_internal(struct auth_xport_io_buffer *iobuf, + uint8_t *out_buf, size_t num_bytes, bool peek) +{ + /* if no valid bytes, just return zero */ + if (iobuf->num_valid_bytes == 0) { + return 0; + } + + /* lock mutex */ + int err = k_mutex_lock(&iobuf->buf_mutex, K_FOREVER); + if (err) { + return err; + } + + /* number bytes to copy */ + uint32_t copy_cnt = MIN(iobuf->num_valid_bytes, num_bytes); + uint32_t total_copied = 0; + uint32_t byte_cnt = 0; + + if (iobuf->head_index <= iobuf->tail_index) { + + /* How may bytes are available? */ + byte_cnt = XPORT_IOBUF_LEN - iobuf->tail_index; + + if (byte_cnt > copy_cnt) { + byte_cnt = copy_cnt; + } + + /* copy from tail to end of buffer */ + memcpy(out_buf, iobuf->io_buffer + iobuf->tail_index, byte_cnt); + + if (!peek) { + /* update tail index */ + iobuf->tail_index += byte_cnt; + } + + out_buf += byte_cnt; + total_copied += byte_cnt; + + /* update copy count and num valid bytes */ + copy_cnt -= byte_cnt; + + if (!peek) { + iobuf->num_valid_bytes -= byte_cnt; + } + + /* wrapped around, copy from beginning of buffer until + copy_count is satisfied */ + if (copy_cnt > 0) { + + memcpy(out_buf, iobuf->io_buffer, copy_cnt); + + if (!peek) { + iobuf->tail_index = copy_cnt; + iobuf->num_valid_bytes -= copy_cnt; + } + + total_copied += copy_cnt; + } + + } else if (iobuf->head_index > iobuf->tail_index) { + + byte_cnt = iobuf->head_index - iobuf->tail_index; + + if (byte_cnt > copy_cnt) { + byte_cnt = copy_cnt; + } + + memcpy(out_buf, iobuf->io_buffer + iobuf->tail_index, byte_cnt); + + total_copied += byte_cnt; + copy_cnt -= byte_cnt; + + if (!peek) { + iobuf->tail_index += byte_cnt; + iobuf->num_valid_bytes -= byte_cnt; + } + } + + /* unlock */ + k_mutex_unlock(&iobuf->buf_mutex); + + return (int)total_copied; +} + +/** + * Peek at the contents of the input buffer. Copies bytes but does not advance + * the queue pointers. + * + * @param iobuf IO buffer to peek. + * @param out_buf Buffer to copy bytes into. + * @param num_bytes Number of bytes to peek. + * + * @return On success, number of bytes copied into buffer. Can be less than requested. + * Negative number on error. + */ +static int auth_xport_buffer_peek(struct auth_xport_io_buffer *iobuf, + uint8_t *out_buf, size_t num_bytes) +{ + return auth_xport_buffer_get_internal(iobuf, out_buf, num_bytes, true); +} + +/** + * Gets data from a IO buffer. + * + * @param iobuf IO buffer to get data from. + * @param out_buf Buffer to copy bytes into. + * @param num_bytes Number of bytes requested. + * + * @return On success, number of bytes copied into buffer. Can be less than requested. + * Negative number on error. + */ +static int auth_xport_buffer_get(struct auth_xport_io_buffer *iobuf, + uint8_t *out_buf, size_t num_bytes) +{ + return auth_xport_buffer_get_internal(iobuf, out_buf, num_bytes, false); +} + +/** + * Get data from an IO buffer, if no data present wait. + * + * @param iobuf The IO buffer to get from. + * @param out_buf Buffer to copy bytes into. + * @param num_bytes Number of bytes requested. + * @param waitmsec Number of milliseconds to wait if no data. + * + * @return On success, number of bytes copied, can be less than requested amount. + * Negative number on error. -EAGAIN if a timeout occurred. + */ +static int auth_xport_buffer_get_wait(struct auth_xport_io_buffer *iobuf, + uint8_t *out_buf, + int num_bytes, int waitmsec) +{ + /* return any bytes that might be sitting in the buffer */ + int bytecount = auth_xport_buffer_get(iobuf, out_buf, num_bytes); + + if (bytecount > 0) { + /* bytes are avail, return them */ + return bytecount; + } + + do { + int err = k_sem_take(&iobuf->buf_sem, K_MSEC(waitmsec)); + + if (err) { + return err; /* timed out -EAGAIN or error */ + } + + /* return byte count or error (bytecount < 0) */ + bytecount = auth_xport_buffer_get(iobuf, out_buf, num_bytes); + + } while (bytecount == 0); + + return bytecount; +} + +/** + * Get the number of bytes in an IO buffer. + * + * @param iobuf The IO buffer to check. + * + * @return On success, number of bytes. Negative number on error. + */ +static int auth_xport_buffer_bytecount(struct auth_xport_io_buffer *iobuf) +{ + int err = k_mutex_lock(&iobuf->buf_mutex, K_FOREVER); + + if (!err) { + err = (int)iobuf->num_valid_bytes; + } + + /* unlock */ + k_mutex_unlock(&iobuf->buf_mutex); + + return err; +} + + +/** + * Wait for a non-zero byte count. Used to wait until data is received + * from the sender. Wait until waitmsec has timed out or data has been received. + * + * @param iobuf IO buffer to wait on bytes. + * @param waitmsec Number of milliseconds to wait. + * + * @return On success, number of bytes in the IO buffer. + * -EAGAIN on timeout. + */ +static int auth_xport_buffer_bytecount_wait(struct auth_xport_io_buffer *iobuf, + uint32_t waitmsec) +{ + int num_bytes = auth_xport_buffer_bytecount(iobuf); + + /* an error occurred or there are bytes sitting in the io buffer. */ + if (num_bytes != 0) { + return num_bytes; + } + + /* wait for byte to fill the io buffer */ + int err = k_sem_take(&iobuf->buf_sem, K_MSEC(waitmsec)); + + if (err) { + return err; /* timed out -EAGAIN or error */ + } + + /* return the number of bytes in the queue */ + return auth_xport_buffer_bytecount(iobuf); +} + + +/** + * Get the frees pace within an IO bufer. + * + * @param iobuf IO Buffer to check. + * + * @return Amount of free space. + */ +static int auth_xport_buffer_avail_bytes(struct auth_xport_io_buffer *iobuf) +{ + return sizeof(iobuf->io_buffer) - auth_xport_buffer_bytecount(iobuf); +} + +/** + * Internal function to send data to peer + * + * @param xporthdl Transport handle + * @param data Buffer to send. + * @param len Number of bytes to send. + * + * @return Number of bytes sent on success, can be less than requested. + * On error, negative error code. + */ +static int auth_xport_internal_send(const auth_xport_hdl_t xporthdl, + const uint8_t *data, size_t len) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + /* if the lower transport set a send function, call it */ + if (xp_inst->send_func != NULL) { + return xp_inst->send_func(xporthdl, data, len); + } + + /* queue the send bytes into tx buffer */ + return auth_xport_buffer_put(&xp_inst->send_buf, data, len); +} + +/** + * Initializes message receive struct. Used to re-assemble message + * fragments received. + * + * @param recv_msg Received message buffer. + */ +static void auth_message_frag_init(struct auth_message_recv *recv_msg) +{ + recv_msg->rx_curr_offset = 0; + recv_msg->rx_first_frag = true; +} + + +/** + * Returns the lower transport type associated with the opaque + * transport handle. + * + * @param xporthdl The transport handle. + * + * @return Transport type + */ +static enum auth_xport_type auth_get_xport_type(auth_xport_hdl_t xporthdl) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + if (xp_inst == NULL) { + return AUTH_XP_TYPE_NONE; + } + + return xp_inst->xport_type; +} + + +/* ==================== Non static funcs ================== */ + +/** + * @see auth_xport.h + */ +int auth_xport_init(auth_xport_hdl_t *xporthdl, enum auth_instance_id instance, + enum auth_xport_type xport_type, void *xport_params) +{ + int ret = -1; + + /* verify instance */ + if ((instance < 0) || (instance >= AUTH_MAX_INSTANCES)) { + return AUTH_ERROR_INVALID_PARAM; + } + + if ((xport_type != AUTH_XP_TYPE_BLUETOOTH) && + (xport_type != AUTH_XP_TYPE_SERIAL)) { + return AUTH_ERROR_INVALID_PARAM; + } + + + *xporthdl = &xport_inst[instance]; + + /* init IO buffers */ + auth_xport_iobuffer_init(&xport_inst[instance].send_buf); + auth_xport_iobuffer_init(&xport_inst[instance].recv_buf); + auth_message_frag_init(&xport_inst[instance].recv_msg); + + /* Set the lower transport type */ + xport_inst[instance].xport_type = xport_type; + + +#if defined(CONFIG_BT_XPORT) + if (xport_type == AUTH_XP_TYPE_BLUETOOTH) { + ret = auth_xp_bt_init(*xporthdl, 0, xport_params); + } +#endif + +#if defined(CONFIG_SERIAL_XPORT) + if (xport_type == AUTH_XP_TYPE_SERIAL) { + ret = auth_xp_serial_init(*xporthdl, 0, xport_params); + } +#endif + + + return ret; +} + +/** + * @see auth_xport.h + */ +int auth_xport_deinit(const auth_xport_hdl_t xporthdl) +{ + int ret = 0; + enum auth_xport_type xport_type = AUTH_XP_TYPE_NONE; + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + if (xp_inst == NULL) { + return AUTH_ERROR_INVALID_PARAM; + } + + xport_type = auth_get_xport_type(xporthdl); + + /* reset queues */ + auth_xport_iobuffer_reset(&xp_inst->send_buf); + auth_xport_iobuffer_reset(&xp_inst->recv_buf); + + +#if defined(CONFIG_BT_XPORT) + if (xport_type == AUTH_XP_TYPE_BLUETOOTH) { + ret = auth_xp_bt_deinit(xporthdl); + } +#endif + +#if defined(CONFIG_SERIAL_XPORT) + if (xport_type == AUTH_XP_TYPE_SERIAL) { + ret = auth_xp_serial_deinit(xporthdl); + } +#endif + + xp_inst->xport_type = AUTH_XP_TYPE_NONE; + + return ret; +} + +/** + * @see auth_xport.h + */ +int auth_xport_event(const auth_xport_hdl_t xporthdl, struct auth_xport_evt *event) +{ + int ret = 0; + enum auth_xport_type xport_type = auth_get_xport_type(xporthdl); + +#if defined(CONFIG_BT_XPORT) + if (xport_type == AUTH_XP_TYPE_BLUETOOTH) { + ret = auth_xp_bt_event(xporthdl, event); + } +#endif + +#if defined(CONFIG_SERIAL_XPORT) + if (xport_type == AUTH_XP_TYPE_SERIAL) { + ret = auth_xp_serial_event(xporthdl, event); + } +#endif + + return ret; +} + +/** + * @see auth_xport.h + */ +int auth_xport_get_max_payload(const auth_xport_hdl_t xporthdl) +{ + int mtu = 0; + enum auth_xport_type xport_type = auth_get_xport_type(xporthdl); + +#if defined(CONFIG_BT_XPORT) + if (xport_type == AUTH_XP_TYPE_BLUETOOTH) { + mtu = auth_xp_bt_get_max_payload(xporthdl); + } +#endif + +#if defined(CONFIG_SERIAL_XPORT) + if (xport_type == AUTH_XP_TYPE_SERIAL) { + mtu = auth_xp_serial_get_max_payload(xporthdl); + } +#endif + return mtu; +} + +/** + * @see auth_xport.h + */ +int auth_xport_send(const auth_xport_hdl_t xporthdl, const uint8_t *data, size_t len) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + int fragment_bytes; + int payload_bytes; + int send_count = 0; + int num_fragments = 0; + int send_ret = AUTH_SUCCESS; + struct auth_message_fragment msg_frag; + + + /* If the lower transport MTU size isn't set, get it. This can happen + * when the the MTU is negotiated after the initial connection. */ + if (xp_inst->payload_size == 0) { + xp_inst->payload_size = auth_xport_get_max_payload(xporthdl); + } + + const uint16_t max_frame = MIN(sizeof(msg_frag), xp_inst->payload_size); + const uint16_t max_payload = max_frame - XPORT_FRAG_HDR_BYTECNT; + + /* sanity check */ + if (xp_inst == NULL) { + return AUTH_ERROR_INVALID_PARAM; + } + + /* set frame header */ + msg_frag.hdr.sync_flags = XPORT_FRAG_SYNC_BITS | XPORT_FRAG_BEGIN; + + /* Break up data to fit into lower transport MTU */ + while (len > 0) { + + /* get payload bytes */ + payload_bytes = MIN(max_payload, len); + + fragment_bytes = payload_bytes + XPORT_FRAG_HDR_BYTECNT; + + /* is this the last frame? */ + if ((len - payload_bytes) == 0) { + + msg_frag.hdr.sync_flags = XPORT_FRAG_SYNC_BITS | XPORT_FRAG_END; + + /* now check if we're only sending one frame, then set + * the frame begin flag */ + if (num_fragments == 0) { + msg_frag.hdr.sync_flags |= XPORT_FRAG_BEGIN; + } + } + + /* copy body */ + memcpy(msg_frag.frag_payload, data, payload_bytes); + msg_frag.hdr.payload_len = payload_bytes; + + /* convert header to Big Endian, network byte order */ + auth_message_hdr_to_be16(&msg_frag.hdr); + + /* send frame */ + send_ret = auth_xport_internal_send(xporthdl, + (const uint8_t *)&msg_frag, fragment_bytes); + + if (send_ret < 0) { + LOG_ERR("Failed to send xport frame, error: %d", send_ret); + return AUTH_ERROR_XPORT_SEND; + } + + /* verify all bytes were sent */ + if (send_ret != fragment_bytes) { + LOG_ERR("Failed to to send all bytes, send: %d, requested: %d", send_ret, fragment_bytes); + return AUTH_ERROR_XPORT_SEND; + } + + /* set next flags */ + msg_frag.hdr.sync_flags = XPORT_FRAG_SYNC_BITS | XPORT_FRAG_NEXT; + + len -= payload_bytes; + data += payload_bytes; + send_count += payload_bytes; + num_fragments++; + } + + return send_count; +} + + +/** + * @see auth_xport.h + * + * Put bytes received from lower transport into receive queue + */ +int auth_xport_put_recv(const auth_xport_hdl_t xporthdl, const uint8_t *buf, + size_t buflen) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + if (xp_inst == NULL) { + return AUTH_ERROR_INVALID_PARAM; + } + + return auth_xport_buffer_put(&xp_inst->recv_buf, buf, buflen); +} + + +/** + * @see auth_xport.h + */ +int auth_xport_recv(const auth_xport_hdl_t xporthdl, uint8_t *buf, + uint32_t buf_len, uint32_t timeoutMsec) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + if (xp_inst == NULL) { + return AUTH_ERROR_INVALID_PARAM; + } + + return auth_xport_buffer_get_wait(&xp_inst->recv_buf, buf, buf_len, + timeoutMsec); +} + +/** + * @see auth_xport.h + */ +int auth_xport_recv_peek(const auth_xport_hdl_t xporthdl, uint8_t *buff, + uint32_t buf_len) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + if (xp_inst == NULL) { + return AUTH_ERROR_INVALID_PARAM; + } + + return auth_xport_buffer_peek(&xp_inst->recv_buf, buff, buf_len); +} + +/** + * @see auth_xport.h + */ +int auth_xport_getnum_send_queued_bytes(const auth_xport_hdl_t xporthdl) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + if (xp_inst == NULL) { + return AUTH_ERROR_INVALID_PARAM; + } + + return auth_xport_buffer_bytecount(&xp_inst->send_buf); +} + +/** + * @see auth_xport.h + */ +int auth_xport_getnum_recvqueue_bytes(const auth_xport_hdl_t xporthdl) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + if (xp_inst == NULL) { + return AUTH_ERROR_INVALID_PARAM; + } + + return auth_xport_buffer_bytecount(&xp_inst->recv_buf); +} + +/** + * @see auth_xport.h + */ +int auth_xport_getnum_recvqueue_bytes_wait(const auth_xport_hdl_t xporthdl, + uint32_t waitmsec) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + if (xp_inst == NULL) { + return AUTH_ERROR_INVALID_PARAM; + } + + return auth_xport_buffer_bytecount_wait(&xp_inst->recv_buf, waitmsec); +} + + +/** + * @see auth_internal.h + */ +bool auth_message_get_fragment(const uint8_t *buffer, uint16_t buflen, + uint16_t *frag_beg_offset, uint16_t *frag_byte_cnt) +{ + uint16_t cur_offset; + uint16_t temp_payload_len; + struct auth_message_frag_hdr *frm_hdr; + bool found_sync_bytes = false; + + /* quick check */ + if (buflen < XPORT_MIN_FRAGMENT) { + return false; + } + + /* look for sync bytes */ + for (cur_offset = 0; cur_offset < buflen; cur_offset++, buffer++) { + /* look for the first sync byte */ + if (*buffer == XPORT_FRAG_SYNC_BYTE_HIGH) { + if (cur_offset + 1 < buflen) { + if ((*(buffer + 1) & XPORT_FRAG_LOWBYTE_MASK) == + XPORT_FRAG_SYNC_BYTE_LOW) { + /* found sync bytes */ + found_sync_bytes = true; + break; + } + } + } + } + + /* Didn't find Fragment sync bytes */ + if (!found_sync_bytes) { + return false; + } + + /* should have a full header, check frame len */ + frm_hdr = (struct auth_message_frag_hdr *)buffer; + + /* convert from be to cpu */ + temp_payload_len = sys_be16_to_cpu(frm_hdr->payload_len) + + XPORT_FRAG_HDR_BYTECNT; + + /* does the buffer contian all of the fragment bytes? + * Including the header. */ + if (temp_payload_len > (buflen - cur_offset)) { + /* not enough bytes for a full frame */ + return false; + } + + /* Put header vars into CPU byte order. */ + auth_message_hdr_to_cpu(frm_hdr); + + /* Have a full fragment */ + *frag_beg_offset = cur_offset; + + /* Return fragment byte count, including the header */ + *frag_byte_cnt = frm_hdr->payload_len + XPORT_FRAG_HDR_BYTECNT; + + return true; +} + +/** + * @see auth_internal.h + */ +int auth_message_assemble(const auth_xport_hdl_t xporthdl, const uint8_t *buf, + size_t buflen) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + struct auth_message_recv *msg_recv; + struct auth_message_fragment *rx_frag; + int free_buf_space; + int recv_ret = 0; + + /* check input params */ + if ((xp_inst == NULL) || (buf == NULL) || (buflen == 0u)) { + return AUTH_ERROR_INVALID_PARAM; + } + + msg_recv = (struct auth_message_recv *)&xp_inst->recv_msg; + + /* If max payload size isn't set, get it from the lower transport. + * This can happen if the lower transports frame/MTU size is set + * after an initial connection. */ + if (xp_inst->payload_size == 0) { + xp_inst->payload_size = auth_xport_get_max_payload(xporthdl); + } + + /* Reassemble a message from one for more fragments. */ + rx_frag = (struct auth_message_fragment *)buf; + + /* check for start flag */ + if (msg_recv->rx_first_frag) { + + msg_recv->rx_first_frag = false; + + if (!(rx_frag->hdr.sync_flags & XPORT_FRAG_BEGIN)) { + /* reset vars */ + msg_recv->rx_curr_offset = 0; + msg_recv->rx_first_frag = true; + + LOG_ERR("RX-Missing beginning fragment"); + return AUTH_ERROR_XPORT_FRAME; + } + + LOG_DBG("RX-Got BEGIN fragment."); + } + + /* check fragment sync bytes */ + if ((rx_frag->hdr.sync_flags & XPORT_FRAG_SYNC_MASK) != XPORT_FRAG_SYNC_BITS) { + /* reset vars */ + msg_recv->rx_curr_offset = 0; + msg_recv->rx_first_frag = true; + + LOG_ERR("RX-Invalid fragment."); + return AUTH_ERROR_XPORT_FRAME; + } + + + /* Subtract out fragment header */ + buflen -= XPORT_FRAG_HDR_BYTECNT; + + /* move beyond fragment header */ + buf += XPORT_FRAG_HDR_BYTECNT; + + /* sanity check, if zero or negative */ + if (buflen <= 0) { + /* reset vars */ + msg_recv->rx_curr_offset = 0; + msg_recv->rx_first_frag = true; + LOG_ERR("RX-Empty fragment!!"); + return AUTH_ERROR_XPORT_FRAME; + } + + /* ensure there's enough free space in our temp buffer */ + free_buf_space = sizeof(msg_recv->rx_buffer) - msg_recv->rx_curr_offset; + + if (free_buf_space < buflen) { + /* reset vars */ + msg_recv->rx_curr_offset = 0; + msg_recv->rx_first_frag = true; + LOG_ERR("RX-not enough free space"); + return AUTH_ERROR_XPORT_FRAME; + } + + /* copy payload bytes */ + memcpy(msg_recv->rx_buffer + msg_recv->rx_curr_offset, buf, buflen); + + msg_recv->rx_curr_offset += buflen; + + /* returned the number of bytes queued */ + recv_ret = buflen; + + /* Is this the last fragment of the message? */ + if (rx_frag->hdr.sync_flags & XPORT_FRAG_END) { + + /* log number payload bytes received. */ + LOG_INF("RX-Got LAST fragment, total bytes: %d", msg_recv->rx_curr_offset); + + int free_bytes = auth_xport_buffer_avail_bytes(&xp_inst->recv_buf); + + /* Is there enough free space to write entire message? */ + if (free_bytes >= msg_recv->rx_curr_offset) { + + /* copy message into receive buffer */ + recv_ret = auth_xport_buffer_put(&xp_inst->recv_buf, + msg_recv->rx_buffer, + msg_recv->rx_curr_offset); + + } else { + int need = msg_recv->rx_curr_offset - free_bytes; + LOG_ERR("Not enough room in RX buffer, free: %d, need %d bytes.", free_bytes, need); + } + + /* reset vars */ + msg_recv->rx_curr_offset = 0; + msg_recv->rx_first_frag = true; + } + + return recv_ret; +} + +/** + * @see auth_internal.h + */ +void auth_message_hdr_to_cpu(struct auth_message_frag_hdr *frag_hdr) +{ + frag_hdr->sync_flags = sys_be16_to_cpu(frag_hdr->sync_flags); + frag_hdr->payload_len = sys_be16_to_cpu(frag_hdr->payload_len); +} + +/** + * @see auth_internal.h + */ +void auth_message_hdr_to_be16(struct auth_message_frag_hdr *frag_hdr) +{ + frag_hdr->sync_flags = sys_cpu_to_be16(frag_hdr->sync_flags); + frag_hdr->payload_len = sys_cpu_to_be16(frag_hdr->payload_len); +} + + +/** + * @see auth_xport.h + */ +void auth_xport_set_sendfunc(auth_xport_hdl_t xporthdl, send_xport_t send_func) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + xp_inst->send_func = send_func; +} + +/** + * @see auth_xport.h + */ +void auth_xport_set_context(auth_xport_hdl_t xporthdl, void *context) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + xp_inst->xport_ctx = context; +} + +/** + * @see auth_xport.h + */ +void *auth_xport_get_context(auth_xport_hdl_t xporthdl) +{ + struct auth_xport_instance *xp_inst = (struct auth_xport_instance *)xporthdl; + + return xp_inst->xport_ctx; +} + + + + + + diff --git a/lib/auth/auth_xport_serial.c b/lib/auth/auth_xport_serial.c new file mode 100644 index 000000000000..b00005b0761f --- /dev/null +++ b/lib/auth/auth_xport_serial.c @@ -0,0 +1,602 @@ +/** + * @file auth_xport_serial.c + * + * @brief Lower serial transport layer. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "auth_internal.h" + + +#define LOG_LEVEL CONFIG_AUTH_LOGLEVEL +#include +LOG_MODULE_REGISTER(auth_serial_xport, CONFIG_AUTH_LOG_LEVEL); + + +#define SERIAL_LINK_MTU (600u) +#define SERIAL_XP_BUFFER_LEN SERIAL_LINK_MTU +#define NUM_BUFFERS (7u) + +/* Number of events on the msg queue. NOTE: This queue is shared by all instances. */ +#define RX_EVENT_MSGQ_COUNT (10u) + +#define MAX_SERIAL_INSTANCES (3u) +#define SERIAL_XP_RECV_THRD_PRIORITY (0) +#define SERIAL_XP_RECV_STACK_SIZE (1024u) + +#define SERIAL_TX_WAIT_MSEC (1500u) + + + +/* Serial transport instance */ +struct serial_xp_instance { + bool in_use; + auth_xport_hdl_t xport_hdl; + + /* The UART device instance */ + const struct device *uart_dev; + + /* Current transmit buffer */ + uint8_t *tx_buf; + uint16_t tx_bytes; /* number of bytes to send */ + uint16_t curr_tx_cnt; /* current tx send count */ + + /* Semaphore for TX */ + struct k_sem tx_sem; + + /* current rx buffer */ + uint8_t *rx_buf; + uint16_t curr_rx_cnt; +}; + + +/* Buffer used for TX/RX */ +struct serial_xp_buffer { + bool in_use; + uint32_t bufidx; /* Buffer bit index. */ + uint8_t buffer[SERIAL_XP_BUFFER_LEN]; +}; + +/** + * Used to pass bytes to the receive thread from the + * UART interrupt routine. + */ +struct serial_recv_event { + + struct serial_xp_instance *serial_inst; + uint8_t *rx_buf; + uint16_t frag_offset; + uint16_t frag_len; + + /* struct needs to be multiple of 4 bytes to work with the + * message queue, add padding here */ + uint8_t pad[4]; +}; + + +static struct serial_xp_instance serial_xp_inst[CONFIG_NUM_AUTH_INSTANCES]; + + +/** + * Serial receive thread. Used to forward bytes from the ISR to the + * transport receive queue. One thread used for all serial instances. + */ +static void auth_xp_serial_recv_thrd(void *arg1, void *arg2, void *arg3); + +K_THREAD_DEFINE(serial_recv, SERIAL_XP_RECV_STACK_SIZE, auth_xp_serial_recv_thrd, + NULL, NULL, NULL, SERIAL_XP_RECV_THRD_PRIORITY, 0, 0); + +/** + * Receive event message queue + */ +K_MSGQ_DEFINE(recv_event_queue, sizeof(struct serial_recv_event), RX_EVENT_MSGQ_COUNT, 4); + + +/* Atomic bits to determine if a buffer is in use. If bit is set + * buffer is in use. */ +ATOMIC_DEFINE(buffer_in_use, NUM_BUFFERS); + + +/** + * Serial buffer pool, used across all serial instances. + * + * @note: This is a simple implementation of a buffer pool. Another + * option considered was creating a memory slab, this buffer + * pool was selected because of its simplicity. + * + * @note: There is a trade-off between the number of buffers and the + * serial MTU size (SERIAL_LINK_MTU). The larger the MTU size the + * fewer buffers needed. + */ +static struct serial_xp_buffer serial_xp_bufs[NUM_BUFFERS] = { + { .in_use = false, .bufidx = 0 }, + { .in_use = false, .bufidx = 1 }, + { .in_use = false, .bufidx = 2 }, + { .in_use = false, .bufidx = 3 }, + { .in_use = false, .bufidx = 4 }, + { .in_use = false, .bufidx = 5 }, + { .in_use = false, .bufidx = 6 } +}; + + +/** + * Returns information about a serial buffer. + * + * @param buf Pointer to buffer. + * + * @return Pointer to serial buffer info. + */ +static struct serial_xp_buffer *serial_xp_buffer_info(const uint8_t *buf) +{ + if (buf == NULL) { + return NULL; + } + + /* get pointer to containing struct*/ + struct serial_xp_buffer *xp_buf = + (struct serial_xp_buffer *)CONTAINER_OF(buf, struct serial_xp_buffer, buffer); + + return xp_buf; +} + + +/** + * Gets a free buffer. + * + * @param buflen Requested buffer size, if too large NULL return. + * + * @return Pointer to buffer, else NULL on error. + */ +static uint8_t *serial_xp_get_buffer(uint32_t buflen) +{ + int cnt; + + if (buflen > SERIAL_LINK_MTU) { + LOG_ERR("Buffer request too large: %d", buflen); + return NULL; + } + + /* check array of tx buffers */ + for (cnt = 0; cnt < NUM_BUFFERS; cnt++) { + if (!atomic_test_and_set_bit(buffer_in_use, cnt)) { + serial_xp_bufs[cnt].in_use = true; + return serial_xp_bufs[cnt].buffer; + } + } + + return NULL; +} + + +/** + * Free buffer. + * + * @param buffer Buffer to free. + */ +static void serial_xp_free_buffer(const uint8_t *buffer) +{ + if (buffer == NULL) { + return; + } + struct serial_xp_buffer *xp_buffer = serial_xp_buffer_info(buffer); + + xp_buffer->in_use = false; + atomic_clear_bit(buffer_in_use, xp_buffer->bufidx); +} + + +/** + * Gets a serial transport instance. + * + * @return Pointer to serial transport, else NULL on error. + */ +static struct serial_xp_instance *auth_xp_serial_get_instance(void) +{ + uint32_t cnt; + + for (cnt = 0; cnt < MAX_SERIAL_INSTANCES; cnt++) { + + if (!serial_xp_inst[cnt].in_use) { + serial_xp_inst[cnt].in_use = true; + return &serial_xp_inst[cnt]; + } + } + + return NULL; +} + +/** + * Free serial transport instance. + * + * @param serial_inst Pointer to serial transport instance. + */ +static void auth_xp_serial_free_instance(struct serial_xp_instance *serial_inst) +{ + if (serial_inst != NULL) { + + serial_inst->in_use = false; + serial_inst->xport_hdl = NULL; + serial_inst->uart_dev = NULL; + + /* free tx and rx buffers */ + if (serial_inst->tx_buf != NULL) { + serial_xp_free_buffer(serial_inst->tx_buf); + serial_inst->tx_buf = NULL; + } + + if (serial_inst->rx_buf != NULL) { + serial_xp_free_buffer(serial_inst->rx_buf); + serial_inst->rx_buf = NULL; + } + + serial_inst->tx_bytes = 0; + serial_inst->curr_tx_cnt = 0; + serial_inst->curr_rx_cnt = 0; + } +} + + +/** + * UART receive thread. Handles bytes from the UART interrupt routine + * and forwards to the common transport receive queue. + * + * Handles receive byes from all of the serial instances. + * + */ +static void auth_xp_serial_recv_thrd(void *arg1, void *arg2, void *arg3) +{ + struct serial_recv_event recv_event; + + /* unused */ + (void)arg1; + (void)arg2; + (void)arg3; + + while (true) { + + if (k_msgq_get(&recv_event_queue, &recv_event, K_FOREVER) == 0) { + + auth_message_assemble(recv_event.serial_inst->xport_hdl, + recv_event.rx_buf + recv_event.frag_offset, + recv_event.frag_len); + + /* free RX buffer */ + serial_xp_free_buffer(recv_event.rx_buf); + } + + } /* end while() */ + +} + + +/** + * Reads bytes from UART into a rx buffer. When enough bytes have + * accumulated to fill a message fragment, put fragment onto message queue. + * The serial RX thread will read this message and forward bytes to upper + * layer transport. + * + * This function is called in an ISR context. + * + * @param uart Pointer to UART device + * @param xp_inst Pointer to serial transport instance. + * + */ +static void auth_xp_serial_irq_recv_fragment(struct serial_xp_instance *xp_inst) +{ + int num_bytes; + int total_cnt; + uint8_t *new_rxbuf = NULL; + uint16_t frag_beg_offset; + uint16_t frag_bytes; + uint16_t remaining_buffer_bytes; + struct serial_recv_event recv_event; + + if (xp_inst->rx_buf == NULL) { + /* try to allocate buffer */ + xp_inst->rx_buf = serial_xp_get_buffer(SERIAL_XP_BUFFER_LEN); + xp_inst->curr_rx_cnt = 0; + + if (xp_inst->rx_buf == NULL) { + LOG_ERR("Out of free buffers for Rx."); + return; + } + } + + /* Check if rx buffer is full, if so then something went wrong. + * Log and error and drop bytes received so far */ + if (xp_inst->curr_rx_cnt == SERIAL_XP_BUFFER_LEN) { + xp_inst->curr_rx_cnt = 0; + LOG_ERR("Receive buffer full, dropped %d bytes.", SERIAL_XP_BUFFER_LEN); + } + + num_bytes = uart_fifo_read(xp_inst->uart_dev, xp_inst->rx_buf + xp_inst->curr_rx_cnt, + SERIAL_XP_BUFFER_LEN - xp_inst->curr_rx_cnt); + total_cnt += num_bytes; + + xp_inst->curr_rx_cnt += num_bytes; + + + /* Is there a full frame? */ + if (auth_message_get_fragment(xp_inst->rx_buf, xp_inst->curr_rx_cnt, + &frag_beg_offset, &frag_bytes)) { + + /* A full message fragment is present in the input buffer + * starting at frag_beg_offset and frag_bytes in length. + * It's possible to have the beginning of a second fragment + * following this first fragment. */ + + /* get new rx buffer */ + new_rxbuf = serial_xp_get_buffer(SERIAL_XP_BUFFER_LEN); + + /* If there's garbage before the frame start,then skip. + * If there is another fragment or partial fragment following + * then copy to new buffer. 'remaining_buffer_bytes' is the + * number of valid bytes after the current fragment. */ + remaining_buffer_bytes = xp_inst->curr_rx_cnt - frag_beg_offset - frag_bytes; + + /* If fragment bytes are less than the current, then the buffer contains bytes + * for the next fragment. */ + if ((remaining_buffer_bytes != 0) && (new_rxbuf != NULL)) { + /* copy extra bytes to new buffer */ + memcpy(new_rxbuf, xp_inst->rx_buf + frag_beg_offset + frag_bytes, + remaining_buffer_bytes); + } + + + /* Setup receive event */ + recv_event.rx_buf = xp_inst->rx_buf; + recv_event.frag_offset = frag_beg_offset; + recv_event.frag_len = frag_bytes; + recv_event.serial_inst = xp_inst; + + /* send fragment to receive thread via message queue */ + if (k_msgq_put(&recv_event_queue, &recv_event, K_NO_WAIT) != 0) { + /* an error occurred, free buffer */ + serial_xp_free_buffer(recv_event.rx_buf); + LOG_ERR("Failed to queue recv event, dropping recv bytes."); + } + + /* now setup new RX fragment */ + if (new_rxbuf != NULL) { + xp_inst->rx_buf = new_rxbuf; + xp_inst->curr_rx_cnt = remaining_buffer_bytes; + } else { + /* no free buffers */ + xp_inst->rx_buf = NULL; + xp_inst->curr_rx_cnt = 0; + } + } +} + +/** + * For interrupt driven IO for serial link. + * @param dev Pointer to UART device. + * @param user_data Used to store serial instance. + */ +static void auth_xp_serial_irq_cb(const struct device *uart_dev, void *user_data) +{ + int num_bytes; + static int total_cnt = 0; + + enum uart_rx_stop_reason rx_stop; + struct serial_xp_instance *xp_inst = (struct serial_xp_instance *) user_data; + + uart_irq_update(uart_dev); + + /* did an error happen */ + rx_stop = uart_err_check(uart_dev); + + if (rx_stop != 0) { + /* An error, either: + * UART_ERROR_OVERRUN, UART_ERROR_PARITY, + * UART_ERROR_FRAMING, UART_ERROR_BREAK + */ + LOG_ERR("UART error: %d", rx_stop); + return; + } + + /* read any chars first */ + while (uart_irq_rx_ready(uart_dev)) { + auth_xp_serial_irq_recv_fragment(xp_inst); + } + + /* Any bytes ready to send? */ + if (xp_inst->tx_bytes == 0) { + /* check if we can disable TX */ + if (uart_irq_tx_complete(uart_dev)) { + uart_irq_tx_disable(uart_dev); + } + + return; + } + + total_cnt = 0; + while (uart_irq_tx_ready(uart_dev) && xp_inst->tx_buf != NULL) { + + num_bytes = uart_fifo_fill(uart_dev, + xp_inst->tx_buf + xp_inst->curr_tx_cnt, + xp_inst->tx_bytes); + + /* check return can this be an error? */ + xp_inst->tx_bytes -= num_bytes; + xp_inst->curr_tx_cnt += num_bytes; + + total_cnt += num_bytes; + + /* if no more data to send, then break */ + if (xp_inst->tx_bytes == 0) { + LOG_INF("Sent tx buffer, bytes: %d.", xp_inst->curr_tx_cnt); + break; + } + } + + /* we're done sending */ + if ((xp_inst->tx_bytes == 0) && (xp_inst->tx_buf != NULL)) { + serial_xp_free_buffer(xp_inst->tx_buf); + xp_inst->tx_buf = NULL; + xp_inst->curr_tx_cnt = 0; + + /* signal TX complete */ + k_sem_give(&xp_inst->tx_sem); + } +} + + +/** + * Send bytes on the serial link. + * @note: This call will block!!! Should not call from an ISR. + * + * @param xport_hdl Transport handle. + * @param data Bytes to send. + * @param len Number of bytes to send. + * + * @return Number of bytes sent on success, else negative error value. + */ +static int auth_xp_serial_send(auth_xport_hdl_t xport_hdl, const uint8_t *data, + const size_t len) +{ + if (len > SERIAL_LINK_MTU) { + LOG_ERR("Too many bytes to send."); + return AUTH_ERROR_INVALID_PARAM; + } + + struct serial_xp_instance *serial_inst = + (struct serial_xp_instance *)auth_xport_get_context(xport_hdl); + + /* is there a pending TX operation? If so return busy error */ + if (serial_inst->tx_buf != NULL) { + LOG_ERR("TX operation in process."); + return -EAGAIN; + } + + /* get free buffer for tx */ + serial_inst->tx_buf = serial_xp_get_buffer(len); + + if (serial_inst->tx_buf == NULL) { + LOG_ERR("No free TX buffer."); + serial_inst->tx_bytes = 0; + serial_inst->curr_tx_cnt = 0; + return AUTH_ERROR_NO_RESOURCE; + } + + /* fill buffer, set as _in use */ + memcpy(serial_inst->tx_buf, data, len); + serial_inst->tx_bytes = len; + serial_inst->curr_tx_cnt = 0; + + /* should kick off an interrupt */ + uart_irq_tx_enable(serial_inst->uart_dev); + + LOG_INF("Started TX operation"); + + /* Wait on semaphore for TX to complete */ + int ret = k_sem_take(&serial_inst->tx_sem, K_MSEC(SERIAL_TX_WAIT_MSEC)); + + if (ret) { + return ret; + } + + return (int)len; +} + + +/** + * @see auth_xport.h + */ +int auth_xp_serial_init(const auth_xport_hdl_t xport_hdl, uint32_t flags, + void *xport_param) +{ + struct auth_xp_serial_params *serial_param = + (struct auth_xp_serial_params *)xport_param; + + struct serial_xp_instance *serial_inst = auth_xp_serial_get_instance(); + + if (serial_inst == NULL) { + LOG_ERR("No free serial xport instances."); + return AUTH_ERROR_NO_RESOURCE; + } + + serial_inst->xport_hdl = xport_hdl; + serial_inst->uart_dev = serial_param->uart_dev; + + /* set serial irq callback */ + uart_irq_callback_user_data_set(serial_inst->uart_dev, + auth_xp_serial_irq_cb, + serial_inst); + + /* set context into xport handle */ + auth_xport_set_context(xport_hdl, serial_inst); + + auth_xport_set_sendfunc(xport_hdl, auth_xp_serial_send); + + /* Init semaphore used to wait for TX to complete. */ + k_sem_init(&serial_inst->tx_sem, 0, 1); + + /* reset tx vars */ + serial_inst->tx_buf = NULL; + serial_inst->tx_bytes = 0; + serial_inst->curr_tx_cnt = 0; + + /* get rx buffer */ + serial_inst->rx_buf = serial_xp_get_buffer(SERIAL_XP_BUFFER_LEN); + serial_inst->curr_rx_cnt = 0; + + /* enable rx interrupts */ + uart_irq_rx_enable(serial_param->uart_dev); + + /* enable error irq */ + uart_irq_err_enable(serial_param->uart_dev); + + return AUTH_SUCCESS; +} + +/** + * @see auth_xport.h + */ +int auth_xp_serial_deinit(const auth_xport_hdl_t xport_hdl) +{ + struct serial_xp_instance *serial_inst = auth_xport_get_context(xport_hdl); + + auth_xp_serial_free_instance(serial_inst); + + auth_xport_set_context(xport_hdl, NULL); + + return AUTH_SUCCESS; +} + + +/** + * @see auth_xport.h + */ +int auth_xp_serial_event(const auth_xport_hdl_t xporthdl, + struct auth_xport_evt *event) +{ + /* stub for now */ + return AUTH_ERROR_INTERNAL; +} + +/** + * @see auth_xport.h + */ +int auth_xp_serial_get_max_payload(const auth_xport_hdl_t xporthdl) +{ + return SERIAL_LINK_MTU; +} + + + + + diff --git a/samples/authentication/auth_samples.rst b/samples/authentication/auth_samples.rst new file mode 100644 index 000000000000..563dd2d8784d --- /dev/null +++ b/samples/authentication/auth_samples.rst @@ -0,0 +1,15 @@ +.. auth-samples: + +Authentication Samples +###################### + +The following samples show how to use the Authentication library over Bluetooth or Serial transport. + + +.. toctree:: + :maxdepth: 1 + :glob: + + **/* + + diff --git a/samples/authentication/bluetooth/README.rst b/samples/authentication/bluetooth/README.rst new file mode 100644 index 000000000000..2bc0720eb1f5 --- /dev/null +++ b/samples/authentication/bluetooth/README.rst @@ -0,0 +1,28 @@ +.. _auth_bluetooth-sample: + +Authentication over Bluetooth +############################# + +Overview +******** + +There are two Bluetooth firmware applications, central and peripheral. The Central acts as +the client, the peripheral acts as the server. The Central initiates the authentication +messages. + +The authentication method, DTLS or Challenge-Response, is configurable via KConfig menu. + +Building and Running +-------------------- +This sample was developed and tested with two Nordic nRF52840 dev +kits (see: https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK). Two Ubuntu +VMs were used, one running the Central the other VM running the Peripheral. + +To build the peripheral with DTLS:|br| +cmake -Bbuild_auth_peripheral -DBOARD=nrf52840dk_nrf52840 -DCONF_FILE=dtls.prj.conf samples/authentication/bluetooth/peripheral_auth + +To build the central with DLTS using West:|br| +west build -d build_auth_central -b nrf52840dk_nrf52840 -- -DCONF_FILE=dtls.prj.conf samples/authentication/bluetooth/central_auth |br| +(note: Two dashes '-' after 'west build -b build_auth_central -b nrf52840dk_nrf52840 --', necessary for -DCONF_FILE define) + + diff --git a/samples/authentication/bluetooth/central_auth/CMakeLists.txt b/samples/authentication/bluetooth/central_auth/CMakeLists.txt new file mode 100644 index 000000000000..84226beaad08 --- /dev/null +++ b/samples/authentication/bluetooth/central_auth/CMakeLists.txt @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(central_auth) + +target_include_directories(app PUBLIC ${CMAKE_SOURCE_DIR}) + +target_sources(app PRIVATE + src/main.c +) diff --git a/samples/authentication/bluetooth/central_auth/dtls.prj.conf b/samples/authentication/bluetooth/central_auth/dtls.prj.conf new file mode 100644 index 000000000000..1416fa23be40 --- /dev/null +++ b/samples/authentication/bluetooth/central_auth/dtls.prj.conf @@ -0,0 +1,81 @@ +CONFIG_BT=y +CONFIG_BT_DEBUG_LOG=y +CONFIG_BT_SMP=y +CONFIG_BT_SIGNING=y +CONFIG_BT_DIS=y +CONFIG_BT_ATT_PREPARE_COUNT=5 +CONFIG_BT_PRIVACY=y +CONFIG_BT_DEVICE_NAME="Zephyr Central Auth" +CONFIG_BT_DEVICE_APPEARANCE=833 +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=65 +CONFIG_BT_RX_BUF_LEN=1600 +CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_PERIPHERAL=n +CONFIG_BT_MAX_PAIRED=2 +CONFIG_BT_MAX_CONN=2 + + +# Increase stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +# If not using DTLS, then the main stack size can be reduced to 1024 bytes +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_AUTH_LIB=y +CONFIG_BT_XPORT=y +CONFIG_AUTH_DTLS=y + +# logging +CONFIG_LOG=y +CONFIG_AUTH_LOG_LEVEL=0 +CONFIG_USE_SEGGER_RTT=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_LOG_PRINTK=y + +# The assumes the board has a hardware based random +# number generator. +CONFIG_ENTROPY_DEVICE_RANDOM_GENERATOR=y + + +# Mbed config'CONFIG_MBEDTLS=y +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" +CONFIG_MBEDTLS_TLS_VERSION_1_2=y +CONFIG_MBEDTLS_DTLS=y +CONFIG_MBEDTLS_ENTROPY_ENABLED=y + +# Supported key exchange modes +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED=y + +# Supported elliptic curves +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y + +# Supported cipher modes +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y +CONFIG_MBEDTLS_CIPHER_GCM_ENABLED=y +CONFIG_MBEDTLS_CIPHER_MODE_CBC_ENABLED=y + + + +# Supported message authentication methods +CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y +CONFIG_MBEDTLS_MAC_CMAC_ENABLED=y + + +# Other configurations +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=1500 +CONFIG_MBEDTLS_DEBUG=y +CONFIG_MBEDTLS_DEBUG_LEVEL=0 +CONFIG_MBEDTLS_ENABLE_HEAP=y + +# Mbed uses a chunk of memory, it might be possible to reduce +# this heap usage. +CONFIG_MBEDTLS_HEAP_SIZE=65535 +CONFIG_APP_LINK_WITH_MBEDTLS=y + + + diff --git a/samples/authentication/bluetooth/central_auth/prj.conf b/samples/authentication/bluetooth/central_auth/prj.conf new file mode 100644 index 000000000000..cfe030738af5 --- /dev/null +++ b/samples/authentication/bluetooth/central_auth/prj.conf @@ -0,0 +1,38 @@ +CONFIG_BT=y +CONFIG_BT_DEBUG_LOG=y +CONFIG_BT_SMP=y +CONFIG_BT_SIGNING=y +CONFIG_BT_PERIPHERAL=n +CONFIG_BT_DIS=y +CONFIG_BT_ATT_PREPARE_COUNT=5 +CONFIG_BT_PRIVACY=y +CONFIG_BT_DEVICE_NAME="Zephyr Central Auth" +CONFIG_BT_DEVICE_APPEARANCE=833 +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=65 +CONFIG_BT_RX_BUF_LEN=1600 +CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_MAX_PAIRED=2 +CONFIG_BT_MAX_CONN=2 + + +# Increase stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +# If not using DTLS, then the main stack size can be reduced to 1024 bytes +CONFIG_MAIN_STACK_SIZE=1024 +CONFIG_AUTH_LIB=y +CONFIG_BT_XPORT=y +CONFIG_AUTH_CHALLENGE_RESPONSE=y +CONFIG_TINYCRYPT=y +CONFIG_TINYCRYPT_SHA256=y +CONFIG_AUTH_CHALLENGE_RESPONSE=y + +# logging +CONFIG_LOG=y +CONFIG_AUTH_LOG_LEVEL=0 +CONFIG_USE_SEGGER_RTT=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_LOG_PRINTK=y + diff --git a/samples/authentication/bluetooth/central_auth/sample.yaml b/samples/authentication/bluetooth/central_auth/sample.yaml new file mode 100644 index 000000000000..3e1e7090afbb --- /dev/null +++ b/samples/authentication/bluetooth/central_auth/sample.yaml @@ -0,0 +1,8 @@ +sample: + description: Auth central sample app + name: Auth Central +tests: + sample.central_auth: + harness: bluetooth + platform_allow: qemu_x86 + tags: bluetooth authentication diff --git a/samples/authentication/bluetooth/central_auth/src/main.c b/samples/authentication/bluetooth/central_auth/src/main.c new file mode 100644 index 000000000000..b738c6f3a06c --- /dev/null +++ b/samples/authentication/bluetooth/central_auth/src/main.c @@ -0,0 +1,584 @@ + +/* main.c - Application main entry point + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#if defined(CONFIG_AUTH_DTLS) +#include "../../../certs/auth_certs.h" +#endif + + +LOG_MODULE_REGISTER(central_auth, CONFIG_AUTH_LOG_LEVEL); + + +/** + * Declare UUIDs used for the Authentication service and characteristics + * see auth_xport.h + */ + +static struct bt_uuid_128 auth_service_uuid = AUTH_SERVICE_UUID; + +static struct bt_uuid_128 auth_client_char = AUTH_SVC_CLIENT_CHAR_UUID; + +static struct bt_uuid_128 auth_server_char = AUTH_SVC_SERVER_CHAR; + + + +#if defined(CONFIG_AUTH_DTLS) +/* The Root and Intermediate Certs in a single CA chain. + * plus the server cert. All in PEM format.*/ +static const uint8_t auth_cert_ca_chain[] = AUTH_ROOTCA_CERT_PEM AUTH_INTERMEDIATE_CERT_PEM; +static const uint8_t auth_dev_client_cert[] = AUTH_CLIENT_CERT_PEM; +static const uint8_t auth_client_privatekey[] = AUTH_CLIENT_PRIVATE_KEY_PEM; + +static struct auth_optional_param dtls_certs_param = { + .param_id = AUTH_DTLS_PARAM, + .param_body = { + .dtls_certs = { + .server_ca_chain_pem = { + .cert = auth_cert_ca_chain, + .cert_size = sizeof(auth_cert_ca_chain), + }, + + .device_cert_pem = { + .cert = auth_dev_client_cert, + .cert_size = sizeof(auth_dev_client_cert), + .priv_key = auth_client_privatekey, + .priv_key_size = sizeof(auth_client_privatekey) + } + } + } +}; +#endif + +#if defined(CONFIG_AUTH_CHALLENGE_RESPONSE) +#define NEW_SHARED_KEY_LEN (32u) + +/* Use a different key than default */ +static uint8_t chal_resp_sharedkey[NEW_SHARED_KEY_LEN] = { + 0x21, 0x8e, 0x37, 0x42, 0x1e, 0xe1, 0x2a, 0x22, 0x7c, 0x4b, 0x3f, 0x3f, 0x07, 0x5e, 0x8a, 0xd8, + 0x24, 0xdf, 0xca, 0xf4, 0x04, 0xd0, 0x3e, 0x22, 0x61, 0x9f, 0x24, 0xa3, 0xc7, 0xf6, 0x5d, 0x66 +}; + +static struct auth_optional_param chal_resp_param = { + .param_id = AUTH_CHALRESP_PARAM, + .param_body = { + .chal_resp = { + .shared_key = chal_resp_sharedkey, + }, + } +}; +#endif + +static struct bt_conn *default_conn; + +/* UUID to discover */ +static struct bt_gatt_discover_params discover_params; +static struct bt_gatt_subscribe_params subscribe_params; + + + +/** + * Authentication connect struct + */ +static struct authenticate_conn central_auth_conn; + + + +/* Auth service, client descriptor, server descriptor */ +#define AUTH_SVC_GATT_COUNT (4u) + +#define AUTH_SVC_INDEX (0u) +#define AUTH_SVC_CLIENT_CHAR_INDEX (1u) + +/* for enable/disable of notification */ +#define AUTH_SVC_CLIENT_CCC_INDEX (2u) +#define AUTH_SVC_SERVER_CHAR_INDEX (3u) + +/** + * Used to store Authentication GATT service and characteristics. + */ +typedef struct { + const struct bt_uuid *uuid; + const struct bt_gatt_attr *attr; + uint16_t handle; + uint16_t value_handle; + uint8_t permissions; /* Bitfields from: BT_GATT_PERM_NONE, in gatt.h */ + const uint32_t gatt_disc_type; +} auth_svc_gatt_t; + + +static uint32_t auth_desc_index; + + +/* Table content should match indexes above */ +static auth_svc_gatt_t auth_svc_gatt_tbl[AUTH_SVC_GATT_COUNT] = { + { (const struct bt_uuid *)&auth_service_uuid, NULL, 0, 0, BT_GATT_PERM_NONE, BT_GATT_DISCOVER_PRIMARY }, /* AUTH_SVC_INDEX */ + { (const struct bt_uuid *)&auth_client_char, NULL, 0, 0, BT_GATT_PERM_NONE, BT_GATT_DISCOVER_CHARACTERISTIC }, /* AUTH_SVC_CLIENT_CHAR_INDEX */ + { BT_UUID_GATT_CCC, NULL, 0, 0, BT_GATT_PERM_NONE, BT_GATT_DISCOVER_DESCRIPTOR }, /*AUTH_SVC_CLIENT_CCC_INDEX CCC for Client char */ + { (const struct bt_uuid *)&auth_server_char, NULL, 0, 0, BT_GATT_PERM_NONE, BT_GATT_DISCOVER_CHARACTERISTIC } /* AUTH_SVC_SERVER_CHAR_INDEX */ +}; + + +/** + * Params used to change the connection MTU length. + */ +struct bt_gatt_exchange_params mtu_parms; + +void mtu_change_cb(struct bt_conn *conn, uint8_t err, + struct bt_gatt_exchange_params *params) +{ + if (err) { + LOG_ERR("Failed to set MTU, err: %d", err); + } else { + LOG_DBG("Successfully set MTU to: %d", bt_gatt_get_mtu(conn)); + } +} + + +/** + * Characteristic discovery function + * + * + * @param conn Bluetooth conection. + * @param attr Discovered attribute. NOTE: This pointer will go out fo scope + * do not save pointer for future use. + * @param params Discover params. + * + * @return BT_GATT_ITER_STOP + */ +static uint8_t discover_func(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) +{ + int err; + + if (!attr) { + LOG_INF("Discover complete, NULL attribute."); + (void)memset(params, 0, sizeof(*params)); + return BT_GATT_ITER_STOP; + } + + + /* debug output */ + LOG_DBG("====auth_desc_index is: %d=====", auth_desc_index); + LOG_DBG("[ATTRIBUTE] handle 0x%x", attr->handle); + LOG_DBG("[ATTRIBUTE] value handle 0x%x", bt_gatt_attr_value_handle(attr)); + + /* let's get string */ + char uuid_str[50]; + bt_uuid_to_str(attr->uuid, uuid_str, sizeof(uuid_str)); + LOG_DBG("Attribute UUID: %s", log_strdup(uuid_str)); + + /* print attribute UUID */ + bt_uuid_to_str(discover_params.uuid, uuid_str, sizeof(uuid_str)); + LOG_DBG("Discovery UUID: %s", log_strdup(uuid_str)); + + + /** + * Verify the correct UUID was found + */ + if (bt_uuid_cmp(discover_params.uuid, auth_svc_gatt_tbl[auth_desc_index].uuid)) { + + /* Failed, not the UUID we're expecting */ + LOG_ERR("Error Unknown UUID."); + return BT_GATT_ITER_STOP; + } + + /* save off GATT info */ + auth_svc_gatt_tbl[auth_desc_index].attr = NULL; /* NOTE: attr var not used for the Central */ + auth_svc_gatt_tbl[auth_desc_index].handle = attr->handle; + auth_svc_gatt_tbl[auth_desc_index].value_handle = bt_gatt_attr_value_handle(attr); + auth_svc_gatt_tbl[auth_desc_index].permissions = attr->perm; + + auth_desc_index++; + + /* Are all of the characteristics discovered? */ + if (auth_desc_index >= AUTH_SVC_GATT_COUNT) { + + /* we're done */ + LOG_INF("Discover complete"); + + /* save off the server attribute handle */ + + /* setup the subscribe params + * Value handle for the Client characteristic for indication of + * peripheral data. + */ + subscribe_params.notify = auth_xp_bt_central_notify; + subscribe_params.value = BT_GATT_CCC_NOTIFY; + subscribe_params.value_handle = + auth_svc_gatt_tbl[AUTH_SVC_CLIENT_CHAR_INDEX].value_handle; + + /* Handle for the CCC descriptor itself */ + subscribe_params.ccc_handle = + auth_svc_gatt_tbl[AUTH_SVC_CLIENT_CCC_INDEX].handle; + + err = bt_gatt_subscribe(conn, &subscribe_params); + if (err && err != -EALREADY) { + LOG_ERR("Subscribe failed (err %d)", err); + } + + /* Get the server BT characteristic, the central sends data to this characteristic */ + uint16_t server_char_handle = + auth_svc_gatt_tbl[AUTH_SVC_SERVER_CHAR_INDEX].value_handle; + + /* setup the BT transport params */ + struct auth_xp_bt_params xport_params = + { .conn = conn, .is_central = true, + .server_char_hdl = server_char_handle }; + + err = auth_xport_init(¢ral_auth_conn.xport_hdl, + central_auth_conn.instance, + AUTH_XP_TYPE_BLUETOOTH, &xport_params); + + if (err) { + LOG_ERR("Failed to initialize Bluetooth transport, err: %d", err); + return BT_GATT_ITER_STOP; + } + + /* Start auth process */ + err = auth_lib_start(¢ral_auth_conn); + if (err) { + LOG_ERR("Failed to start auth service, err: %d", err); + } else { + LOG_INF("Started auth service."); + } + + return BT_GATT_ITER_STOP; + } + + /* set up the next discovery params */ + discover_params.uuid = auth_svc_gatt_tbl[auth_desc_index].uuid; + discover_params.start_handle = attr->handle + 1; + discover_params.type = auth_svc_gatt_tbl[auth_desc_index].gatt_disc_type; + + + /* Start discovery */ + err = bt_gatt_discover(conn, &discover_params); + if (err) { + LOG_ERR("Discover failed (err %d)", err); + } + + + return BT_GATT_ITER_STOP; +} + +/** + * Connected to the peripheral device + * + * @param conn The Bluetooth connection. + * @param conn_err Connection error, 0 == no error + */ +static void connected(struct bt_conn *conn, uint8_t conn_err) +{ + char addr[BT_ADDR_LE_STR_LEN]; + int err; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + if (conn_err) { + LOG_ERR("Failed to connect to %s (%u)", log_strdup(addr), conn_err); + return; + } + + LOG_INF("Connected: %s", log_strdup(addr)); + + if (conn == default_conn) { + + /* set the max MTU, only for GATT interface */ + mtu_parms.func = mtu_change_cb; + bt_gatt_exchange_mtu(conn, &mtu_parms); + + /* reset gatt discovery index */ + auth_desc_index = 0; + + discover_params.uuid = auth_svc_gatt_tbl[auth_desc_index].uuid; + discover_params.func = discover_func; + discover_params.start_handle = 0x0001; + discover_params.end_handle = 0xffff; + discover_params.type = auth_svc_gatt_tbl[auth_desc_index].gatt_disc_type; + + /** + * Discover characteristics for the service + */ + err = bt_gatt_discover(default_conn, &discover_params); + if (err) { + LOG_ERR("Discover failed(err %d)", err); + return; + } + } +} + +/** + * Parse through the BLE adv data, looking for our service + * + * @param data Bluetooth data + * @param user_data User data. + * + * @return true on success, else false + */ +static bool bt_adv_data_found(struct bt_data *data, void *user_data) +{ + bt_addr_le_t *addr = user_data; + int err; + struct bt_uuid_128 auth_uuid; + + LOG_DBG("[AD]: %u data_len %u", data->type, data->data_len); + + if (data->type == BT_DATA_UUID128_ALL) { + + if (data->data_len != BT_UUID_SIZE_128) { + LOG_WRN("AD malformed"); + return false; + } + + /* build full UUID to check */ + auth_uuid.uuid.type = BT_UUID_TYPE_128; + memcpy(auth_uuid.val, data->data, BT_UUID_SIZE_128); + + + /** + * Is this the service we're looking for? If not continue + * else stop the scan and connect to the device. + */ + if (bt_uuid_cmp((const struct bt_uuid *)&auth_uuid, + (const struct bt_uuid *)&auth_service_uuid)) { + return true; + } + + /* stop scanning, we've found the service */ + err = bt_le_scan_stop(); + if (err) { + LOG_ERR("Stop LE scan failed (err %d)", err); + return false; + } + + /** + * @brief Connect to the device + */ + struct bt_conn_le_create_param param = BT_CONN_LE_CREATE_PARAM_INIT( + BT_CONN_LE_OPT_NONE, + BT_GAP_SCAN_FAST_INTERVAL, + BT_GAP_SCAN_FAST_INTERVAL); + + if (bt_conn_le_create(addr, ¶m, BT_LE_CONN_PARAM_DEFAULT, + &default_conn)) { + LOG_ERR("Failed to create BLE connection to peripheral."); + } + + return false; + } + + return true; +} + +/** + * Found a device when scanning. + * + * @param addr BT address + * @param rssi Signal strength + * @param type Device type + * @param ad Simple buffer. + */ +static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, + struct net_buf_simple *ad) +{ + char dev[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(addr, dev, sizeof(dev)); + LOG_DBG("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i", + log_strdup(dev), type, ad->len, rssi); + + /* We're only interested in connectable events */ + if (type == BT_HCI_ADV_IND || type == BT_HCI_ADV_DIRECT_IND) { + bt_data_parse(ad, bt_adv_data_found, (void *)addr); + } +} + +/** + * BT disconnect callback. + * + * @param conn The Bluetooth connection. + * @param reason Disconnect reason. + */ +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + LOG_INF("Disconnected: %s (reason 0x%02x)", log_strdup(addr), reason); + + if (default_conn != conn) { + return; + } + + /* de init transport */ + /* Send disconnect event to BT transport. */ + struct auth_xport_evt conn_evt = { .event = XP_EVT_DISCONNECT }; + auth_xport_event(central_auth_conn.xport_hdl, &conn_evt); + + /* Deinit lower transport */ + auth_xport_deinit(central_auth_conn.xport_hdl); + central_auth_conn.xport_hdl = NULL; + + bt_conn_unref(default_conn); + default_conn = NULL; +} + +/** + * (dis)Connection callbacks + */ +static struct bt_conn_cb conn_callbacks = { + .connected = connected, + .disconnected = disconnected, +}; + + +/** + * Authentication status callback. + * + * @param auth_conn Authentication connection. + * @param instance Instance ID. + * @param status Auth status. + * @param context Optional context. + */ +static void auth_status(struct authenticate_conn *auth_conn, enum auth_instance_id instance, + enum auth_status status, void *context) +{ + /* display status */ + printk("Authentication instance (%d) status: %s\n", instance, + auth_lib_getstatus_str(status)); + +#if defined(CONFIG_AUTH_DTLS) + if (status == AUTH_STATUS_IN_PROCESS) { + printk(" DTLS may take 30-60 seconds.\n"); + } +#endif +} + +/** + * Process log messages. + */ +static void process_log_msgs(void) +{ + while (log_process(false)) { + ; /* intentionally empty statement */ + } +} + +/** + * Idle process for app. + */ +static void idle_function(void) +{ + /* Just spin while the BT modules handle the connection */ + while (true) { + + process_log_msgs(); + + /* Let the handshake thread run */ + k_yield(); + } +} + + +/** + * Central main entry point. + */ +void main(void) +{ + int err = 0; + struct auth_optional_param *opt_parms = NULL; + + log_init(); + + LOG_INF("Client Auth started."); + + + uint32_t flags = AUTH_CONN_CLIENT; + +#if defined(CONFIG_AUTH_DTLS) && defined(CONFIG_AUTH_CHALLENGE_RESPONSE) +#error Invalid authentication config, either DTLS or Challenge-Response, not both. +#endif + +#if defined(CONFIG_AUTH_DTLS) + flags |= AUTH_CONN_DTLS_AUTH_METHOD; + + /* set TLS certs */ + opt_parms = &dtls_certs_param; + printk("Using DTLS authentication method.\n"); +#endif + + +#if defined(CONFIG_AUTH_CHALLENGE_RESPONSE) + flags |= AUTH_CONN_CHALLENGE_AUTH_METHOD; + + /* Use different shared key */ + opt_parms = &chal_resp_param; + printk("Using Challenge-Response authentication method.\n"); +#endif + + + err = auth_lib_init(¢ral_auth_conn, AUTH_INST_1_ID, auth_status, + NULL, opt_parms, flags); + + if (err) { + LOG_ERR("Failed to init authentication service, err: %d.", err); + idle_function(); /* does not return */ + } + + /** + * @brief Enable the Bluetooth module. Passing NULL to bt_enable + * will block while the BLE stack is initialized. + * Enable bluetooth module + */ + err = bt_enable(NULL); + if (err) { + LOG_ERR("Failed to enable the bluetooth module, err: %d", err); + idle_function(); /* does not return */ + } + + /* Register connect/disconnect callbacks */ + bt_conn_cb_register(&conn_callbacks); + + printk("Starting BLE scanning\n"); + + /* start scanning for peripheral */ + err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, device_found); + + if (err) { + printk("Scanning failed to start (err %d)\n", err); + } + + /* does not return */ + idle_function(); + + /* should not reach here */ + +} diff --git a/samples/authentication/bluetooth/peripheral_auth/CMakeLists.txt b/samples/authentication/bluetooth/peripheral_auth/CMakeLists.txt new file mode 100644 index 000000000000..5d88823fd65f --- /dev/null +++ b/samples/authentication/bluetooth/peripheral_auth/CMakeLists.txt @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(periph_auth) + +target_include_directories(app PUBLIC ${CMAKE_SOURCE_DIR}) + + +target_sources(app PRIVATE + src/main.c +) diff --git a/samples/authentication/bluetooth/peripheral_auth/dtls.prj.conf b/samples/authentication/bluetooth/peripheral_auth/dtls.prj.conf new file mode 100644 index 000000000000..03992cb768e5 --- /dev/null +++ b/samples/authentication/bluetooth/peripheral_auth/dtls.prj.conf @@ -0,0 +1,76 @@ +CONFIG_BT=y +CONFIG_BT_DEBUG_LOG=y +CONFIG_BT_SMP=y +CONFIG_BT_SIGNING=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DIS=y +CONFIG_BT_ATT_PREPARE_COUNT=5 +CONFIG_BT_PRIVACY=y +CONFIG_BT_DEVICE_NAME="Zephyr Peripheral Auth" +CONFIG_BT_DEVICE_APPEARANCE=833 +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=65 +CONFIG_BT_RX_BUF_LEN=1600 + + +# Increase stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +# If not using DTLS, then the main stack size can be reduced to 1024 bytes +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_AUTH_LIB=y +CONFIG_BT_XPORT=y +CONFIG_AUTH_DTLS=y + +CONFIG_AUTH_LOG_LEVEL=0 + +# logging +CONFIG_LOG=y +CONFIG_USE_SEGGER_RTT=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_LOG_PRINTK=y + +# The assumes the board has a hardware based random +# number generator. +CONFIG_ENTROPY_DEVICE_RANDOM_GENERATOR=y + +# Mbed config'CONFIG_MBEDTLS=y +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" +CONFIG_MBEDTLS_TLS_VERSION_1_2=y +CONFIG_MBEDTLS_DTLS=y +CONFIG_MBEDTLS_ENTROPY_ENABLED=y + +# Supported key exchange modes +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED=y + +# Supported elliptic curves +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y + +# Supported cipher modes +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y +CONFIG_MBEDTLS_CIPHER_GCM_ENABLED=y +CONFIG_MBEDTLS_CIPHER_MODE_CBC_ENABLED=y + + +# Supported message authentication methods +CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y +CONFIG_MBEDTLS_MAC_CMAC_ENABLED=y + + +# Other configurations +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=1500 +CONFIG_MBEDTLS_DEBUG=y +CONFIG_MBEDTLS_DEBUG_LEVEL=0 +CONFIG_MBEDTLS_ENABLE_HEAP=y + +# Mbed uses a chunk of memory, it might be possible to reduce +# this heap usage. +CONFIG_MBEDTLS_HEAP_SIZE=65535 +CONFIG_APP_LINK_WITH_MBEDTLS=y + + + diff --git a/samples/authentication/bluetooth/peripheral_auth/prj.conf b/samples/authentication/bluetooth/peripheral_auth/prj.conf new file mode 100644 index 000000000000..78ca73f07904 --- /dev/null +++ b/samples/authentication/bluetooth/peripheral_auth/prj.conf @@ -0,0 +1,38 @@ +CONFIG_BT=y +CONFIG_BT_DEBUG_LOG=y +CONFIG_BT_SMP=y +CONFIG_BT_SIGNING=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DIS=y +CONFIG_BT_ATT_PREPARE_COUNT=5 +CONFIG_BT_PRIVACY=y +CONFIG_BT_DEVICE_NAME="Zephyr Peripheral Auth" +CONFIG_BT_DEVICE_APPEARANCE=833 +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=65 +CONFIG_BT_RX_BUF_LEN=1600 + + +# Increase stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +# If not using DTLS, then the main stack size can be reduced to 1024 bytes +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_AUTH_LIB=y +CONFIG_BT_XPORT=y +CONFIG_AUTH_CHALLENGE_RESPONSE=y +CONFIG_TINYCRYPT=y +CONFIG_TINYCRYPT_SHA256=y +CONFIG_LOG=y +CONFIG_AUTH_LOG_LEVEL=3 +CONFIG_AUTH_CHALLENGE_RESPONSE=y + +# logging +CONFIG_LOG=y +CONFIG_USE_SEGGER_RTT=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_LOG_PRINTK=y + + + + diff --git a/samples/authentication/bluetooth/peripheral_auth/sample.yaml b/samples/authentication/bluetooth/peripheral_auth/sample.yaml new file mode 100644 index 000000000000..3efb45478de8 --- /dev/null +++ b/samples/authentication/bluetooth/peripheral_auth/sample.yaml @@ -0,0 +1,8 @@ +sample: + description: Auth peripheral sample app + name: Auth Peripheral +tests: + sample.peripheral_auth: + harness: bluetooth + platform_allow: qemu_x86 + tags: bluetooth authentication \ No newline at end of file diff --git a/samples/authentication/bluetooth/peripheral_auth/src/main.c b/samples/authentication/bluetooth/peripheral_auth/src/main.c new file mode 100644 index 000000000000..da66c91600ca --- /dev/null +++ b/samples/authentication/bluetooth/peripheral_auth/src/main.c @@ -0,0 +1,380 @@ +/* + * Sample authentication BLE peripheral + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +LOG_MODULE_REGISTER(periph_auth, CONFIG_AUTH_LOG_LEVEL); + + + +/** + * Declare UUIDs used for the Authentication service and characteristics + */ + +static struct bt_uuid_128 auth_service_uuid = AUTH_SERVICE_UUID; + +static struct bt_uuid_128 auth_client_char = AUTH_SVC_CLIENT_CHAR_UUID; + +static struct bt_uuid_128 auth_server_char = AUTH_SVC_SERVER_CHAR; + + + +#if defined(CONFIG_AUTH_DTLS) +#include "../../../certs/auth_certs.h" +#endif + + +static void client_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value); + +/* AUTH Service Declaration */ +BT_GATT_SERVICE_DEFINE(auth_svc, + BT_GATT_PRIMARY_SERVICE(&auth_service_uuid), + + /** + * Central (client role) bt_gatt_write() ---> server characteristic --> bt_gatt_read() Peripheral (server role) + * + * Central <--- Notification (client characteristic) <--- Peripheral + * + */ + + /** + * Client characteristic, used by the peripheral (server role) to write messages authentication messages + * to the central (client role). The peripheral needs to alert the central a message is + * ready to be read. + */ + BT_GATT_CHARACTERISTIC((const struct bt_uuid*)&auth_client_char, BT_GATT_CHRC_INDICATE, + (BT_GATT_PERM_READ|BT_GATT_PERM_WRITE), NULL, NULL, NULL), + BT_GATT_CCC(client_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), + + /** + * Server characteristic, used by the central (client role) to write authentication messages to. + * to the server (peripheral) + */ + BT_GATT_CHARACTERISTIC((const struct bt_uuid*)&auth_server_char, BT_GATT_CHRC_WRITE, + (BT_GATT_PERM_READ|BT_GATT_PERM_WRITE), NULL, auth_xp_bt_central_write, NULL), +); + + + +struct bt_conn *default_conn; + +static bool is_connected = false; + +static struct authenticate_conn auth_conn; + +#if defined(CONFIG_AUTH_DTLS) +/* The Root and Intermediate Certs in a single CA chain. + * plus the server cert. All in PEM format.*/ +static const uint8_t auth_cert_ca_chain[] = AUTH_ROOTCA_CERT_PEM AUTH_INTERMEDIATE_CERT_PEM; +static const uint8_t auth_dev_server_cert[] = AUTH_SERVER_CERT_PEM; +static const uint8_t auth_server_privatekey[] = AUTH_SERVER_PRIVATE_KEY_PEM; + + +static struct auth_optional_param dtls_certs_param = { + .param_id = AUTH_DTLS_PARAM, + .param_body = { + .dtls_certs = { + .server_ca_chain_pem = { + .cert = auth_cert_ca_chain, + .cert_size = sizeof(auth_cert_ca_chain), + }, + + .device_cert_pem = { + .cert = auth_dev_server_cert, + .cert_size = sizeof(auth_dev_server_cert), + .priv_key = auth_server_privatekey, + .priv_key_size = sizeof(auth_server_privatekey) + } + } + } +}; +#endif + +#if defined(CONFIG_AUTH_CHALLENGE_RESPONSE) + +#define NEW_SHARED_KEY_LEN (32u) + +/* Use a different key than default */ +static uint8_t chal_resp_sharedkey[NEW_SHARED_KEY_LEN] = { + 0x21, 0x8e, 0x37, 0x42, 0x1e, 0xe1, 0x2a, 0x22, 0x7c, 0x4b, 0x3f, 0x3f, 0x07, 0x5e, 0x8a, 0xd8, + 0x24, 0xdf, 0xca, 0xf4, 0x04, 0xd0, 0x3e, 0x22, 0x61, 0x9f, 0x24, 0xa3, 0xc7, 0xf6, 0x5d, 0x66 +}; + + +static struct auth_optional_param chal_resp_param = { + .param_id = AUTH_CHALRESP_PARAM, + .param_body = { + .chal_resp = { + .shared_key = chal_resp_sharedkey, + }, + } +}; +#endif + +/** + * Set up the advertising data + */ +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + + /* auth service UUID */ + BT_DATA_BYTES(BT_DATA_UUID128_ALL, AUTH_SERVICE_UUID_BYTES), +}; + +/** + * Connection callback + * + * @param conn The Bluetooth connection. + * @param err Error value, 0 == success + */ +static void connected(struct bt_conn *conn, uint8_t err) +{ + int ret; + struct auth_xport_evt conn_evt; + + if (err) { + printk("Connection failed (err 0x%02x)\n", err); + } else { + default_conn = bt_conn_ref(conn); + printk("Connected\n"); + + struct auth_xp_bt_params xport_param = { .conn = conn, .is_central = false, + .client_attr = &auth_svc.attrs[1] }; + + ret = auth_xport_init(&auth_conn.xport_hdl, auth_conn.instance, + AUTH_XP_TYPE_BLUETOOTH, &xport_param); + + if(ret) { + printk("Failed to initialize BT transport, err: %d", ret); + return; + } + + is_connected = true; + + /* send connection event to BT transport */ + conn_evt.event = XP_EVT_CONNECT; + auth_xport_event(auth_conn.xport_hdl, &conn_evt); + + /* Start authentication */ + ret = auth_lib_start(&auth_conn); + + if(ret) { + printk("Failed to start authentication, err: %d\n", ret); + } + } +} + +/** + * The disconnect callback. + * + * @param conn Bluetooth connection struct + * @param reason Disconnect reason code. + */ +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + struct auth_xport_evt conn_evt; + + printk("Disconnected (reason 0x%02x)\n", reason); + + is_connected = false; + + /* Send disconnect event to BT transport. */ + conn_evt.event = XP_EVT_DISCONNECT; + auth_xport_event(auth_conn.xport_hdl, &conn_evt); + + /* Deinit lower transport */ + auth_xport_deinit(auth_conn.xport_hdl); + auth_conn.xport_hdl = NULL; + + if (default_conn) { + bt_conn_unref(default_conn); + default_conn = NULL; + } +} + +/** + * Connect callbacks + */ +static struct bt_conn_cb conn_callbacks = { + .connected = connected, + .disconnected = disconnected, +}; + +/** + * If the pairing was canceled. + * + * @param conn The Bluetooth connection. + */ +static void auth_cancel(struct bt_conn *conn) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + printk("Pairing cancelled: %s\n", addr); +} + +static struct bt_conn_auth_cb auth_cb_display = { + .cancel = auth_cancel, +}; + +/** + * Called after the BT module has initialized or not (error occurred). + * + * @param err Error code, 0 == success + */ +static void bt_ready(int err) +{ + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + printk("Bluetooth initialized\n"); + + /* Start advertising after BT module has initialized OK */ + err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0); + if (err) { + printk("Advertising failed to start (err %d)\n", err); + return; + } + + printk("Advertising successfully started\n"); +} + +/** + * Authentication status callback + * + * @param auth_conn The authentication connection. + * @param instance Instance ID. + * @param status Status + * @param context Optional context. + */ +static void auth_status(struct authenticate_conn *auth_conn, enum auth_instance_id instance, + enum auth_status status, void *context) +{ + /* display status */ + printk("Authentication instance (%d) status: %s\n", instance, + auth_lib_getstatus_str(status)); + +#if defined(CONFIG_AUTH_DTLS) + if(status == AUTH_STATUS_IN_PROCESS) { + printk(" DTLS may take 30-60 seconds.\n"); + } +#endif +} + + +/** + * Called when client notification is (dis)enabled by the Central + * + * @param attr GATT attribute. + * @param value BT_GATT_CCC_NOTIFY if changes are notified. + */ +static void client_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) +{ + ARG_UNUSED(attr); + + bool notif_enabled = (value == BT_GATT_CCC_NOTIFY) ? true : false; + + LOG_INF("Client notifications %s", notif_enabled ? "enabled" : "disabled"); +} + +/** + * Process log messages + */ +static void process_log_msgs(void) +{ + while(log_process(false)) { + ; /* intentionally empty statement */ + } +} + +/** + * Handle idle, never returns + */ +static void idle_function(void) +{ + while(true) { + + process_log_msgs(); + + /* give the handshake thread a chance to run */ + k_yield(); + } +} + +void main(void) +{ + int err = 0; + struct auth_optional_param *opt_parms = NULL; + + log_init(); + + uint32_t auth_flags = AUTH_CONN_SERVER; + +#if defined(CONFIG_AUTH_DTLS) + auth_flags |= AUTH_CONN_DTLS_AUTH_METHOD; + + /* Add certificates to authentication instance.*/ + opt_parms = &dtls_certs_param; + + printk("Using DTLS authentication method.\n"); +#endif + +#if defined(CONFIG_AUTH_CHALLENGE_RESPONSE) + auth_flags |= AUTH_CONN_CHALLENGE_AUTH_METHOD; + + /* Use different shared key */ + opt_parms = &chal_resp_param; + + printk("Using Challenge-Response authentication method.\n"); +#endif + + err = auth_lib_init(&auth_conn, AUTH_INST_1_ID, auth_status, NULL, + opt_parms, auth_flags); + + if(err){ + printk("Failed to init authentication service.\n"); + return; + } + + err = bt_enable(bt_ready); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + idle_function(); + } + + bt_conn_cb_register(&conn_callbacks); + bt_conn_auth_cb_register(&auth_cb_display); + + printk("Peripheral Auth started\n"); + + /* Never returns */ + idle_function(); + +} + diff --git a/samples/authentication/certs/README.rst b/samples/authentication/certs/README.rst new file mode 100644 index 000000000000..2b36e22221de --- /dev/null +++ b/samples/authentication/certs/README.rst @@ -0,0 +1,14 @@ +.. _auth_certs-sample: + +Test X.509 certs for DLTS Authentication examples +################################################# + +Overview +******** + +These test X.509 certificates are used with the DTLS authentication method. The private keys +for each certificate are located in the **keys** directory. + + + + diff --git a/samples/authentication/certs/auth_certs.h b/samples/authentication/certs/auth_certs.h new file mode 100644 index 000000000000..2744ea436124 --- /dev/null +++ b/samples/authentication/certs/auth_certs.h @@ -0,0 +1,100 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_INCLUDE_AUTH_CERTS_H_ +#define ZEPHYR_INCLUDE_AUTH_CERTS_H_ + + +#define AUTH_ROOTCA_CERT_PEM \ + "-----BEGIN CERTIFICATE-----\r\n" \ + "MIICeTCCAh+gAwIBAgIUPt+1qmulufx3ze5ZMsGpact9ekwwCgYIKoZIzj0EAwIw\r\n" \ + "gYkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlT\r\n" \ + "YW4gRGllZ28xGjAYBgNVBAoMEUF1dGggQ29tcGFueSBUZXN0MRwwGgYDVQQLDBNB\r\n" \ + "dXRoZW50aWNhdGlvbiBURVNUMRcwFQYDVQQDDA5BdXRoIFJvb3QgVEVTVDAeFw0y\r\n" \ + "MDA4MjYxOTM5MDJaFw0zNDA1MDUxOTM5MDJaMIGJMQswCQYDVQQGEwJVUzETMBEG\r\n" \ + "A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU2FuIERpZWdvMRowGAYDVQQKDBFB\r\n" \ + "dXRoIENvbXBhbnkgVGVzdDEcMBoGA1UECwwTQXV0aGVudGljYXRpb24gVEVTVDEX\r\n" \ + "MBUGA1UEAwwOQXV0aCBSb290IFRFU1QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC\r\n" \ + "AATCxBhg/MkpI+6PfUg6NUdk03pOd8x0Q8qcJngr4Gje+AzQLFoJHN0NE+PaTf2k\r\n" \ + "ILZX1hT2l39oKFXxhQedPB/uo2MwYTAdBgNVHQ4EFgQU4wEI2+yUj/DsbPmqLakg\r\n" \ + "jsuWUKkwHwYDVR0jBBgwFoAU4wEI2+yUj/DsbPmqLakgjsuWUKkwDwYDVR0TAQH/\r\n" \ + "BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDSAAwRQIgT8TvcsCl\r\n" \ + "adPrqWv50U87LON0QMsaTAqvVV6tFd06knICIQCxH+oRA9l0NSiyHJuPHRuKn6a9\r\n" \ + "xfOTq1zTpr6fNu9vuw==\r\n" \ + "-----END CERTIFICATE-----\r\n" + + +#define AUTH_INTERMEDIATE_CERT_PEM \ + "-----BEGIN CERTIFICATE-----\r\n" \ + "MIICXTCCAgOgAwIBAgICEAAwCgYIKoZIzj0EAwIwgYkxCzAJBgNVBAYTAlVTMRMw\r\n" \ + "EQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTYW4gRGllZ28xGjAYBgNVBAoM\r\n" \ + "EUF1dGggQ29tcGFueSBUZXN0MRwwGgYDVQQLDBNBdXRoZW50aWNhdGlvbiBURVNU\r\n" \ + "MRcwFQYDVQQDDA5BdXRoIFJvb3QgVEVTVDAeFw0yMDA4MjYyMDEyMjVaFw0zMTA4\r\n" \ + "MDkyMDEyMjVaMH0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRow\r\n" \ + "GAYDVQQKDBFBdXRoIENvbXBhbnkgVGVzdDEcMBoGA1UECwwTQXV0aGVudGljYXRp\r\n" \ + "b24gVEVTVDEfMB0GA1UEAwwWQXV0aCBJbnRlcm1lZGlhdGUgVEVTVDBZMBMGByqG\r\n" \ + "SM49AgEGCCqGSM49AwEHA0IABICgo5Ku11qZi4vFoTb2HmCShvvpsCM/0U3SDdqF\r\n" \ + "syBUT/cXdlYuqY+DdhM+GpQ0Qd4KNZEjwFWFEMXVH66gJgqjZjBkMB0GA1UdDgQW\r\n" \ + "BBQ2H3A6aIoNgXkAIVzaz+199JaFKjAfBgNVHSMEGDAWgBTjAQjb7JSP8Oxs+aot\r\n" \ + "qSCOy5ZQqTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAKBggq\r\n" \ + "hkjOPQQDAgNIADBFAiB3iPwLrBmMN6erAl+6w96pQJRH62R5s5T59nLP32d5QQIh\r\n" \ + "AMx3N8ijw8cE35Jg5szKy3hYgiN/6VTFEYADSdi737Si\r\n" \ + "-----END CERTIFICATE-----\r\n" + +#define AUTH_SERVER_CERT_PEM \ + "-----BEGIN CERTIFICATE-----\r\n" \ + "MIICNjCCAd2gAwIBAgICIAEwCgYIKoZIzj0EAwIwfTELMAkGA1UEBhMCVVMxEzAR\r\n" \ + "BgNVBAgMCkNhbGlmb3JuaWExGjAYBgNVBAoMEUF1dGggQ29tcGFueSBUZXN0MRww\r\n" \ + "GgYDVQQLDBNBdXRoZW50aWNhdGlvbiBURVNUMR8wHQYDVQQDDBZBdXRoIEludGVy\r\n" \ + "bWVkaWF0ZSBURVNUMB4XDTIwMDgyNjIwMzgzOFoXDTIxMDkwNTIwMzgzOFowgYsx\r\n" \ + "CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTYW4g\r\n" \ + "RGllZ28xGjAYBgNVBAoMEUF1dGggQ29tcGFueSBUZXN0MRwwGgYDVQQLDBNBdXRo\r\n" \ + "ZW50aWNhdGlvbiBURVNUMRkwFwYDVQQDDBBBdXRoIFNlcnZlciBURVNUMFkwEwYH\r\n" \ + "KoZIzj0CAQYIKoZIzj0DAQcDQgAEuHrjl6aMJTYsMqOP6cdzQ22pJwBl3Cy698Co\r\n" \ + "SpH6Ek5NMTJbSphGi2QdSCaQFDcq+T5H51j9Ch6m7+sblSDupqM+MDwwCQYDVR0T\r\n" \ + "BAIwADAfBgNVHSMEGDAWgBQ2H3A6aIoNgXkAIVzaz+199JaFKjAOBgNVHQ8BAf8E\r\n" \ + "BAMCB4AwCgYIKoZIzj0EAwIDRwAwRAIgAtXzzTzbEc/5yI4AU6+cFoOwaf3RK7/E\r\n" \ + "8kOsxGiemNECICLojqGP5O/tb5F/xfIYjuGvrkdAlY2ykb1btv9DoMUg\r\n" \ + "-----END CERTIFICATE-----\r\n" + + +#define AUTH_CLIENT_CERT_PEM \ + "-----BEGIN CERTIFICATE-----\r\n" \ + "MIICNzCCAd2gAwIBAgICIAAwCgYIKoZIzj0EAwIwfTELMAkGA1UEBhMCVVMxEzAR\r\n" \ + "BgNVBAgMCkNhbGlmb3JuaWExGjAYBgNVBAoMEUF1dGggQ29tcGFueSBUZXN0MRww\r\n" \ + "GgYDVQQLDBNBdXRoZW50aWNhdGlvbiBURVNUMR8wHQYDVQQDDBZBdXRoIEludGVy\r\n" \ + "bWVkaWF0ZSBURVNUMB4XDTIwMDgyNjIwMzgxMFoXDTIxMDkwNTIwMzgxMFowgYsx\r\n" \ + "CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTYW4g\r\n" \ + "RGllZ28xGjAYBgNVBAoMEUF1dGggQ29tcGFueSBUZXN0MRwwGgYDVQQLDBNBdXRo\r\n" \ + "ZW50aWNhdGlvbiBURVNUMRkwFwYDVQQDDBBBdXRoIENsaWVudCBURVNUMFkwEwYH\r\n" \ + "KoZIzj0CAQYIKoZIzj0DAQcDQgAEkwj+WKKVM3k9AEiS/efDhXoICq/e8rVKew2q\r\n" \ + "pun6zie8xATw7pyte1Hf59Ga2Vmti/QTvzczZrvj/m0LbKzmn6M+MDwwCQYDVR0T\r\n" \ + "BAIwADAfBgNVHSMEGDAWgBQ2H3A6aIoNgXkAIVzaz+199JaFKjAOBgNVHQ8BAf8E\r\n" \ + "BAMCB4AwCgYIKoZIzj0EAwIDSAAwRQIgfynezJlsshDJnyVVEC/twUHJAyhpIrEU\r\n" \ + "+qomFnsDB20CIQC9FxlDtKVrn4MOfI1yx9grPCN9ztwq1pRTLAh9LB/F9w==\r\n" \ + "-----END CERTIFICATE-----\r\n" + + +/** + * Sever and client private keys + */ + +#define AUTH_SERVER_PRIVATE_KEY_PEM \ + "-----BEGIN EC PRIVATE KEY-----\r\n" \ + "MHcCAQEEIP8LVrSz0CD/KvTTo5/qmOKIFtepGBTDG2odkSnYx/ssoAoGCCqGSM49\r\n" \ + "AwEHoUQDQgAEuHrjl6aMJTYsMqOP6cdzQ22pJwBl3Cy698CoSpH6Ek5NMTJbSphG\r\n" \ + "i2QdSCaQFDcq+T5H51j9Ch6m7+sblSDupg==\r\n" \ + "-----END EC PRIVATE KEY-----\r\n" + + +#define AUTH_CLIENT_PRIVATE_KEY_PEM \ + "-----BEGIN EC PRIVATE KEY-----\r\n" \ + "MHcCAQEEIJ87D6q+Z4ulXG2B9lQblbgCQh4xhMgImQpyKxUCtP3soAoGCCqGSM49\r\n" \ + "AwEHoUQDQgAEkwj+WKKVM3k9AEiS/efDhXoICq/e8rVKew2qpun6zie8xATw7pyt\r\n" \ + "e1Hf59Ga2Vmti/QTvzczZrvj/m0LbKzmnw==\r\n" \ + "-----END EC PRIVATE KEY-----\r\n" + +#endif // ZEPHYR_INCLUDE_AUTH_CERTS_H_ + + diff --git a/samples/authentication/certs/auth_dev_client.crt b/samples/authentication/certs/auth_dev_client.crt new file mode 100644 index 000000000000..3a6dabe7b5ef --- /dev/null +++ b/samples/authentication/certs/auth_dev_client.crt @@ -0,0 +1,48 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 8192 (0x2000) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: C=US, ST=California, O=Auth Company Test, OU=Authentication TEST, CN=Auth Intermediate TEST + Validity + Not Before: Aug 26 20:38:10 2020 GMT + Not After : Sep 5 20:38:10 2021 GMT + Subject: C=US, ST=California, L=San Diego, O=Auth Company Test, OU=Authentication TEST, CN=Auth Client TEST + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:93:08:fe:58:a2:95:33:79:3d:00:48:92:fd:e7: + c3:85:7a:08:0a:af:de:f2:b5:4a:7b:0d:aa:a6:e9: + fa:ce:27:bc:c4:04:f0:ee:9c:ad:7b:51:df:e7:d1: + 9a:d9:59:ad:8b:f4:13:bf:37:33:66:bb:e3:fe:6d: + 0b:6c:ac:e6:9f + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Authority Key Identifier: + keyid:36:1F:70:3A:68:8A:0D:81:79:00:21:5C:DA:CF:ED:7D:F4:96:85:2A + + X509v3 Key Usage: critical + Digital Signature + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:20:7f:29:de:cc:99:6c:b2:10:c9:9f:25:55:10:2f: + ed:c1:41:c9:03:28:69:22:b1:14:fa:aa:26:16:7b:03:07:6d: + 02:21:00:bd:17:19:43:b4:a5:6b:9f:83:0e:7c:8d:72:c7:d8: + 2b:3c:23:7d:ce:dc:2a:d6:94:53:2c:08:7d:2c:1f:c5:f7 +-----BEGIN CERTIFICATE----- +MIICNzCCAd2gAwIBAgICIAAwCgYIKoZIzj0EAwIwfTELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMCkNhbGlmb3JuaWExGjAYBgNVBAoMEUF1dGggQ29tcGFueSBUZXN0MRww +GgYDVQQLDBNBdXRoZW50aWNhdGlvbiBURVNUMR8wHQYDVQQDDBZBdXRoIEludGVy +bWVkaWF0ZSBURVNUMB4XDTIwMDgyNjIwMzgxMFoXDTIxMDkwNTIwMzgxMFowgYsx +CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTYW4g +RGllZ28xGjAYBgNVBAoMEUF1dGggQ29tcGFueSBUZXN0MRwwGgYDVQQLDBNBdXRo +ZW50aWNhdGlvbiBURVNUMRkwFwYDVQQDDBBBdXRoIENsaWVudCBURVNUMFkwEwYH +KoZIzj0CAQYIKoZIzj0DAQcDQgAEkwj+WKKVM3k9AEiS/efDhXoICq/e8rVKew2q +pun6zie8xATw7pyte1Hf59Ga2Vmti/QTvzczZrvj/m0LbKzmn6M+MDwwCQYDVR0T +BAIwADAfBgNVHSMEGDAWgBQ2H3A6aIoNgXkAIVzaz+199JaFKjAOBgNVHQ8BAf8E +BAMCB4AwCgYIKoZIzj0EAwIDSAAwRQIgfynezJlsshDJnyVVEC/twUHJAyhpIrEU ++qomFnsDB20CIQC9FxlDtKVrn4MOfI1yx9grPCN9ztwq1pRTLAh9LB/F9w== +-----END CERTIFICATE----- diff --git a/samples/authentication/certs/auth_dev_server.crt b/samples/authentication/certs/auth_dev_server.crt new file mode 100644 index 000000000000..9e1126a7fa32 --- /dev/null +++ b/samples/authentication/certs/auth_dev_server.crt @@ -0,0 +1,48 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 8193 (0x2001) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: C=US, ST=California, O=Auth Company Test, OU=Authentication TEST, CN=Auth Intermediate TEST + Validity + Not Before: Aug 26 20:38:38 2020 GMT + Not After : Sep 5 20:38:38 2021 GMT + Subject: C=US, ST=California, L=San Diego, O=Auth Company Test, OU=Authentication TEST, CN=Auth Server TEST + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:b8:7a:e3:97:a6:8c:25:36:2c:32:a3:8f:e9:c7: + 73:43:6d:a9:27:00:65:dc:2c:ba:f7:c0:a8:4a:91: + fa:12:4e:4d:31:32:5b:4a:98:46:8b:64:1d:48:26: + 90:14:37:2a:f9:3e:47:e7:58:fd:0a:1e:a6:ef:eb: + 1b:95:20:ee:a6 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Basic Constraints: + CA:FALSE + X509v3 Authority Key Identifier: + keyid:36:1F:70:3A:68:8A:0D:81:79:00:21:5C:DA:CF:ED:7D:F4:96:85:2A + + X509v3 Key Usage: critical + Digital Signature + Signature Algorithm: ecdsa-with-SHA256 + 30:44:02:20:02:d5:f3:cd:3c:db:11:cf:f9:c8:8e:00:53:af: + 9c:16:83:b0:69:fd:d1:2b:bf:c4:f2:43:ac:c4:68:9e:98:d1: + 02:20:22:e8:8e:a1:8f:e4:ef:ed:6f:91:7f:c5:f2:18:8e:e1: + af:ae:47:40:95:8d:b2:91:bd:5b:b6:ff:43:a0:c5:20 +-----BEGIN CERTIFICATE----- +MIICNjCCAd2gAwIBAgICIAEwCgYIKoZIzj0EAwIwfTELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMCkNhbGlmb3JuaWExGjAYBgNVBAoMEUF1dGggQ29tcGFueSBUZXN0MRww +GgYDVQQLDBNBdXRoZW50aWNhdGlvbiBURVNUMR8wHQYDVQQDDBZBdXRoIEludGVy +bWVkaWF0ZSBURVNUMB4XDTIwMDgyNjIwMzgzOFoXDTIxMDkwNTIwMzgzOFowgYsx +CzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTYW4g +RGllZ28xGjAYBgNVBAoMEUF1dGggQ29tcGFueSBUZXN0MRwwGgYDVQQLDBNBdXRo +ZW50aWNhdGlvbiBURVNUMRkwFwYDVQQDDBBBdXRoIFNlcnZlciBURVNUMFkwEwYH +KoZIzj0CAQYIKoZIzj0DAQcDQgAEuHrjl6aMJTYsMqOP6cdzQ22pJwBl3Cy698Co +SpH6Ek5NMTJbSphGi2QdSCaQFDcq+T5H51j9Ch6m7+sblSDupqM+MDwwCQYDVR0T +BAIwADAfBgNVHSMEGDAWgBQ2H3A6aIoNgXkAIVzaz+199JaFKjAOBgNVHQ8BAf8E +BAMCB4AwCgYIKoZIzj0EAwIDRwAwRAIgAtXzzTzbEc/5yI4AU6+cFoOwaf3RK7/E +8kOsxGiemNECICLojqGP5O/tb5F/xfIYjuGvrkdAlY2ykb1btv9DoMUg +-----END CERTIFICATE----- diff --git a/samples/authentication/certs/auth_intermediate.crt b/samples/authentication/certs/auth_intermediate.crt new file mode 100644 index 000000000000..db29f1f7e9d5 --- /dev/null +++ b/samples/authentication/certs/auth_intermediate.crt @@ -0,0 +1,51 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 4096 (0x1000) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: C=US, ST=California, L=San Diego, O=Auth Company Test, OU=Authentication TEST, CN=Auth Root TEST + Validity + Not Before: Aug 26 20:12:25 2020 GMT + Not After : Aug 9 20:12:25 2031 GMT + Subject: C=US, ST=California, O=Auth Company Test, OU=Authentication TEST, CN=Auth Intermediate TEST + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:80:a0:a3:92:ae:d7:5a:99:8b:8b:c5:a1:36:f6: + 1e:60:92:86:fb:e9:b0:23:3f:d1:4d:d2:0d:da:85: + b3:20:54:4f:f7:17:76:56:2e:a9:8f:83:76:13:3e: + 1a:94:34:41:de:0a:35:91:23:c0:55:85:10:c5:d5: + 1f:ae:a0:26:0a + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Key Identifier: + 36:1F:70:3A:68:8A:0D:81:79:00:21:5C:DA:CF:ED:7D:F4:96:85:2A + X509v3 Authority Key Identifier: + keyid:E3:01:08:DB:EC:94:8F:F0:EC:6C:F9:AA:2D:A9:20:8E:CB:96:50:A9 + + X509v3 Basic Constraints: critical + CA:TRUE, pathlen:0 + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + Signature Algorithm: ecdsa-with-SHA256 + 30:45:02:20:77:88:fc:0b:ac:19:8c:37:a7:ab:02:5f:ba:c3: + de:a9:40:94:47:eb:64:79:b3:94:f9:f6:72:cf:df:67:79:41: + 02:21:00:cc:77:37:c8:a3:c3:c7:04:df:92:60:e6:cc:ca:cb: + 78:58:82:23:7f:e9:54:c5:11:80:03:49:d8:bb:df:b4:a2 +-----BEGIN CERTIFICATE----- +MIICXTCCAgOgAwIBAgICEAAwCgYIKoZIzj0EAwIwgYkxCzAJBgNVBAYTAlVTMRMw +EQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlTYW4gRGllZ28xGjAYBgNVBAoM +EUF1dGggQ29tcGFueSBUZXN0MRwwGgYDVQQLDBNBdXRoZW50aWNhdGlvbiBURVNU +MRcwFQYDVQQDDA5BdXRoIFJvb3QgVEVTVDAeFw0yMDA4MjYyMDEyMjVaFw0zMTA4 +MDkyMDEyMjVaMH0xCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRow +GAYDVQQKDBFBdXRoIENvbXBhbnkgVGVzdDEcMBoGA1UECwwTQXV0aGVudGljYXRp +b24gVEVTVDEfMB0GA1UEAwwWQXV0aCBJbnRlcm1lZGlhdGUgVEVTVDBZMBMGByqG +SM49AgEGCCqGSM49AwEHA0IABICgo5Ku11qZi4vFoTb2HmCShvvpsCM/0U3SDdqF +syBUT/cXdlYuqY+DdhM+GpQ0Qd4KNZEjwFWFEMXVH66gJgqjZjBkMB0GA1UdDgQW +BBQ2H3A6aIoNgXkAIVzaz+199JaFKjAfBgNVHSMEGDAWgBTjAQjb7JSP8Oxs+aot +qSCOy5ZQqTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjAKBggq +hkjOPQQDAgNIADBFAiB3iPwLrBmMN6erAl+6w96pQJRH62R5s5T59nLP32d5QQIh +AMx3N8ijw8cE35Jg5szKy3hYgiN/6VTFEYADSdi737Si +-----END CERTIFICATE----- diff --git a/samples/authentication/certs/auth_rootca.crt b/samples/authentication/certs/auth_rootca.crt new file mode 100644 index 000000000000..2b0e752170d7 --- /dev/null +++ b/samples/authentication/certs/auth_rootca.crt @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICeTCCAh+gAwIBAgIUPt+1qmulufx3ze5ZMsGpact9ekwwCgYIKoZIzj0EAwIw +gYkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRIwEAYDVQQHDAlT +YW4gRGllZ28xGjAYBgNVBAoMEUF1dGggQ29tcGFueSBUZXN0MRwwGgYDVQQLDBNB +dXRoZW50aWNhdGlvbiBURVNUMRcwFQYDVQQDDA5BdXRoIFJvb3QgVEVTVDAeFw0y +MDA4MjYxOTM5MDJaFw0zNDA1MDUxOTM5MDJaMIGJMQswCQYDVQQGEwJVUzETMBEG +A1UECAwKQ2FsaWZvcm5pYTESMBAGA1UEBwwJU2FuIERpZWdvMRowGAYDVQQKDBFB +dXRoIENvbXBhbnkgVGVzdDEcMBoGA1UECwwTQXV0aGVudGljYXRpb24gVEVTVDEX +MBUGA1UEAwwOQXV0aCBSb290IFRFU1QwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC +AATCxBhg/MkpI+6PfUg6NUdk03pOd8x0Q8qcJngr4Gje+AzQLFoJHN0NE+PaTf2k +ILZX1hT2l39oKFXxhQedPB/uo2MwYTAdBgNVHQ4EFgQU4wEI2+yUj/DsbPmqLakg +jsuWUKkwHwYDVR0jBBgwFoAU4wEI2+yUj/DsbPmqLakgjsuWUKkwDwYDVR0TAQH/ +BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwCgYIKoZIzj0EAwIDSAAwRQIgT8TvcsCl +adPrqWv50U87LON0QMsaTAqvVV6tFd06knICIQCxH+oRA9l0NSiyHJuPHRuKn6a9 +xfOTq1zTpr6fNu9vuw== +-----END CERTIFICATE----- diff --git a/samples/authentication/certs/keys/auth_dev_client.key b/samples/authentication/certs/keys/auth_dev_client.key new file mode 100644 index 000000000000..2e6b11b1fcd1 --- /dev/null +++ b/samples/authentication/certs/keys/auth_dev_client.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIJ87D6q+Z4ulXG2B9lQblbgCQh4xhMgImQpyKxUCtP3soAoGCCqGSM49 +AwEHoUQDQgAEkwj+WKKVM3k9AEiS/efDhXoICq/e8rVKew2qpun6zie8xATw7pyt +e1Hf59Ga2Vmti/QTvzczZrvj/m0LbKzmnw== +-----END EC PRIVATE KEY----- diff --git a/samples/authentication/certs/keys/auth_dev_server.key b/samples/authentication/certs/keys/auth_dev_server.key new file mode 100644 index 000000000000..06968e59a681 --- /dev/null +++ b/samples/authentication/certs/keys/auth_dev_server.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIP8LVrSz0CD/KvTTo5/qmOKIFtepGBTDG2odkSnYx/ssoAoGCCqGSM49 +AwEHoUQDQgAEuHrjl6aMJTYsMqOP6cdzQ22pJwBl3Cy698CoSpH6Ek5NMTJbSphG +i2QdSCaQFDcq+T5H51j9Ch6m7+sblSDupg== +-----END EC PRIVATE KEY----- diff --git a/samples/authentication/certs/keys/auth_inter.key b/samples/authentication/certs/keys/auth_inter.key new file mode 100644 index 000000000000..a2d10a601156 --- /dev/null +++ b/samples/authentication/certs/keys/auth_inter.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIDV34Cl2CsPrR9VhASoOPRU1ROQ4tUN+z9LuuxkhcgIroAoGCCqGSM49 +AwEHoUQDQgAEgKCjkq7XWpmLi8WhNvYeYJKG++mwIz/RTdIN2oWzIFRP9xd2Vi6p +j4N2Ez4alDRB3go1kSPAVYUQxdUfrqAmCg== +-----END EC PRIVATE KEY----- diff --git a/samples/authentication/certs/keys/auth_rootca.key b/samples/authentication/certs/keys/auth_rootca.key new file mode 100644 index 000000000000..f811aba022fb --- /dev/null +++ b/samples/authentication/certs/keys/auth_rootca.key @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIEbm67ja033uNzPco68U4+UtBjJBoNJMsGJcb6zbi0Z4oAoGCCqGSM49 +AwEHoUQDQgAEwsQYYPzJKSPuj31IOjVHZNN6TnfMdEPKnCZ4K+Bo3vgM0CxaCRzd +DRPj2k39pCC2V9YU9pd/aChV8YUHnTwf7g== +-----END EC PRIVATE KEY----- diff --git a/samples/authentication/multi_instance/README.rst b/samples/authentication/multi_instance/README.rst new file mode 100644 index 000000000000..58f6277fba50 --- /dev/null +++ b/samples/authentication/multi_instance/README.rst @@ -0,0 +1,37 @@ +.. _auth_multi-sample: + +Multiple Authentication Instances +################################# + +Overview +******** + +This sample shows how to use two authentication instances using the serial and Bluetooth transports. In this sample, +DTLS authentication is instance 1 and uses the Bluetooth transport, the Challenge-Response authentication is instance +2 and uses the Serial transport. The choice of instance and authentication methods are just for sample purposes, it is +possible to use any combination of authentication method and transport. For example, two instances of Challenge-Response +using the Serial transport. + +Here is a real world example of multiple authentication instances. A medical device with disposable heart monitor pads and a +Bluetooth connection. The monitor pads are connected via serial link and are authenticated when plugged into the device. +When the pads are replaced, the new pads are authenticated. The mobile app is authenticated over the Bluetooth link. + +.. image:: heart_monitor_device.png + +One authentication instance handles the monitor pads, the second instance handles the mobile app connection. + + +Building and Running +-------------------- +This sample was developed and tested with two Nordic nRF52840 dev +kits (see: https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK). Two Ubuntu +VMs were used, one running the Central the other VM running the Peripheral. + +To build:|br| +cmake -Bbuild_auth_client -DBOARD=nrf52840dk_nrf52840 samples/authentication/multi_instance/auth_client + +To build using West:|br| +west build -d build_auth_client -b nrf52840dk_nrf52840 samples/authentication/multi_instance/auth_client + + +Replace auth_client with auth_server to build the server. \ No newline at end of file diff --git a/samples/authentication/multi_instance/auth_client/CMakeLists.txt b/samples/authentication/multi_instance/auth_client/CMakeLists.txt new file mode 100644 index 000000000000..5d88823fd65f --- /dev/null +++ b/samples/authentication/multi_instance/auth_client/CMakeLists.txt @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(periph_auth) + +target_include_directories(app PUBLIC ${CMAKE_SOURCE_DIR}) + + +target_sources(app PRIVATE + src/main.c +) diff --git a/samples/authentication/multi_instance/auth_client/prj.conf b/samples/authentication/multi_instance/auth_client/prj.conf new file mode 100644 index 000000000000..fd4eba505eaf --- /dev/null +++ b/samples/authentication/multi_instance/auth_client/prj.conf @@ -0,0 +1,95 @@ +CONFIG_BT=y +CONFIG_BT_DEBUG_LOG=y +CONFIG_BT_SMP=y +CONFIG_BT_SIGNING=y +CONFIG_BT_PERIPHERAL=n +CONFIG_BT_DIS=y +CONFIG_BT_ATT_PREPARE_COUNT=5 +CONFIG_BT_PRIVACY=y +CONFIG_BT_DEVICE_NAME="Zephyr Central Auth" +CONFIG_BT_DEVICE_APPEARANCE=833 +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=65 +CONFIG_BT_RX_BUF_LEN=1600 +CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_MAX_PAIRED=2 +CONFIG_BT_MAX_CONN=2 + + +# Increase stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +CONFIG_MAIN_STACK_SIZE=4096 + +# logging +CONFIG_LOG=y +CONFIG_USE_SEGGER_RTT=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_LOG_PRINTK=y + + +CONFIG_AUTH_LIB=y +# Enable Bluetooth and serial transports +CONFIG_SERIAL_XPORT=y +CONFIG_BT_XPORT=y + +# Using both DTLS and Challenge-Response authentication methods +# in different instances. +CONFIG_AUTH_DTLS=y +CONFIG_AUTH_CHALLENGE_RESPONSE=y + +# two authentication instance +CONFIG_NUM_AUTH_INSTANCES=2 + +CONFIG_TINYCRYPT=y +CONFIG_TINYCRYPT_SHA256=y +CONFIG_LOG=y +CONFIG_AUTH_LOG_LEVEL=0 +CONFIG_AUTH_CHALLENGE_RESPONSE=y + +# Config vars for Challenge-Response +CONFIG_TINYCRYPT=y +CONFIG_TINYCRYPT_SHA256=y +CONFIG_UART_INTERRUPT_DRIVEN=y + + +# Mbed config vars +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" +CONFIG_MBEDTLS_TLS_VERSION_1_2=y +CONFIG_MBEDTLS_DTLS=y +CONFIG_MBEDTLS_ENTROPY_ENABLED=y + +# Supported key exchange modes +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED=y + +# Supported elliptic curves +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y + +# Supported cipher modes +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y +CONFIG_MBEDTLS_CIPHER_GCM_ENABLED=y +CONFIG_MBEDTLS_CIPHER_MODE_CBC_ENABLED=y + + +# Supported message authentication methods +CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y +CONFIG_MBEDTLS_MAC_CMAC_ENABLED=y + + +# Other configurations +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=1500 +CONFIG_MBEDTLS_DEBUG=y +CONFIG_MBEDTLS_DEBUG_LEVEL=0 +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=65535 +CONFIG_APP_LINK_WITH_MBEDTLS=y + + + + + diff --git a/samples/authentication/multi_instance/auth_client/sample.yaml b/samples/authentication/multi_instance/auth_client/sample.yaml new file mode 100644 index 000000000000..eee7e986afb1 --- /dev/null +++ b/samples/authentication/multi_instance/auth_client/sample.yaml @@ -0,0 +1,8 @@ +sample: + description: Auth Multi Instance Client sample app + name: Auth Client Multi Instance +tests: + sample.auth_client_multi: + harness: bluetooth + platform_allow: qemu_x86 + tags: bluetooth authentication diff --git a/samples/authentication/multi_instance/auth_client/src/main.c b/samples/authentication/multi_instance/auth_client/src/main.c new file mode 100644 index 000000000000..63aa78c6ec86 --- /dev/null +++ b/samples/authentication/multi_instance/auth_client/src/main.c @@ -0,0 +1,645 @@ + +/* main.c - Application main entry point + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + + +#include "../../../certs/auth_certs.h" + + +LOG_MODULE_REGISTER(central_auth, CONFIG_AUTH_LOG_LEVEL); + + +/** + * Declare UUIDs used for the Authentication service and characteristics + * see auth_xport.h + */ + +static struct bt_uuid_128 auth_service_uuid = AUTH_SERVICE_UUID; + +static struct bt_uuid_128 auth_client_char = AUTH_SVC_CLIENT_CHAR_UUID; + +static struct bt_uuid_128 auth_server_char = AUTH_SVC_SERVER_CHAR; + + +#define AUTH_DTLS_INST AUTH_INST_1_ID +#define AUTH_CHALLENGE_INST AUTH_INST_2_ID + +/* The Root and Intermediate Certs in a single CA chain. + * plus the server cert. All in PEM format.*/ +static const uint8_t auth_cert_ca_chain[] = AUTH_ROOTCA_CERT_PEM AUTH_INTERMEDIATE_CERT_PEM; +static const uint8_t auth_dev_client_cert[] = AUTH_CLIENT_CERT_PEM; +static const uint8_t auth_client_privatekey[] = AUTH_CLIENT_PRIVATE_KEY_PEM; + +static struct auth_optional_param dtls_certs_param = { + .param_id = AUTH_DTLS_PARAM, + .param_body = { + .dtls_certs = { + .server_ca_chain_pem = { + .cert = auth_cert_ca_chain, + .cert_size = sizeof(auth_cert_ca_chain), + }, + + .device_cert_pem = { + .cert = auth_dev_client_cert, + .cert_size = sizeof(auth_dev_client_cert), + .priv_key = auth_client_privatekey, + .priv_key_size = sizeof(auth_client_privatekey) + } + } + } +}; + + +#define NEW_SHARED_KEY_LEN (32u) + +/* Use a different key than default */ +static uint8_t chal_resp_sharedkey[NEW_SHARED_KEY_LEN] = { + 0x21, 0x8e, 0x37, 0x42, 0x1e, 0xe1, 0x2a, 0x22, 0x7c, 0x4b, 0x3f, 0x3f, 0x07, 0x5e, 0x8a, 0xd8, + 0x24, 0xdf, 0xca, 0xf4, 0x04, 0xd0, 0x3e, 0x22, 0x61, 0x9f, 0x24, 0xa3, 0xc7, 0xf6, 0x5d, 0x66 +}; + +static struct auth_optional_param chal_resp_param = { + .param_id = AUTH_CHALRESP_PARAM, + .param_body = { + .chal_resp = { + .shared_key = chal_resp_sharedkey, + }, + } +}; + + +static struct bt_conn *default_conn; +static struct bt_gatt_discover_params discover_params; +static struct bt_gatt_subscribe_params subscribe_params; + + + +/** + * Authentication connect struct + */ +static struct authenticate_conn auth_conn_bt; +static struct authenticate_conn auth_conn_serial; + + + +/* Auth service, client descriptor, server descriptor */ +#define AUTH_SVC_GATT_COUNT (4u) + +#define AUTH_SVC_INDEX (0u) +#define AUTH_SVC_CLIENT_CHAR_INDEX (1u) +#define AUTH_SVC_CLIENT_CCC_INDEX (2u) +#define AUTH_SVC_SERVER_CHAR_INDEX (3u) + +/** + * Used to store Authentication GATT service and characteristics. + */ +typedef struct { + const struct bt_uuid *uuid; + const struct bt_gatt_attr *attr; + uint16_t handle; + uint16_t value_handle; + uint8_t permissions; /* Bitfields from: BT_GATT_PERM_NONE, BT_GATT_PERM_READ, .. in gatt.h */ + const uint32_t gatt_disc_type; +} auth_svc_gatt_t; + + +static uint32_t auth_desc_index; + +/* Table content should match indexes above */ +static auth_svc_gatt_t auth_svc_gatt_tbl[AUTH_SVC_GATT_COUNT] = { + { (const struct bt_uuid *)&auth_service_uuid, NULL, 0, 0, BT_GATT_PERM_NONE, BT_GATT_DISCOVER_PRIMARY }, /* AUTH_SVC_INDEX */ + { (const struct bt_uuid *)&auth_client_char, NULL, 0, 0, BT_GATT_PERM_NONE, BT_GATT_DISCOVER_CHARACTERISTIC }, /* AUTH_SVC_CLIENT_CHAR_INDEX */ + { BT_UUID_GATT_CCC, NULL, 0, 0, BT_GATT_PERM_NONE, BT_GATT_DISCOVER_DESCRIPTOR }, /* AUTH_SVC_CLIENT_CCC_INDEX CCC for Client char */ + { (const struct bt_uuid *)&auth_server_char, NULL, 0, 0, BT_GATT_PERM_NONE, BT_GATT_DISCOVER_CHARACTERISTIC } /* AUTH_SVC_SERVER_CHAR_INDEX */ +}; + +static const struct device *uart_dev; + +static struct uart_config uart_cfg = { + .baudrate = 115200, + .parity = UART_CFG_PARITY_NONE, + .stop_bits = UART_CFG_STOP_BITS_1, + .data_bits = UART_CFG_DATA_BITS_8, + .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, +}; + + + +/** + * Params used to change the connection MTU length. + */ +struct bt_gatt_exchange_params mtu_parms; + +void mtu_change_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params) +{ + if (err) { + LOG_ERR("Failed to set MTU, err: %d", err); + } else { + LOG_DBG("Successfully set MTU to: %d", bt_gatt_get_mtu(conn)); + } +} + +/** + * Start DTLS and Challenge-Response auth methods. + * + * @return 0 on success, else negative error value + */ +static int start_authentication(void) +{ + int err = auth_lib_start(&auth_conn_bt); + + if (err) { + LOG_ERR("Failed to start DTLS auth method, err: %d", err); + return err; + } + + err = auth_lib_start(&auth_conn_serial); + + if (err) { + LOG_ERR("Failed to start Challenge-Response auth method, err: %d", err); + return err; + } + + return 0; +} + +/** + * Characteristic discovery function + * + * + * @param conn Bluetooth connection. + * @param attr Discovered attribute. NOTE: This pointer will go out fo scope + * do not save pointer for future use. + * @param params Discover params. + * + * @return BT_GATT_ITER_STOP + */ +static uint8_t discover_func(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) +{ + int err; + + if (!attr) { + LOG_INF("Discover complete, NULL attribute."); + (void)memset(params, 0, sizeof(*params)); + return BT_GATT_ITER_STOP; + } + + + /* debug output */ + LOG_DBG("====auth_desc_index is: %d=====", auth_desc_index); + LOG_DBG("[ATTRIBUTE] handle 0x%x", attr->handle); + LOG_DBG("[ATTRIBUTE] value handle 0x%x", bt_gatt_attr_value_handle(attr)); + + /* let's get string */ + char uuid_str[50]; + bt_uuid_to_str(attr->uuid, uuid_str, sizeof(uuid_str)); + LOG_DBG("Attribute UUID: %s", log_strdup(uuid_str)); + + /* print attribute UUID */ + bt_uuid_to_str(discover_params.uuid, uuid_str, sizeof(uuid_str)); + LOG_DBG("Discovery UUID: %s", log_strdup(uuid_str)); + + + /** + * Verify the correct UUID was found + */ + if (bt_uuid_cmp(discover_params.uuid, auth_svc_gatt_tbl[auth_desc_index].uuid)) { + + /* Failed, not the UUID we're expecting */ + LOG_ERR("Error Unknown UUID."); + return BT_GATT_ITER_STOP; + } + + /* save off GATT info */ + auth_svc_gatt_tbl[auth_desc_index].attr = NULL; /* NOTE: attr var not used for the Central */ + auth_svc_gatt_tbl[auth_desc_index].handle = attr->handle; + auth_svc_gatt_tbl[auth_desc_index].value_handle = bt_gatt_attr_value_handle(attr); + auth_svc_gatt_tbl[auth_desc_index].permissions = attr->perm; + + auth_desc_index++; + + /* Are all of the characteristics discovered? */ + if (auth_desc_index >= AUTH_SVC_GATT_COUNT) { + + /* we're done */ + LOG_INF("Discover complete"); + + /* save off the server attribute handle */ + + /* setup the subscribe params + * Value handle for the Client characteristic for indication of + * peripheral data. + */ + subscribe_params.notify = auth_xp_bt_central_notify; + subscribe_params.value = BT_GATT_CCC_NOTIFY; + subscribe_params.value_handle = + auth_svc_gatt_tbl[AUTH_SVC_CLIENT_CHAR_INDEX].value_handle; + + /* Handle for the CCC descriptor itself */ + subscribe_params.ccc_handle = + auth_svc_gatt_tbl[AUTH_SVC_CLIENT_CCC_INDEX].handle; + + err = bt_gatt_subscribe(conn, &subscribe_params); + if (err && err != -EALREADY) { + LOG_ERR("Subscribe failed (err %d)", err); + } + + /* Get the server BT characteristic, the central sends data to this characteristic */ + uint16_t server_char_handle = + auth_svc_gatt_tbl[AUTH_SVC_SERVER_CHAR_INDEX].value_handle; + + /* setup the BT transport params */ + struct auth_xp_bt_params xport_params = + { .conn = conn, .is_central = true, + .server_char_hdl = server_char_handle }; + + err = auth_xport_init(&auth_conn_bt.xport_hdl, + auth_conn_bt.instance, + AUTH_XP_TYPE_BLUETOOTH, &xport_params); + + if (err) { + LOG_ERR("Failed to initialize Bluetooth transport, err: %d", err); + return BT_GATT_ITER_STOP; + } + + /* Start DTLS and Challenge-Response auth process */ + err = start_authentication(); + + if (err == 0) { + LOG_INF("Started auth services."); + } + + return BT_GATT_ITER_STOP; + } + + /* set up the next discovery params */ + discover_params.uuid = auth_svc_gatt_tbl[auth_desc_index].uuid, + discover_params.start_handle = attr->handle + 1; + discover_params.type = auth_svc_gatt_tbl[auth_desc_index].gatt_disc_type; + + + /* Start discovery */ + err = bt_gatt_discover(conn, &discover_params); + if (err) { + LOG_ERR("Discover failed (err %d)", err); + } + + + return BT_GATT_ITER_STOP; +} + +/** + * Connected to the peripheral device + * + * @param conn The Bluetooth connection. + * @param conn_err Connection error, 0 == no error + */ +static void connected(struct bt_conn *conn, uint8_t conn_err) +{ + char addr[BT_ADDR_LE_STR_LEN]; + int err; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + if (conn_err) { + LOG_ERR("Failed to connect to %s (%u)", log_strdup(addr), conn_err); + return; + } + + LOG_INF("Connected: %s", log_strdup(addr)); + + if (conn == default_conn) { + + /* set the max MTU, only for GATT interface */ + mtu_parms.func = mtu_change_cb; + bt_gatt_exchange_mtu(conn, &mtu_parms); + + /* reset gatt discovery index */ + auth_desc_index = 0; + + discover_params.uuid = auth_svc_gatt_tbl[auth_desc_index].uuid; + discover_params.func = discover_func; + discover_params.start_handle = 0x0001; + discover_params.end_handle = 0xffff; + discover_params.type = auth_svc_gatt_tbl[auth_desc_index].gatt_disc_type; + + /** + * Discover characteristics for the service + */ + err = bt_gatt_discover(default_conn, &discover_params); + if (err) { + LOG_ERR("Discover failed(err %d)", err); + return; + } + } +} + +/** + * Parse through the BLE adv data, looking for our service + * + * @param data Bluetooth data + * @param user_data User data. + * + * @return true on success, else false + */ +static bool bt_adv_data_found(struct bt_data *data, void *user_data) +{ + bt_addr_le_t *addr = user_data; + int err; + struct bt_uuid_128 auth_uuid; + + LOG_DBG("[AD]: %u data_len %u", data->type, data->data_len); + + if (data->type == BT_DATA_UUID128_ALL) { + + if (data->data_len != BT_UUID_SIZE_128) { + LOG_WRN("AD malformed"); + return false; + } + + /* build full UUID to check */ + auth_uuid.uuid.type = BT_UUID_TYPE_128; + memcpy(auth_uuid.val, data->data, BT_UUID_SIZE_128); + + + /** + * Is this the service we're looking for? If not continue + * else stop the scan and connect to the device. + */ + if (bt_uuid_cmp((const struct bt_uuid *)&auth_uuid, + (const struct bt_uuid *)&auth_service_uuid)) { + return true; + } + + /* stop scanning, we've found the service */ + err = bt_le_scan_stop(); + if (err) { + LOG_ERR("Stop LE scan failed (err %d)", err); + return false; + } + + /** + * @brief Connect to the device + */ + struct bt_conn_le_create_param param = BT_CONN_LE_CREATE_PARAM_INIT( + BT_CONN_LE_OPT_NONE, + BT_GAP_SCAN_FAST_INTERVAL, + BT_GAP_SCAN_FAST_INTERVAL); + + if (bt_conn_le_create(addr, ¶m, BT_LE_CONN_PARAM_DEFAULT, + &default_conn)) { + LOG_ERR("Failed to create BLE connection to peripheral."); + } + + return false; + } + + return true; +} + +/** + * Found a device when scanning. + * + * @param addr BT address + * @param rssi Signal strength + * @param type Device type + * @param ad Simple buffer. + */ +static void device_found(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, + struct net_buf_simple *ad) +{ + char dev[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(addr, dev, sizeof(dev)); + LOG_DBG("[DEVICE]: %s, AD evt type %u, AD data len %u, RSSI %i", + log_strdup(dev), type, ad->len, rssi); + + /* We're only interested in connectable events */ + if (type == BT_HCI_ADV_IND || type == BT_HCI_ADV_DIRECT_IND) { + bt_data_parse(ad, bt_adv_data_found, (void *)addr); + } +} + +/** + * BT disconnect callback. + * + * @param conn The Bluetooth connection. + * @param reason Disconnect reason. + */ +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + LOG_INF("Disconnected: %s (reason 0x%02x)", log_strdup(addr), reason); + + if (default_conn != conn) { + return; + } + + /* de init transport */ + /* Send disconnect event to BT transport. */ + struct auth_xport_evt conn_evt = { .event = XP_EVT_DISCONNECT }; + auth_xport_event(auth_conn_bt.xport_hdl, &conn_evt); + + /* Deinit lower transport */ + auth_xport_deinit(auth_conn_bt.xport_hdl); + auth_conn_bt.xport_hdl = NULL; + + bt_conn_unref(default_conn); + default_conn = NULL; +} + +/** + * (dis)Connection callbacks + */ +static struct bt_conn_cb conn_callbacks = { + .connected = connected, + .disconnected = disconnected, +}; + +/** + * Configures the UART + * + * @return 0 on success, else negative error code. + */ +static int config_uart(void) +{ + struct auth_xp_serial_params xp_params; + + uart_dev = device_get_binding(DT_LABEL(DT_NODELABEL(uart0))); + + int err = uart_configure(uart_dev, &uart_cfg); + + if (err) { + LOG_ERR("Failed to configure UART, err: %d", err); + return err; + } + + /* If successful,then init lower transport layer. */ + xp_params.uart_dev = uart_dev; + + err = auth_xport_init(&auth_conn_serial.xport_hdl, auth_conn_serial.instance, + AUTH_XP_TYPE_SERIAL, &xp_params); + + if (err) { + LOG_ERR("Failed to initialize authentication transport, error: %d", err); + } + + return err; +} + + +/** + * Authentication status callback. + * + * @param auth_conn Authentication connection. + * @param instance Instance ID. + * @param status Auth status. + * @param context Optional context. + */ +static void auth_status(struct authenticate_conn *auth_conn, + enum auth_instance_id instance, + enum auth_status status, void *context) +{ + /* display status */ + if (instance == AUTH_DTLS_INST) { + printk("Auth instance DTLS (%d) status: %s\n", instance, + auth_lib_getstatus_str(status)); + + if (status == AUTH_STATUS_IN_PROCESS) { + printk(" DTLS may take 30-60 seconds.\n"); + } + } + + if (instance == AUTH_CHALLENGE_INST) { + printk("Auth instance Challenge-Response (%d) status: %s\n", + instance, auth_lib_getstatus_str(status)); + } + +} + +/** + * Process log messages. + */ +static void process_log_msgs(void) +{ + while (log_process(false)) { + ; /* intentionally empty statement */ + } +} + +/** + * Idle process for app. + */ +static void idle_function(void) +{ + /* Just spin while the BT modules handle the connection. */ + while (true) { + + process_log_msgs(); + + /* Let the handshake thread run */ + k_yield(); + } +} + + +/** + * main entry point. + */ +void main(void) +{ + int err = 0; + + log_init(); + + + printk("Starting multi auth client.\n"); + + /* Instance 1 uses DTLS auth method over Bluetooth. */ + err = auth_lib_init(&auth_conn_bt, AUTH_DTLS_INST, auth_status, NULL, + &dtls_certs_param, + AUTH_CONN_CLIENT | AUTH_CONN_DTLS_AUTH_METHOD); + + if (err) { + LOG_ERR("Failed to init DTLS authentication service, err: %d.", err); + idle_function(); /* does not return */ + } + + /* Instance 2 uses Challenge-Response auth method over Serial transport. */ + err = auth_lib_init(&auth_conn_serial, AUTH_CHALLENGE_INST, auth_status, + NULL, &chal_resp_param, + AUTH_CONN_CLIENT | AUTH_CONN_CHALLENGE_AUTH_METHOD); + + if (err) { + LOG_ERR("Failed to init Challenge-Response authentication service, err: %d.", err); + idle_function(); /* does not return */ + } + + + /* Configure the UART for serial transport. */ + err = config_uart(); + + if (err) { + LOG_ERR("Failed to configure the UART, err: %d.", err); + idle_function(); /* does not return */ + } + + /** + * @brief Enable the Bluetooth module. Passing NULL to bt_enable + * will block while the BLE stack is initialized. + * Enable bluetooth module + */ + err = bt_enable(NULL); + if (err) { + LOG_ERR("Failed to enable the bluetooth module, err: %d", err); + idle_function(); /* does not return */ + } + + /* Register connect/disconnect callbacks */ + bt_conn_cb_register(&conn_callbacks); + + /* start scanning for peripheral */ + err = bt_le_scan_start(BT_LE_SCAN_ACTIVE, device_found); + + if (err) { + printk("Scanning failed to start (err %d)\n", err); + } + + + /* does not return */ + idle_function(); + + /* should not reach here */ + +} diff --git a/samples/authentication/multi_instance/auth_server/CMakeLists.txt b/samples/authentication/multi_instance/auth_server/CMakeLists.txt new file mode 100644 index 000000000000..524177640d57 --- /dev/null +++ b/samples/authentication/multi_instance/auth_server/CMakeLists.txt @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(auth_serial_server) + + +target_include_directories(app PUBLIC ${CMAKE_SOURCE_DIR}) + +zephyr_include_directories(${CMAKE_SOURCE_DIR}/src) + +target_sources(app PRIVATE + src/main.c +) + +#zephyr_library_include_directories(${CMAKE_SOURCE_DIR}) diff --git a/samples/authentication/multi_instance/auth_server/prj.conf b/samples/authentication/multi_instance/auth_server/prj.conf new file mode 100644 index 000000000000..3df813a2bf10 --- /dev/null +++ b/samples/authentication/multi_instance/auth_server/prj.conf @@ -0,0 +1,96 @@ +# +# +# + +# Bluetooth config vars +CONFIG_BT=y +CONFIG_BT_DEBUG_LOG=y +CONFIG_BT_SMP=y +CONFIG_BT_SIGNING=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_DIS=y +CONFIG_BT_ATT_PREPARE_COUNT=5 +CONFIG_BT_PRIVACY=y +CONFIG_BT_DEVICE_NAME="Zephyr Peripheral Auth" +CONFIG_BT_DEVICE_APPEARANCE=833 +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=65 +CONFIG_BT_RX_BUF_LEN=1600 + + +# Increase stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +CONFIG_MAIN_STACK_SIZE=4096 + +# Don't use UART backend for logging, will collide with serial link +CONFIG_LOG=y +CONFIG_USE_SEGGER_RTT=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_AUTH_LOG_LEVEL=0 +CONFIG_LOG_PRINTK=y + + +CONFIG_AUTH_LIB=y +# Enable Bluetooth and seiral transports +CONFIG_SERIAL_XPORT=y +CONFIG_BT_XPORT=y + +# Using both DTLS and Challenge-Response authentication methods +# in different instances. +CONFIG_AUTH_DTLS=y +CONFIG_AUTH_CHALLENGE_RESPONSE=y + +# two authentication instance +CONFIG_NUM_AUTH_INSTANCES=2 + +# Config vars for Challenge-Response +CONFIG_TINYCRYPT=y +CONFIG_TINYCRYPT_SHA256=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# For production products, should use a real hardware +# random number generator. +#CONFIG_TEST_RANDOM_GENERATOR=y + + +# Mbed config vars +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" +CONFIG_MBEDTLS_TLS_VERSION_1_2=y +CONFIG_MBEDTLS_DTLS=y +CONFIG_MBEDTLS_ENTROPY_ENABLED=y + +# Supported key exchange modes +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED=y + +# Supported elliptic curves +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y + +# Supported cipher modes +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y +CONFIG_MBEDTLS_CIPHER_GCM_ENABLED=y +CONFIG_MBEDTLS_CIPHER_MODE_CBC_ENABLED=yy + + +# Supported message authentication methods +CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y +CONFIG_MBEDTLS_MAC_CMAC_ENABLED=y + + +# Other configurations +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=1500 +CONFIG_MBEDTLS_DEBUG=y +CONFIG_MBEDTLS_DEBUG_LEVEL=0 +CONFIG_MBEDTLS_ENABLE_HEAP=y +CONFIG_MBEDTLS_HEAP_SIZE=65535 +CONFIG_APP_LINK_WITH_MBEDTLS=y + + + + + + diff --git a/samples/authentication/multi_instance/auth_server/sample.yaml b/samples/authentication/multi_instance/auth_server/sample.yaml new file mode 100644 index 000000000000..6fda38efc3d1 --- /dev/null +++ b/samples/authentication/multi_instance/auth_server/sample.yaml @@ -0,0 +1,8 @@ +sample: + description: Auth Multi Instance Server sample app + name: Auth Server Multi Instance +tests: + sample.auth_server_multi: + harness: bluetooth + platform_allow: qemu_x86 + tags: bluetooth authentication diff --git a/samples/authentication/multi_instance/auth_server/src/main.c b/samples/authentication/multi_instance/auth_server/src/main.c new file mode 100644 index 000000000000..88b8043ebe4b --- /dev/null +++ b/samples/authentication/multi_instance/auth_server/src/main.c @@ -0,0 +1,430 @@ +/* + * Sample authentication BLE peripheral + * + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + + +LOG_MODULE_REGISTER(periph_auth, CONFIG_AUTH_LOG_LEVEL); + + +#include "../../../certs/auth_certs.h" + +/** + * Declare UUIDs used for the Authentication service and characteristics + */ + +static struct bt_uuid_128 auth_service_uuid = AUTH_SERVICE_UUID; + +static struct bt_uuid_128 auth_client_char = AUTH_SVC_CLIENT_CHAR_UUID; + +static struct bt_uuid_128 auth_server_char = AUTH_SVC_SERVER_CHAR; + + +static void client_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value); + +/* AUTH Service Declaration */ +BT_GATT_SERVICE_DEFINE(auth_svc, + BT_GATT_PRIMARY_SERVICE(&auth_service_uuid), + + /** + * Central (client role) bt_gatt_write() ---> server characteristic --> bt_gatt_read() Peripheral (server role) + * + * Central <--- Notification (client characteristic) <--- Peripheral + * + */ + + /** + * Client characteristic, used by the peripheral (server role) to write messages authentication messages + * to the central (client role). The peripheral needs to alert the central a message is + * ready to be read. + */ + BT_GATT_CHARACTERISTIC((const struct bt_uuid *)&auth_client_char, BT_GATT_CHRC_INDICATE, + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), NULL, NULL, NULL), + BT_GATT_CCC(client_ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), + + /** + * Server characteristic, used by the central (client role) to write authentication messages to. + * to the server (peripheral) + */ + BT_GATT_CHARACTERISTIC((const struct bt_uuid *)&auth_server_char, BT_GATT_CHRC_WRITE, + (BT_GATT_PERM_READ | BT_GATT_PERM_WRITE), NULL, auth_xp_bt_central_write, NULL), + ); + + + +struct bt_conn *default_conn; + +static bool is_connected = false; + +/* Authentication connection info for serial and Bluetooth */ +static struct authenticate_conn auth_conn_bt; +static struct authenticate_conn auth_conn_serial; + + +/* The Root and Intermediate Certs in a single CA chain. + * plus the server cert. All in PEM format.*/ +static const uint8_t auth_cert_ca_chain[] = AUTH_ROOTCA_CERT_PEM AUTH_INTERMEDIATE_CERT_PEM; +static const uint8_t auth_dev_server_cert[] = AUTH_SERVER_CERT_PEM; +static const uint8_t auth_server_privatekey[] = AUTH_SERVER_PRIVATE_KEY_PEM; + + +#define AUTH_DTLS_INST AUTH_INST_1_ID +#define AUTH_CHALLENGE_INST AUTH_INST_2_ID + + +static struct auth_optional_param dtls_certs_param = { + .param_id = AUTH_DTLS_PARAM, + .param_body = { + .dtls_certs = { + .server_ca_chain_pem = { + .cert = auth_cert_ca_chain, + .cert_size = sizeof(auth_cert_ca_chain), + }, + + .device_cert_pem = { + .cert = auth_dev_server_cert, + .cert_size = sizeof(auth_dev_server_cert), + .priv_key = auth_server_privatekey, + .priv_key_size = sizeof(auth_server_privatekey) + } + } + } +}; + + +#define NEW_SHARED_KEY_LEN (32u) + +/* Use a different key than default */ +static uint8_t chal_resp_sharedkey[NEW_SHARED_KEY_LEN] = { + 0x21, 0x8e, 0x37, 0x42, 0x1e, 0xe1, 0x2a, 0x22, 0x7c, 0x4b, 0x3f, 0x3f, 0x07, 0x5e, 0x8a, 0xd8, + 0x24, 0xdf, 0xca, 0xf4, 0x04, 0xd0, 0x3e, 0x22, 0x61, 0x9f, 0x24, 0xa3, 0xc7, 0xf6, 0x5d, 0x66 +}; + + +static struct auth_optional_param chal_resp_param = { + .param_id = AUTH_CHALRESP_PARAM, + .param_body = { + .chal_resp = { + .shared_key = chal_resp_sharedkey, + }, + } +}; + +/** + * Set up the advertising data + */ +static const struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA_BYTES(BT_DATA_UUID128_ALL, AUTH_SERVICE_UUID_BYTES), /* auth service UUID */ +}; + + +static const struct device *uart_dev; + +static struct uart_config uart_cfg = { + .baudrate = 115200, + .parity = UART_CFG_PARITY_NONE, + .stop_bits = UART_CFG_STOP_BITS_1, + .data_bits = UART_CFG_DATA_BITS_8, + .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, +}; + +/** + * Connection callback + * + * @param conn The Bluetooth connection. + * @param err Error value, 0 == success + */ +static void connected(struct bt_conn *conn, uint8_t err) +{ + int ret; + struct auth_xport_evt conn_evt; + + if (err) { + printk("Connection failed (err 0x%02x)\n", err); + } else { + default_conn = bt_conn_ref(conn); + printk("Connected\n"); + + struct auth_xp_bt_params xport_param = + { .conn = conn, .is_central = false, + .client_attr = &auth_svc.attrs[1] }; + + ret = auth_xport_init(&auth_conn_bt.xport_hdl, auth_conn_bt.instance, + AUTH_XP_TYPE_BLUETOOTH, &xport_param); + + if (ret) { + printk("Failed to initialize BT transport, err: %d", ret); + return; + } + + is_connected = true; + + /* send connection event to BT transport */ + conn_evt.event = XP_EVT_CONNECT; + auth_xport_event(auth_conn_bt.xport_hdl, &conn_evt); + + /* Start Bluetooth authentication */ + ret = auth_lib_start(&auth_conn_bt); + + if (ret) { + printk("Failed to start authentication, err: %d\n", ret); + } + } +} + +/** + * The disconnect callback. + * + * @param conn Bluetooth connection struct + * @param reason Disconnect reason code. + */ +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + struct auth_xport_evt conn_evt; + + printk("Disconnected (reason 0x%02x)\n", reason); + + is_connected = false; + + /* Send disconnect event to BT transport. */ + conn_evt.event = XP_EVT_DISCONNECT; + auth_xport_event(auth_conn_bt.xport_hdl, &conn_evt); + + /* Deinit lower transport */ + auth_xport_deinit(auth_conn_bt.xport_hdl); + auth_conn_bt.xport_hdl = NULL; + + if (default_conn) { + bt_conn_unref(default_conn); + default_conn = NULL; + } +} + +/** + * Connect callbacks + */ +static struct bt_conn_cb conn_callbacks = { + .connected = connected, + .disconnected = disconnected, +}; + +/** + * If the pairing was canceled. + * + * @param conn The Bluetooth connection. + */ +static void auth_cancel(struct bt_conn *conn) +{ + char addr[BT_ADDR_LE_STR_LEN]; + + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + printk("Pairing cancelled: %s\n", addr); +} + +static struct bt_conn_auth_cb auth_cb_display = { + .cancel = auth_cancel, +}; + +/** + * Called after the BT module has initialized or not (error occurred). + * + * @param err Error code, 0 == success + */ +static void bt_ready(int err) +{ + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + return; + } + + printk("Bluetooth initialized\n"); + + /* Start advertising after BT module has initialized OK */ + err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0); + if (err) { + printk("Advertising failed to start (err %d)\n", err); + return; + } + + printk("Advertising successfully started\n"); +} + +/** + * Authentication status callback + * + * @param auth_conn The authentication connection. + * @param instance Instance ID. + * @param status Status + * @param context Optional context. + */ +static void auth_status(struct authenticate_conn *auth_conn, + enum auth_instance_id instance, + enum auth_status status, void *context) +{ + /* display status */ + if (instance == AUTH_DTLS_INST) { + printk("Auth instance DTLS (%d) status: %s\n", instance, + auth_lib_getstatus_str(status)); + + if (status == AUTH_STATUS_IN_PROCESS) { + printk(" DTLS may take 30-60 seconds.\n"); + } + } + + if (instance == AUTH_CHALLENGE_INST) { + printk("Auth instance Challenge-Response (%d) status: %s\n", instance, + auth_lib_getstatus_str(status)); + } +} + + +/** + * Called when client notification is (dis)enabled by the Central + * + * @param attr GATT attribute. + * @param value BT_GATT_CCC_NOTIFY if changes are notified. + */ +static void client_ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) +{ + ARG_UNUSED(attr); + + bool notif_enabled = (value == BT_GATT_CCC_NOTIFY) ? true : false; + + LOG_INF("Client notifications %s", notif_enabled ? "enabled" : "disabled"); +} + +/** + * Configure the UART params. + * + * @return 0 on success, else negative error value. + */ +static int config_uart(void) +{ + struct auth_xp_serial_params xp_params; + + uart_dev = device_get_binding(DT_LABEL(DT_NODELABEL(uart0))); + + int err = uart_configure(uart_dev, &uart_cfg); + + if (err) { + LOG_ERR("Failed to configure UART, err: %d", err); + return err; + } + + /* If successful,then init lower transport layer. */ + xp_params.uart_dev = uart_dev; + + err = auth_xport_init(&auth_conn_serial.xport_hdl, auth_conn_serial.instance, + AUTH_XP_TYPE_SERIAL, &xp_params); + + if (err) { + LOG_ERR("Failed to initialize authentication transport, error: %d", err); + } + + return err; +} + + +/** + * Process log messages + */ +static void process_log_msgs(void) +{ + while (log_process(false)) { + ; /* intentionally empty statement */ + } +} + + +static void idle_function(void) +{ + while (true) { + process_log_msgs(); + k_yield(); + } +} + +void main(void) +{ + int err = 0; + + log_init(); + + printk("Starting multi auth server.\n"); + + /* Instance uses DTLS auth method over Bluetooth transport. */ + err = auth_lib_init(&auth_conn_bt, AUTH_DTLS_INST, auth_status, NULL, + &dtls_certs_param, + AUTH_CONN_SERVER | AUTH_CONN_DTLS_AUTH_METHOD); + + if (err) { + printk("Failed to initialize DTLS auth method.\n"); + idle_function(); + } + + /* Instance 2 uses Challenge-Response method over serial transport */ + err = auth_lib_init(&auth_conn_serial, AUTH_CHALLENGE_INST, auth_status, + NULL, &chal_resp_param, + AUTH_CONN_SERVER | AUTH_CONN_CHALLENGE_AUTH_METHOD); + + if (err) { + printk("Failed to initialize Challenge-Response auth method.\n"); + idle_function(); /* never returns */ + } + + /* Configure the UART for the serial transport. */ + err = config_uart(); + if (err) { + printk("Failed to initialize UART for serial link.\n"); + idle_function(); /* never returns */ + } + + + err = bt_enable(bt_ready); + if (err) { + printk("Bluetooth init failed (err %d)\n", err); + idle_function(); /* never returns */ + } + + bt_conn_cb_register(&conn_callbacks); + bt_conn_auth_cb_register(&auth_cb_display); + + /* Start Challenge-Response auth over serial transport. */ + err = auth_lib_start(&auth_conn_serial); + + if (err) { + printk("Failed to start Challenge-Response authentication, err: %d\n", err); + } + + /* never returns */ + idle_function(); + + /* should never reach here */ +} + diff --git a/samples/authentication/multi_instance/heart_monitor_device.png b/samples/authentication/multi_instance/heart_monitor_device.png new file mode 100644 index 000000000000..345c6ac25d18 Binary files /dev/null and b/samples/authentication/multi_instance/heart_monitor_device.png differ diff --git a/samples/authentication/serial/README.rst b/samples/authentication/serial/README.rst new file mode 100644 index 000000000000..ae5641d0b44f --- /dev/null +++ b/samples/authentication/serial/README.rst @@ -0,0 +1,37 @@ +.. _auth_serial-sample: + +Authentication over Serial +########################## + +Overview +******** + +There are two Serial firmware applications, client and server. Pin P.06 and P.08 were used +for TX and RX lines on the Nordic nRF52840-DK board, one board acting as the server, the other as +the client. + +The authentication method, DTLS or Challenge-Response, is configurable via KConfig menu. + +Building and Running +-------------------- +This sample was developed and tested with two Nordic nRF52840 dev +kits (see: https://www.nordicsemi.com/Software-and-Tools/Development-Kits/nRF52840-DK). Two Ubuntu +VMs were used, one running the Client the other VM running the Server. + +There are two project config files, proj.conf for use with the Challenge-Response authentication method and dtls.prj.conf for +use with the DTLS authentication method. Use the -DCONFIG_FILE=dtls.prj.conf to use DLTS. + +To build the client with DTLS:|br| +cmake -Bbuild_auth_client -DBOARD=nrf52840dk_nrf52840 -DCONF_FILE=dtls.prj.conf samples/authentication/serial/auth_client + +To build the server with DLTS using West:|br| +west build -d build_auth_server -b nrf52840dk_nrf52840 -- -DCONF_FILE=dtls.prj.conf samples/authentication/serial/auth_server |br| +(note: Two dashes '-' after 'west build -b build_auth_server -b nrf52840dk_nrf52840 --', necessary for -DCONF_FILE define) + + +Note: To avoid problems, ensure debug output is done via Jlink RTT, not the UART backend. + + + + + diff --git a/samples/authentication/serial/auth_client/CMakeLists.txt b/samples/authentication/serial/auth_client/CMakeLists.txt new file mode 100644 index 000000000000..6737ad8ef9c6 --- /dev/null +++ b/samples/authentication/serial/auth_client/CMakeLists.txt @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(auth_serial_client) + + +target_include_directories(app PUBLIC ${CMAKE_SOURCE_DIR}) + +target_sources(app PRIVATE + src/main.c +) + +#zephyr_library_include_directories(${CMAKE_SOURCE_DIR}) diff --git a/samples/authentication/serial/auth_client/dtls.prj.conf b/samples/authentication/serial/auth_client/dtls.prj.conf new file mode 100644 index 000000000000..5dddfd25baf8 --- /dev/null +++ b/samples/authentication/serial/auth_client/dtls.prj.conf @@ -0,0 +1,64 @@ +# +# Project configuration to us if DTLS authentication method +# is enabled. +# + +CONFIG_MAIN_STACK_SIZE=4096 + +# Increase stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +CONFIG_AUTH_LIB=y +CONFIG_SERIAL_XPORT=y +CONFIG_AUTH_DTLS=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# Don't use UART backend for logging, will collide with serial link +CONFIG_LOG=y +CONFIG_USE_SEGGER_RTT=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_LOG_PRINTK=y + +# For production products, should use a real hardware +# random number generator. +CONFIG_TEST_RANDOM_GENERATOR=y + +# Mbed config'CONFIG_MBEDTLS=y +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" +CONFIG_MBEDTLS_TLS_VERSION_1_2=y +CONFIG_MBEDTLS_DTLS=y +CONFIG_MBEDTLS_ENTROPY_ENABLED=y + +# Supported key exchange modes +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED=y + +# Supported elliptic curves +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y + +# Supported cipher modes +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y +CONFIG_MBEDTLS_CIPHER_GCM_ENABLED=y +CONFIG_MBEDTLS_CIPHER_MODE_CBC_ENABLED=y + + +# Supported message authentication methods +CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y +CONFIG_MBEDTLS_MAC_CMAC_ENABLED=y + + +# Other configurations +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=1500 +CONFIG_MBEDTLS_DEBUG=y +CONFIG_MBEDTLS_DEBUG_LEVEL=0 +CONFIG_MBEDTLS_ENABLE_HEAP=y + +# Mbed uses a chunk of memory, it might be possible to reduce +# this heap usage. +CONFIG_MBEDTLS_HEAP_SIZE=65535 +CONFIG_APP_LINK_WITH_MBEDTLS=y + + diff --git a/samples/authentication/serial/auth_client/prj.conf b/samples/authentication/serial/auth_client/prj.conf new file mode 100644 index 000000000000..a308987edcf4 --- /dev/null +++ b/samples/authentication/serial/auth_client/prj.conf @@ -0,0 +1,26 @@ +# +# Project configuration to us if Challenge-Response authentication method +# is enabled. +# + + +# Increase stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +CONFIG_MAIN_STACK_SIZE=1024 +CONFIG_AUTH_LIB=y +CONFIG_SERIAL_XPORT=y +CONFIG_AUTH_CHALLENGE_RESPONSE=y +CONFIG_TINYCRYPT=y +CONFIG_TINYCRYPT_SHA256=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# For production products, should use a real hardware +# random number generator. +CONFIG_TEST_RANDOM_GENERATOR=y + +# Don't use UART backend for logging, will collide with serial link +CONFIG_LOG=y +CONFIG_USE_SEGGER_RTT=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_LOG_PRINTK=y diff --git a/samples/authentication/serial/auth_client/sample.yaml b/samples/authentication/serial/auth_client/sample.yaml new file mode 100644 index 000000000000..670aab75d5e1 --- /dev/null +++ b/samples/authentication/serial/auth_client/sample.yaml @@ -0,0 +1,8 @@ +sample: + description: Auth Client serial transport sample app + name: Auth Client Serial +tests: + sample.auth_client_serial: + harness: bluetooth + platform_allow: qemu_x86 + tags: authentication \ No newline at end of file diff --git a/samples/authentication/serial/auth_client/src/main.c b/samples/authentication/serial/auth_client/src/main.c new file mode 100644 index 000000000000..4c9712e6c943 --- /dev/null +++ b/samples/authentication/serial/auth_client/src/main.c @@ -0,0 +1,239 @@ + +/* main.c - Application main entry point + * Sample authenticating over a UART link */ + +/* + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#if defined(CONFIG_AUTH_DTLS) +#include "../../../certs/auth_certs.h" +#endif + +LOG_MODULE_REGISTER(auth_serial_client, CONFIG_AUTH_LOG_LEVEL); + + +static const struct device *uart_dev; + +static struct uart_config uart_cfg = { + .baudrate = 115200, + .parity = UART_CFG_PARITY_NONE, + .stop_bits = UART_CFG_STOP_BITS_1, + .data_bits = UART_CFG_DATA_BITS_8, + .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, +}; + +#if defined(CONFIG_AUTH_DTLS) +/* The Root and Intermediate Certs in a single CA chain. + * plus the server cert. All in PEM format.*/ +static const uint8_t auth_cert_ca_chain[] = AUTH_ROOTCA_CERT_PEM AUTH_INTERMEDIATE_CERT_PEM; +static const uint8_t auth_dev_client_cert[] = AUTH_CLIENT_CERT_PEM; +static const uint8_t auth_client_privatekey[] = AUTH_CLIENT_PRIVATE_KEY_PEM; + +static struct auth_optional_param tls_certs_param = { + .param_id = AUTH_DTLS_PARAM, + .param_body = { + .dtls_certs = { + .server_ca_chain_pem = { + .cert = auth_cert_ca_chain, + .cert_size = sizeof(auth_cert_ca_chain), + }, + + .device_cert_pem = { + .cert = auth_dev_client_cert, + .cert_size = sizeof(auth_dev_client_cert), + .priv_key = auth_client_privatekey, + .priv_key_size = sizeof(auth_client_privatekey) + } + } + } +}; +#endif + +#if defined(CONFIG_AUTH_CHALLENGE_RESPONSE) +#define NEW_SHARED_KEY_LEN (32u) + +/* Use a different key than default */ +static uint8_t chal_resp_sharedkey[NEW_SHARED_KEY_LEN] = { + 0x21, 0x8e, 0x37, 0x42, 0x1e, 0xe1, 0x2a, 0x22, 0x7c, 0x4b, 0x3f, 0x3f, 0x07, 0x5e, 0x8a, 0xd8, + 0x24, 0xdf, 0xca, 0xf4, 0x04, 0xd0, 0x3e, 0x22, 0x61, 0x9f, 0x24, 0xa3, 0xc7, 0xf6, 0x5d, 0x66 +}; + + +static struct auth_optional_param chal_resp_param = { + .param_id = AUTH_CHALRESP_PARAM, + .param_body = { + .chal_resp = { + .shared_key = chal_resp_sharedkey, + }, + } +}; +#endif + + +/* Authentication connection info */ +static struct authenticate_conn auth_conn_serial; + +/** + * Authentication status callback. + * + * @param auth_conn The authentication connection. + * @param instance Authentication instance id. + * @param status Authentication status. + * @param context Optional context + */ +void auth_status_callback(struct authenticate_conn *auth_conn, + enum auth_instance_id instance, + enum auth_status status, void *context) +{ + printk("Authentication instance (%d) status: %s\n", instance, + auth_lib_getstatus_str(status)); + +#if defined(CONFIG_AUTH_DTLS) + if (status == AUTH_STATUS_IN_PROCESS) { + printk(" DTLS may take 30-60 seconds.\n"); + } +#endif + + + if ((status == AUTH_STATUS_FAILED) || + (status == AUTH_STATUS_AUTHENTICATION_FAILED) || + (status == AUTH_STATUS_SUCCESSFUL)) { + /* Authentication has finished */ + auth_lib_deinit(auth_conn); + } +} + +/** + * Process log messages + */ +static void process_log_msgs(void) +{ + while (log_process(false)) { + ; /* intentionally empty statement */ + } +} + +/** + * Idle processing. + */ +static void idle_process(void) +{ + /* Just spin while the BT modules handle the connection. */ + while (true) { + + process_log_msgs(); + + /* Let the handshake thread run */ + k_yield(); + } +} + + +/** + * Configures the UART + * + * @return 0 on success, else negative error code. + */ +static int config_uart(void) +{ + struct auth_xp_serial_params xp_params; + + uart_dev = device_get_binding(DT_LABEL(DT_NODELABEL(uart0))); + + int err = uart_configure(uart_dev, &uart_cfg); + + if (err) { + LOG_ERR("Failed to configure UART, err: %d", err); + return err; + } + + /* If successful,then init lower transport layer. */ + xp_params.uart_dev = uart_dev; + + err = auth_xport_init(&auth_conn_serial.xport_hdl, + auth_conn_serial.instance, + AUTH_XP_TYPE_SERIAL, &xp_params); + + if (err) { + LOG_ERR("Failed to initialize authentication transport, error: %d", err); + } + + return err; +} + + +void main(void) +{ + struct auth_optional_param *opt_parms = NULL; + + log_init(); + +#if defined(CONFIG_AUTH_DTLS) && defined(CONFIG_AUTH_CHALLENGE_RESPONSE) +#error Invalid authentication config, either DTLS or Challenge-Response, not both. +#endif + + uint32_t flags = AUTH_CONN_CLIENT; + +#if defined(CONFIG_AUTH_DTLS) + flags |= AUTH_CONN_DTLS_AUTH_METHOD; + + /* set TLS certs */ + opt_parms = &tls_certs_param; + printk("Using DTLS authentication method.\n"); +#endif + + +#if defined(CONFIG_AUTH_CHALLENGE_RESPONSE) + flags |= AUTH_CONN_CHALLENGE_AUTH_METHOD; + + /* Use different shared key */ + opt_parms = &chal_resp_param; + printk("Using Challenge-Response authentication method.\n"); +#endif + + + /* init authentication library */ + int err = auth_lib_init(&auth_conn_serial, AUTH_INST_1_ID, + auth_status_callback, NULL, + opt_parms, flags); + + /* If successful, then configure the UAR and start the + * authentication process */ + if (!err) { + + /* configure the UART and init the lower serial transport */ + err = config_uart(); + + /* start authentication */ + if (!err) { + err = auth_lib_start(&auth_conn_serial); + + if (err) { + LOG_ERR("Failed to start authentication, err: %d", err); + } + } + + } + + /* does not return */ + idle_process(); + + /* should not reach here */ +} diff --git a/samples/authentication/serial/auth_server/CMakeLists.txt b/samples/authentication/serial/auth_server/CMakeLists.txt new file mode 100644 index 000000000000..524177640d57 --- /dev/null +++ b/samples/authentication/serial/auth_server/CMakeLists.txt @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(auth_serial_server) + + +target_include_directories(app PUBLIC ${CMAKE_SOURCE_DIR}) + +zephyr_include_directories(${CMAKE_SOURCE_DIR}/src) + +target_sources(app PRIVATE + src/main.c +) + +#zephyr_library_include_directories(${CMAKE_SOURCE_DIR}) diff --git a/samples/authentication/serial/auth_server/dtls.prj.conf b/samples/authentication/serial/auth_server/dtls.prj.conf new file mode 100644 index 000000000000..e3e8b581f6f5 --- /dev/null +++ b/samples/authentication/serial/auth_server/dtls.prj.conf @@ -0,0 +1,65 @@ +# +# Project configuration to us if DTLS authentication method +# is enabled. +# + +CONFIG_MAIN_STACK_SIZE=4096 + +# Increase stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +CONFIG_AUTH_LIB=y +CONFIG_SERIAL_XPORT=y +CONFIG_AUTH_DTLS=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# Don't use UART backend for logging, will collide with serial link +CONFIG_LOG=y +CONFIG_USE_SEGGER_RTT=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_LOG_PRINTK=y + +# For production products, should use a real hardware +# random number generator. +CONFIG_TEST_RANDOM_GENERATOR=y + +# Mbed config'CONFIG_MBEDTLS=y +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_CFG_FILE="config-tls-generic.h" +CONFIG_MBEDTLS_TLS_VERSION_1_2=y +CONFIG_MBEDTLS_DTLS=y +CONFIG_MBEDTLS_ENTROPY_ENABLED=y + +# Supported key exchange modes +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA_ENABLED=y + +# Supported elliptic curves +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y + +# Supported cipher modes +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y +CONFIG_MBEDTLS_CIPHER_GCM_ENABLED=y +CONFIG_MBEDTLS_CIPHER_MODE_CBC_ENABLED=y + + + +# Supported message authentication methods +CONFIG_MBEDTLS_MAC_SHA256_ENABLED=y +CONFIG_MBEDTLS_MAC_CMAC_ENABLED=y + + +# Other configurations +CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y +CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=1500 +CONFIG_MBEDTLS_DEBUG=y +CONFIG_MBEDTLS_DEBUG_LEVEL=0 +CONFIG_MBEDTLS_ENABLE_HEAP=y + +# Mbed uses a chunk of memory, it might be possible to reduce +# this heap usage. +CONFIG_MBEDTLS_HEAP_SIZE=65535 +CONFIG_APP_LINK_WITH_MBEDTLS=y + + diff --git a/samples/authentication/serial/auth_server/prj.conf b/samples/authentication/serial/auth_server/prj.conf new file mode 100644 index 000000000000..34230db03adf --- /dev/null +++ b/samples/authentication/serial/auth_server/prj.conf @@ -0,0 +1,26 @@ +# +# Project configuration to us if Challenge-Response authentication method +# is enabled. +# + + +# Increase stack due to settings API usage +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 +CONFIG_MAIN_STACK_SIZE=1024 +CONFIG_AUTH_LIB=y +CONFIG_SERIAL_XPORT=y +CONFIG_AUTH_CHALLENGE_RESPONSE=y +CONFIG_TINYCRYPT=y +CONFIG_TINYCRYPT_SHA256=y +CONFIG_UART_INTERRUPT_DRIVEN=y + +# For production products, should use a real hardware +# random number generator. +CONFIG_TEST_RANDOM_GENERATOR=y + +# Don't use UART backend for logging, will collide with serial link +CONFIG_LOG=y +CONFIG_USE_SEGGER_RTT=y +CONFIG_LOG_BACKEND_RTT=y +CONFIG_LOG_BACKEND_UART=n +CONFIG_LOG_PRINTK=y \ No newline at end of file diff --git a/samples/authentication/serial/auth_server/sample.yaml b/samples/authentication/serial/auth_server/sample.yaml new file mode 100644 index 000000000000..8fe6dbd28d6b --- /dev/null +++ b/samples/authentication/serial/auth_server/sample.yaml @@ -0,0 +1,8 @@ +sample: + description: Auth Server serial transport sample app + name: Auth Server Serial +tests: + sample.auth_server_serial: + harness: bluetooth + platform_allow: qemu_x86 + tags: authentication \ No newline at end of file diff --git a/samples/authentication/serial/auth_server/src/main.c b/samples/authentication/serial/auth_server/src/main.c new file mode 100644 index 000000000000..774cc3bf96f7 --- /dev/null +++ b/samples/authentication/serial/auth_server/src/main.c @@ -0,0 +1,233 @@ + +/* main.c - Application main entry point + * Sample authenticating over a UART link */ + +/* + * SPDX-License-Identifier: Apache-2.0 + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + + +#if defined(CONFIG_AUTH_DTLS) +#include "../../../certs/auth_certs.h" +#endif + +LOG_MODULE_REGISTER(auth_serial_server, CONFIG_AUTH_LOG_LEVEL); + + + +static struct uart_config uart_cfg = { + .baudrate = 115200, + .parity = UART_CFG_PARITY_NONE, + .stop_bits = UART_CFG_STOP_BITS_1, + .data_bits = UART_CFG_DATA_BITS_8, + .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, +}; + +#if defined(CONFIG_AUTH_DTLS) +/* The Root and Intermediate Certs in a single CA chain. + * plus the server cert. All in PEM format.*/ +static const uint8_t auth_cert_ca_chain[] = AUTH_ROOTCA_CERT_PEM AUTH_INTERMEDIATE_CERT_PEM; +static const uint8_t auth_dev_client_cert[] = AUTH_CLIENT_CERT_PEM; +static const uint8_t auth_client_privatekey[] = AUTH_CLIENT_PRIVATE_KEY_PEM; + +static struct auth_optional_param tls_certs_param = { + .param_id = AUTH_DTLS_PARAM, + .param_body = { + .dtls_certs = { + .server_ca_chain_pem = { + .cert = auth_cert_ca_chain, + .cert_size = sizeof(auth_cert_ca_chain), + }, + + .device_cert_pem = { + .cert = auth_dev_client_cert, + .cert_size = sizeof(auth_dev_client_cert), + .priv_key = auth_client_privatekey, + .priv_key_size = sizeof(auth_client_privatekey) + } + } + } +}; +#endif + +#if defined(CONFIG_AUTH_CHALLENGE_RESPONSE) +#define NEW_SHARED_KEY_LEN (32u) + +/* Use a different key than default */ +static uint8_t chal_resp_sharedkey[NEW_SHARED_KEY_LEN] = { + 0x21, 0x8e, 0x37, 0x42, 0x1e, 0xe1, 0x2a, 0x22, 0x7c, 0x4b, 0x3f, 0x3f, 0x07, 0x5e, 0x8a, 0xd8, + 0x24, 0xdf, 0xca, 0xf4, 0x04, 0xd0, 0x3e, 0x22, 0x61, 0x9f, 0x24, 0xa3, 0xc7, 0xf6, 0x5d, 0x66 +}; + + +static struct auth_optional_param chal_resp_param = { + .param_id = AUTH_CHALRESP_PARAM, + .param_body = { + .chal_resp = { + .shared_key = chal_resp_sharedkey, + }, + } +}; +#endif + +static const struct device *uart_dev; + +/* Authentication connection info */ +static struct authenticate_conn auth_conn_serial; + +/** + * Authentication status callback. + * + * @param auth_conn The authentication connection. + * @param instance Authentication instance. + * @param status Status + * @param context Optional context. + */ +static void auth_status_callback(struct authenticate_conn *auth_conn, + enum auth_instance_id instance, + enum auth_status status, void *context) +{ + printk("Authentication instance (%d) status: %s\n", instance, + auth_lib_getstatus_str(status)); + +#if defined(CONFIG_AUTH_DTLS) + if (status == AUTH_STATUS_IN_PROCESS) { + printk(" DTLS may take 30-60 seconds.\n"); + } +#endif + + if ((status == AUTH_STATUS_FAILED) || + (status == AUTH_STATUS_AUTHENTICATION_FAILED) || + (status == AUTH_STATUS_SUCCESSFUL)) { + /* Authentication has finished */ + auth_lib_deinit(auth_conn); + } +} + +static void process_log_msgs(void) +{ + while (log_process(false)) { + ; /* intentionally empty statement */ + } +} + +static void idle_process(void) +{ + /* Just spin while the BT modules handle the connection. */ + while (true) { + + process_log_msgs(); + + /* Let the handshake thread run */ + k_yield(); + } +} + +/** + * Configure the UART params. + * + * @return 0 on success, else negative error value. + */ +static int config_uart(void) +{ + struct auth_xp_serial_params xp_params; + + uart_dev = device_get_binding(DT_LABEL(DT_NODELABEL(uart0))); + + int err = uart_configure(uart_dev, &uart_cfg); + + if (err) { + LOG_ERR("Failed to configure UART, err: %d", err); + return err; + } + + /* If successful,then init lower transport layer. */ + xp_params.uart_dev = uart_dev; + + err = auth_xport_init(&auth_conn_serial.xport_hdl, + auth_conn_serial.instance, + AUTH_XP_TYPE_SERIAL, &xp_params); + + if (err) { + LOG_ERR("Failed to initialize authentication transport, error: %d", err); + } + + return err; +} + + +void main(void) +{ + struct auth_optional_param *opt_parms = NULL; + + log_init(); + +#if defined(CONFIG_AUTH_DTLS) && defined(CONFIG_AUTH_CHALLENGE_RESPONSE) +#error Invalid authentication config, either DTLS or Challenge-Response, not both. +#endif + + uint32_t flags = AUTH_CONN_SERVER; + + +#if defined(CONFIG_AUTH_DTLS) + flags |= AUTH_CONN_DTLS_AUTH_METHOD; + + /* set TLS certs */ + opt_parms = &tls_certs_param; + printk("Using DTLS authentication method.\n"); +#endif + + +#if defined(CONFIG_AUTH_CHALLENGE_RESPONSE) + flags |= AUTH_CONN_CHALLENGE_AUTH_METHOD; + + /* Use different shared key */ + opt_parms = &chal_resp_param; + printk("Using Challenge-Response authentication method.\n"); +#endif + + + /* init authentication library */ + int err = auth_lib_init(&auth_conn_serial, AUTH_INST_1_ID, + auth_status_callback, NULL, + opt_parms, flags); + + /* If successful, then configure the UAR and start the + * authentication process */ + if (!err) { + + /* configure the UART and init the lower serial transport */ + err = config_uart(); + + /* start authentication */ + if (!err) { + err = auth_lib_start(&auth_conn_serial); + + if (err) { + LOG_ERR("Failed to start authentication, err: %d", err); + } + } + + } + + /* does not return */ + idle_process(); + + /* should not reach here */ +} diff --git a/samples/index.rst b/samples/index.rst index 8581e763db30..51e059508c60 100644 --- a/samples/index.rst +++ b/samples/index.rst @@ -28,6 +28,7 @@ Samples and Demos smp/* tfm_integration/tfm_integration.rst debug/* + authentication/* .. comment To add a new sample document, please use the template available under diff --git a/tests/lib/authentication/CMakeLists.txt b/tests/lib/authentication/CMakeLists.txt new file mode 100644 index 000000000000..445aaf129642 --- /dev/null +++ b/tests/lib/authentication/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.13.1) +include($ENV{ZEPHYR_BASE}/cmake/app/boilerplate.cmake NO_POLICY_SCOPE) +project(integration) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/tests/lib/authentication/prj.conf b/tests/lib/authentication/prj.conf new file mode 100644 index 000000000000..7d1fac3d043b --- /dev/null +++ b/tests/lib/authentication/prj.conf @@ -0,0 +1,13 @@ +CONFIG_ZTEST=y +CONFIG_AUTH_LIB=y +CONFIG_SERIAL=y +CONFIG_SERIAL_XPORT=y +CONFIG_AUTH_CHALLENGE_RESPONSE=y +CONFIG_TINYCRYPT=y +CONFIG_TINYCRYPT_SHA256=y +CONFIG_LOG=y +CONFIG_AUTH_LOG_LEVEL=3 +CONFIG_AUTH_CHALLENGE_RESPONSE=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_TEST_RANDOM_GENERATOR=y + diff --git a/tests/lib/authentication/src/main.c b/tests/lib/authentication/src/main.c new file mode 100644 index 000000000000..5e7e7c500e4b --- /dev/null +++ b/tests/lib/authentication/src/main.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +/** + * @brief Test Asserts + * + * This test verifies various assert macros provided by ztest. + * + */ + + +static void auth_status_callback(struct authenticate_conn *auth_conn, enum auth_instance_id instance, + enum auth_status status, void *context) +{ + // dummy function +} + +static void test_auth_api(void) +{ + int ret_val = 0; + struct authenticate_conn auth_conn; + + /* init library with NULL status function callback */ + ret_val = auth_lib_init(&auth_conn, AUTH_INST_1_ID, NULL, NULL, + NULL, AUTH_CONN_SERVER | AUTH_CONN_CHALLENGE_AUTH_METHOD); + + zassert_equal(ret_val, AUTH_ERROR_INVALID_PARAM, "NULL status function param test failed."); + + /* Verify server and client roles flags fail */ + ret_val = auth_lib_init(&auth_conn, AUTH_INST_1_ID, auth_status_callback, NULL, + NULL, AUTH_CONN_SERVER | AUTH_CONN_CLIENT | AUTH_CONN_CHALLENGE_AUTH_METHOD); + + zassert_equal(ret_val, AUTH_ERROR_INVALID_PARAM, "Invalid flags test failed."); + + /* Verify DLTS and Challenge-Reponse flags fail */ + ret_val = auth_lib_init(&auth_conn, AUTH_INST_1_ID, auth_status_callback, NULL, + NULL, AUTH_CONN_SERVER | AUTH_CONN_DTLS_AUTH_METHOD | AUTH_CONN_CHALLENGE_AUTH_METHOD); + + zassert_equal(ret_val, AUTH_ERROR_INVALID_PARAM, "Invalid flags test failed."); + + // init lib with valid params + ret_val = auth_lib_init(&auth_conn, AUTH_INST_1_ID, auth_status_callback, NULL, + NULL, AUTH_CONN_SERVER | AUTH_CONN_CHALLENGE_AUTH_METHOD); + + zassert_equal(ret_val, AUTH_SUCCESS, "Failed to initialize Authentication library."); + + /* de-init */ + ret_val = auth_lib_deinit(&auth_conn); + + zassert_equal(ret_val, AUTH_SUCCESS, "Failed to start Challenge-Response authentication."); + +} + +void test_main(void) +{ + ztest_test_suite(authentication_tests, + ztest_unit_test(test_auth_api) + ); + + ztest_run_test_suite(authentication_tests); +} diff --git a/tests/lib/authentication/testcase.yaml b/tests/lib/authentication/testcase.yaml new file mode 100644 index 000000000000..463b8a93a391 --- /dev/null +++ b/tests/lib/authentication/testcase.yaml @@ -0,0 +1,4 @@ +tests: + authentication.auth: + tags: authentication + platform_allow: qemu_cortex_m3 qemu_x86