Skip to content

Commit

Permalink
[crypto] Add HKDF key handle and use it during PASE
Browse files Browse the repository at this point in the history
Current SPAKE2+ interface assumes that raw shared secret
is extracted and used by the application to derive session
keys. This prevents using secure crypto APIs, such as PSA,
to perform SPAKE2+ and do the key derivation in a secure
environment, and isolate the application from key material.

1. Add Hkdf128KeyHandle type and add methods for deriving
   session keys from an HKDF key.
2. Change SPAKE2+ interface to return HKDF key handle
   instead of raw key secret.

A similar approach can be taken to improve CASE security
in the future though we would need 256-bit HKDF key support
in such a case.
  • Loading branch information
Damian-Nordic committed Jan 9, 2024
1 parent 7381d76 commit cd208d6
Show file tree
Hide file tree
Showing 14 changed files with 281 additions and 85 deletions.
18 changes: 9 additions & 9 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,15 @@ 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, Hkdf128KeyHandle & key) const
{
CHIP_ERROR error = CHIP_ERROR_INTERNAL;
VerifyOrReturnError(state == CHIP_SPAKE2P_STATE::KC, CHIP_ERROR_INTERNAL);
VerifyOrReturnError(hash_size / 2 == sizeof(Symmetric128BitsKeyByteArray), CHIP_ERROR_INVALID_ARGUMENT);

VerifyOrExit(state == CHIP_SPAKE2P_STATE::KC, error = CHIP_ERROR_INTERNAL);
VerifyOrExit(*out_len >= hash_size / 2, error = CHIP_ERROR_INVALID_ARGUMENT);
Symmetric128BitsKeyByteArray keyMaterial;
memcpy(keyMaterial, Ke, hash_size / 2);

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

CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::InitImpl()
Expand Down
34 changes: 16 additions & 18 deletions src/crypto/CHIPCryptoPAL.h
Original file line number Diff line number Diff line change
Expand Up @@ -575,7 +575,6 @@ using Symmetric128BitsKeyByteArray = uint8_t[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BY
*
* @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.
*/
class Symmetric128BitsKeyHandle
{
Expand Down Expand Up @@ -621,27 +620,20 @@ class Symmetric128BitsKeyHandle
*/
class Aes128KeyHandle final : public Symmetric128BitsKeyHandle
{
public:
Aes128KeyHandle() = default;

Aes128KeyHandle(const Aes128KeyHandle &) = delete;
Aes128KeyHandle(Aes128KeyHandle &&) = delete;
void operator=(const Aes128KeyHandle &) = delete;
void operator=(Aes128KeyHandle &&) = delete;
};

/**
* @brief Platform-specific 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
*/
class Hkdf128KeyHandle final : public Symmetric128BitsKeyHandle
{
};

/**
Expand Down Expand Up @@ -1060,6 +1052,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 @@ -1175,14 +1170,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, Hkdf128KeyHandle & 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 Hkdf128KeyHandle & 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 Hkdf128KeyHandle & 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
54 changes: 50 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,25 @@ CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & ke
return CHIP_NO_ERROR;
}

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

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 +141,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 Hkdf128KeyHandle & 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 Down
9 changes: 9 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,19 @@ 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 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,
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
AttestationChallenge & attestationChallenge) override;
void DestroyKey(Symmetric128BitsKeyHandle & key) override;

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

} // namespace Crypto
Expand Down
14 changes: 14 additions & 0 deletions src/crypto/RawKeySessionKeystore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ CHIP_ERROR RawKeySessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray &
return CHIP_NO_ERROR;
}

CHIP_ERROR RawKeySessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Hkdf128KeyHandle & key)
{
memcpy(key.AsMutable<Symmetric128BitsKeyByteArray>(), keyMaterial, sizeof(Symmetric128BitsKeyByteArray));
return CHIP_NO_ERROR;
}

CHIP_ERROR RawKeySessionKeystore::DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info,
Aes128KeyHandle & key)
{
Expand Down Expand Up @@ -63,6 +69,14 @@ CHIP_ERROR RawKeySessionKeystore::DeriveSessionKeys(const ByteSpan & secret, con
.StatusCode();
}

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

void RawKeySessionKeystore::DestroyKey(Symmetric128BitsKeyHandle & key)
{
ClearSecretData(key.AsMutable<Symmetric128BitsKeyByteArray>());
Expand Down
4 changes: 4 additions & 0 deletions src/crypto/RawKeySessionKeystore.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,14 @@ 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 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,
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
AttestationChallenge & attestationChallenge) override;
void DestroyKey(Symmetric128BitsKeyHandle & key) override;
};

Expand Down
Loading

0 comments on commit cd208d6

Please sign in to comment.