/* * 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 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(); 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()); 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()); 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()); HkdfKeyAttributes attrs; psa_status_t status = psa_import_key(&attrs.Get(), keyMaterial.data(), keyMaterial.size(), &key.AsMutable()); 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()); } 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())); SuccessOrExit(error = kdf.DeriveKey(attrs.Get(), r2iKey.AsMutable())); 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_destroy_key(keyId); keyId = 0; } void PSASessionKeystore::DestroyKey(HkdfKeyHandle & key) { auto & keyId = key.AsMutable(); 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(), &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(), &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