| /* |
| * |
| * Copyright (c) 2021-2022 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 implements a unit test suite for the additional payload generation |
| * code functionality. |
| * |
| */ |
| |
| #include <math.h> |
| #include <memory> |
| #include <nlunit-test.h> |
| #include <stdio.h> |
| |
| #include <lib/support/BytesToHex.h> |
| #include <lib/support/CHIPMem.h> |
| #include <lib/support/CHIPMemString.h> |
| #include <lib/support/CHIPPlatformMemory.h> |
| #include <lib/support/UnitTestContext.h> |
| #include <setup_payload/AdditionalDataPayloadGenerator.h> |
| #include <setup_payload/AdditionalDataPayloadParser.h> |
| #include <setup_payload/SetupPayload.h> |
| #include <system/SystemPacketBuffer.h> |
| |
| #include <lib/support/UnitTestRegistration.h> |
| #include <lib/support/verhoeff/Verhoeff.h> |
| |
| using namespace chip; |
| |
| namespace { |
| |
| constexpr char kAdditionalDataPayloadWithoutRotatingDeviceId[] = "1518"; |
| constexpr char kAdditionalDataPayloadWithRotatingDeviceId[] = "153000120A00D00561E77A68A9FD975057375B9283A818"; |
| constexpr char kAdditionalDataPayloadWithInvalidRotatingDeviceIdLength[] = "153000FF0A001998AB7130E38B7E9A401CFE9F7B79AF18"; |
| constexpr char kAdditionalDataPayloadWithLongRotatingDeviceId[] = "153000130A00191998AB7130E38B7E9A401CFE9F7B79AF18"; |
| constexpr char kAdditionalDataPayloadWithShortRotatingDeviceId[] = "153000110A001998AB7130E38B7E9A401CFE9F7B7918"; |
| constexpr char kRotatingDeviceId[] = "0A00D00561E77A68A9FD975057375B9283A8"; |
| constexpr char kShortRotatingDeviceId[] = "0A001998AB7130E38B7E9A401CFE9F7B79"; |
| constexpr uint16_t kAdditionalDataPayloadLength = 51; |
| #if CHIP_ENABLE_ROTATING_DEVICE_ID |
| constexpr uint8_t kUniqueId[] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; |
| constexpr char kAdditionalDataPayloadWithRotatingDeviceIdAndMaxLifetimeCounter[] = "15300012FFFF8BEA0C775F001981365D6362E1C0665A18"; |
| constexpr uint16_t kLifetimeCounter = 10; |
| constexpr uint16_t kShortRotatingIdLength = 5; |
| #endif // CHIP_ENABLE_ROTATING_DEVICE_ID |
| |
| CHIP_ERROR GenerateAdditionalDataPayload(nlTestSuite * inSuite, AdditionalDataPayloadGeneratorParams & additionalDataPayloadParams, |
| BitFlags<AdditionalDataFields> additionalDataFields, char * additionalDataPayloadOutput) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| chip::System::PacketBufferHandle bufferHandle; |
| |
| err = AdditionalDataPayloadGenerator().generateAdditionalDataPayload(additionalDataPayloadParams, bufferHandle, |
| additionalDataFields); |
| if (err == CHIP_NO_ERROR) |
| { |
| NL_TEST_ASSERT(inSuite, !bufferHandle.IsNull()); |
| } |
| else |
| { |
| return err; |
| } |
| char output[kAdditionalDataPayloadLength]; |
| err = chip::Encoding::BytesToUppercaseHexString(bufferHandle->Start(), bufferHandle->DataLength(), output, ArraySize(output)); |
| |
| if (err == CHIP_NO_ERROR) |
| { |
| memmove(additionalDataPayloadOutput, output, kAdditionalDataPayloadLength); |
| } |
| return err; |
| } |
| |
| CHIP_ERROR ParseAdditionalDataPayload(const char * additionalDataPayload, size_t additionalDataPayloadLength, |
| chip::SetupPayloadData::AdditionalDataPayload & outPayload) |
| { |
| if (additionalDataPayloadLength % 2 != 0) |
| { |
| return CHIP_ERROR_INVALID_STRING_LENGTH; |
| } |
| size_t additionalDataPayloadBytesLength = additionalDataPayloadLength / 2; |
| std::unique_ptr<uint8_t[]> additionalDataPayloadBytes(new uint8_t[additionalDataPayloadBytesLength]); |
| size_t bufferSize = chip::Encoding::HexToBytes(additionalDataPayload, additionalDataPayloadLength, |
| additionalDataPayloadBytes.get(), additionalDataPayloadBytesLength); |
| return AdditionalDataPayloadParser(additionalDataPayloadBytes.get(), bufferSize).populatePayload(outPayload); |
| } |
| |
| void TestGeneratingAdditionalDataPayloadWithoutRotatingDeviceId(nlTestSuite * inSuite, void * inContext) |
| { |
| BitFlags<AdditionalDataFields> additionalDataFields; |
| char output[kAdditionalDataPayloadLength]; |
| AdditionalDataPayloadGeneratorParams additionalDataPayloadParams; |
| |
| NL_TEST_ASSERT(inSuite, |
| GenerateAdditionalDataPayload(inSuite, additionalDataPayloadParams, additionalDataFields, output) == |
| CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, strcmp(output, kAdditionalDataPayloadWithoutRotatingDeviceId) == 0); |
| } |
| |
| #if CHIP_ENABLE_ROTATING_DEVICE_ID |
| void TestGeneratingAdditionalDataPayloadWithRotatingDeviceId(nlTestSuite * inSuite, void * inContext) |
| { |
| BitFlags<AdditionalDataFields> additionalDataFields; |
| additionalDataFields.Set(AdditionalDataFields::RotatingDeviceId); |
| AdditionalDataPayloadGeneratorParams additionalDataPayloadParams; |
| additionalDataPayloadParams.rotatingDeviceIdLifetimeCounter = kLifetimeCounter; |
| additionalDataPayloadParams.rotatingDeviceIdUniqueId = ByteSpan{ kUniqueId, sizeof(kUniqueId) }; |
| |
| char output[kAdditionalDataPayloadLength]; |
| NL_TEST_ASSERT(inSuite, |
| GenerateAdditionalDataPayload(inSuite, additionalDataPayloadParams, additionalDataFields, output) == |
| CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, strcmp(output, kAdditionalDataPayloadWithRotatingDeviceId) == 0); |
| } |
| |
| void TestGeneratingAdditionalDataPayloadWithRotatingDeviceIdAndMaxLifetimeCounter(nlTestSuite * inSuite, void * inContext) |
| { |
| BitFlags<AdditionalDataFields> additionalDataFields; |
| additionalDataFields.Set(AdditionalDataFields::RotatingDeviceId); |
| AdditionalDataPayloadGeneratorParams additionalDataPayloadParams; |
| additionalDataPayloadParams.rotatingDeviceIdLifetimeCounter = std::numeric_limits<uint16_t>::max(); |
| additionalDataPayloadParams.rotatingDeviceIdUniqueId = ByteSpan{ kUniqueId, sizeof(kUniqueId) }; |
| |
| char output[kAdditionalDataPayloadLength]; |
| NL_TEST_ASSERT(inSuite, |
| GenerateAdditionalDataPayload(inSuite, additionalDataPayloadParams, additionalDataFields, output) == |
| CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, strcmp(output, kAdditionalDataPayloadWithRotatingDeviceIdAndMaxLifetimeCounter) == 0); |
| } |
| |
| void TestGeneratingAdditionalDataPayloadWithRotatingDeviceIdWithNullInputs(nlTestSuite * inSuite, void * inContext) |
| { |
| BitFlags<AdditionalDataFields> additionalDataFields; |
| additionalDataFields.Set(AdditionalDataFields::RotatingDeviceId); |
| AdditionalDataPayloadGeneratorParams additionalDataPayloadParams; |
| |
| char output[kAdditionalDataPayloadLength]; |
| NL_TEST_ASSERT(inSuite, |
| GenerateAdditionalDataPayload(inSuite, additionalDataPayloadParams, additionalDataFields, output) == |
| CHIP_ERROR_INVALID_ARGUMENT); |
| } |
| |
| void TestGeneratingRotatingDeviceIdAsString(nlTestSuite * inSuite, void * inContext) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| char rotatingDeviceIdHexBuffer[RotatingDeviceId::kHexMaxLength]; |
| size_t rotatingDeviceIdValueOutputSize = 0; |
| AdditionalDataPayloadGeneratorParams additionalDataPayloadParams; |
| additionalDataPayloadParams.rotatingDeviceIdLifetimeCounter = kLifetimeCounter; |
| additionalDataPayloadParams.rotatingDeviceIdUniqueId = ByteSpan{ kUniqueId, sizeof(kUniqueId) }; |
| err = AdditionalDataPayloadGenerator().generateRotatingDeviceIdAsHexString( |
| additionalDataPayloadParams, rotatingDeviceIdHexBuffer, ArraySize(rotatingDeviceIdHexBuffer), |
| rotatingDeviceIdValueOutputSize); |
| NL_TEST_ASSERT(inSuite, err == CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, strcmp(rotatingDeviceIdHexBuffer, kRotatingDeviceId) == 0); |
| // Parsing out the lifetime counter value |
| long lifetimeCounter; |
| char lifetimeCounterStr[3]; |
| Platform::CopyString(lifetimeCounterStr, rotatingDeviceIdHexBuffer); |
| |
| char * parseEnd; |
| lifetimeCounter = strtol(lifetimeCounterStr, &parseEnd, 16); |
| NL_TEST_ASSERT(inSuite, lifetimeCounter == kLifetimeCounter); |
| } |
| |
| void TestGeneratingRotatingDeviceIdAsStringWithNullInputs(nlTestSuite * inSuite, void * inContext) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| char rotatingDeviceIdHexBuffer[RotatingDeviceId::kHexMaxLength]; |
| size_t rotatingDeviceIdValueOutputSize = 0; |
| AdditionalDataPayloadGeneratorParams additionalDataPayloadParams; |
| additionalDataPayloadParams.rotatingDeviceIdLifetimeCounter = 0; |
| additionalDataPayloadParams.rotatingDeviceIdUniqueId = MutableByteSpan{ nullptr, sizeof(kUniqueId) }; |
| err = AdditionalDataPayloadGenerator().generateRotatingDeviceIdAsHexString( |
| additionalDataPayloadParams, rotatingDeviceIdHexBuffer, ArraySize(rotatingDeviceIdHexBuffer), |
| rotatingDeviceIdValueOutputSize); |
| NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_INVALID_ARGUMENT); |
| } |
| |
| void TestGeneratingRotatingDeviceIdWithSmallBuffer(nlTestSuite * inSuite, void * inContext) |
| { |
| CHIP_ERROR err = CHIP_NO_ERROR; |
| char rotatingDeviceIdHexBuffer[kShortRotatingIdLength]; |
| size_t rotatingDeviceIdValueOutputSize = 0; |
| AdditionalDataPayloadGeneratorParams additionalDataPayloadParams; |
| additionalDataPayloadParams.rotatingDeviceIdLifetimeCounter = kLifetimeCounter; |
| additionalDataPayloadParams.rotatingDeviceIdUniqueId = ByteSpan{ kUniqueId, sizeof(kUniqueId) }; |
| err = AdditionalDataPayloadGenerator().generateRotatingDeviceIdAsHexString( |
| additionalDataPayloadParams, rotatingDeviceIdHexBuffer, ArraySize(rotatingDeviceIdHexBuffer), |
| rotatingDeviceIdValueOutputSize); |
| NL_TEST_ASSERT(inSuite, err == CHIP_ERROR_BUFFER_TOO_SMALL); |
| } |
| #endif // CHIP_ENABLE_ROTATING_DEVICE_ID |
| |
| void TestParsingAdditionalDataPayloadWithRotatingDeviceId(nlTestSuite * inSuite, void * inContext) |
| { |
| chip::SetupPayloadData::AdditionalDataPayload resultPayload; |
| NL_TEST_ASSERT(inSuite, |
| ParseAdditionalDataPayload(kAdditionalDataPayloadWithRotatingDeviceId, |
| strlen(kAdditionalDataPayloadWithRotatingDeviceId), resultPayload) == CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, strcmp(resultPayload.rotatingDeviceId.c_str(), kRotatingDeviceId) == 0); |
| } |
| |
| void TestParsingAdditionalDataPayloadWithoutRotatingDeviceId(nlTestSuite * inSuite, void * inContext) |
| { |
| chip::SetupPayloadData::AdditionalDataPayload resultPayload; |
| NL_TEST_ASSERT(inSuite, |
| ParseAdditionalDataPayload(kAdditionalDataPayloadWithoutRotatingDeviceId, |
| strlen(kAdditionalDataPayloadWithoutRotatingDeviceId), |
| resultPayload) == CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, strcmp(resultPayload.rotatingDeviceId.c_str(), "") == 0); |
| } |
| |
| void TestParsingAdditionalDataPayloadWithInvalidRotatingDeviceIdLength(nlTestSuite * inSuite, void * inContext) |
| { |
| chip::SetupPayloadData::AdditionalDataPayload resultPayload; |
| NL_TEST_ASSERT(inSuite, |
| ParseAdditionalDataPayload(kAdditionalDataPayloadWithInvalidRotatingDeviceIdLength, |
| strlen(kAdditionalDataPayloadWithInvalidRotatingDeviceIdLength), |
| resultPayload) == CHIP_ERROR_TLV_UNDERRUN); |
| } |
| |
| void TestParsingAdditionalDataPayloadWithLongRotatingDeviceId(nlTestSuite * inSuite, void * inContext) |
| { |
| chip::SetupPayloadData::AdditionalDataPayload resultPayload; |
| NL_TEST_ASSERT(inSuite, |
| ParseAdditionalDataPayload(kAdditionalDataPayloadWithLongRotatingDeviceId, |
| strlen(kAdditionalDataPayloadWithLongRotatingDeviceId), |
| resultPayload) == CHIP_ERROR_INVALID_STRING_LENGTH); |
| } |
| |
| void TestParsingAdditionalDataPayloadWithShortRotatingDeviceId(nlTestSuite * inSuite, void * inContext) |
| { |
| chip::SetupPayloadData::AdditionalDataPayload resultPayload; |
| NL_TEST_ASSERT(inSuite, |
| ParseAdditionalDataPayload(kAdditionalDataPayloadWithShortRotatingDeviceId, |
| strlen(kAdditionalDataPayloadWithShortRotatingDeviceId), |
| resultPayload) == CHIP_NO_ERROR); |
| NL_TEST_ASSERT(inSuite, strcmp(resultPayload.rotatingDeviceId.c_str(), kShortRotatingDeviceId) == 0); |
| } |
| |
| /** |
| * Test Suite that lists all the Test functions. |
| */ |
| // clang-format off |
| const nlTest sTests[] = |
| { |
| NL_TEST_DEF("Test Generating Additional Data Payload without Rotatin gDevice Id", TestGeneratingAdditionalDataPayloadWithoutRotatingDeviceId), |
| #if CHIP_ENABLE_ROTATING_DEVICE_ID |
| NL_TEST_DEF("Test Generating Additional Data Payload with Rotating Device Id", TestGeneratingAdditionalDataPayloadWithRotatingDeviceId), |
| NL_TEST_DEF("Test Generating Additional Data Payload with Rotating Device Id + Max Lifetime Counter", TestGeneratingAdditionalDataPayloadWithRotatingDeviceIdAndMaxLifetimeCounter), |
| NL_TEST_DEF("Test Generating Additional Data Payload with Rotating Device Id + Null/Empty Inputs", TestGeneratingAdditionalDataPayloadWithRotatingDeviceIdWithNullInputs), |
| NL_TEST_DEF("Test Generating Rotating Device Id as string", TestGeneratingRotatingDeviceIdAsString), |
| NL_TEST_DEF("Test Generating Rotating Device Id as string with null/invalid inputs", TestGeneratingRotatingDeviceIdAsStringWithNullInputs), |
| NL_TEST_DEF("Test Generating Rotating Device Id as string with small buffer", TestGeneratingRotatingDeviceIdWithSmallBuffer), |
| #endif |
| NL_TEST_DEF("Test Parsing Additional Data Payload with Rotating Device Id", TestParsingAdditionalDataPayloadWithRotatingDeviceId), |
| NL_TEST_DEF("Test Parsing Additional Data Payload without Rotating Device Id", TestParsingAdditionalDataPayloadWithoutRotatingDeviceId), |
| NL_TEST_DEF("Test Parsing Additional Data Payload with Invalid Rotating Device Id Length", TestParsingAdditionalDataPayloadWithInvalidRotatingDeviceIdLength), |
| NL_TEST_DEF("Test Parsing Additional Data Payload with Long Rotating Device Id", TestParsingAdditionalDataPayloadWithLongRotatingDeviceId), |
| NL_TEST_DEF("Test Parsing Additional Data Payload with Short Rotating Device Id", TestParsingAdditionalDataPayloadWithShortRotatingDeviceId), |
| NL_TEST_SENTINEL() |
| }; |
| // clang-format on |
| |
| } // namespace |
| |
| /** |
| * Set up the test suite. |
| */ |
| int TestAdditionalDataPayload_Setup(void * inContext) |
| { |
| CHIP_ERROR error = chip::Platform::MemoryInit(); |
| if (error != CHIP_NO_ERROR) |
| return FAILURE; |
| return SUCCESS; |
| } |
| |
| /** |
| * Tear down the test suite. |
| */ |
| int TestAdditionalDataPayload_Teardown(void * inContext) |
| { |
| chip::Platform::MemoryShutdown(); |
| return SUCCESS; |
| } |
| |
| /** |
| * Main |
| */ |
| int TestAdditionalDataPayload() |
| { |
| // clang-format off |
| nlTestSuite theSuite = |
| { |
| "chip-additional-data-payload-general-Tests", |
| &sTests[0], |
| TestAdditionalDataPayload_Setup, |
| TestAdditionalDataPayload_Teardown |
| }; |
| // clang-format on |
| |
| // Generate machine-readable, comma-separated value (CSV) output. |
| nl_test_set_output_style(OUTPUT_CSV); |
| |
| return chip::ExecuteTestsWithoutContext(&theSuite); |
| } |
| |
| CHIP_REGISTER_TEST_SUITE(TestAdditionalDataPayload); |