@matter/protocol 0.12.0-alpha.0-20241225-902d76201 → 0.12.0-alpha.0-20241227-9e7d81837

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/cjs/fabric/Fabric.d.ts +2 -1
  2. package/dist/cjs/fabric/Fabric.d.ts.map +1 -1
  3. package/dist/cjs/fabric/Fabric.js +21 -5
  4. package/dist/cjs/fabric/Fabric.js.map +1 -1
  5. package/dist/cjs/fabric/FabricAuthority.d.ts +2 -0
  6. package/dist/cjs/fabric/FabricAuthority.d.ts.map +1 -1
  7. package/dist/cjs/fabric/FabricAuthority.js +7 -1
  8. package/dist/cjs/fabric/FabricAuthority.js.map +1 -1
  9. package/dist/cjs/peer/ControllerCommissioningFlow.d.ts.map +1 -1
  10. package/dist/cjs/peer/ControllerCommissioningFlow.js +105 -49
  11. package/dist/cjs/peer/ControllerCommissioningFlow.js.map +1 -1
  12. package/dist/cjs/session/SecureSession.d.ts +1 -0
  13. package/dist/cjs/session/SecureSession.d.ts.map +1 -1
  14. package/dist/cjs/session/SecureSession.js +7 -1
  15. package/dist/cjs/session/SecureSession.js.map +1 -1
  16. package/dist/esm/fabric/Fabric.d.ts +2 -1
  17. package/dist/esm/fabric/Fabric.d.ts.map +1 -1
  18. package/dist/esm/fabric/Fabric.js +22 -5
  19. package/dist/esm/fabric/Fabric.js.map +1 -1
  20. package/dist/esm/fabric/FabricAuthority.d.ts +2 -0
  21. package/dist/esm/fabric/FabricAuthority.d.ts.map +1 -1
  22. package/dist/esm/fabric/FabricAuthority.js +7 -1
  23. package/dist/esm/fabric/FabricAuthority.js.map +1 -1
  24. package/dist/esm/peer/ControllerCommissioningFlow.d.ts.map +1 -1
  25. package/dist/esm/peer/ControllerCommissioningFlow.js +105 -49
  26. package/dist/esm/peer/ControllerCommissioningFlow.js.map +1 -1
  27. package/dist/esm/session/SecureSession.d.ts +1 -0
  28. package/dist/esm/session/SecureSession.d.ts.map +1 -1
  29. package/dist/esm/session/SecureSession.js +7 -1
  30. package/dist/esm/session/SecureSession.js.map +1 -1
  31. package/package.json +6 -6
  32. package/src/fabric/Fabric.ts +24 -5
  33. package/src/fabric/FabricAuthority.ts +11 -2
  34. package/src/peer/ControllerCommissioningFlow.ts +116 -51
  35. package/src/session/SecureSession.ts +7 -0
@@ -15,6 +15,7 @@ import {
15
15
  ClusterId,
16
16
  ClusterType,
17
17
  EndpointNumber,
18
+ FabricIndex,
18
19
  StatusResponseError,
19
20
  TypeFromPartialBitSchema,
20
21
  TypeFromSchema,
@@ -123,6 +124,7 @@ type CollectedCommissioningData = {
123
124
  productId?: number;
124
125
  supportsConcurrentConnection?: boolean;
125
126
  successfullyConnectedToNetwork?: boolean;
127
+ fabricIndex?: FabricIndex;
126
128
  };
127
129
 
128
130
  /** Error that throws when Commissioning fails and process cannot be continued. */
@@ -284,18 +286,23 @@ export class ControllerCommissioningFlow {
284
286
  }
285
287
 
286
288
  /**
287
- * Initialize commissioning steps and add them in the default order
289
+ * Initialize commissioning steps and add them in the default order as defined by
290
+ * @see {@link MatterSpecification.v13.Core} § 5.5
288
291
  */
289
292
  #initializeCommissioningSteps() {
290
293
  this.#commissioningSteps.push({
291
- stepNumber: 0,
294
+ stepNumber: 0, // Preparation
292
295
  subStepNumber: 1,
293
296
  name: "GetInitialData",
294
297
  stepLogic: () => this.#getInitialData(),
295
298
  });
296
299
 
300
+ // Step 1: is outside of this class and requires to have relevant information needed by next steps
301
+ // Step 2: is about discovery which is already done before this starts here
302
+ // Step 3: is the PASE session establishment which is done before this starts here
303
+
297
304
  this.#commissioningSteps.push({
298
- stepNumber: 3,
305
+ stepNumber: 4,
299
306
  subStepNumber: 1,
300
307
  name: "GeneralCommissioning.ArmFailsafe",
301
308
  stepLogic: () => this.#armFailsafe(),
@@ -323,14 +330,23 @@ export class ControllerCommissioningFlow {
323
330
  });
324
331
 
325
332
  this.#commissioningSteps.push({
326
- stepNumber: 7,
333
+ stepNumber: 7, // includes 7-9
327
334
  subStepNumber: 1,
328
335
  name: "OperationalCredentials.Certificates",
329
336
  stepLogic: () => this.#certificates(),
330
337
  });
331
338
 
332
339
  this.#commissioningSteps.push({
333
- stepNumber: 10,
340
+ stepNumber: 9,
341
+ subStepNumber: 2,
342
+ name: "OperationalCredentials.UpdateFabricLabel",
343
+ stepLogic: () => this.#updateFabricLabel(),
344
+ });
345
+
346
+ // TODO Step 10: TimeSynchronization.SetTrustedTimeSource if supported
347
+
348
+ this.#commissioningSteps.push({
349
+ stepNumber: 11,
334
350
  subStepNumber: 1,
335
351
  name: "AccessControl",
336
352
  stepLogic: () => this.#configureAccessControlLists(),
@@ -339,14 +355,14 @@ export class ControllerCommissioningFlow {
339
355
  // Care about Network commissioning only when we are on BLE, because else we are already on IP network
340
356
  if (this.#interactionClient.channelType === ChannelType.BLE) {
341
357
  this.#commissioningSteps.push({
342
- stepNumber: 11,
358
+ stepNumber: 12,
343
359
  subStepNumber: 1,
344
360
  name: "NetworkCommissioning.Validate",
345
361
  stepLogic: () => this.#validateNetwork(),
346
362
  });
347
363
  if (this.#commissioningOptions.wifiNetwork !== undefined) {
348
364
  this.#commissioningSteps.push({
349
- stepNumber: 11,
365
+ stepNumber: 12, // includes step 13
350
366
  subStepNumber: 2,
351
367
  name: "NetworkCommissioning.Wifi",
352
368
  stepLogic: () => this.#configureNetworkWifi(),
@@ -354,7 +370,7 @@ export class ControllerCommissioningFlow {
354
370
  }
355
371
  if (this.#commissioningOptions.threadNetwork !== undefined) {
356
372
  this.#commissioningSteps.push({
357
- stepNumber: 11,
373
+ stepNumber: 12, // includes step 13
358
374
  subStepNumber: 3,
359
375
  name: "NetworkCommissioning.Thread",
360
376
  stepLogic: () => this.#configureNetworkThread(),
@@ -367,14 +383,14 @@ export class ControllerCommissioningFlow {
367
383
  }
368
384
 
369
385
  this.#commissioningSteps.push({
370
- stepNumber: 12,
386
+ stepNumber: 14, // includes step 15 (CASE connection)
371
387
  subStepNumber: 1,
372
388
  name: "Reconnect",
373
389
  stepLogic: () => this.#reconnectWithDevice(),
374
390
  });
375
391
 
376
392
  this.#commissioningSteps.push({
377
- stepNumber: 15,
393
+ stepNumber: 16,
378
394
  subStepNumber: 1,
379
395
  name: "GeneralCommissioning.Complete",
380
396
  stepLogic: () => this.#completeCommissioning(),
@@ -490,13 +506,13 @@ export class ControllerCommissioningFlow {
490
506
  }
491
507
 
492
508
  /**
493
- * Step 3
494
- * Upon completion of PASE session establishment, the Commissionee SHALL autonomously arm the Fail-safe timer for a
495
- * timeout of 60 seconds. This is to guard against the Commissioner aborting the Commissioning process without
496
- * arming the fail-safe, which may leave the device unable to accept additional connections.
497
- * A Commissioner MAY obtain device information including guidance on the fail-safe value from the Commissionee by
498
- * reading BasicCommissioningInfo attribute (see Section 11.9.5.2, “BasicCommissioningInfo Attribute”) prior to
499
- * invoking the ArmFailSafe command.
509
+ * Step 4
510
+ * Commissioner SHALL re-arm the Fail-safe timer on the Commissionee to the desired commissioning
511
+ * timeout within 60 seconds of the completion of PASE session establishment, using the
512
+ * ArmFailSafe command (see Section 11.10.6.2, “ArmFailSafe Command”). A Commissioner MAY
513
+ * obtain device information including guidance on the fail-safe value from the Commissionee by
514
+ * reading BasicCommissioningInfo attribute (see Section 11.10.5.2, “BasicCommissioningInfo
515
+ * Attribute”) prior to invoking the ArmFailSafe command.
500
516
  */
501
517
  async #armFailsafe() {
502
518
  const client = this.#getClusterClient(GeneralCommissioning.Cluster);
@@ -524,12 +540,14 @@ export class ControllerCommissioningFlow {
524
540
  }
525
541
 
526
542
  async #resetFailsafeTimer() {
543
+ if (this.#lastFailSafeTime === undefined) return;
527
544
  try {
528
545
  const client = this.#getClusterClient(GeneralCommissioning.Cluster);
529
546
  await client.armFailSafe({
530
547
  breadcrumb: this.#lastBreadcrumb,
531
548
  expiryLengthSeconds: 0,
532
549
  });
550
+ this.#lastFailSafeTime = undefined; // No failsafe active anymore
533
551
  } catch (error) {
534
552
  logger.error(`Error while resetting failsafe timer`, error);
535
553
  }
@@ -537,11 +555,10 @@ export class ControllerCommissioningFlow {
537
555
 
538
556
  /**
539
557
  * Step 5 - 1
540
- * Commissioner SHALL configure regulatory information in the Commissionee if it has at least one instance of
541
- * Network Commissioning cluster on any endpoint with either the WI (i.e. Wi-Fi) or TH (i.e. Thread) feature flags
542
- * set in its FeatureMap.
543
- * The regulatory information is configured using SetRegulatoryConfig (see Section 11.9.6.4,
544
- * “SetRegulatoryConfig Command”).
558
+ * Commissioner SHALL configure regulatory information if the Commissionee has at least one instance of
559
+ * the Network Commissioning cluster on any endpoint with either the WI (i.e. Wi-Fi) or TH (i.e. Thread)
560
+ * feature flags set in its FeatureMap, Commissioner SHALL configure regulatory information in the
561
+ * Commissionee using the SetRegulatoryConfig command.
545
562
  */
546
563
  async #configureRegulatoryInformation() {
547
564
  if (this.#collectedCommissioningData.networkFeatures === undefined) {
@@ -609,11 +626,12 @@ export class ControllerCommissioningFlow {
609
626
 
610
627
  /**
611
628
  * Step 5 - 2
612
- * Commissioner SHOULD configure UTC time, timezone, and DST offset, if the Commissionee supports the time cluster.
613
- * The order of configuration of this information is not critical. The UTC time is configured using SetUtcTime
614
- * command (see Section 11.16.9.1, “SetUtcTime Command”) while timezone and DST offset are set through TimeZone
615
- * (see Section 11.16.8.6, “TimeZone Attribute”) and DstOffset attribute (see Section 11.16.8.7,
616
- * “DSTOffset Attribute”), respectively.
629
+ * Commissioner SHOULD configure UTC time, timezone, and DST offset, if the Commissionee supports the
630
+ * time synchronization cluster.
631
+ * The Commissioner SHOULD configure UTC time using the SetUTCTime command.
632
+ * The Commissioner SHOULD set the time zone using the SetTimeZone command, if the TimeZone Feature is supported.
633
+ * The Commissioner SHOULD set the DST offsets using the SetDSTOffset command if the TimeZone Feature is supported, and the SetTimeZoneResponse from the Commissionee had the DSTOffsetsRequired field set to True.
634
+ * ▪ The Commissioner SHOULD set a Default NTP server using the SetDefaultNTP command if the NTPClient Feature is supported and the DefaultNTP attribute is null. If the current value is non-null, Commissioners MAY opt to overwrite the current value.
617
635
  */
618
636
  async #synchronizeTime() {
619
637
  if (
@@ -633,8 +651,8 @@ export class ControllerCommissioningFlow {
633
651
 
634
652
  /**
635
653
  * Step 6
636
- * Commissioner SHALL establish the authenticity of the Commissionee as a certified Matter device (see Section
637
- * 6.2.3, “Device Attestation Procedure”).
654
+ * Commissioner SHALL establish the authenticity of the Commissionee as a certified Matter device
655
+ * (see Section 6.2.3, “Device Attestation Procedure”).
638
656
  */
639
657
  async #deviceAttestation() {
640
658
  const operationalCredentialsClusterClient = this.#getClusterClient(OperationalCredentials.Cluster);
@@ -686,8 +704,12 @@ export class ControllerCommissioningFlow {
686
704
  * 8: Commissioner SHALL generate or otherwise obtain an Operational Certificate containing Operational ID after
687
705
  * receiving the CSRResponse command from the Commissionee (see Section 11.17.6.5, “CSRRequest Command”), using
688
706
  * implementation-specific means.
689
- * 9: Commissioner SHALL install operational credentials (see Figure 38, “Node Operational Credentials flow”) on
690
- * the Commissionee using the AddTrustedRootCertificate and AddNOC commands.
707
+ * 9: Commissioner SHALL install operational credentials (see Figure 40, “Node Operational Credentials
708
+ * flow”) on the Commissionee using the AddTrustedRootCertificate and AddNOC commands,
709
+ * and SHALL use the UpdateFabricLabel command to set a string that the user can recognize and
710
+ * relate to this Commissioner/Administrator.
711
+ * The AdminVendorId field of the AddNOC command SHALL be set to a value for which the Vendor Schema in
712
+ * DCL contains the name and other information of the Commissioner’s manufacturer.
691
713
  */
692
714
  async #certificates() {
693
715
  const operationalCredentialsClusterClient = this.#getClusterClient(OperationalCredentials.Cluster);
@@ -715,20 +737,62 @@ export class ControllerCommissioningFlow {
715
737
  this.#fabric.fabricId,
716
738
  this.#interactionClient.address.nodeId,
717
739
  );
718
- this.#ensureOperationalCredentialsSuccess(
719
- "addNoc",
720
- await operationalCredentialsClusterClient.addNoc(
721
- {
722
- nocValue: peerOperationalCert,
723
- icacValue: new Uint8Array(0),
724
- ipkValue: this.#fabric.identityProtectionKey,
725
- adminVendorId: this.#fabric.rootVendorId,
726
- caseAdminSubject: this.#fabric.rootNodeId,
727
- },
728
- { useExtendedFailSafeMessageResponseTimeout: true },
729
- ),
740
+
741
+ const addNocResponse = await operationalCredentialsClusterClient.addNoc(
742
+ {
743
+ nocValue: peerOperationalCert,
744
+ icacValue: new Uint8Array(0),
745
+ ipkValue: this.#fabric.identityProtectionKey,
746
+ adminVendorId: this.#fabric.rootVendorId,
747
+ caseAdminSubject: this.#fabric.rootNodeId,
748
+ },
749
+ { useExtendedFailSafeMessageResponseTimeout: true },
730
750
  );
731
751
 
752
+ this.#ensureOperationalCredentialsSuccess("addNoc", addNocResponse);
753
+
754
+ const { fabricIndex } = addNocResponse;
755
+ this.#collectedCommissioningData.fabricIndex = fabricIndex;
756
+
757
+ return {
758
+ code: CommissioningStepResultCode.Success,
759
+ breadcrumb: this.#lastBreadcrumb,
760
+ };
761
+ }
762
+
763
+ /**
764
+ * Step 9 - 2
765
+ * The Administrator having established a CASE session with the Commissionee over the operational network in the
766
+ * previous steps SHALL invoke the CommissioningComplete command (see Section 11.9.6.6,
767
+ * “CommissioningComplete Command”). A success response after invocation of the CommissioningComplete command ends
768
+ * the commissioning process.
769
+ */
770
+ async #updateFabricLabel() {
771
+ const { fabricIndex } = this.#collectedCommissioningData;
772
+ if (fabricIndex === undefined) {
773
+ logger.error("No fabric index available after addNoc. This should never happen.");
774
+ return {
775
+ code: CommissioningStepResultCode.Failure,
776
+ breadcrumb: this.#lastBreadcrumb,
777
+ };
778
+ }
779
+ const operationalCredentialCluster = this.#getClusterClient(OperationalCredentials.Cluster);
780
+ try {
781
+ this.#ensureOperationalCredentialsSuccess(
782
+ "updateFabricLabel",
783
+ await operationalCredentialCluster.updateFabricLabel({
784
+ label: this.#fabric.label,
785
+ fabricIndex,
786
+ }),
787
+ );
788
+ } catch (error) {
789
+ CommissioningError.accept(error);
790
+ return {
791
+ code: CommissioningStepResultCode.Failure,
792
+ breadcrumb: this.#lastBreadcrumb,
793
+ };
794
+ }
795
+
732
796
  return {
733
797
  code: CommissioningStepResultCode.Success,
734
798
  breadcrumb: this.#lastBreadcrumb,
@@ -736,7 +800,7 @@ export class ControllerCommissioningFlow {
736
800
  }
737
801
 
738
802
  /**
739
- * Step 10
803
+ * Step 11
740
804
  * Commissioner MAY configure the Access Control List (see Access Control Cluster) on the Commissionee in any way
741
805
  * it sees fit, if the singular entry added by the AddNOC command in the previous step granting Administer
742
806
  * privilege over CASE authentication type for the Node ID provided with the command is not sufficient to express
@@ -752,15 +816,15 @@ export class ControllerCommissioningFlow {
752
816
  }
753
817
 
754
818
  /**
755
- * Step 11-12
756
- * 11: If the Commissionee both supports it and requires it, the Commissioner SHALL configure the operational network
819
+ * Step 12-13
820
+ * 12: If the Commissionee both supports it and requires it, the Commissioner SHALL configure the operational network
757
821
  * at the Commissionee using commands such as AddOrUpdateWiFiNetwork (see Section 11.8.7.3, “AddOrUpdateWiFiNetwork
758
822
  * Command”) and AddOrUpdateThreadNetwork (see Section 11.8.7.4, “AddOrUpdateThreadNetwork Command”).
759
823
  * A Commissionee requires network commissioning if it is not already on the desired operational network.
760
824
  * A Commissionee supports network commissioning if it has any NetworkCommissioning cluster instances.
761
825
  * A Commissioner MAY learn about the networks visible to the Commissionee using ScanNetworks command
762
826
  * (see Section 11.8.7.1, “ScanNetworks Command”).
763
- * 12: The Commissioner SHALL trigger the Commissionee to connect to the operational network using ConnectNetwork
827
+ * 13: The Commissioner SHALL trigger the Commissionee to connect to the operational network using ConnectNetwork
764
828
  * command (see Section 11.8.7.9, “ConnectNetwork Command”) unless the Commissionee is already on the desired operational network.
765
829
  */
766
830
  async #validateNetwork() {
@@ -1072,11 +1136,11 @@ export class ControllerCommissioningFlow {
1072
1136
  }
1073
1137
 
1074
1138
  /**
1075
- * Step 13-14
1076
- * 13: Finalization of the Commissioning process begins. An Administrator configured in the ACL of the Commissionee
1139
+ * Step 14-15
1140
+ * 14: Finalization of the Commissioning process begins. An Administrator configured in the ACL of the Commissionee
1077
1141
  * by the Commissioner SHALL use Operational Discovery to discover the Commissionee. This Administrator MAY be
1078
1142
  * the Commissioner itself, or another Node to which the Commissioner has delegated the task.
1079
- * 14: The Administrator SHALL open a CASE (see Section 4.13.2, “Certificate Authenticated Session Establishment
1143
+ * 15: The Administrator SHALL open a CASE (see Section 4.13.2, “Certificate Authenticated Session Establishment
1080
1144
  * (CASE)”) session with the Commissionee over the operational network.
1081
1145
  *
1082
1146
  */
@@ -1104,7 +1168,7 @@ export class ControllerCommissioningFlow {
1104
1168
  }
1105
1169
 
1106
1170
  /**
1107
- * Step 15
1171
+ * Step 16
1108
1172
  * The Administrator having established a CASE session with the Commissionee over the operational network in the
1109
1173
  * previous steps SHALL invoke the CommissioningComplete command (see Section 11.9.6.6,
1110
1174
  * “CommissioningComplete Command”). A success response after invocation of the CommissioningComplete command ends
@@ -1118,6 +1182,7 @@ export class ControllerCommissioningFlow {
1118
1182
  useExtendedFailSafeMessageResponseTimeout: true,
1119
1183
  }),
1120
1184
  );
1185
+ this.#lastFailSafeTime = undefined; // gets deactivated when successful
1121
1186
 
1122
1187
  return {
1123
1188
  code: CommissioningStepResultCode.Success,
@@ -41,6 +41,7 @@ export class SecureSession extends Session {
41
41
  #closingAfterExchangeFinished = false;
42
42
  #sendCloseMessageWhenClosing = true;
43
43
  readonly #id: number;
44
+ readonly #isInitiator: boolean;
44
45
  #fabric: Fabric | undefined;
45
46
  readonly #peerNodeId: NodeId;
46
47
  readonly #peerSessionId: number;
@@ -135,6 +136,7 @@ export class SecureSession extends Session {
135
136
  encryptKey,
136
137
  attestationKey,
137
138
  caseAuthenticatedTags,
139
+ isInitiator,
138
140
  } = args;
139
141
 
140
142
  this.#id = id;
@@ -145,6 +147,7 @@ export class SecureSession extends Session {
145
147
  this.#encryptKey = encryptKey;
146
148
  this.#attestationKey = attestationKey;
147
149
  this.#caseAuthenticatedTags = caseAuthenticatedTags ?? [];
150
+ this.#isInitiator = isInitiator;
148
151
 
149
152
  manager?.sessions.add(this);
150
153
  fabric?.addSession(this);
@@ -189,6 +192,10 @@ export class SecureSession extends Session {
189
192
  return this.#subscriptions;
190
193
  }
191
194
 
195
+ get isInitiator() {
196
+ return this.#isInitiator;
197
+ }
198
+
192
199
  get isClosing() {
193
200
  return this.#isClosing;
194
201
  }