Skip to content

Commit 408f9fc

Browse files
Implement separate Produce and Consume SetupPayload validation modes (project-chip#33285)
* Implement separate Produce and Consume SetupPayload validation modes In Consume mode, unknown rendevouz flags are allowed. * Simplify the validation code by using VerifyOrReturnValue setUpPINCode range is checked in IsValidSetupPIN() via CheckPayloadCommonConstraints() so no separate check is needed. * Fix comment spelling. --------- Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
1 parent 881676d commit 408f9fc

File tree

3 files changed

+48
-54
lines changed

3 files changed

+48
-54
lines changed

src/setup_payload/SetupPayload.cpp

+26-52
Original file line numberDiff line numberDiff line change
@@ -36,60 +36,43 @@ namespace chip {
3636
// Check the Setup Payload for validity
3737
//
3838
// `vendor_id` and `product_id` are allowed all of uint16_t
39-
bool PayloadContents::isValidQRCodePayload() const
39+
bool PayloadContents::isValidQRCodePayload(ValidationMode mode) const
4040
{
4141
// 3-bit value specifying the QR code payload version.
42-
if (version >= 1 << kVersionFieldLengthInBits)
43-
{
44-
return false;
45-
}
42+
VerifyOrReturnValue(version < (1 << kVersionFieldLengthInBits), false);
4643

47-
if (static_cast<uint8_t>(commissioningFlow) > static_cast<uint8_t>((1 << kCommissioningFlowFieldLengthInBits) - 1))
48-
{
49-
return false;
50-
}
44+
VerifyOrReturnValue(static_cast<uint8_t>(commissioningFlow) < (1 << kCommissioningFlowFieldLengthInBits), false);
5145

5246
// Device Commissioning Flow
47+
// Even in ValidationMode::kConsume we can only handle modes that we understand.
5348
// 0: Standard commissioning flow: such a device, when uncommissioned, always enters commissioning mode upon power-up, subject
5449
// to the rules in [ref_Announcement_Commencement]. 1: User-intent commissioning flow: user action required to enter
5550
// commissioning mode. 2: Custom commissioning flow: interaction with a vendor-specified means is needed before commissioning.
5651
// 3: Reserved
57-
if (commissioningFlow != CommissioningFlow::kStandard && commissioningFlow != CommissioningFlow::kUserActionRequired &&
58-
commissioningFlow != CommissioningFlow::kCustom)
59-
{
60-
return false;
61-
}
62-
63-
chip::RendezvousInformationFlags allvalid(RendezvousInformationFlag::kBLE, RendezvousInformationFlag::kOnNetwork,
64-
RendezvousInformationFlag::kSoftAP);
65-
if (!rendezvousInformation.HasValue() || !rendezvousInformation.Value().HasOnly(allvalid))
66-
{
67-
return false;
68-
}
52+
VerifyOrReturnValue(commissioningFlow == CommissioningFlow::kStandard ||
53+
commissioningFlow == CommissioningFlow::kUserActionRequired ||
54+
commissioningFlow == CommissioningFlow::kCustom,
55+
false);
6956

7057
// 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-
}
58+
VerifyOrReturnValue(!discriminator.IsShortDiscriminator(), false);
7559

76-
if (setUpPINCode >= 1 << kSetupPINCodeFieldLengthInBits)
60+
// RendevouzInformation must be present for a QR code.
61+
VerifyOrReturnValue(rendezvousInformation.HasValue(), false);
62+
if (mode == ValidationMode::kProduce)
7763
{
78-
return false;
64+
chip::RendezvousInformationFlags valid(RendezvousInformationFlag::kBLE, RendezvousInformationFlag::kOnNetwork,
65+
RendezvousInformationFlag::kSoftAP);
66+
VerifyOrReturnValue(rendezvousInformation.Value().HasOnly(valid), false);
7967
}
8068

8169
return CheckPayloadCommonConstraints();
8270
}
8371

84-
bool PayloadContents::isValidManualCode() const
72+
bool PayloadContents::isValidManualCode(ValidationMode mode) const
8573
{
86-
// Discriminator validity is enforced by the SetupDiscriminator class.
87-
88-
if (setUpPINCode >= 1 << kSetupPINCodeFieldLengthInBits)
89-
{
90-
return false;
91-
}
92-
74+
// No additional constraints apply to Manual Pairing Codes.
75+
// (If the payload has a long discriminator it will be converted automatically.)
9376
return CheckPayloadCommonConstraints();
9477
}
9578

@@ -109,31 +92,22 @@ bool PayloadContents::IsValidSetupPIN(uint32_t setupPIN)
10992

11093
bool PayloadContents::CheckPayloadCommonConstraints() const
11194
{
112-
// A version not equal to 0 would be invalid for v1 and would indicate new format (e.g. version 2)
113-
if (version != 0)
114-
{
115-
return false;
116-
}
95+
// Validation rules in this method apply to all validation modes.
11796

118-
if (!IsValidSetupPIN(setUpPINCode))
119-
{
120-
return false;
121-
}
97+
// Even in ValidationMode::kConsume we don't understand how to handle any payload version other than 0.
98+
VerifyOrReturnValue(version == 0, false);
99+
100+
VerifyOrReturnValue(IsValidSetupPIN(setUpPINCode), false);
122101

123102
// VendorID must be unspecified (0) or in valid range expected.
124-
if (!IsVendorIdValidOperationally(vendorID) && (vendorID != VendorId::Unspecified))
125-
{
126-
return false;
127-
}
103+
VerifyOrReturnValue((vendorID == VendorId::Unspecified) || IsVendorIdValidOperationally(vendorID), false);
128104

129105
// A value of 0x0000 SHALL NOT be assigned to a product since Product ID = 0x0000 is used for these specific cases:
130106
// * To announce an anonymized Product ID as part of device discovery
131107
// * To indicate an OTA software update file applies to multiple Product IDs equally.
132108
// * To avoid confusion when presenting the Onboarding Payload for ECM with multiple nodes
133-
if (productID == 0 && vendorID != VendorId::Unspecified)
134-
{
135-
return false;
136-
}
109+
// In these special cases the vendorID must be 0 (Unspecified)
110+
VerifyOrReturnValue(productID != 0 || vendorID == VendorId::Unspecified, false);
137111

138112
return true;
139113
}

src/setup_payload/SetupPayload.h

+15-2
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ inline constexpr uint8_t kCommissioningTimeoutTag = 0x04;
7676

7777
inline constexpr uint32_t kSetupPINCodeMaximumValue = 99999998;
7878
inline constexpr uint32_t kSetupPINCodeUndefinedValue = 0;
79+
static_assert(kSetupPINCodeMaximumValue < (1 << kSetupPINCodeFieldLengthInBits));
7980

8081
// clang-format off
8182
const int kTotalPayloadDataSizeInBits =
@@ -128,8 +129,20 @@ struct PayloadContents
128129
SetupDiscriminator discriminator;
129130
uint32_t setUpPINCode = 0;
130131

131-
bool isValidQRCodePayload() const;
132-
bool isValidManualCode() const;
132+
enum class ValidationMode : uint8_t
133+
{
134+
kProduce, ///< Only flags or values allowed by the current spec version are allowed.
135+
/// Producers of a Setup Payload should use this mode to ensure the
136+
// payload is valid according to the current spec version.
137+
kConsume, ///< Flags or values that are reserved for future use, or were allowed in
138+
/// a previous spec version may be present. Consumers of a Setup Payload
139+
/// should use this mode to ensure they are forward and backwards
140+
/// compatible with payloads from older or newer Matter devices.
141+
};
142+
143+
bool isValidQRCodePayload(ValidationMode mode = ValidationMode::kProduce) const;
144+
bool isValidManualCode(ValidationMode mode = ValidationMode::kProduce) const;
145+
133146
bool operator==(const PayloadContents & input) const;
134147

135148
static bool IsValidSetupPIN(uint32_t setupPIN);

src/setup_payload/tests/TestQRCode.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,13 @@ TEST(TestQRCode, TestSetupPayloadVerify)
307307
invalid.SetRaw(static_cast<uint8_t>(invalid.Raw() + 1));
308308
test_payload.rendezvousInformation.SetValue(invalid);
309309
EXPECT_EQ(test_payload.isValidQRCodePayload(), false);
310+
// When validating in Consume mode, unknown rendezvous flags are OK.
311+
EXPECT_TRUE(test_payload.isValidQRCodePayload(PayloadContents::ValidationMode::kConsume));
312+
test_payload.rendezvousInformation.SetValue(RendezvousInformationFlags(0xff));
313+
EXPECT_TRUE(test_payload.isValidQRCodePayload(PayloadContents::ValidationMode::kConsume));
314+
// Rendezvous information is still required even in Consume mode.
315+
test_payload.rendezvousInformation.ClearValue();
316+
EXPECT_FALSE(test_payload.isValidQRCodePayload(PayloadContents::ValidationMode::kConsume));
310317

311318
// test invalid setup PIN
312319
test_payload = payload;

0 commit comments

Comments
 (0)