Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[crypto] Add HKDF key handle and use it during PASE #31311

Merged
merged 3 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions src/crypto/CHIPCryptoPAL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
*/

#include "CHIPCryptoPAL.h"

#include "SessionKeystore.h"

#include <lib/asn1/ASN1.h>
#include <lib/asn1/ASN1Macros.h>
#include <lib/core/CHIPEncoding.h>
Expand Down Expand Up @@ -498,18 +501,11 @@ CHIP_ERROR Spake2p::KeyConfirm(const uint8_t * in, size_t in_len)
return CHIP_NO_ERROR;
}

CHIP_ERROR Spake2p::GetKeys(uint8_t * out, size_t * out_len)
CHIP_ERROR Spake2p::GetKeys(SessionKeystore & keystore, HkdfKeyHandle & key) const
{
CHIP_ERROR error = CHIP_ERROR_INTERNAL;

VerifyOrExit(state == CHIP_SPAKE2P_STATE::KC, error = CHIP_ERROR_INTERNAL);
VerifyOrExit(*out_len >= hash_size / 2, error = CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(state == CHIP_SPAKE2P_STATE::KC, CHIP_ERROR_INTERNAL);

memcpy(out, Ke, hash_size / 2);
error = CHIP_NO_ERROR;
exit:
*out_len = hash_size / 2;
return error;
return keystore.CreateKey(ByteSpan(Ke, hash_size / 2), key);
}

CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::InitImpl()
Expand Down
77 changes: 40 additions & 37 deletions src/crypto/CHIPCryptoPAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -590,27 +590,24 @@ class P256Keypair : public P256KeypairBase
bool mInitialized = false;
};

using Symmetric128BitsKeyByteArray = uint8_t[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];

/**
* @brief Platform-specific Symmetric key handle
* @brief Platform-specific symmetric key handle
*
* The class represents a key used by the Matter stack either in the form of raw key material or key
* reference, depending on the platform. To achieve that, it contains an opaque context that can be
* cast to a concrete representation used by the given platform. Note that currently Matter uses
* 128-bit symmetric keys only.
* cast to a concrete representation used by the given platform.
*
* @note Symmetric128BitsKeyHandle is an abstract class to force child classes for each key handle type.
* Symmetric128BitsKeyHandle class implements all the necessary components for handles.
* Child classes only need to implement a constructor and delete all the copy operators.
* @note SymmetricKeyHandle is an abstract class to force child classes for each key handle type.
* SymmetricKeyHandle class implements all the necessary components for handles.
*/
class Symmetric128BitsKeyHandle
template <size_t ContextSize>
class SymmetricKeyHandle
{
public:
Symmetric128BitsKeyHandle(const Symmetric128BitsKeyHandle &) = delete;
Symmetric128BitsKeyHandle(Symmetric128BitsKeyHandle &&) = delete;
void operator=(const Symmetric128BitsKeyHandle &) = delete;
void operator=(Symmetric128BitsKeyHandle &&) = delete;
SymmetricKeyHandle(const SymmetricKeyHandle &) = delete;
SymmetricKeyHandle(SymmetricKeyHandle &&) = delete;
void operator=(const SymmetricKeyHandle &) = delete;
void operator=(SymmetricKeyHandle &&) = delete;

/**
* @brief Get internal context cast to the desired key representation
Expand All @@ -631,44 +628,44 @@ class Symmetric128BitsKeyHandle
}

protected:
Symmetric128BitsKeyHandle() = default;
~Symmetric128BitsKeyHandle() { ClearSecretData(mContext.mOpaque); }
SymmetricKeyHandle() = default;
~SymmetricKeyHandle() { ClearSecretData(mContext.mOpaque); }

private:
static constexpr size_t kContextSize = CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES;

struct alignas(uintptr_t) OpaqueContext
{
uint8_t mOpaque[kContextSize] = {};
uint8_t mOpaque[ContextSize] = {};
} mContext;
};

using Symmetric128BitsKeyByteArray = uint8_t[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];

/**
* @brief Platform-specific AES key handle
* @brief Platform-specific 128-bit symmetric key handle
*/
class Aes128KeyHandle final : public Symmetric128BitsKeyHandle
class Symmetric128BitsKeyHandle : public SymmetricKeyHandle<CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES>
{
public:
Aes128KeyHandle() = default;
};

Aes128KeyHandle(const Aes128KeyHandle &) = delete;
Aes128KeyHandle(Aes128KeyHandle &&) = delete;
void operator=(const Aes128KeyHandle &) = delete;
void operator=(Aes128KeyHandle &&) = delete;
/**
* @brief Platform-specific 128-bit AES key handle
*/
class Aes128KeyHandle final : public Symmetric128BitsKeyHandle
{
};

/**
* @brief Platform-specific HMAC key handle
* @brief Platform-specific 128-bit HMAC key handle
*/
class Hmac128KeyHandle final : public Symmetric128BitsKeyHandle
{
public:
Hmac128KeyHandle() = default;
};

Hmac128KeyHandle(const Hmac128KeyHandle &) = delete;
Hmac128KeyHandle(Hmac128KeyHandle &&) = delete;
void operator=(const Hmac128KeyHandle &) = delete;
void operator=(Hmac128KeyHandle &&) = delete;
/**
* @brief Platform-specific HKDF key handle
Damian-Nordic marked this conversation as resolved.
Show resolved Hide resolved
*/
class HkdfKeyHandle final : public SymmetricKeyHandle<CHIP_CONFIG_HKDF_KEY_HANDLE_CONTEXT_SIZE>
{
};

/**
Expand Down Expand Up @@ -1087,6 +1084,9 @@ class PBKDF2_sha256
unsigned int iteration_count, uint32_t key_length, uint8_t * output);
};

// TODO: Extract Spake2p to a separate header and replace the forward declaration with #include SessionKeystore.h
class SessionKeystore;

/**
* The below class implements the draft 01 version of the Spake2+ protocol as
* defined in https://www.ietf.org/id/draft-bar-cfrg-spake2plus-01.html.
Expand Down Expand Up @@ -1202,14 +1202,17 @@ class Spake2p
virtual CHIP_ERROR KeyConfirm(const uint8_t * in, size_t in_len);

/**
* @brief Return the shared secret.
* @brief Return the shared HKDF key.
*
* Returns the shared key established during the Spake2+ process, which can be used
* to derive application-specific keys using HKDF.
*
* @param out The output secret.
* @param out_len The output secret length.
* @param keystore The session keystore for managing the HKDF key lifetime.
* @param key The output HKDF key.
*
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
**/
CHIP_ERROR GetKeys(uint8_t * out, size_t * out_len);
CHIP_ERROR GetKeys(SessionKeystore & keystore, HkdfKeyHandle & key) const;

CHIP_ERROR InternalHash(const uint8_t * in, size_t in_len);
CHIP_ERROR WriteMN();
Expand Down
19 changes: 14 additions & 5 deletions src/crypto/CHIPCryptoPALPSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ void Hash_SHA256_stream::Clear()
psa_hash_abort(toHashOperation(&mContext));
}

CHIP_ERROR PsaKdf::Init(psa_algorithm_t algorithm, const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info)
CHIP_ERROR PsaKdf::Init(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info)
{
psa_status_t status = PSA_SUCCESS;
psa_key_attributes_t attrs = PSA_KEY_ATTRIBUTES_INIT;
Expand All @@ -284,7 +284,17 @@ CHIP_ERROR PsaKdf::Init(psa_algorithm_t algorithm, const ByteSpan & secret, cons
psa_reset_key_attributes(&attrs);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);

status = psa_key_derivation_setup(&mOperation, algorithm);
return InitOperation(mSecretKeyId, salt, info);
}

CHIP_ERROR PsaKdf::Init(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info)
{
return InitOperation(hkdfKey.As<psa_key_id_t>(), salt, info);
}

CHIP_ERROR PsaKdf::InitOperation(psa_key_id_t hkdfKey, const ByteSpan & salt, const ByteSpan & info)
{
psa_status_t status = psa_key_derivation_setup(&mOperation, PSA_ALG_HKDF(PSA_ALG_SHA_256));
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);

if (salt.size() > 0)
Expand All @@ -293,7 +303,7 @@ CHIP_ERROR PsaKdf::Init(psa_algorithm_t algorithm, const ByteSpan & secret, cons
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
}

status = psa_key_derivation_input_key(&mOperation, PSA_KEY_DERIVATION_INPUT_SECRET, mSecretKeyId);
status = psa_key_derivation_input_key(&mOperation, PSA_KEY_DERIVATION_INPUT_SECRET, hkdfKey);
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);

status = psa_key_derivation_input_bytes(&mOperation, PSA_KEY_DERIVATION_INPUT_INFO, info.data(), info.size());
Expand Down Expand Up @@ -328,8 +338,7 @@ CHIP_ERROR HKDF_sha::HKDF_SHA256(const uint8_t * secret, const size_t secret_len

PsaKdf kdf;

ReturnErrorOnFailure(kdf.Init(PSA_ALG_HKDF(PSA_ALG_SHA_256), ByteSpan(secret, secret_length), ByteSpan(salt, salt_length),
ByteSpan(info, info_length)));
ReturnErrorOnFailure(kdf.Init(ByteSpan(secret, secret_length), ByteSpan(salt, salt_length), ByteSpan(info, info_length)));

return kdf.DeriveBytes(MutableByteSpan(out_buffer, out_length));
}
Expand Down
9 changes: 8 additions & 1 deletion src/crypto/CHIPCryptoPALPSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,12 @@ class PsaKdf
/**
* @brief Initializes the key derivation operation.
*/
CHIP_ERROR Init(psa_algorithm_t algorithm, const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info);
CHIP_ERROR Init(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info);

/**
* @brief Initializes the key derivation operation.
*/
CHIP_ERROR Init(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info);

/**
* @brief Derives raw key material from the operation.
Expand Down Expand Up @@ -139,6 +144,8 @@ class PsaKdf
CHIP_ERROR DeriveKey(const psa_key_attributes_t & attributes, psa_key_id_t & keyId);

private:
CHIP_ERROR InitOperation(psa_key_id_t hkdfKey, const ByteSpan & salt, const ByteSpan & info);

psa_key_id_t mSecretKeyId = 0;
psa_key_derivation_operation_t mOperation = PSA_KEY_DERIVATION_OPERATION_INIT;
};
Expand Down
61 changes: 57 additions & 4 deletions src/crypto/PSASessionKeystore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

#include "PSASessionKeystore.h"

#include <crypto/CHIPCryptoPALPSA.h>

#include <psa/crypto.h>

namespace chip {
Expand Down Expand Up @@ -66,6 +64,24 @@ class HmacKeyAttributes
psa_key_attributes_t mAttrs = PSA_KEY_ATTRIBUTES_INIT;
};

class HkdfKeyAttributes
{
public:
HkdfKeyAttributes()
{
psa_set_key_type(&mAttrs, PSA_KEY_TYPE_DERIVE);
psa_set_key_algorithm(&mAttrs, PSA_ALG_HKDF(PSA_ALG_SHA_256));
psa_set_key_usage_flags(&mAttrs, PSA_KEY_USAGE_DERIVE);
}

~HkdfKeyAttributes() { psa_reset_key_attributes(&mAttrs); }

const psa_key_attributes_t & Get() { return mAttrs; }

private:
psa_key_attributes_t mAttrs = PSA_KEY_ATTRIBUTES_INIT;
};

} // namespace

CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Aes128KeyHandle & key)
Expand Down Expand Up @@ -95,11 +111,24 @@ CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & ke
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>());

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(PSA_ALG_HKDF(PSA_ALG_SHA_256), secret.Span(), salt, info));
ReturnErrorOnFailure(kdf.Init(secret.Span(), salt, info));

AesKeyAttributes attrs;

Expand All @@ -111,8 +140,24 @@ CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(const ByteSpan & secret, const
AttestationChallenge & attestationChallenge)
{
PsaKdf kdf;
ReturnErrorOnFailure(kdf.Init(PSA_ALG_HKDF(PSA_ALG_SHA_256), secret, salt, info));
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;

Expand All @@ -138,5 +183,13 @@ void PSASessionKeystore::DestroyKey(Symmetric128BitsKeyHandle & key)
keyId = 0;
}

void PSASessionKeystore::DestroyKey(HkdfKeyHandle & key)
{
auto & keyId = key.AsMutable<psa_key_id_t>();

psa_destroy_key(keyId);
keyId = 0;
}

} // namespace Crypto
} // namespace chip
10 changes: 10 additions & 0 deletions src/crypto/PSASessionKeystore.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#pragma once

#include <crypto/CHIPCryptoPALPSA.h>
#include <crypto/SessionKeystore.h>

namespace chip {
Expand All @@ -27,11 +28,20 @@ class PSASessionKeystore : public SessionKeystore
public:
CHIP_ERROR CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Aes128KeyHandle & key) override;
CHIP_ERROR CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Hmac128KeyHandle & key) override;
CHIP_ERROR CreateKey(const ByteSpan & keyMaterial, HkdfKeyHandle & key) override;
CHIP_ERROR DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info,
Aes128KeyHandle & key) override;
CHIP_ERROR DeriveSessionKeys(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info, Aes128KeyHandle & i2rKey,
Aes128KeyHandle & r2iKey, AttestationChallenge & attestationChallenge) override;
CHIP_ERROR DeriveSessionKeys(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
AttestationChallenge & attestationChallenge) override;
void DestroyKey(Symmetric128BitsKeyHandle & key) override;
void DestroyKey(HkdfKeyHandle & key) override;

private:
CHIP_ERROR DeriveSessionKeys(PsaKdf & kdf, Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
AttestationChallenge & attestationChallenge);
};

} // namespace Crypto
Expand Down
Loading
Loading