From a04bf546e4fbaebbd21e6d1070ba0db77af219ea Mon Sep 17 00:00:00 2001 From: Tennessee Carmel-Veilleux Date: Wed, 19 Jan 2022 07:52:11 -0500 Subject: [PATCH] Add message receive and send tracing to chip-tool (#13637) * Add message receive and send tracing to chip-tool - Move tracing from SecureMessageCodec to SessionManager to allow covering both received and sent messages - Add received message tracing - Update trace handling to be flexible to receiving a context pointer. - Refactor trace handling logic in common/tracing to be more flexible to - Add bookend FinishEvent to the TraceStream interface to assist in event flushing. Testing done: - Integration tests pass, unit tests all pass - Validated manually through several runs that with chip-tool (using `--trace_log 1`) that dumped received/sent message traces match the outer higher-level (but less detailed) logs around them * Restyled by clang-format * Address review comments Co-authored-by: Restyled.io --- examples/common/tracing/TraceHandlers.cpp | 234 ++++++++++++++++-- examples/common/tracing/TraceHandlers.h | 36 ++- .../public/pw_trace_chip/trace_chip.h | 6 +- .../platform/linux/pw_trace_chip/trace.cc | 19 +- src/transport/SecureMessageCodec.cpp | 3 - src/transport/SessionManager.cpp | 19 +- src/transport/TraceMessage.h | 49 +++- src/transport/raw/MessageHeader.h | 3 + 8 files changed, 318 insertions(+), 51 deletions(-) diff --git a/examples/common/tracing/TraceHandlers.cpp b/examples/common/tracing/TraceHandlers.cpp index 5842b3dc87305a..fb131dd6d46fab 100644 --- a/examples/common/tracing/TraceHandlers.cpp +++ b/examples/common/tracing/TraceHandlers.cpp @@ -24,8 +24,13 @@ #include "pw_trace/trace.h" #include "pw_trace_chip/trace_chip.h" #include "transport/TraceMessage.h" +#include +#include #include +// For `s` std::string literal suffix +using namespace std::string_literals; + namespace chip { namespace trace { @@ -50,21 +55,30 @@ class TraceOutput void DeleteStream() { SetStream(nullptr); } - void Stream(const std::string & tag, const std::string & data) + void StartEvent(const std::string & label) + { + std::lock_guard guard(mLock); + if (mStream) + { + mStream->StartEvent(label); + } + } + + void AddField(const std::string & tag, const std::string & data) { std::lock_guard guard(mLock); if (mStream) { - mStream->Stream(tag, data); + mStream->AddField(tag, data); } } - void Handler(const std::string & label) + void FinishEvent() { std::lock_guard guard(mLock); if (mStream) { - mStream->Handler(label); + mStream->FinishEvent(); } } @@ -73,42 +87,228 @@ class TraceOutput TraceStream * mStream = nullptr; }; -TraceOutput output; +struct TraceHandlerContext +{ + TraceOutput * sink; +}; + +TraceOutput gTraceOutput; +TraceHandlerContext gTraceHandlerContext = { .sink = &gTraceOutput }; -// TODO: Framework this into a registry of handlers for different message types. -bool TraceDefaultHandler(const TraceEventFields & trace) +std::string AsJsonKey(const std::string & key, const std::string & value, bool prepend_comma) +{ + return (prepend_comma ? ", "s : ""s) + "\""s + key + "\": " + value; +} + +std::string AsFirstJsonKey(const std::string & key, const std::string & value) +{ + return AsJsonKey(key, value, /* prepend_comma = */ false); +} + +std::string AsNextJsonKey(const std::string & key, const std::string & value) +{ + return AsJsonKey(key, value, /* prepend_comma = */ true); +} + +std::string AsJsonString(const std::string & value) +{ + return "\""s + value + "\""s; +} + +std::string AsJsonString(const Transport::PeerAddress * peerAddress) +{ + char convBuf[Transport::PeerAddress::kMaxToStringSize] = { 0 }; + + peerAddress->ToString(convBuf); + return AsJsonString(convBuf); +} + +std::string AsJsonBool(bool isTrue) +{ + return isTrue ? "true"s : "false"s; +} + +std::string AsJsonHexString(const uint8_t * buf, size_t bufLen) +{ + // Fill a string long enough for the hex conversion, that will be overwritten + std::string hexBuf(2 * bufLen, '0'); + + CHIP_ERROR status = Encoding::BytesToLowercaseHexBuffer(buf, bufLen, hexBuf.data(), hexBuf.size()); + + // Static conditions exist that should ensure never failing. Catch failure in an assert. + VerifyOrDie(status == CHIP_NO_ERROR); + + return AsJsonString(hexBuf); +} + +std::string PacketHeaderToJson(const PacketHeader * packetHeader) +{ + std::string jsonBody; + + uint32_t messageCounter = packetHeader->GetMessageCounter(); + jsonBody += AsFirstJsonKey("msg_counter", std::to_string(messageCounter)); + + const Optional & sourceNodeId = packetHeader->GetSourceNodeId(); + if (sourceNodeId.HasValue()) + { + jsonBody += AsNextJsonKey("source_node_id", std::to_string(sourceNodeId.Value())); + } + + uint16_t sessionId = packetHeader->GetSessionId(); + const Optional & groupId = packetHeader->GetDestinationGroupId(); + const Optional & destNodeId = packetHeader->GetDestinationNodeId(); + if (packetHeader->IsValidGroupMsg()) + { + if (groupId.HasValue()) + { + jsonBody += AsNextJsonKey("group_id", std::to_string(groupId.Value())); + } + + jsonBody += AsNextJsonKey("group_key_hash", std::to_string(sessionId)); + } + else if (destNodeId.HasValue()) + { + jsonBody += AsNextJsonKey("dest_node_id", std::to_string(destNodeId.Value())); + } + + jsonBody += AsNextJsonKey("session_id", std::to_string(sessionId)); + + uint8_t messageFlags = packetHeader->GetMessageFlags(); + jsonBody += AsNextJsonKey("msg_flags", std::to_string(messageFlags)); + + uint8_t securityFlags = packetHeader->GetSecurityFlags(); + jsonBody += AsNextJsonKey("security_flags", std::to_string(securityFlags)); + + return jsonBody; +} + +std::string PayloadHeaderToJson(const PayloadHeader * payloadHeader) +{ + + std::string jsonBody; + + uint8_t exchangeFlags = payloadHeader->GetExhangeFlags(); + jsonBody += AsFirstJsonKey("exchange_flags", std::to_string(exchangeFlags)); + + uint16_t exchangeId = payloadHeader->GetExchangeID(); + jsonBody += AsNextJsonKey("exchange_id", std::to_string(exchangeId)); + + uint32_t protocolId = payloadHeader->GetProtocolID().ToFullyQualifiedSpecForm(); + jsonBody += AsNextJsonKey("protocol_id", std::to_string(protocolId)); + + uint8_t protocolOpcode = payloadHeader->GetMessageType(); + jsonBody += AsNextJsonKey("protocol_opcode", std::to_string(protocolOpcode)); + + bool isInitiator = payloadHeader->IsInitiator(); + jsonBody += AsNextJsonKey("is_initiator", AsJsonBool(isInitiator)); + + bool needsAck = payloadHeader->NeedsAck(); + jsonBody += AsNextJsonKey("is_ack_requested", AsJsonBool(needsAck)); + + const Optional & acknowledgedMessageCounter = payloadHeader->GetAckMessageCounter(); + if (acknowledgedMessageCounter.HasValue()) + { + jsonBody += AsNextJsonKey("acknowledged_msg_counter", std::to_string(acknowledgedMessageCounter.Value())); + } + + return jsonBody; +} + +bool SecureMessageSentHandler(const TraceEventFields & eventFields, TraceHandlerContext * context) +{ + if (eventFields.dataSize != sizeof(TraceSecureMessageSentData)) + { + return false; + } + + const auto * eventData = reinterpret_cast(eventFields.dataBuffer); + std::string jsonBody = "{"; + jsonBody += PacketHeaderToJson(eventData->packetHeader); + jsonBody += ", "; + jsonBody += PayloadHeaderToJson(eventData->payloadHeader); + jsonBody += ", "; + jsonBody += AsFirstJsonKey("payload_size", std::to_string(eventData->packetSize)); + jsonBody += ", "; + jsonBody += AsFirstJsonKey("payload_hex", AsJsonHexString(eventData->packetPayload, eventData->packetSize)); + jsonBody += "}"; + + TraceOutput * sink = context->sink; + sink->StartEvent(std::string{ kTraceMessageEvent } + "." + kTraceMessageSentDataFormat); + sink->AddField("json", jsonBody); + sink->FinishEvent(); + + return true; +} + +bool SecureMessageReceivedHandler(const TraceEventFields & eventFields, TraceHandlerContext * context) { - if (strcmp(trace.dataFormat, kTraceSecureMessageDataFormat) != 0 || trace.dataSize != sizeof(TraceSecureMessageData)) + if (eventFields.dataSize != sizeof(TraceSecureMessageReceivedData)) { return false; } - const TraceSecureMessageData * msg = reinterpret_cast(trace.dataBuffer); - output.Handler("Default"); - output.Stream("ExchangeId", std::to_string(msg->payloadHeader->GetExchangeID())); - output.Stream("MsgType", std::to_string(msg->payloadHeader->GetMessageType())); - output.Stream("MessageCounter", std::to_string(msg->packetHeader->GetMessageCounter())); - output.Stream("PacketSize", std::to_string(msg->packetSize)); + const auto * eventData = reinterpret_cast(eventFields.dataBuffer); + + std::string jsonBody = "{"; + jsonBody += AsFirstJsonKey("peer_address", AsJsonString(eventData->peerAddress)); + jsonBody += ", "; + jsonBody += PacketHeaderToJson(eventData->packetHeader); + jsonBody += ", "; + jsonBody += PayloadHeaderToJson(eventData->payloadHeader); + jsonBody += ", "; + jsonBody += AsFirstJsonKey("payload_size", std::to_string(eventData->packetSize)); + jsonBody += ", "; + jsonBody += AsFirstJsonKey("payload_hex", AsJsonHexString(eventData->packetPayload, eventData->packetSize)); + jsonBody += "}"; + + TraceOutput * sink = context->sink; + sink->StartEvent(std::string{ kTraceMessageEvent } + "." + kTraceMessageReceivedDataFormat); + sink->AddField("json", jsonBody); + sink->FinishEvent(); + + // Note that `eventData->session` is currently ignored. return true; } +// TODO: Framework this into a registry of handlers for different message types. +bool TraceDefaultHandler(const TraceEventFields & eventFields, void * context) +{ + TraceHandlerContext * ourContext = reinterpret_cast(context); + if (ourContext == nullptr) + { + return false; + } + + if (std::string{ eventFields.dataFormat } == kTraceMessageSentDataFormat) + { + return SecureMessageSentHandler(eventFields, ourContext); + } + else if (std::string{ eventFields.dataFormat } == kTraceMessageReceivedDataFormat) + { + return SecureMessageReceivedHandler(eventFields, ourContext); + } + + return false; +} + } // namespace void SetTraceStream(TraceStream * stream) { - output.SetStream(stream); + gTraceOutput.SetStream(stream); } void InitTrace() { - RegisterTraceHandler(TraceDefaultHandler); + void * context = &gTraceHandlerContext; + RegisterTraceHandler(TraceDefaultHandler, context); } void DeInitTrace() { UnregisterAllTraceHandlers(); - output.DeleteStream(); + gTraceOutput.DeleteStream(); } } // namespace trace diff --git a/examples/common/tracing/TraceHandlers.h b/examples/common/tracing/TraceHandlers.h index 434f253b62a014..fb60e3e4db316d 100644 --- a/examples/common/tracing/TraceHandlers.h +++ b/examples/common/tracing/TraceHandlers.h @@ -28,19 +28,32 @@ namespace trace { class TraceStream { public: - virtual ~TraceStream() = default; - virtual void Stream(const std::string & tag, const std::string & data) = 0; - virtual void Handler(const std::string & label) = 0; + virtual ~TraceStream() = default; + virtual void StartEvent(const std::string & label) = 0; + virtual void AddField(const std::string & tag, const std::string & data) = 0; + virtual void FinishEvent() = 0; }; class TraceStreamLog : public TraceStream { public: - void Stream(const std::string & tag, const std::string & data) override + void StartEvent(const std::string & label) override { - ChipLogAutomation(" %s: %s", tag.data(), data.data()); + mStreamLine = ">>>" + label + "<<<\t"; + mIsFirstField = true; } - void Handler(const std::string & label) override { ChipLogAutomation("%s", label.data()); } + + void AddField(const std::string & tag, const std::string & data) override + { + mStreamLine += (mIsFirstField ? "" : "\t") + tag + "|" + data; + mIsFirstField = false; + } + + void FinishEvent() override { ChipLogAutomation("TraceStream:%s", mStreamLine.c_str()); } + +protected: + std::string mStreamLine; + bool mIsFirstField = true; }; class TraceStreamFile : public TraceStream @@ -49,25 +62,28 @@ class TraceStreamFile : public TraceStream TraceStreamFile(const char * fileName) { mFile.open(fileName); } ~TraceStreamFile() { mFile.close(); } - void Stream(const std::string & tag, const std::string & data) override + void AddField(const std::string & tag, const std::string & data) override { if (mFile.is_open()) { - mFile << " " << tag.data() << ": " << data.data() << "\n"; + mFile << " " << tag.data() << "\t" << data.data() << "\n"; mFile.flush(); } } - void Handler(const std::string & label) override + + void StartEvent(const std::string & label) override { if (mFile.is_open()) { struct timeval tv; gettimeofday(&tv, nullptr); - mFile << label << " [" << tv.tv_sec << "." << tv.tv_usec << "]\n"; + mFile << "\f[" << tv.tv_sec << "." << tv.tv_usec << "]\t" << label << "\n"; mFile.flush(); } } + void FinishEvent() override {} + private: std::ofstream mFile; }; diff --git a/examples/platform/linux/pw_trace_chip/public/pw_trace_chip/trace_chip.h b/examples/platform/linux/pw_trace_chip/public/pw_trace_chip/trace_chip.h index 3eca6335360c6f..c65899c29b0e0e 100644 --- a/examples/platform/linux/pw_trace_chip/public/pw_trace_chip/trace_chip.h +++ b/examples/platform/linux/pw_trace_chip/public/pw_trace_chip/trace_chip.h @@ -69,10 +69,10 @@ struct TraceEventFields size_t dataSize; }; -typedef bool (*TraceHandlerCallback)(const TraceEventFields & trace); +typedef bool (*TraceHandlerCallback)(const TraceEventFields & eventFields, void * context); -// Register a trace handler which gets called for all data trace events. -void RegisterTraceHandler(TraceHandlerCallback callback); +// Register an additional trace handler which gets called for all data trace events with the given context +void RegisterTraceHandler(TraceHandlerCallback callback, void * context); void UnregisterAllTraceHandlers(); // These are what the facade actually calls. diff --git a/examples/platform/linux/pw_trace_chip/trace.cc b/examples/platform/linux/pw_trace_chip/trace.cc index 427f22d3f5e80d..bf005c518933fd 100644 --- a/examples/platform/linux/pw_trace_chip/trace.cc +++ b/examples/platform/linux/pw_trace_chip/trace.cc @@ -25,31 +25,40 @@ namespace chip { namespace trace { + namespace { +struct TraceHandlerEntry +{ + // Handler callback that will receive the event + TraceHandlerCallback callback; + // Registration-time context provided for the given handler + void * context; +}; + std::mutex handlerLock; -std::vector traceHandlers; +std::vector traceHandlers; } // namespace void TraceEvent(const char * module, const char * label, TraceEventType eventType, const char * group, uint32_t traceId, uint8_t flags, const char * dataFormat, const void * dataBuffer, size_t dataSize) { - TraceEventFields fields = { module, label, eventType, group, traceId, flags, dataFormat, dataBuffer, dataSize }; + TraceEventFields eventFields = { module, label, eventType, group, traceId, flags, dataFormat, dataBuffer, dataSize }; std::lock_guard guard(handlerLock); for (const auto & handler : traceHandlers) { - if (handler(fields)) + if (handler.callback(eventFields, handler.context)) { return; } } } -void RegisterTraceHandler(TraceHandlerCallback callback) +void RegisterTraceHandler(TraceHandlerCallback callback, void * context) { std::lock_guard guard(handlerLock); - traceHandlers.push_back(callback); + traceHandlers.push_back(TraceHandlerEntry{ callback, context }); } void UnregisterAllTraceHandlers() diff --git a/src/transport/SecureMessageCodec.cpp b/src/transport/SecureMessageCodec.cpp index 3fb6775311fd3d..5ae7a56d3eee19 100644 --- a/src/transport/SecureMessageCodec.cpp +++ b/src/transport/SecureMessageCodec.cpp @@ -29,8 +29,6 @@ #include #include -#include "transport/TraceMessage.h" - namespace chip { using System::PacketBuffer; @@ -53,7 +51,6 @@ CHIP_ERROR Encrypt(Transport::SecureSession * session, PayloadHeader & payloadHe uint8_t * data = msgBuf->Start(); uint16_t totalLen = msgBuf->TotalLength(); - CHIP_TRACE_MESSAGE(payloadHeader, packetHeader, data, totalLen); MessageAuthenticationCode mac; ReturnErrorOnFailure(session->EncryptBeforeSend(data, totalLen, data, packetHeader, mac)); diff --git a/src/transport/SessionManager.cpp b/src/transport/SessionManager.cpp index 25a195ff3fca9e..2aca1cdcaf8445 100644 --- a/src/transport/SessionManager.cpp +++ b/src/transport/SessionManager.cpp @@ -29,6 +29,7 @@ #include #include +#include "transport/TraceMessage.h" #include #include #include @@ -135,6 +136,10 @@ CHIP_ERROR SessionManager::PrepareMessage(const SessionHandle & sessionHandle, P { return CHIP_ERROR_INTERNAL; } + + // Trace before any encryption + CHIP_TRACE_MESSAGE_SENT(payloadHeader, packetHeader, message->Start(), message->TotalLength()); + // TODO #11911 Update SecureMessageCodec::Encrypt for Group ReturnErrorOnFailure(payloadHeader.EncodeBeforeData(message)); @@ -158,6 +163,9 @@ CHIP_ERROR SessionManager::PrepareMessage(const SessionHandle & sessionHandle, P .SetSessionId(session->GetPeerSessionId()) // .SetSessionType(Header::SessionType::kUnicastSession); + // Trace before any encryption + CHIP_TRACE_MESSAGE_SENT(payloadHeader, packetHeader, message->Start(), message->TotalLength()); + ReturnErrorOnFailure(SecureMessageCodec::Encrypt(session, payloadHeader, packetHeader, message)); ReturnErrorOnFailure(counter.Advance()); @@ -168,14 +176,16 @@ CHIP_ERROR SessionManager::PrepareMessage(const SessionHandle & sessionHandle, P } break; case Transport::Session::SessionType::kUnauthenticated: { - ReturnErrorOnFailure(payloadHeader.EncodeBeforeData(message)); - MessageCounter & counter = mGlobalUnencryptedMessageCounter; uint32_t messageCounter = counter.Value(); ReturnErrorOnFailure(counter.Advance()); - packetHeader.SetMessageCounter(messageCounter); + // Trace after all headers are settled. + CHIP_TRACE_MESSAGE_SENT(payloadHeader, packetHeader, message->Start(), message->TotalLength()); + + ReturnErrorOnFailure(payloadHeader.EncodeBeforeData(message)); + #if CHIP_PROGRESS_LOGGING destination = kUndefinedNodeId; fabricIndex = kUndefinedFabricIndex; @@ -457,6 +467,7 @@ void SessionManager::MessageDispatch(const PacketHeader & packetHeader, const Tr if (mCB != nullptr) { + CHIP_TRACE_MESSAGE_RECEIVED(payloadHeader, packetHeader, unsecuredSession, peerAddress, msg->Start(), msg->TotalLength()); mCB->OnMessageReceived(packetHeader, payloadHeader, session, peerAddress, isDuplicate, std::move(msg)); } } @@ -532,6 +543,7 @@ void SessionManager::SecureUnicastMessageDispatch(const PacketHeader & packetHea if (mCB != nullptr) { + CHIP_TRACE_MESSAGE_RECEIVED(payloadHeader, packetHeader, secureSession, peerAddress, msg->Start(), msg->TotalLength()); mCB->OnMessageReceived(packetHeader, payloadHeader, session.Value(), peerAddress, isDuplicate, std::move(msg)); } } @@ -609,6 +621,7 @@ void SessionManager::SecureGroupMessageDispatch(const PacketHeader & packetHeade VerifyOrReturn(session.HasValue(), ChipLogError(Inet, "Error when creating group session handle.")); Transport::GroupSession * groupSession = session.Value()->AsGroupSession(); + CHIP_TRACE_MESSAGE_RECEIVED(payloadHeader, packetHeader, groupSession, peerAddress, msg->Start(), msg->TotalLength()); mCB->OnMessageReceived(packetHeader, payloadHeader, session.Value(), peerAddress, isDuplicate, std::move(msg)); RemoveGroupSession(groupSession); diff --git a/src/transport/TraceMessage.h b/src/transport/TraceMessage.h index b59ba910e76e72..0863bf0cc9cec8 100644 --- a/src/transport/TraceMessage.h +++ b/src/transport/TraceMessage.h @@ -19,20 +19,37 @@ #pragma once #include +#include #include +#include #if CHIP_CONFIG_TRANSPORT_TRACE_ENABLED #include "pw_trace/trace.h" -#define CHIP_TRACE_MESSAGE(payloadHeader, packetHeader, data, dataLen) \ +#define CHIP_TRACE_MESSAGE_SENT(payloadHeader, packetHeader, data, dataLen) \ do \ { \ - ::chip::trace::TraceSecureMessageData _trace_data{ &payloadHeader, &packetHeader, data, dataLen }; \ - PW_TRACE_INSTANT_DATA("SecureMsg", ::chip::trace::kTraceSecureMessageDataFormat, \ - reinterpret_cast(&_trace_data), sizeof(_trace_data)); \ + const ::chip::trace::TraceSecureMessageSentData _trace_data{ &payloadHeader, &packetHeader, data, dataLen }; \ + PW_TRACE_INSTANT_DATA("SecureMsg", ::chip::trace::kTraceMessageSentDataFormat, \ + reinterpret_cast(&_trace_data), sizeof(_trace_data)); \ } while (0) + +#define CHIP_TRACE_MESSAGE_RECEIVED(payloadHeader, packetHeader, session, peerAddress, data, dataLen) \ + do \ + { \ + const ::chip::trace::TraceSecureMessageReceivedData _trace_data{ &payloadHeader, &packetHeader, session, \ + &peerAddress, data, dataLen }; \ + PW_TRACE_INSTANT_DATA("SecureMsg", ::chip::trace::kTraceMessageReceivedDataFormat, \ + reinterpret_cast(&_trace_data), sizeof(_trace_data)); \ + } while (0) + #else -#define CHIP_TRACE_MESSAGE(payloadHeader, packetHeader, data, dataLen) \ +#define CHIP_TRACE_MESSAGE_SENT(payloadHeader, packetHeader, data, dataLen) \ + do \ + { \ + } while (0) + +#define CHIP_TRACE_MESSAGE_RECEIVED(payloadHeader, packetHeader, session, peerAddress, data, dataLen) \ do \ { \ } while (0) @@ -42,13 +59,25 @@ namespace chip { namespace trace { -constexpr const char * kTraceSecureMessageDataFormat = "SecMsg"; +constexpr const char * kTraceMessageEvent = "SecureMsg"; +constexpr const char * kTraceMessageSentDataFormat = "SecMsgSent"; +constexpr const char * kTraceMessageReceivedDataFormat = "SecMsgReceived"; + +struct TraceSecureMessageSentData +{ + const PayloadHeader * payloadHeader; + const PacketHeader * packetHeader; + const uint8_t * packetPayload; + size_t packetSize; +}; -struct TraceSecureMessageData +struct TraceSecureMessageReceivedData { - PayloadHeader * payloadHeader; - PacketHeader * packetHeader; - uint8_t * packetPayload; + const PayloadHeader * payloadHeader; + const PacketHeader * packetHeader; + const Transport::Session * session; + const Transport::PeerAddress * peerAddress; + const uint8_t * packetPayload; size_t packetSize; }; diff --git a/src/transport/raw/MessageHeader.h b/src/transport/raw/MessageHeader.h index 14e49272db62b3..e5e048855d5060 100644 --- a/src/transport/raw/MessageHeader.h +++ b/src/transport/raw/MessageHeader.h @@ -439,6 +439,9 @@ class PayloadHeader /** Get the secure msg type from this header. */ uint8_t GetMessageType() const { return mMessageType; } + /** Get the raw exchange flags from this header. */ + uint8_t GetExhangeFlags() const { return mExchangeFlags.Raw(); } + /** Check whether the header has a given secure message type */ bool HasMessageType(uint8_t type) const { return mMessageType == type; } template ::value>>