Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/* Copyright (c) 2023 Nordic Semiconductor ASA
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/bluetooth/ead.h>
#include <zephyr/bluetooth/crypto.h>
#include <zephyr/bluetooth/bluetooth.h>

#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include <zephyr/sys/check.h>

#include <zephyr/logging/log.h>

/** nonce size in bytes */
#define BT_EAD_NONCE_SIZE 13

/* This value is used to set the directionBit of the CCM  nonce to the MSB of the Randomizer field
 * (see Supplement to the Bluetooth Core   Specification v11, Part A 1.23.3)
 */
#define BT_EAD_RANDOMIZER_DIRECTION_BIT 7

/** Additional Authenticated Data size in bytes */
#define BT_EAD_AAD_SIZE 1

/* Fixed value used for the Additional Authenticated Data (see Supplement to the Bluetooth Core
 * Specification v11, Part A 1.23.3)
 */
static uint8_t bt_ead_aad[] = {0xEA};
BUILD_ASSERT(sizeof(bt_ead_aad) == BT_EAD_AAD_SIZE);

LOG_MODULE_REGISTER(bt_encrypted_ad_data, CONFIG_BT_EAD_LOG_LEVEL);

static int bt_ead_generate_randomizer(uint8_t randomizer[BT_EAD_RANDOMIZER_SIZE])
{
	int err;

	err = bt_rand(randomizer, BT_EAD_RANDOMIZER_SIZE);

	if (err != 0) {
		return -ECANCELED;
	}

	/* From Supplement to the Bluetooth Core Specification v11, Part A 1.23.3: The directionBit
	 * of the CCM nonce shall be set to the most significant bit of the Randomizer field.
	 */
	randomizer[4] |= 1 << BT_EAD_RANDOMIZER_DIRECTION_BIT;

	return 0;
}

static int bt_ead_generate_nonce(const uint8_t iv[BT_EAD_IV_SIZE],
				 const uint8_t randomizer[BT_EAD_RANDOMIZER_SIZE], uint8_t *nonce)
{
	uint8_t new_randomizer[BT_EAD_RANDOMIZER_SIZE];

	if (randomizer == NULL) {
		int err;

		err = bt_ead_generate_randomizer(new_randomizer);

		if (err != 0) {
			LOG_DBG("Failed to generate Randomizer");
			return -ECANCELED;
		}

		randomizer = new_randomizer;
	}

	memcpy(&nonce[0], randomizer, BT_EAD_RANDOMIZER_SIZE);
	memcpy(&nonce[BT_EAD_RANDOMIZER_SIZE], iv, BT_EAD_IV_SIZE);

	return 0;
}

static int ead_encrypt(const uint8_t session_key[BT_EAD_KEY_SIZE], const uint8_t iv[BT_EAD_IV_SIZE],
		       const uint8_t randomizer[BT_EAD_RANDOMIZER_SIZE], const uint8_t *payload,
		       size_t payload_size, uint8_t *encrypted_payload)
{
	int err;
	uint8_t nonce[BT_EAD_NONCE_SIZE];
	size_t ead_size = BT_EAD_RANDOMIZER_SIZE + payload_size + BT_EAD_MIC_SIZE;

	err = bt_ead_generate_nonce(iv, randomizer, nonce);
	if (err != 0) {
		return -ECANCELED;
	}

	memcpy(encrypted_payload, nonce, BT_EAD_RANDOMIZER_SIZE);

	err = bt_ccm_encrypt(session_key, nonce, payload, payload_size, bt_ead_aad, BT_EAD_AAD_SIZE,
			     &encrypted_payload[BT_EAD_RANDOMIZER_SIZE], BT_EAD_MIC_SIZE);
	if (err != 0) {
		LOG_DBG("Failed to encrypt the payload (bt_ccm_encrypt err %d)", err);
		return -EIO;
	}

	LOG_HEXDUMP_DBG(encrypted_payload, ead_size, "Encrypted Data: ");

	return 0;
}

int bt_ead_encrypt(const uint8_t session_key[BT_EAD_KEY_SIZE], const uint8_t iv[BT_EAD_IV_SIZE],
		   const uint8_t *payload, size_t payload_size, uint8_t *encrypted_payload)
{
	CHECKIF(session_key == NULL) {
		LOG_DBG("session_key is NULL");
		return -EINVAL;
	}

	CHECKIF(iv == NULL) {
		LOG_DBG("iv is NULL");
		return -EINVAL;
	}

	CHECKIF(payload == NULL) {
		LOG_DBG("payload is NULL");
		return -EINVAL;
	}

	CHECKIF(encrypted_payload == NULL) {
		LOG_DBG("encrypted_payload is NULL");
		return -EINVAL;
	}

	if (payload_size == 0) {
		LOG_WRN("payload_size is set to 0. The encrypted result will only contain the "
			"Randomizer and the MIC.");
	}

	return ead_encrypt(session_key, iv, NULL, payload, payload_size, encrypted_payload);
}

#if defined(CONFIG_BT_TESTING)

int bt_test_ead_encrypt(const uint8_t session_key[BT_EAD_KEY_SIZE],
			const uint8_t iv[BT_EAD_IV_SIZE],
			const uint8_t randomizer[BT_EAD_RANDOMIZER_SIZE], const uint8_t *payload,
			size_t payload_size, uint8_t *encrypted_payload)
{
	CHECKIF(session_key == NULL) {
		LOG_DBG("session_key is NULL");
		return -EINVAL;
	}

	CHECKIF(iv == NULL) {
		LOG_DBG("iv is NULL");
		return -EINVAL;
	}

	CHECKIF(randomizer == NULL) {
		LOG_DBG("randomizer is NULL");
		return -EINVAL;
	}

	CHECKIF(payload == NULL) {
		LOG_DBG("payload is NULL");
		return -EINVAL;
	}

	CHECKIF(encrypted_payload == NULL) {
		LOG_DBG("encrypted_payload is NULL");
		return -EINVAL;
	}

	if (payload_size == 0) {
		LOG_WRN("payload_size is set to 0. The encrypted result will be filled with only "
			"the Randomizer and the MIC.");
	}

	return ead_encrypt(session_key, iv, randomizer, payload, payload_size, encrypted_payload);
}

#endif /* CONFIG_BT_TESTING */

static int ead_decrypt(const uint8_t session_key[BT_EAD_KEY_SIZE], const uint8_t iv[BT_EAD_IV_SIZE],
		       const uint8_t *encrypted_payload, size_t encrypted_payload_size,
		       uint8_t *payload)
{
	int err;
	uint8_t nonce[BT_EAD_NONCE_SIZE];
	const uint8_t *encrypted_ad_data = &encrypted_payload[BT_EAD_RANDOMIZER_SIZE];
	size_t encrypted_ad_data_size = encrypted_payload_size - BT_EAD_RANDOMIZER_SIZE;
	size_t payload_size = encrypted_ad_data_size - BT_EAD_MIC_SIZE;

	const uint8_t *randomizer = encrypted_payload;

	err = bt_ead_generate_nonce(iv, randomizer, nonce);
	if (err != 0) {
		return -EIO;
	}

	LOG_HEXDUMP_DBG(encrypted_ad_data, encrypted_ad_data_size, "Encrypted Data: ");

	err = bt_ccm_decrypt(session_key, nonce, encrypted_ad_data, payload_size, bt_ead_aad,
			     BT_EAD_AAD_SIZE, payload, BT_EAD_MIC_SIZE);
	LOG_HEXDUMP_DBG(payload, payload_size, "Decrypted Data: ");
	if (err != 0) {
		LOG_DBG("Failed to decrypt the data (bt_ccm_decrypt err %d)", err);
		return -EIO;
	}

	return 0;
}

int bt_ead_decrypt(const uint8_t session_key[BT_EAD_KEY_SIZE], const uint8_t iv[BT_EAD_IV_SIZE],
		   const uint8_t *encrypted_payload, size_t encrypted_payload_size,
		   uint8_t *payload)
{
	CHECKIF(session_key == NULL) {
		LOG_DBG("session_key is NULL");
		return -EINVAL;
	}

	CHECKIF(iv == NULL) {
		LOG_DBG("iv is NULL");
		return -EINVAL;
	}

	CHECKIF(encrypted_payload == NULL) {
		LOG_DBG("encrypted_payload is NULL");
		return -EINVAL;
	}

	CHECKIF(payload == NULL) {
		LOG_DBG("payload is NULL");
		return -EINVAL;
	}

	if (encrypted_payload_size < BT_EAD_RANDOMIZER_SIZE + BT_EAD_MIC_SIZE) {
		LOG_DBG("encrypted_payload_size is not large enough.");
		return -EINVAL;
	} else if (encrypted_payload_size == BT_EAD_RANDOMIZER_SIZE + BT_EAD_MIC_SIZE) {
		LOG_WRN("encrypted_payload_size not large enough to contain encrypted data.");
	}

	return ead_decrypt(session_key, iv, encrypted_payload, encrypted_payload_size, payload);
}