Skip to content

Commit

Permalink
Change HKDF key handle to hold key of any length
Browse files Browse the repository at this point in the history
  • Loading branch information
Damian-Nordic committed Jan 11, 2024
1 parent 6121c45 commit 60b7fa3
Show file tree
Hide file tree
Showing 15 changed files with 112 additions and 61 deletions.
8 changes: 2 additions & 6 deletions src/crypto/CHIPCryptoPAL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -501,15 +501,11 @@ CHIP_ERROR Spake2p::KeyConfirm(const uint8_t * in, size_t in_len)
return CHIP_NO_ERROR;
}

CHIP_ERROR Spake2p::GetKeys(SessionKeystore & keystore, Hkdf128KeyHandle & key) const
CHIP_ERROR Spake2p::GetKeys(SessionKeystore & keystore, HkdfKeyHandle & key) const
{
VerifyOrReturnError(state == CHIP_SPAKE2P_STATE::KC, CHIP_ERROR_INTERNAL);
VerifyOrReturnError(hash_size / 2 == sizeof(Symmetric128BitsKeyByteArray), CHIP_ERROR_INVALID_ARGUMENT);

Symmetric128BitsKeyByteArray keyMaterial;
memcpy(keyMaterial, Ke, hash_size / 2);

return keystore.CreateKey(keyMaterial, key);
return keystore.CreateKey(ByteSpan(Ke, hash_size / 2), key);
}

CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::InitImpl()
Expand Down
47 changes: 26 additions & 21 deletions src/crypto/CHIPCryptoPAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -563,26 +563,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.
* @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 @@ -603,27 +601,34 @@ 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 128-bit symmetric key handle
*/
class Symmetric128BitsKeyHandle : public SymmetricKeyHandle<CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES>
{
};

/**
* @brief Platform-specific AES key handle
* @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
{
Expand All @@ -632,7 +637,7 @@ class Hmac128KeyHandle final : public Symmetric128BitsKeyHandle
/**
* @brief Platform-specific HKDF key handle
*/
class Hkdf128KeyHandle final : public Symmetric128BitsKeyHandle
class HkdfKeyHandle final : public SymmetricKeyHandle<CHIP_CONFIG_HKDF_KEY_HANDLE_CONTEXT_SIZE>
{
};

Expand Down Expand Up @@ -1180,7 +1185,7 @@ class Spake2p
*
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
**/
CHIP_ERROR GetKeys(SessionKeystore & keystore, Hkdf128KeyHandle & key) const;
CHIP_ERROR GetKeys(SessionKeystore & keystore, HkdfKeyHandle & key) const;

CHIP_ERROR InternalHash(const uint8_t * in, size_t in_len);
CHIP_ERROR WriteMN();
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/CHIPCryptoPALPSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ CHIP_ERROR PsaKdf::Init(const ByteSpan & secret, const ByteSpan & salt, const By
return InitOperation(mSecretKeyId, salt, info);
}

CHIP_ERROR PsaKdf::Init(const Hkdf128KeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info)
CHIP_ERROR PsaKdf::Init(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info)
{
return InitOperation(hkdfKey.As<psa_key_id_t>(), salt, info);
}
Expand Down
2 changes: 1 addition & 1 deletion src/crypto/CHIPCryptoPALPSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ class PsaKdf
/**
* @brief Initializes the key derivation operation.
*/
CHIP_ERROR Init(const Hkdf128KeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info);
CHIP_ERROR Init(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info);

/**
* @brief Derives raw key material from the operation.
Expand Down
15 changes: 11 additions & 4 deletions src/crypto/PSASessionKeystore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,13 @@ CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & ke
return CHIP_NO_ERROR;
}

CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Hkdf128KeyHandle & key)
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, sizeof(Symmetric128BitsKeyByteArray), &key.AsMutable<psa_key_id_t>());
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);

Expand Down Expand Up @@ -146,7 +145,7 @@ CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(const ByteSpan & secret, const
return DeriveSessionKeys(kdf, i2rKey, r2iKey, attestationChallenge);
}

CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(const Hkdf128KeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
AttestationChallenge & attestationChallenge)
{
Expand Down Expand Up @@ -184,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
5 changes: 3 additions & 2 deletions src/crypto/PSASessionKeystore.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,16 @@ 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 Symmetric128BitsKeyByteArray & keyMaterial, Hkdf128KeyHandle & 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 Hkdf128KeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
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,
Expand Down
39 changes: 31 additions & 8 deletions src/crypto/RawKeySessionKeystore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,21 @@

#include <lib/support/BufferReader.h>

#include <cstdint>

namespace chip {
namespace Crypto {

using HKDF_sha_crypto = HKDF_sha;
// The underlying representation of the HKDF key handle
struct RawHkdfKeyHandle
{
ByteSpan Span() const { return ByteSpan(data, size); }

static constexpr size_t kMaxDataSize = std::min(CHIP_CONFIG_HKDF_KEY_HANDLE_CONTEXT_SIZE - 1, UINT8_MAX);

uint8_t data[kMaxDataSize];
uint8_t size;
};

CHIP_ERROR RawKeySessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Aes128KeyHandle & key)
{
Expand All @@ -36,16 +47,21 @@ CHIP_ERROR RawKeySessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray &
return CHIP_NO_ERROR;
}

CHIP_ERROR RawKeySessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Hkdf128KeyHandle & key)
CHIP_ERROR RawKeySessionKeystore::CreateKey(const ByteSpan & keyMaterial, HkdfKeyHandle & key)
{
memcpy(key.AsMutable<Symmetric128BitsKeyByteArray>(), keyMaterial, sizeof(Symmetric128BitsKeyByteArray));
RawHkdfKeyHandle & rawKey = key.AsMutable<RawHkdfKeyHandle>();

VerifyOrReturnError(keyMaterial.size() <= sizeof(rawKey.data), CHIP_ERROR_BUFFER_TOO_SMALL);
memcpy(rawKey.data, keyMaterial.data(), keyMaterial.size());
rawKey.size = static_cast<uint8_t>(keyMaterial.size());

return CHIP_NO_ERROR;
}

CHIP_ERROR RawKeySessionKeystore::DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info,
Aes128KeyHandle & key)
{
HKDF_sha_crypto hkdf;
HKDF_sha hkdf;

return hkdf.HKDF_SHA256(secret.ConstBytes(), secret.Length(), salt.data(), salt.size(), info.data(), info.size(),
key.AsMutable<Symmetric128BitsKeyByteArray>(), sizeof(Symmetric128BitsKeyByteArray));
Expand All @@ -55,7 +71,7 @@ CHIP_ERROR RawKeySessionKeystore::DeriveSessionKeys(const ByteSpan & secret, con
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
AttestationChallenge & attestationChallenge)
{
HKDF_sha_crypto hkdf;
HKDF_sha hkdf;
uint8_t keyMaterial[2 * sizeof(Symmetric128BitsKeyByteArray) + AttestationChallenge::Capacity()];

ReturnErrorOnFailure(hkdf.HKDF_SHA256(secret.data(), secret.size(), salt.data(), salt.size(), info.data(), info.size(),
Expand All @@ -69,18 +85,25 @@ CHIP_ERROR RawKeySessionKeystore::DeriveSessionKeys(const ByteSpan & secret, con
.StatusCode();
}

CHIP_ERROR RawKeySessionKeystore::DeriveSessionKeys(const Hkdf128KeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
CHIP_ERROR RawKeySessionKeystore::DeriveSessionKeys(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
AttestationChallenge & attestationChallenge)
{
return DeriveSessionKeys(ByteSpan(hkdfKey.As<Symmetric128BitsKeyByteArray>()), salt, info, i2rKey, r2iKey,
attestationChallenge);
return DeriveSessionKeys(hkdfKey.As<RawHkdfKeyHandle>().Span(), salt, info, i2rKey, r2iKey, attestationChallenge);
}

void RawKeySessionKeystore::DestroyKey(Symmetric128BitsKeyHandle & key)
{
ClearSecretData(key.AsMutable<Symmetric128BitsKeyByteArray>());
}

void RawKeySessionKeystore::DestroyKey(HkdfKeyHandle & key)
{
RawHkdfKeyHandle & rawKey = key.AsMutable<RawHkdfKeyHandle>();

ClearSecretData(rawKey.data);
rawKey.size = 0;
}

} // namespace Crypto
} // namespace chip
5 changes: 3 additions & 2 deletions src/crypto/RawKeySessionKeystore.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,16 @@ class RawKeySessionKeystore : 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 Symmetric128BitsKeyByteArray & keyMaterial, Hkdf128KeyHandle & 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 Hkdf128KeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
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;
};

} // namespace Crypto
Expand Down
14 changes: 11 additions & 3 deletions src/crypto/SessionKeystore.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class SessionKeystore
virtual CHIP_ERROR CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Hmac128KeyHandle & key) = 0;

/**
* @brief Import raw key material and return a key handle for a 128-bit key that can be used to do HKDF.
* @brief Import raw key material and return a key handle for an HKDF key.
*
* @note This method should only be used when using the raw key material in the Matter stack
* cannot be avoided. Ideally, crypto interfaces should allow platforms to perform all the
Expand All @@ -77,7 +77,7 @@ class SessionKeystore
* If the method returns no error, the application is responsible for destroying the handle
* using the DestroyKey() method when the key is no longer needed.
*/
virtual CHIP_ERROR CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Hkdf128KeyHandle & key) = 0;
virtual CHIP_ERROR CreateKey(const ByteSpan & keyMaterial, HkdfKeyHandle & key) = 0;

/**
* @brief Destroy key.
Expand All @@ -87,6 +87,14 @@ class SessionKeystore
*/
virtual void DestroyKey(Symmetric128BitsKeyHandle & key) = 0;

/**
* @brief Destroy key.
*
* The method can take an uninitialized handle in which case it is a no-op.
* As a result of calling this method, the handle is put in the uninitialized state.
*/
virtual void DestroyKey(HkdfKeyHandle & key) = 0;

/****************************
* SessionKeyDerivation APIs
*****************************/
Expand Down Expand Up @@ -126,7 +134,7 @@ class SessionKeystore
* using DestroyKey() method when the keys are no longer needed. On failure, the method must
* release all handles that it allocated so far.
*/
virtual CHIP_ERROR DeriveSessionKeys(const Hkdf128KeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
virtual CHIP_ERROR DeriveSessionKeys(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
AttestationChallenge & attestationChallenge) = 0;
};
Expand Down
16 changes: 6 additions & 10 deletions src/crypto/tests/CHIPCryptoPALTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ class HeapChecker

// Verify that two HKDF keys are equal by checking if they generate the same attestation challenge.
// Note that the keys cannot be compared directly because they are given as key handles.
void AssertKeysEqual(nlTestSuite * inSuite, SessionKeystore & keystore, Hkdf128KeyHandle & left, const Hkdf128KeyHandle & right)
void AssertKeysEqual(nlTestSuite * inSuite, SessionKeystore & keystore, HkdfKeyHandle & left, const HkdfKeyHandle & right)
{
auto generateChallenge = [&](const Hkdf128KeyHandle & key, AttestationChallenge & challenge) -> void {
auto generateChallenge = [&](const HkdfKeyHandle & key, AttestationChallenge & challenge) -> void {
constexpr uint8_t kTestSalt[] = { 'T', 'E', 'S', 'T', 'S', 'A', 'L', 'T' };
constexpr uint8_t kTestInfo[] = { 'T', 'E', 'S', 'T', 'I', 'N', 'F', 'O' };

Expand Down Expand Up @@ -1965,23 +1965,19 @@ static void TestSPAKE2P_RFC(nlTestSuite * inSuite, void * inContext)
NL_TEST_ASSERT(inSuite, error == CHIP_NO_ERROR);

// Import HKDF key from the test vector to the keystore
Symmetric128BitsKeyByteArray vectorKeMaterial;
Hkdf128KeyHandle vectorKe;

NL_TEST_ASSERT(inSuite, sizeof(vectorKeMaterial) == vector->Ke_len);
memcpy(vectorKeMaterial, vector->Ke, vector->Ke_len);
error = keystore.CreateKey(vectorKeMaterial, vectorKe);
HkdfKeyHandle vectorKe;
error = keystore.CreateKey(ByteSpan(vector->Ke, vector->Ke_len), vectorKe);
NL_TEST_ASSERT(inSuite, error == CHIP_NO_ERROR);

// Verify that both sides generated the same HKDF key as in the test vector
// Since the HKDF keys may not be availabe in the raw form, do not compare them directly,
// but rather check if the same attestation challenge is derived from
Hkdf128KeyHandle PKe;
HkdfKeyHandle PKe;
error = Prover.GetKeys(keystore, PKe);
NL_TEST_ASSERT(inSuite, error == CHIP_NO_ERROR);
AssertKeysEqual(inSuite, keystore, PKe, vectorKe);

Hkdf128KeyHandle VKe;
HkdfKeyHandle VKe;
error = Verifier.GetKeys(keystore, VKe);
NL_TEST_ASSERT(inSuite, error == CHIP_NO_ERROR);
AssertKeysEqual(inSuite, keystore, VKe, vectorKe);
Expand Down
13 changes: 13 additions & 0 deletions src/lib/core/CHIPConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,19 @@
#define CHIP_CONFIG_SHA256_CONTEXT_ALIGN size_t
#endif // CHIP_CONFIG_SHA256_CONTEXT_ALIGN

/**
* @def CHIP_CONFIG_HKDF_KEY_HANDLE_CONTEXT_SIZE
*
* @brief
* Size of the statically allocated context for the HKDF key handle in CryptoPAL.
*
* The default size is selected so that the key handle is able to fit 256-bit raw key
* material along with the size information.
*/
#ifndef CHIP_CONFIG_HKDF_KEY_HANDLE_CONTEXT_SIZE
#define CHIP_CONFIG_HKDF_KEY_HANDLE_CONTEXT_SIZE (32 + 1)
#endif // CHIP_CONFIG_HKDF_KEY_HANDLE_CONTEXT_SIZE

/**
* @def CHIP_CONFIG_MAX_UNSOLICITED_MESSAGE_HANDLERS
*
Expand Down
Loading

0 comments on commit 60b7fa3

Please sign in to comment.