blob: 97d4bfc9ec3fafaee659f0d487e021be335f6b03 [file] [log] [blame]
/*
*
* Copyright (c) 2020 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>
#include <support/Base64.h>
#include <system/SystemPacketBuffer.h>
#include <transport/SecureSession.h>
#include <transport/raw/MessageHeader.h>
#include <transport/raw/PeerAddress.h>
namespace chip {
extern const char * kSpake2pI2RSessionInfo;
extern const char * kSpake2pR2ISessionInfo;
using namespace Crypto;
class DLL_EXPORT SecurePairingSessionDelegate
{
public:
/**
* @brief
* Called when pairing session generates a new message that should be sent to peer.
*
* @param header the message header for the sent message
* @param payloadFlags payload encoding flags
* @param peerAddress the destination of the message
* @param msgBuf the raw data for the message being sent
* @return CHIP_ERROR Error thrown when sending the message
*/
virtual CHIP_ERROR SendPairingMessage(const PacketHeader & header, Header::Flags payloadFlags,
const Transport::PeerAddress & peerAddress, System::PacketBuffer * msgBuf)
{
return CHIP_ERROR_NOT_IMPLEMENTED;
}
/**
* @brief
* Called when pairing fails with an error
*
* @param error error code
*/
virtual void OnPairingError(CHIP_ERROR error) {}
/**
* @brief
* Called when the pairing is complete and the new secure session has been established
*/
virtual void OnPairingComplete() {}
virtual ~SecurePairingSessionDelegate() {}
};
struct SecurePairingSessionSerialized;
struct SecurePairingSessionSerializable
{
uint16_t mKeLen;
uint8_t mKe[kMAX_Hash_Length];
uint8_t mPairingComplete;
uint64_t mLocalNodeId;
uint64_t mPeerNodeId;
uint16_t mLocalKeyId;
uint16_t mPeerKeyId;
};
class DLL_EXPORT SecurePairingSession
{
public:
SecurePairingSession();
SecurePairingSession(SecurePairingSession &&) = default;
SecurePairingSession(const SecurePairingSession &) = delete;
SecurePairingSession & operator=(const SecurePairingSession &) = default;
SecurePairingSession & operator=(SecurePairingSession &&) = default;
virtual ~SecurePairingSession();
/**
* @brief
* Initialize using setup PIN code and wait for pairing requests.
*
* @param mySetUpPINCode Setup PIN code of the local 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 WaitForPairing(uint32_t mySetUpPINCode, uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen,
Optional<NodeId> myNodeId, uint16_t myKeyId, SecurePairingSessionDelegate * delegate);
/**
* @brief
* Create a pairing request using peer's setup PIN code.
*
* @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<NodeId> myNodeId, uint16_t myKeyId,
SecurePairingSessionDelegate * delegate);
/**
* @brief
* Derive a secure session from the paired session. The API will return error
* if called before pairing is established.
*
* @param info Information string used for key derivation
* @param info_len Length of info string
* @param session Referene to the sescure session that will be
* initialized once pairing is complete
* @return CHIP_ERROR The result of session derivation
*/
virtual CHIP_ERROR DeriveSecureSession(const uint8_t * info, size_t info_len, SecureSession & session);
/**
* @brief
* Handler for peer's messages, exchanged during pairing handshake.
*
* @param packetHeader Message header for the received message
* @param peerAddress Source of the message
* @param msg Message sent by the peer
* @return CHIP_ERROR The result of message processing
*/
virtual CHIP_ERROR HandlePeerMessage(const PacketHeader & packetHeader, const Transport::PeerAddress & peerAddress,
System::PacketBufferHandle msg);
/**
* @brief
* Return the associated secure session peer NodeId
*
* @return Optional<NodeId> The associated peer NodeId
*/
NodeId GetPeerNodeId() const { return mPeerNodeId.Value(); }
/**
* @brief
* Return the associated peer key id
*
* @return uint16_t The associated peer key id
*/
uint16_t GetPeerKeyId() { return mPeerKeyId; }
/**
* @brief
* Return the associated local key id
*
* @return uint16_t The assocated local key id
*/
uint16_t GetLocalKeyId() { return mLocalKeyId; }
/** @brief Serialize the Pairing Session to a string.
*
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
**/
CHIP_ERROR Serialize(SecurePairingSessionSerialized & output);
/** @brief Deserialize the Pairing Session from the string.
*
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
**/
CHIP_ERROR Deserialize(SecurePairingSessionSerialized & input);
/** @brief Serialize the SecurePairingSession to the given serializable data structure for secure pairing
*
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
**/
CHIP_ERROR ToSerializable(SecurePairingSessionSerializable & output);
/** @brief Reconstruct secure pairing class from the serializable data structure.
*
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
**/
CHIP_ERROR FromSerializable(const SecurePairingSessionSerializable & output);
private:
CHIP_ERROR Init(uint32_t setupCode, uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen, Optional<NodeId> myNodeId,
uint16_t myKeyId, SecurePairingSessionDelegate * delegate);
CHIP_ERROR HandleCompute_pA(const PacketHeader & header, System::PacketBuffer * msg);
CHIP_ERROR HandleCompute_pB_cB(const PacketHeader & header, System::PacketBuffer * msg);
CHIP_ERROR HandleCompute_cA(const PacketHeader & header, System::PacketBuffer * msg);
CHIP_ERROR AttachHeaderAndSend(uint8_t msgType, System::PacketBuffer * msgBuf);
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,
};
SecurePairingSessionDelegate * mDelegate = nullptr;
Spake2pMsgType mNextExpectedMsg = Spake2pMsgType::kSpake2pMsgTypeMax;
Spake2p_P256_SHA256_HKDF_HMAC mSpake2p;
uint8_t mPoint[kMAX_Point_Length];
/* w0s and w1s */
uint8_t mWS[2][kSpake2p_WS_Length];
protected:
Optional<NodeId> mLocalNodeId = Optional<NodeId>::Value(kUndefinedNodeId);
Optional<NodeId> mPeerNodeId = Optional<NodeId>::Value(kUndefinedNodeId);
uint16_t mLocalKeyId;
uint16_t mPeerKeyId;
Transport::PeerAddress mPeerAddress;
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 SecurePairingSession
{
public:
SecurePairingUsingTestSecret()
{
const char * secret = "Test secret for key derivation";
size_t secretLen = strlen(secret);
mKeLen = secretLen;
memmove(mKe, secret, mKeLen);
mPairingComplete = true;
}
SecurePairingUsingTestSecret(Optional<NodeId> peerNodeId, uint16_t peerKeyId, uint16_t localKeyId)
{
const char * secret = "Test secret for key derivation";
size_t secretLen = strlen(secret);
mPeerNodeId = peerNodeId;
mPeerKeyId = peerKeyId;
mLocalKeyId = localKeyId;
mKeLen = secretLen;
memmove(mKe, secret, mKeLen);
mPairingComplete = true;
}
~SecurePairingUsingTestSecret() override {}
CHIP_ERROR WaitForPairing(uint32_t mySetUpPINCode, uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen,
Optional<NodeId> myNodeId, uint16_t myKeyId, SecurePairingSessionDelegate * delegate)
{
return CHIP_NO_ERROR;
}
CHIP_ERROR Pair(uint32_t peerSetUpPINCode, uint32_t pbkdf2IterCount, const uint8_t * salt, size_t saltLen,
Optional<NodeId> myNodeId, uint16_t myKeyId, SecurePairingSessionDelegate * delegate)
{
return CHIP_NO_ERROR;
}
CHIP_ERROR HandlePeerMessage(const PacketHeader & packetHeader, const Transport::PeerAddress & peerAddress,
System::PacketBufferHandle msg) override
{
return CHIP_NO_ERROR;
}
};
typedef struct SecurePairingSessionSerialized
{
// Extra uint64_t to account for padding bytes (NULL termination, and some decoding overheads)
uint8_t inner[BASE64_ENCODED_LEN(sizeof(SecurePairingSessionSerializable) + sizeof(uint64_t))];
} SecurePairingSessionSerialized;
} // namespace chip