Line data Source code
1 : /** 2 : * 3 : * Copyright (c) 2020 Project CHIP Authors 4 : * 5 : * Licensed under the Apache License, Version 2.0 (the "License"); 6 : * you may not use this file except in compliance with the License. 7 : * You may obtain a copy of the License at 8 : * 9 : * http://www.apache.org/licenses/LICENSE-2.0 10 : * 11 : * Unless required by applicable law or agreed to in writing, software 12 : * distributed under the License is distributed on an "AS IS" BASIS, 13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 : * See the License for the specific language governing permissions and 15 : * limitations under the License. 16 : */ 17 : 18 : /** 19 : * @file 20 : * This file describes a Manual Entry Code Generator. 21 : * 22 : */ 23 : 24 : #include "ManualSetupPayloadGenerator.h" 25 : 26 : #include <inttypes.h> 27 : #include <limits> 28 : 29 : #include <lib/support/logging/CHIPLogging.h> 30 : #include <lib/support/verhoeff/Verhoeff.h> 31 : 32 : namespace chip { 33 : 34 12 : static uint32_t chunk1PayloadRepresentation(const PayloadContents & payload) 35 : { 36 : /* <1 digit> Represents: 37 : * - <bits 1..0> Discriminator <bits 11.10> 38 : * - <bit 2> VID/PID present flag 39 : */ 40 : 41 12 : constexpr int kDiscriminatorShift = (kManualSetupDiscriminatorFieldLengthInBits - kManualSetupChunk1DiscriminatorMsbitsLength); 42 12 : constexpr uint32_t kDiscriminatorMask = (1 << kManualSetupChunk1DiscriminatorMsbitsLength) - 1; 43 : 44 : static_assert(kManualSetupChunk1VidPidPresentBitPos >= 45 : kManualSetupChunk1DiscriminatorMsbitsPos + kManualSetupChunk1DiscriminatorMsbitsLength, 46 : "Discriminator won't fit"); 47 : 48 12 : uint32_t discriminatorChunk = (payload.discriminator.GetShortValue() >> kDiscriminatorShift) & kDiscriminatorMask; 49 12 : uint32_t vidPidPresentFlag = payload.commissioningFlow != CommissioningFlow::kStandard ? 1 : 0; 50 : 51 12 : uint32_t result = (discriminatorChunk << kManualSetupChunk1DiscriminatorMsbitsPos) | 52 12 : (vidPidPresentFlag << kManualSetupChunk1VidPidPresentBitPos); 53 : 54 12 : return result; 55 : } 56 : 57 12 : static uint32_t chunk2PayloadRepresentation(const PayloadContents & payload) 58 : { 59 : /* <5 digits> Represents: 60 : * - <bits 13..0> PIN Code <bits 13..0> 61 : * - <bits 15..14> Discriminator <bits 9..8> 62 : */ 63 : 64 12 : constexpr uint32_t kDiscriminatorMask = (1 << kManualSetupChunk2DiscriminatorLsbitsLength) - 1; 65 12 : constexpr uint32_t kPincodeMask = (1 << kManualSetupChunk2PINCodeLsbitsLength) - 1; 66 : 67 12 : uint32_t discriminatorChunk = payload.discriminator.GetShortValue() & kDiscriminatorMask; 68 : 69 12 : uint32_t result = ((payload.setUpPINCode & kPincodeMask) << kManualSetupChunk2PINCodeLsbitsPos) | 70 12 : (discriminatorChunk << kManualSetupChunk2DiscriminatorLsbitsPos); 71 : 72 12 : return result; 73 : } 74 : 75 12 : static uint32_t chunk3PayloadRepresentation(const PayloadContents & payload) 76 : { 77 : /* <4 digits> Represents: 78 : * - <bits 12..0> PIN Code <bits 26..14> 79 : */ 80 : 81 12 : constexpr int kPincodeShift = (kSetupPINCodeFieldLengthInBits - kManualSetupChunk3PINCodeMsbitsLength); 82 12 : constexpr uint32_t kPincodeMask = (1 << kManualSetupChunk3PINCodeMsbitsLength) - 1; 83 : 84 12 : uint32_t result = ((payload.setUpPINCode >> kPincodeShift) & kPincodeMask) << kManualSetupChunk3PINCodeMsbitsPos; 85 : 86 12 : return result; 87 : } 88 : 89 62 : static CHIP_ERROR decimalStringWithPadding(MutableCharSpan buffer, uint32_t number) 90 : { 91 62 : int len = static_cast<int>(buffer.size() - 1); 92 62 : int retval = snprintf(buffer.data(), buffer.size(), "%0*" PRIu32, len, number); 93 : 94 62 : return (retval >= static_cast<int>(buffer.size())) ? CHIP_ERROR_BUFFER_TOO_SMALL : CHIP_NO_ERROR; 95 : } 96 : 97 13 : CHIP_ERROR ManualSetupPayloadGenerator::payloadDecimalStringRepresentation(MutableCharSpan & outBuffer) 98 : { 99 : static_assert(kManualSetupCodeChunk1CharLength + kManualSetupCodeChunk2CharLength + kManualSetupCodeChunk3CharLength == 100 : kManualSetupShortCodeCharLength, 101 : "Manual code length mismatch (short)"); 102 : static_assert(kManualSetupShortCodeCharLength + kManualSetupVendorIdCharLength + kManualSetupProductIdCharLength == 103 : kManualSetupLongCodeCharLength, 104 : "Manual code length mismatch (long)"); 105 : static_assert(kManualSetupChunk1DiscriminatorMsbitsLength + kManualSetupChunk2DiscriminatorLsbitsLength == 106 : kManualSetupDiscriminatorFieldLengthInBits, 107 : "Discriminator won't fit"); 108 : static_assert(kManualSetupChunk2PINCodeLsbitsLength + kManualSetupChunk3PINCodeMsbitsLength == kSetupPINCodeFieldLengthInBits, 109 : "PIN code won't fit"); 110 : 111 13 : if (!mAllowInvalidPayload && !mPayloadContents.isValidManualCode()) 112 : { 113 1 : ChipLogError(SetupPayload, "Failed encoding invalid payload"); 114 1 : return CHIP_ERROR_INVALID_ARGUMENT; 115 : } 116 : 117 12 : bool useLongCode = (mPayloadContents.commissioningFlow != CommissioningFlow::kStandard) && !mForceShortCode; 118 : 119 : // Add two for the check digit and null terminator. 120 24 : if ((useLongCode && outBuffer.size() < kManualSetupLongCodeCharLength + 2) || 121 12 : (!useLongCode && outBuffer.size() < kManualSetupShortCodeCharLength + 2)) 122 : { 123 0 : ChipLogError(SetupPayload, "Failed encoding payload to buffer"); 124 0 : return CHIP_ERROR_BUFFER_TOO_SMALL; 125 : } 126 : 127 12 : uint32_t chunk1 = chunk1PayloadRepresentation(mPayloadContents); 128 12 : uint32_t chunk2 = chunk2PayloadRepresentation(mPayloadContents); 129 12 : uint32_t chunk3 = chunk3PayloadRepresentation(mPayloadContents); 130 : 131 12 : size_t offset = 0; 132 : 133 : // Add one to the length of each chunk, since snprintf writes a null terminator. 134 12 : ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupCodeChunk1CharLength + 1), chunk1)); 135 12 : offset += kManualSetupCodeChunk1CharLength; 136 12 : ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupCodeChunk2CharLength + 1), chunk2)); 137 12 : offset += kManualSetupCodeChunk2CharLength; 138 12 : ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupCodeChunk3CharLength + 1), chunk3)); 139 12 : offset += kManualSetupCodeChunk3CharLength; 140 : 141 12 : if (useLongCode) 142 : { 143 7 : ReturnErrorOnFailure( 144 : decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupVendorIdCharLength + 1), mPayloadContents.vendorID)); 145 7 : offset += kManualSetupVendorIdCharLength; 146 7 : ReturnErrorOnFailure( 147 : decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupProductIdCharLength + 1), mPayloadContents.productID)); 148 7 : offset += kManualSetupProductIdCharLength; 149 : } 150 : 151 12 : int checkDigit = Verhoeff10::CharToVal(Verhoeff10::ComputeCheckChar(outBuffer.data())); 152 12 : ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, 2), static_cast<uint32_t>(checkDigit))); 153 12 : offset += 1; 154 : 155 : // Reduce outBuffer span size to be the size of written data and to not include null-terminator. 156 12 : outBuffer.reduce_size(offset); 157 : 158 12 : return CHIP_NO_ERROR; 159 : } 160 : 161 13 : CHIP_ERROR ManualSetupPayloadGenerator::payloadDecimalStringRepresentation(std::string & outDecimalString) 162 : { 163 : // One extra char for the check digit, another for the null terminator. 164 13 : char decimalString[kManualSetupLongCodeCharLength + 1 + 1] = ""; 165 13 : MutableCharSpan outBuffer(decimalString); 166 : 167 13 : ReturnErrorOnFailure(payloadDecimalStringRepresentation(outBuffer)); 168 12 : outDecimalString.assign(decimalString); 169 : 170 12 : return CHIP_NO_ERROR; 171 : } 172 : 173 : } // namespace chip