blob: dfe8d8cabdca187831d85e3f7609625afc045f9d [file] [log] [blame]
/*
*
* Copyright (c) 2020-2021 Project CHIP Authors
*
* 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 CHIP binary header encode/decode.
*/
#pragma once
#include <cstddef>
#include <cstdint>
#include <string.h>
#include <type_traits>
#include <core/CHIPError.h>
#include <core/Optional.h>
#include <protocols/Protocols.h>
#include <support/BitFlags.h>
#include <system/SystemPacketBuffer.h>
namespace chip {
/// Convenience type to make it clear a number represents a node id.
typedef uint64_t NodeId;
static constexpr NodeId kUndefinedNodeId = 0ULL;
static constexpr NodeId kAnyNodeId = 0xFFFFFFFFFFFFFFFFULL;
static constexpr size_t kMaxTagLen = 16;
typedef int PacketHeaderFlags;
namespace Header {
enum class EncryptionType
{
kAESCCMTagLen8 = 0,
kAESCCMTagLen12 = 1,
kAESCCMTagLen16 = 2,
};
/**
* @brief
* The CHIP Exchange header flag bits.
*/
enum class ExFlagValues : uint8_t
{
/// Set when current message is sent by the initiator of an exchange.
kExchangeFlag_Initiator = 0x01,
/// Set when current message is an acknowledgment for a previously received message.
kExchangeFlag_AckMsg = 0x02,
/// Set when current message is requesting an acknowledgment from the recipient.
kExchangeFlag_NeedsAck = 0x04,
/// Set when a vendor id is prepended to the Message Protocol Id field.
kExchangeFlag_VendorIdPresent = 0x10,
};
enum class InternalFlagValues : uint8_t
{
// Header flag indicates that the peer's group key message counter is not synchronized.
kPeerGroupMsgIdNotSynchronized = 0x01,
};
enum class FlagValues : uint16_t
{
/// Header flag specifying that a destination node id is included in the header.
kDestinationNodeIdPresent = 0x0100,
/// Header flag specifying that a source node id is included in the header.
kSourceNodeIdPresent = 0x0200,
/// Header flag specifying that it is a control message for secure session.
kSecureSessionControlMessage = 0x0800,
/// Header flag specifying that it is a encrypted message.
kSecure = 0x0001,
};
using Flags = BitFlags<FlagValues>;
using ExFlags = BitFlags<ExFlagValues>;
using InternalFlags = BitFlags<InternalFlagValues>;
// Header is a 16-bit value of the form
// | 4 bit | 4 bit |8 bit Security Flags|
// +---------+-------+--------------------|
// | version | Flags | P | C |Reserved| E |
// | | +---Encrypted
// | +----------------Control message (TODO: Implement this)
// +--------------------Privacy enhancements (TODO: Implement this)
static constexpr uint16_t kFlagsMask = 0x0F01;
} // namespace Header
/**
* Handles encoding/decoding of CHIP packet message headers.
*
* Packet headers are **UNENCRYPTED** and are placed at the start of
* a message buffer.
*/
class PacketHeader
{
public:
/**
* Gets the message id set in the header.
*
* Message IDs are expecte to monotonically increase by one for each mesage
* that has been sent.
*/
uint32_t GetMessageId() const { return mMessageId; }
/**
* Gets the source node id in the current message.
*
* NOTE: the source node id is optional and may be missing.
*/
const Optional<NodeId> & GetSourceNodeId() const { return mSourceNodeId; }
/**
* Gets the destination node id in the current message.
*
* NOTE: the destination node id is optional and may be missing.
*/
const Optional<NodeId> & GetDestinationNodeId() const { return mDestinationNodeId; }
uint16_t GetEncryptionKeyID() const { return mEncryptionKeyID; }
Header::Flags & GetFlags() { return mFlags; }
const Header::Flags & GetFlags() const { return mFlags; }
/** Check if it's a secure session control message. */
bool IsSecureSessionControlMsg() const { return mFlags.Has(Header::FlagValues::kSecureSessionControlMessage); }
/** Check if the peer's group key message counter is not synchronized. */
bool IsPeerGroupMsgIdNotSynchronized() const
{
return mInternalFlags.Has(Header::InternalFlagValues::kPeerGroupMsgIdNotSynchronized);
}
Header::EncryptionType GetEncryptionType() const { return mEncryptionType; }
PacketHeader & SetSecureSessionControlMsg(bool value)
{
mFlags.Set(Header::FlagValues::kSecureSessionControlMessage, value);
return *this;
}
PacketHeader & SetPeerGroupMsgIdNotSynchronized(bool value)
{
mInternalFlags.Set(Header::InternalFlagValues::kPeerGroupMsgIdNotSynchronized, value);
return *this;
}
PacketHeader & SetSourceNodeId(NodeId id)
{
mSourceNodeId.SetValue(id);
mFlags.Set(Header::FlagValues::kSourceNodeIdPresent);
return *this;
}
PacketHeader & SetSourceNodeId(Optional<NodeId> id)
{
mSourceNodeId = id;
mFlags.Set(Header::FlagValues::kSourceNodeIdPresent, id.HasValue());
return *this;
}
PacketHeader & ClearSourceNodeId()
{
mSourceNodeId.ClearValue();
mFlags.Clear(Header::FlagValues::kSourceNodeIdPresent);
return *this;
}
PacketHeader & SetDestinationNodeId(NodeId id)
{
mDestinationNodeId.SetValue(id);
mFlags.Set(Header::FlagValues::kDestinationNodeIdPresent);
return *this;
}
PacketHeader & SetDestinationNodeId(Optional<NodeId> id)
{
mDestinationNodeId = id;
mFlags.Set(Header::FlagValues::kDestinationNodeIdPresent, id.HasValue());
return *this;
}
PacketHeader & ClearDestinationNodeId()
{
mDestinationNodeId.ClearValue();
mFlags.Clear(Header::FlagValues::kDestinationNodeIdPresent);
return *this;
}
PacketHeader & SetEncryptionKeyID(uint16_t id)
{
mEncryptionKeyID = id;
return *this;
}
PacketHeader & SetMessageId(uint32_t id)
{
mMessageId = id;
return *this;
}
PacketHeader & SetEncryptionType(Header::EncryptionType type)
{
mEncryptionType = type;
return *this;
}
/**
* A call to `Encode` will require at least this many bytes on the current
* object to be successful.
*
* @return the number of bytes needed in a buffer to be able to Encode.
*/
uint16_t EncodeSizeBytes() const;
/**
* Decodes a header from the given buffer.
*
* @param data - the buffer to read from
* @param size - bytes available in the buffer
* @param decode_size - number of bytes read from the buffer to decode the
* object
*
* @return CHIP_NO_ERROR on success.
*
* Possible failures:
* CHIP_ERROR_INVALID_ARGUMENT on insufficient buffer size
* CHIP_ERROR_VERSION_MISMATCH if header version is not supported.
*/
CHIP_ERROR Decode(const uint8_t * data, uint16_t size, uint16_t * decode_size);
/**
* A version of Decode that uses the type system to determine available
* space.
*/
template <uint16_t N>
inline CHIP_ERROR Decode(const uint8_t (&data)[N], uint16_t * decode_size)
{
return Decode(data, N, decode_size);
}
/**
* A version of Decode that decodes from the start of a PacketBuffer and
* consumes the bytes we decoded from.
*/
CHIP_ERROR DecodeAndConsume(const System::PacketBufferHandle & buf);
/**
* Encodes a header into the given buffer.
*
* @param data - the buffer to write to
* @param size - space available in the buffer (in bytes)
* @param encode_size - number of bytes written to the buffer.
*
* @return CHIP_NO_ERROR on success.
*
* Possible failures:
* CHIP_ERROR_INVALID_ARGUMENT on insufficient buffer size
*/
CHIP_ERROR Encode(uint8_t * data, uint16_t size, uint16_t * encode_size) const;
/**
* A version of Encode that uses the type system to determine available
* space.
*/
template <int N>
inline CHIP_ERROR Encode(uint8_t (&data)[N], uint16_t * encode_size) const
{
return Encode(data, N, encode_size);
}
/**
* A version of Encode that encodes into a PacketBuffer before the
* PacketBuffer's current data.
*/
CHIP_ERROR EncodeBeforeData(const System::PacketBufferHandle & buf) const;
/**
* A version of Encode that encodes into a PacketBuffer at the start of the
* current data space. This assumes that someone has already preallocated
* space for the header.
*/
inline CHIP_ERROR EncodeAtStart(const System::PacketBufferHandle & buf, uint16_t * encode_size) const
{
return Encode(buf->Start(), buf->DataLength(), encode_size);
}
private:
/// Represents the current encode/decode header version
static constexpr int kHeaderVersion = 2;
/// Value expected to be incremented for each message sent.
uint32_t mMessageId = 0;
/// What node the message originated from
Optional<NodeId> mSourceNodeId;
/// Intended recipient of the message.
Optional<NodeId> mDestinationNodeId;
/// Encryption Key ID
uint16_t mEncryptionKeyID = 0;
/// Message flags read from the message.
Header::Flags mFlags;
/// Message flags not encoded into the packet sent over wire.
Header::InternalFlags mInternalFlags;
/// Represents encryption type used for encrypting current packet
Header::EncryptionType mEncryptionType = Header::EncryptionType::kAESCCMTagLen16;
};
/**
* Handles encoding/decoding of CHIP payload headers.
*
* Payload headers are **ENCRYPTED** and are placed at the start of
* an encrypted message payload.
*/
class PayloadHeader
{
public:
PayloadHeader & operator=(const PayloadHeader &) = default;
/**
* Gets the vendor id in the current message.
*
* NOTE: the vendor id is optional and may be missing.
*/
const Optional<uint16_t> & GetVendorId() const { return mVendorId; }
/** Get the Session ID from this header. */
uint16_t GetExchangeID() const { return mExchangeID; }
/** Get the Protocol ID from this header. */
uint16_t GetProtocolID() const { return mProtocolID; }
/** Get the secure msg type from this header. */
uint8_t GetMessageType() const { return mMessageType; }
/** Check whether the header has a given secure message type */
bool HasMessageType(uint8_t type) const { return mMessageType == type; }
template <typename MessageType, typename = std::enable_if_t<std::is_enum<MessageType>::value>>
bool HasMessageType(MessageType type) const
{
static_assert(std::is_same<std::underlying_type_t<MessageType>, uint8_t>::value, "Enum is wrong size; cast is not safe");
return mProtocolID == Protocols::MessageTypeTraits<MessageType>::ProtocolId && HasMessageType(static_cast<uint8_t>(type));
}
/**
* Gets the Acknowledged Message Counter from this header.
*
* NOTE: the Acknowledged Message Counter is optional and may be missing.
*/
const Optional<uint32_t> & GetAckId() const { return mAckId; }
/** Set the vendor id for this header. */
PayloadHeader & SetVendorId(uint16_t id)
{
mVendorId.SetValue(id);
mExchangeFlags.Set(Header::ExFlagValues::kExchangeFlag_VendorIdPresent);
return *this;
}
/** Set the vendor id for this header. */
PayloadHeader & SetVendorId(Optional<uint16_t> id)
{
mVendorId = id;
mExchangeFlags.Set(Header::ExFlagValues::kExchangeFlag_VendorIdPresent, id.HasValue());
return *this;
}
/** Clear the vendor id for this header. */
PayloadHeader & ClearVendorId()
{
mVendorId.ClearValue();
return *this;
}
/**
* Set the message type for this header. This requires setting the protocol
* id as well, because the meaning of a message type is only relevant given
* a specific protocol.
*
* This should only be used for cases when we don't have a strongly typed
* message type and hence can't automatically determine the protocol from
* the message type.
*/
PayloadHeader & SetMessageType(uint16_t protocol, uint8_t type)
{
mProtocolID = protocol;
mMessageType = type;
return *this;
}
/** Set the secure message type, with the protocol id derived from the
message type. */
template <typename MessageType, typename = std::enable_if_t<std::is_enum<MessageType>::value>>
PayloadHeader & SetMessageType(MessageType type)
{
static_assert(std::is_same<std::underlying_type_t<MessageType>, uint8_t>::value, "Enum is wrong size; cast is not safe");
mMessageType = static_cast<uint8_t>(type);
mProtocolID = Protocols::MessageTypeTraits<MessageType>::ProtocolId;
return *this;
}
/** Set the security session ID for this header. */
PayloadHeader & SetExchangeID(uint16_t id)
{
mExchangeID = id;
return *this;
}
/** Set the Initiator flag bit. */
PayloadHeader & SetInitiator(bool inInitiator)
{
mExchangeFlags.Set(Header::ExFlagValues::kExchangeFlag_Initiator, inInitiator);
return *this;
}
PayloadHeader & SetAckId(uint32_t id)
{
mAckId.SetValue(id);
mExchangeFlags.Set(Header::ExFlagValues::kExchangeFlag_AckMsg);
return *this;
}
/** Set the AckMsg flag bit. */
PayloadHeader & SetAckId(Optional<uint32_t> id)
{
mAckId = id;
mExchangeFlags.Set(Header::ExFlagValues::kExchangeFlag_AckMsg, id.HasValue());
return *this;
}
/** Set the NeedsAck flag bit. */
PayloadHeader & SetNeedsAck(bool inNeedsAck)
{
mExchangeFlags.Set(Header::ExFlagValues::kExchangeFlag_NeedsAck, inNeedsAck);
return *this;
}
/**
* Determine whether the initiator of the exchange.
*
* @return Returns 'true' if it is the initiator, else 'false'.
*
*/
bool IsInitiator() const { return mExchangeFlags.Has(Header::ExFlagValues::kExchangeFlag_Initiator); }
/**
* Determine whether the current message is an acknowledgment for a previously received message.
*
* @return Returns 'true' if current message is an acknowledgment, else 'false'.
*
*/
bool IsAckMsg() const { return mExchangeFlags.Has(Header::ExFlagValues::kExchangeFlag_AckMsg); }
/**
* Determine whether current message is requesting an acknowledgment from the recipient.
*
* @return Returns 'true' if the current message is requesting an acknowledgment from the recipient, else 'false'.
*
*/
bool NeedsAck() const { return mExchangeFlags.Has(Header::ExFlagValues::kExchangeFlag_NeedsAck); }
/**
* A call to `Encode` will require at least this many bytes on the current
* object to be successful.
*
* @return the number of bytes needed in a buffer to be able to Encode.
*/
uint16_t EncodeSizeBytes() const;
/**
* Decodes the encrypted header fields from the given buffer.
*
* @param data - the buffer to read from
* @param size - bytes available in the buffer
* @param decode_size - number of bytes read from the buffer to decode the
* object
*
* @return CHIP_NO_ERROR on success.
*
* Possible failures:
* CHIP_ERROR_INVALID_ARGUMENT on insufficient buffer size
* CHIP_ERROR_VERSION_MISMATCH if header version is not supported.
*/
CHIP_ERROR Decode(const uint8_t * data, uint16_t size, uint16_t * decode_size);
/**
* A version of Decode that uses the type system to determine available
* space.
*/
template <uint16_t N>
inline CHIP_ERROR Decode(const uint8_t (&data)[N], uint16_t * decode_size)
{
return Decode(data, N, decode_size);
}
/**
* A version of Decode that decodes from the start of a PacketBuffer and
* consumes the bytes we decoded from.
*/
CHIP_ERROR DecodeAndConsume(const System::PacketBufferHandle & buf);
/**
* Encodes the encrypted part of the header into the given buffer.
*
* @param data - the buffer to write to
* @param size - space available in the buffer (in bytes)
* @param encode_size - number of bytes written to the buffer.
*
* @return CHIP_NO_ERROR on success.
*
* Possible failures:
* CHIP_ERROR_INVALID_ARGUMENT on insufficient buffer size
*/
CHIP_ERROR Encode(uint8_t * data, uint16_t size, uint16_t * encode_size) const;
/**
* A version of Encode that uses the type system to determine available
* space.
*/
template <uint16_t N>
inline CHIP_ERROR Encode(uint8_t (&data)[N], uint16_t * decode_size) const
{
return Encode(data, N, decode_size);
}
/**
* A version of Encode that encodes into a PacketBuffer before the
* PacketBuffer's current data.
*/
CHIP_ERROR EncodeBeforeData(const System::PacketBufferHandle & buf) const;
/**
* A version of Encode that encodes into a PacketBuffer at the start of the
* current data space. This assumes that someone has already preallocated
* space for the header.
*/
inline CHIP_ERROR EncodeAtStart(const System::PacketBufferHandle & buf, uint16_t * encode_size) const
{
return Encode(buf->Start(), buf->DataLength(), encode_size);
}
private:
/// Packet type (application data, security control packets, e.g. pairing,
/// configuration, rekey etc)
uint8_t mMessageType = 0;
/// Security session identifier
uint16_t mExchangeID = 0;
/// Vendor identifier
Optional<uint16_t> mVendorId;
/// Protocol identifier
uint16_t mProtocolID = 0;
/// Bit flag indicators for CHIP Exchange header
Header::ExFlags mExchangeFlags;
/// Message counter of a previous message that is being acknowledged by the current message
Optional<uint32_t> mAckId;
};
/** Handles encoding/decoding of CHIP message headers */
class MessageAuthenticationCode
{
public:
const uint8_t * GetTag() const { return &mTag[0]; }
/** Set the message auth tag for this header. */
MessageAuthenticationCode & SetTag(PacketHeader * header, Header::EncryptionType encType, uint8_t * tag, size_t len)
{
const size_t tagLen = TagLenForEncryptionType(encType);
if (tagLen > 0 && tagLen <= kMaxTagLen && len == tagLen)
{
header->SetEncryptionType(encType);
memcpy(&mTag, tag, tagLen);
}
return *this;
}
/**
* Decodes the Message Authentication Tag from the given buffer.
*
* @param packetHeader - header containing encryption information
* @param data - the buffer to read from
* @param size - bytes available in the buffer
* @param decode_size - number of bytes read from the buffer to decode the
* object
*
* @return CHIP_NO_ERROR on success.
*
* Possible failures:
* CHIP_ERROR_INVALID_ARGUMENT on insufficient buffer size
* CHIP_ERROR_VERSION_MISMATCH if header version is not supported.
*/
CHIP_ERROR Decode(const PacketHeader & packetHeader, const uint8_t * data, uint16_t size, uint16_t * decode_size);
/**
* Encodes the Messae Authentication Tag into the given buffer.
*
* @param packetHeader - header containing encryption information
* @param data - the buffer to write to
* @param size - space available in the buffer (in bytes)
* @param encode_size - number of bytes written to the buffer.
*
* @return CHIP_NO_ERROR on success.
*
* Possible failures:
* CHIP_ERROR_INVALID_ARGUMENT on insufficient buffer size
*/
CHIP_ERROR Encode(const PacketHeader & packetHeader, uint8_t * data, uint16_t size, uint16_t * encode_size) const;
static uint16_t TagLenForEncryptionType(Header::EncryptionType encType);
private:
/// Message authentication tag generated at encryption of the message.
uint8_t mTag[kMaxTagLen];
};
} // namespace chip