feat(nimble): signed write support

This commit is contained in:
Sumeet Singh
2024-02-29 16:23:47 +05:30
committed by Abhinav Kudnar
parent 5668e0c2f2
commit 4712c3c890
14 changed files with 415 additions and 2 deletions
+1
View File
@@ -207,6 +207,7 @@ struct os_mbuf;
/** Write Command. */
#define BLE_ATT_OP_WRITE_CMD 0x52
#define BLE_ATT_OP_SIGNED_WRITE_CMD 0xD2
/** @} */
+15
View File
@@ -522,6 +522,21 @@ int ble_gattc_write_no_rsp(uint16_t conn_handle, uint16_t attr_handle,
int ble_gattc_write_no_rsp_flat(uint16_t conn_handle, uint16_t attr_handle,
const void *data, uint16_t data_len);
/**
* Initiates GATT procedure: Signed Write. This function consumes the
* supplied mbuf regardless of the outcome.
*
* @param conn_handle The connection over which to execute the
* procedure.
* @param attr_handle The handle of the characteristic value to write
* to.
* @param txom The value to write to the characteristic.
*
* @return 0 on success; nonzero on failure.
*/
int ble_gattc_signed_write(uint16_t conn_handle, uint16_t attr_handle,
struct os_mbuf * txom);
/**
* Initiates GATT procedure: Write Characteristic Value. This function
* consumes the supplied mbuf regardless of the outcome.
+1
View File
@@ -81,6 +81,7 @@ struct ble_store_value_sec {
uint8_t csrk[16];
uint8_t csrk_present:1;
uint32_t sign_counter;
unsigned authenticated:1;
uint8_t sc:1;
+1
View File
@@ -71,6 +71,7 @@ static const struct ble_att_rx_dispatch_entry ble_att_rx_dispatch[] = {
{ BLE_ATT_OP_INDICATE_RSP, ble_att_clt_rx_indicate },
{ BLE_ATT_OP_READ_MULT_VAR_REQ, ble_att_svr_rx_read_mult_var },
{ BLE_ATT_OP_WRITE_CMD, ble_att_svr_rx_write_no_rsp },
{ BLE_ATT_OP_SIGNED_WRITE_CMD, ble_att_svr_rx_signed_write },
};
#define BLE_ATT_RX_DISPATCH_SZ \
+89
View File
@@ -25,6 +25,11 @@
#include "nimble/ble.h"
#include "host/ble_uuid.h"
#include "ble_hs_priv.h"
#include "esp_nimble_mem.h"
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
#if NIMBLE_BLE_CONNECT
/*****************************************************************************
@@ -748,6 +753,90 @@ ble_att_clt_rx_write(uint16_t conn_handle, struct os_mbuf **rxom)
return 0;
}
int
ble_att_clt_tx_signed_write_cmd(uint16_t conn_handle, uint16_t handle, uint8_t *csrk,
uint32_t counter, struct os_mbuf *txom)
{
#if !NIMBLE_BLE_ATT_CLT_SIGNED_WRITE
return BLE_HS_ENOTSUP;
#endif
struct ble_att_signed_write_cmd *cmd;
struct os_mbuf *txom2;
uint8_t cmac[16];
uint8_t *message = NULL;
int rc;
int i;
BLE_HS_LOG(DEBUG, "ble_att_clt_tx_signed_write_cmd(): ");
for (i = 0; i < OS_MBUF_PKTLEN(txom); i++) {
BLE_HS_LOG(DEBUG, "0x%02x", (OS_MBUF_DATA(txom, uint8_t *))[i]);
}
cmd = ble_att_cmd_get(BLE_ATT_OP_SIGNED_WRITE_CMD,
sizeof(*cmd), &txom2);
if (cmd == NULL) {
rc = BLE_HS_ENOMEM;
goto err;
}
cmd->handle = htole16(handle);
/* Message to be signed is message||sign_counter,
* where || represents concatenation
*/
message = nimble_platform_mem_malloc(OS_MBUF_PKTLEN(txom) + sizeof(counter));
rc = os_mbuf_copydata(txom, 0, OS_MBUF_PKTLEN(txom), message);
if (rc != 0) {
goto err;
}
memcpy(&message[OS_MBUF_PKTLEN(txom)], &counter, sizeof(counter));
/* ble_sm_alg_aes_cmac takes data in little-endian format,
* so converting it to LE.
*/
swap_in_place(message, OS_MBUF_PKTLEN(txom) + sizeof(counter));
/* Getting the CMAC (Cipher-based Message Authentication Code)
* for the message using our CSRK for this connection.
*/
memset(cmac, 0, sizeof cmac);
rc = ble_sm_alg_aes_cmac(csrk, message,
OS_MBUF_PKTLEN(txom) + sizeof(counter), cmac);
if (rc != 0) {
goto err;
}
/* After using the csrk to sign data,
* the sign counter needs to be updated.
*/
rc = ble_sm_incr_our_sign_counter(conn_handle);
if (rc != 0) {
goto err;
}
/* Converting cmac to little-endian */
swap_in_place(cmac, sizeof(cmac));
/* Creating final signed message */
rc = os_mbuf_append(txom, (void *)&counter, sizeof(counter));
if (rc != 0) {
goto err;
}
rc = os_mbuf_copyinto(txom, OS_MBUF_PKTLEN(txom),
cmac + (sizeof(cmac)/2), sizeof(cmac)/2);
if (rc != 0) {
goto err;
}
if(message != NULL) nimble_platform_mem_free(message);
os_mbuf_concat(txom2, txom);
return ble_att_tx(conn_handle, txom2);
err:
if(message != NULL) nimble_platform_mem_free(message);
os_mbuf_free_chain(txom2);
return rc;
}
/*****************************************************************************
* $prepare write request *
*****************************************************************************/
+14
View File
@@ -353,6 +353,20 @@ struct ble_att_write_cmd {
uint8_t value[0];
} __attribute__((packed));
/**
* | Parameter | Size (octets) |
* +------------------------------------+-------------------+
* | Attribute Opcode | 1 |
* | Attribute Handle | 2 |
* | Attribute Value | 0 to (ATT_MTU-15) |
* | Authentication Signature | 12 |
*/
#define BLE_ATT_SIGNED_WRITE_CMD_BASE_SZ 15
#define BLE_ATT_SIGNED_WRITE_DATA_OFFSET 3
struct ble_att_signed_write_cmd {
uint16_t handle;
} __attribute__((packed));
void ble_att_error_rsp_parse(const void *payload, int len,
struct ble_att_error_rsp *rsp);
void ble_att_error_rsp_write(void *payload, int len,
+4
View File
@@ -212,6 +212,7 @@ int ble_att_svr_rx_read_mult(uint16_t conn_handle,
int ble_att_svr_rx_write(uint16_t conn_handle,
struct os_mbuf **rxom);
int ble_att_svr_rx_write_no_rsp(uint16_t conn_handle, struct os_mbuf **rxom);
int ble_att_svr_rx_signed_write(uint16_t conn_handle, struct os_mbuf **rxom);
int ble_att_svr_rx_prep_write(uint16_t conn_handle,
struct os_mbuf **rxom);
int ble_att_svr_rx_exec_write(uint16_t conn_handle,
@@ -298,6 +299,9 @@ int ble_att_clt_tx_prep_write(uint16_t conn_handle, uint16_t handle,
uint16_t offset, struct os_mbuf *txom);
int ble_att_clt_rx_prep_write(uint16_t conn_handle, struct os_mbuf **rxom);
int ble_att_clt_tx_exec_write(uint16_t conn_handle, uint8_t flags);
int ble_att_clt_tx_signed_write_cmd(uint16_t conn_handle, uint16_t handle,
uint8_t * csrk, uint32_t counter,
struct os_mbuf * txom);
int ble_att_clt_rx_exec_write(uint16_t conn_handle, struct os_mbuf **rxom);
int ble_att_clt_rx_write(uint16_t conn_handle, struct os_mbuf **rxom);
int ble_att_clt_tx_notify(uint16_t conn_handle, uint16_t handle,
+106 -1
View File
@@ -2348,6 +2348,111 @@ ble_att_svr_rx_write_no_rsp(uint16_t conn_handle, struct os_mbuf **rxom)
return ble_att_svr_write_handle(conn_handle, handle, 0, rxom, &att_err);
}
int
ble_att_svr_rx_signed_write(uint16_t conn_handle, struct os_mbuf **rxom)
{
#if !MYNEWT_VAL(BLE_ATT_SVR_SIGNED_WRITE)
return BLE_HS_ENOTSUP;
#endif
struct ble_att_signed_write_cmd *req;
struct ble_store_value_sec value_sec;
struct ble_store_key_sec key_sec;
struct ble_gap_conn_desc desc;
uint8_t att_err;
uint16_t handle;
uint8_t sign[12];
uint8_t cmac[16];
uint8_t *message = NULL;
int rc;
rc = ble_gap_conn_find(conn_handle, &desc);
if (rc != 0) {
goto err;
}
memset(&key_sec, 0, sizeof key_sec);
key_sec.peer_addr = desc.peer_id_addr;
/* Getting the CSRK for authentication */
rc = ble_store_read_peer_sec(&key_sec, &value_sec);
if (rc != 0) {
goto err;
}
if (value_sec.csrk_present != 1) {
rc = BLE_HS_EAUTHEN;
goto err;
}
rc = ble_att_svr_pullup_req_base(rxom, sizeof(*req), &att_err);
if (rc != 0) {
return rc;
}
req = (struct ble_att_signed_write_cmd *)(*rxom)->om_data;
handle = le16toh(req->handle);
/* Strip the request base from the front of the mbuf. */
os_mbuf_adj(*rxom, sizeof(*req));
os_mbuf_copydata(*rxom,
OS_MBUF_PKTLEN(*rxom) - (BLE_ATT_SIGNED_WRITE_CMD_BASE_SZ - BLE_ATT_SIGNED_WRITE_DATA_OFFSET),
BLE_ATT_SIGNED_WRITE_CMD_BASE_SZ - BLE_ATT_SIGNED_WRITE_DATA_OFFSET,
sign);
/* Strip the signature from the end of the mbuf. */
os_mbuf_adj(*rxom, -(BLE_ATT_SIGNED_WRITE_CMD_BASE_SZ - BLE_ATT_SIGNED_WRITE_DATA_OFFSET));
/* Authentication procedure */
message = nimble_platform_mem_malloc(OS_MBUF_PKTLEN(*rxom) + sizeof(value_sec.sign_counter));
os_mbuf_copydata(*rxom, 0, OS_MBUF_PKTLEN(*rxom), message);
memcpy(&message[OS_MBUF_PKTLEN(*rxom)], &value_sec.sign_counter, sizeof(value_sec.sign_counter));
/* Converting message into little endian format */
swap_in_place(message, OS_MBUF_PKTLEN(*rxom) + sizeof(value_sec.sign_counter));
/* Using AES-CMAC to get the CMAC from the message and CSRK of this device */
memset(cmac, 0, sizeof cmac);
rc = ble_sm_alg_aes_cmac(value_sec.csrk, message, OS_MBUF_PKTLEN(*rxom) + sizeof(value_sec.sign_counter), cmac);
if (rc != 0) {
goto err;
}
/* Converting cmac to little endian */
swap_in_place(cmac, sizeof cmac);
/* Comparing sign counter */
if(memcmp(sign, &value_sec.sign_counter, sizeof(value_sec.sign_counter)) != 0) {
rc = BLE_HS_EAUTHEN;
goto err;
}
/* Comparing signature */
if(memcmp(&sign[sizeof(value_sec.sign_counter)], &cmac[sizeof(cmac) / 2], sizeof(cmac) / 2) != 0) {
rc = BLE_HS_EAUTHEN;
goto err;
}
/* Signature matches, increment sign counter and pass the data to the upper layer */
rc = ble_sm_incr_peer_sign_counter(conn_handle);
if (rc != 0) {
goto err;
}
rc = ble_att_svr_write_handle(conn_handle, handle, 0, rxom, &att_err);
if (rc != 0) {
goto err;
}
if(message != NULL) nimble_platform_mem_free(message);
return 0;
err:
if(message != NULL) nimble_platform_mem_free(message);
ble_gap_terminate(conn_handle, BLE_ERR_AUTH_FAIL);
return rc;
}
int
ble_att_svr_write_local(uint16_t attr_handle, struct os_mbuf *om)
{
@@ -3026,7 +3131,7 @@ ble_att_svr_reset(void)
}
ble_att_svr_id = 0;
/* Note: prep entries do not get freed here because it is assumed there are
* no established connections.
*/
+2
View File
@@ -56,6 +56,8 @@ STATS_SECT_START(ble_gattc_stats)
STATS_SECT_ENTRY(read_long_fail)
STATS_SECT_ENTRY(read_mult)
STATS_SECT_ENTRY(read_mult_fail)
STATS_SECT_ENTRY(signed_write)
STATS_SECT_ENTRY(signed_write_fail)
STATS_SECT_ENTRY(write_no_rsp)
STATS_SECT_ENTRY(write_no_rsp_fail)
STATS_SECT_ENTRY(write)
+65
View File
@@ -457,6 +457,8 @@ STATS_NAME_START(ble_gattc_stats)
STATS_NAME(ble_gattc_stats, read_long_fail)
STATS_NAME(ble_gattc_stats, read_mult)
STATS_NAME(ble_gattc_stats, read_mult_fail)
STATS_NAME(ble_gattc_stats, signed_write)
STATS_NAME(ble_gattc_stats, signed_write_fail)
STATS_NAME(ble_gattc_stats, write_no_rsp)
STATS_NAME(ble_gattc_stats, write_no_rsp_fail)
STATS_NAME(ble_gattc_stats, write)
@@ -611,6 +613,13 @@ ble_gattc_log_write(uint16_t att_handle, uint16_t len, int expecting_rsp)
BLE_HS_LOG(INFO, "att_handle=%d len=%d\n", att_handle, len);
}
static void
ble_gattc_log_signed_write(uint16_t att_handle, uint16_t len)
{
ble_gattc_log_proc_init("signed write; ");
BLE_HS_LOG(INFO, "att_handle=%d len=%d\n", att_handle, len);
}
static void
ble_gattc_log_write_long(struct ble_gattc_proc *proc)
{
@@ -3495,6 +3504,62 @@ ble_gattc_write_no_rsp_flat(uint16_t conn_handle, uint16_t attr_handle,
return 0;
}
/*****************************************************************************
* $signed write *
****************************************************************************/
int
ble_gattc_signed_write(uint16_t conn_handle, uint16_t attr_handle,
struct os_mbuf *txom)
{
#if !MYNEWT_VAL(BLE_GATT_SIGNED_WRITE)
return BLE_HS_ENOTSUP;
#endif
int rc;
struct ble_store_value_sec value_sec;
struct ble_store_key_sec key_sec;
struct ble_gap_conn_desc desc;
STATS_INC(ble_gattc_stats, signed_write);
ble_gattc_log_signed_write(attr_handle, OS_MBUF_PKTLEN(txom));
rc = ble_gap_conn_find(conn_handle, &desc);
if (rc != 0) {
goto err;
}
if (desc.sec_state.encrypted == 1) {
rc = BLE_HS_EENCRYPT;
goto err;
}
memset(&key_sec, 0, sizeof key_sec);
key_sec.peer_addr = desc.peer_id_addr;
/* Getting the CSRK for signing */
rc = ble_store_read_our_sec(&key_sec, &value_sec);
if (rc != 0) {
goto err;
}
if (value_sec.csrk_present != 1) {
rc = BLE_HS_EAUTHEN;
goto err;
}
rc = ble_att_clt_tx_signed_write_cmd(conn_handle, attr_handle,
value_sec.csrk, value_sec.sign_counter, txom);
if (rc != 0) {
goto err;
}
return 0;
err:
STATS_INC(ble_gattc_stats, signed_write_fail);
os_mbuf_free_chain(txom);
return rc;
}
/*****************************************************************************
* $write *
*****************************************************************************/
+104
View File
@@ -492,6 +492,7 @@ ble_sm_fill_store_value(const ble_addr_t *peer_addr,
if (keys->csrk_valid) {
memcpy(value_sec->csrk, keys->csrk, sizeof value_sec->csrk);
value_sec->sign_counter = keys->sign_counter;
value_sec->csrk_present = 1;
}
}
@@ -2331,6 +2332,8 @@ ble_sm_key_exch_exec(struct ble_sm_proc *proc, struct ble_sm_result *res,
memcpy(sign_info->sig_key, proc->our_keys.csrk, 16);
}
proc->our_keys.csrk_valid = 1;
proc->our_keys.sign_counter = 0;
memcpy(proc->our_keys.csrk, sign_info->sig_key, 16);
rc = ble_sm_tx(proc->conn_handle, txom);
if (rc != 0) {
@@ -2532,6 +2535,7 @@ ble_sm_sign_info_rx(uint16_t conn_handle, struct os_mbuf **om,
memcpy(proc->peer_keys.csrk, cmd->sig_key, 16);
proc->peer_keys.csrk_valid = 1;
proc->peer_keys.sign_counter = 0;
ble_sm_key_rxed(proc, res);
}
@@ -2564,6 +2568,106 @@ ble_sm_fail_rx(uint16_t conn_handle, struct os_mbuf **om,
* $api *
*****************************************************************************/
/**
* API to be used to increment the sign-counter whenever the CSRK is used
* to sign a message.
*
* @param conn_handle The connection_handle of the peer to whom
* the signed message is sent, and with
* whom the CSRK was shared that was used
* to sign the message.
*/
int
ble_sm_incr_our_sign_counter(uint16_t conn_handle)
{
struct ble_store_key_sec key_sec;
struct ble_store_value_sec value_sec;
struct ble_gap_conn_desc desc;
int rc;
rc = ble_gap_conn_find(conn_handle, &desc);
if (rc != 0) {
return rc;
}
memset(&key_sec, 0, sizeof key_sec);
key_sec.peer_addr = desc.peer_id_addr;
rc = ble_store_read_our_sec(&key_sec, &value_sec);
if (rc != 0) {
return rc;
}
if (value_sec.csrk_present != 1) {
return BLE_HS_ENOENT;
}
if (value_sec.sign_counter == (uint32_t)0xffffffff) {
return BLE_HS_ENOMEM;
}
rc = ble_store_delete_our_sec(&key_sec);
if (rc != 0) {
return rc;
}
value_sec.sign_counter += 1;
rc = ble_store_write_our_sec(&value_sec);
if (rc != 0) {
return rc;
}
return 0;
}
/**
* API to be used to increment the sign-counter whenever the CSRK is used
* to authenticate a received signed message.
*
* @param conn_handle The connection_handle of the peer from whom
* the signed message is received, and with
* whom the CSRK was shared that was used
* to authenticate the signed message.
*/
int
ble_sm_incr_peer_sign_counter(uint16_t conn_handle)
{
struct ble_store_key_sec key_sec;
struct ble_store_value_sec value_sec;
struct ble_gap_conn_desc desc;
int rc;
rc = ble_gap_conn_find(conn_handle, &desc);
if (rc != 0) {
return rc;
}
memset(&key_sec, 0, sizeof key_sec);
key_sec.peer_addr = desc.peer_id_addr;
rc = ble_store_read_peer_sec(&key_sec, &value_sec);
if (rc != 0) {
return rc;
}
if (value_sec.csrk_present != 1) {
return BLE_HS_ENOENT;
}
if (value_sec.sign_counter == (uint32_t)0xffffffff) {
return BLE_HS_ENOMEM;
}
rc = ble_store_delete_peer_sec(&key_sec);
if (rc != 0) {
return rc;
}
value_sec.sign_counter += 1;
rc = ble_store_write_peer_sec(&value_sec);
if (rc != 0) {
return rc;
}
return 0;
}
/**
* Times out expired SM procedures.
*
+8
View File
@@ -234,6 +234,7 @@ struct ble_sm_keys {
unsigned irk_valid:1;
unsigned csrk_valid:1;
unsigned addr_valid:1;
uint32_t sign_counter;
uint16_t ediv;
uint64_t rand_val;
uint8_t addr_type;
@@ -391,6 +392,10 @@ void ble_sm_ia_ra(struct ble_sm_proc *proc,
uint8_t *out_iat, uint8_t *out_ia,
uint8_t *out_rat, uint8_t *out_ra);
int ble_sm_incr_our_sign_counter(uint16_t conn_handle);
int ble_sm_incr_peer_sign_counter(uint16_t conn_handle);
int ble_sm_alg_aes_cmac(const uint8_t *key, const uint8_t *in, size_t len,
uint8_t *out);
int32_t ble_sm_timer(void);
void ble_sm_connection_broken(uint16_t conn_handle);
int ble_sm_pair_initiate(uint16_t conn_handle);
@@ -403,6 +408,9 @@ int ble_sm_alg_encrypt(const uint8_t *key, const uint8_t *plaintext,
int ble_sm_init(void);
#else
#define ble_sm_incr_our_sign_counter(conn_handle) BLE_HS_ENOTSUP
#define ble_sm_incr_peer_sign_counter(conn_handle) BLE_HS_ENOTSUP
#define ble_sm_alg_aes_cmac(key, in, len, out) BLE_HS_ENOTSUP
#define ble_sm_enc_change_rx(evt) ((void)(evt))
#define ble_sm_ltk_req_rx(evt) ((void)(evt))
#define ble_sm_enc_key_refresh_rx(evt) ((void)(evt))
@@ -84,7 +84,7 @@ ble_store_config_print_value_sec(const struct ble_store_value_sec *sec)
if (sec->csrk_present) {
BLE_HS_LOG(DEBUG, "csrk=");
ble_hs_log_flat_buf(sec->csrk, 16);
BLE_HS_LOG(DEBUG, " ");
BLE_HS_LOG(DEBUG, " sign_counter = %u", sec->sign_counter);
}
BLE_HS_LOG(DEBUG, "\n");
+4
View File
@@ -81,6 +81,10 @@ extern "C" {
#define NIMBLE_BLE_ATT_CLT_READ_GROUP_TYPE \
(MYNEWT_VAL(BLE_GATT_DISC_ALL_SVCS))
#undef NIMBLE_BLE_ATT_CLT_SIGNED_WRITE
#define NIMBLE_BLE_ATT_CLT_SIGNED_WRITE \
(MYNEWT_VAL(BLE_GATT_SIGNED_WRITE))
#undef NIMBLE_BLE_ATT_CLT_WRITE
#define NIMBLE_BLE_ATT_CLT_WRITE \
(MYNEWT_VAL(BLE_GATT_WRITE))