blob: d2b3aa5bf2f41ab989b3fcf7e04406f3a1b03c1e [file] [log] [blame]
/*
* Copyright (c) 2021 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mesh_test.h"
#include "settings_test_backend.h"
#include "mesh/mesh.h"
#include "mesh/net.h"
#define LOG_MODULE_NAME test_rpc
#include <logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_INF);
#define WAIT_TIME 60 /*seconds*/
#define TEST_DATA_WAITING_TIME 5 /* seconds */
#define TEST_DATA_SIZE 20
static const struct bt_mesh_test_cfg tx_cfg = {
.addr = 0x0001,
.dev_key = { 0x01 },
};
static const struct bt_mesh_test_cfg rx_cfg = {
.addr = 0x0002,
.dev_key = { 0x02 },
};
static uint8_t test_data[TEST_DATA_SIZE];
static uint8_t rx_cnt;
static bool is_tx_succeeded;
static void test_tx_init(void)
{
bt_mesh_test_cfg_set(&tx_cfg, WAIT_TIME);
}
static void test_rx_init(void)
{
bt_mesh_test_cfg_set(&rx_cfg, WAIT_TIME);
}
static void tx_started(uint16_t dur, int err, void *data)
{
if (err) {
FAIL("Couldn't start sending (err: %d)", err);
}
LOG_INF("Sending started");
}
static void tx_ended(int err, void *data)
{
struct k_sem *sem = data;
if (err) {
is_tx_succeeded = false;
LOG_INF("Sending failed (%d)", err);
} else {
is_tx_succeeded = true;
LOG_INF("Sending succeeded");
}
k_sem_give(sem);
}
static void rx_ended(uint8_t *data, size_t len)
{
memset(test_data, rx_cnt++, sizeof(test_data));
if (memcmp(test_data, data, len)) {
FAIL("Unexpected rx data");
}
LOG_INF("Receiving succeeded");
}
static void test_tx_immediate_replay_attack(void)
{
settings_test_backend_clear();
bt_mesh_test_setup();
static const struct bt_mesh_send_cb send_cb = {
.start = tx_started,
.end = tx_ended,
};
struct k_sem sem;
k_sem_init(&sem, 0, 1);
uint32_t seq = bt_mesh.seq;
for (int i = 0; i < 3; i++) {
is_tx_succeeded = false;
memset(test_data, i, sizeof(test_data));
ASSERT_OK(bt_mesh_test_send_ra(rx_cfg.addr, test_data,
sizeof(test_data), &send_cb, &sem));
if (k_sem_take(&sem, K_SECONDS(TEST_DATA_WAITING_TIME))) {
LOG_ERR("Send timed out");
}
ASSERT_TRUE(is_tx_succeeded);
}
bt_mesh.seq = seq;
for (int i = 0; i < 3; i++) {
is_tx_succeeded = true;
memset(test_data, i, sizeof(test_data));
ASSERT_OK(bt_mesh_test_send_ra(rx_cfg.addr, test_data,
sizeof(test_data), &send_cb, &sem));
if (k_sem_take(&sem, K_SECONDS(TEST_DATA_WAITING_TIME))) {
LOG_ERR("Send timed out");
}
ASSERT_TRUE(!is_tx_succeeded);
}
PASS();
}
static void test_rx_immediate_replay_attack(void)
{
settings_test_backend_clear();
bt_mesh_test_setup();
bt_mesh_test_ra_cb_setup(rx_ended);
k_sleep(K_SECONDS(6 * TEST_DATA_WAITING_TIME));
ASSERT_TRUE(rx_cnt == 3, "Device didn't receive expected data");
PASS();
}
static void test_tx_power_replay_attack(void)
{
settings_test_backend_clear();
bt_mesh_test_setup();
static const struct bt_mesh_send_cb send_cb = {
.start = tx_started,
.end = tx_ended,
};
struct k_sem sem;
k_sem_init(&sem, 0, 1);
for (int i = 0; i < 3; i++) {
is_tx_succeeded = true;
memset(test_data, i, sizeof(test_data));
ASSERT_OK(bt_mesh_test_send_ra(rx_cfg.addr, test_data,
sizeof(test_data), &send_cb, &sem));
if (k_sem_take(&sem, K_SECONDS(TEST_DATA_WAITING_TIME))) {
LOG_ERR("Send timed out");
}
ASSERT_TRUE(!is_tx_succeeded);
}
for (int i = 0; i < 3; i++) {
is_tx_succeeded = false;
memset(test_data, i, sizeof(test_data));
ASSERT_OK(bt_mesh_test_send_ra(rx_cfg.addr, test_data,
sizeof(test_data), &send_cb, &sem));
if (k_sem_take(&sem, K_SECONDS(TEST_DATA_WAITING_TIME))) {
LOG_ERR("Send timed out");
}
ASSERT_TRUE(is_tx_succeeded);
}
PASS();
}
static void test_rx_power_replay_attack(void)
{
bt_mesh_test_setup();
bt_mesh_test_ra_cb_setup(rx_ended);
k_sleep(K_SECONDS(6 * TEST_DATA_WAITING_TIME));
ASSERT_TRUE(rx_cnt == 3, "Device didn't receive expected data");
PASS();
}
#define TEST_CASE(role, name, description) \
{ \
.test_id = "rpc_" #role "_" #name, \
.test_descr = description, \
.test_post_init_f = test_##role##_init, \
.test_tick_f = bt_mesh_test_timeout, \
.test_main_f = test_##role##_##name, \
}
static const struct bst_test_instance test_rpc[] = {
TEST_CASE(tx, immediate_replay_attack, "RPC: perform replay attack immediately"),
TEST_CASE(tx, power_replay_attack, "RPC: perform replay attack after power cycle"),
TEST_CASE(rx, immediate_replay_attack, "RPC: device under immediate attack"),
TEST_CASE(rx, power_replay_attack, "RPC: device under power cycle reply attack"),
BSTEST_END_MARKER
};
struct bst_test_list *test_rpc_install(struct bst_test_list *tests)
{
tests = bst_add_tests(tests, test_rpc);
return tests;
}