|  | /** | 
|  | * | 
|  | *    Copyright (c) 2020 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 describes a Manual Entry Code Generator. | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include "ManualSetupPayloadGenerator.h" | 
|  |  | 
|  | #include <inttypes.h> | 
|  | #include <limits> | 
|  |  | 
|  | #include <lib/support/logging/CHIPLogging.h> | 
|  | #include <lib/support/verhoeff/Verhoeff.h> | 
|  |  | 
|  | namespace chip { | 
|  |  | 
|  | static uint32_t chunk1PayloadRepresentation(const PayloadContents & payload) | 
|  | { | 
|  | /* <1 digit> Represents: | 
|  | *     - <bits 1..0> Discriminator <bits 11.10> | 
|  | *     - <bit 2> VID/PID present flag | 
|  | */ | 
|  |  | 
|  | constexpr int kDiscriminatorShift = (kManualSetupDiscriminatorFieldLengthInBits - kManualSetupChunk1DiscriminatorMsbitsLength); | 
|  | constexpr uint32_t kDiscriminatorMask = (1 << kManualSetupChunk1DiscriminatorMsbitsLength) - 1; | 
|  |  | 
|  | static_assert(kManualSetupChunk1VidPidPresentBitPos >= | 
|  | kManualSetupChunk1DiscriminatorMsbitsPos + kManualSetupChunk1DiscriminatorMsbitsLength, | 
|  | "Discriminator won't fit"); | 
|  |  | 
|  | uint32_t discriminatorChunk = (payload.discriminator.GetShortValue() >> kDiscriminatorShift) & kDiscriminatorMask; | 
|  | uint32_t vidPidPresentFlag  = payload.commissioningFlow != CommissioningFlow::kStandard ? 1 : 0; | 
|  |  | 
|  | uint32_t result = (discriminatorChunk << kManualSetupChunk1DiscriminatorMsbitsPos) | | 
|  | (vidPidPresentFlag << kManualSetupChunk1VidPidPresentBitPos); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static uint32_t chunk2PayloadRepresentation(const PayloadContents & payload) | 
|  | { | 
|  | /* <5 digits> Represents: | 
|  | *     - <bits 13..0> PIN Code <bits 13..0> | 
|  | *     - <bits 15..14> Discriminator <bits 9..8> | 
|  | */ | 
|  |  | 
|  | constexpr uint32_t kDiscriminatorMask = (1 << kManualSetupChunk2DiscriminatorLsbitsLength) - 1; | 
|  | constexpr uint32_t kPincodeMask       = (1 << kManualSetupChunk2PINCodeLsbitsLength) - 1; | 
|  |  | 
|  | uint32_t discriminatorChunk = payload.discriminator.GetShortValue() & kDiscriminatorMask; | 
|  |  | 
|  | uint32_t result = ((payload.setUpPINCode & kPincodeMask) << kManualSetupChunk2PINCodeLsbitsPos) | | 
|  | (discriminatorChunk << kManualSetupChunk2DiscriminatorLsbitsPos); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static uint32_t chunk3PayloadRepresentation(const PayloadContents & payload) | 
|  | { | 
|  | /* <4 digits> Represents: | 
|  | *     - <bits 12..0> PIN Code <bits 26..14> | 
|  | */ | 
|  |  | 
|  | constexpr int kPincodeShift     = (kSetupPINCodeFieldLengthInBits - kManualSetupChunk3PINCodeMsbitsLength); | 
|  | constexpr uint32_t kPincodeMask = (1 << kManualSetupChunk3PINCodeMsbitsLength) - 1; | 
|  |  | 
|  | uint32_t result = ((payload.setUpPINCode >> kPincodeShift) & kPincodeMask) << kManualSetupChunk3PINCodeMsbitsPos; | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static CHIP_ERROR decimalStringWithPadding(MutableCharSpan buffer, uint32_t number) | 
|  | { | 
|  | int len    = static_cast<int>(buffer.size() - 1); | 
|  | int retval = snprintf(buffer.data(), buffer.size(), "%0*" PRIu32, len, number); | 
|  |  | 
|  | return (retval >= static_cast<int>(buffer.size())) ? CHIP_ERROR_BUFFER_TOO_SMALL : CHIP_NO_ERROR; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ManualSetupPayloadGenerator::payloadDecimalStringRepresentation(MutableCharSpan & outBuffer) | 
|  | { | 
|  | static_assert(kManualSetupCodeChunk1CharLength + kManualSetupCodeChunk2CharLength + kManualSetupCodeChunk3CharLength == | 
|  | kManualSetupShortCodeCharLength, | 
|  | "Manual code length mismatch (short)"); | 
|  | static_assert(kManualSetupShortCodeCharLength + kManualSetupVendorIdCharLength + kManualSetupProductIdCharLength == | 
|  | kManualSetupLongCodeCharLength, | 
|  | "Manual code length mismatch (long)"); | 
|  | static_assert(kManualSetupChunk1DiscriminatorMsbitsLength + kManualSetupChunk2DiscriminatorLsbitsLength == | 
|  | kManualSetupDiscriminatorFieldLengthInBits, | 
|  | "Discriminator won't fit"); | 
|  | static_assert(kManualSetupChunk2PINCodeLsbitsLength + kManualSetupChunk3PINCodeMsbitsLength == kSetupPINCodeFieldLengthInBits, | 
|  | "PIN code won't fit"); | 
|  |  | 
|  | if (!mAllowInvalidPayload && !mPayloadContents.isValidManualCode()) | 
|  | { | 
|  | ChipLogError(SetupPayload, "Failed encoding invalid payload"); | 
|  | return CHIP_ERROR_INVALID_ARGUMENT; | 
|  | } | 
|  |  | 
|  | bool useLongCode = (mPayloadContents.commissioningFlow != CommissioningFlow::kStandard) && !mForceShortCode; | 
|  |  | 
|  | // Add two for the check digit and null terminator. | 
|  | if ((useLongCode && outBuffer.size() < kManualSetupLongCodeCharLength + 2) || | 
|  | (!useLongCode && outBuffer.size() < kManualSetupShortCodeCharLength + 2)) | 
|  | { | 
|  | ChipLogError(SetupPayload, "Failed encoding payload to buffer"); | 
|  | return CHIP_ERROR_BUFFER_TOO_SMALL; | 
|  | } | 
|  |  | 
|  | uint32_t chunk1 = chunk1PayloadRepresentation(mPayloadContents); | 
|  | uint32_t chunk2 = chunk2PayloadRepresentation(mPayloadContents); | 
|  | uint32_t chunk3 = chunk3PayloadRepresentation(mPayloadContents); | 
|  |  | 
|  | size_t offset = 0; | 
|  |  | 
|  | // Add one to the length of each chunk, since snprintf writes a null terminator. | 
|  | ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupCodeChunk1CharLength + 1), chunk1)); | 
|  | offset += kManualSetupCodeChunk1CharLength; | 
|  | ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupCodeChunk2CharLength + 1), chunk2)); | 
|  | offset += kManualSetupCodeChunk2CharLength; | 
|  | ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupCodeChunk3CharLength + 1), chunk3)); | 
|  | offset += kManualSetupCodeChunk3CharLength; | 
|  |  | 
|  | if (useLongCode) | 
|  | { | 
|  | ReturnErrorOnFailure( | 
|  | decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupVendorIdCharLength + 1), mPayloadContents.vendorID)); | 
|  | offset += kManualSetupVendorIdCharLength; | 
|  | ReturnErrorOnFailure( | 
|  | decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupProductIdCharLength + 1), mPayloadContents.productID)); | 
|  | offset += kManualSetupProductIdCharLength; | 
|  | } | 
|  |  | 
|  | int checkDigit = Verhoeff10::CharToVal(Verhoeff10::ComputeCheckChar(outBuffer.data())); | 
|  | ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, 2), static_cast<uint32_t>(checkDigit))); | 
|  | offset += 1; | 
|  |  | 
|  | // Reduce outBuffer span size to be the size of written data and to not include null-terminator. | 
|  | outBuffer.reduce_size(offset); | 
|  |  | 
|  | return CHIP_NO_ERROR; | 
|  | } | 
|  |  | 
|  | CHIP_ERROR ManualSetupPayloadGenerator::payloadDecimalStringRepresentation(std::string & outDecimalString) | 
|  | { | 
|  | // One extra char for the check digit, another for the null terminator. | 
|  | char decimalString[kManualSetupLongCodeCharLength + 1 + 1] = ""; | 
|  | MutableCharSpan outBuffer(decimalString); | 
|  |  | 
|  | ReturnErrorOnFailure(payloadDecimalStringRepresentation(outBuffer)); | 
|  | outDecimalString.assign(decimalString); | 
|  |  | 
|  | return CHIP_NO_ERROR; | 
|  | } | 
|  |  | 
|  | } // namespace chip |