|  | /* | 
|  | *    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 |