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) 2017 Intel Corporation.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#if defined(CONFIG_NET_DEBUG_L2_IEEE802154)
#define SYS_LOG_DOMAIN "net/ieee802154"
#define NET_LOG_ENABLED 1
#endif

#include <crypto/cipher.h>
#include <net/net_core.h>

#include "ieee802154_frame.h"
#include "ieee802154_security.h"

extern const u8_t level_2_tag_size[4];

int ieee802154_security_setup_session(struct ieee802154_security_ctx *sec_ctx,
				      u8_t level, u8_t key_mode,
				      u8_t *key, u8_t key_len)
{
	u8_t tag_size;

	if (level > IEEE802154_SECURITY_LEVEL_ENC_MIC_128 ||
	    key_mode > IEEE802154_KEY_ID_MODE_SRC_8_INDEX) {
		return -EINVAL;
	}

	/* ToDo: supporting other key modes */
	if (level > IEEE802154_SECURITY_LEVEL_NONE &&
	    (key_len > IEEE802154_KEY_MAX_LEN || !key ||
	     key_mode != IEEE802154_KEY_ID_MODE_IMPLICIT)) {
		return -EINVAL;
	}

	if (level >= IEEE802154_SECURITY_LEVEL_ENC) {
		tag_size = level_2_tag_size[level - 4];
	} else {
		tag_size = level_2_tag_size[level];
	}

	sec_ctx->level = level;

	if (level > IEEE802154_SECURITY_LEVEL_NONE) {
		memcpy(sec_ctx->key, key, key_len);
		sec_ctx->key_len = key_len;
		sec_ctx->key_mode = key_mode;

		sec_ctx->enc.key.bit_stream = sec_ctx->key;
		sec_ctx->enc.keylen = sec_ctx->key_len;

		sec_ctx->dec.key.bit_stream = sec_ctx->key;
		sec_ctx->dec.keylen = sec_ctx->key_len;
	}

	sec_ctx->enc.mode_params.ccm_info.tag_len = tag_size;
	sec_ctx->dec.mode_params.ccm_info.tag_len = tag_size;

	return 0;
}

bool ieee802154_decrypt_auth(struct ieee802154_security_ctx *sec_ctx,
			     u8_t *frame,
			     u8_t auth_payload_len,
			     u8_t decrypt_payload_len,
			     u8_t *src_ext_addr,
			     u32_t frame_counter)
{
	struct cipher_aead_pkt apkt;
	struct cipher_pkt pkt;
	u8_t nonce[13];
	int ret;

	if (!sec_ctx || sec_ctx->level == IEEE802154_SECURITY_LEVEL_NONE) {
		return true;
	}

	/* See Section 7.3.2 */
	memcpy(nonce, src_ext_addr, IEEE802154_EXT_ADDR_LENGTH);
	nonce[8] = (u8_t)(frame_counter >> 24);
	nonce[9] = (u8_t)(frame_counter >> 16);
	nonce[10] = (u8_t)(frame_counter >> 8);
	nonce[11] = (u8_t)frame_counter;
	nonce[12] = sec_ctx->level;

	pkt.in_buf = decrypt_payload_len ? frame + auth_payload_len : NULL;
	pkt.in_len = decrypt_payload_len;
	pkt.out_buf = frame;
	pkt.out_buf_max = IEEE802154_MTU - IEEE802154_MFR_LENGTH;

	apkt.ad = frame;
	apkt.ad_len = auth_payload_len;
	apkt.tag = sec_ctx->dec.mode_params.ccm_info.tag_len ?
		frame + auth_payload_len + decrypt_payload_len : NULL;
	apkt.pkt = &pkt;

	ret = cipher_ccm_op(&sec_ctx->dec, &apkt, nonce);
	if (ret) {
		NET_ERR("Cannot decrypt/auth (%i): %p %u/%u - fc %u",
			ret, frame, auth_payload_len, decrypt_payload_len,
			frame_counter);
		return false;
	}

	return true;
}

bool ieee802154_encrypt_auth(struct ieee802154_security_ctx *sec_ctx,
			     u8_t *frame,
			     u8_t auth_payload_len,
			     u8_t encrypt_payload_len,
			     u8_t *src_ext_addr)
{
	struct cipher_aead_pkt apkt;
	struct cipher_pkt pkt;
	u8_t nonce[13];
	int ret;

	if (!sec_ctx || sec_ctx->level == IEEE802154_SECURITY_LEVEL_NONE) {
		return true;
	}

	/* See Section 7.3.2 */
	memcpy(nonce, src_ext_addr, IEEE802154_EXT_ADDR_LENGTH);
	sys_put_be32(sec_ctx->frame_counter, &nonce[8]);
	nonce[12] = sec_ctx->level;

	pkt.in_buf = encrypt_payload_len ? frame + auth_payload_len : NULL;
	pkt.in_len = encrypt_payload_len;
	pkt.out_buf = frame;
	pkt.out_buf_max = IEEE802154_MTU - IEEE802154_MFR_LENGTH;

	apkt.ad = frame;
	apkt.ad_len = auth_payload_len;
	apkt.tag = NULL;
	apkt.pkt = &pkt;

	ret = cipher_ccm_op(&sec_ctx->enc, &apkt, nonce);
	if (ret) {
		NET_ERR("Cannot encrypt/auth (%i): %p %u/%u - fc %u",
			ret, frame, auth_payload_len, encrypt_payload_len,
			sec_ctx->frame_counter);
		return false;
	}

	sec_ctx->frame_counter++;

	return true;
}

int ieee802154_security_init(struct ieee802154_security_ctx *sec_ctx)
{
	struct device *dev;
	int ret;

	memset(&sec_ctx->enc, 0, sizeof(struct cipher_ctx));
	memset(&sec_ctx->dec, 0, sizeof(struct cipher_ctx));

	dev = device_get_binding(
		CONFIG_NET_L2_IEEE802154_SECURITY_CRYPTO_DEV_NAME);
	if (!dev) {
		return -ENODEV;
	}

	sec_ctx->enc.flags = cipher_query_hwcaps(dev);
	sec_ctx->dec.flags = cipher_query_hwcaps(dev);

	sec_ctx->enc.mode_params.ccm_info.nonce_len = 13;
	sec_ctx->dec.mode_params.ccm_info.nonce_len = 13;

	ret = cipher_begin_session(dev, &sec_ctx->enc,
				   CRYPTO_CIPHER_ALGO_AES,
				   CRYPTO_CIPHER_MODE_CCM,
				   CRYPTO_CIPHER_OP_ENCRYPT);
	if (ret) {
		NET_ERR("Could not setup encryption context");

		return ret;
	}

	ret = cipher_begin_session(dev, &sec_ctx->dec,
				   CRYPTO_CIPHER_ALGO_AES,
				   CRYPTO_CIPHER_MODE_CCM,
				   CRYPTO_CIPHER_OP_DECRYPT);
	if (ret) {
		NET_ERR("Could not setup decryption context");
		cipher_free_session(dev, &sec_ctx->enc);

		return ret;
	}

	return 0;
}