blob: fc3e312715c4449eb050b36a23c280146ffb4657 [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 <core/CHIPError.h>
#include <support/BufferReader.h>
#include <support/CodeUtils.h>
/**********************************************
* Header format (little endian):
*
* -------- Unencrypted header -----------------------------------------------------
* 16 bit: | VERSION: 4 bit | FLAGS: 4 bit | ENCRYPTTYPE: 4 bit | RESERVED: 4 bit |
* 32 bit: | MESSAGE_ID |
* 64 bit: | SOURCE_NODE_ID (iff source node flag is set) |
* 64 bit: | DEST_NODE_ID (iff destination node flag is set) |
* 16 bit: | Encryption Key ID |
* 16 bit: | Payload Length |
* -------- Encrypted header -------------------------------------------------------
* 8 bit: | Exchange Header |
* 8 bit: | Message Type |
* 16 bit: | Exchange ID |
* 16 bit: | Optional Vendor ID |
* 16 bit: | Protocol 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 vendor id inside a header
constexpr size_t kVendorIdSizeBytes = 2;
/// size of a serialized ack id inside a header
constexpr size_t kAckIdSizeBytes = 4;
/// Mask to extract just the version part from a 16bit header prefix.
constexpr uint16_t kVersionMask = 0x00F0;
/// Shift to convert to/from a masked version 16bit value to a 4bit version.
constexpr int kVersionShift = 4;
/// Mask to extract just the encryption type part from a 16bit header prefix.
constexpr uint16_t kEncryptionTypeMask = 0x3000;
/// Shift to convert to/from a masked encryption type 16bit value to a 2bit encryption type.
constexpr int kEncryptionTypeShift = 12;
} // namespace
uint16_t PacketHeader::EncodeSizeBytes() const
{
size_t size = kFixedUnencryptedHeaderSizeBytes;
if (mSourceNodeId.HasValue())
{
size += kNodeIdSizeBytes;
}
if (mDestinationNodeId.HasValue())
{
size += kNodeIdSizeBytes;
}
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 (mAckId.HasValue())
{
size += kAckIdSizeBytes;
}
static_assert(kEncryptedHeaderSizeBytes + kVendorIdSizeBytes + kAckIdSizeBytes <= UINT16_MAX,
"Header size does not fit in uint16_t");
return static_cast<uint16_t>(size);
}
uint16_t MessageAuthenticationCode::TagLenForEncryptionType(Header::EncryptionType encType)
{
switch (encType)
{
case Header::EncryptionType::kAESCCMTagLen16:
return 16;
default:
return 0;
}
}
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;
uint16_t octets_read;
uint16_t header;
err = reader.Read16(&header).StatusCode();
SuccessOrExit(err);
version = ((header & kVersionMask) >> kVersionShift);
VerifyOrExit(version == kHeaderVersion, err = CHIP_ERROR_VERSION_MISMATCH);
mFlags.SetRaw(header);
mEncryptionType = static_cast<Header::EncryptionType>((header & kEncryptionTypeMask) >> kEncryptionTypeShift);
err = reader.Read32(&mMessageId).StatusCode();
SuccessOrExit(err);
if (mFlags.Has(Header::FlagValues::kSourceNodeIdPresent))
{
uint64_t sourceNodeId;
err = reader.Read64(&sourceNodeId).StatusCode();
SuccessOrExit(err);
mSourceNodeId.SetValue(sourceNodeId);
}
else
{
mSourceNodeId.ClearValue();
}
if (mFlags.Has(Header::FlagValues::kDestinationNodeIdPresent))
{
uint64_t destinationNodeId;
err = reader.Read64(&destinationNodeId).StatusCode();
SuccessOrExit(err);
mDestinationNodeId.SetValue(destinationNodeId);
}
else
{
mDestinationNodeId.ClearValue();
}
err = reader.Read16(&mEncryptionKeyID).StatusCode();
SuccessOrExit(err);
octets_read = reader.OctetsRead();
VerifyOrExit(octets_read == EncodeSizeBytes(), err = CHIP_ERROR_INTERNAL);
*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;
err = reader.Read8(&header).Read8(&mMessageType).Read16(&mExchangeID).StatusCode();
SuccessOrExit(err);
mExchangeFlags.SetRaw(header);
VendorId vendor_id;
if (HaveVendorId())
{
uint16_t vendor_id_raw;
err = reader.Read16(&vendor_id_raw).StatusCode();
SuccessOrExit(err);
vendor_id = static_cast<VendorId>(vendor_id_raw);
}
else
{
vendor_id = VendorId::Common;
}
uint16_t protocol_id;
err = reader.Read16(&protocol_id).StatusCode();
SuccessOrExit(err);
mProtocolID = Protocols::Id(vendor_id, protocol_id);
if (mExchangeFlags.Has(Header::ExFlagValues::kExchangeFlag_AckMsg))
{
uint32_t ack_id;
err = reader.Read32(&ack_id).StatusCode();
SuccessOrExit(err);
mAckId.SetValue(ack_id);
}
else
{
mAckId.ClearValue();
}
octets_read = reader.OctetsRead();
VerifyOrExit(octets_read == EncodeSizeBytes(), err = CHIP_ERROR_INTERNAL);
*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);
Header::Flags encodeFlags = mFlags;
encodeFlags.Set(Header::FlagValues::kSourceNodeIdPresent, mSourceNodeId.HasValue())
.Set(Header::FlagValues::kDestinationNodeIdPresent, mDestinationNodeId.HasValue());
uint16_t header = (kHeaderVersion << kVersionShift) | encodeFlags.Raw();
header |= (static_cast<uint16_t>(static_cast<uint16_t>(mEncryptionType) << kEncryptionTypeShift) & kEncryptionTypeMask);
uint8_t * p = data;
LittleEndian::Write16(p, header);
LittleEndian::Write32(p, mMessageId);
if (mSourceNodeId.HasValue())
{
LittleEndian::Write64(p, mSourceNodeId.Value());
}
if (mDestinationNodeId.HasValue())
{
LittleEndian::Write64(p, mDestinationNodeId.Value());
}
LittleEndian::Write16(p, mEncryptionKeyID);
// 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, static_cast<std::underlying_type_t<VendorId>>(mProtocolID.GetVendorId()));
}
LittleEndian::Write16(p, mProtocolID.GetProtocolId());
if (mAckId.HasValue())
{
LittleEndian::Write32(p, mAckId.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 = TagLenForEncryptionType(packetHeader.GetEncryptionType());
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 = TagLenForEncryptionType(packetHeader.GetEncryptionType());
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