Skip to content

Commit fcfc9bc

Browse files
Fix handling of short discriminator in QRCodeSetupPayloadGenerator (project-chip#33250)
* Fix handling of short discriminator in QRCodeSetupPayloadGenerator If a SetupPayload contains a short discriminator then - isValidQRCodePayload() should return false - trying to generate a QR Code should return INVALID_ARGUMENT - generating with SetAllowInvalidPayload(true) should work (not die) * SetupPayload tweaks Make IsCommonTag, IsVendorTag, and getOptionalVendorData(tag, &info) public. The first two are just encoding spec rules that are useful for clients, and the latter allows clients to read vendor data by tag instead of having to read the whole list. Also use default values instead of explicit constructors for OptionalQRCodeInfo and fix up some doc comments to correctly reference the vendor tag range. * Address review comments Elaborate on the use case for AllowInvalidPayload in the comments, and encode a missing long discriminator as 0, to avoid a client encoding invalid payloads from relying on round-tripping a short discriminator through a QR code. * Handle rendezvousInformation and discriminator the same way * Address review comment: use ValueOr
1 parent ddc06d4 commit fcfc9bc

File tree

4 files changed

+72
-45
lines changed

4 files changed

+72
-45
lines changed

src/setup_payload/QRCodeSetupPayloadGenerator.cpp

+9-3
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ static CHIP_ERROR generateBitSet(PayloadContents & payload, MutableByteSpan & bi
162162
size_t totalPayloadSizeInBits = kTotalPayloadDataSizeInBits + (tlvDataLengthInBytes * 8);
163163
VerifyOrReturnError(bits.size() * 8 >= totalPayloadSizeInBits, CHIP_ERROR_BUFFER_TOO_SMALL);
164164

165+
// isValidQRCodePayload() has already performed all relevant checks (including that we have a
166+
// long discriminator and rendevouz information). But if AllowInvalidPayload is set these
167+
// requirements might be violated; in that case simply encode 0 for the relevant fields.
168+
// Encoding an invalid (or partially valid) payload is useful for clients that need to be able
169+
// to serialize and deserialize partially populated or invalid payloads.
165170
ReturnErrorOnFailure(
166171
populateBits(bits.data(), offset, payload.version, kVersionFieldLengthInBits, kTotalPayloadDataSizeInBits));
167172
ReturnErrorOnFailure(
@@ -170,10 +175,11 @@ static CHIP_ERROR generateBitSet(PayloadContents & payload, MutableByteSpan & bi
170175
populateBits(bits.data(), offset, payload.productID, kProductIDFieldLengthInBits, kTotalPayloadDataSizeInBits));
171176
ReturnErrorOnFailure(populateBits(bits.data(), offset, static_cast<uint64_t>(payload.commissioningFlow),
172177
kCommissioningFlowFieldLengthInBits, kTotalPayloadDataSizeInBits));
173-
VerifyOrReturnError(payload.rendezvousInformation.HasValue(), CHIP_ERROR_INVALID_ARGUMENT);
174-
ReturnErrorOnFailure(populateBits(bits.data(), offset, payload.rendezvousInformation.Value().Raw(),
178+
ReturnErrorOnFailure(populateBits(bits.data(), offset,
179+
payload.rendezvousInformation.ValueOr(RendezvousInformationFlag::kNone).Raw(),
175180
kRendezvousInfoFieldLengthInBits, kTotalPayloadDataSizeInBits));
176-
ReturnErrorOnFailure(populateBits(bits.data(), offset, payload.discriminator.GetLongValue(),
181+
auto const & pd = payload.discriminator;
182+
ReturnErrorOnFailure(populateBits(bits.data(), offset, (!pd.IsShortDiscriminator() ? pd.GetLongValue() : 0),
177183
kPayloadDiscriminatorFieldLengthInBits, kTotalPayloadDataSizeInBits));
178184
ReturnErrorOnFailure(
179185
populateBits(bits.data(), offset, payload.setUpPINCode, kSetupPINCodeFieldLengthInBits, kTotalPayloadDataSizeInBits));

src/setup_payload/SetupPayload.cpp

+5-13
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,6 @@
3333

3434
namespace chip {
3535

36-
// Spec 5.1.4.2 CHIPCommon tag numbers are in the range [0x00, 0x7F]
37-
bool SetupPayload::IsCommonTag(uint8_t tag)
38-
{
39-
return tag < 0x80;
40-
}
41-
42-
// Spec 5.1.4.1 Manufacture-specific tag numbers are in the range [0x80, 0xFF]
43-
bool SetupPayload::IsVendorTag(uint8_t tag)
44-
{
45-
return !IsCommonTag(tag);
46-
}
47-
4836
// Check the Setup Payload for validity
4937
//
5038
// `vendor_id` and `product_id` are allowed all of uint16_t
@@ -79,7 +67,11 @@ bool PayloadContents::isValidQRCodePayload() const
7967
return false;
8068
}
8169

82-
// Discriminator validity is enforced by the SetupDiscriminator class.
70+
// General discriminator validity is enforced by the SetupDiscriminator class, but it can't be short for QR a code.
71+
if (discriminator.IsShortDiscriminator())
72+
{
73+
return false;
74+
}
8375

8476
if (setUpPINCode >= 1 << kSetupPINCodeFieldLengthInBits)
8577
{

src/setup_payload/SetupPayload.h

+20-29
Original file line numberDiff line numberDiff line change
@@ -153,29 +153,19 @@ enum optionalQRCodeInfoType
153153
*/
154154
struct OptionalQRCodeInfo
155155
{
156-
OptionalQRCodeInfo() { int32 = 0; }
157-
158156
/*@{*/
159157
uint8_t tag; /**< the tag number of the optional info */
160158
enum optionalQRCodeInfoType type; /**< the type (String or Int) of the optional info */
161159
std::string data; /**< the string value if type is optionalQRCodeInfoTypeString, otherwise should not be set */
162-
int32_t int32; /**< the integer value if type is optionalQRCodeInfoTypeInt, otherwise should not be set */
160+
int32_t int32 = 0; /**< the integer value if type is optionalQRCodeInfoTypeInt32, otherwise should not be set */
163161
/*@}*/
164162
};
165163

166164
struct OptionalQRCodeInfoExtension : OptionalQRCodeInfo
167165
{
168-
OptionalQRCodeInfoExtension()
169-
{
170-
int32 = 0;
171-
int64 = 0;
172-
uint32 = 0;
173-
uint64 = 0;
174-
}
175-
176-
int64_t int64;
177-
uint64_t uint32;
178-
uint64_t uint64;
166+
int64_t int64 = 0; /**< the integer value if type is optionalQRCodeInfoTypeInt64, otherwise should not be set */
167+
uint64_t uint32 = 0; /**< the integer value if type is optionalQRCodeInfoTypeUInt32, otherwise should not be set */
168+
uint64_t uint64 = 0; /**< the integer value if type is optionalQRCodeInfoTypeUInt64, otherwise should not be set */
179169
};
180170

181171
class SetupPayload : public PayloadContents
@@ -193,17 +183,25 @@ class SetupPayload : public PayloadContents
193183
CHIP_ERROR addOptionalVendorData(uint8_t tag, std::string data);
194184

195185
/** @brief A function to add an optional vendor data
196-
* @param tag 7 bit [0-127] tag number
186+
* @param tag tag number in the [0x80-0xFF] range
197187
* @param data Integer representation of data to add
198188
* @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise
199189
**/
200190
CHIP_ERROR addOptionalVendorData(uint8_t tag, int32_t data);
201191

202192
/** @brief A function to remove an optional vendor data
203-
* @param tag 7 bit [0-127] tag number
193+
* @param tag tag number in the [0x80-0xFF] range
204194
* @return Returns a CHIP_ERROR_KEY_NOT_FOUND on error, CHIP_NO_ERROR otherwise
205195
**/
206196
CHIP_ERROR removeOptionalVendorData(uint8_t tag);
197+
198+
/** @brief A function to retrieve an optional QR Code info vendor object
199+
* @param tag tag number in the [0x80-0xFF] range
200+
* @param info retrieved OptionalQRCodeInfo object
201+
* @return Returns a CHIP_ERROR_KEY_NOT_FOUND on error, CHIP_NO_ERROR otherwise
202+
**/
203+
CHIP_ERROR getOptionalVendorData(uint8_t tag, OptionalQRCodeInfo & info) const;
204+
207205
/**
208206
* @brief A function to retrieve the vector of OptionalQRCodeInfo infos
209207
* @return Returns a vector of optionalQRCodeInfos
@@ -235,21 +233,21 @@ class SetupPayload : public PayloadContents
235233

236234
bool operator==(const SetupPayload & input) const;
237235

238-
private:
239-
std::map<uint8_t, OptionalQRCodeInfo> optionalVendorData;
240-
std::map<uint8_t, OptionalQRCodeInfoExtension> optionalExtensionData;
241-
242236
/** @brief Checks if the tag is CHIP Common type
243237
* @param tag Tag to be checked
244238
* @return Returns True if the tag is of Common type
245239
**/
246-
static bool IsCommonTag(uint8_t tag);
240+
static bool IsCommonTag(uint8_t tag) { return tag < 0x80; }
247241

248242
/** @brief Checks if the tag is vendor-specific
249243
* @param tag Tag to be checked
250244
* @return Returns True if the tag is Vendor-specific
251245
**/
252-
static bool IsVendorTag(uint8_t tag);
246+
static bool IsVendorTag(uint8_t tag) { return !IsCommonTag(tag); }
247+
248+
private:
249+
std::map<uint8_t, OptionalQRCodeInfo> optionalVendorData;
250+
std::map<uint8_t, OptionalQRCodeInfoExtension> optionalExtensionData;
253251

254252
/** @brief A function to add an optional QR Code info vendor object
255253
* @param info Optional QR code info object to add
@@ -269,13 +267,6 @@ class SetupPayload : public PayloadContents
269267
**/
270268
std::vector<OptionalQRCodeInfoExtension> getAllOptionalExtensionData() const;
271269

272-
/** @brief A function to retrieve an optional QR Code info vendor object
273-
* @param tag 7 bit [0-127] tag number
274-
* @param info retrieved OptionalQRCodeInfo object
275-
* @return Returns a CHIP_ERROR_KEY_NOT_FOUND on error, CHIP_NO_ERROR otherwise
276-
**/
277-
CHIP_ERROR getOptionalVendorData(uint8_t tag, OptionalQRCodeInfo & info) const;
278-
279270
/** @brief A function to retrieve an optional QR Code info extended object
280271
* @param tag 8 bit [128-255] tag number
281272
* @param info retrieved OptionalQRCodeInfoExtension object

src/setup_payload/tests/TestQRCode.cpp

+38
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,44 @@ TEST(TestQRCode, TestQRCodeToPayloadGeneration)
381381
EXPECT_EQ(result, true);
382382
}
383383

384+
TEST(TestQRCode, TestGenerateWithShortDiscriminatorInvalid)
385+
{
386+
SetupPayload payload = GetDefaultPayload();
387+
EXPECT_TRUE(payload.isValidQRCodePayload());
388+
389+
// A short discriminator isn't valid for a QR Code
390+
payload.discriminator.SetShortValue(1);
391+
EXPECT_FALSE(payload.isValidQRCodePayload());
392+
393+
// QRCodeSetupPayloadGenerator should therefore return an error
394+
string base38Rep;
395+
QRCodeSetupPayloadGenerator generator(payload);
396+
EXPECT_EQ(generator.payloadBase38Representation(base38Rep), CHIP_ERROR_INVALID_ARGUMENT);
397+
398+
// If we allow invalid payloads we should be able to encode
399+
generator.SetAllowInvalidPayload(true);
400+
EXPECT_EQ(generator.payloadBase38Representation(base38Rep), CHIP_NO_ERROR);
401+
}
402+
403+
TEST(TestQRCode, TestGenerateWithoutRendezvousInformation)
404+
{
405+
SetupPayload payload = GetDefaultPayload();
406+
EXPECT_TRUE(payload.isValidQRCodePayload());
407+
408+
// Rendezvouz Information is required for a QR code
409+
payload.rendezvousInformation.ClearValue();
410+
EXPECT_FALSE(payload.isValidQRCodePayload());
411+
412+
// QRCodeSetupPayloadGenerator should therefore return an error
413+
string base38Rep;
414+
QRCodeSetupPayloadGenerator generator(payload);
415+
EXPECT_EQ(generator.payloadBase38Representation(base38Rep), CHIP_ERROR_INVALID_ARGUMENT);
416+
417+
// If we allow invalid payloads we should be able to encode
418+
generator.SetAllowInvalidPayload(true);
419+
EXPECT_EQ(generator.payloadBase38Representation(base38Rep), CHIP_NO_ERROR);
420+
}
421+
384422
TEST(TestQRCode, TestExtractPayload)
385423
{
386424
EXPECT_EQ(QRCodeSetupPayloadParser::ExtractPayload(string("MT:ABC")), string("ABC"));

0 commit comments

Comments
 (0)