blob: 1b8ef89680d75f98ed032411cc66cc8e2d0f5729 [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 contains the implementation the chip message header
* encode/decode classes.
*/
#include "MessageHeader.h"
#include <assert.h>
#include <limits.h>
#include <stdint.h>
#include <type_traits>
#include <lib/core/CHIPError.h>
#include <lib/support/BufferReader.h>
#include <lib/support/CodeUtils.h>
/**********************************************
* Header format (little endian):
*
* -------- Unencrypted header -----------------------------------------------------
* 8 bit: | Message Flags: VERSION: 4 bit | S: 1 bit | RESERVED: 1 bit | DSIZ: 2 bit |
* 8 bit: | Security Flags: P: 1 bit | C: 1 bit | MX: 1 bit | RESERVED: 3 bit | Session Type: 2 bit |
* 16 bit: | Session ID |
* 32 bit: | Message Counter |
* 64 bit: | SOURCE_NODE_ID (iff source node flag is set) |
* 64 bit: | DEST_NODE_ID (iff destination node flag is set) |
* -------- Encrypted header -------------------------------------------------------
* 8 bit: | Exchange Flags: RESERVED: 3 bit | V: 1 bit | SX: 1 bit | R: 1 bit | A: 1 bit | I: 1 bit |
* 8 bit: | Protocol Opcode |
* 16 bit: | Exchange ID |
* 16 bit: | Protocol ID |
* 16 bit: | Optional Vendor ID |
* 32 bit: | Acknowledged Message Counter (if A flag in the Header is set) |
* -------- Encrypted Application Data Start ---------------------------------------
* <var>: | Encrypted Data |
* -------- Encrypted Application Data End -----------------------------------------
* <var>: | (Unencrypted) Message Authentication Tag |
*
**********************************************/
namespace chip {
namespace {
using namespace chip::Encoding;
/// size of the fixed portion of the header
constexpr size_t kFixedUnencryptedHeaderSizeBytes = 8;
/// size of the encrypted portion of the header
constexpr size_t kEncryptedHeaderSizeBytes = 6;
/// size of a serialized node id inside a header
constexpr size_t kNodeIdSizeBytes = 8;
/// size of a serialized group id inside a header
constexpr size_t kGroupIdSizeBytes = 2;
/// size of a serialized vendor id inside a header
constexpr size_t kVendorIdSizeBytes = 2;
/// size of a serialized ack message counter inside a header
constexpr size_t kAckMessageCounterSizeBytes = 4;
/// Mask to extract just the version part from a 8bits header prefix.
constexpr uint8_t kVersionMask = 0xF0;
constexpr uint8_t kMsgFlagsMask = 0x07;
/// Shift to convert to/from a masked version 8bit value to a 4bit version.
constexpr int kVersionShift = 4;
} // namespace
uint16_t PacketHeader::EncodeSizeBytes() const
{
size_t size = kFixedUnencryptedHeaderSizeBytes;
if (mSourceNodeId.HasValue())
{
size += kNodeIdSizeBytes;
}
if (mDestinationNodeId.HasValue())
{
size += kNodeIdSizeBytes;
}
else if (mDestinationGroupId.HasValue())
{
size += kGroupIdSizeBytes;
}
static_assert(kFixedUnencryptedHeaderSizeBytes + kNodeIdSizeBytes + kNodeIdSizeBytes <= UINT16_MAX,
"Header size does not fit in uint16_t");
return static_cast<uint16_t>(size);
}
uint16_t PayloadHeader::EncodeSizeBytes() const
{
size_t size = kEncryptedHeaderSizeBytes;
if (HaveVendorId())
{
size += kVendorIdSizeBytes;
}
if (mAckMessageCounter.HasValue())
{
size += kAckMessageCounterSizeBytes;
}
static_assert(kEncryptedHeaderSizeBytes + kVendorIdSizeBytes + kAckMessageCounterSizeBytes <= UINT16_MAX,
"Header size does not fit in uint16_t");
return static_cast<uint16_t>(size);
}
CHIP_ERROR PacketHeader::Decode(const uint8_t * const data, uint16_t size, uint16_t * decode_len)
{
CHIP_ERROR err = CHIP_NO_ERROR;
LittleEndian::Reader reader(data, size);
int version;
// TODO: De-uint16-ify everything related to this library
uint16_t octets_read;
uint8_t msgFlags;
SuccessOrExit(err = reader.Read8(&msgFlags).StatusCode());
version = ((msgFlags & kVersionMask) >> kVersionShift);
VerifyOrExit(version == kMsgHeaderVersion, err = CHIP_ERROR_VERSION_MISMATCH);
SetMessageFlags(msgFlags);
SuccessOrExit(err = reader.Read16(&mSessionId).StatusCode());
uint8_t securityFlags;
SuccessOrExit(err = reader.Read8(&securityFlags).StatusCode());
SetSecurityFlags(securityFlags);
SuccessOrExit(err = reader.Read32(&mMessageCounter).StatusCode());
if (mMsgFlags.Has(Header::MsgFlagValues::kSourceNodeIdPresent))
{
uint64_t sourceNodeId;
SuccessOrExit(err = reader.Read64(&sourceNodeId).StatusCode());
mSourceNodeId.SetValue(sourceNodeId);
}
else
{
mSourceNodeId.ClearValue();
}
if (!IsSessionTypeValid())
{
// Reserved.
SuccessOrExit(err = CHIP_ERROR_INTERNAL);
}
if (mMsgFlags.HasAll(Header::MsgFlagValues::kDestinationNodeIdPresent, Header::MsgFlagValues::kDestinationGroupIdPresent))
{
// Reserved.
SuccessOrExit(err = CHIP_ERROR_INTERNAL);
}
else if (mMsgFlags.Has(Header::MsgFlagValues::kDestinationNodeIdPresent))
{
// No need to check if session is Unicast because for MCSP
// a destination node ID is present with a group session ID.
// Spec 4.9.2.4
uint64_t destinationNodeId;
SuccessOrExit(err = reader.Read64(&destinationNodeId).StatusCode());
mDestinationNodeId.SetValue(destinationNodeId);
mDestinationGroupId.ClearValue();
}
else if (mMsgFlags.Has(Header::MsgFlagValues::kDestinationGroupIdPresent))
{
if (mSessionType != Header::SessionType::kGroupSession)
{
SuccessOrExit(err = CHIP_ERROR_INTERNAL);
}
uint16_t destinationGroupId;
SuccessOrExit(err = reader.Read16(&destinationGroupId).StatusCode());
mDestinationGroupId.SetValue(destinationGroupId);
mDestinationNodeId.ClearValue();
}
else
{
mDestinationNodeId.ClearValue();
mDestinationGroupId.ClearValue();
}
if (mSecFlags.Has(Header::SecFlagValues::kMsgExtensionFlag))
{
// If present, skip over Message Extension block.
// Spec 4.4.1.8. Message Extensions (variable)
uint16_t mxLength;
SuccessOrExit(err = reader.Read16(&mxLength).StatusCode());
VerifyOrExit(mxLength <= reader.Remaining(), err = CHIP_ERROR_INTERNAL);
reader.Skip(mxLength);
}
octets_read = static_cast<uint16_t>(reader.OctetsRead());
*decode_len = octets_read;
exit:
return err;
}
CHIP_ERROR PacketHeader::DecodeAndConsume(const System::PacketBufferHandle & buf)
{
uint16_t headerSize = 0;
ReturnErrorOnFailure(Decode(buf->Start(), buf->DataLength(), &headerSize));
buf->ConsumeHead(headerSize);
return CHIP_NO_ERROR;
}
CHIP_ERROR PayloadHeader::Decode(const uint8_t * const data, uint16_t size, uint16_t * decode_len)
{
CHIP_ERROR err = CHIP_NO_ERROR;
LittleEndian::Reader reader(data, size);
uint8_t header;
uint16_t octets_read;
SuccessOrExit(err = reader.Read8(&header).Read8(&mMessageType).Read16(&mExchangeID).StatusCode());
mExchangeFlags.SetRaw(header);
VendorId vendor_id;
if (HaveVendorId())
{
uint16_t vendor_id_raw;
SuccessOrExit(err = reader.Read16(&vendor_id_raw).StatusCode());
vendor_id = static_cast<VendorId>(vendor_id_raw);
}
else
{
vendor_id = VendorId::Common;
}
uint16_t protocol_id;
SuccessOrExit(err = reader.Read16(&protocol_id).StatusCode());
mProtocolID = Protocols::Id(vendor_id, protocol_id);
if (mExchangeFlags.Has(Header::ExFlagValues::kExchangeFlag_AckMsg))
{
uint32_t ack_message_counter;
SuccessOrExit(err = reader.Read32(&ack_message_counter).StatusCode());
mAckMessageCounter.SetValue(ack_message_counter);
}
else
{
mAckMessageCounter.ClearValue();
}
if (mExchangeFlags.Has(Header::ExFlagValues::kExchangeFlag_SecuredExtension))
{
// If present, skip over Secured Extension block.
// Spec 4.4.3.7. Secured Extensions (variable)
uint16_t sxLength;
SuccessOrExit(err = reader.Read16(&sxLength).StatusCode());
VerifyOrExit(sxLength <= reader.Remaining(), err = CHIP_ERROR_INTERNAL);
reader.Skip(sxLength);
}
octets_read = static_cast<uint16_t>(reader.OctetsRead());
*decode_len = octets_read;
exit:
return err;
}
CHIP_ERROR PayloadHeader::DecodeAndConsume(const System::PacketBufferHandle & buf)
{
uint16_t headerSize = 0;
ReturnErrorOnFailure(Decode(buf->Start(), buf->DataLength(), &headerSize));
buf->ConsumeHead(headerSize);
return CHIP_NO_ERROR;
}
CHIP_ERROR PacketHeader::Encode(uint8_t * data, uint16_t size, uint16_t * encode_size) const
{
VerifyOrReturnError(size >= EncodeSizeBytes(), CHIP_ERROR_INVALID_ARGUMENT);
VerifyOrReturnError(!(mDestinationNodeId.HasValue() && mDestinationGroupId.HasValue()), CHIP_ERROR_INTERNAL);
VerifyOrReturnError(encode_size != nullptr, CHIP_ERROR_INTERNAL);
VerifyOrReturnError(IsSessionTypeValid(), CHIP_ERROR_INTERNAL);
Header::MsgFlags messageFlags = mMsgFlags;
messageFlags.Set(Header::MsgFlagValues::kSourceNodeIdPresent, mSourceNodeId.HasValue())
.Set(Header::MsgFlagValues::kDestinationNodeIdPresent, mDestinationNodeId.HasValue())
.Set(Header::MsgFlagValues::kDestinationGroupIdPresent, mDestinationGroupId.HasValue());
uint8_t msgFlags = (kMsgHeaderVersion << kVersionShift) | (messageFlags.Raw() & kMsgFlagsMask);
uint8_t * p = data;
Write8(p, msgFlags);
LittleEndian::Write16(p, mSessionId);
Write8(p, mSecFlags.Raw());
LittleEndian::Write32(p, mMessageCounter);
if (mSourceNodeId.HasValue())
{
LittleEndian::Write64(p, mSourceNodeId.Value());
}
if (mDestinationNodeId.HasValue())
{
LittleEndian::Write64(p, mDestinationNodeId.Value());
}
else if (mDestinationGroupId.HasValue())
{
LittleEndian::Write16(p, mDestinationGroupId.Value());
}
// Written data size provided to caller on success
VerifyOrReturnError(p - data == EncodeSizeBytes(), CHIP_ERROR_INTERNAL);
*encode_size = static_cast<uint16_t>(p - data);
return CHIP_NO_ERROR;
}
CHIP_ERROR PacketHeader::EncodeBeforeData(const System::PacketBufferHandle & buf) const
{
// Note: PayloadHeader::EncodeBeforeData probably needs changes if you
// change anything here.
uint16_t headerSize = EncodeSizeBytes();
VerifyOrReturnError(buf->EnsureReservedSize(headerSize), CHIP_ERROR_NO_MEMORY);
buf->SetStart(buf->Start() - headerSize);
uint16_t actualEncodedHeaderSize;
ReturnErrorOnFailure(EncodeAtStart(buf, &actualEncodedHeaderSize));
VerifyOrReturnError(actualEncodedHeaderSize == headerSize, CHIP_ERROR_INTERNAL);
return CHIP_NO_ERROR;
}
CHIP_ERROR PayloadHeader::Encode(uint8_t * data, uint16_t size, uint16_t * encode_size) const
{
VerifyOrReturnError(size >= EncodeSizeBytes(), CHIP_ERROR_INVALID_ARGUMENT);
uint8_t * p = data;
const uint8_t header = mExchangeFlags.Raw();
Write8(p, header);
Write8(p, mMessageType);
LittleEndian::Write16(p, mExchangeID);
if (HaveVendorId())
{
LittleEndian::Write16(p, to_underlying(mProtocolID.GetVendorId()));
}
LittleEndian::Write16(p, mProtocolID.GetProtocolId());
if (mAckMessageCounter.HasValue())
{
LittleEndian::Write32(p, mAckMessageCounter.Value());
}
// Written data size provided to caller on success
VerifyOrReturnError(p - data == EncodeSizeBytes(), CHIP_ERROR_INTERNAL);
*encode_size = static_cast<uint16_t>(p - data);
return CHIP_NO_ERROR;
}
CHIP_ERROR PayloadHeader::EncodeBeforeData(const System::PacketBufferHandle & buf) const
{
// Note: PacketHeader::EncodeBeforeData probably needs changes if you change
// anything here.
uint16_t headerSize = EncodeSizeBytes();
VerifyOrReturnError(buf->EnsureReservedSize(headerSize), CHIP_ERROR_NO_MEMORY);
buf->SetStart(buf->Start() - headerSize);
uint16_t actualEncodedHeaderSize;
ReturnErrorOnFailure(EncodeAtStart(buf, &actualEncodedHeaderSize));
VerifyOrReturnError(actualEncodedHeaderSize == headerSize, CHIP_ERROR_INTERNAL);
return CHIP_NO_ERROR;
}
CHIP_ERROR MessageAuthenticationCode::Decode(const PacketHeader & packetHeader, const uint8_t * const data, uint16_t size,
uint16_t * decode_len)
{
const uint16_t taglen = packetHeader.MICTagLength();
VerifyOrReturnError(taglen != 0, CHIP_ERROR_WRONG_ENCRYPTION_TYPE_FROM_PEER);
VerifyOrReturnError(size >= taglen, CHIP_ERROR_INVALID_ARGUMENT);
memcpy(&mTag[0], data, taglen);
*decode_len = taglen;
return CHIP_NO_ERROR;
}
CHIP_ERROR MessageAuthenticationCode::Encode(const PacketHeader & packetHeader, uint8_t * data, uint16_t size,
uint16_t * encode_size) const
{
uint8_t * p = data;
const uint16_t taglen = packetHeader.MICTagLength();
VerifyOrReturnError(taglen != 0, CHIP_ERROR_WRONG_ENCRYPTION_TYPE);
VerifyOrReturnError(size >= taglen, CHIP_ERROR_INVALID_ARGUMENT);
memcpy(p, &mTag[0], taglen);
// Written data size provided to caller on success
*encode_size = taglen;
return CHIP_NO_ERROR;
}
} // namespace chip