Skip to content

Commit 157480a

Browse files
Damian-Nordickkasperczyk-no
authored andcommitted
[nrf fromtree][crypto] Add HKDF key handle and use it during PASE (#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 bdded49 commit 157480a

16 files changed

+375
-120
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
@@ -562,27 +562,24 @@ class P256Keypair : public P256KeypairBase
562562
bool mInitialized = false;
563563
};
564564

565-
using Symmetric128BitsKeyByteArray = uint8_t[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];
566-
567565
/**
568-
* @brief Platform-specific Symmetric key handle
566+
* @brief Platform-specific symmetric key handle
569567
*
570568
* The class represents a key used by the Matter stack either in the form of raw key material or key
571569
* reference, depending on the platform. To achieve that, it contains an opaque context that can be
572-
* cast to a concrete representation used by the given platform. Note that currently Matter uses
573-
* 128-bit symmetric keys only.
570+
* cast to a concrete representation used by the given platform.
574571
*
575-
* @note Symmetric128BitsKeyHandle is an abstract class to force child classes for each key handle type.
576-
* Symmetric128BitsKeyHandle class implements all the necessary components for handles.
577-
* Child classes only need to implement a constructor and delete all the copy operators.
572+
* @note SymmetricKeyHandle is an abstract class to force child classes for each key handle type.
573+
* SymmetricKeyHandle class implements all the necessary components for handles.
578574
*/
579-
class Symmetric128BitsKeyHandle
575+
template <size_t ContextSize>
576+
class SymmetricKeyHandle
580577
{
581578
public:
582-
Symmetric128BitsKeyHandle(const Symmetric128BitsKeyHandle &) = delete;
583-
Symmetric128BitsKeyHandle(Symmetric128BitsKeyHandle &&) = delete;
584-
void operator=(const Symmetric128BitsKeyHandle &) = delete;
585-
void operator=(Symmetric128BitsKeyHandle &&) = delete;
579+
SymmetricKeyHandle(const SymmetricKeyHandle &) = delete;
580+
SymmetricKeyHandle(SymmetricKeyHandle &&) = delete;
581+
void operator=(const SymmetricKeyHandle &) = delete;
582+
void operator=(SymmetricKeyHandle &&) = delete;
586583

587584
/**
588585
* @brief Get internal context cast to the desired key representation
@@ -603,44 +600,44 @@ class Symmetric128BitsKeyHandle
603600
}
604601

605602
protected:
606-
Symmetric128BitsKeyHandle() = default;
607-
~Symmetric128BitsKeyHandle() { ClearSecretData(mContext.mOpaque); }
603+
SymmetricKeyHandle() = default;
604+
~SymmetricKeyHandle() { ClearSecretData(mContext.mOpaque); }
608605

609606
private:
610-
static constexpr size_t kContextSize = CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES;
611-
612607
struct alignas(uintptr_t) OpaqueContext
613608
{
614-
uint8_t mOpaque[kContextSize] = {};
609+
uint8_t mOpaque[ContextSize] = {};
615610
} mContext;
616611
};
617612

613+
using Symmetric128BitsKeyByteArray = uint8_t[CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES];
614+
618615
/**
619-
* @brief Platform-specific AES key handle
616+
* @brief Platform-specific 128-bit symmetric key handle
620617
*/
621-
class Aes128KeyHandle final : public Symmetric128BitsKeyHandle
618+
class Symmetric128BitsKeyHandle : public SymmetricKeyHandle<CHIP_CRYPTO_SYMMETRIC_KEY_LENGTH_BYTES>
622619
{
623-
public:
624-
Aes128KeyHandle() = default;
620+
};
625621

626-
Aes128KeyHandle(const Aes128KeyHandle &) = delete;
627-
Aes128KeyHandle(Aes128KeyHandle &&) = delete;
628-
void operator=(const Aes128KeyHandle &) = delete;
629-
void operator=(Aes128KeyHandle &&) = delete;
622+
/**
623+
* @brief Platform-specific 128-bit AES key handle
624+
*/
625+
class Aes128KeyHandle final : public Symmetric128BitsKeyHandle
626+
{
630627
};
631628

632629
/**
633-
* @brief Platform-specific HMAC key handle
630+
* @brief Platform-specific 128-bit HMAC key handle
634631
*/
635632
class Hmac128KeyHandle final : public Symmetric128BitsKeyHandle
636633
{
637-
public:
638-
Hmac128KeyHandle() = default;
634+
};
639635

640-
Hmac128KeyHandle(const Hmac128KeyHandle &) = delete;
641-
Hmac128KeyHandle(Hmac128KeyHandle &&) = delete;
642-
void operator=(const Hmac128KeyHandle &) = delete;
643-
void operator=(Hmac128KeyHandle &&) = delete;
636+
/**
637+
* @brief Platform-specific HKDF key handle
638+
*/
639+
class HkdfKeyHandle final : public SymmetricKeyHandle<CHIP_CONFIG_HKDF_KEY_HANDLE_CONTEXT_SIZE>
640+
{
644641
};
645642

646643
/**
@@ -1059,6 +1056,9 @@ class PBKDF2_sha256
10591056
unsigned int iteration_count, uint32_t key_length, uint8_t * output);
10601057
};
10611058

1059+
// TODO: Extract Spake2p to a separate header and replace the forward declaration with #include SessionKeystore.h
1060+
class SessionKeystore;
1061+
10621062
/**
10631063
* The below class implements the draft 01 version of the Spake2+ protocol as
10641064
* defined in https://www.ietf.org/id/draft-bar-cfrg-spake2plus-01.html.
@@ -1174,14 +1174,17 @@ class Spake2p
11741174
virtual CHIP_ERROR KeyConfirm(const uint8_t * in, size_t in_len);
11751175

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

11861189
CHIP_ERROR InternalHash(const uint8_t * in, size_t in_len);
11871190
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
@@ -110,7 +110,12 @@ class PsaKdf
110110
/**
111111
* @brief Initializes the key derivation operation.
112112
*/
113-
CHIP_ERROR Init(psa_algorithm_t algorithm, const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info);
113+
CHIP_ERROR Init(const ByteSpan & secret, const ByteSpan & salt, const ByteSpan & info);
114+
115+
/**
116+
* @brief Initializes the key derivation operation.
117+
*/
118+
CHIP_ERROR Init(const HkdfKeyHandle & hkdfKey, const ByteSpan & salt, const ByteSpan & info);
114119

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

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

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)