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 :
28 : #include <lib/support/logging/CHIPLogging.h>
29 : #include <lib/support/verhoeff/Verhoeff.h>
30 :
31 : namespace chip {
32 :
33 12 : static uint32_t chunk1PayloadRepresentation(const PayloadContents & payload)
34 : {
35 : /* <1 digit> Represents:
36 : * - <bits 1..0> Discriminator <bits 11.10>
37 : * - <bit 2> VID/PID present flag
38 : */
39 :
40 12 : constexpr int kDiscriminatorShift = (kManualSetupDiscriminatorFieldLengthInBits - kManualSetupChunk1DiscriminatorMsbitsLength);
41 12 : constexpr uint32_t kDiscriminatorMask = (1 << kManualSetupChunk1DiscriminatorMsbitsLength) - 1;
42 :
43 : static_assert(kManualSetupChunk1VidPidPresentBitPos >=
44 : kManualSetupChunk1DiscriminatorMsbitsPos + kManualSetupChunk1DiscriminatorMsbitsLength,
45 : "Discriminator won't fit");
46 :
47 12 : uint32_t discriminatorChunk = (payload.discriminator.GetShortValue() >> kDiscriminatorShift) & kDiscriminatorMask;
48 12 : uint32_t vidPidPresentFlag = payload.commissioningFlow != CommissioningFlow::kStandard ? 1 : 0;
49 :
50 12 : uint32_t result = (discriminatorChunk << kManualSetupChunk1DiscriminatorMsbitsPos) |
51 12 : (vidPidPresentFlag << kManualSetupChunk1VidPidPresentBitPos);
52 :
53 12 : return result;
54 : }
55 :
56 12 : static uint32_t chunk2PayloadRepresentation(const PayloadContents & payload)
57 : {
58 : /* <5 digits> Represents:
59 : * - <bits 13..0> PIN Code <bits 13..0>
60 : * - <bits 15..14> Discriminator <bits 9..8>
61 : */
62 :
63 12 : constexpr uint32_t kDiscriminatorMask = (1 << kManualSetupChunk2DiscriminatorLsbitsLength) - 1;
64 12 : constexpr uint32_t kPincodeMask = (1 << kManualSetupChunk2PINCodeLsbitsLength) - 1;
65 :
66 12 : uint32_t discriminatorChunk = payload.discriminator.GetShortValue() & kDiscriminatorMask;
67 :
68 12 : uint32_t result = ((payload.setUpPINCode & kPincodeMask) << kManualSetupChunk2PINCodeLsbitsPos) |
69 12 : (discriminatorChunk << kManualSetupChunk2DiscriminatorLsbitsPos);
70 :
71 12 : return result;
72 : }
73 :
74 12 : static uint32_t chunk3PayloadRepresentation(const PayloadContents & payload)
75 : {
76 : /* <4 digits> Represents:
77 : * - <bits 12..0> PIN Code <bits 26..14>
78 : */
79 :
80 12 : constexpr int kPincodeShift = (kSetupPINCodeFieldLengthInBits - kManualSetupChunk3PINCodeMsbitsLength);
81 12 : constexpr uint32_t kPincodeMask = (1 << kManualSetupChunk3PINCodeMsbitsLength) - 1;
82 :
83 12 : uint32_t result = ((payload.setUpPINCode >> kPincodeShift) & kPincodeMask) << kManualSetupChunk3PINCodeMsbitsPos;
84 :
85 12 : return result;
86 : }
87 :
88 62 : static CHIP_ERROR decimalStringWithPadding(MutableCharSpan buffer, uint32_t number)
89 : {
90 62 : int len = static_cast<int>(buffer.size() - 1);
91 62 : int retval = snprintf(buffer.data(), buffer.size(), "%0*" PRIu32, len, number);
92 :
93 62 : return (retval >= static_cast<int>(buffer.size())) ? CHIP_ERROR_BUFFER_TOO_SMALL : CHIP_NO_ERROR;
94 : }
95 :
96 13 : CHIP_ERROR ManualSetupPayloadGenerator::payloadDecimalStringRepresentation(MutableCharSpan & outBuffer)
97 : {
98 : static_assert(kManualSetupCodeChunk1CharLength + kManualSetupCodeChunk2CharLength + kManualSetupCodeChunk3CharLength ==
99 : kManualSetupShortCodeCharLength,
100 : "Manual code length mismatch (short)");
101 : static_assert(kManualSetupShortCodeCharLength + kManualSetupVendorIdCharLength + kManualSetupProductIdCharLength ==
102 : kManualSetupLongCodeCharLength,
103 : "Manual code length mismatch (long)");
104 : static_assert(kManualSetupChunk1DiscriminatorMsbitsLength + kManualSetupChunk2DiscriminatorLsbitsLength ==
105 : kManualSetupDiscriminatorFieldLengthInBits,
106 : "Discriminator won't fit");
107 : static_assert(kManualSetupChunk2PINCodeLsbitsLength + kManualSetupChunk3PINCodeMsbitsLength == kSetupPINCodeFieldLengthInBits,
108 : "PIN code won't fit");
109 :
110 13 : if (!mAllowInvalidPayload && !mPayloadContents.isValidManualCode())
111 : {
112 1 : ChipLogError(SetupPayload, "Failed encoding invalid payload");
113 1 : return CHIP_ERROR_INVALID_ARGUMENT;
114 : }
115 :
116 12 : bool useLongCode = (mPayloadContents.commissioningFlow != CommissioningFlow::kStandard) && !mForceShortCode;
117 :
118 : // Add two for the check digit and null terminator.
119 24 : if ((useLongCode && outBuffer.size() < kManualSetupLongCodeCharLength + 2) ||
120 12 : (!useLongCode && outBuffer.size() < kManualSetupShortCodeCharLength + 2))
121 : {
122 0 : ChipLogError(SetupPayload, "Failed encoding payload to buffer");
123 0 : return CHIP_ERROR_BUFFER_TOO_SMALL;
124 : }
125 :
126 12 : uint32_t chunk1 = chunk1PayloadRepresentation(mPayloadContents);
127 12 : uint32_t chunk2 = chunk2PayloadRepresentation(mPayloadContents);
128 12 : uint32_t chunk3 = chunk3PayloadRepresentation(mPayloadContents);
129 :
130 12 : size_t offset = 0;
131 :
132 : // Add one to the length of each chunk, since snprintf writes a null terminator.
133 12 : ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupCodeChunk1CharLength + 1), chunk1));
134 12 : offset += kManualSetupCodeChunk1CharLength;
135 12 : ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupCodeChunk2CharLength + 1), chunk2));
136 12 : offset += kManualSetupCodeChunk2CharLength;
137 12 : ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupCodeChunk3CharLength + 1), chunk3));
138 12 : offset += kManualSetupCodeChunk3CharLength;
139 :
140 12 : if (useLongCode)
141 : {
142 7 : ReturnErrorOnFailure(
143 : decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupVendorIdCharLength + 1), mPayloadContents.vendorID));
144 7 : offset += kManualSetupVendorIdCharLength;
145 7 : ReturnErrorOnFailure(
146 : decimalStringWithPadding(outBuffer.SubSpan(offset, kManualSetupProductIdCharLength + 1), mPayloadContents.productID));
147 7 : offset += kManualSetupProductIdCharLength;
148 : }
149 :
150 12 : int checkDigit = Verhoeff10::CharToVal(Verhoeff10::ComputeCheckChar(outBuffer.data()));
151 12 : ReturnErrorOnFailure(decimalStringWithPadding(outBuffer.SubSpan(offset, 2), static_cast<uint32_t>(checkDigit)));
152 12 : offset += 1;
153 :
154 : // Reduce outBuffer span size to be the size of written data and to not include null-terminator.
155 12 : outBuffer.reduce_size(offset);
156 :
157 12 : return CHIP_NO_ERROR;
158 : }
159 :
160 13 : CHIP_ERROR ManualSetupPayloadGenerator::payloadDecimalStringRepresentation(std::string & outDecimalString)
161 : {
162 : // One extra char for the check digit, another for the null terminator.
163 13 : char decimalString[kManualSetupLongCodeCharLength + 1 + 1] = "";
164 13 : MutableCharSpan outBuffer(decimalString);
165 :
166 13 : ReturnErrorOnFailure(payloadDecimalStringRepresentation(outBuffer));
167 12 : outDecimalString.assign(decimalString);
168 :
169 12 : return CHIP_NO_ERROR;
170 : }
171 :
172 : } // namespace chip
|