Skip to content

Commit 214f2d7

Browse files
apahl-clrestyled-commitsForemanZack-CableLabsrobertfarnum
authored
Align TC_JFPKI_2_2.py with test plan (project-chip#43119)
* Implement ICACCSRRequest handler guard * Restyled by clang-format * Reset mVerifiedFabricIndex at the start of HandleAnnounceJointFabricAdministrator * Update TC_JFADMIN to test VIDNotVerified response from ICACCSRRequest * Switch pairing command to use onnetwork-long with discriminator * Add missing shutdowns * full compile of todos in HandleICACCSReq, no testing yet * Successful compile of handle ICAC checks, still no tests * change for passing all current tests in JFADMIN2_2 * fixed error on handle fabric index from testing * Quick python testing changes of uncommented test case 8, not fully working * Add test step details * Move commissioning and subsequent actions from setup to step 1 * Rename JFADMIN 2.2 to JFPKI 2.2 * Reimplement test steps using enums instead of strings * Add a try/finally to shut down the resources initialized at the start of the script * Remove outdated todo * Remove unnecessary guard on VIDNotVerified check * Reorder HandleICACCSRRequest checks to satisfy TC_JFPKI_2_2 * Implement JFPKI_2_2 steps 5-7 * Add TC_JFPKI_2.2.py to slow_tests * Implement all TC_JFPKI_2_2 steps except 17 * Use teardown_class for resource shutdown * Refactor to use default_controller * Immediately expire failsafes to speed up the test * Implement Step 17 * Clean up code * Switch to with assert_raises where relevant * Relocate VID verification tracking to joint-fabric-administrator-server * Revert ReadAdministratorFabricIndex implementation * Add PICS and desc * Use new name in desc_TC_JFPKI_2_2 * Implement Copilot feedback * Remove section number from description --------- Co-authored-by: Restyled.io <commits@restyled.io> Co-authored-by: Zack Foreman <z.foreman@cablelabs.com> Co-authored-by: Robert Farnum <robertfarnum@gmail.com>
1 parent 2937efc commit 214f2d7

4 files changed

Lines changed: 514 additions & 279 deletions

File tree

src/app/clusters/joint-fabric-administrator-server/joint-fabric-administrator-server.cpp

Lines changed: 117 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include <access/AccessControl.h>
2424
#include <app-common/zap-generated/attributes/Accessors.h>
25+
#include <app-common/zap-generated/cluster-objects.h>
2526
#include <app-common/zap-generated/ids/Attributes.h>
2627
#include <app/AttributeAccessInterface.h>
2728
#include <app/AttributeAccessInterfaceRegistry.h>
@@ -74,6 +75,7 @@ class JointFabricAdministratorGlobalInstance : public AttributeAccessInterface,
7475
{}
7576

7677
CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
78+
void OnFailSafeTimerExpired();
7779

7880
private:
7981
CHIP_ERROR ReadAdministratorFabricIndex(AttributeValueEncoder & aEncoder);
@@ -90,6 +92,10 @@ class JointFabricAdministratorGlobalInstance : public AttributeAccessInterface,
9092

9193
static void OnTrustVerificationCompletion(CHIP_ERROR err);
9294

95+
CHIP_ERROR VerifyAddICACStep1(const FabricIndex accessFabric, const Commands::AddICAC::DecodableType & commandData);
96+
CHIP_ERROR VerifyAddICACPublicKey(const Commands::AddICAC::DecodableType & commandData);
97+
CHIP_ERROR VerifyAddICACDNEncodingRules(const Commands::AddICAC::DecodableType & commandData);
98+
9399
// Cleans up asynchronous resources used in HandleAnnounceJointFabricAdministrator
94100
void CleanupAnnounceJFA();
95101

@@ -99,6 +105,18 @@ class JointFabricAdministratorGlobalInstance : public AttributeAccessInterface,
99105

100106
JointFabricAdministratorGlobalInstance gJointFabricAdministratorGlobalInstance;
101107

108+
namespace {
109+
void OnPlatformEventHandler(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg)
110+
{
111+
if (event->Type == DeviceLayer::DeviceEventType::kFailSafeTimerExpired)
112+
{
113+
auto * instance = reinterpret_cast<JointFabricAdministratorGlobalInstance *>(arg);
114+
VerifyOrReturn(instance != nullptr);
115+
instance->OnFailSafeTimerExpired();
116+
}
117+
}
118+
} // anonymous namespace
119+
102120
CHIP_ERROR JointFabricAdministratorGlobalInstance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
103121
{
104122
VerifyOrDie(aPath.mClusterId == JointFabricAdministratorCluster::Id);
@@ -167,6 +185,7 @@ void JointFabricAdministratorGlobalInstance::HandleOJCW(HandlerContext & ctx,
167185
Optional<StatusCodeEnum> status = Optional<StatusCodeEnum>::Missing();
168186
Status globalStatus = Status::Success;
169187
Spake2pVerifier verifier;
188+
DataModel::Nullable<FabricIndex> administratorFabricIndex;
170189

171190
ChipLogProgress(Zcl, "Received command to open joint commissioning window");
172191

@@ -176,6 +195,11 @@ void JointFabricAdministratorGlobalInstance::HandleOJCW(HandlerContext & ctx,
176195
auto & commissionMgr = Server::GetInstance().GetCommissioningWindowManager();
177196

178197
VerifyOrExit(fabricInfo != nullptr, status.Emplace(StatusCodeEnum::kPAKEParameterError));
198+
VerifyOrExit(Attributes::AdministratorFabricIndex::Get(ctx.mRequestPath.mEndpointId, administratorFabricIndex) ==
199+
Status::Success,
200+
globalStatus = Status::Failure);
201+
VerifyOrExit(!administratorFabricIndex.IsNull() && administratorFabricIndex.Value() != 0,
202+
status.Emplace(StatusCodeEnum::kInvalidAdministratorFabricIndex));
179203
VerifyOrExit(failSafeContext.IsFailSafeFullyDisarmed(), status.Emplace(StatusCodeEnum::kBusy));
180204

181205
VerifyOrExit(!commissionMgr.IsCommissioningWindowOpen(), status.Emplace(StatusCodeEnum::kBusy));
@@ -219,14 +243,19 @@ void JointFabricAdministratorGlobalInstance::HandleAnnounceJointFabricAdministra
219243

220244
std::optional<Status> globalStatus = std::nullopt;
221245
ConcreteCommandPath cachedPath(ctx.mRequestPath.mEndpointId, ctx.mRequestPath.mClusterId, ctx.mRequestPath.mCommandId);
246+
const FabricIndex accessingFabricIndex = ctx.mCommandHandler.GetAccessingFabricIndex();
247+
248+
// Ensure that the accessing fabric is not marked as verified before we proceed
249+
Server::GetInstance().GetJointFabricAdministrator().ClearVidVerificationForFabric();
222250

223-
auto onComplete = [this, cachedPath](const CHIP_ERROR & err) {
251+
auto onComplete = [this, cachedPath, accessingFabricIndex](const CHIP_ERROR & err) {
224252
if (mActiveCommandHandle.has_value())
225253
{
226254
auto * commandHandler = mActiveCommandHandle.value().Get();
227255
if (err == CHIP_NO_ERROR)
228256
{
229257
ChipLogProgress(JointFabric, "Successfully verified trust against commissioning fabric administrator");
258+
Server::GetInstance().GetJointFabricAdministrator().SetVidVerificationForFabric(accessingFabricIndex);
230259
commandHandler->AddStatus(cachedPath, Status::Success);
231260
}
232261
else
@@ -236,8 +265,6 @@ void JointFabricAdministratorGlobalInstance::HandleAnnounceJointFabricAdministra
236265
}
237266
}
238267

239-
// TODO: Potential edge case: if TrustVerification is interrupted such that this callback isn't invoked,
240-
// CleanupAnnounceJFA wouldn't run and HandleAnnounceJointFabricAdministrator will permanently return Status::Busy.
241268
CleanupAnnounceJFA();
242269
};
243270

@@ -267,19 +294,28 @@ void JointFabricAdministratorGlobalInstance::CleanupAnnounceJFA()
267294
mActiveCommandHandle.reset();
268295
}
269296

297+
void JointFabricAdministratorGlobalInstance::OnFailSafeTimerExpired()
298+
{
299+
CleanupAnnounceJFA();
300+
Server::GetInstance().GetJointFabricAdministrator().ClearVidVerificationForFabric();
301+
Server::GetInstance().GetJointFabricAdministrator().SetPeerJFAdminClusterEndpointId(kInvalidEndpointId);
302+
}
303+
270304
void JointFabricAdministratorGlobalInstance::HandleICACCSRRequest(HandlerContext & ctx,
271305
const Commands::ICACCSRRequest::DecodableType & commandData)
272306
{
273307
MATTER_TRACE_SCOPE("ICACCSRRequest", "JointFabricAdministrator");
274308
ChipLogProgress(Zcl, "JointFabricAdministrator: Received an ICACCSRRequest command");
275309

276310
auto nonDefaultStatus = Status::Success;
311+
Optional<StatusCodeEnum> status = Optional<StatusCodeEnum>::Missing();
277312
auto & failSafeContext = Server::GetInstance().GetFailSafeContext();
278313
auto & jointFabricAdministrator = Server::GetInstance().GetJointFabricAdministrator();
279314

280315
uint8_t buf[Credentials::kMaxDERCertLength];
281316
MutableByteSpan icacCsr(buf, Credentials::kMaxDERCertLength);
282317
Commands::ICACCSRResponse::Type response;
318+
DataModel::Nullable<FabricIndex> administratorFabricIndex;
283319

284320
// command must be invoked over CASE
285321
VerifyOrExit(ctx.mCommandHandler.GetSubjectDescriptor().authMode == Access::AuthMode::kCase,
@@ -288,20 +324,28 @@ void JointFabricAdministratorGlobalInstance::HandleICACCSRRequest(HandlerContext
288324
VerifyOrExit(failSafeContext.IsFailSafeArmed(ctx.mCommandHandler.GetAccessingFabricIndex()),
289325
nonDefaultStatus = Status::FailsafeRequired);
290326

291-
/* TODO spec.: If the <<ref_FabricTableVendorIdVerificationProcedure, FabricFabric Table Vendor ID Verification Procedure>>
292-
* has not been executed against the initiator of this command, the command SHALL fail
293-
* with a <<ref_JFVidNotVerified, JfVidNotVerified>> status code SHALL be sent back to the initiator.*/
294-
295327
VerifyOrExit(!failSafeContext.AddICACCommandHasBeenInvoked(), nonDefaultStatus = Status::ConstraintError);
296328

329+
VerifyOrExit(jointFabricAdministrator.WasVidVerificationExecutedForFabric(ctx.mCommandHandler.GetAccessingFabricIndex()),
330+
status.Emplace(StatusCodeEnum::kVIDNotVerified));
331+
332+
VerifyOrExit(Attributes::AdministratorFabricIndex::Get(ctx.mRequestPath.mEndpointId, administratorFabricIndex) ==
333+
Status::Success,
334+
nonDefaultStatus = Status::Failure);
335+
VerifyOrExit(!administratorFabricIndex.IsNull(), status.Emplace(StatusCodeEnum::kInvalidAdministratorFabricIndex));
336+
297337
VerifyOrExit(jointFabricAdministrator.GetDelegate() != nullptr, nonDefaultStatus = Status::Failure);
298338
VerifyOrExit(jointFabricAdministrator.GetDelegate()->GetIcacCsr(icacCsr) == CHIP_NO_ERROR, nonDefaultStatus = Status::Failure);
299339

300340
response.icaccsr = icacCsr;
301341
ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
302342

303343
exit:
304-
if (nonDefaultStatus != Status::Success)
344+
if (status.HasValue())
345+
{
346+
TEMPORARY_RETURN_IGNORED ctx.mCommandHandler.AddClusterSpecificFailure(ctx.mRequestPath, to_underlying(status.Value()));
347+
}
348+
else if (nonDefaultStatus != Status::Success)
305349
{
306350
ctx.mCommandHandler.AddStatus(ctx.mRequestPath, nonDefaultStatus);
307351
}
@@ -313,9 +357,9 @@ void JointFabricAdministratorGlobalInstance::HandleAddICAC(HandlerContext & ctx,
313357
MATTER_TRACE_SCOPE("AddICAC", "JointFabricAdministrator");
314358
ChipLogProgress(Zcl, "JointFabricAdministrator: Received an AddICAC command");
315359

316-
auto nonDefaultStatus = Status::Success;
317-
auto & failSafeContext = Server::GetInstance().GetFailSafeContext();
318-
360+
auto nonDefaultStatus = Status::Success;
361+
auto & failSafeContext = Server::GetInstance().GetFailSafeContext();
362+
Optional<ICACResponseStatusEnum> status = Optional<ICACResponseStatusEnum>::Missing();
319363
// command must be invoked over CASE
320364
VerifyOrExit(ctx.mCommandHandler.GetSubjectDescriptor().authMode == Access::AuthMode::kCase,
321365
nonDefaultStatus = Status::InvalidCommand);
@@ -326,12 +370,70 @@ void JointFabricAdministratorGlobalInstance::HandleAddICAC(HandlerContext & ctx,
326370
VerifyOrExit(!failSafeContext.AddICACCommandHasBeenInvoked(), nonDefaultStatus = Status::ConstraintError);
327371
failSafeContext.SetAddICACHasBeenInvoked();
328372

329-
/* TODO: implement rest of the AddICAC checks */
373+
VerifyOrExit(VerifyAddICACStep1(ctx.mCommandHandler.GetAccessingFabricIndex(), commandData) == CHIP_NO_ERROR,
374+
status.Emplace(ICACResponseStatusEnum::kInvalidICAC));
375+
376+
VerifyOrExit(VerifyAddICACPublicKey(commandData) == CHIP_NO_ERROR, status.Emplace(ICACResponseStatusEnum::kInvalidPublicKey));
377+
378+
VerifyOrExit(VerifyAddICACDNEncodingRules(commandData) == CHIP_NO_ERROR, status.Emplace(ICACResponseStatusEnum::kInvalidICAC));
330379

331380
exit:
381+
if (status.HasValue())
382+
{
383+
Commands::ICACResponse::Type response;
384+
response.statusCode = status.Value();
385+
ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response);
386+
return;
387+
}
332388
ctx.mCommandHandler.AddStatus(ctx.mRequestPath, nonDefaultStatus);
333389
}
334390

391+
CHIP_ERROR JointFabricAdministratorGlobalInstance::VerifyAddICACStep1(const FabricIndex accessFabric,
392+
const Commands::AddICAC::DecodableType & commandData)
393+
{
394+
constexpr uint8_t kMaxCertsInChain = 2;
395+
uint8_t rcacBuf[Credentials::kMaxCHIPCertLength];
396+
MutableByteSpan rcacSpan{ rcacBuf };
397+
ChipCertificateSet certificates;
398+
ValidationContext validContext;
399+
400+
ReturnErrorOnFailure(Server::GetInstance().GetFabricTable().FetchRootCert(accessFabric, rcacSpan));
401+
ReturnErrorOnFailure(certificates.Init(kMaxCertsInChain));
402+
ReturnErrorOnFailure(certificates.LoadCert(rcacSpan, BitFlags<CertDecodeFlags>(CertDecodeFlags::kIsTrustAnchor)));
403+
ReturnErrorOnFailure(
404+
certificates.LoadCert(commandData.ICACValue, BitFlags<CertDecodeFlags>(CertDecodeFlags::kGenerateTBSHash)));
405+
validContext.Reset();
406+
validContext.mRequiredKeyUsages.Set(KeyUsageFlags::kKeyCertSign);
407+
validContext.mRequiredCertType = CertType::kICA;
408+
return certificates.ValidateCert(certificates.GetLastCert(), validContext);
409+
}
410+
411+
CHIP_ERROR JointFabricAdministratorGlobalInstance::VerifyAddICACPublicKey(const Commands::AddICAC::DecodableType & commandData)
412+
{
413+
uint8_t csrBuf[Credentials::kMaxDERCertLength];
414+
MutableByteSpan icacCsr{ csrBuf };
415+
Crypto::P256PublicKey csrPubKey;
416+
Credentials::P256PublicKeySpan icacPubKeySpan;
417+
418+
auto & jointFabricAdministrator = Server::GetInstance().GetJointFabricAdministrator();
419+
VerifyOrReturnError(jointFabricAdministrator.GetDelegate() != nullptr, CHIP_ERROR_INCORRECT_STATE);
420+
ReturnErrorOnFailure(jointFabricAdministrator.GetDelegate()->GetIcacCsr(icacCsr));
421+
ReturnErrorOnFailure(Crypto::VerifyCertificateSigningRequest(icacCsr.data(), icacCsr.size(), csrPubKey));
422+
ReturnErrorOnFailure(Credentials::ExtractPublicKeyFromChipCert(commandData.ICACValue, icacPubKeySpan));
423+
424+
Credentials::P256PublicKeySpan csrPubKeySpan(csrPubKey.ConstBytes());
425+
VerifyOrReturnError(memcmp(icacPubKeySpan.data(), csrPubKeySpan.data(), icacPubKeySpan.size()) == 0,
426+
CHIP_ERROR_INVALID_ARGUMENT);
427+
return CHIP_NO_ERROR;
428+
}
429+
430+
CHIP_ERROR
431+
JointFabricAdministratorGlobalInstance::VerifyAddICACDNEncodingRules(const Commands::AddICAC::DecodableType & commandData)
432+
{
433+
Credentials::ChipCertificateData certData;
434+
return Credentials::DecodeChipCert(commandData.ICACValue, certData);
435+
}
436+
335437
void JointFabricAdministratorGlobalInstance::HandleTransferAnchorRequest(
336438
HandlerContext & ctx, const Commands::TransferAnchorRequest::DecodableType & commandData)
337439
{
@@ -374,10 +476,13 @@ void MatterJointFabricAdministratorPluginServerInitCallback()
374476
ChipLogProgress(DataManagement, "JointFabricAdministrator: initializing");
375477
AttributeAccessInterfaceRegistry::Instance().Register(&gJointFabricAdministratorGlobalInstance);
376478
ReturnOnFailure(CommandHandlerInterfaceRegistry::Instance().RegisterCommandHandler(&gJointFabricAdministratorGlobalInstance));
479+
ReturnOnFailure(DeviceLayer::PlatformMgr().AddEventHandler(
480+
OnPlatformEventHandler, reinterpret_cast<intptr_t>(&gJointFabricAdministratorGlobalInstance)));
377481
}
378482

379483
void MatterJointFabricAdministratorPluginServerShutdownCallback()
380484
{
485+
DeviceLayer::PlatformMgr().RemoveEventHandler(OnPlatformEventHandler);
381486
AttributeAccessInterfaceRegistry::Instance().Unregister(&gJointFabricAdministratorGlobalInstance);
382487
ReturnOnFailure(CommandHandlerInterfaceRegistry::Instance().UnregisterCommandHandler(&gJointFabricAdministratorGlobalInstance));
383488
}

src/app/server/JointFabricAdministrator.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,11 @@
2020
#include <app-common/zap-generated/cluster-objects.h>
2121
#include <lib/core/CHIPPersistentStorageDelegate.h>
2222
#include <lib/core/CHIPVendorIdentifiers.hpp>
23+
#include <lib/core/DataModelTypes.h>
2324
#include <lib/core/NodeId.h>
2425

26+
#include <optional>
27+
2528
namespace chip {
2629
namespace app {
2730

@@ -48,6 +51,13 @@ class JointFabricAdministrator
4851
mPeerJFAdminClusterEndpointId = peerJFAdminClusterEndpointId;
4952
}
5053

54+
void SetVidVerificationForFabric(chip::FabricIndex fabricIndex) { mVidVerificationFabricIndex = fabricIndex; }
55+
void ClearVidVerificationForFabric() { mVidVerificationFabricIndex.reset(); }
56+
bool WasVidVerificationExecutedForFabric(chip::FabricIndex fabricIndex) const
57+
{
58+
return mVidVerificationFabricIndex.has_value() && (mVidVerificationFabricIndex.value() == fabricIndex);
59+
}
60+
5161
CHIP_ERROR SetDelegate(JointFabricAdministrator::Delegate * delegate)
5262
{
5363
VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
@@ -62,6 +72,7 @@ class JointFabricAdministrator
6272

6373
private:
6474
chip::EndpointId mPeerJFAdminClusterEndpointId = chip::kInvalidEndpointId;
75+
std::optional<chip::FabricIndex> mVidVerificationFabricIndex;
6576
JointFabricAdministrator::Delegate * mDelegate = nullptr;
6677
};
6778

0 commit comments

Comments
 (0)