From 34d8115c89a7f6849f787bf0ce580731858a6e2c Mon Sep 17 00:00:00 2001 From: Pankaj Garg Date: Fri, 11 Dec 2020 20:13:24 -0800 Subject: [PATCH] Update SPAKE2p pairing code to match spec (#4166) * Update SPAKE2p pairing code to match spec - update message handshake to include PBKDF param exchange - update msg types and error codes - add error handling via error message handshake * delete commented out code * use little endian byte ordering in the messages * address review comments * fix build --- src/crypto/CHIPCryptoPAL.cpp | 20 +- src/crypto/CHIPCryptoPAL.h | 15 +- src/crypto/CHIPCryptoPALOpenSSL.cpp | 5 +- src/crypto/CHIPCryptoPALmbedTLS.cpp | 5 +- src/crypto/tests/CHIPCryptoPALTest.cpp | 26 +- src/crypto/tests/SPAKE2P_RFC_test_vectors.h | 64 +-- src/transport/RendezvousSession.cpp | 4 +- src/transport/SecurePairingSession.cpp | 459 +++++++++++++++--- src/transport/SecurePairingSession.h | 64 ++- .../tests/TestSecurePairingSession.cpp | 24 +- 10 files changed, 526 insertions(+), 160 deletions(-) diff --git a/src/crypto/CHIPCryptoPAL.cpp b/src/crypto/CHIPCryptoPAL.cpp index a6b6085a515f8d..07f6156439e3ae 100644 --- a/src/crypto/CHIPCryptoPAL.cpp +++ b/src/crypto/CHIPCryptoPAL.cpp @@ -82,12 +82,12 @@ Spake2p::Spake2p(size_t _fe_size, size_t _point_size, size_t _hash_size) tempbn = nullptr; } -CHIP_ERROR Spake2p::Init(const uint8_t * context, size_t context_len) +CHIP_ERROR Spake2p::Init(const Hash_SHA256_stream * context) { CHIP_ERROR error = CHIP_ERROR_INTERNAL; state = CHIP_SPAKE2P_STATE::PREINIT; - error = InitImpl(); + error = InitImpl(context); VerifyOrExit(error == CHIP_NO_ERROR, error = CHIP_ERROR_INTERNAL); error = PointLoad(spake2p_M_p256, sizeof(spake2p_M_p256), M); @@ -96,9 +96,6 @@ CHIP_ERROR Spake2p::Init(const uint8_t * context, size_t context_len) error = PointLoad(spake2p_N_p256, sizeof(spake2p_N_p256), N); VerifyOrExit(error == CHIP_NO_ERROR, error = CHIP_ERROR_INTERNAL); - error = InternalHash(context, context_len); - VerifyOrExit(error == CHIP_NO_ERROR, error = CHIP_ERROR_INTERNAL); - state = CHIP_SPAKE2P_STATE::INIT; error = CHIP_NO_ERROR; exit: @@ -387,12 +384,19 @@ CHIP_ERROR Spake2p::GetKeys(uint8_t * out, size_t * out_len) return error; } -CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::InitImpl() +CHIP_ERROR Spake2p_P256_SHA256_HKDF_HMAC::InitImpl(const Hash_SHA256_stream * context) { CHIP_ERROR error = CHIP_ERROR_INTERNAL; - error = sha256_hash_ctx.Begin(); - VerifyOrExit(error == CHIP_NO_ERROR, error = CHIP_ERROR_INTERNAL); + if (context != nullptr) + { + sha256_hash_ctx = (*context); + } + else + { + error = sha256_hash_ctx.Begin(); + VerifyOrExit(error == CHIP_NO_ERROR, error = CHIP_ERROR_INTERNAL); + } error = InitInternal(); VerifyOrExit(error == CHIP_NO_ERROR, error = CHIP_ERROR_INTERNAL); diff --git a/src/crypto/CHIPCryptoPAL.h b/src/crypto/CHIPCryptoPAL.h index 59a66fc639c173..502ba9b12f0f4d 100644 --- a/src/crypto/CHIPCryptoPAL.h +++ b/src/crypto/CHIPCryptoPAL.h @@ -350,6 +350,8 @@ CHIP_ERROR Hash_SHA256(const uint8_t * data, size_t data_length, uint8_t * out_b /** * @brief A class that defines stream based implementation of SHA-256 hash + * It's expected that the object of this class can be safely copied. + * All implementations must check for std::is_trivially_copyable. **/ struct HashSHA256OpaqueContext @@ -456,14 +458,13 @@ class Spake2p /** * @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. + * @param context The spake2p session will bootstrap from this hash context. + * If the provided context pointer is null, the spake2p session will bootstrap + * from a blank hash context. * * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise **/ - CHIP_ERROR Init(const uint8_t * context, size_t context_len); + CHIP_ERROR Init(const Hash_SHA256_stream * context); /** * @brief Start the Spake2+ process as a verifier (i.e. an accessory being provisioned). @@ -716,7 +717,7 @@ class Spake2p * * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise **/ - virtual CHIP_ERROR InitImpl() = 0; + virtual CHIP_ERROR InitImpl(const Hash_SHA256_stream * context) = 0; /** * @brief Hash in_len bytes of in into the internal hash context. @@ -828,7 +829,7 @@ class Spake2p_P256_SHA256_HKDF_HMAC : public Spake2p CHIP_ERROR ComputeL(uint8_t * Lout, size_t * L_len, const uint8_t * w1in, size_t w1in_len) override; protected: - CHIP_ERROR InitImpl() override; + CHIP_ERROR InitImpl(const Hash_SHA256_stream * context) override; CHIP_ERROR Hash(const uint8_t * in, size_t in_len) override; CHIP_ERROR HashFinalize(uint8_t * out) override; CHIP_ERROR KDF(const uint8_t * secret, size_t secret_length, const uint8_t * salt, size_t salt_length, const uint8_t * info, diff --git a/src/crypto/CHIPCryptoPALOpenSSL.cpp b/src/crypto/CHIPCryptoPALOpenSSL.cpp index 30ab992473b2fd..0733f9face0e92 100644 --- a/src/crypto/CHIPCryptoPALOpenSSL.cpp +++ b/src/crypto/CHIPCryptoPALOpenSSL.cpp @@ -22,6 +22,8 @@ #include "CHIPCryptoPAL.h" +#include + #include #include #include @@ -298,7 +300,8 @@ Hash_SHA256_stream::~Hash_SHA256_stream() {} static inline SHA256_CTX * to_inner_hash_sha256_context(HashSHA256OpaqueContext * context) { - nlSTATIC_ASSERT_PRINT(sizeof(HashSHA256OpaqueContext) >= sizeof(SHA256_CTX), "Need more memory for SHA256 Context"); + static_assert(sizeof(HashSHA256OpaqueContext) >= sizeof(SHA256_CTX), "Need more memory for SHA256 Context"); + static_assert(std::is_trivially_copyable(), "SHA256_CTX values must copyable"); return reinterpret_cast(context->mOpaque); } diff --git a/src/crypto/CHIPCryptoPALmbedTLS.cpp b/src/crypto/CHIPCryptoPALmbedTLS.cpp index fec42ed1ee6346..a98d7471378428 100644 --- a/src/crypto/CHIPCryptoPALmbedTLS.cpp +++ b/src/crypto/CHIPCryptoPALmbedTLS.cpp @@ -22,6 +22,8 @@ #include "CHIPCryptoPAL.h" +#include + #include #include #include @@ -192,7 +194,8 @@ Hash_SHA256_stream::~Hash_SHA256_stream(void) {} static inline mbedtls_sha256_context * to_inner_hash_sha256_context(HashSHA256OpaqueContext * context) { - nlSTATIC_ASSERT_PRINT(sizeof(context->mOpaque) >= sizeof(mbedtls_sha256_context), "Need more memory for SHA256 Context"); + static_assert(sizeof(context->mOpaque) >= sizeof(mbedtls_sha256_context), "Need more memory for SHA256 Context"); + static_assert(std::is_trivially_copyable(), "mbedtls_sha256_context values must copyable"); return reinterpret_cast(context->mOpaque); } diff --git a/src/crypto/tests/CHIPCryptoPALTest.cpp b/src/crypto/tests/CHIPCryptoPALTest.cpp index 0c27c6531a5a3b..576cfb033d5aef 100644 --- a/src/crypto/tests/CHIPCryptoPALTest.cpp +++ b/src/crypto/tests/CHIPCryptoPALTest.cpp @@ -989,7 +989,7 @@ static void TestSPAKE2P_spake2p_FEMul(nlTestSuite * inSuite, void * inContext) const struct spake2p_fe_mul_tv * vector = fe_mul_tvs[vectorIndex]; Spake2p_P256_SHA256_HKDF_HMAC spake2p; - CHIP_ERROR err = spake2p.Init(nullptr, 0); + CHIP_ERROR err = spake2p.Init(nullptr); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); err = spake2p.FELoad(vector->fe1, vector->fe1_len, spake2p.w0); @@ -1022,7 +1022,7 @@ static void TestSPAKE2P_spake2p_FELoadWrite(nlTestSuite * inSuite, void * inCont const struct spake2p_fe_rw_tv * vector = fe_rw_tvs[vectorIndex]; Spake2p_P256_SHA256_HKDF_HMAC spake2p; - CHIP_ERROR err = spake2p.Init(nullptr, 0); + CHIP_ERROR err = spake2p.Init(nullptr); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); err = spake2p.FELoad(vector->fe_in, vector->fe_in_len, spake2p.w0); @@ -1049,7 +1049,7 @@ static void TestSPAKE2P_spake2p_Mac(nlTestSuite * inSuite, void * inContext) const struct spake2p_hmac_tv * vector = hmac_tvs[vectorIndex]; Spake2p_P256_SHA256_HKDF_HMAC spake2p; - CHIP_ERROR err = spake2p.Init(nullptr, 0); + CHIP_ERROR err = spake2p.Init(nullptr); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); err = spake2p.Mac(vector->key, vector->key_len, vector->input, vector->input_len, mac); @@ -1079,7 +1079,7 @@ static void TestSPAKE2P_spake2p_PointMul(nlTestSuite * inSuite, void * inContext const struct spake2p_point_mul_tv * vector = point_mul_tvs[vectorIndex]; Spake2p_P256_SHA256_HKDF_HMAC spake2p; - CHIP_ERROR err = spake2p.Init(nullptr, 0); + CHIP_ERROR err = spake2p.Init(nullptr); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); err = spake2p.PointLoad(vector->point, vector->point_len, spake2p.L); @@ -1115,7 +1115,7 @@ static void TestSPAKE2P_spake2p_PointMulAdd(nlTestSuite * inSuite, void * inCont const struct spake2p_point_muladd_tv * vector = point_muladd_tvs[vectorIndex]; Spake2p_P256_SHA256_HKDF_HMAC spake2p; - CHIP_ERROR err = spake2p.Init(nullptr, 0); + CHIP_ERROR err = spake2p.Init(nullptr); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); err = spake2p.PointLoad(vector->point1, vector->point1_len, spake2p.X); @@ -1157,7 +1157,7 @@ static void TestSPAKE2P_spake2p_PointLoadWrite(nlTestSuite * inSuite, void * inC const struct spake2p_point_rw_tv * vector = point_rw_tvs[vectorIndex]; Spake2p_P256_SHA256_HKDF_HMAC spake2p; - CHIP_ERROR err = spake2p.Init(nullptr, 0); + CHIP_ERROR err = spake2p.Init(nullptr); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); err = spake2p.PointLoad(vector->point, vector->point_len, spake2p.L); @@ -1183,7 +1183,7 @@ static void TestSPAKE2P_spake2p_PointIsValid(nlTestSuite * inSuite, void * inCon const struct spake2p_point_valid_tv * vector = point_valid_tvs[vectorIndex]; Spake2p_P256_SHA256_HKDF_HMAC spake2p; - CHIP_ERROR err = spake2p.Init(nullptr, 0); + CHIP_ERROR err = spake2p.Init(nullptr); NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); err = spake2p.PointLoad(vector->point, vector->point_len, spake2p.L); @@ -1255,8 +1255,16 @@ static void TestSPAKE2P_RFC(nlTestSuite * inSuite, void * inContext) Test_Spake2p_P256_SHA256_HKDF_HMAC Verifier; Test_Spake2p_P256_SHA256_HKDF_HMAC Prover; + Hash_SHA256_stream hashContext; + + error = hashContext.Begin(); + NL_TEST_ASSERT(inSuite, error == CHIP_NO_ERROR); + + error = hashContext.AddData(vector->context, vector->context_len); + NL_TEST_ASSERT(inSuite, error == CHIP_NO_ERROR); + // First start the prover - error = Prover.Init(vector->context, vector->context_len); + error = Prover.Init(&hashContext); NL_TEST_ASSERT(inSuite, error == CHIP_NO_ERROR); error = Prover.BeginProver(vector->prover_identity, vector->prover_identity_len, vector->verifier_identity, @@ -1275,7 +1283,7 @@ static void TestSPAKE2P_RFC(nlTestSuite * inSuite, void * inContext) NL_TEST_ASSERT(inSuite, memcmp(X, vector->X, vector->X_len) == 0); // Start up the verifier - error = Verifier.Init(vector->context, vector->context_len); + error = Verifier.Init(&hashContext); NL_TEST_ASSERT(inSuite, error == CHIP_NO_ERROR); // First pre-compute L (accessories with dynamic setup codes will do this) diff --git a/src/crypto/tests/SPAKE2P_RFC_test_vectors.h b/src/crypto/tests/SPAKE2P_RFC_test_vectors.h index 20a77596a7a874..40d31a43da0c45 100644 --- a/src/crypto/tests/SPAKE2P_RFC_test_vectors.h +++ b/src/crypto/tests/SPAKE2P_RFC_test_vectors.h @@ -104,18 +104,18 @@ static const uint8_t chiptest_e4836c3b50dd_V_9[] = { 0x04, 0x67, 0x18, 0x98, 0 0x28, 0x53, 0xa4, 0x07, 0x12, 0x51, 0xa3, 0x9f, 0xbe, 0x8c, 0xfc, 0x39, 0xbc }; static const uint8_t chiptest_e4836c3b50dd_Ka_10[] = { 0xf9, 0xca, 0xb9, 0xad, 0xcc, 0x0e, 0xd8, 0xe5, 0xa4, 0xdb, 0x11, 0xa8, 0x50, 0x59, 0x14, 0xb2 }; -static const uint8_t chiptest_e4836c3b50dd_Ke_11[] = { 0x80, 0x1d, 0xb2, 0x97, 0x65, 0x48, 0x16, 0xeb, - 0x4f, 0x02, 0x86, 0x81, 0x29, 0xb9, 0xdc, 0x89 }; +static const uint8_t chiptest_e4836c3b50dd_Ke_11[] = { 0x19, 0x29, 0x9c, 0x25, 0x57, 0x7d, 0xef, 0x6a, + 0xca, 0x4f, 0x21, 0x98, 0xd8, 0xb9, 0x18, 0x5c }; static const uint8_t chiptest_e4836c3b50dd_KcA_12[] = { 0x0d, 0x24, 0x8d, 0x7d, 0x19, 0x23, 0x4f, 0x14, 0x86, 0xb2, 0xef, 0xba, 0x51, 0x79, 0xc5, 0x2d }; static const uint8_t chiptest_e4836c3b50dd_KcB_13[] = { 0x55, 0x62, 0x91, 0xdf, 0x26, 0xd7, 0x05, 0xa2, 0xca, 0xed, 0xd6, 0x47, 0x4d, 0xd0, 0x07, 0x9b }; -static const uint8_t chiptest_e4836c3b50dd_MAC_KcA_14[] = { 0xd4, 0x37, 0x6f, 0x2d, 0xa9, 0xc7, 0x22, 0x26, 0xdd, 0x15, 0x1b, - 0x77, 0xc2, 0x91, 0x90, 0x71, 0x15, 0x5f, 0xc2, 0x2a, 0x20, 0x68, - 0xd9, 0x0b, 0x5f, 0xaa, 0x6c, 0x78, 0xc1, 0x1e, 0x77, 0xdd }; -static const uint8_t chiptest_e4836c3b50dd_MAC_KcB_15[] = { 0x06, 0x60, 0xa6, 0x80, 0x66, 0x3e, 0x8c, 0x56, 0x95, 0x95, 0x6f, - 0xb2, 0x2d, 0xff, 0x29, 0x8b, 0x1d, 0x07, 0xa5, 0x26, 0xcf, 0x3c, - 0xc5, 0x91, 0xad, 0xfe, 0xcd, 0x1f, 0x6e, 0xf6, 0xe0, 0x2e }; +static const uint8_t chiptest_e4836c3b50dd_MAC_KcA_14[] = { 0xe0, 0x2d, 0x9e, 0x25, 0x6b, 0xe6, 0xc4, 0xa9, 0x34, 0x6f, 0x0d, + 0x76, 0x67, 0xae, 0xcd, 0x61, 0xaf, 0x7c, 0x63, 0xfe, 0xfb, 0xb4, + 0xf7, 0x1b, 0x84, 0xd4, 0x01, 0x15, 0x9f, 0xaa, 0xbe, 0xce }; +static const uint8_t chiptest_e4836c3b50dd_MAC_KcB_15[] = { 0xf7, 0xae, 0x1b, 0xeb, 0x80, 0xd5, 0x74, 0x33, 0x12, 0x88, 0x6b, + 0x0d, 0xcb, 0xce, 0x62, 0x02, 0x23, 0x4a, 0x20, 0xa6, 0x11, 0x9e, + 0x95, 0x31, 0x0a, 0xad, 0xaf, 0x32, 0x3a, 0x2a, 0xcd, 0x39 }; static const struct spake2p_rfc_tv chiptest_e4836c3b50dd_test_vector_16 = { .context = reinterpret_cast("SPAKE2+-P256-SHA256-HKDF draft-01"), .context_len = 33, @@ -198,18 +198,18 @@ static const uint8_t chiptest_e4836c3b50dd_V_25[] = { }; static const uint8_t chiptest_e4836c3b50dd_Ka_26[] = { 0xe2, 0xcb, 0xee, 0x3a, 0xe1, 0x9a, 0x4d, 0xbe, 0x9f, 0x14, 0x6b, 0xe6, 0xbe, 0xe9, 0xbf, 0xa1 }; -static const uint8_t chiptest_e4836c3b50dd_Ke_27[] = { 0x69, 0x89, 0xd8, 0xf9, 0x17, 0x7e, 0xf7, 0xdf, - 0x67, 0xda, 0x43, 0x79, 0x87, 0xf0, 0x72, 0x55 }; +static const uint8_t chiptest_e4836c3b50dd_Ke_27[] = { 0x65, 0x2a, 0x19, 0x95, 0x9d, 0x48, 0xc9, 0xcc, + 0x1b, 0x87, 0x6e, 0x2f, 0x2a, 0x3c, 0xe4, 0x38 }; static const uint8_t chiptest_e4836c3b50dd_KcA_28[] = { 0x2f, 0x9e, 0x0b, 0xb6, 0x69, 0xd2, 0xc2, 0x26, 0x45, 0xbc, 0xe3, 0x4d, 0xa0, 0x4a, 0xc1, 0x6a }; static const uint8_t chiptest_e4836c3b50dd_KcB_29[] = { 0xeb, 0x7a, 0x35, 0x16, 0x87, 0x59, 0xdd, 0x8e, 0x9c, 0xe4, 0x4e, 0x4d, 0xc5, 0x12, 0x77, 0xce }; -static const uint8_t chiptest_e4836c3b50dd_MAC_KcA_30[] = { 0xe1, 0xb9, 0x25, 0x88, 0x07, 0xba, 0x47, 0x50, 0xda, 0xe1, 0xd7, - 0xf3, 0xc3, 0xc2, 0x94, 0xf1, 0x3d, 0xc4, 0xfa, 0x60, 0xcd, 0xe3, - 0x46, 0xd5, 0xde, 0x7d, 0x20, 0x0e, 0x2f, 0x8f, 0xd3, 0xfc }; -static const uint8_t chiptest_e4836c3b50dd_MAC_KcB_31[] = { 0xb9, 0xc3, 0x9d, 0xfa, 0x49, 0xc4, 0x77, 0x57, 0xde, 0x77, 0x8d, - 0x9b, 0xed, 0xea, 0xca, 0x24, 0x48, 0xb9, 0x05, 0xbe, 0x19, 0xa4, - 0x3b, 0x94, 0xee, 0x24, 0xb7, 0x70, 0x20, 0x81, 0x35, 0xe3 }; +static const uint8_t chiptest_e4836c3b50dd_MAC_KcA_30[] = { 0xfa, 0xda, 0x17, 0xfa, 0xeb, 0x1a, 0xb8, 0x7d, 0x1d, 0x56, 0xef, + 0xea, 0x91, 0xac, 0x5b, 0x04, 0xb6, 0x92, 0x6c, 0xe7, 0x8d, 0x1d, + 0x2f, 0x6d, 0x2e, 0xd9, 0xc2, 0x3f, 0x4c, 0xc8, 0xcc, 0x3b }; +static const uint8_t chiptest_e4836c3b50dd_MAC_KcB_31[] = { 0xe4, 0x26, 0x7d, 0x40, 0x78, 0xa4, 0xef, 0x9c, 0xd2, 0xf6, 0xef, + 0xc7, 0x6d, 0x65, 0xb4, 0x9f, 0xb8, 0xd7, 0x80, 0xb0, 0x0a, 0x45, + 0xb8, 0x92, 0x0b, 0xc5, 0xe1, 0xe8, 0x0e, 0x97, 0x02, 0xe4 }; static const struct spake2p_rfc_tv chiptest_e4836c3b50dd_test_vector_32 = { .context = reinterpret_cast("SPAKE2+-P256-SHA256-HKDF draft-01"), .context_len = 33, @@ -292,18 +292,18 @@ static const uint8_t chiptest_e4836c3b50dd_V_41[] = { }; static const uint8_t chiptest_e4836c3b50dd_Ka_42[] = { 0xec, 0x8d, 0x19, 0xb8, 0x07, 0xff, 0xb1, 0xd1, 0xee, 0xa8, 0x1a, 0x93, 0xba, 0x35, 0xcd, 0xfe }; -static const uint8_t chiptest_e4836c3b50dd_Ke_43[] = { 0x2e, 0xa4, 0x0e, 0x4b, 0xad, 0xfa, 0x54, 0x52, - 0xb5, 0x74, 0x4d, 0xc5, 0x98, 0x3e, 0x99, 0xba }; +static const uint8_t chiptest_e4836c3b50dd_Ke_43[] = { 0x26, 0xed, 0x24, 0xeb, 0x31, 0xb0, 0x7b, 0x28, + 0x6a, 0x3d, 0x5f, 0xe6, 0x26, 0x37, 0xf1, 0xa7 }; static const uint8_t chiptest_e4836c3b50dd_KcA_44[] = { 0x66, 0xde, 0x53, 0x4d, 0x9b, 0xf1, 0xe4, 0x4e, 0x96, 0xa5, 0x3a, 0x4b, 0x48, 0xd6, 0xb3, 0x53 }; static const uint8_t chiptest_e4836c3b50dd_KcB_45[] = { 0x49, 0x45, 0xc3, 0x8b, 0xb4, 0x76, 0xcb, 0x0f, 0x34, 0x7f, 0x32, 0x22, 0xbe, 0x9b, 0x64, 0xa2 }; -static const uint8_t chiptest_e4836c3b50dd_MAC_KcA_46[] = { 0xe5, 0x64, 0xc9, 0x3b, 0x30, 0x15, 0xef, 0xb9, 0x46, 0xdc, 0x16, - 0xd6, 0x42, 0xbb, 0xe7, 0xd1, 0xc8, 0xda, 0x5b, 0xe1, 0x64, 0xed, - 0x9f, 0xc3, 0xba, 0xe4, 0xe0, 0xff, 0x86, 0xe1, 0xbd, 0x3c }; -static const uint8_t chiptest_e4836c3b50dd_MAC_KcB_47[] = { 0x07, 0x2a, 0x94, 0xd9, 0xa5, 0x4e, 0xdc, 0x20, 0x1d, 0x88, 0x91, - 0x53, 0x4c, 0x23, 0x17, 0xca, 0xdf, 0x3e, 0xa3, 0x79, 0x28, 0x27, - 0xf4, 0x79, 0xe8, 0x73, 0xf9, 0x3e, 0x90, 0xf2, 0x15, 0x52 }; +static const uint8_t chiptest_e4836c3b50dd_MAC_KcA_46[] = { 0x3e, 0x65, 0xa5, 0x4e, 0x88, 0x94, 0xe6, 0x8d, 0x10, 0x0e, 0x91, + 0xf5, 0xe8, 0x55, 0xd0, 0x1b, 0x9c, 0x6c, 0x33, 0xb4, 0x27, 0x2f, + 0x5b, 0xa2, 0x2a, 0x1d, 0x09, 0x85, 0x76, 0x6e, 0x3d, 0x2c }; +static const uint8_t chiptest_e4836c3b50dd_MAC_KcB_47[] = { 0xef, 0x57, 0xce, 0x5a, 0x96, 0x62, 0x06, 0x59, 0x10, 0x49, 0xfd, + 0xde, 0x1c, 0x35, 0x09, 0xa5, 0xcb, 0x6c, 0x4d, 0xa8, 0x00, 0x01, + 0x55, 0x5c, 0xcb, 0x13, 0xb5, 0x88, 0x08, 0x41, 0xd7, 0xb0 }; static const struct spake2p_rfc_tv chiptest_e4836c3b50dd_test_vector_48 = { .context = reinterpret_cast("SPAKE2+-P256-SHA256-HKDF draft-01"), .context_len = 33, @@ -386,18 +386,18 @@ static const uint8_t chiptest_e4836c3b50dd_V_57[] = { }; static const uint8_t chiptest_e4836c3b50dd_Ka_58[] = { 0x59, 0x29, 0xa3, 0xce, 0x98, 0x22, 0xc8, 0x14, 0x01, 0xbf, 0x0f, 0x76, 0x4f, 0x69, 0xaf, 0x08 }; -static const uint8_t chiptest_e4836c3b50dd_Ke_59[] = { 0xea, 0x32, 0x76, 0xd6, 0x83, 0x34, 0x57, 0x60, - 0x97, 0xe0, 0x4b, 0x19, 0xee, 0x5a, 0x3a, 0x8b }; +static const uint8_t chiptest_e4836c3b50dd_Ke_59[] = { 0x02, 0xe6, 0x6a, 0xfc, 0x73, 0x1e, 0x20, 0x74, + 0x46, 0xcf, 0x30, 0x31, 0xb2, 0x22, 0xd9, 0x79 }; static const uint8_t chiptest_e4836c3b50dd_KcA_60[] = { 0x7f, 0x84, 0xb9, 0x39, 0xd6, 0x00, 0x11, 0x72, 0x56, 0xb0, 0xc8, 0xa6, 0xd4, 0x0c, 0xf1, 0x81 }; static const uint8_t chiptest_e4836c3b50dd_KcB_61[] = { 0xf7, 0xd7, 0x54, 0x7c, 0xed, 0x93, 0xf6, 0x81, 0xe8, 0xdf, 0x4c, 0x25, 0x8c, 0x45, 0x16, 0xfd }; -static const uint8_t chiptest_e4836c3b50dd_MAC_KcA_62[] = { 0x71, 0xd9, 0x41, 0x27, 0x79, 0xb6, 0xc4, 0x5a, 0x2c, 0x61, 0x5c, - 0x9d, 0xf3, 0xf1, 0xfd, 0x93, 0xdc, 0x0a, 0xaf, 0x63, 0x10, 0x4d, - 0xa8, 0xec, 0xe4, 0xaa, 0x1b, 0x5a, 0x3a, 0x41, 0x5f, 0xea }; -static const uint8_t chiptest_e4836c3b50dd_MAC_KcB_63[] = { 0x09, 0x5d, 0xc0, 0x40, 0x03, 0x55, 0xcc, 0x23, 0x3f, 0xde, 0x74, - 0x37, 0x81, 0x18, 0x15, 0xb3, 0xc1, 0x52, 0x4a, 0xae, 0x80, 0xfd, - 0x4e, 0x68, 0x10, 0xcf, 0x53, 0x1c, 0xf1, 0x1d, 0x20, 0xe3 }; +static const uint8_t chiptest_e4836c3b50dd_MAC_KcA_62[] = { 0x3e, 0x1b, 0xab, 0x1b, 0x88, 0xa9, 0x55, 0x87, 0x91, 0x2c, 0x2d, + 0x4a, 0xcb, 0xde, 0xaa, 0x5f, 0xcf, 0xe9, 0xda, 0xf1, 0x89, 0x3e, + 0xbe, 0xb5, 0x2d, 0x07, 0xe9, 0xf8, 0xe5, 0x4b, 0x85, 0x3d }; +static const uint8_t chiptest_e4836c3b50dd_MAC_KcB_63[] = { 0xaf, 0x83, 0xd2, 0xd0, 0x03, 0xf9, 0x42, 0x5c, 0x6a, 0x5c, 0xe1, + 0x52, 0x46, 0x10, 0x74, 0x5e, 0xb6, 0xb9, 0x4f, 0x0e, 0x62, 0xc4, + 0xf4, 0x2a, 0x4b, 0x16, 0x39, 0xd8, 0x34, 0x74, 0x0b, 0x67 }; static const struct spake2p_rfc_tv chiptest_e4836c3b50dd_test_vector_64 = { .context = reinterpret_cast("SPAKE2+-P256-SHA256-HKDF draft-01"), .context_len = 33, diff --git a/src/transport/RendezvousSession.cpp b/src/transport/RendezvousSession.cpp index e0d23b18e7cb38..6b497bf9bfca3c 100644 --- a/src/transport/RendezvousSession.cpp +++ b/src/transport/RendezvousSession.cpp @@ -444,9 +444,7 @@ CHIP_ERROR RendezvousSession::WaitForPairing(Optional nodeId, uint32_t s CHIP_ERROR RendezvousSession::Pair(Optional nodeId, uint32_t setupPINCode) { UpdateState(State::kSecurePairing); - return mPairingSession.Pair(mParams.GetPeerAddress(), setupPINCode, kSpake2p_Iteration_Count, - reinterpret_cast(kSpake2pKeyExchangeSalt), strlen(kSpake2pKeyExchangeSalt), - nodeId, mNextKeyId++, this); + return mPairingSession.Pair(mParams.GetPeerAddress(), setupPINCode, nodeId, mNextKeyId++, this); } void RendezvousSession::SendNetworkCredentials(const char * ssid, const char * passwd) diff --git a/src/transport/SecurePairingSession.cpp b/src/transport/SecurePairingSession.cpp index dc370dd73467e5..ebcf7d44d9dc07 100644 --- a/src/transport/SecurePairingSession.cpp +++ b/src/transport/SecurePairingSession.cpp @@ -32,9 +32,11 @@ #include #include +#include #include #include #include +#include #include #include @@ -42,7 +44,7 @@ namespace chip { using namespace Crypto; -const char * kSpake2pContext = "CHIP 1.0 Provisioning"; +const char * kSpake2pContext = "SPAKE2+ Commissioning"; const char * kSpake2pI2RSessionInfo = "Commissioning I2R Key"; const char * kSpake2pR2ISessionInfo = "Commissioning R2I Key"; @@ -50,9 +52,34 @@ SecurePairingSession::SecurePairingSession() {} SecurePairingSession::~SecurePairingSession() { + // Let's clear out any security state stored in the object, before destroying it. + Clear(); +} + +void SecurePairingSession::Clear() +{ + // This function zeroes out and resets the memory used by the object. + // It's done so that no security related information will be leaked. memset(&mPoint[0], 0, sizeof(mPoint)); memset(&mWS[0][0], 0, sizeof(mWS)); memset(&mKe[0], 0, sizeof(mKe)); + mNextExpectedMsg = Spake2pMsgType::kSpake2pMsgError; + mSpake2p.Init(nullptr); + mCommissioningHash.Clear(); + mIterationCount = 0; + mSaltLength = 0; + if (mSalt != nullptr) + { + chip::Platform::MemoryFree(mSalt); + mSalt = nullptr; + } + mLocalNodeId = Optional::Value(kUndefinedNodeId); + mPeerNodeId = Optional::Value(kUndefinedNodeId); + mLocalKeyId = 0; + mPeerKeyId = 0; + mPeerAddress = Transport::PeerAddress::Uninitialized(); + mKeLen = sizeof(mKe); + mPairingComplete = false; } CHIP_ERROR SecurePairingSession::Serialize(SecurePairingSessionSerialized & output) @@ -145,25 +172,41 @@ CHIP_ERROR SecurePairingSession::FromSerializable(const SecurePairingSessionSeri return error; } -CHIP_ERROR SecurePairingSession::Init(uint32_t setupCode, uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen, - Optional myNodeId, uint16_t myKeyId, SecurePairingSessionDelegate * delegate) +CHIP_ERROR SecurePairingSession::Init(Optional myNodeId, uint16_t myKeyId, uint32_t setupCode, + SecurePairingSessionDelegate * delegate) { CHIP_ERROR err = CHIP_NO_ERROR; - VerifyOrExit(salt != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(saltLen > 0, err = CHIP_ERROR_INVALID_ARGUMENT); VerifyOrExit(delegate != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); - err = mSpake2p.Init(Uint8::from_const_char(kSpake2pContext), strlen(kSpake2pContext)); + err = mCommissioningHash.Begin(); SuccessOrExit(err); - err = pbkdf2_sha256(reinterpret_cast(&setupCode), sizeof(setupCode), salt, saltLen, pbkdf2IterCount, + err = mCommissioningHash.AddData(Uint8::from_const_char(kSpake2pContext), strlen(kSpake2pContext)); + SuccessOrExit(err); + + mDelegate = delegate; + mLocalNodeId = myNodeId; + mLocalKeyId = myKeyId; + mSetupPINCode = setupCode; + +exit: + return err; +} + +CHIP_ERROR SecurePairingSession::SetupSpake2p(uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(salt != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(saltLen > 0, err = CHIP_ERROR_INVALID_ARGUMENT); + + err = pbkdf2_sha256(reinterpret_cast(&mSetupPINCode), sizeof(mSetupPINCode), salt, saltLen, pbkdf2IterCount, sizeof(mWS), &mWS[0][0]); SuccessOrExit(err); - mDelegate = delegate; - mLocalNodeId = myNodeId; - mLocalKeyId = myKeyId; + err = mSpake2p.Init(&mCommissioningHash); + SuccessOrExit(err); exit: return err; @@ -173,18 +216,40 @@ CHIP_ERROR SecurePairingSession::WaitForPairing(uint32_t mySetUpPINCode, uint32_ size_t saltLen, Optional myNodeId, uint16_t myKeyId, SecurePairingSessionDelegate * delegate) { - size_t sizeof_point = sizeof(mPoint); + CHIP_ERROR err = CHIP_NO_ERROR; - CHIP_ERROR err = Init(mySetUpPINCode, pbkdf2IterCount, salt, saltLen, myNodeId, myKeyId, delegate); - SuccessOrExit(err); + VerifyOrExit(salt != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(saltLen > 0, err = CHIP_ERROR_INVALID_ARGUMENT); - err = mSpake2p.ComputeL(mPoint, &sizeof_point, &mWS[1][0], kSpake2p_WS_Length); + err = Init(myNodeId, myKeyId, mySetUpPINCode, delegate); SuccessOrExit(err); - mNextExpectedMsg = Spake2pMsgType::kSpake2pCompute_pA; + VerifyOrExit(CanCastTo(saltLen), err = CHIP_ERROR_INVALID_ARGUMENT); + mSaltLength = static_cast(saltLen); + + if (mSalt != nullptr) + { + chip::Platform::MemoryFree(mSalt); + mSalt = nullptr; + } + + mSalt = static_cast(chip::Platform::MemoryAlloc(mSaltLength)); + VerifyOrExit(mSalt != nullptr, err = CHIP_ERROR_NO_MEMORY); + + memmove(mSalt, salt, mSaltLength); + + mIterationCount = pbkdf2IterCount; + + mNextExpectedMsg = Spake2pMsgType::kPBKDFParamRequest; mPairingComplete = false; + ChipLogDetail(Ble, "Waiting for PBKDF param request"); + exit: + if (err != CHIP_NO_ERROR) + { + Clear(); + } return err; } @@ -218,68 +283,243 @@ CHIP_ERROR SecurePairingSession::AttachHeaderAndSend(uint8_t msgType, System::Pa return err; } -CHIP_ERROR SecurePairingSession::Pair(const Transport::PeerAddress peerAddress, uint32_t peerSetUpPINCode, uint32_t pbkdf2IterCount, - const uint8_t * salt, size_t saltLen, Optional myNodeId, uint16_t myKeyId, - SecurePairingSessionDelegate * delegate) +CHIP_ERROR SecurePairingSession::Pair(const Transport::PeerAddress peerAddress, uint32_t peerSetUpPINCode, + Optional myNodeId, uint16_t myKeyId, SecurePairingSessionDelegate * delegate) { - uint8_t X[kMAX_Point_Length]; - size_t X_len = sizeof(X); - uint16_t data_len; // Will be the same as X_len in practice. + CHIP_ERROR err = Init(myNodeId, myKeyId, peerSetUpPINCode, delegate); + SuccessOrExit(err); - System::PacketBufferHandle resp; + mPeerAddress = peerAddress; - CHIP_ERROR err = Init(peerSetUpPINCode, pbkdf2IterCount, salt, saltLen, myNodeId, myKeyId, delegate); + err = SendPBKDFParamRequest(); SuccessOrExit(err); - mPeerAddress = peerAddress; +exit: + if (err != CHIP_NO_ERROR) + { + Clear(); + } + return err; +} - err = mSpake2p.BeginProver(reinterpret_cast(""), 0, reinterpret_cast(""), 0, &mWS[0][0], - kSpake2p_WS_Length, &mWS[1][0], kSpake2p_WS_Length); +CHIP_ERROR SecurePairingSession::DeriveSecureSession(const uint8_t * info, size_t info_len, SecureSession & session) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + VerifyOrExit(info != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(info_len > 0, err = CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrExit(mPairingComplete, err = CHIP_ERROR_INCORRECT_STATE); + + err = session.InitFromSecret(mKe, mKeLen, nullptr, 0, info, info_len); SuccessOrExit(err); - err = mSpake2p.ComputeRoundOne(X, &X_len); +exit: + return err; +} + +CHIP_ERROR SecurePairingSession::SendPBKDFParamRequest() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + System::PacketBufferHandle req = System::PacketBuffer::NewWithAvailableSize(kPBKDFParamRandomNumberSize); + VerifyOrExit(!req.IsNull(), err = CHIP_SYSTEM_ERROR_NO_MEMORY); + + err = DRBG_get_bytes(req->Start(), kPBKDFParamRandomNumberSize); SuccessOrExit(err); - VerifyOrExit(CanCastTo(X_len), err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); - data_len = static_cast(X_len); - resp = System::PacketBuffer::NewWithAvailableSize(data_len); + req->SetDataLength(kPBKDFParamRandomNumberSize); + + // Update commissioning hash with the pbkdf2 param request that's being sent. + err = mCommissioningHash.AddData(req->Start(), req->DataLength()); + SuccessOrExit(err); + + mNextExpectedMsg = Spake2pMsgType::kPBKDFParamResponse; + + err = AttachHeaderAndSend(Spake2pMsgType::kPBKDFParamRequest, req.Release_ForNow()); + SuccessOrExit(err); + + ChipLogDetail(Ble, "Sent PBKDF param request"); + +exit: + + if (err != CHIP_NO_ERROR) + { + Clear(); + } + return err; +} + +CHIP_ERROR SecurePairingSession::HandlePBKDFParamRequest(const PacketHeader & header, const System::PacketBufferHandle & msg) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + // Request message processing + const uint8_t * req = msg->Start(); + size_t reqlen = msg->DataLength(); + + VerifyOrExit(req != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); + VerifyOrExit(reqlen == kPBKDFParamRandomNumberSize, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + + ChipLogDetail(Ble, "Received PBKDF param request"); + + // Update commissioning hash with the received pbkdf2 param request + err = mCommissioningHash.AddData(req, reqlen); + SuccessOrExit(err); + + err = SendPBKDFParamResponse(); + SuccessOrExit(err); + +exit: + + if (err != CHIP_NO_ERROR) + { + SendErrorMsg(Spake2pErrorType::kUnexpected); + } + return err; +} + +CHIP_ERROR SecurePairingSession::SendPBKDFParamResponse() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + System::PacketBufferHandle resp; + static_assert(CHAR_BIT == 8, "Assuming sizeof() returns octets here and for sizeof(mPoint)"); + size_t resplen = kPBKDFParamRandomNumberSize + sizeof(uint64_t) + sizeof(uint32_t) + mSaltLength; + uint16_t u16len = 0; + + size_t sizeof_point = sizeof(mPoint); + + uint8_t * msg = nullptr; + + VerifyOrExit(CanCastTo(resplen), err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + u16len = static_cast(resplen); + + resp = System::PacketBuffer::NewWithAvailableSize(u16len); VerifyOrExit(!resp.IsNull(), err = CHIP_SYSTEM_ERROR_NO_MEMORY); + msg = resp->Start(); + + // Fill in the random value + err = DRBG_get_bytes(msg, kPBKDFParamRandomNumberSize); + SuccessOrExit(err); + + // Let's construct the rest of the message using BufBound { - BufBound bbuf(resp->Start(), data_len); - bbuf.Put(&X[0], X_len); + BufBound bbuf(&msg[kPBKDFParamRandomNumberSize], resplen - kPBKDFParamRandomNumberSize); + bbuf.Put64(mIterationCount); + bbuf.Put32(mSaltLength); + bbuf.Put(mSalt, mSaltLength); VerifyOrExit(bbuf.Fit(), err = CHIP_ERROR_NO_MEMORY); } - resp->SetDataLength(data_len); - mNextExpectedMsg = Spake2pMsgType::kSpake2pCompute_pB_cB; + resp->SetDataLength(u16len); - // Call delegate to send the Compute_pA to peer - err = AttachHeaderAndSend(Spake2pMsgType::kSpake2pCompute_pA, resp.Release_ForNow()); + // Update commissioning hash with the pbkdf2 param response that's being sent. + err = mCommissioningHash.AddData(resp->Start(), resp->DataLength()); + SuccessOrExit(err); + + err = SetupSpake2p(mIterationCount, mSalt, mSaltLength); + SuccessOrExit(err); + + err = mSpake2p.ComputeL(mPoint, &sizeof_point, &mWS[1][0], kSpake2p_WS_Length); + SuccessOrExit(err); + + mNextExpectedMsg = Spake2pMsgType::kSpake2pMsg1; + + err = AttachHeaderAndSend(Spake2pMsgType::kPBKDFParamResponse, resp.Release_ForNow()); SuccessOrExit(err); - return err; + + ChipLogDetail(Ble, "Sent PBKDF param response"); exit: - mNextExpectedMsg = Spake2pMsgType::kSpake2pMsgTypeMax; return err; } -CHIP_ERROR SecurePairingSession::DeriveSecureSession(const uint8_t * info, size_t info_len, SecureSession & session) +CHIP_ERROR SecurePairingSession::HandlePBKDFParamResponse(const PacketHeader & header, const System::PacketBufferHandle & msg) { CHIP_ERROR err = CHIP_NO_ERROR; - VerifyOrExit(info != nullptr, err = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(info_len > 0, err = CHIP_ERROR_INVALID_ARGUMENT); - VerifyOrExit(mPairingComplete, err = CHIP_ERROR_INCORRECT_STATE); + // Response message processing + const uint8_t * resp = msg->Start(); + size_t resplen = msg->DataLength(); - err = session.InitFromSecret(mKe, mKeLen, nullptr, 0, info, info_len); + // This the fixed part of the message. The variable part of the message contains the salt. + // The length of the variable part is determined by the salt length in the fixed header. + static_assert(CHAR_BIT == 8, "Assuming that sizeof returns octets"); + size_t fixed_resplen = kPBKDFParamRandomNumberSize + sizeof(uint64_t) + sizeof(uint32_t); + + ChipLogDetail(Ble, "Received PBKDF param response"); + + VerifyOrExit(resp != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); + VerifyOrExit(resplen >= fixed_resplen, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + + { + // Let's skip the random number portion of the message + const uint8_t * msgptr = &resp[kPBKDFParamRandomNumberSize]; + uint64_t iterCount = chip::Encoding::LittleEndian::Read64(msgptr); + uint32_t saltlen = chip::Encoding::LittleEndian::Read32(msgptr); + + VerifyOrExit(resplen == fixed_resplen + saltlen, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + + // Specifications allow message to carry a uint64_t sized iteration count. Current APIs are + // limiting it to uint32_t. Let's make sure it'll fit the size limit. + VerifyOrExit(CanCastTo(iterCount), err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + + // Update commissioning hash with the received pbkdf2 param response + err = mCommissioningHash.AddData(resp, resplen); + SuccessOrExit(err); + + err = SetupSpake2p(static_cast(iterCount), msgptr, saltlen); + SuccessOrExit(err); + } + + err = SendMsg1(); SuccessOrExit(err); exit: + if (err != CHIP_NO_ERROR) + { + SendErrorMsg(Spake2pErrorType::kUnexpected); + } return err; } -CHIP_ERROR SecurePairingSession::HandleCompute_pA(const PacketHeader & header, const System::PacketBufferHandle & msg) +CHIP_ERROR SecurePairingSession::SendMsg1() +{ + uint8_t X[kMAX_Point_Length]; + size_t X_len = sizeof(X); + uint16_t data_len; // Will be the same as X_len in practice. + + System::PacketBufferHandle msg_pA; + + CHIP_ERROR err = mSpake2p.BeginProver(reinterpret_cast(""), 0, reinterpret_cast(""), 0, + &mWS[0][0], kSpake2p_WS_Length, &mWS[1][0], kSpake2p_WS_Length); + SuccessOrExit(err); + + err = mSpake2p.ComputeRoundOne(X, &X_len); + SuccessOrExit(err); + VerifyOrExit(CanCastTo(X_len), err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + data_len = static_cast(X_len); + + msg_pA = System::PacketBuffer::NewWithAvailableSize(data_len); + VerifyOrExit(!msg_pA.IsNull(), err = CHIP_SYSTEM_ERROR_NO_MEMORY); + + memcpy(msg_pA->Start(), &X[0], X_len); + + msg_pA->SetDataLength(data_len); + mNextExpectedMsg = Spake2pMsgType::kSpake2pMsg2; + + // Call delegate to send the Msg1 to peer + err = AttachHeaderAndSend(Spake2pMsgType::kSpake2pMsg1, msg_pA.Release_ForNow()); + SuccessOrExit(err); + + ChipLogDetail(Ble, "Sent spake2p msg1"); + +exit: + return err; +} + +CHIP_ERROR SecurePairingSession::HandleMsg1_and_SendMsg2(const PacketHeader & header, const System::PacketBufferHandle & msg) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -292,10 +532,12 @@ CHIP_ERROR SecurePairingSession::HandleCompute_pA(const PacketHeader & header, c uint16_t data_len; // To be initialized once we compute it. const uint8_t * buf = msg->Start(); - size_t buf_len = msg->TotalLength(); + size_t buf_len = msg->DataLength(); System::PacketBufferHandle resp; + ChipLogDetail(Ble, "Received spake2p msg1"); + VerifyOrExit(buf != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); VerifyOrExit(buf_len == kMAX_Point_Length, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); @@ -328,20 +570,24 @@ CHIP_ERROR SecurePairingSession::HandleCompute_pA(const PacketHeader & header, c } resp->SetDataLength(data_len); - mNextExpectedMsg = Spake2pMsgType::kSpake2pCompute_cA; + mNextExpectedMsg = Spake2pMsgType::kSpake2pMsg3; - // Call delegate to send the Compute_pB_cB to peer - err = AttachHeaderAndSend(Spake2pMsgType::kSpake2pCompute_pB_cB, resp.Release_ForNow()); + // Call delegate to send the Msg2 to peer + err = AttachHeaderAndSend(Spake2pMsgType::kSpake2pMsg2, resp.Release_ForNow()); SuccessOrExit(err); - return err; + + ChipLogDetail(Ble, "Sent spake2p msg2"); exit: - mNextExpectedMsg = Spake2pMsgType::kSpake2pMsgTypeMax; + if (err != CHIP_NO_ERROR) + { + SendErrorMsg(Spake2pErrorType::kUnexpected); + } return err; } -CHIP_ERROR SecurePairingSession::HandleCompute_pB_cB(const PacketHeader & header, const System::PacketBufferHandle & msg) +CHIP_ERROR SecurePairingSession::HandleMsg2_and_SendMsg3(const PacketHeader & header, const System::PacketBufferHandle & msg) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -350,10 +596,14 @@ CHIP_ERROR SecurePairingSession::HandleCompute_pB_cB(const PacketHeader & header uint16_t verifier_len; // To be inited one we check length is small enough const uint8_t * buf = msg->Start(); - size_t buf_len = msg->TotalLength(); + size_t buf_len = msg->DataLength(); System::PacketBufferHandle resp; + Spake2pErrorType spake2pErr = Spake2pErrorType::kUnexpected; + + ChipLogDetail(Ble, "Received spake2p msg2"); + VerifyOrExit(buf != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); VerifyOrExit(buf_len == kMAX_Point_Length + kMAX_Hash_Length, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); @@ -376,14 +626,20 @@ CHIP_ERROR SecurePairingSession::HandleCompute_pB_cB(const PacketHeader & header resp->SetDataLength(verifier_len); - // Call delegate to send the Compute_cA to peer - err = AttachHeaderAndSend(Spake2pMsgType::kSpake2pCompute_cA, resp.Release_ForNow()); + // Call delegate to send the Msg3 to peer + err = AttachHeaderAndSend(Spake2pMsgType::kSpake2pMsg3, resp.Release_ForNow()); SuccessOrExit(err); + ChipLogDetail(Ble, "Sent spake2p msg3"); + { const uint8_t * hash = &buf[kMAX_Point_Length]; err = mSpake2p.KeyConfirm(hash, kMAX_Hash_Length); - SuccessOrExit(err); + if (err != CHIP_NO_ERROR) + { + spake2pErr = Spake2pErrorType::kInvalidKeyConfirmation; + SuccessOrExit(err); + } err = mSpake2p.GetKeys(mKe, &mKeLen); SuccessOrExit(err); @@ -396,27 +652,37 @@ CHIP_ERROR SecurePairingSession::HandleCompute_pB_cB(const PacketHeader & header exit: - mNextExpectedMsg = Spake2pMsgType::kSpake2pMsgTypeMax; + if (err != CHIP_NO_ERROR) + { + SendErrorMsg(spake2pErr); + } return err; } -CHIP_ERROR SecurePairingSession::HandleCompute_cA(const PacketHeader & header, const System::PacketBufferHandle & msg) +CHIP_ERROR SecurePairingSession::HandleMsg3(const PacketHeader & header, const System::PacketBufferHandle & msg) { - CHIP_ERROR err = CHIP_NO_ERROR; - const uint8_t * hash = msg->Start(); + CHIP_ERROR err = CHIP_NO_ERROR; + const uint8_t * hash = msg->Start(); + Spake2pErrorType spake2pErr = Spake2pErrorType::kUnexpected; + + ChipLogDetail(Ble, "Received spake2p msg3"); - // We will set NextExpectedMsg to kSpake2pMsgTypeMax in all cases - // However, when we are using IP rendezvous, we might set it to kSpake2pCompute_pA. - mNextExpectedMsg = Spake2pMsgType::kSpake2pMsgTypeMax; + // We will set NextExpectedMsg to kSpake2pMsgError in all cases + // However, when we are using IP rendezvous, we might set it to kSpake2pMsg1. + mNextExpectedMsg = Spake2pMsgType::kSpake2pMsgError; VerifyOrExit(hash != nullptr, err = CHIP_ERROR_MESSAGE_INCOMPLETE); - VerifyOrExit(msg->TotalLength() == kMAX_Hash_Length, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); + VerifyOrExit(msg->DataLength() == kMAX_Hash_Length, err = CHIP_ERROR_INVALID_MESSAGE_LENGTH); VerifyOrExit(header.GetSourceNodeId() == mPeerNodeId, err = CHIP_ERROR_WRONG_NODE_ID); VerifyOrExit(header.GetEncryptionKeyID() == mPeerKeyId, err = CHIP_ERROR_INVALID_KEY_ID); err = mSpake2p.KeyConfirm(hash, kMAX_Hash_Length); - SuccessOrExit(err); + if (err != CHIP_NO_ERROR) + { + spake2pErr = Spake2pErrorType::kInvalidKeyConfirmation; + SuccessOrExit(err); + } err = mSpake2p.GetKeys(mKe, &mKeLen); SuccessOrExit(err); @@ -427,9 +693,54 @@ CHIP_ERROR SecurePairingSession::HandleCompute_cA(const PacketHeader & header, c mDelegate->OnPairingComplete(); exit: + + if (err != CHIP_NO_ERROR) + { + SendErrorMsg(spake2pErr); + } return err; } +void SecurePairingSession::SendErrorMsg(Spake2pErrorType errorCode) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + + System::PacketBufferHandle msg; + uint16_t msglen = sizeof(Spake2pErrorMsg); + Spake2pErrorMsg * pMsg = nullptr; + + msg = System::PacketBuffer::NewWithAvailableSize(msglen); + VerifyOrExit(!msg.IsNull(), err = CHIP_SYSTEM_ERROR_NO_MEMORY); + + pMsg = reinterpret_cast(msg->Start()); + pMsg->error = errorCode; + + msg->SetDataLength(msglen); + + err = AttachHeaderAndSend(Spake2pMsgType::kSpake2pMsgError, msg.Release_ForNow()); + SuccessOrExit(err); + +exit: + Clear(); +} + +void SecurePairingSession::HandleErrorMsg(const PacketHeader & header, const System::PacketBufferHandle & msg) +{ + // Request message processing + const uint8_t * buf = msg->Start(); + size_t buflen = msg->DataLength(); + Spake2pErrorMsg * pMsg = nullptr; + + VerifyOrExit(buf != nullptr, ChipLogError(Ble, "Null error msg received during pairing")); + VerifyOrExit(buflen == sizeof(Spake2pErrorMsg), ChipLogError(Ble, "Error msg with incorrect length received during pairing")); + + pMsg = reinterpret_cast(msg->Start()); + ChipLogError(Ble, "Received error (%d) during pairing process", pMsg->error); + +exit: + Clear(); +} + CHIP_ERROR SecurePairingSession::HandlePeerMessage(const PacketHeader & packetHeader, const Transport::PeerAddress & peerAddress, System::PacketBufferHandle msg) { @@ -451,16 +762,24 @@ CHIP_ERROR SecurePairingSession::HandlePeerMessage(const PacketHeader & packetHe switch (static_cast(payloadHeader.GetMessageType())) { - case Spake2pMsgType::kSpake2pCompute_pA: - err = HandleCompute_pA(packetHeader, msg); + case Spake2pMsgType::kPBKDFParamRequest: + err = HandlePBKDFParamRequest(packetHeader, msg); + break; + + case Spake2pMsgType::kPBKDFParamResponse: + err = HandlePBKDFParamResponse(packetHeader, msg); + break; + + case Spake2pMsgType::kSpake2pMsg1: + err = HandleMsg1_and_SendMsg2(packetHeader, msg); break; - case Spake2pMsgType::kSpake2pCompute_pB_cB: - err = HandleCompute_pB_cB(packetHeader, msg); + case Spake2pMsgType::kSpake2pMsg2: + err = HandleMsg2_and_SendMsg3(packetHeader, msg); break; - case Spake2pMsgType::kSpake2pCompute_cA: - err = HandleCompute_cA(packetHeader, msg); + case Spake2pMsgType::kSpake2pMsg3: + err = HandleMsg3(packetHeader, msg); break; default: diff --git a/src/transport/SecurePairingSession.h b/src/transport/SecurePairingSession.h index 7693c2d94ecf9d..8385ce3d2944cc 100644 --- a/src/transport/SecurePairingSession.h +++ b/src/transport/SecurePairingSession.h @@ -38,6 +38,8 @@ namespace chip { extern const char * kSpake2pI2RSessionInfo; extern const char * kSpake2pR2ISessionInfo; +constexpr uint16_t kPBKDFParamRandomNumberSize = 32; + using namespace Crypto; class DLL_EXPORT SecurePairingSessionDelegate @@ -122,18 +124,14 @@ class DLL_EXPORT SecurePairingSession * * @param peerAddress Address of peer to pair * @param peerSetUpPINCode Setup PIN code of the peer device - * @param pbkdf2IterCount Iteration count for PBKDF2 function - * @param salt Salt to be used for SPAKE2P opertation - * @param saltLen Length of salt * @param myNodeId Optional node id of local node * @param myKeyId Key ID to be assigned to the secure session on the peer node * @param delegate Callback object * * @return CHIP_ERROR The result of initialization */ - CHIP_ERROR Pair(const Transport::PeerAddress peerAddress, uint32_t peerSetUpPINCode, uint32_t pbkdf2IterCount, - const uint8_t * salt, size_t saltLen, Optional myNodeId, uint16_t myKeyId, - SecurePairingSessionDelegate * delegate); + CHIP_ERROR Pair(const Transport::PeerAddress peerAddress, uint32_t peerSetUpPINCode, Optional myNodeId, + uint16_t myKeyId, SecurePairingSessionDelegate * delegate); /** * @brief @@ -209,28 +207,50 @@ class DLL_EXPORT SecurePairingSession CHIP_ERROR FromSerializable(const SecurePairingSessionSerializable & output); private: - CHIP_ERROR Init(uint32_t setupCode, uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen, Optional myNodeId, - uint16_t myKeyId, SecurePairingSessionDelegate * delegate); + enum Spake2pErrorType : uint8_t + { + kInvalidKeyConfirmation = 0x00, + kUnexpected = 0xff, + }; + + CHIP_ERROR Init(Optional myNodeId, uint16_t myKeyId, uint32_t setupCode, SecurePairingSessionDelegate * delegate); + + CHIP_ERROR SetupSpake2p(uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen); + + CHIP_ERROR SendPBKDFParamRequest(); + CHIP_ERROR HandlePBKDFParamRequest(const PacketHeader & header, const System::PacketBufferHandle & msg); + + CHIP_ERROR SendPBKDFParamResponse(); + CHIP_ERROR HandlePBKDFParamResponse(const PacketHeader & header, const System::PacketBufferHandle & msg); - CHIP_ERROR HandleCompute_pA(const PacketHeader & header, const System::PacketBufferHandle & msg); - CHIP_ERROR HandleCompute_pB_cB(const PacketHeader & header, const System::PacketBufferHandle & msg); - CHIP_ERROR HandleCompute_cA(const PacketHeader & header, const System::PacketBufferHandle & msg); + CHIP_ERROR SendMsg1(); + + CHIP_ERROR HandleMsg1_and_SendMsg2(const PacketHeader & header, const System::PacketBufferHandle & msg); + CHIP_ERROR HandleMsg2_and_SendMsg3(const PacketHeader & header, const System::PacketBufferHandle & msg); + CHIP_ERROR HandleMsg3(const PacketHeader & header, const System::PacketBufferHandle & msg); + + void SendErrorMsg(Spake2pErrorType errorCode); + void HandleErrorMsg(const PacketHeader & header, const System::PacketBufferHandle & msg); CHIP_ERROR AttachHeaderAndSend(uint8_t msgType, System::PacketBuffer * msgBuf); + void Clear(); + static constexpr size_t kSpake2p_WS_Length = kP256_FE_Length + 8; enum Spake2pMsgType : uint8_t { - kSpake2pCompute_pA = 0, - kSpake2pCompute_pB_cB = 1, - kSpake2pCompute_cA = 2, - kSpake2pMsgTypeMax = 3, + kPBKDFParamRequest = 0x20, + kPBKDFParamResponse = 0x21, + kSpake2pMsg1 = 0x22, + kSpake2pMsg2 = 0x23, + kSpake2pMsg3 = 0x24, + kSpake2pMsgError = 0x2f, }; SecurePairingSessionDelegate * mDelegate = nullptr; - Spake2pMsgType mNextExpectedMsg = Spake2pMsgType::kSpake2pMsgTypeMax; + Spake2pMsgType mNextExpectedMsg = Spake2pMsgType::kSpake2pMsgError; Spake2p_P256_SHA256_HKDF_HMAC mSpake2p; @@ -239,6 +259,18 @@ class DLL_EXPORT SecurePairingSession /* w0s and w1s */ uint8_t mWS[2][kSpake2p_WS_Length]; + uint32_t mSetupPINCode; + + Hash_SHA256_stream mCommissioningHash; + uint32_t mIterationCount = 0; + uint16_t mSaltLength = 0; + uint8_t * mSalt = nullptr; + + struct Spake2pErrorMsg + { + Spake2pErrorType error; + }; + protected: Optional mLocalNodeId = Optional::Value(kUndefinedNodeId); diff --git a/src/transport/tests/TestSecurePairingSession.cpp b/src/transport/tests/TestSecurePairingSession.cpp index 98208b9581751d..b198c2ac5057e3 100644 --- a/src/transport/tests/TestSecurePairingSession.cpp +++ b/src/transport/tests/TestSecurePairingSession.cpp @@ -82,14 +82,11 @@ void SecurePairingStartTest(nlTestSuite * inSuite, void * inContext) SecurePairingSession pairing; NL_TEST_ASSERT(inSuite, - pairing.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, 500, nullptr, 0, Optional::Value(1), 0, - &delegate) == CHIP_ERROR_INVALID_ARGUMENT); - NL_TEST_ASSERT(inSuite, - pairing.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, 500, (const uint8_t *) "salt", 4, - Optional::Value(1), 0, nullptr) == CHIP_ERROR_INVALID_ARGUMENT); + pairing.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, Optional::Value(1), 0, nullptr) != + CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, - pairing.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, 500, (const uint8_t *) "salt", 4, - Optional::Value(1), 0, &delegate) == CHIP_NO_ERROR); + pairing.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, Optional::Value(1), 0, &delegate) == + CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, delegate.mNumMessageSend == 1); @@ -98,8 +95,8 @@ void SecurePairingStartTest(nlTestSuite * inSuite, void * inContext) SecurePairingSession pairing1; NL_TEST_ASSERT(inSuite, - pairing1.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, 500, (const uint8_t *) "salt", 4, - Optional::Value(1), 0, &delegate) == CHIP_ERROR_BAD_REQUEST); + pairing1.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, Optional::Value(1), 0, &delegate) == + CHIP_ERROR_BAD_REQUEST); } void SecurePairingHandshakeTestCommon(nlTestSuite * inSuite, void * inContext, SecurePairingSession & pairingCommissioner, @@ -116,13 +113,13 @@ void SecurePairingHandshakeTestCommon(nlTestSuite * inSuite, void * inContext, S pairingAccessory.WaitForPairing(1234, 500, (const uint8_t *) "salt", 4, Optional::Value(1), 0, &delegateAccessory) == CHIP_NO_ERROR); NL_TEST_ASSERT(inSuite, - pairingCommissioner.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, 500, (const uint8_t *) "salt", 4, - Optional::Value(2), 0, &delegateCommissioner) == CHIP_NO_ERROR); + pairingCommissioner.Pair(Transport::PeerAddress(Transport::Type::kBle), 1234, Optional::Value(2), 0, + &delegateCommissioner) == CHIP_NO_ERROR); - NL_TEST_ASSERT(inSuite, delegateAccessory.mNumMessageSend == 1); + NL_TEST_ASSERT(inSuite, delegateAccessory.mNumMessageSend == 2); NL_TEST_ASSERT(inSuite, delegateAccessory.mNumPairingComplete == 1); - NL_TEST_ASSERT(inSuite, delegateCommissioner.mNumMessageSend == 2); + NL_TEST_ASSERT(inSuite, delegateCommissioner.mNumMessageSend == 3); NL_TEST_ASSERT(inSuite, delegateCommissioner.mNumPairingComplete == 1); } @@ -254,6 +251,7 @@ void LogV(uint8_t module, uint8_t category, const char * format, va_list argptr) { (void) module, (void) category; vfprintf(stderr, format, argptr); + fprintf(stderr, "\n"); } } // namespace Logging } // namespace chip