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
7880private:
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
100106JointFabricAdministratorGlobalInstance 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+
102120CHIP_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+
270304void 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
303343exit:
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
331380exit:
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+
335437void 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
379483void MatterJointFabricAdministratorPluginServerShutdownCallback ()
380484{
485+ DeviceLayer::PlatformMgr ().RemoveEventHandler (OnPlatformEventHandler);
381486 AttributeAccessInterfaceRegistry::Instance ().Unregister (&gJointFabricAdministratorGlobalInstance );
382487 ReturnOnFailure (CommandHandlerInterfaceRegistry::Instance ().UnregisterCommandHandler (&gJointFabricAdministratorGlobalInstance ));
383488}
0 commit comments