| /* |
| * |
| * Copyright (c) 2020-2022 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. |
| */ |
| |
| /** |
| * @file |
| * This file defines the CHIP SPAKE2P Session object that provides |
| * APIs for constructing spake2p messages and establishing encryption |
| * keys. |
| * |
| */ |
| |
| #pragma once |
| |
| #include <crypto/CHIPCryptoPAL.h> |
| #if CHIP_CRYPTO_HSM |
| #include <crypto/hsm/CHIPCryptoPALHsm.h> |
| #endif |
| #include <lib/support/Base64.h> |
| #include <messaging/ExchangeContext.h> |
| #include <messaging/ExchangeDelegate.h> |
| #include <messaging/ExchangeMessageDispatch.h> |
| #include <protocols/secure_channel/Constants.h> |
| #include <protocols/secure_channel/SessionEstablishmentDelegate.h> |
| #include <protocols/secure_channel/SessionEstablishmentExchangeDispatch.h> |
| #include <system/SystemPacketBuffer.h> |
| #include <transport/CryptoContext.h> |
| #include <transport/PairingSession.h> |
| #include <transport/raw/MessageHeader.h> |
| #include <transport/raw/PeerAddress.h> |
| |
| namespace chip { |
| |
| extern const char * kSpake2pI2RSessionInfo; |
| extern const char * kSpake2pR2ISessionInfo; |
| |
| constexpr uint16_t kPBKDFParamRandomNumberSize = 32; |
| |
| constexpr uint32_t kSetupPINCodeMaximumValue = 99999998; |
| constexpr uint32_t kSetupPINCodeUndefinedValue = 0; |
| |
| using namespace Crypto; |
| |
| struct PASESessionSerialized; |
| |
| struct PASESessionSerializable |
| { |
| uint16_t mKeLen; |
| uint8_t mKe[kMAX_Hash_Length]; |
| uint8_t mPairingComplete; |
| uint16_t mLocalSessionId; |
| uint16_t mPeerSessionId; |
| }; |
| |
| class DLL_EXPORT PASESession : public Messaging::UnsolicitedMessageHandler, |
| public Messaging::ExchangeDelegate, |
| public PairingSession |
| { |
| public: |
| PASESession(); |
| PASESession(PASESession &&) = default; |
| PASESession(const PASESession &) = delete; |
| |
| ~PASESession() override; |
| |
| // TODO: The SetPeerNodeId method should not be exposed; PASE sessions |
| // should not need to be told their peer node ID |
| using PairingSession::SetPeerNodeId; |
| |
| CHIP_ERROR OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate) override; |
| |
| /** |
| * @brief |
| * Initialize using PASE verifier and wait for pairing requests. |
| * |
| * @param sessionManager session manager from which to allocate a secure session object |
| * @param verifier PASE verifier to be used for SPAKE2P pairing |
| * @param pbkdf2IterCount Iteration count for PBKDF2 function |
| * @param salt Salt to be used for SPAKE2P operation |
| * @param delegate Callback object |
| * |
| * @return CHIP_ERROR The result of initialization |
| */ |
| CHIP_ERROR WaitForPairing(SessionManager & sessionManager, const Spake2pVerifier & verifier, uint32_t pbkdf2IterCount, |
| const ByteSpan & salt, Optional<ReliableMessageProtocolConfig> mrpConfig, |
| SessionEstablishmentDelegate * delegate); |
| |
| /** |
| * @brief |
| * Create a pairing request using peer's setup PIN code. |
| * |
| * @param sessionManager session manager from which to allocate a secure session object |
| * @param peerAddress Address of peer to pair |
| * @param peerSetUpPINCode Setup PIN code of the peer device |
| * @param exchangeCtxt The exchange context to send and receive messages with the peer |
| * Note: It's expected that the caller of this API hands over the |
| * ownership of the exchangeCtxt to PASESession object. PASESession |
| * will close the exchange on (successful/failed) handshake completion. |
| * @param delegate Callback object |
| * |
| * @return CHIP_ERROR The result of initialization |
| */ |
| CHIP_ERROR Pair(SessionManager & sessionManager, const Transport::PeerAddress peerAddress, uint32_t peerSetUpPINCode, |
| Optional<ReliableMessageProtocolConfig> mrpConfig, Messaging::ExchangeContext * exchangeCtxt, |
| SessionEstablishmentDelegate * delegate); |
| |
| /** |
| * @brief |
| * Generate a new PASE verifier. |
| * |
| * @param verifier The generated PASE verifier |
| * @param pbkdf2IterCount Iteration count for PBKDF2 function |
| * @param salt Salt to be used for SPAKE2P operation |
| * @param useRandomPIN Generate a random setup PIN, if true. Else, use the provided PIN |
| * @param setupPIN Provided setup PIN (if useRandomPIN is false), or the generated PIN |
| * |
| * @return CHIP_ERROR The result of PASE verifier generation |
| */ |
| static CHIP_ERROR GeneratePASEVerifier(Spake2pVerifier & verifier, uint32_t pbkdf2IterCount, const ByteSpan & salt, |
| bool useRandomPIN, uint32_t & setupPIN); |
| |
| /** |
| * @brief |
| * Derive a secure session from the paired session. The API will return error |
| * if called before pairing is established. |
| * |
| * @param session Reference to the secure session that will be |
| * initialized once pairing is complete |
| * @param role Role of the new session (initiator or responder) |
| * @return CHIP_ERROR The result of session derivation |
| */ |
| CHIP_ERROR DeriveSecureSession(CryptoContext & session, CryptoContext::SessionRole role) 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. |
| **/ |
| void Clear(); |
| |
| //// ExchangeDelegate Implementation //// |
| /** |
| * @brief |
| * This function is the called by exchange context or exchange manager when it receives |
| * a CHIP message corresponding to the context, or registered unsolicited message handler. |
| * |
| * Note: If the function is called by unsolicited message handler, the ownership of the |
| * provide exchange context is handed over to PASE Session object. The PASE Session |
| * object ensures that the exchange will be closed on completion of the handshake. |
| * |
| * @param[in] ec A pointer to the ExchangeContext object. |
| * @param[in] payloadHeader A reference to the PayloadHeader object. |
| * @param[in] payload A handle to the PacketBuffer object holding the message payload. |
| */ |
| CHIP_ERROR OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader, |
| System::PacketBufferHandle && payload) override; |
| |
| /** |
| * @brief |
| * This function is the protocol callback to invoke when the timeout for the receipt |
| * of a response message has expired. |
| * |
| * @param[in] ec A pointer to the ExchangeContext object. |
| */ |
| void OnResponseTimeout(Messaging::ExchangeContext * ec) override; |
| |
| Messaging::ExchangeMessageDispatch & GetMessageDispatch() override { return SessionEstablishmentExchangeDispatch::Instance(); } |
| |
| private: |
| enum Spake2pErrorType : uint8_t |
| { |
| kInvalidKeyConfirmation = 0x00, |
| kUnexpected = 0xff, |
| }; |
| |
| CHIP_ERROR Init(SessionManager & sessionManager, uint32_t setupCode, SessionEstablishmentDelegate * delegate); |
| |
| CHIP_ERROR ValidateReceivedMessage(Messaging::ExchangeContext * exchange, const PayloadHeader & payloadHeader, |
| const System::PacketBufferHandle & msg); |
| |
| CHIP_ERROR SetupSpake2p(); |
| |
| CHIP_ERROR SendPBKDFParamRequest(); |
| CHIP_ERROR HandlePBKDFParamRequest(System::PacketBufferHandle && msg); |
| |
| CHIP_ERROR SendPBKDFParamResponse(ByteSpan initiatorRandom, bool initiatorHasPBKDFParams); |
| CHIP_ERROR HandlePBKDFParamResponse(System::PacketBufferHandle && msg); |
| |
| CHIP_ERROR SendMsg1(); |
| |
| CHIP_ERROR HandleMsg1_and_SendMsg2(System::PacketBufferHandle && msg); |
| CHIP_ERROR HandleMsg2_and_SendMsg3(System::PacketBufferHandle && msg); |
| CHIP_ERROR HandleMsg3(System::PacketBufferHandle && msg); |
| |
| void OnSuccessStatusReport() override; |
| CHIP_ERROR OnFailureStatusReport(Protocols::SecureChannel::GeneralStatusCode generalCode, uint16_t protocolCode) override; |
| |
| void CloseExchange(); |
| |
| /** |
| * Clear our reference to our exchange context pointer so that it can close |
| * itself at some later time. |
| */ |
| void DiscardExchange(); |
| |
| SessionEstablishmentDelegate * mDelegate = nullptr; |
| |
| Protocols::SecureChannel::MsgType mNextExpectedMsg = Protocols::SecureChannel::MsgType::PASE_PakeError; |
| |
| #ifdef ENABLE_HSM_SPAKE |
| Spake2pHSM_P256_SHA256_HKDF_HMAC mSpake2p; |
| #else |
| Spake2p_P256_SHA256_HKDF_HMAC mSpake2p; |
| #endif |
| |
| Spake2pVerifier mPASEVerifier; |
| |
| uint32_t mSetupPINCode; |
| |
| bool mHavePBKDFParameters = false; |
| |
| uint8_t mPBKDFLocalRandomData[kPBKDFParamRandomNumberSize]; |
| |
| Hash_SHA256_stream mCommissioningHash; |
| uint32_t mIterationCount = 0; |
| uint16_t mSaltLength = 0; |
| uint8_t * mSalt = nullptr; |
| |
| Messaging::ExchangeContext * mExchangeCtxt = nullptr; |
| |
| Optional<ReliableMessageProtocolConfig> mLocalMRPConfig; |
| |
| struct Spake2pErrorMsg |
| { |
| Spake2pErrorType error; |
| }; |
| |
| protected: |
| uint8_t mKe[kMAX_Hash_Length]; |
| |
| size_t mKeLen = sizeof(mKe); |
| |
| bool mPairingComplete = false; |
| }; |
| |
| /* |
| * The following constants are node IDs that test devices and test |
| * controllers use while using the SecurePairingUsingTestSecret to |
| * establish secure channel |
| */ |
| constexpr chip::NodeId kTestControllerNodeId = 112233; |
| constexpr chip::NodeId kTestDeviceNodeId = 12344321; |
| |
| /* |
| * The following class should only be used for test usecases. |
| * The class is currently also used for devices that do no yet support |
| * rendezvous. Once all the non-test usecases start supporting |
| * rendezvous, this class will be moved to the test code. |
| */ |
| class SecurePairingUsingTestSecret : public PairingSession |
| { |
| public: |
| SecurePairingUsingTestSecret() : PairingSession(Transport::SecureSession::Type::kPASE) |
| { |
| // Do not set to 0 to prevent an unwanted unsecured session |
| // since the session type is unknown. |
| SetPeerSessionId(1); |
| } |
| |
| void Init(SessionManager & sessionManager) |
| { |
| // Do not set to 0 to prevent an unwanted unsecured session |
| // since the session type is unknown. |
| AllocateSecureSession(sessionManager, mLocalSessionId); |
| } |
| |
| SecurePairingUsingTestSecret(uint16_t peerSessionId, uint16_t localSessionId, SessionManager & sessionManager) : |
| PairingSession(Transport::SecureSession::Type::kPASE), mLocalSessionId(localSessionId) |
| { |
| AllocateSecureSession(sessionManager, localSessionId); |
| SetPeerSessionId(peerSessionId); |
| } |
| |
| CHIP_ERROR DeriveSecureSession(CryptoContext & session, CryptoContext::SessionRole role) override |
| { |
| size_t secretLen = strlen(kTestSecret); |
| return session.InitFromSecret(ByteSpan(reinterpret_cast<const uint8_t *>(kTestSecret), secretLen), ByteSpan(nullptr, 0), |
| CryptoContext::SessionInfoType::kSessionEstablishment, role); |
| } |
| |
| private: |
| // Do not set to 0 to prevent an unwanted unsecured session |
| // since the session type is unknown. |
| uint16_t mLocalSessionId = 1; |
| const char * kTestSecret = CHIP_CONFIG_TEST_SHARED_SECRET_VALUE; |
| }; |
| |
| } // namespace chip |