blob: 56f8299c68e9cfabb3b944b40f4a2e6d14be588c [file] [log] [blame]
/*
* This file is part of the UWB stack for linux.
*
* Copyright (c) 2020-2021 Qorvo US, Inc.
*
* This software is provided under the GNU General Public License, version 2
* (GPLv2), as well as under a Qorvo commercial license.
*
* You may choose to use this software under the terms of the GPLv2 License,
* version 2 ("GPLv2"), as published by the Free Software Foundation.
* You should have received a copy of the GPLv2 along with this program. If
* not, see <http://www.gnu.org/licenses/>.
*
* This program is distributed under the GPLv2 in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GPLv2 for more
* details.
*
* If you cannot meet the requirements of the GPLv2, you may not use this
* software for any purpose without first obtaining a commercial license from
* Qorvo. Please contact Qorvo to inquire about licensing terms.
*/
#include "fira_crypto.h"
#include "fira_cmac.h"
#include "fira_region.h"
#include "fira_session.h"
#include <asm/unaligned.h>
#include <linux/errno.h>
#include <linux/printk.h>
#include <linux/skbuff.h>
#include <linux/string.h>
#include <net/mcps802154_frame.h>
#define FIRA_STATIC_STS_SESSION_KEY "StaticTSStaticTS"
/**
* fira_crypto_config_digest() - Compute session config digest.
* @local: FiRa context.
* @session: Session.
*
* Return: 0 or error.
*/
static int fira_crypto_config_digest(struct fira_local *local,
struct fira_session *session)
{
u8 derivation_data[17];
u8 *p = derivation_data;
int slot_duration_us;
static const u8 zero_key[AES_KEYSIZE_128];
const struct mcps802154_channel *channel;
channel = mcps802154_get_current_channel(local->llhw);
slot_duration_us = session->params.slot_duration_dtu * 1000 /
(local->llhw->dtu_freq_hz / 1000);
*p++ = session->params.ranging_round_usage;
*p++ = session->params.sts_config;
*p++ = session->params.multi_node_mode;
*p++ = session->params.channel_number ?: channel->channel;
put_unaligned_be16(slot_duration_us, p);
p += sizeof(u16);
*p++ = session->params.mac_fcs_type;
*p++ = session->params.rframe_config;
*p++ = session->params.preamble_code_index ?: channel->preamble_code;
*p++ = session->params.sfd_id;
*p++ = session->params.psdu_data_rate;
*p++ = session->params.preamble_duration;
*p++ = 3; /* Constant. */
put_unaligned_be32(session->id, p);
return fira_digest(zero_key, sizeof(zero_key), derivation_data,
sizeof(derivation_data),
session->crypto.config_digest);
}
int fira_crypto_derive_per_session(struct fira_local *local,
struct fira_session *session)
{
struct fira_crypto *crypto = &session->crypto;
u8 sts_index_init_tmp[AES_KEYSIZE_128];
int r;
if (session->params.sts_config != FIRA_STS_CONFIG_STATIC)
return -EOPNOTSUPP;
r = fira_crypto_config_digest(local, session);
if (r)
goto out;
memcpy(session->crypto.session_key, FIRA_STATIC_STS_SESSION_KEY,
AES_KEYSIZE_128);
session->crypto.key_size = AES_KEYSIZE_128;
r = fira_kdf(crypto->session_key, crypto->key_size, "DataPrtK",
crypto->config_digest, crypto->data_protection_key,
crypto->key_size);
if (r)
goto out;
r = fira_kdf(crypto->data_protection_key, crypto->key_size, "StsIndIn",
crypto->config_digest, sts_index_init_tmp,
AES_KEYSIZE_128);
if (r)
goto out;
crypto->sts_index_init =
get_unaligned_be32(sts_index_init_tmp + AES_KEYSIZE_128 -
sizeof(u32)) &
0x7fffffff;
out:
memzero_explicit(sts_index_init_tmp, sizeof(sts_index_init_tmp));
return r;
}
int fira_crypto_derive_per_rotation(struct fira_local *local,
struct fira_session *session, u32 sts_index)
{
struct fira_crypto *crypto = &session->crypto;
u8 context[AES_BLOCK_SIZE];
u8 derived_authentication_iv[AES_BLOCK_SIZE];
u32 crypto_sts_index;
u32 sts_v_counter;
u8 *sts_v;
int r;
/* Zero for Static STS. */
crypto_sts_index = 0;
memcpy(context, crypto->config_digest + sizeof(u32),
AES_BLOCK_SIZE - sizeof(u32));
put_unaligned_be32(crypto_sts_index,
context + AES_BLOCK_SIZE - sizeof(u32));
r = fira_kdf(crypto->data_protection_key, crypto->key_size, "DerAuthI",
context, derived_authentication_iv, AES_KEYSIZE_128);
if (r)
goto out;
sts_v = crypto->sts_v;
memcpy(sts_v, session->params.vupper64, FIRA_VUPPER64_SIZE);
sts_v += FIRA_VUPPER64_SIZE;
memset(sts_v, 0, sizeof(u32));
sts_v += sizeof(u32);
sts_v_counter = get_unaligned_be32(derived_authentication_iv +
AES_BLOCK_SIZE - sizeof(u32)) &
0x7fffffff;
put_unaligned_be32(sts_v_counter, sts_v);
r = fira_kdf(crypto->data_protection_key, crypto->key_size, "DerAuthK",
context, crypto->derived_authentication_key,
AES_KEYSIZE_128);
if (r)
goto out;
r = fira_kdf(crypto->data_protection_key, crypto->key_size, "DerPaylK",
context, crypto->derived_payload_key, AES_KEYSIZE_128);
if (r)
goto out;
r = fira_aead_set_key(&crypto->aead, crypto->derived_payload_key);
out:
memzero_explicit(context, sizeof(context));
memzero_explicit(derived_authentication_iv,
sizeof(derived_authentication_iv));
return r;
}
#ifndef CONFIG_MCPS802154_DISABLE_AUTO_TEST
int fira_crypto_test(void)
{
/* LCOV_EXCL_START */
static const u8 zero_key[AES_KEYSIZE_128];
struct sk_buff *skb = NULL;
int r;
struct fira_round_hopping_sequence round_hopping_sequence;
static const u8 digest_data[] = { 0x02, 0x00, 0x00, 0x09, 0x07, 0xD0,
0x00, 0x03, 0x0a, 0x02, 0x00, 0x01,
0x03, 0x01, 0x23, 0x45, 0x67 };
u8 digest[AES_BLOCK_SIZE];
static const u8 digest_expect[] = { 0xa0, 0x43, 0x90, 0xcf, 0x8a, 0x33,
0xf6, 0xeb, 0x7e, 0x2f, 0xc3, 0x78,
0x87, 0xb6, 0xb2, 0xa3 };
static const u8 frame_key[] = { 0xa5, 0x5f, 0xab, 0x83, 0xb6, 0x20,
0xf9, 0xf6, 0xa4, 0x7c, 0xdb, 0x72,
0x91, 0x7c, 0x73, 0x8a };
static const u8 frame[] = {
0x49, 0x2b, 0xa2, 0xaa, 0x20, 0x13, 0x00, 0xff, 0x18, 0x5a,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x67, 0x45,
0x23, 0x01, 0x78, 0xbe, 0x9b, 0x0b, 0x00, 0x3f, 0x1b, 0x90,
0xff, 0x18, 0x5a, 0x03, 0x05, 0x00, 0x00, 0x03, 0x42, 0x55,
0x01, 0x04, 0x44, 0x55, 0x03, 0x07, 0x42, 0x55, 0x05, 0x09,
0x42, 0x55, 0x09, 0x0a, 0x44, 0x55, 0x0b
};
static const u8 frame_enc[] = {
0x49, 0x2b, 0xa2, 0xaa, 0x26, 0x13, 0x00, 0xff, 0x18, 0x5a,
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x67, 0x45,
0x23, 0x01, 0x78, 0xbe, 0x9b, 0x0b, 0x00, 0x3f, 0xcb, 0xa4,
0xfd, 0x37, 0xd1, 0x99, 0x44, 0x88, 0x7c, 0x2b, 0xec, 0x2e,
0x1a, 0x99, 0x8e, 0x80, 0x61, 0x7c, 0x44, 0xb5, 0xe8, 0xe3,
0xf3, 0x35, 0x3a, 0xb9, 0xf2, 0x29, 0x1b, 0x80, 0x4b, 0xba,
0xe1, 0xa9, 0x2a, 0x20, 0x28
};
const unsigned int frame_header_len = 28;
const __le16 frame_src_short_addr = 0xaaa1;
const u32 frame_counter = 0;
struct fira_aead aead = {};
/* Test digest. */
r = fira_digest(zero_key, AES_KEYSIZE_128, digest_data,
sizeof(digest_data), digest);
if (r != 0 || memcmp(digest, digest_expect, sizeof(digest)) != 0) {
pr_err("fira_digest test failed: r = %d\n", r);
print_hex_dump(KERN_ERR, "digest: ", DUMP_PREFIX_NONE,
16, 1, digest, sizeof(digest), false);
print_hex_dump(KERN_ERR, "digest_expect: ", DUMP_PREFIX_NONE,
16, 1, digest_expect, sizeof(digest_expect),
false);
return -EINVAL;
}
/* Test AEAD. */
r = fira_aead_set_key(&aead, frame_key);
if (r != 0) {
pr_err("fira_aead_set_key test failed: r = %d\n", r);
return -EINVAL;
}
/* AEAD enc. */
skb = alloc_skb(sizeof(frame_enc), GFP_KERNEL);
if (!skb) {
r = -ENOMEM;
goto out;
}
skb_put_data(skb, frame, sizeof(frame));
r = fira_aead_encrypt(&aead, skb, frame_header_len,
frame_src_short_addr, frame_counter);
if (r != 0 || skb->len != sizeof(frame_enc) ||
memcmp(skb->data, frame_enc, sizeof(frame_enc)) != 0) {
pr_err("fira_aead_encrypt test failed: r = %d\n", r);
print_hex_dump(KERN_ERR, "frame_enc: ", DUMP_PREFIX_NONE, 16, 1,
skb->data, skb->len, false);
print_hex_dump(KERN_ERR, "expect: ", DUMP_PREFIX_NONE, 16, 1,
frame_enc, sizeof(frame_enc), false);
r = -EINVAL;
goto out;
}
/* AEAD dec. */
kfree_skb(skb);
skb = alloc_skb(sizeof(frame_enc), GFP_KERNEL);
if (!skb) {
r = -ENOMEM;
goto out;
}
skb_put_data(skb, frame_enc, sizeof(frame_enc));
skb_pull(skb, frame_header_len);
/* Prepare cannot fail. */
fira_aead_decrypt_prepare(skb);
r = fira_aead_decrypt(&aead, skb, frame_header_len,
frame_src_short_addr, frame_counter);
skb_push(skb, frame_header_len);
if (r != 0 || skb->len != sizeof(frame) ||
memcmp(skb->data, frame, sizeof(frame)) != 0) {
pr_err("fira_aead_decrypt test failed: r = %d\n", r);
print_hex_dump(KERN_ERR, "frame: ", DUMP_PREFIX_NONE, 16, 1,
skb->data, skb->len, false);
print_hex_dump(KERN_ERR, "expect: ", DUMP_PREFIX_NONE, 16, 1,
frame, sizeof(frame), false);
r = -EINVAL;
goto out;
}
/* AEAD dec bad tag. */
kfree_skb(skb);
skb = alloc_skb(sizeof(frame_enc), GFP_KERNEL);
if (!skb) {
r = -ENOMEM;
goto out;
}
skb_put_data(skb, frame_enc, sizeof(frame_enc));
skb_pull(skb, frame_header_len);
skb->data[skb->len - 1]++;
/* Prepare cannot fail. */
fira_aead_decrypt_prepare(skb);
r = fira_aead_decrypt(&aead, skb, frame_header_len,
frame_src_short_addr, frame_counter);
if (r != -EBADMSG) {
pr_err("fira_aead_decrypt bad msg test failed: r = %d\n", r);
r = -EINVAL;
goto out;
}
/* Test ecb(aes) presence for hopping. */
r = fira_round_hopping_crypto_init(&round_hopping_sequence);
if (r)
goto out;
fira_round_hopping_crypto_destroy(&round_hopping_sequence);
r = 0;
out:
kfree_skb(skb);
fira_aead_destroy(&aead);
/* LCOV_EXCL_STOP */
return r;
}
#endif /* !CONFIG_MCPS802154_DISABLE_AUTO_TEST */