| /** |
| * |
| * 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 |