blob: 8a5bab0aad3d43fca5df04fa9c0c1cb9d0f9b86d [file] [log] [blame]
/*
*
* Copyright (c) 2018 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* Provides an implementation of the Weave GroupKeyStore interface
* for the ESP32 platform.
*/
#include <Weave/DeviceLayer/internal/WeaveDeviceLayerInternal.h>
#include <Weave/DeviceLayer/ESP32/GroupKeyStoreImpl.h>
#include "nvs_flash.h"
#include "nvs.h"
using namespace ::nl;
using namespace ::nl::Weave;
using namespace ::nl::Weave::Profiles::Security::AppKeys;
namespace nl {
namespace Weave {
namespace DeviceLayer {
namespace Internal {
WEAVE_ERROR GroupKeyStoreImpl::RetrieveGroupKey(uint32_t keyId, WeaveGroupKey & key)
{
WEAVE_ERROR err;
size_t keyLen;
char keyName[kMaxConfigKeyNameLength + 1];
ESP32Config::Key configKey { kConfigNamespace_WeaveConfig, keyName };
err = FormKeyName(keyId, keyName, sizeof(keyName));
SuccessOrExit(err);
err = ReadConfigValueBin(configKey, key.Key, sizeof(key.Key), keyLen);
if (err == WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND)
{
err = WEAVE_ERROR_KEY_NOT_FOUND;
}
SuccessOrExit(err);
if (keyId != WeaveKeyId::kFabricSecret)
{
memcpy(&key.StartTime, key.Key + kWeaveAppGroupKeySize, sizeof(uint32_t));
keyLen -= sizeof(uint32_t);
}
key.KeyId = keyId;
key.KeyLen = keyLen;
exit:
return err;
}
WEAVE_ERROR GroupKeyStoreImpl::StoreGroupKey(const WeaveGroupKey & key)
{
WEAVE_ERROR err;
nvs_handle handle;
char keyName[kMaxConfigKeyNameLength + 1];
uint8_t keyData[WeaveGroupKey::MaxKeySize];
bool needClose = false;
bool indexUpdated = false;
err = FormKeyName(key.KeyId, keyName, sizeof(keyName));
SuccessOrExit(err);
err = AddKeyToIndex(key.KeyId, indexUpdated);
SuccessOrExit(err);
err = nvs_open(kConfigNamespace_WeaveConfig, NVS_READWRITE, &handle);
SuccessOrExit(err);
needClose = true;
memcpy(keyData, key.Key, WeaveGroupKey::MaxKeySize);
if (key.KeyId != WeaveKeyId::kFabricSecret)
{
memcpy(keyData + kWeaveAppGroupKeySize, (const void *)&key.StartTime, sizeof(uint32_t));
}
#if WEAVE_PROGRESS_LOGGING
if (WeaveKeyId::IsAppEpochKey(key.KeyId))
{
WeaveLogProgress(DeviceLayer, "GroupKeyStore: storing epoch key %s/%s (key len %" PRId8 ", start time %" PRIu32 ")",
kConfigNamespace_WeaveConfig, keyName, key.KeyLen, key.StartTime);
}
else if (WeaveKeyId::IsAppGroupMasterKey(key.KeyId))
{
WeaveLogProgress(DeviceLayer, "GroupKeyStore: storing app master key %s/%s (key len %" PRId8 ", global id 0x%" PRIX32 ")",
kConfigNamespace_WeaveConfig, keyName, key.KeyLen, key.GlobalId);
}
else
{
const char * keyType = (WeaveKeyId::IsAppRootKey(key.KeyId)) ? "root": "general";
WeaveLogProgress(DeviceLayer, "GroupKeyStore: storing %s key %s/%s (key len %" PRId8 ")", keyType,
kConfigNamespace_WeaveConfig, keyName, key.KeyLen);
}
#endif // WEAVE_PROGRESS_LOGGING
err = nvs_set_blob(handle, keyName, keyData, WeaveGroupKey::MaxKeySize);
SuccessOrExit(err);
if (indexUpdated)
{
err = WriteKeyIndex(handle);
SuccessOrExit(err);
}
// Commit the value to the persistent store.
err = nvs_commit(handle);
SuccessOrExit(err);
exit:
if (needClose)
{
nvs_close(handle);
}
if (err != WEAVE_NO_ERROR && indexUpdated)
{
mNumKeys--;
}
ClearSecretData(keyData, sizeof(keyData));
return err;
}
WEAVE_ERROR GroupKeyStoreImpl::DeleteGroupKey(uint32_t keyId)
{
return DeleteKeyOrKeys(keyId, WeaveKeyId::kType_None);
}
WEAVE_ERROR GroupKeyStoreImpl::DeleteGroupKeysOfAType(uint32_t keyType)
{
return DeleteKeyOrKeys(WeaveKeyId::kNone, keyType);
}
WEAVE_ERROR GroupKeyStoreImpl::EnumerateGroupKeys(uint32_t keyType, uint32_t * keyIds,
uint8_t keyIdsArraySize, uint8_t & keyCount)
{
keyCount = 0;
for (uint8_t i = 0; i < mNumKeys && keyCount < keyIdsArraySize; i++)
{
if (keyType == WeaveKeyId::kType_None || WeaveKeyId::GetType(mKeyIndex[i]) == keyType)
{
keyIds[keyCount++] = mKeyIndex[i];
}
}
return WEAVE_NO_ERROR;
}
WEAVE_ERROR GroupKeyStoreImpl::Clear(void)
{
return DeleteKeyOrKeys(WeaveKeyId::kNone, WeaveKeyId::kType_None);
}
WEAVE_ERROR GroupKeyStoreImpl::RetrieveLastUsedEpochKeyId(void)
{
WEAVE_ERROR err;
err = ReadConfigValue(kConfigKey_LastUsedEpochKeyId, LastUsedEpochKeyId);
if (err == WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND)
{
LastUsedEpochKeyId = WeaveKeyId::kNone;
err = WEAVE_NO_ERROR;
}
return err;
}
WEAVE_ERROR GroupKeyStoreImpl::StoreLastUsedEpochKeyId(void)
{
return WriteConfigValue(kConfigKey_LastUsedEpochKeyId, LastUsedEpochKeyId);
}
WEAVE_ERROR GroupKeyStoreImpl::Init()
{
WEAVE_ERROR err;
size_t indexSizeBytes;
err = ReadConfigValueBin(kConfigKey_GroupKeyIndex,
(uint8_t *)mKeyIndex, sizeof(mKeyIndex), indexSizeBytes);
if (err == WEAVE_DEVICE_ERROR_CONFIG_NOT_FOUND)
{
err = WEAVE_NO_ERROR;
indexSizeBytes = 0;
}
SuccessOrExit(err);
mNumKeys = indexSizeBytes / sizeof(uint32_t);
exit:
return err;
}
WEAVE_ERROR GroupKeyStoreImpl::AddKeyToIndex(uint32_t keyId, bool & indexUpdated)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
indexUpdated = false;
for (uint8_t i = 0; i < mNumKeys; i++)
{
if (mKeyIndex[i] == keyId)
{
ExitNow(err = WEAVE_NO_ERROR);
}
}
VerifyOrExit(mNumKeys < kMaxGroupKeys, err = WEAVE_ERROR_TOO_MANY_KEYS);
mKeyIndex[mNumKeys++] = keyId;
indexUpdated = true;
exit:
return err;
}
WEAVE_ERROR GroupKeyStoreImpl::WriteKeyIndex(nvs_handle handle)
{
WeaveLogProgress(DeviceLayer, "GroupKeyStore: writing key index %s/%s (num keys %" PRIu8 ")",
kConfigKey_GroupKeyIndex.Namespace, kConfigKey_GroupKeyIndex.Name, mNumKeys);
return nvs_set_blob(handle, kConfigKey_GroupKeyIndex.Name, mKeyIndex, mNumKeys * sizeof(uint32_t));
}
WEAVE_ERROR GroupKeyStoreImpl::DeleteKeyOrKeys(uint32_t targetKeyId, uint32_t targetKeyType)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
nvs_handle handle;
char keyName[kMaxConfigKeyNameLength + 1];
bool needClose = false;
for (uint8_t i = 0; i < mNumKeys; )
{
uint32_t curKeyId = mKeyIndex[i];
if ((targetKeyId == WeaveKeyId::kNone && targetKeyType == WeaveKeyId::kType_None) ||
curKeyId == targetKeyId ||
WeaveKeyId::GetType(curKeyId) == targetKeyType)
{
if (!needClose)
{
err = nvs_open(kConfigNamespace_WeaveConfig, NVS_READWRITE, &handle);
SuccessOrExit(err);
needClose = true;
}
err = FormKeyName(curKeyId, keyName, sizeof(keyName));
SuccessOrExit(err);
err = nvs_erase_key(handle, keyName);
#if WEAVE_PROGRESS_LOGGING
if (err == ESP_OK)
{
const char * keyType;
if (WeaveKeyId::IsAppRootKey(curKeyId))
{
keyType = "root";
}
else if (WeaveKeyId::IsAppGroupMasterKey(curKeyId))
{
keyType = "app master";
}
else if (WeaveKeyId::IsAppEpochKey(curKeyId))
{
keyType = "epoch";
}
else
{
keyType = "general";
}
WeaveLogProgress(DeviceLayer, "GroupKeyStore: erasing %s key %s/%s", keyType,
kConfigNamespace_WeaveConfig, keyName);
}
else
#endif // WEAVE_PROGRESS_LOGGING
if (err == ESP_ERR_NVS_NOT_FOUND)
{
err = WEAVE_NO_ERROR;
}
SuccessOrExit(err);
mNumKeys--;
memmove(&mKeyIndex[i], &mKeyIndex[i+1], (mNumKeys - i) * sizeof(uint32_t));
}
else
{
i++;
}
}
if (needClose)
{
err = WriteKeyIndex(handle);
SuccessOrExit(err);
// Commit to the persistent store.
err = nvs_commit(handle);
SuccessOrExit(err);
}
exit:
if (needClose)
{
nvs_close(handle);
}
return err;
}
WEAVE_ERROR GroupKeyStoreImpl::FormKeyName(uint32_t keyId, char * buf, size_t bufSize)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(bufSize >= kMaxConfigKeyNameLength, err = WEAVE_ERROR_BUFFER_TOO_SMALL);
if (keyId == WeaveKeyId::kFabricSecret)
{
strcpy(buf, kConfigKey_FabricSecret.Name);
}
else
{
snprintf(buf, bufSize, "%s%08" PRIX32, kGroupKeyNamePrefix, keyId);
}
exit:
return err;
}
} // namespace Internal
} // namespace DeviceLayer
} // namespace Weave
} // namespace nl