blob: 77c8976cba935069bd4819def7707b69474d8957 [file] [log] [blame]
/*
*
* Copyright (c) 2020 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.
*/
#ifndef MESSAGEHEADER_H_
#define MESSAGEHEADER_H_
#include <cstddef>
#include <cstdint>
#include <string.h>
#include <core/CHIPError.h>
#include <core/Optional.h>
#include <support/BitFlags.h>
namespace chip {
/// Convenience type to make it clear a number represents a node id.
typedef uint64_t NodeId;
constexpr NodeId kUndefinedNodeId = 0xFFFFFFFFFFFFFFFFll;
constexpr size_t kMaxTagLen = 16;
typedef int PacketHeaderFlags;
namespace Header {
enum class EncryptionType
{
kAESCCMTagLen8 = 0,
kAESCCMTagLen12 = 1,
kAESCCMTagLen16 = 2,
};
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 a source vendor id is included in the header.
kVendorIdPresent = 0x0400,
/// Header flag specifying that it is a control message for secure session.
kSecureSessionControlMessage = 0x0800,
};
using Flags = BitFlags<uint16_t, FlagValues>;
// Header is a 16-bit value of the form
// | 4 bit | 4 bit | 4 bit | 4 bit |
// +---------+-------+---------+----------|
// | version | Flags | encType | reserved |
static constexpr uint16_t kFlagsMask = 0x0F00;
} // 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; }
/** Get the length of encrypted payload. */
uint16_t GetPayloadLength() const { return mPayloadLength; }
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); }
Header::EncryptionType GetEncryptionType() const { return mEncryptionType; }
PacketHeader & SetSecureSessionControlMsg(bool value)
{
mFlags.Set(Header::FlagValues::kSecureSessionControlMessage, 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;
}
/** Set the secure payload length for this header. */
PacketHeader & SetPayloadLength(uint16_t len)
{
mPayloadLength = len;
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, size_t size, uint16_t * decode_size);
/**
* 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.
* @param payloadFlags extra flags for packet header encoding
*
* @return CHIP_NO_ERROR on success.
*
* Possible failures:
* CHIP_ERROR_INVALID_ARGUMENT on insufficient buffer size
*/
CHIP_ERROR Encode(uint8_t * data, size_t size, uint16_t * encode_size, Header::Flags payloadFlags) const;
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;
/// Length of application data (payload)
uint16_t mPayloadLength = 0;
/// Encryption Key ID
uint16_t mEncryptionKeyID = 0;
/// Message flags read from the message.
Header::Flags mFlags;
/// 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:
/**
* 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; }
/** Set the vendor id for this header. */
PayloadHeader & SetVendorId(uint16_t id)
{
mVendorId.SetValue(id);
return *this;
}
/** Set the vendor id for this header. */
PayloadHeader & SetVendorId(Optional<uint16_t> id)
{
mVendorId = id;
return *this;
}
/** Clear the vendor id for this header. */
PayloadHeader & ClearVendorId()
{
mVendorId.ClearValue();
return *this;
}
/** Set the secure message type for this header. */
PayloadHeader & SetMessageType(uint8_t type)
{
mMessageType = type;
return *this;
}
/** Set the security session ID for this header. */
PayloadHeader & SetExchangeID(uint16_t id)
{
mExchangeID = id;
return *this;
}
/** Set the Protocol ID for this header. */
PayloadHeader & SetProtocolID(uint16_t id)
{
mProtocolID = id;
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 the encrypted header fields from the given buffer.
*
* @param flags - decode flags
* @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(Header::Flags flags, const uint8_t * data, size_t size, uint16_t * decode_size);
/**
* 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, size_t size, uint16_t * encode_size) const;
/** Flags required for encoding this payload. */
Header::Flags GetEncodePacketFlags() const;
private:
/// Header structure for exchange information
uint8_t mExchangeHeader = 0;
/// 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;
};
/** 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, size_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, size_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
#endif // MESSAGEHEADER_H_