blob: ece38e74ffeda2494f3780649cce8549398cd820 [file] [log] [blame]
/*
* Copyright (c) 2023 Project CHIP Authors
* 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.
*/
#include "PSASessionKeystore.h"
#include <psa/crypto.h>
namespace chip {
namespace Crypto {
namespace {
class KeyAttributesBase
{
public:
KeyAttributesBase(psa_key_type_t type, psa_algorithm_t algorithm, psa_key_usage_t usageFlags, size_t bits)
{
psa_set_key_type(&mAttrs, type);
psa_set_key_algorithm(&mAttrs, algorithm);
psa_set_key_usage_flags(&mAttrs, usageFlags);
psa_set_key_bits(&mAttrs, bits);
}
~KeyAttributesBase() { psa_reset_key_attributes(&mAttrs); }
const psa_key_attributes_t & Get() { return mAttrs; }
private:
psa_key_attributes_t mAttrs = PSA_KEY_ATTRIBUTES_INIT;
};
class AesKeyAttributes : public KeyAttributesBase
{
public:
AesKeyAttributes() :
KeyAttributesBase(PSA_KEY_TYPE_AES, PSA_ALG_AEAD_WITH_AT_LEAST_THIS_LENGTH_TAG(PSA_ALG_CCM, 8),
PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_COPY,
CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES * 8)
{}
};
class HmacKeyAttributes : public KeyAttributesBase
{
public:
HmacKeyAttributes() :
KeyAttributesBase(PSA_KEY_TYPE_HMAC, PSA_ALG_HMAC(PSA_ALG_SHA_256), PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_COPY,
CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES * 8)
{}
};
class HkdfKeyAttributes : public KeyAttributesBase
{
public:
HkdfKeyAttributes() : KeyAttributesBase(PSA_KEY_TYPE_DERIVE, PSA_ALG_HKDF(PSA_ALG_SHA_256), PSA_KEY_USAGE_DERIVE, 0) {}
};
#if CHIP_CONFIG_ENABLE_ICD_CIP
void SetKeyId(Symmetric128BitsKeyHandle & key, psa_key_id_t newKeyId)
{
auto & KeyId = key.AsMutable<psa_key_id_t>();
KeyId = newKeyId;
}
#endif
} // namespace
CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Aes128KeyHandle & key)
{
// Destroy the old key if already allocated
DestroyKey(key);
AesKeyAttributes attrs;
psa_status_t status =
psa_import_key(&attrs.Get(), keyMaterial, sizeof(Symmetric128BitsKeyByteArray), &key.AsMutable<psa_key_id_t>());
LogPsaError(status);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
return CHIP_NO_ERROR;
}
CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Hmac128KeyHandle & key)
{
// Destroy the old key if already allocated
DestroyKey(key);
HmacKeyAttributes attrs;
psa_status_t status =
psa_import_key(&attrs.Get(), keyMaterial, sizeof(Symmetric128BitsKeyByteArray), &key.AsMutable<psa_key_id_t>());
LogPsaError(status);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
return CHIP_NO_ERROR;
}
CHIP_ERROR PSASessionKeystore::CreateKey(const ByteSpan & keyMaterial, HkdfKeyHandle & key)
{
// Destroy the old key if already allocated
psa_destroy_key(key.As<psa_key_id_t>());
HkdfKeyAttributes attrs;
psa_status_t status = psa_import_key(&attrs.Get(), keyMaterial.data(), keyMaterial.size(), &key.AsMutable<psa_key_id_t>());
LogPsaError(status);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
return CHIP_NO_ERROR;
}
CHIP_ERROR PSASessionKeystore::DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info,
Aes128KeyHandle & key)
{
PsaKdf kdf;
ReturnErrorOnFailure(kdf.Init(secret.Span(), salt, info));
AesKeyAttributes attrs;
return kdf.DeriveKey(attrs.Get(), key.AsMutable<psa_key_id_t>());
}
CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info,
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
AttestationChallenge & attestationChallenge)
{
PsaKdf kdf;
ReturnErrorOnFailure(kdf.Init(secret, salt, info));
return DeriveSessionKeys(kdf, i2rKey, r2iKey, attestationChallenge);
}
CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
AttestationChallenge & attestationChallenge)
{
PsaKdf kdf;
ReturnErrorOnFailure(kdf.Init(hkdfKey, salt, info));
return DeriveSessionKeys(kdf, i2rKey, r2iKey, attestationChallenge);
}
CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(PsaKdf & kdf, Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
AttestationChallenge & attestationChallenge)
{
CHIP_ERROR error;
AesKeyAttributes attrs;
SuccessOrExit(error = kdf.DeriveKey(attrs.Get(), i2rKey.AsMutable<psa_key_id_t>()));
SuccessOrExit(error = kdf.DeriveKey(attrs.Get(), r2iKey.AsMutable<psa_key_id_t>()));
SuccessOrExit(error = kdf.DeriveBytes(MutableByteSpan(attestationChallenge.Bytes(), AttestationChallenge::Capacity())));
exit:
if (error != CHIP_NO_ERROR)
{
DestroyKey(i2rKey);
DestroyKey(r2iKey);
}
return error;
}
void PSASessionKeystore::DestroyKey(Symmetric128BitsKeyHandle & key)
{
auto & keyId = key.AsMutable<psa_key_id_t>();
psa_destroy_key(keyId);
keyId = 0;
}
void PSASessionKeystore::DestroyKey(HkdfKeyHandle & key)
{
auto & keyId = key.AsMutable<psa_key_id_t>();
psa_destroy_key(keyId);
keyId = PSA_KEY_ID_NULL;
}
#if CHIP_CONFIG_ENABLE_ICD_CIP
CHIP_ERROR PSASessionKeystore::PersistICDKey(Symmetric128BitsKeyHandle & key)
{
CHIP_ERROR err;
psa_key_id_t newKeyId = PSA_KEY_ID_NULL;
psa_key_attributes_t attrs;
psa_get_key_attributes(key.As<psa_key_id_t>(), &attrs);
// Exit early if key is already persistent
if (psa_get_key_lifetime(&attrs) == PSA_KEY_LIFETIME_PERSISTENT)
{
psa_reset_key_attributes(&attrs);
return CHIP_NO_ERROR;
}
SuccessOrExit(err = Crypto::FindFreeKeySlotInRange(newKeyId, to_underlying(KeyIdBase::ICDKeyRangeStart), kMaxICDClientKeys));
psa_set_key_lifetime(&attrs, PSA_KEY_LIFETIME_PERSISTENT);
psa_set_key_id(&attrs, newKeyId);
VerifyOrExit(psa_copy_key(key.As<psa_key_id_t>(), &attrs, &newKeyId) == PSA_SUCCESS, err = CHIP_ERROR_INTERNAL);
exit:
DestroyKey(key);
psa_reset_key_attributes(&attrs);
if (err == CHIP_NO_ERROR)
{
SetKeyId(key, newKeyId);
}
return err;
}
#endif
} // namespace Crypto
} // namespace chip