From 1819fb1d55256b223092297e7f13998a54673119 Mon Sep 17 00:00:00 2001 From: Damian Krolik Date: Fri, 6 Oct 2023 12:52:36 +0200 Subject: [PATCH 1/3] [crypto] Add PSA SPAKE2+ implementation Implement Matter's SPAKE2+ protocol using PSA crypto API. Use the PSA implementation in PASE session for nRF Connect platform. Signed-off-by: Damian Krolik --- config/nrfconnect/chip-module/CMakeLists.txt | 3 +- src/crypto/BUILD.gn | 8 + src/crypto/PSASpake2p.cpp | 203 +++++++++++++++++++ src/crypto/PSASpake2p.h | 166 +++++++++++++++ src/crypto/crypto.gni | 7 + src/protocols/secure_channel/PASESession.h | 7 + 6 files changed, 393 insertions(+), 1 deletion(-) create mode 100644 src/crypto/PSASpake2p.cpp create mode 100644 src/crypto/PSASpake2p.h diff --git a/config/nrfconnect/chip-module/CMakeLists.txt b/config/nrfconnect/chip-module/CMakeLists.txt index 98295406628cc1..66f564967c9191 100644 --- a/config/nrfconnect/chip-module/CMakeLists.txt +++ b/config/nrfconnect/chip-module/CMakeLists.txt @@ -168,7 +168,8 @@ else() endif() if (CONFIG_CHIP_CRYPTO_PSA) - matter_add_gn_arg_string("chip_crypto" "psa") + matter_add_gn_arg_string("chip_crypto" "psa") + matter_add_gn_arg_bool ("chip_crypto_psa_spake2p" TRUE) endif() if (BOARD STREQUAL "native_posix") diff --git a/src/crypto/BUILD.gn b/src/crypto/BUILD.gn index bfd633947c903d..1fb8b94f39a82f 100644 --- a/src/crypto/BUILD.gn +++ b/src/crypto/BUILD.gn @@ -51,6 +51,7 @@ buildconfig_header("crypto_buildconfig") { defines = [ "CHIP_CRYPTO_MBEDTLS=${chip_crypto_mbedtls}", "CHIP_CRYPTO_PSA=${chip_crypto_psa}", + "CHIP_CRYPTO_PSA_SPAKE2P=${chip_crypto_psa_spake2p}", "CHIP_CRYPTO_OPENSSL=${chip_crypto_openssl}", "CHIP_CRYPTO_BORINGSSL=${chip_crypto_boringssl}", "CHIP_CRYPTO_PLATFORM=${chip_crypto_platform}", @@ -156,6 +157,13 @@ static_library("crypto") { ] } + if (chip_crypto_psa_spake2p) { + sources += [ + "PSASpake2p.cpp", + "PSASpake2p.h", + ] + } + public_configs = [] cflags = [ "-Wconversion" ] diff --git a/src/crypto/PSASpake2p.cpp b/src/crypto/PSASpake2p.cpp new file mode 100644 index 00000000000000..55a0b1d906afdc --- /dev/null +++ b/src/crypto/PSASpake2p.cpp @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "PSASpake2p.h" + +#include "CHIPCryptoPALPSA.h" + +#include + +#include + +namespace chip { +namespace Crypto { + +CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::Init(const uint8_t * context, size_t context_len) +{ + Clear(); + + VerifyOrReturnError(context_len <= sizeof(mContext), CHIP_ERROR_BUFFER_TOO_SMALL); + + psa_pake_cipher_suite_t cs = PSA_PAKE_CIPHER_SUITE_INIT; + psa_pake_cs_set_algorithm(&cs, PSA_ALG_SPAKE2P); + psa_pake_cs_set_primitive(&cs, PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, PSA_ECC_FAMILY_SECP_R1, 256)); + psa_pake_cs_set_hash(&cs, PSA_ALG_SHA_256); + + psa_status_t status = psa_pake_setup(&mOperation, &cs); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + memcpy(mContext, context, context_len); + mContextLen = context_len; + + return CHIP_NO_ERROR; +} + +void PSASpake2p_P256_SHA256_HKDF_HMAC::Clear() +{ + IgnoreUnusedVariable(psa_pake_abort(&mOperation)); + mOperation = psa_pake_operation_init(); + + IgnoreUnusedVariable(psa_destroy_key(mKey)); + mKey = PSA_KEY_ID_NULL; +} + +CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::BeginVerifier(const uint8_t * my_identity, size_t my_identity_len, + const uint8_t * peer_identity, size_t peer_identity_len, + const uint8_t * w0in, size_t w0in_len, const uint8_t * Lin, + size_t Lin_len) +{ + VerifyOrReturnError(w0in_len <= kSpake2p_WS_Length, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(Lin_len == kP256_Point_Length, CHIP_ERROR_INVALID_ARGUMENT); + + mRole = PSA_PAKE_ROLE_SERVER; + psa_status_t status = psa_pake_set_role(&mOperation, PSA_PAKE_ROLE_SERVER); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_set_peer(&mOperation, peer_identity, peer_identity_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_set_user(&mOperation, my_identity, my_identity_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + uint8_t password[kSpake2p_WS_Length + kP256_Point_Length]; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + + memcpy(password + 0, w0in, w0in_len); + memcpy(password + w0in_len, Lin, Lin_len); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attributes, PSA_ALG_SPAKE2P); + psa_set_key_type(&attributes, PSA_KEY_TYPE_PASSWORD); + + status = psa_import_key(&attributes, password, w0in_len + Lin_len, &mKey); + psa_reset_key_attributes(&attributes); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_set_password_key(&mOperation, mKey); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_input(&mOperation, PSA_PAKE_STEP_CONTEXT, mContext, mContextLen); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::BeginProver(const uint8_t * my_identity, size_t my_identity_len, + const uint8_t * peer_identity, size_t peer_identity_len, + const uint8_t * w0in, size_t w0in_len, const uint8_t * w1in, + size_t w1in_len) +{ + VerifyOrReturnError(w0in_len <= kSpake2p_WS_Length, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(w1in_len <= kSpake2p_WS_Length, CHIP_ERROR_INVALID_ARGUMENT); + + mRole = PSA_PAKE_ROLE_CLIENT; + psa_status_t status = psa_pake_set_role(&mOperation, PSA_PAKE_ROLE_CLIENT); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_set_user(&mOperation, my_identity, my_identity_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_set_peer(&mOperation, peer_identity, peer_identity_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + uint8_t password[kSpake2p_WS_Length * 2]; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + + memcpy(password + 0, w0in, w0in_len); + memcpy(password + w0in_len, w1in, w1in_len); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attributes, PSA_ALG_SPAKE2P); + psa_set_key_type(&attributes, PSA_KEY_TYPE_PASSWORD); + + status = psa_import_key(&attributes, password, w0in_len + w1in_len, &mKey); + psa_reset_key_attributes(&attributes); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_set_password_key(&mOperation, mKey); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_input(&mOperation, PSA_PAKE_STEP_CONTEXT, mContext, mContextLen); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::ComputeRoundOne(const uint8_t * pab, size_t pab_len, uint8_t * out, size_t * out_len) +{ + VerifyOrReturnError(out_len != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + psa_status_t status; + + if (mRole == PSA_PAKE_ROLE_SERVER) + { + status = psa_pake_input(&mOperation, PSA_PAKE_STEP_KEY_SHARE, pab, pab_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + } + + status = psa_pake_output(&mOperation, PSA_PAKE_STEP_KEY_SHARE, out, *out_len, out_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::ComputeRoundTwo(const uint8_t * in, size_t in_len, uint8_t * out, size_t * out_len) +{ + VerifyOrReturnError(out_len != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + psa_status_t status; + + if (mRole == PSA_PAKE_ROLE_CLIENT) + { + status = psa_pake_input(&mOperation, PSA_PAKE_STEP_KEY_SHARE, in, in_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + } + + status = psa_pake_output(&mOperation, PSA_PAKE_STEP_CONFIRM, out, *out_len, out_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::KeyConfirm(const uint8_t * in, size_t in_len) +{ + psa_status_t status = psa_pake_input(&mOperation, PSA_PAKE_STEP_CONFIRM, in, in_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::GetKeys(uint8_t * out, size_t * out_len) +{ + VerifyOrReturnError(out != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(out_len != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + + /* + * TODO: either: + * - use psa_pake_shared_secret() proposed in https://github.com/ARM-software/psa-api/issues/86 + * - refactor Matter's GetKeys API to take an abstract shared secret instead of raw secret bytes. + */ + oberon_spake2p_operation_t & oberonCtx = mOperation.MBEDTLS_PRIVATE(ctx).oberon_spake2p_ctx; + + VerifyOrReturnError((oberonCtx.hash_len / 2) <= *out_len, CHIP_ERROR_BUFFER_TOO_SMALL); + + memcpy(out, oberonCtx.shared, oberonCtx.hash_len / 2); + *out_len = oberonCtx.hash_len / 2; + + return CHIP_NO_ERROR; +} + +} // namespace Crypto +} // namespace chip diff --git a/src/crypto/PSASpake2p.h b/src/crypto/PSASpake2p.h new file mode 100644 index 00000000000000..b416fa002bb28c --- /dev/null +++ b/src/crypto/PSASpake2p.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2023 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "CHIPCryptoPAL.h" + +#include + +namespace chip { +namespace Crypto { + +/** + * 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. + * + * The following describes the protocol flows: + * + * Commissioner Accessory + * ------------ --------- + * + * Init + * BeginProver + * ComputeRoundOne -------------> + * Init + * BeginVerifier + * /- ComputeRoundOne + * <------------- ComputeRoundTwo + * ComputeRoundTwo -------------> + * KeyConfirm KeyConfirm + * GetKeys GetKeys + * + **/ +class PSASpake2p_P256_SHA256_HKDF_HMAC +{ +public: + /** + * @brief Initialize Spake2+ with some context specific information. + * + * @param context The context is arbitrary but should include information about the + * protocol being run, contain the transcript for negotiation, include + * the PKBDF parameters, etc. + * @param context_len The length of the context. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR Init(const uint8_t * context, size_t context_len); + + /** + * @brief Free Spake2+ underlying objects. + **/ + void Clear(); + + /** + * @brief Start the Spake2+ process as a verifier (i.e. an accessory being provisioned). + * + * @param my_identity The verifier identity. May be NULL if identities are not established. + * @param my_identity_len The verifier identity length. + * @param peer_identity The peer identity. May be NULL if identities are not established. + * @param peer_identity_len The peer identity length. + * @param w0in The input w0 (a parameter baked into the device or computed with ComputeW0). + * @param w0in_len The input w0 length. + * @param Lin The input L (a parameter baked into the device or computed with ComputeL). + * @param Lin_len The input L length. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR BeginVerifier(const uint8_t * my_identity, size_t my_identity_len, const uint8_t * peer_identity, + size_t peer_identity_len, const uint8_t * w0in, size_t w0in_len, const uint8_t * Lin, size_t Lin_len); + + /** + * @brief Start the Spake2+ process as a prover (i.e. a commissioner). + * + * @param my_identity The prover identity. May be NULL if identities are not established. + * @param my_identity_len The prover identity length. + * @param peer_identity The peer identity. May be NULL if identities are not established. + * @param peer_identity_len The peer identity length. + * @param w0in The input w0 (an output from the PBKDF). + * @param w0in_len The input w0 length. + * @param w1in The input w1 (an output from the PBKDF). + * @param w1in_len The input w1 length. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR BeginProver(const uint8_t * my_identity, size_t my_identity_len, const uint8_t * peer_identity, + size_t peer_identity_len, const uint8_t * w0in, size_t w0in_len, const uint8_t * w1in, size_t w1in_len); + + /** + * @brief Compute the first round of the protocol. + * + * @param pab X value from commissioner. + * @param pab_len X length. + * @param out The output first round Spake2+ contribution. + * @param out_len The output first round Spake2+ contribution length. + * + * The out_len parameter is expected to point to an integer that holds + * the size of the buffer to put the first round Spake2+ contribution. + * After successful execution of this method, the variable is set to the + * actual size of the generated output. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR ComputeRoundOne(const uint8_t * pab, size_t pab_len, uint8_t * out, size_t * out_len); + + /** + * @brief Compute the second round of the protocol. + * + * @param in The peer first round Spake2+ contribution. + * @param in_len The peer first round Spake2+ contribution length. + * @param out The output second round Spake2+ contribution. + * @param out_len The output second round Spake2+ contribution length. + * + * The out_len parameter is expected to point to an integer that holds + * the size of the buffer to put the second round Spake2+ contribution. + * After successful execution of this method, the variable is set to the + * actual size of the generated output. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR ComputeRoundTwo(const uint8_t * in, size_t in_len, uint8_t * out, size_t * out_len); + + /** + * @brief Confirm that each party computed the same keys. + * + * @param in The peer second round Spake2+ contribution. + * @param in_len The peer second round Spake2+ contribution length. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR KeyConfirm(const uint8_t * in, size_t in_len); + + /** + * @brief Return the shared secret. + * + * @param out The output secret. + * @param out_len The output secret length. + * + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ + CHIP_ERROR GetKeys(uint8_t * out, size_t * out_len); + +private: + psa_pake_operation_t mOperation = PSA_PAKE_OPERATION_INIT; + psa_key_id_t mKey = PSA_KEY_ID_NULL; + + psa_pake_role_t mRole; + uint8_t mContext[kSHA256_Hash_Length]; + size_t mContextLen; +}; + +} // namespace Crypto +} // namespace chip diff --git a/src/crypto/crypto.gni b/src/crypto/crypto.gni index 4fc63b800826b8..6b1c251e06733e 100644 --- a/src/crypto/crypto.gni +++ b/src/crypto/crypto.gni @@ -19,8 +19,15 @@ declare_args() { # Compile mbedtls externally. Only used if chip_crypto == "mbedtls" chip_external_mbedtls = false + + # Use PSA Spake2+ implementation. Only used if chip_crypto == "psa" + chip_crypto_psa_spake2p = false } assert( !chip_external_mbedtls || chip_crypto == "mbedtls" || chip_crypto == "psa", "Use of external mbedtls requires the mbedtls or psa crypto impl") + +assert( + !chip_crypto_psa_spake2p || chip_crypto == "psa", + "Use of psa spake2+ requires the psa crypto impl") diff --git a/src/protocols/secure_channel/PASESession.h b/src/protocols/secure_channel/PASESession.h index f90c2545b7d787..4f134a6b6144fd 100644 --- a/src/protocols/secure_channel/PASESession.h +++ b/src/protocols/secure_channel/PASESession.h @@ -27,6 +27,9 @@ #pragma once #include +#if CHIP_CRYPTO_PSA_SPAKE2P +#include +#endif #include #include #include @@ -198,7 +201,11 @@ class DLL_EXPORT PASESession : public Messaging::UnsolicitedMessageHandler, // mNextExpectedMsg is set when we are expecting a message. Optional mNextExpectedMsg; +#if CHIP_CRYPTO_PSA_SPAKE2P + Spake2p_P256_SHA256_HKDF_HMAC mSpake2p; +#else Crypto::Spake2p_P256_SHA256_HKDF_HMAC mSpake2p; +#endif Crypto::Spake2pVerifier mPASEVerifier; From 5933e57f969b5d473de620113705571539582a89 Mon Sep 17 00:00:00 2001 From: Arkadiusz Balys Date: Fri, 23 Feb 2024 16:40:10 +0100 Subject: [PATCH 2/3] [crypto] Spake2+ alignments regarding the newest PSA version Aligned Spake2+ algorithm to the newest PSA Crypto API version that allows useing the psa_pake_get_shared_key function. --- .../nrfconnect/chip-module/Kconfig.defaults | 1 + src/crypto/CHIPCryptoPALPSA.cpp | 1 + src/crypto/CHIPCryptoPALPSA.h | 2 +- src/crypto/PSASessionKeystore.cpp | 2 +- src/crypto/PSASpake2p.cpp | 96 +++++++++---------- 5 files changed, 51 insertions(+), 51 deletions(-) diff --git a/config/nrfconnect/chip-module/Kconfig.defaults b/config/nrfconnect/chip-module/Kconfig.defaults index d7bcfbd2fb0516..562755b14b9c25 100644 --- a/config/nrfconnect/chip-module/Kconfig.defaults +++ b/config/nrfconnect/chip-module/Kconfig.defaults @@ -298,6 +298,7 @@ config MBEDTLS_HEAP_SIZE config CHIP_CRYPTO_PSA default y if !CHIP_WIFI + imply PSA_WANT_ALG_SPAKE2P_MATTER if CHIP_CRYPTO_PSA diff --git a/src/crypto/CHIPCryptoPALPSA.cpp b/src/crypto/CHIPCryptoPALPSA.cpp index 554feb0d52fee4..14485ac50541a4 100644 --- a/src/crypto/CHIPCryptoPALPSA.cpp +++ b/src/crypto/CHIPCryptoPALPSA.cpp @@ -315,6 +315,7 @@ CHIP_ERROR PsaKdf::InitOperation(psa_key_id_t hkdfKey, const ByteSpan & salt, co CHIP_ERROR PsaKdf::DeriveBytes(const MutableByteSpan & output) { psa_status_t status = psa_key_derivation_output_bytes(&mOperation, output.data(), output.size()); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; diff --git a/src/crypto/CHIPCryptoPALPSA.h b/src/crypto/CHIPCryptoPALPSA.h index 101a6d0b26568b..2f91b3b4d49765 100644 --- a/src/crypto/CHIPCryptoPALPSA.h +++ b/src/crypto/CHIPCryptoPALPSA.h @@ -146,7 +146,7 @@ class PsaKdf private: CHIP_ERROR InitOperation(psa_key_id_t hkdfKey, const ByteSpan & salt, const ByteSpan & info); - psa_key_id_t mSecretKeyId = 0; + psa_key_id_t mSecretKeyId = PSA_KEY_ID_NULL; psa_key_derivation_operation_t mOperation = PSA_KEY_DERIVATION_OPERATION_INIT; }; diff --git a/src/crypto/PSASessionKeystore.cpp b/src/crypto/PSASessionKeystore.cpp index 7a0fc4af106b46..0ae3ed50755495 100644 --- a/src/crypto/PSASessionKeystore.cpp +++ b/src/crypto/PSASessionKeystore.cpp @@ -188,7 +188,7 @@ void PSASessionKeystore::DestroyKey(HkdfKeyHandle & key) auto & keyId = key.AsMutable(); psa_destroy_key(keyId); - keyId = 0; + keyId = PSA_KEY_ID_NULL; } } // namespace Crypto diff --git a/src/crypto/PSASpake2p.cpp b/src/crypto/PSASpake2p.cpp index 55a0b1d906afdc..ee47c76d88719a 100644 --- a/src/crypto/PSASpake2p.cpp +++ b/src/crypto/PSASpake2p.cpp @@ -32,14 +32,6 @@ CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::Init(const uint8_t * context, size_ VerifyOrReturnError(context_len <= sizeof(mContext), CHIP_ERROR_BUFFER_TOO_SMALL); - psa_pake_cipher_suite_t cs = PSA_PAKE_CIPHER_SUITE_INIT; - psa_pake_cs_set_algorithm(&cs, PSA_ALG_SPAKE2P); - psa_pake_cs_set_primitive(&cs, PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, PSA_ECC_FAMILY_SECP_R1, 256)); - psa_pake_cs_set_hash(&cs, PSA_ALG_SHA_256); - - psa_status_t status = psa_pake_setup(&mOperation, &cs); - VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - memcpy(mContext, context, context_len); mContextLen = context_len; @@ -63,33 +55,38 @@ CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::BeginVerifier(const uint8_t * my_id VerifyOrReturnError(w0in_len <= kSpake2p_WS_Length, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(Lin_len == kP256_Point_Length, CHIP_ERROR_INVALID_ARGUMENT); - mRole = PSA_PAKE_ROLE_SERVER; - psa_status_t status = psa_pake_set_role(&mOperation, PSA_PAKE_ROLE_SERVER); - VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - - status = psa_pake_set_peer(&mOperation, peer_identity, peer_identity_len); - VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - - status = psa_pake_set_user(&mOperation, my_identity, my_identity_len); - VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - uint8_t password[kSpake2p_WS_Length + kP256_Point_Length]; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_pake_cipher_suite_t cp = PSA_PAKE_CIPHER_SUITE_INIT; + + psa_pake_cs_set_algorithm(&cp, PSA_ALG_SPAKE2P_MATTER); + psa_pake_cs_set_primitive(&cp, PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, PSA_ECC_FAMILY_SECP_R1, 256)); memcpy(password + 0, w0in, w0in_len); memcpy(password + w0in_len, Lin, Lin_len); psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE); - psa_set_key_algorithm(&attributes, PSA_ALG_SPAKE2P); - psa_set_key_type(&attributes, PSA_KEY_TYPE_PASSWORD); + psa_set_key_algorithm(&attributes, PSA_ALG_SPAKE2P_MATTER); + psa_set_key_type(&attributes, PSA_KEY_TYPE_SPAKE2P_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + + psa_status_t status = psa_import_key(&attributes, password, w0in_len + Lin_len, &mKey); - status = psa_import_key(&attributes, password, w0in_len + Lin_len, &mKey); psa_reset_key_attributes(&attributes); VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - status = psa_pake_set_password_key(&mOperation, mKey); + status = psa_pake_setup(&mOperation, mKey, &cp); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + mRole = PSA_PAKE_ROLE_SERVER; + status = psa_pake_set_role(&mOperation, PSA_PAKE_ROLE_SERVER); VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - status = psa_pake_input(&mOperation, PSA_PAKE_STEP_CONTEXT, mContext, mContextLen); + status = psa_pake_set_peer(&mOperation, peer_identity, peer_identity_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_set_user(&mOperation, my_identity, my_identity_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_set_context(&mOperation, mContext, mContextLen); VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; @@ -103,33 +100,38 @@ CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::BeginProver(const uint8_t * my_iden VerifyOrReturnError(w0in_len <= kSpake2p_WS_Length, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(w1in_len <= kSpake2p_WS_Length, CHIP_ERROR_INVALID_ARGUMENT); - mRole = PSA_PAKE_ROLE_CLIENT; - psa_status_t status = psa_pake_set_role(&mOperation, PSA_PAKE_ROLE_CLIENT); - VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - - status = psa_pake_set_user(&mOperation, my_identity, my_identity_len); - VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - - status = psa_pake_set_peer(&mOperation, peer_identity, peer_identity_len); - VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - uint8_t password[kSpake2p_WS_Length * 2]; psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_pake_cipher_suite_t cp = PSA_PAKE_CIPHER_SUITE_INIT; + + psa_pake_cs_set_algorithm(&cp, PSA_ALG_SPAKE2P_MATTER); + psa_pake_cs_set_primitive(&cp, PSA_PAKE_PRIMITIVE(PSA_PAKE_PRIMITIVE_TYPE_ECC, PSA_ECC_FAMILY_SECP_R1, 256)); memcpy(password + 0, w0in, w0in_len); memcpy(password + w0in_len, w1in, w1in_len); psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE); - psa_set_key_algorithm(&attributes, PSA_ALG_SPAKE2P); - psa_set_key_type(&attributes, PSA_KEY_TYPE_PASSWORD); + psa_set_key_algorithm(&attributes, PSA_ALG_SPAKE2P_MATTER); + psa_set_key_type(&attributes, PSA_KEY_TYPE_SPAKE2P_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + + psa_status_t status = psa_import_key(&attributes, password, w0in_len + w1in_len, &mKey); - status = psa_import_key(&attributes, password, w0in_len + w1in_len, &mKey); psa_reset_key_attributes(&attributes); VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - status = psa_pake_set_password_key(&mOperation, mKey); + status = psa_pake_setup(&mOperation, mKey, &cp); VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); - status = psa_pake_input(&mOperation, PSA_PAKE_STEP_CONTEXT, mContext, mContextLen); + mRole = PSA_PAKE_ROLE_CLIENT; + status = psa_pake_set_role(&mOperation, PSA_PAKE_ROLE_CLIENT); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_set_user(&mOperation, my_identity, my_identity_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_set_peer(&mOperation, peer_identity, peer_identity_len); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); + + status = psa_pake_set_context(&mOperation, mContext, mContextLen); VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; @@ -181,20 +183,16 @@ CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::KeyConfirm(const uint8_t * in, size CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::GetKeys(uint8_t * out, size_t * out_len) { - VerifyOrReturnError(out != nullptr, CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrReturnError(out_len != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + auto & keyId = key.AsMutable(); - /* - * TODO: either: - * - use psa_pake_shared_secret() proposed in https://github.com/ARM-software/psa-api/issues/86 - * - refactor Matter's GetKeys API to take an abstract shared secret instead of raw secret bytes. - */ - oberon_spake2p_operation_t & oberonCtx = mOperation.MBEDTLS_PRIVATE(ctx).oberon_spake2p_ctx; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; - VerifyOrReturnError((oberonCtx.hash_len / 2) <= *out_len, CHIP_ERROR_BUFFER_TOO_SMALL); + psa_set_key_type(&attributes, PSA_KEY_TYPE_DERIVE); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attributes, PSA_ALG_HKDF(PSA_ALG_SHA_256)); - memcpy(out, oberonCtx.shared, oberonCtx.hash_len / 2); - *out_len = oberonCtx.hash_len / 2; + psa_status_t status = psa_pake_get_shared_key(&mOperation, &attributes, &keyId); + VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; } From 0140ec0db172f513148c254197a184a5a591f3bf Mon Sep 17 00:00:00 2001 From: Arkadiusz Balys Date: Wed, 10 Apr 2024 12:19:56 +0200 Subject: [PATCH 3/3] Alignments to the Matter Core. Restyled by gn --- config/nrfconnect/chip-module/CMakeLists.txt | 2 +- src/crypto/CHIPCryptoPAL.cpp | 2 +- src/crypto/CHIPCryptoPAL.h | 2 +- src/crypto/CHIPCryptoPALPSA.cpp | 1 - src/crypto/PSASpake2p.cpp | 6 +++--- src/crypto/PSASpake2p.h | 4 ++-- src/crypto/crypto.gni | 5 ++--- src/protocols/secure_channel/CASESession.cpp | 2 +- src/protocols/secure_channel/CASESession.h | 2 +- src/protocols/secure_channel/PASESession.cpp | 2 +- src/protocols/secure_channel/PASESession.h | 4 ++-- src/protocols/secure_channel/PairingSession.h | 2 +- src/protocols/secure_channel/tests/TestPairingSession.cpp | 2 +- 13 files changed, 17 insertions(+), 19 deletions(-) diff --git a/config/nrfconnect/chip-module/CMakeLists.txt b/config/nrfconnect/chip-module/CMakeLists.txt index 66f564967c9191..51ca4689de1ca1 100644 --- a/config/nrfconnect/chip-module/CMakeLists.txt +++ b/config/nrfconnect/chip-module/CMakeLists.txt @@ -169,7 +169,7 @@ endif() if (CONFIG_CHIP_CRYPTO_PSA) matter_add_gn_arg_string("chip_crypto" "psa") - matter_add_gn_arg_bool ("chip_crypto_psa_spake2p" TRUE) + matter_add_gn_arg_bool ("chip_crypto_psa_spake2p" CONFIG_PSA_WANT_ALG_SPAKE2P_MATTER) endif() if (BOARD STREQUAL "native_posix") diff --git a/src/crypto/CHIPCryptoPAL.cpp b/src/crypto/CHIPCryptoPAL.cpp index 3e58db3a480ba9..fa84d786b8eabb 100644 --- a/src/crypto/CHIPCryptoPAL.cpp +++ b/src/crypto/CHIPCryptoPAL.cpp @@ -501,7 +501,7 @@ CHIP_ERROR Spake2p::KeyConfirm(const uint8_t * in, size_t in_len) return CHIP_NO_ERROR; } -CHIP_ERROR Spake2p::GetKeys(SessionKeystore & keystore, HkdfKeyHandle & key) const +CHIP_ERROR Spake2p::GetKeys(SessionKeystore & keystore, HkdfKeyHandle & key) { VerifyOrReturnError(state == CHIP_SPAKE2P_STATE::KC, CHIP_ERROR_INTERNAL); diff --git a/src/crypto/CHIPCryptoPAL.h b/src/crypto/CHIPCryptoPAL.h index 74b7f363d4fb7e..80611e93d12d88 100644 --- a/src/crypto/CHIPCryptoPAL.h +++ b/src/crypto/CHIPCryptoPAL.h @@ -1216,7 +1216,7 @@ class Spake2p * * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise **/ - CHIP_ERROR GetKeys(SessionKeystore & keystore, HkdfKeyHandle & key) const; + CHIP_ERROR GetKeys(SessionKeystore & keystore, HkdfKeyHandle & key); CHIP_ERROR InternalHash(const uint8_t * in, size_t in_len); CHIP_ERROR WriteMN(); diff --git a/src/crypto/CHIPCryptoPALPSA.cpp b/src/crypto/CHIPCryptoPALPSA.cpp index 14485ac50541a4..554feb0d52fee4 100644 --- a/src/crypto/CHIPCryptoPALPSA.cpp +++ b/src/crypto/CHIPCryptoPALPSA.cpp @@ -315,7 +315,6 @@ CHIP_ERROR PsaKdf::InitOperation(psa_key_id_t hkdfKey, const ByteSpan & salt, co CHIP_ERROR PsaKdf::DeriveBytes(const MutableByteSpan & output) { psa_status_t status = psa_key_derivation_output_bytes(&mOperation, output.data(), output.size()); - VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; diff --git a/src/crypto/PSASpake2p.cpp b/src/crypto/PSASpake2p.cpp index ee47c76d88719a..ae98b083c8a812 100644 --- a/src/crypto/PSASpake2p.cpp +++ b/src/crypto/PSASpake2p.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -181,7 +181,7 @@ CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::KeyConfirm(const uint8_t * in, size return CHIP_NO_ERROR; } -CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::GetKeys(uint8_t * out, size_t * out_len) +CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::GetKeys(SessionKeystore & keystore, HkdfKeyHandle & key) { auto & keyId = key.AsMutable(); @@ -191,7 +191,7 @@ CHIP_ERROR PSASpake2p_P256_SHA256_HKDF_HMAC::GetKeys(uint8_t * out, size_t * out psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE); psa_set_key_algorithm(&attributes, PSA_ALG_HKDF(PSA_ALG_SHA_256)); - psa_status_t status = psa_pake_get_shared_key(&mOperation, &attributes, &keyId); + psa_status_t status = psa_pake_get_shared_key(&(mOperation), &attributes, &keyId); VerifyOrReturnError(status == PSA_SUCCESS, CHIP_ERROR_INTERNAL); return CHIP_NO_ERROR; diff --git a/src/crypto/PSASpake2p.h b/src/crypto/PSASpake2p.h index b416fa002bb28c..9907b1ec09a5bc 100644 --- a/src/crypto/PSASpake2p.h +++ b/src/crypto/PSASpake2p.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 Project CHIP Authors + * Copyright (c) 2024 Project CHIP Authors * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -151,7 +151,7 @@ class PSASpake2p_P256_SHA256_HKDF_HMAC * * @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); private: psa_pake_operation_t mOperation = PSA_PAKE_OPERATION_INIT; diff --git a/src/crypto/crypto.gni b/src/crypto/crypto.gni index 6b1c251e06733e..96f506033bb56e 100644 --- a/src/crypto/crypto.gni +++ b/src/crypto/crypto.gni @@ -28,6 +28,5 @@ assert( !chip_external_mbedtls || chip_crypto == "mbedtls" || chip_crypto == "psa", "Use of external mbedtls requires the mbedtls or psa crypto impl") -assert( - !chip_crypto_psa_spake2p || chip_crypto == "psa", - "Use of psa spake2+ requires the psa crypto impl") +assert(!chip_crypto_psa_spake2p || chip_crypto == "psa", + "Use of psa spake2+ requires the psa crypto impl") diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 4436d7fbb77881..e1bac768f81da6 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -571,7 +571,7 @@ void CASESession::AbortPendingEstablish(CHIP_ERROR err) NotifySessionEstablishmentError(err, state); } -CHIP_ERROR CASESession::DeriveSecureSession(CryptoContext & session) const +CHIP_ERROR CASESession::DeriveSecureSession(CryptoContext & session) { switch (mState) { diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index 9e41f6c69fbe84..b7c6b429b950ad 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -146,7 +146,7 @@ class DLL_EXPORT CASESession : public Messaging::UnsolicitedMessageHandler, * @param session Reference to the secure session that will be initialized once session establishment is complete * @return CHIP_ERROR The result of session derivation */ - CHIP_ERROR DeriveSecureSession(CryptoContext & session) const override; + CHIP_ERROR DeriveSecureSession(CryptoContext & session) override; //// UnsolicitedMessageHandler Implementation //// CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate) override diff --git a/src/protocols/secure_channel/PASESession.cpp b/src/protocols/secure_channel/PASESession.cpp index ca2524d5e3f302..e0f409616ab26c 100644 --- a/src/protocols/secure_channel/PASESession.cpp +++ b/src/protocols/secure_channel/PASESession.cpp @@ -258,7 +258,7 @@ void PASESession::OnResponseTimeout(ExchangeContext * ec) NotifySessionEstablishmentError(CHIP_ERROR_TIMEOUT); } -CHIP_ERROR PASESession::DeriveSecureSession(CryptoContext & session) const +CHIP_ERROR PASESession::DeriveSecureSession(CryptoContext & session) { VerifyOrReturnError(mPairingComplete, CHIP_ERROR_INCORRECT_STATE); diff --git a/src/protocols/secure_channel/PASESession.h b/src/protocols/secure_channel/PASESession.h index 4f134a6b6144fd..2e731283c88aa8 100644 --- a/src/protocols/secure_channel/PASESession.h +++ b/src/protocols/secure_channel/PASESession.h @@ -128,7 +128,7 @@ class DLL_EXPORT PASESession : public Messaging::UnsolicitedMessageHandler, * @param session Reference to the secure session that will be initialized once pairing is complete * @return CHIP_ERROR The result of session derivation */ - CHIP_ERROR DeriveSecureSession(CryptoContext & session) const override; + CHIP_ERROR DeriveSecureSession(CryptoContext & session) override; // TODO: remove Clear, we should create a new instance instead reset the old instance. /** @brief This function zeroes out and resets the memory used by the object. @@ -202,7 +202,7 @@ class DLL_EXPORT PASESession : public Messaging::UnsolicitedMessageHandler, Optional mNextExpectedMsg; #if CHIP_CRYPTO_PSA_SPAKE2P - Spake2p_P256_SHA256_HKDF_HMAC mSpake2p; + Crypto::PSASpake2p_P256_SHA256_HKDF_HMAC mSpake2p; #else Crypto::Spake2p_P256_SHA256_HKDF_HMAC mSpake2p; #endif diff --git a/src/protocols/secure_channel/PairingSession.h b/src/protocols/secure_channel/PairingSession.h index ea69f65bfaccb7..fa639a7dd9459a 100644 --- a/src/protocols/secure_channel/PairingSession.h +++ b/src/protocols/secure_channel/PairingSession.h @@ -96,7 +96,7 @@ class DLL_EXPORT PairingSession : public SessionDelegate * @param session Reference to the secure session that will be initialized once pairing is complete * @return CHIP_ERROR The result of session derivation */ - virtual CHIP_ERROR DeriveSecureSession(CryptoContext & session) const = 0; + virtual CHIP_ERROR DeriveSecureSession(CryptoContext & session) = 0; const ReliableMessageProtocolConfig & GetRemoteMRPConfig() const { return mRemoteSessionParams.GetMRPConfig(); } const SessionParameters & GetRemoteSessionParameters() const { return mRemoteSessionParams; } diff --git a/src/protocols/secure_channel/tests/TestPairingSession.cpp b/src/protocols/secure_channel/tests/TestPairingSession.cpp index e0e4a7cd9601c8..4a3fe88d55c7aa 100644 --- a/src/protocols/secure_channel/tests/TestPairingSession.cpp +++ b/src/protocols/secure_channel/tests/TestPairingSession.cpp @@ -48,7 +48,7 @@ class TestPairingSession : public PairingSession const ReliableMessageProtocolConfig & GetRemoteMRPConfig() const { return PairingSession::GetRemoteMRPConfig(); } - CHIP_ERROR DeriveSecureSession(CryptoContext & session) const override { return CHIP_NO_ERROR; } + CHIP_ERROR DeriveSecureSession(CryptoContext & session) override { return CHIP_NO_ERROR; } CHIP_ERROR DecodeMRPParametersIfPresent(TLV::Tag expectedTag, System::PacketBufferTLVReader & tlvReader) {