blob: 03f57ba586f858c4dbbb9fd9c18a5f47bdfe6e75 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// clang-format off
#pragma GCC diagnostic push
#include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h>
#pragma GCC diagnostic pop
#include "group_key_store_impl.h"
// clang-format on
#include <gtest/gtest.h>
#include "environment_config.h"
#include "src/lib/files/file.h"
#include "src/lib/testing/loop_fixture/test_loop_fixture.h"
namespace weave::adaptation::testing {
namespace {
using nl::Weave::WeaveKeyId;
using nl::Weave::DeviceLayer::Internal::EnvironmentConfig;
using nl::Weave::DeviceLayer::Internal::GroupKeyStoreImpl;
using nl::Weave::Profiles::Security::AppKeys::WeaveGroupKey;
constexpr uint8_t kWeaveAppGroupKeySize =
nl::Weave::Profiles::Security::AppKeys::kWeaveAppGroupKeySize;
constexpr uint8_t kMaxGroupKeys =
nl::Weave::DeviceLayer::Internal::GroupKeyStoreImpl::kMaxGroupKeys;
constexpr uint32_t kTestKeyId = WeaveKeyId::kFabricSecret + 1u;
constexpr char kWeaveConfigStoreTestPath[] = "/data/environment.json";
} // namespace
class GroupKeyStoreTest : public ::gtest::TestLoopFixture {
public:
void SetUp() override {
TestLoopFixture::SetUp();
EXPECT_EQ(group_key_store_.Init(), WEAVE_NO_ERROR);
}
void TearDown() override {
EXPECT_EQ(EnvironmentConfig::FactoryResetConfig(), WEAVE_NO_ERROR);
TestLoopFixture::TearDown();
}
protected:
static void WriteConfigStore(const char* config_data) {
EXPECT_EQ(EnvironmentConfig::FactoryResetConfig(), WEAVE_NO_ERROR);
EXPECT_TRUE(files::WriteFile(kWeaveConfigStoreTestPath, config_data, strlen(config_data)));
}
static std::string ReadConfigStore() {
std::string file_contents;
EXPECT_TRUE(files::ReadFileToString(kWeaveConfigStoreTestPath, &file_contents));
return file_contents;
}
static WeaveGroupKey CreateGroupKey(uint32_t key_id, uint8_t key_byte = 0,
uint8_t key_len = kWeaveAppGroupKeySize,
uint32_t start_time = 0) {
WeaveGroupKey group_key{
.KeyId = key_id,
.KeyLen = key_len,
.StartTime = start_time,
};
memset(group_key.Key, 0, sizeof(group_key.Key));
memset(group_key.Key, key_byte, key_len);
return group_key;
}
static void VerifyGroupKey(const WeaveGroupKey& key, const WeaveGroupKey& key_expected) {
EXPECT_EQ(key.KeyId, key_expected.KeyId);
EXPECT_EQ(key.KeyLen, key_expected.KeyLen);
EXPECT_EQ(memcmp(key.Key, key_expected.Key, sizeof(key_expected.Key)), 0);
if (key_expected.KeyId != WeaveKeyId::kFabricSecret) {
EXPECT_EQ(key.StartTime, key_expected.StartTime);
}
}
GroupKeyStoreImpl& group_key_store() { return group_key_store_; }
private:
GroupKeyStoreImpl group_key_store_;
};
TEST_F(GroupKeyStoreTest, InitializeWithExistingStore) {
uint32_t key_ids[kMaxGroupKeys] = {};
uint8_t key_count = 0;
// Sample valid key set.
uint32_t test_key_ids[1] = {kTestKeyId};
uint8_t test_key_data[kWeaveAppGroupKeySize];
EXPECT_EQ(EnvironmentConfig::WriteConfigValueBin(EnvironmentConfig::kConfigKey_GroupKeyIndex,
(uint8_t*)test_key_ids, sizeof(test_key_ids)),
WEAVE_NO_ERROR);
EXPECT_EQ(
EnvironmentConfig::WriteConfigValueBin("gk-00001002", test_key_data, sizeof(test_key_data)),
WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().Init(), WEAVE_NO_ERROR);
EXPECT_EQ(
group_key_store().EnumerateGroupKeys(WeaveKeyId::kNone, key_ids, kMaxGroupKeys, key_count),
WEAVE_NO_ERROR);
EXPECT_EQ(key_count, 1);
}
TEST_F(GroupKeyStoreTest, StoreAndRetrieveKey) {
const WeaveGroupKey test_key = CreateGroupKey(kTestKeyId, 0, kWeaveAppGroupKeySize, 0xABCDEF);
EXPECT_EQ(group_key_store().StoreGroupKey(test_key), WEAVE_NO_ERROR);
WeaveGroupKey retrieved_key;
EXPECT_EQ(group_key_store().RetrieveGroupKey(kTestKeyId, retrieved_key), WEAVE_NO_ERROR);
VerifyGroupKey(retrieved_key, test_key);
}
TEST_F(GroupKeyStoreTest, StoreInvalidKey) {
const WeaveGroupKey test_key{.KeyLen = WeaveGroupKey::MaxKeySize + 1};
EXPECT_EQ(group_key_store().StoreGroupKey(test_key), WEAVE_ERROR_INVALID_ARGUMENT);
}
TEST_F(GroupKeyStoreTest, StoreAndRetrieveFabricSecretKey) {
const WeaveGroupKey test_key =
CreateGroupKey(WeaveKeyId::kFabricSecret, 0, kWeaveAppGroupKeySize);
EXPECT_EQ(group_key_store().StoreGroupKey(test_key), WEAVE_NO_ERROR);
WeaveGroupKey retrieved_key;
EXPECT_EQ(group_key_store().RetrieveGroupKey(WeaveKeyId::kFabricSecret, retrieved_key),
WEAVE_NO_ERROR);
VerifyGroupKey(retrieved_key, test_key);
}
TEST_F(GroupKeyStoreTest, RetrieveNonExistentKey) {
WeaveGroupKey retrieved_key;
EXPECT_EQ(group_key_store().RetrieveGroupKey(kTestKeyId, retrieved_key),
WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND);
const WeaveGroupKey test_key = CreateGroupKey(kTestKeyId + 1, 0, kWeaveAppGroupKeySize, 12345U);
EXPECT_EQ(group_key_store().StoreGroupKey(test_key), WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().RetrieveGroupKey(kTestKeyId, retrieved_key),
WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND);
}
TEST_F(GroupKeyStoreTest, DeleteKey) {
const WeaveGroupKey test_key = CreateGroupKey(kTestKeyId, 0, kWeaveAppGroupKeySize, 0xABCDEF);
EXPECT_EQ(group_key_store().StoreGroupKey(test_key), WEAVE_NO_ERROR);
WeaveGroupKey retrieved_key;
EXPECT_EQ(group_key_store().RetrieveGroupKey(kTestKeyId, retrieved_key), WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().DeleteGroupKey(kTestKeyId), WEAVE_NO_ERROR);
}
TEST_F(GroupKeyStoreTest, DeleteNonExistentKey) {
EXPECT_EQ(group_key_store().DeleteGroupKey(kTestKeyId), WEAVE_ERROR_KEY_NOT_FOUND);
}
TEST_F(GroupKeyStoreTest, EnumerateKeys) {
const uint32_t none_key_id = WeaveKeyId::kType_None + 1;
const uint32_t general_key_id = WeaveKeyId::kType_General + 1;
const uint32_t rotating_key_id = WeaveKeyId::kType_AppRotatingKey + 1;
const WeaveGroupKey none_key = CreateGroupKey(none_key_id, 0, kWeaveAppGroupKeySize);
const WeaveGroupKey general_key = CreateGroupKey(general_key_id, 0, kWeaveAppGroupKeySize);
const WeaveGroupKey rotating_key = CreateGroupKey(rotating_key_id, 0, kWeaveAppGroupKeySize);
EXPECT_EQ(group_key_store().StoreGroupKey(none_key), WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().StoreGroupKey(general_key), WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().StoreGroupKey(rotating_key), WEAVE_NO_ERROR);
uint32_t key_ids[kMaxGroupKeys] = {};
uint8_t key_count = 0;
// Verify that WeaveKeyId::kNone returns all keys.
EXPECT_EQ(
group_key_store().EnumerateGroupKeys(WeaveKeyId::kNone, key_ids, kMaxGroupKeys, key_count),
WEAVE_NO_ERROR);
EXPECT_EQ(key_count, 3);
EXPECT_NE(std::find(std::begin(key_ids), std::end(key_ids), none_key_id), std::end(key_ids));
EXPECT_NE(std::find(std::begin(key_ids), std::end(key_ids), general_key_id), std::end(key_ids));
EXPECT_NE(std::find(std::begin(key_ids), std::end(key_ids), rotating_key_id), std::end(key_ids));
// Verify that only the general key is returned.
EXPECT_EQ(group_key_store().EnumerateGroupKeys(WeaveKeyId::kType_General, key_ids, kMaxGroupKeys,
key_count),
WEAVE_NO_ERROR);
EXPECT_EQ(key_count, 1);
EXPECT_EQ(key_ids[0], general_key_id);
// Verify that only the rotating key is returned.
EXPECT_EQ(group_key_store().EnumerateGroupKeys(WeaveKeyId::kType_AppRotatingKey, key_ids,
kMaxGroupKeys, key_count),
WEAVE_NO_ERROR);
EXPECT_EQ(key_count, 1);
EXPECT_EQ(key_ids[0], rotating_key_id);
// Verify that no keys are returned if the type doesn't exist.
EXPECT_EQ(group_key_store().EnumerateGroupKeys(WeaveKeyId::kType_AppRootKey, key_ids,
kMaxGroupKeys, key_count),
WEAVE_NO_ERROR);
EXPECT_EQ(key_count, 0);
}
TEST_F(GroupKeyStoreTest, EnumerateKeysWithoutSpace) {
const WeaveGroupKey test_key = CreateGroupKey(kTestKeyId, 0, kWeaveAppGroupKeySize);
EXPECT_EQ(group_key_store().StoreGroupKey(test_key), WEAVE_NO_ERROR);
uint32_t key_ids[kMaxGroupKeys] = {};
uint8_t key_count = 0;
EXPECT_EQ(group_key_store().EnumerateGroupKeys(WeaveKeyId::kNone, key_ids, 0, key_count),
WEAVE_ERROR_BUFFER_TOO_SMALL);
EXPECT_EQ(group_key_store().EnumerateGroupKeys(WeaveKeyId::kNone, key_ids, 1, key_count),
WEAVE_NO_ERROR);
}
TEST_F(GroupKeyStoreTest, DeleteKeysOfType) {
const uint32_t none_key_id = WeaveKeyId::kType_None + 1;
const uint32_t general1_key_id = WeaveKeyId::kType_General + 1;
const uint32_t general2_key_id = WeaveKeyId::kType_General + 2;
const WeaveGroupKey none_key = CreateGroupKey(none_key_id);
const WeaveGroupKey general1_key = CreateGroupKey(general1_key_id);
const WeaveGroupKey general2_key = CreateGroupKey(general2_key_id);
EXPECT_EQ(group_key_store().StoreGroupKey(none_key), WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().StoreGroupKey(general1_key), WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().StoreGroupKey(general2_key), WEAVE_NO_ERROR);
uint32_t key_ids[kMaxGroupKeys] = {};
uint8_t key_count = 0;
EXPECT_EQ(group_key_store().DeleteGroupKeysOfAType(WeaveKeyId::kType_General), WEAVE_NO_ERROR);
EXPECT_EQ(
group_key_store().EnumerateGroupKeys(WeaveKeyId::kNone, key_ids, kMaxGroupKeys, key_count),
WEAVE_NO_ERROR);
EXPECT_EQ(key_count, 1);
EXPECT_EQ(key_ids[0], none_key_id);
}
TEST_F(GroupKeyStoreTest, ClearKeys) {
const uint32_t none_key_id = WeaveKeyId::kType_None + 1;
const uint32_t general_key_id = WeaveKeyId::kType_General + 1;
const WeaveGroupKey none_key = CreateGroupKey(none_key_id);
const WeaveGroupKey general_key = CreateGroupKey(general_key_id);
EXPECT_EQ(group_key_store().StoreGroupKey(none_key), WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().StoreGroupKey(general_key), WEAVE_NO_ERROR);
uint32_t key_ids[kMaxGroupKeys] = {};
uint8_t key_count = 0;
EXPECT_EQ(group_key_store().Clear(), WEAVE_NO_ERROR);
EXPECT_EQ(
group_key_store().EnumerateGroupKeys(WeaveKeyId::kNone, key_ids, kMaxGroupKeys, key_count),
WEAVE_NO_ERROR);
EXPECT_EQ(key_count, 0);
EXPECT_EQ(ReadConfigStore().find("gk-"), std::string::npos);
}
TEST_F(GroupKeyStoreTest, RetrieveLastUsedEpochKeyId) {
// Verify that retrieval succeeds even when the config value isn't stored.
EXPECT_EQ(EnvironmentConfig::FactoryResetConfig(), WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().RetrieveLastUsedEpochKeyId(), WEAVE_NO_ERROR);
// Verify that the key id is readable if already stored.
EXPECT_EQ(EnvironmentConfig::FactoryResetConfig(), WEAVE_NO_ERROR);
EXPECT_EQ(
EnvironmentConfig::WriteConfigValue(EnvironmentConfig::kConfigKey_LastUsedEpochKeyId, 100u),
WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().RetrieveLastUsedEpochKeyId(), WEAVE_NO_ERROR);
}
TEST_F(GroupKeyStoreTest, StoreLastUsedEpochKeyId) {
EXPECT_EQ(
EnvironmentConfig::WriteConfigValue(EnvironmentConfig::kConfigKey_LastUsedEpochKeyId, 100u),
WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().RetrieveLastUsedEpochKeyId(), WEAVE_NO_ERROR);
EXPECT_EQ(EnvironmentConfig::FactoryResetConfig(), WEAVE_NO_ERROR);
EXPECT_EQ(group_key_store().StoreLastUsedEpochKeyId(), WEAVE_NO_ERROR);
EXPECT_EQ(ReadConfigStore(), "{\"last-used-epoch-key-id\":100}");
}
} // namespace weave::adaptation::testing