From 3453209fdfa572160831cf8eca389142c5fa0e3c Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Wed, 29 Sep 2021 21:51:22 -0400 Subject: [PATCH] Fix various issues in CASE Sigma1 processing (#10084) * Factor out the parsing of a Sigma1 message into a separate function. This allows testing the parsing, and catching bugs in it. * Add various sanity checks to Sigma1 processing. * Verify that the TLV struct is actually close. * Verify that the octet strings are the right sizes. * Fix incorrect tag value checking. --- src/lib/core/CHIPTLV.h | 26 +++ src/lib/core/CHIPTLVReader.cpp | 14 +- src/lib/core/CHIPTLVTags.h | 4 +- src/protocols/secure_channel/CASESession.cpp | 145 ++++++------ src/protocols/secure_channel/CASESession.h | 35 ++- .../secure_channel/tests/TestCASESession.cpp | 212 ++++++++++++++++++ 6 files changed, 348 insertions(+), 88 deletions(-) diff --git a/src/lib/core/CHIPTLV.h b/src/lib/core/CHIPTLV.h index 88515d8a6858bc..3ba177703b12d9 100644 --- a/src/lib/core/CHIPTLV.h +++ b/src/lib/core/CHIPTLV.h @@ -186,6 +186,32 @@ class DLL_EXPORT TLVReader */ CHIP_ERROR Next(); + /** + * Advances the TLVReader object to the next TLV element to be read, asserting the tag of + * the new element. + * + * The Next(uint64_t expectedTag) method is a convenience method that has the + * same behavior as Next(), but also verifies that the tag of the new TLV element matches + * the supplied argument. + * + * @param[in] expectedTag The expected tag for the next element. + * + * @retval #CHIP_NO_ERROR If the reader was successfully positioned on a new element. + * @retval #CHIP_END_OF_TLV If no further elements are available. + * @retval #CHIP_ERROR_UNEXPECTED_TLV_ELEMENT + * If the tag associated with the new element does not match the + * value of the @p expectedTag argument. + * @retval #CHIP_ERROR_TLV_UNDERRUN If the underlying TLV encoding ended prematurely. + * @retval #CHIP_ERROR_INVALID_TLV_ELEMENT + * If the reader encountered an invalid or unsupported TLV + * element type. + * @retval #CHIP_ERROR_INVALID_TLV_TAG If the reader encountered a TLV tag in an invalid context. + * @retval other Other CHIP or platform error codes returned by the configured + * TLVBackingStore. + * + */ + CHIP_ERROR Next(uint64_t expectedTag); + /** * Advances the TLVReader object to the next TLV element to be read, asserting the type and tag of * the new element. diff --git a/src/lib/core/CHIPTLVReader.cpp b/src/lib/core/CHIPTLVReader.cpp index 0b6c348dc8a96f..0dea760055fa45 100644 --- a/src/lib/core/CHIPTLVReader.cpp +++ b/src/lib/core/CHIPTLVReader.cpp @@ -505,18 +505,26 @@ CHIP_ERROR TLVReader::Next() return CHIP_NO_ERROR; } -CHIP_ERROR TLVReader::Next(TLVType expectedType, uint64_t expectedTag) +CHIP_ERROR TLVReader::Next(uint64_t expectedTag) { CHIP_ERROR err = Next(); if (err != CHIP_NO_ERROR) return err; - if (GetType() != expectedType) - return CHIP_ERROR_WRONG_TLV_TYPE; if (mElemTag != expectedTag) return CHIP_ERROR_UNEXPECTED_TLV_ELEMENT; return CHIP_NO_ERROR; } +CHIP_ERROR TLVReader::Next(TLVType expectedType, uint64_t expectedTag) +{ + CHIP_ERROR err = Next(expectedTag); + if (err != CHIP_NO_ERROR) + return err; + if (GetType() != expectedType) + return CHIP_ERROR_WRONG_TLV_TYPE; + return CHIP_NO_ERROR; +} + CHIP_ERROR TLVReader::Skip() { CHIP_ERROR err; diff --git a/src/lib/core/CHIPTLVTags.h b/src/lib/core/CHIPTLVTags.h index 98bb85b501dacc..a88de6eaf6770b 100644 --- a/src/lib/core/CHIPTLVTags.h +++ b/src/lib/core/CHIPTLVTags.h @@ -86,7 +86,7 @@ enum * @param[in] tagNum The profile-specific tag number assigned to the tag. * @return A 64-bit integer representing the tag. */ -inline uint64_t ProfileTag(uint32_t profileId, uint32_t tagNum) +inline constexpr uint64_t ProfileTag(uint32_t profileId, uint32_t tagNum) { return ((static_cast(profileId)) << kProfileIdShift) | tagNum; } @@ -111,7 +111,7 @@ inline uint64_t ProfileTag(uint16_t vendorId, uint16_t profileNum, uint32_t tagN * @param[in] tagNum The context-specific tag number assigned to the tag. * @return A 64-bit integer representing the tag. */ -inline uint64_t ContextTag(uint8_t tagNum) +inline constexpr uint64_t ContextTag(uint8_t tagNum) { return kSpecialTagMarker | tagNum; } diff --git a/src/protocols/secure_channel/CASESession.cpp b/src/protocols/secure_channel/CASESession.cpp index 89c9cb8a4604d2..0c2855ad056bb2 100644 --- a/src/protocols/secure_channel/CASESession.cpp +++ b/src/protocols/secure_channel/CASESession.cpp @@ -418,38 +418,26 @@ CHIP_ERROR CASESession::HandleSigma1(System::PacketBufferHandle && msg) { CHIP_ERROR err = CHIP_NO_ERROR; System::PacketBufferTLVReader tlvReader; - TLV::TLVType containerType = TLV::kTLVType_Structure; uint16_t initiatorSessionId; - uint8_t destinationIdentifier[kSHA256_Hash_Length]; - uint8_t initiatorRandom[kSigmaParamRandomNumberSize]; - - uint32_t decodeTagIdSeq = 0; + ByteSpan destinationIdentifier; + ByteSpan initiatorRandom; ChipLogDetail(SecureChannel, "Received Sigma1 msg"); bool sessionResumptionRequested = false; ByteSpan resumptionId; ByteSpan resume1MIC; + ByteSpan initiatorPubKey; const ByteSpan * ipkListSpan = GetIPKList(); FabricIndex fabricIndex = Transport::kUndefinedFabricIndex; - SuccessOrExit(err = IsResumptionRequestPresent(msg, sessionResumptionRequested, resumptionId, resume1MIC)); - SuccessOrExit(err = mCommissioningHash.AddData(ByteSpan{ msg->Start(), msg->DataLength() })); tlvReader.Init(std::move(msg)); - SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag)); - SuccessOrExit(err = tlvReader.EnterContainer(containerType)); - - SuccessOrExit(err = tlvReader.Next()); - VerifyOrExit(TLV::TagNumFromTag(tlvReader.GetTag()) == ++decodeTagIdSeq, err = CHIP_ERROR_INVALID_TLV_TAG); - SuccessOrExit(err = tlvReader.GetBytes(initiatorRandom, sizeof(initiatorRandom))); - - SuccessOrExit(err = tlvReader.Next()); - VerifyOrExit(TLV::TagNumFromTag(tlvReader.GetTag()) == ++decodeTagIdSeq, err = CHIP_ERROR_INVALID_TLV_TAG); - SuccessOrExit(err = tlvReader.Get(initiatorSessionId)); + SuccessOrExit(err = ParseSigma1(tlvReader, initiatorRandom, initiatorSessionId, destinationIdentifier, initiatorPubKey, + sessionResumptionRequested, resumptionId, resume1MIC)); ChipLogDetail(SecureChannel, "Peer assigned session key ID %d", initiatorSessionId); SetPeerSessionId(initiatorSessionId); @@ -457,11 +445,11 @@ CHIP_ERROR CASESession::HandleSigma1(System::PacketBufferHandle && msg) if (sessionResumptionRequested && resumptionId.data_equal(ByteSpan(mResumptionId))) { // Cross check resume1MIC with the shared secret - if (ValidateSigmaResumeMIC(resume1MIC, ByteSpan(initiatorRandom), resumptionId, ByteSpan(kKDFS1RKeyInfo), + if (ValidateSigmaResumeMIC(resume1MIC, initiatorRandom, resumptionId, ByteSpan(kKDFS1RKeyInfo), ByteSpan(kResume1MIC_Nonce)) == CHIP_NO_ERROR) { // Send Sigma2Resume message to the initiator - SuccessOrExit(err = SendSigma2Resume(ByteSpan(initiatorRandom))); + SuccessOrExit(err = SendSigma2Resume(initiatorRandom)); mDelegate->OnSessionEstablishmentStarted(); @@ -472,21 +460,17 @@ CHIP_ERROR CASESession::HandleSigma1(System::PacketBufferHandle && msg) memcpy(mIPK, ipkListSpan->data(), sizeof(mIPK)); - SuccessOrExit(err = tlvReader.Next()); - VerifyOrExit(TLV::TagNumFromTag(tlvReader.GetTag()) == ++decodeTagIdSeq, err = CHIP_ERROR_INVALID_TLV_TAG); - SuccessOrExit(err = tlvReader.GetBytes(destinationIdentifier, sizeof(destinationIdentifier))); - VerifyOrExit(mFabricsTable != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - fabricIndex = mFabricsTable->FindDestinationIDCandidate(ByteSpan(destinationIdentifier), ByteSpan(initiatorRandom), ipkListSpan, - GetIPKListEntries()); + fabricIndex = + mFabricsTable->FindDestinationIDCandidate(destinationIdentifier, initiatorRandom, ipkListSpan, GetIPKListEntries()); VerifyOrExit(fabricIndex != Transport::kUndefinedFabricIndex, err = CHIP_ERROR_KEY_NOT_FOUND); mFabricInfo = mFabricsTable->FindFabricWithIndex(fabricIndex); VerifyOrExit(mFabricInfo != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - SuccessOrExit(err = tlvReader.Next()); - VerifyOrExit(TLV::TagNumFromTag(tlvReader.GetTag()) == ++decodeTagIdSeq, err = CHIP_ERROR_INVALID_TLV_TAG); - SuccessOrExit(err = tlvReader.GetBytes(mRemotePubKey, static_cast(mRemotePubKey.Length()))); + // ParseSigma1 ensures that: + // mRemotePubKey.Length() == initiatorPubKey.size() == kP256_PublicKey_Length. + memcpy(mRemotePubKey.Bytes(), initiatorPubKey.data(), mRemotePubKey.Length()); SuccessOrExit(err = SendSigma2()); @@ -1360,58 +1344,75 @@ CHIP_ERROR CASESession::OnFailureStatusReport(Protocols::SecureChannel::GeneralS return err; } -CHIP_ERROR CASESession::IsResumptionRequestPresent(const System::PacketBufferHandle & msg, bool & resumptionRequested, - ByteSpan & resumptionID, ByteSpan & resume1MIC) +CHIP_ERROR CASESession::ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, ByteSpan & initiatorRandom, + uint16_t & initiatorSessionId, ByteSpan & destinationId, ByteSpan & initiatorEphPubKey, + bool & resumptionRequested, ByteSpan & resumptionId, ByteSpan & initiatorResumeMIC) { - CHIP_ERROR err = CHIP_NO_ERROR; - - System::PacketBufferTLVReader tlvReader; - TLV::TLVType containerType = TLV::kTLVType_Structure; - - constexpr uint32_t kResumptionIDTag = 6; - constexpr uint32_t kResume1MICTag = 7; - - uint32_t lastDecodedTLVTag = 0; - bool resumptionIDTagFound = false; - bool resume1MICTagFound = false; - - System::PacketBufferHandle msg_clone = msg.CloneData(); + using namespace TLV; + + constexpr uint8_t kInitiatorRandomTag = 1; + constexpr uint8_t kInitiatorSessionIdTag = 2; + constexpr uint8_t kDestinationIdTag = 3; + constexpr uint8_t kInitiatorPubKeyTag = 4; + constexpr uint8_t kInitiatorMRPParamsTag = 5; + constexpr uint8_t kResumptionIDTag = 6; + constexpr uint8_t kResume1MICTag = 7; + + TLVType containerType = kTLVType_Structure; + ReturnErrorOnFailure(tlvReader.Next(containerType, AnonymousTag)); + ReturnErrorOnFailure(tlvReader.EnterContainer(containerType)); + + ReturnErrorOnFailure(tlvReader.Next(ContextTag(kInitiatorRandomTag))); + ReturnErrorOnFailure(tlvReader.GetByteView(initiatorRandom)); + VerifyOrReturnError(initiatorRandom.size() == kSigmaParamRandomNumberSize, CHIP_ERROR_INVALID_CASE_PARAMETER); + + ReturnErrorOnFailure(tlvReader.Next(ContextTag(kInitiatorSessionIdTag))); + ReturnErrorOnFailure(tlvReader.Get(initiatorSessionId)); + + ReturnErrorOnFailure(tlvReader.Next(ContextTag(kDestinationIdTag))); + ReturnErrorOnFailure(tlvReader.GetByteView(destinationId)); + VerifyOrReturnError(destinationId.size() == kSHA256_Hash_Length, CHIP_ERROR_INVALID_CASE_PARAMETER); + + ReturnErrorOnFailure(tlvReader.Next(ContextTag(kInitiatorPubKeyTag))); + ReturnErrorOnFailure(tlvReader.GetByteView(initiatorEphPubKey)); + VerifyOrReturnError(initiatorEphPubKey.size() == kP256_PublicKey_Length, CHIP_ERROR_INVALID_CASE_PARAMETER); + + // Optional members start here. + CHIP_ERROR err = tlvReader.Next(); + if (err == CHIP_NO_ERROR && tlvReader.GetTag() == ContextTag(kInitiatorMRPParamsTag)) + { + // We don't handle this yet; just move on. + err = tlvReader.Next(); + } - tlvReader.Init(std::move(msg_clone)); - SuccessOrExit(err = tlvReader.Next(containerType, TLV::AnonymousTag)); - SuccessOrExit(err = tlvReader.EnterContainer(containerType)); + bool resumptionIDTagFound = false; + bool resume1MICTagFound = false; - while (!resumptionIDTagFound || !resume1MICTagFound) + if (err == CHIP_NO_ERROR && tlvReader.GetTag() == ContextTag(kResumptionIDTag)) { - SuccessOrExit(err = tlvReader.Next()); - - // Make sure that tlv tags are in order. - // There are optional TLV elements, so some of them may not be present. - // So the check cannot match the absolute value of the expected tag. - uint32_t tlvTag = TLV::TagNumFromTag(tlvReader.GetTag()); - VerifyOrExit(tlvTag > lastDecodedTLVTag, CHIP_ERROR_INVALID_TLV_TAG); - lastDecodedTLVTag = tlvTag; + resumptionIDTagFound = true; + ReturnErrorOnFailure(tlvReader.GetByteView(resumptionId)); + VerifyOrReturnError(resumptionId.size() == kCASEResumptionIDSize, CHIP_ERROR_INVALID_CASE_PARAMETER); + err = tlvReader.Next(); + } - if (tlvTag == kResumptionIDTag) - { - resumptionIDTagFound = true; - SuccessOrExit(err = tlvReader.GetByteView(resumptionID)); - } - else if (tlvTag == kResume1MICTag) - { - VerifyOrExit(resumptionIDTagFound, CHIP_ERROR_INVALID_TLV_TAG); - resume1MICTagFound = true; - SuccessOrExit(err = tlvReader.GetByteView(resume1MIC)); - } + if (err == CHIP_NO_ERROR && tlvReader.GetTag() == ContextTag(kResume1MICTag)) + { + resume1MICTagFound = true; + ReturnErrorOnFailure(tlvReader.GetByteView(initiatorResumeMIC)); + VerifyOrReturnError(initiatorResumeMIC.size() == kTAGSize, CHIP_ERROR_INVALID_CASE_PARAMETER); + err = tlvReader.Next(); } -exit: - if (err != CHIP_NO_ERROR && err != CHIP_END_OF_TLV) + if (err == CHIP_END_OF_TLV) { - return err; + // We ran out of struct members, but that's OK, because they were optional. + err = CHIP_NO_ERROR; } - err = CHIP_NO_ERROR; + ReturnErrorOnFailure(err); + ReturnErrorOnFailure(tlvReader.ExitContainer(containerType)); + if (resumptionIDTagFound && resume1MICTagFound) { resumptionRequested = true; @@ -1422,10 +1423,10 @@ CHIP_ERROR CASESession::IsResumptionRequestPresent(const System::PacketBufferHan } else { - err = CHIP_ERROR_UNEXPECTED_TLV_ELEMENT; + return CHIP_ERROR_UNEXPECTED_TLV_ELEMENT; } - return err; + return CHIP_NO_ERROR; } CHIP_ERROR CASESession::ValidateReceivedMessage(ExchangeContext * ec, const PayloadHeader & payloadHeader, diff --git a/src/protocols/secure_channel/CASESession.h b/src/protocols/secure_channel/CASESession.h index 2ea99058e8cf84..d22a7f20a34284 100644 --- a/src/protocols/secure_channel/CASESession.h +++ b/src/protocols/secure_channel/CASESession.h @@ -31,6 +31,7 @@ #if CHIP_CRYPTO_HSM #include #endif +#include #include #include #include @@ -118,20 +119,27 @@ class DLL_EXPORT CASESession : public Messaging::ExchangeDelegate, public Pairin SessionEstablishmentDelegate * delegate); /** - * Parse the message to check if it has a session resumption request. - * A valid session resumption request must have Resumption ID, and InitiationResumeMIC. + * Parse a sigma1 message. This function will return success only if the + * message passes schema checks. Specifically: + * * The tags come in order. + * * The required tags are present. + * * The values for the tags that are present satisfy schema requirements + * (e.g. constraints on octet string lengths) + * * Either resumptionID and initiatorResume1MIC are both present or both + * absent. * - * If the message is a valid session resumption request, the output parameter resumptionRequested is set to true, - * and corresponding resumption ID and MIC are returned in the output parameters. Return value is set to CHIP_NO_ERROR. + * On success, the initiatorRandom, initiatorSessionId, destinationId, + * initiatorEphPubKey outparams will be set to the corresponding values in + * the message. * - * If the message is not a session resumption request (i.e. it doesn't contain both Resumption ID, and InitiationResumeMIC), - * the output parameter resumptionRequested is set to false. Return value is set to CHIP_NO_ERROR. - * - * If the message doesn't contain either Resumption ID or InitiationResumeMIC (i.e. contains only one of these fields), the - * function returns CHIP_ERROR_INVALID_ARGUMENT. + * On success, either the resumptionRequested outparam will be set to true + * and the resumptionID and initiatorResumeMIC outparams will be set to + * valid values, or the resumptionRequested outparam will be set to false. */ - static CHIP_ERROR IsResumptionRequestPresent(const System::PacketBufferHandle & message, bool & resumptionRequested, - ByteSpan & resumptionID, ByteSpan & resume1MIC); + static CHIP_ERROR ParseSigma1(TLV::ContiguousBufferTLVReader & tlvReader, ByteSpan & initiatorRandom, + uint16_t & initiatorSessionId, ByteSpan & destinationId, ByteSpan & initiatorEphPubKey, + // TODO: MRP param parsing + bool & resumptionRequested, ByteSpan & resumptionId, ByteSpan & initiatorResumeMIC); /** * @brief @@ -191,6 +199,11 @@ class DLL_EXPORT CASESession : public Messaging::ExchangeDelegate, public Pairin **/ void Clear(); + /** + * Parse the TLV for Sigma1 message. + */ + CHIP_ERROR ParseSigma1(); + private: enum State : uint8_t { diff --git a/src/protocols/secure_channel/tests/TestCASESession.cpp b/src/protocols/secure_channel/tests/TestCASESession.cpp index 2cd67489e6b5c6..72abb58c9f5d6c 100644 --- a/src/protocols/secure_channel/tests/TestCASESession.cpp +++ b/src/protocols/secure_channel/tests/TestCASESession.cpp @@ -430,6 +430,217 @@ void CASE_SecurePairingSerializeTest(nlTestSuite * inSuite, void * inContext) chip::Platform::Delete(testPairingSession2); } +struct Sigma1Params +{ + // Purposefully not using constants like kSigmaParamRandomNumberSize that + // the code uses, so we have a cross-check. + static constexpr size_t initiatorRandomLen = 32; + static constexpr uint16_t initiatorSessionId = 0; + static constexpr size_t destinationIdLen = 32; + static constexpr size_t initiatorEphPubKeyLen = 65; + static constexpr size_t resumptionIdLen = 0; // Nonzero means include it. + static constexpr size_t initiatorResumeMICLen = 0; // Nonzero means include it. + + static constexpr uint8_t initiatorRandomTag = 1; + static constexpr uint8_t initiatorSessionIdTag = 2; + static constexpr uint8_t destinationIdTag = 3; + static constexpr uint8_t initiatorEphPubKeyTag = 4; + static constexpr uint8_t resumptionIdTag = 6; + static constexpr uint8_t initiatorResumeMICTag = 7; + static constexpr uint64_t NumToTag(uint8_t num) { return TLV::ContextTag(num); } + + static constexpr bool includeStructEnd = true; + + static constexpr bool expectSuccess = true; +}; + +template +static CHIP_ERROR EncodeSigma1(MutableByteSpan & buf) +{ + using namespace TLV; + + TLVWriter writer; + writer.Init(buf); + + TLVType containerType; + ReturnErrorOnFailure(writer.StartContainer(AnonymousTag, kTLVType_Structure, containerType)); + uint8_t initiatorRandom[Params::initiatorRandomLen] = { 1 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::initiatorRandomTag), ByteSpan(initiatorRandom))); + + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::initiatorSessionIdTag), Params::initiatorSessionId)); + + uint8_t destinationId[Params::destinationIdLen] = { 2 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::destinationIdTag), ByteSpan(destinationId))); + + uint8_t initiatorEphPubKey[Params::initiatorEphPubKeyLen] = { 3 }; + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::initiatorEphPubKeyTag), ByteSpan(initiatorEphPubKey))); + + // I wish we had "if constexpr" support here, so the compiler would know + // resumptionIdLen is nonzero inside the block.... + if (Params::resumptionIdLen != 0) + { + uint8_t resumptionId[Params::resumptionIdLen]; + memset(resumptionId, 4, Params::resumptionIdLen); + ReturnErrorOnFailure( + writer.Put(Params::NumToTag(Params::resumptionIdTag), ByteSpan(resumptionId, Params::resumptionIdLen))); + } + + if (Params::initiatorResumeMICLen != 0) + { + uint8_t initiatorResumeMIC[Params::initiatorResumeMICLen]; + memset(initiatorResumeMIC, 5, Params::initiatorResumeMICLen); + ReturnErrorOnFailure(writer.Put(Params::NumToTag(Params::initiatorResumeMICTag), + ByteSpan(initiatorResumeMIC, Params::initiatorResumeMICLen))); + } + + if (Params::includeStructEnd) + { + ReturnErrorOnFailure(writer.EndContainer(containerType)); + } + + buf.reduce_size(writer.GetLengthWritten()); + return CHIP_NO_ERROR; +} + +// A macro, so we can tell which test failed based on line number. +#define TestSigma1Parsing(inSuite, mem, bufferSize, params) \ + do \ + { \ + MutableByteSpan buf(mem.Get(), bufferSize); \ + CHIP_ERROR err = EncodeSigma1(buf); \ + NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); \ + \ + TLV::ContiguousBufferTLVReader reader; \ + reader.Init(buf); \ + \ + ByteSpan initiatorRandom; \ + uint16_t initiatorSessionId; \ + ByteSpan destinationId; \ + ByteSpan initiatorEphPubKey; \ + bool resumptionRequested; \ + ByteSpan resumptionId; \ + ByteSpan initiatorResumeMIC; \ + err = CASESession::ParseSigma1(reader, initiatorRandom, initiatorSessionId, destinationId, initiatorEphPubKey, \ + resumptionRequested, resumptionId, initiatorResumeMIC); \ + NL_TEST_ASSERT(inSuite, (err == CHIP_NO_ERROR) == params::expectSuccess); \ + if (params::expectSuccess) \ + { \ + NL_TEST_ASSERT(inSuite, resumptionRequested == (params::resumptionIdLen != 0 && params::initiatorResumeMICLen != 0)); \ + /* Add other verification tests here as desired */ \ + } \ + } while (0) + +struct BadSigma1ParamsBase : public Sigma1Params +{ + static constexpr bool expectSuccess = false; +}; + +struct Sigma1NoStructEnd : public BadSigma1ParamsBase +{ + static constexpr bool includeStructEnd = false; +}; + +struct Sigma1WrongTags : public BadSigma1ParamsBase +{ + static constexpr uint64_t NumToTag(uint8_t num) { return TLV::ProfileTag(0, num); } +}; + +struct Sigma1TooLongRandom : public BadSigma1ParamsBase +{ + static constexpr size_t initiatorRandomLen = 33; +}; + +struct Sigma1TooShortRandom : public BadSigma1ParamsBase +{ + static constexpr size_t initiatorRandomLen = 31; +}; + +struct Sigma1TooLongDest : public BadSigma1ParamsBase +{ + static constexpr size_t destinationIdLen = 33; +}; + +struct Sigma1TooShortDest : public BadSigma1ParamsBase +{ + static constexpr size_t destinationIdLen = 31; +}; + +struct Sigma1TooLongPubkey : public BadSigma1ParamsBase +{ + static constexpr size_t initiatorEphPubKeyLen = 66; +}; + +struct Sigma1TooShortPubkey : public BadSigma1ParamsBase +{ + static constexpr size_t initiatorEphPubKeyLen = 64; +}; + +struct Sigma1WithResumption : public Sigma1Params +{ + static constexpr size_t resumptionIdLen = 16; + static constexpr size_t initiatorResumeMICLen = 16; +}; + +struct Sigma1TooLongResumptionId : public Sigma1WithResumption +{ + static constexpr size_t resumptionIdLen = 17; + static constexpr bool expectSuccess = false; +}; + +struct Sigma1TooShortResumptionId : public BadSigma1ParamsBase +{ + static constexpr size_t resumptionIdLen = 15; + static constexpr bool expectSuccess = false; +}; + +struct Sigma1TooLongResumeMIC : public Sigma1WithResumption +{ + static constexpr size_t resumptionIdLen = 17; + static constexpr bool expectSuccess = false; +}; + +struct Sigma1TooShortResumeMIC : public Sigma1WithResumption +{ + static constexpr size_t initiatorResumeMICLen = 15; + static constexpr bool expectSuccess = false; +}; + +struct Sigma1SessionIdMax : public Sigma1Params +{ + static constexpr uint32_t initiatorSessionId = UINT16_MAX; +}; + +struct Sigma1SessionIdTooBig : public BadSigma1ParamsBase +{ + static constexpr uint32_t initiatorSessionId = UINT16_MAX + 1; +}; + +static void CASE_Sigma1ParsingTest(nlTestSuite * inSuite, void * inContext) +{ + // 1280 bytes must be enough by definition. + constexpr size_t bufferSize = 1280; + chip::Platform::ScopedMemoryBuffer mem; + NL_TEST_ASSERT(inSuite, mem.Calloc(bufferSize)); + + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1Params); + + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1NoStructEnd); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1WrongTags); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooLongRandom); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooShortRandom); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooLongDest); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooShortDest); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooLongPubkey); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooShortPubkey); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1WithResumption); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooLongResumptionId); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooShortResumptionId); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooLongResumeMIC); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1TooShortResumeMIC); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1SessionIdMax); + TestSigma1Parsing(inSuite, mem, bufferSize, Sigma1SessionIdTooBig); +} + // Test Suite /** @@ -443,6 +654,7 @@ static const nlTest sTests[] = NL_TEST_DEF("Handshake", CASE_SecurePairingHandshakeTest), NL_TEST_DEF("ServerHandshake", CASE_SecurePairingHandshakeServerTest), NL_TEST_DEF("Serialize", CASE_SecurePairingSerializeTest), + NL_TEST_DEF("Sigma1Parsing", CASE_Sigma1ParsingTest), NL_TEST_SENTINEL() };