blob: ded62954f0dfbbb9a45e921c3e3b079ae0a8b5bf [file] [log] [blame]
/*
*
* Copyright (c) 2016-2017 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
* This file implements interfaces for deriving and retrieving
* Weave constituent and application group keys.
*
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#include <Weave/Core/WeaveCore.h>
#include <Weave/Core/WeaveKeyIds.h>
#include "WeaveApplicationKeys.h"
#include <Weave/Core/WeaveEncoding.h>
#include <Weave/Support/crypto/WeaveCrypto.h>
#include <Weave/Support/crypto/HKDF.h>
#include <Weave/Support/CodeUtils.h>
namespace nl {
namespace Weave {
namespace Profiles {
namespace Security {
namespace AppKeys {
using namespace nl::Weave::Encoding;
using namespace nl::Weave::Crypto;
// Key diversifier used for Weave fabric root key derivation.
const uint8_t kWeaveAppFabricRootKeyDiversifier[] = { 0x21, 0xFA, 0x8F, 0x6A };
// Key diversifier used for Weave client root key derivation.
const uint8_t kWeaveAppClientRootKeyDiversifier[] = { 0x53, 0xE3, 0xFF, 0xE5 };
// Key diversifier used for Weave intermediate key derivation.
const uint8_t kWeaveAppIntermediateKeyDiversifier[] = { 0xBC, 0xAA, 0x95, 0xAD };
/**
* Get application group master key ID given application group global ID.
*
* @param[in] groupGlobalId The application group global ID.
* @param[in] groupKeyStore A pointer to the group key store object.
* @param[out] groupMasterKeyId The application group master key ID.
*
* @retval #WEAVE_NO_ERROR On success.
* @retval #WEAVE_ERROR_INVALID_ARGUMENT
* If pointer to the group key store is not provided.
* @retval #WEAVE_ERROR_UNSUPPORTED_WEAVE_FEATURE
* If FabricState object wasn't initialized with fully
* functional group key store.
* @retval #WEAVE_ERROR_KEY_NOT_FOUND
* If a group key with specified global ID is not found
* in the platform key store.
* @retval other Other platform-specific errors returned by the platform
* key store APIs.
*
*/
WEAVE_ERROR GetAppGroupMasterKeyId(uint32_t groupGlobalId, GroupKeyStoreBase *groupKeyStore, uint32_t& groupMasterKeyId)
{
WEAVE_ERROR err;
uint32_t groupMasterKeyIds[WEAVE_CONFIG_MAX_APPLICATION_GROUPS];
uint8_t groupMasterKeyCount;
WeaveGroupKey groupMasterKey;
// Verify the group key store object is provided.
VerifyOrExit(groupKeyStore != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Enumerate all application group master keys.
err = groupKeyStore->EnumerateGroupKeys(WeaveKeyId::kType_AppGroupMasterKey, groupMasterKeyIds, sizeof(groupMasterKeyIds) / sizeof(uint32_t), groupMasterKeyCount);
SuccessOrExit(err);
for (int i = 0; i < groupMasterKeyCount; i++)
{
// Get application group master key.
err = groupKeyStore->RetrieveGroupKey(groupMasterKeyIds[i], groupMasterKey);
SuccessOrExit(err);
// If group global Id matches.
if (groupMasterKey.GlobalId == groupGlobalId)
{
groupMasterKeyId = groupMasterKey.KeyId;
ExitNow();
}
}
ExitNow(err = WEAVE_ERROR_KEY_NOT_FOUND);
exit:
ClearSecretData(groupMasterKey.Key, groupMasterKey.MaxKeySize);
return err;
}
/**
* Initialize local group key store parameters.
*/
void GroupKeyStoreBase::Init(void)
{
LastUsedEpochKeyId = WeaveKeyId::kNone;
NextEpochKeyStartTime = UINT32_MAX;
}
/**
* Returns current key ID.
* Sets member variables associated with epoch keys to the default values when any change
* (delete or store) happens to the set of application epoch keys. It is the responsibility
* of the subclass that implements StoreGroupKey(), DeleteGroupKey(), and
* DeleteGroupKeysOfAType() functions to call this method.
*/
void GroupKeyStoreBase::OnEpochKeysChange(void)
{
LastUsedEpochKeyId = WeaveKeyId::kNone;
NextEpochKeyStartTime = UINT32_MAX;
}
/**
* Returns current key ID.
* Finds current epoch key based on the current system time and the start time parameter
* of each epoch key. If system doesn't have valid, accurate time then last-used epoch key
* ID is returned.
*
* @param[in] keyId The application key ID.
* @param[out] curKeyId The application current key ID.
*
* @retval #WEAVE_NO_ERROR On success.
* @retval #WEAVE_ERROR_INVALID_KEY_ID
* If the input key ID had an invalid value.
* @retval #WEAVE_ERROR_KEY_NOT_FOUND
* If epoch keys are not found in the platform key store.
* @retval other Other platform-specific errors returned by the platform
* key store APIs.
*
*/
WEAVE_ERROR GroupKeyStoreBase::GetCurrentAppKeyId(uint32_t keyId, uint32_t& curKeyId)
{
WEAVE_ERROR err;
uint32_t epochKeyIds[WEAVE_CONFIG_MAX_APPLICATION_EPOCH_KEYS];
uint32_t epochKeyStartTimes[WEAVE_CONFIG_MAX_APPLICATION_EPOCH_KEYS];
uint8_t epochKeyCount;
uint32_t curUTCTime;
int curEpochKeyIdIdx;
WeaveGroupKey epochKey;
// If current application key is not used to derive this application key.
if (!WeaveKeyId::UsesCurrentEpochKey(keyId))
{
curKeyId = keyId;
ExitNow(err = WEAVE_NO_ERROR);
}
// If platform key store state is idle (happens after platform reboot)
// retrieve the last used epoch key Id from the store.
if (LastUsedEpochKeyId == WeaveKeyId::kNone)
{
err = RetrieveLastUsedEpochKeyId();
// If failed to retrieve value from the store assume that the value is still idle.
if (err != WEAVE_NO_ERROR)
{
LastUsedEpochKeyId = WeaveKeyId::kNone;
err = WEAVE_NO_ERROR;
}
}
// Get current UTC time.
// If platform doesn't support time functions or doesn't have an accurate time yet then assume that current
// time is zero so the protocol below selects "oldest" epoch key (epoch key with smallest StartTime value).
err = GetCurrentUTCTime(curUTCTime);
if (err == WEAVE_ERROR_UNSUPPORTED_CLOCK || err == WEAVE_ERROR_TIME_NOT_SYNCED_YET)
{
curUTCTime = 0;
err = WEAVE_NO_ERROR;
}
SuccessOrExit(err);
// Update LastUsedEpochKeyId and NextEpochKeyStartTime values if needed.
if (LastUsedEpochKeyId == WeaveKeyId::kNone || curUTCTime > NextEpochKeyStartTime)
{
// Enumerate all application epoch keys.
err = EnumerateGroupKeys(WeaveKeyId::kType_AppEpochKey, epochKeyIds, sizeof(epochKeyIds) / sizeof(uint32_t), epochKeyCount);
SuccessOrExit(err);
VerifyOrExit(epochKeyCount > 0, err = WEAVE_ERROR_KEY_NOT_FOUND);
// Get start time for each epoch key.
for (int i = 0; i < epochKeyCount; i++)
{
err = RetrieveGroupKey(epochKeyIds[i], epochKey);
SuccessOrExit(err);
epochKeyStartTimes[i] = epochKey.StartTime;
}
// Search the list of epoch keys for the "current" epoch key. The current epoch key is defined
// as the newest epoch key (i.e. the key with the greatest start time) that has a start time that
// is less than or equal to the current time. In the case that the current time is unknown
// (curUTCTime == 0) then the oldest epoch key (the key with the earliest start time) is selected.
// Similarly, if the current time is known, but it falls before the start times of all keys, then
// the oldest key is used. If there is only one epoch key in the list then it is selected by default
// (curEpochKeyIdIdx is initialized to 0).
curEpochKeyIdIdx = 0;
for (int i = 1; i < epochKeyCount; i++)
{
// Search unsorted list.
if ((epochKeyStartTimes[i] > epochKeyStartTimes[curEpochKeyIdIdx] && epochKeyStartTimes[i] <= curUTCTime) ||
(epochKeyStartTimes[i] < epochKeyStartTimes[curEpochKeyIdIdx] && epochKeyStartTimes[curEpochKeyIdIdx] > curUTCTime))
{
curEpochKeyIdIdx = i;
}
}
// Search the list of keys again to find the next key (in order of start time), relative to the
// current epoch key. The start time of this key effectively marks the end-time of the current
// key. In the case where there is no next key (e.g. when the current key is the only key in the
// list) use an end time of "indefinite" (UINT32_MAX), implying that the current key should remain
// current until a new set of epoch keys is received.
for (int i = 0; i < epochKeyCount; i++)
{
if ((epochKeyStartTimes[i] > epochKeyStartTimes[curEpochKeyIdIdx] && epochKeyStartTimes[i] < NextEpochKeyStartTime))
{
NextEpochKeyStartTime = epochKeyStartTimes[i];
}
}
LastUsedEpochKeyId = epochKeyIds[curEpochKeyIdIdx];
// Store GroupKeyStoreBase state.
err = StoreLastUsedEpochKeyId();
SuccessOrExit(err);
}
// Encode current epoch key id in the key id value.
curKeyId = WeaveKeyId::UpdateEpochKeyId(keyId, LastUsedEpochKeyId);
exit:
ClearSecretData(epochKey.Key, epochKey.MaxKeySize);
return err;
}
/**
* Get application group key.
* This function derives or retrieves application group keys. Key types supported by
* this function are: fabric secret, root key, epoch key, group master key, and intermediate key.
*
* @param[in] keyId The group key ID.
* @param[out] groupKey A reference to the group key object.
*
* @retval #WEAVE_NO_ERROR On success.
* @retval #WEAVE_ERROR_INVALID_KEY_ID
* If the requested key has invalid key ID.
* @retval #WEAVE_ERROR_INVALID_ARGUMENT
* If the platform key store returns invalid key parameters.
* @retval other Other platform-specific errors returned by the platform
* key store APIs.
*
*/
WEAVE_ERROR GroupKeyStoreBase::GetGroupKey(uint32_t keyId, WeaveGroupKey& groupKey)
{
WEAVE_ERROR err;
uint32_t rootKeyId;
uint8_t expectedKeyLen;
// Get current key Id.
err = GetCurrentAppKeyId(keyId, keyId);
SuccessOrExit(err);
switch (WeaveKeyId::GetType(keyId))
{
case WeaveKeyId::kType_AppRootKey:
rootKeyId = WeaveKeyId::GetRootKeyId(keyId);
// Derive fabric/client root key.
if (rootKeyId == WeaveKeyId::kFabricRootKey || rootKeyId == WeaveKeyId::kClientRootKey)
{
err = DeriveFabricOrClientRootKey(rootKeyId, groupKey);
break;
}
// Otherwise, fall through to retrieve service root key from the key store.
case WeaveKeyId::kType_General:
case WeaveKeyId::kType_AppEpochKey:
case WeaveKeyId::kType_AppGroupMasterKey:
// Retrieve key from the key store.
err = RetrieveGroupKey(keyId, groupKey);
break;
case WeaveKeyId::kType_AppIntermediateKey:
// Derive intermediate key.
err = DeriveIntermediateKey(keyId, groupKey);
break;
default:
ExitNow(err = WEAVE_ERROR_INVALID_KEY_ID);
}
SuccessOrExit(err);
if (keyId == WeaveKeyId::kFabricSecret)
expectedKeyLen = kWeaveFabricSecretSize;
else
expectedKeyLen = kWeaveAppGroupKeySize;
// Verify correct key length and key id.
VerifyOrExit(groupKey.KeyLen == expectedKeyLen &&
groupKey.KeyId == keyId, err = WEAVE_ERROR_INVALID_ARGUMENT);
exit:
return err;
}
/**
* Derive fabric/client root key.
* Fabric and client root keys are derived from fabric secret, which is retrieved
* from the platform key store.
*
* @param[in] rootKeyId The key ID associated with the requested root key.
* @param[out] rootKey A reference to the key material object.
*
* @retval #WEAVE_NO_ERROR On success.
* @retval #WEAVE_ERROR_INVALID_KEY_ID
* If the requested key has invalid key ID.
* @retval #WEAVE_ERROR_INVALID_ARGUMENT
* If the platform key store returns invalid key parameters.
* @retval other Other platform-specific errors returned by the platform
* key store APIs.
*
*/
WEAVE_ERROR GroupKeyStoreBase::DeriveFabricOrClientRootKey(uint32_t rootKeyId, WeaveGroupKey& rootKey)
{
WEAVE_ERROR err;
WeaveGroupKey fabricSecret;
const uint8_t *rootKeyDiversifier;
// Set group root key id.
rootKey.KeyId = rootKeyId;
// Set root key diversifier value.
if (rootKeyId == WeaveKeyId::kFabricRootKey)
rootKeyDiversifier = kWeaveAppFabricRootKeyDiversifier;
else
rootKeyDiversifier = kWeaveAppClientRootKeyDiversifier;
// Set key length.
rootKey.KeyLen = kWeaveAppRootKeySize;
// Get fabric secret.
err = RetrieveGroupKey(WeaveKeyId::kFabricSecret, fabricSecret);
SuccessOrExit(err);
// Verify correct key size.
VerifyOrExit(fabricSecret.KeyLen == kWeaveFabricSecretSize, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Derive fabric/client root key.
err = HKDFSHA1::DeriveKey(NULL, 0,
fabricSecret.Key, fabricSecret.KeyLen,
NULL, 0,
rootKeyDiversifier, kWeaveAppFabricRootKeyDiversifierSize,
rootKey.Key, sizeof(rootKey.Key), kWeaveAppRootKeySize);
SuccessOrExit(err);
exit:
return err;
}
/**
* Derive application intermediate key.
* The intermediate key is derived from the root key and epoch key material specified
* in the @a keyId input.
*
* @param[in] keyId The requested intermediate key ID.
* @param[out] intermediateKey A reference to the key material object.
*
* @retval #WEAVE_NO_ERROR On success.
* @retval #WEAVE_ERROR_INVALID_KEY_ID
* If the requested key has invalid key ID.
* @retval #WEAVE_ERROR_INVALID_ARGUMENT
* If the platform key store returns invalid key parameters.
* @retval other Other platform-specific errors returned by the platform
* key store APIs.
*
*/
WEAVE_ERROR GroupKeyStoreBase::DeriveIntermediateKey(uint32_t keyId, WeaveGroupKey& intermediateKey)
{
WEAVE_ERROR err;
WeaveGroupKey rootKey;
WeaveGroupKey epochKey;
// Set group root key id.
rootKey.KeyId = WeaveKeyId::GetRootKeyId(keyId);
// Get/Derive root key.
err = GetGroupKey(rootKey.KeyId, rootKey);
SuccessOrExit(err);
// Set epoch key id.
epochKey.KeyId = WeaveKeyId::GetEpochKeyId(keyId);
// Get epoch key.
err = RetrieveGroupKey(epochKey.KeyId, epochKey);
SuccessOrExit(err);
// Verify correct key size.
VerifyOrExit(epochKey.KeyLen == kWeaveAppEpochKeySize, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Set intermediate key length and Id.
intermediateKey.KeyLen = kWeaveAppIntermediateKeySize;
intermediateKey.KeyId = keyId;
// Derive intermediate key.
err = HKDFSHA1::DeriveKey(NULL, 0,
rootKey.Key, rootKey.KeyLen,
epochKey.Key, epochKey.KeyLen,
kWeaveAppIntermediateKeyDiversifier, kWeaveAppIntermediateKeyDiversifierSize,
intermediateKey.Key, sizeof(intermediateKey.Key), intermediateKey.KeyLen);
SuccessOrExit(err);
exit:
ClearSecretData(rootKey.Key, rootKey.MaxKeySize);
ClearSecretData(epochKey.Key, epochKey.MaxKeySize);
return err;
}
/**
* Derives application key.
* Three types of application keys are supported: current application key, rotating
* application key and static application key. When current application key is requested
* the function finds and uses the current epoch key based on the current system time and
* the start time parameter of each epoch key.
*
* @param[inout] keyId A reference to the requested key ID. When current application
* key is requested this field is updated to reflect the new
* type (rotating application key) and the actual epoch key ID
* that was used to generate application key.
* @param[in] keySalt A pointer to a buffer with application key salt value.
* @param[in] saltLen The length of the application key salt.
* @param[in] keyDiversifier A pointer to a buffer with application key diversifier value.
* @param[in] diversifierLen The length of the application key diversifier.
* @param[out] appKey A pointer to a buffer where the derived key will be written.
* @param[in] keyBufSize The length of the supplied key buffer.
* @param[in] keyLen The length of the requested key material.
* @param[out] appGroupGlobalId The application group global ID of the associated key.
*
* @retval #WEAVE_NO_ERROR On success.
* @retval #WEAVE_ERROR_BUFFER_TOO_SMALL
* If the provided key buffer size is not sufficient.
* @retval #WEAVE_ERROR_INVALID_KEY_ID
* If the requested key has invalid key ID.
* @retval #WEAVE_ERROR_INVALID_ARGUMENT
* If the platform key store returns invalid key parameters
* or key identifier has invalid value.
* @retval other Other platform-specific errors returned by the platform
* key store APIs.
*
*/
WEAVE_ERROR GroupKeyStoreBase::DeriveApplicationKey(uint32_t& keyId,
const uint8_t *keySalt, uint8_t saltLen,
const uint8_t *keyDiversifier, uint8_t diversifierLen,
uint8_t *appKey, uint8_t keyBufSize, uint8_t keyLen,
uint32_t& appGroupGlobalId)
{
WEAVE_ERROR err;
WeaveGroupKey intermediateKey;
WeaveGroupKey groupMasterKey;
uint32_t localKeyId;
// Verify that key identifier has correct type.
VerifyOrExit(WeaveKeyId::IsAppGroupKey(keyId), err = WEAVE_ERROR_INVALID_ARGUMENT);
// Get current key Id.
err = GetCurrentAppKeyId(keyId, keyId);
SuccessOrExit(err);
// Set first requested key material, which can be of two types:
// - If keyId is an app static key then localKeyId is root key id.
// - If keyId is an app rotating key then localKeyId is intermediate key id.
localKeyId = WeaveKeyId::GetRootKeyId(keyId);
if (WeaveKeyId::IsAppRotatingKey(keyId))
{
uint32_t epochKeyId = WeaveKeyId::GetEpochKeyId(keyId);
localKeyId = WeaveKeyId::MakeAppIntermediateKeyId(localKeyId, epochKeyId, false);
}
// Get root or intermediate key material.
err = GetGroupKey(localKeyId, intermediateKey);
SuccessOrExit(err);
// Set application group master key id.
localKeyId = WeaveKeyId::GetAppGroupMasterKeyId(keyId);
// Retrieve application group master key.
err = RetrieveGroupKey(localKeyId, groupMasterKey);
SuccessOrExit(err);
// Verify correct key size.
VerifyOrExit(groupMasterKey.KeyLen == kWeaveAppGroupMasterKeySize, err = WEAVE_ERROR_INVALID_ARGUMENT);
// Derive application key material.
err = HKDFSHA1::DeriveKey(keySalt, saltLen,
intermediateKey.Key, intermediateKey.KeyLen,
groupMasterKey.Key, groupMasterKey.KeyLen,
keyDiversifier, diversifierLen,
appKey, keyBufSize, keyLen);
SuccessOrExit(err);
// Return the global id of the associated application group.
appGroupGlobalId = groupMasterKey.GlobalId;
exit:
ClearSecretData(intermediateKey.Key, intermediateKey.MaxKeySize);
ClearSecretData(groupMasterKey.Key, groupMasterKey.MaxKeySize);
return err;
}
} // namespace AppKeys
} // namespace Security
} // namespace Profiles
} // namespace Weave
} // namespace nl