Skip to content

Commit 53443cc

Browse files
Damian-Nordicraul-marquez-csa
authored andcommitted
[crypto] Add HKDF key handle and use it during PASE (project-chip#31311)
* [crypto] Add HKDF key handle and use it during PASE 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. * Change HKDF key handle to hold key of any length * Code review
1 parent acd5aa9 commit 53443cc

16 files changed

+368
-117
lines changed

src/crypto/CHIPCryptoPAL.cpp

+6-10
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
*/
2222

2323
#include "CHIPCryptoPAL.h"
24+
25+
#include "SessionKeystore.h"
26+
2427
#include <lib/asn1/ASN1.h>
2528
#include <lib/asn1/ASN1Macros.h>
2629
#include <lib/core/CHIPEncoding.h>
@@ -498,18 +501,11 @@ CHIP_ERROR Spake2p::KeyConfirm(const uint8_t * in, size_t in_len)
498501
return CHIP_NO_ERROR;
499502
}
500503

501-
CHIP_ERROR Spake2p::GetKeys(uint8_t * out, size_t * out_len)
504+
CHIP_ERROR Spake2p::GetKeys(SessionKeystore & keystore, HkdfKeyHandle & key) const
502505
{
503-
CHIP_ERROR error = CHIP_ERROR_INTERNAL;
504-
505-
VerifyOrExit(state == CHIP_SPAKE2P_STATE::KC, error = CHIP_ERROR_INTERNAL);
506-
VerifyOrExit(*out_len >= hash_size / 2, error = CHIP_ERROR_INVALID_ARGUMENT);
506+
VerifyOrReturnError(state == CHIP_SPAKE2P_STATE::KC, CHIP_ERROR_INTERNAL);
507507

508-
memcpy(out, Ke, hash_size / 2);
509-
error = CHIP_NO_ERROR;
510-
exit:
511-
*out_len = hash_size / 2;
512-
return error;
508+
return keystore.CreateKey(ByteSpan(Ke, hash_size / 2), key);
513509
}
514510

515511
CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::InitImpl()

src/crypto/CHIPCryptoPAL.h

+40-37
Original file line numberDiff line numberDiff line change
@@ -593,27 +593,24 @@ class P256Keypair : public P256KeypairBase
593593
bool mInitialized = false;
594594
};
595595

596-
using Symmetric128BitsKeyByteArray = uint8_t[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];
597-
598596
/**
599-
* @brief Platform-specific Symmetric key handle
597+
* @brief Platform-specific symmetric key handle
600598
*
601599
* The class represents a key used by the Matter stack either in the form of raw key material or key
602600
* reference, depending on the platform. To achieve that, it contains an opaque context that can be
603-
* cast to a concrete representation used by the given platform. Note that currently Matter uses
604-
* 128-bit symmetric keys only.
601+
* cast to a concrete representation used by the given platform.
605602
*
606-
* @note Symmetric128BitsKeyHandle is an abstract class to force child classes for each key handle type.
607-
* Symmetric128BitsKeyHandle class implements all the necessary components for handles.
608-
* Child classes only need to implement a constructor and delete all the copy operators.
603+
* @note SymmetricKeyHandle is an abstract class to force child classes for each key handle type.
604+
* SymmetricKeyHandle class implements all the necessary components for handles.
609605
*/
610-
class Symmetric128BitsKeyHandle
606+
template <size_t ContextSize>
607+
class SymmetricKeyHandle
611608
{
612609
public:
613-
Symmetric128BitsKeyHandle(const Symmetric128BitsKeyHandle &) = delete;
614-
Symmetric128BitsKeyHandle(Symmetric128BitsKeyHandle &&) = delete;
615-
void operator=(const Symmetric128BitsKeyHandle &) = delete;
616-
void operator=(Symmetric128BitsKeyHandle &&) = delete;
610+
SymmetricKeyHandle(const SymmetricKeyHandle &) = delete;
611+
SymmetricKeyHandle(SymmetricKeyHandle &&) = delete;
612+
void operator=(const SymmetricKeyHandle &) = delete;
613+
void operator=(SymmetricKeyHandle &&) = delete;
617614

618615
/**
619616
* @brief Get internal context cast to the desired key representation
@@ -634,44 +631,44 @@ class Symmetric128BitsKeyHandle
634631
}
635632

636633
protected:
637-
Symmetric128BitsKeyHandle() = default;
638-
~Symmetric128BitsKeyHandle() { ClearSecretData(mContext.mOpaque); }
634+
SymmetricKeyHandle() = default;
635+
~SymmetricKeyHandle() { ClearSecretData(mContext.mOpaque); }
639636

640637
private:
641-
static constexpr size_t kContextSize = CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES;
642-
643638
struct alignas(uintptr_t) OpaqueContext
644639
{
645-
uint8_t mOpaque[kContextSize] = {};
640+
uint8_t mOpaque[ContextSize] = {};
646641
} mContext;
647642
};
648643

644+
using Symmetric128BitsKeyByteArray = uint8_t[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];
645+
649646
/**
650-
* @brief Platform-specific AES key handle
647+
* @brief Platform-specific 128-bit symmetric key handle
651648
*/
652-
class Aes128KeyHandle final : public Symmetric128BitsKeyHandle
649+
class Symmetric128BitsKeyHandle : public SymmetricKeyHandle<CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES>
653650
{
654-
public:
655-
Aes128KeyHandle() = default;
651+
};
656652

657-
Aes128KeyHandle(const Aes128KeyHandle &) = delete;
658-
Aes128KeyHandle(Aes128KeyHandle &&) = delete;
659-
void operator=(const Aes128KeyHandle &) = delete;
660-
void operator=(Aes128KeyHandle &&) = delete;
653+
/**
654+
* @brief Platform-specific 128-bit AES key handle
655+
*/
656+
class Aes128KeyHandle final : public Symmetric128BitsKeyHandle
657+
{
661658
};
662659

663660
/**
664-
* @brief Platform-specific HMAC key handle
661+
* @brief Platform-specific 128-bit HMAC key handle
665662
*/
666663
class Hmac128KeyHandle final : public Symmetric128BitsKeyHandle
667664
{
668-
public:
669-
Hmac128KeyHandle() = default;
665+
};
670666

671-
Hmac128KeyHandle(const Hmac128KeyHandle &) = delete;
672-
Hmac128KeyHandle(Hmac128KeyHandle &&) = delete;
673-
void operator=(const Hmac128KeyHandle &) = delete;
674-
void operator=(Hmac128KeyHandle &&) = delete;
667+
/**
668+
* @brief Platform-specific HKDF key handle
669+
*/
670+
class HkdfKeyHandle final : public SymmetricKeyHandle<CHIP_CONFIG_HKDF_KEY_HANDLE_CONTEXT_SIZE>
671+
{
675672
};
676673

677674
/**
@@ -1090,6 +1087,9 @@ class PBKDF2_sha256
10901087
unsigned int iteration_count, uint32_t key_length, uint8_t * output);
10911088
};
10921089

1090+
// TODO: Extract Spake2p to a separate header and replace the forward declaration with #include SessionKeystore.h
1091+
class SessionKeystore;
1092+
10931093
/**
10941094
* The below class implements the draft 01 version of the Spake2+ protocol as
10951095
* defined in https://www.ietf.org/id/draft-bar-cfrg-spake2plus-01.html.
@@ -1205,14 +1205,17 @@ class Spake2p
12051205
virtual CHIP_ERROR KeyConfirm(const uint8_t * in, size_t in_len);
12061206

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

12171220
CHIP_ERROR InternalHash(const uint8_t * in, size_t in_len);
12181221
CHIP_ERROR WriteMN();

src/crypto/CHIPCryptoPALPSA.cpp

+14-5
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ void Hash_SHA256_stream::Clear()
271271
psa_hash_abort(toHashOperation(&mContext));
272272
}
273273

274-
CHIP_ERROR PsaKdf::Init(psa_algorithm_t algorithm, const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info)
274+
CHIP_ERROR PsaKdf::Init(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info)
275275
{
276276
psa_status_t status = PSA_SUCCESS;
277277
psa_key_attributes_t attrs = PSA_KEY_ATTRIBUTES_INIT;
@@ -284,7 +284,17 @@ CHIP_ERROR PsaKdf::Init(psa_algorithm_t algorithm, const ByteSpan & secret, cons
284284
psa_reset_key_attributes(&attrs);
285285
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
286286

287-
status = psa_key_derivation_setup(&mOperation, algorithm);
287+
return InitOperation(mSecretKeyId, salt, info);
288+
}
289+
290+
CHIP_ERROR PsaKdf::Init(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info)
291+
{
292+
return InitOperation(hkdfKey.As<psa_key_id_t>(), salt, info);
293+
}
294+
295+
CHIP_ERROR PsaKdf::InitOperation(psa_key_id_t hkdfKey, const ByteSpan & salt, const ByteSpan & info)
296+
{
297+
psa_status_t status = psa_key_derivation_setup(&mOperation, PSA_ALG_HKDF(PSA_ALG_SHA_256));
288298
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
289299

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

296-
status = psa_key_derivation_input_key(&mOperation, PSA_KEY_DERIVATION_INPUT_SECRET, mSecretKeyId);
306+
status = psa_key_derivation_input_key(&mOperation, PSA_KEY_DERIVATION_INPUT_SECRET, hkdfKey);
297307
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
298308

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

329339
PsaKdf kdf;
330340

331-
ReturnErrorOnFailure(kdf.Init(PSA_ALG_HKDF(PSA_ALG_SHA_256), ByteSpan(secret, secret_length), ByteSpan(salt, salt_length),
332-
ByteSpan(info, info_length)));
341+
ReturnErrorOnFailure(kdf.Init(ByteSpan(secret, secret_length), ByteSpan(salt, salt_length), ByteSpan(info, info_length)));
333342

334343
return kdf.DeriveBytes(MutableByteSpan(out_buffer, out_length));
335344
}

src/crypto/CHIPCryptoPALPSA.h

+8-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,12 @@ class PsaKdf
109109
/**
110110
* @brief Initializes the key derivation operation.
111111
*/
112-
CHIP_ERROR Init(psa_algorithm_t algorithm, const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info);
112+
CHIP_ERROR Init(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info);
113+
114+
/**
115+
* @brief Initializes the key derivation operation.
116+
*/
117+
CHIP_ERROR Init(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info);
113118

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

141146
private:
147+
CHIP_ERROR InitOperation(psa_key_id_t hkdfKey, const ByteSpan & salt, const ByteSpan & info);
148+
142149
psa_key_id_t mSecretKeyId = 0;
143150
psa_key_derivation_operation_t mOperation = PSA_KEY_DERIVATION_OPERATION_INIT;
144151
};

src/crypto/PSASessionKeystore.cpp

+57-4
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717

1818
#include "PSASessionKeystore.h"
1919

20-
#include <crypto/CHIPCryptoPALPSA.h>
21-
2220
#include <psa/crypto.h>
2321

2422
namespace chip {
@@ -66,6 +64,24 @@ class HmacKeyAttributes
6664
psa_key_attributes_t mAttrs = PSA_KEY_ATTRIBUTES_INIT;
6765
};
6866

67+
class HkdfKeyAttributes
68+
{
69+
public:
70+
HkdfKeyAttributes()
71+
{
72+
psa_set_key_type(&mAttrs, PSA_KEY_TYPE_DERIVE);
73+
psa_set_key_algorithm(&mAttrs, PSA_ALG_HKDF(PSA_ALG_SHA_256));
74+
psa_set_key_usage_flags(&mAttrs, PSA_KEY_USAGE_DERIVE);
75+
}
76+
77+
~HkdfKeyAttributes() { psa_reset_key_attributes(&mAttrs); }
78+
79+
const psa_key_attributes_t & Get() { return mAttrs; }
80+
81+
private:
82+
psa_key_attributes_t mAttrs = PSA_KEY_ATTRIBUTES_INIT;
83+
};
84+
6985
} // namespace
7086

7187
CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Aes128KeyHandle & key)
@@ -95,11 +111,24 @@ CHIP_ERROR PSASessionKeystore::CreateKey(const Symmetric128BitsKeyByteArray & ke
95111
return CHIP_NO_ERROR;
96112
}
97113

114+
CHIP_ERROR PSASessionKeystore::CreateKey(const ByteSpan & keyMaterial, HkdfKeyHandle & key)
115+
{
116+
// Destroy the old key if already allocated
117+
psa_destroy_key(key.As<psa_key_id_t>());
118+
119+
HkdfKeyAttributes attrs;
120+
psa_status_t status = psa_import_key(&attrs.Get(), keyMaterial.data(), keyMaterial.size(), &key.AsMutable<psa_key_id_t>());
121+
122+
VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL);
123+
124+
return CHIP_NO_ERROR;
125+
}
126+
98127
CHIP_ERROR PSASessionKeystore::DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info,
99128
Aes128KeyHandle & key)
100129
{
101130
PsaKdf kdf;
102-
ReturnErrorOnFailure(kdf.Init(PSA_ALG_HKDF(PSA_ALG_SHA_256), secret.Span(), salt, info));
131+
ReturnErrorOnFailure(kdf.Init(secret.Span(), salt, info));
103132

104133
AesKeyAttributes attrs;
105134

@@ -111,8 +140,24 @@ CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(const ByteSpan & secret, const
111140
AttestationChallenge & attestationChallenge)
112141
{
113142
PsaKdf kdf;
114-
ReturnErrorOnFailure(kdf.Init(PSA_ALG_HKDF(PSA_ALG_SHA_256), secret, salt, info));
143+
ReturnErrorOnFailure(kdf.Init(secret, salt, info));
144+
145+
return DeriveSessionKeys(kdf, i2rKey, r2iKey, attestationChallenge);
146+
}
147+
148+
CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
149+
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
150+
AttestationChallenge & attestationChallenge)
151+
{
152+
PsaKdf kdf;
153+
ReturnErrorOnFailure(kdf.Init(hkdfKey, salt, info));
154+
155+
return DeriveSessionKeys(kdf, i2rKey, r2iKey, attestationChallenge);
156+
}
115157

158+
CHIP_ERROR PSASessionKeystore::DeriveSessionKeys(PsaKdf & kdf, Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
159+
AttestationChallenge & attestationChallenge)
160+
{
116161
CHIP_ERROR error;
117162
AesKeyAttributes attrs;
118163

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

186+
void PSASessionKeystore::DestroyKey(HkdfKeyHandle & key)
187+
{
188+
auto & keyId = key.AsMutable<psa_key_id_t>();
189+
190+
psa_destroy_key(keyId);
191+
keyId = 0;
192+
}
193+
141194
} // namespace Crypto
142195
} // namespace chip

src/crypto/PSASessionKeystore.h

+10
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#pragma once
1919

20+
#include <crypto/CHIPCryptoPALPSA.h>
2021
#include <crypto/SessionKeystore.h>
2122

2223
namespace chip {
@@ -27,11 +28,20 @@ class PSASessionKeystore : public SessionKeystore
2728
public:
2829
CHIP_ERROR CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Aes128KeyHandle & key) override;
2930
CHIP_ERROR CreateKey(const Symmetric128BitsKeyByteArray & keyMaterial, Hmac128KeyHandle & key) override;
31+
CHIP_ERROR CreateKey(const ByteSpan & keyMaterial, HkdfKeyHandle & key) override;
3032
CHIP_ERROR DeriveKey(const P256ECDHDerivedSecret & secret, const ByteSpan & salt, const ByteSpan & info,
3133
Aes128KeyHandle & key) override;
3234
CHIP_ERROR DeriveSessionKeys(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info, Aes128KeyHandle & i2rKey,
3335
Aes128KeyHandle & r2iKey, AttestationChallenge & attestationChallenge) override;
36+
CHIP_ERROR DeriveSessionKeys(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info,
37+
Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
38+
AttestationChallenge & attestationChallenge) override;
3439
void DestroyKey(Symmetric128BitsKeyHandle & key) override;
40+
void DestroyKey(HkdfKeyHandle & key) override;
41+
42+
private:
43+
CHIP_ERROR DeriveSessionKeys(PsaKdf & kdf, Aes128KeyHandle & i2rKey, Aes128KeyHandle & r2iKey,
44+
AttestationChallenge & attestationChallenge);
3545
};
3646

3747
} // namespace Crypto

0 commit comments

Comments
 (0)