@matter/protocol 0.14.1-alpha.0-20250607-a93593303 → 0.15.0-alpha.0-20250613-a55f991d4

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 (217) hide show
  1. package/dist/cjs/certificate/AttestationCertificateManager.js +2 -2
  2. package/dist/cjs/certificate/AttestationCertificateManager.js.map +1 -1
  3. package/dist/cjs/certificate/CertificateAuthority.d.ts +2 -2
  4. package/dist/cjs/certificate/CertificateAuthority.d.ts.map +1 -1
  5. package/dist/cjs/certificate/CertificateAuthority.js +5 -5
  6. package/dist/cjs/certificate/CertificateAuthority.js.map +1 -1
  7. package/dist/cjs/certificate/CertificateManager.d.ts +1 -1
  8. package/dist/cjs/certificate/CertificateManager.d.ts.map +1 -1
  9. package/dist/cjs/certificate/CertificateManager.js +21 -10
  10. package/dist/cjs/certificate/CertificateManager.js.map +1 -1
  11. package/dist/cjs/certificate/CertificationDeclarationManager.d.ts +1 -1
  12. package/dist/cjs/certificate/DeviceCertification.d.ts +1 -1
  13. package/dist/cjs/certificate/DeviceCertification.d.ts.map +1 -1
  14. package/dist/cjs/certificate/DeviceCertification.js +5 -3
  15. package/dist/cjs/certificate/DeviceCertification.js.map +1 -1
  16. package/dist/cjs/codec/BtpCodec.d.ts +1 -1
  17. package/dist/cjs/codec/MessageCodec.d.ts +1 -1
  18. package/dist/cjs/events/OccurrenceManager.d.ts +1 -0
  19. package/dist/cjs/events/OccurrenceManager.d.ts.map +1 -1
  20. package/dist/cjs/events/OccurrenceManager.js +14 -8
  21. package/dist/cjs/events/OccurrenceManager.js.map +1 -1
  22. package/dist/cjs/fabric/Fabric.d.ts +8 -6
  23. package/dist/cjs/fabric/Fabric.d.ts.map +1 -1
  24. package/dist/cjs/fabric/Fabric.js +22 -15
  25. package/dist/cjs/fabric/Fabric.js.map +1 -1
  26. package/dist/cjs/fabric/FabricAuthority.d.ts +4 -0
  27. package/dist/cjs/fabric/FabricAuthority.d.ts.map +1 -1
  28. package/dist/cjs/fabric/FabricAuthority.js +6 -0
  29. package/dist/cjs/fabric/FabricAuthority.js.map +1 -1
  30. package/dist/cjs/fabric/FabricManager.d.ts.map +1 -1
  31. package/dist/cjs/fabric/FabricManager.js +9 -2
  32. package/dist/cjs/fabric/FabricManager.js.map +1 -1
  33. package/dist/cjs/fabric/TestFabric.d.ts +30 -0
  34. package/dist/cjs/fabric/TestFabric.d.ts.map +1 -0
  35. package/dist/cjs/fabric/TestFabric.js +70 -0
  36. package/dist/cjs/fabric/TestFabric.js.map +6 -0
  37. package/dist/cjs/fabric/index.d.ts +1 -0
  38. package/dist/cjs/fabric/index.d.ts.map +1 -1
  39. package/dist/cjs/fabric/index.js +1 -0
  40. package/dist/cjs/fabric/index.js.map +1 -1
  41. package/dist/cjs/groups/{FabricGroupsManager.d.ts → FabricGroups.d.ts} +2 -2
  42. package/dist/cjs/groups/FabricGroups.d.ts.map +1 -0
  43. package/dist/cjs/groups/{FabricGroupsManager.js → FabricGroups.js} +9 -9
  44. package/dist/cjs/groups/FabricGroups.js.map +6 -0
  45. package/dist/cjs/groups/KeySets.js +1 -1
  46. package/dist/cjs/groups/KeySets.js.map +1 -1
  47. package/dist/cjs/groups/index.d.ts +1 -1
  48. package/dist/cjs/groups/index.d.ts.map +1 -1
  49. package/dist/cjs/groups/index.js +1 -1
  50. package/dist/cjs/groups/index.js.map +1 -1
  51. package/dist/cjs/interaction/{AccessControlManager.d.ts → FabricAccessControl.d.ts} +12 -7
  52. package/dist/cjs/interaction/FabricAccessControl.d.ts.map +1 -0
  53. package/dist/cjs/interaction/{AccessControlManager.js → FabricAccessControl.js} +46 -21
  54. package/dist/cjs/interaction/FabricAccessControl.js.map +6 -0
  55. package/dist/cjs/interaction/index.d.ts +1 -1
  56. package/dist/cjs/interaction/index.d.ts.map +1 -1
  57. package/dist/cjs/interaction/index.js +1 -1
  58. package/dist/cjs/interaction/index.js.map +1 -1
  59. package/dist/cjs/mdns/MdnsScanner.d.ts +1 -1
  60. package/dist/cjs/mdns/MdnsScanner.d.ts.map +1 -1
  61. package/dist/cjs/mdns/MdnsScanner.js +2 -6
  62. package/dist/cjs/mdns/MdnsScanner.js.map +1 -1
  63. package/dist/cjs/peer/ControllerCommissioningFlow.d.ts.map +1 -1
  64. package/dist/cjs/peer/ControllerCommissioningFlow.js +2 -4
  65. package/dist/cjs/peer/ControllerCommissioningFlow.js.map +1 -1
  66. package/dist/cjs/protocol/DeviceCommissioner.js +1 -1
  67. package/dist/cjs/protocol/DeviceCommissioner.js.map +1 -1
  68. package/dist/cjs/protocol/MessageExchange.d.ts +6 -0
  69. package/dist/cjs/protocol/MessageExchange.d.ts.map +1 -1
  70. package/dist/cjs/protocol/MessageExchange.js +13 -0
  71. package/dist/cjs/protocol/MessageExchange.js.map +1 -1
  72. package/dist/cjs/securechannel/SecureChannelStatusMessageSchema.d.ts +1 -1
  73. package/dist/cjs/session/NodeSession.js +1 -1
  74. package/dist/cjs/session/Session.d.ts +1 -1
  75. package/dist/cjs/session/SessionManager.d.ts.map +1 -1
  76. package/dist/cjs/session/SessionManager.js +1 -0
  77. package/dist/cjs/session/SessionManager.js.map +1 -1
  78. package/dist/cjs/session/case/CaseClient.d.ts.map +1 -1
  79. package/dist/cjs/session/case/CaseClient.js +34 -30
  80. package/dist/cjs/session/case/CaseClient.js.map +1 -1
  81. package/dist/cjs/session/case/CaseMessages.d.ts +8 -8
  82. package/dist/cjs/session/case/CaseMessages.js +8 -8
  83. package/dist/cjs/session/case/CaseMessages.js.map +1 -1
  84. package/dist/cjs/session/case/CaseServer.d.ts.map +1 -1
  85. package/dist/cjs/session/case/CaseServer.js +26 -23
  86. package/dist/cjs/session/case/CaseServer.js.map +1 -1
  87. package/dist/cjs/session/pase/PaseClient.d.ts.map +1 -1
  88. package/dist/cjs/session/pase/PaseClient.js +4 -1
  89. package/dist/cjs/session/pase/PaseClient.js.map +1 -1
  90. package/dist/cjs/session/pase/PaseServer.d.ts.map +1 -1
  91. package/dist/cjs/session/pase/PaseServer.js +4 -1
  92. package/dist/cjs/session/pase/PaseServer.js.map +1 -1
  93. package/dist/esm/certificate/AttestationCertificateManager.js +2 -2
  94. package/dist/esm/certificate/AttestationCertificateManager.js.map +1 -1
  95. package/dist/esm/certificate/CertificateAuthority.d.ts +2 -2
  96. package/dist/esm/certificate/CertificateAuthority.d.ts.map +1 -1
  97. package/dist/esm/certificate/CertificateAuthority.js +5 -5
  98. package/dist/esm/certificate/CertificateAuthority.js.map +1 -1
  99. package/dist/esm/certificate/CertificateManager.d.ts +1 -1
  100. package/dist/esm/certificate/CertificateManager.d.ts.map +1 -1
  101. package/dist/esm/certificate/CertificateManager.js +22 -11
  102. package/dist/esm/certificate/CertificateManager.js.map +1 -1
  103. package/dist/esm/certificate/CertificationDeclarationManager.d.ts +1 -1
  104. package/dist/esm/certificate/DeviceCertification.d.ts +1 -1
  105. package/dist/esm/certificate/DeviceCertification.d.ts.map +1 -1
  106. package/dist/esm/certificate/DeviceCertification.js +5 -3
  107. package/dist/esm/certificate/DeviceCertification.js.map +1 -1
  108. package/dist/esm/codec/BtpCodec.d.ts +1 -1
  109. package/dist/esm/codec/MessageCodec.d.ts +1 -1
  110. package/dist/esm/events/OccurrenceManager.d.ts +1 -0
  111. package/dist/esm/events/OccurrenceManager.d.ts.map +1 -1
  112. package/dist/esm/events/OccurrenceManager.js +14 -8
  113. package/dist/esm/events/OccurrenceManager.js.map +1 -1
  114. package/dist/esm/fabric/Fabric.d.ts +8 -6
  115. package/dist/esm/fabric/Fabric.d.ts.map +1 -1
  116. package/dist/esm/fabric/Fabric.js +22 -14
  117. package/dist/esm/fabric/Fabric.js.map +1 -1
  118. package/dist/esm/fabric/FabricAuthority.d.ts +4 -0
  119. package/dist/esm/fabric/FabricAuthority.d.ts.map +1 -1
  120. package/dist/esm/fabric/FabricAuthority.js +6 -0
  121. package/dist/esm/fabric/FabricAuthority.js.map +1 -1
  122. package/dist/esm/fabric/FabricManager.d.ts.map +1 -1
  123. package/dist/esm/fabric/FabricManager.js +9 -2
  124. package/dist/esm/fabric/FabricManager.js.map +1 -1
  125. package/dist/esm/fabric/TestFabric.d.ts +30 -0
  126. package/dist/esm/fabric/TestFabric.d.ts.map +1 -0
  127. package/dist/esm/fabric/TestFabric.js +50 -0
  128. package/dist/esm/fabric/TestFabric.js.map +6 -0
  129. package/dist/esm/fabric/index.d.ts +1 -0
  130. package/dist/esm/fabric/index.d.ts.map +1 -1
  131. package/dist/esm/fabric/index.js +1 -0
  132. package/dist/esm/fabric/index.js.map +1 -1
  133. package/dist/esm/groups/{FabricGroupsManager.d.ts → FabricGroups.d.ts} +2 -2
  134. package/dist/esm/groups/FabricGroups.d.ts.map +1 -0
  135. package/dist/esm/groups/{FabricGroupsManager.js → FabricGroups.js} +6 -6
  136. package/dist/esm/groups/FabricGroups.js.map +6 -0
  137. package/dist/esm/groups/KeySets.js +1 -1
  138. package/dist/esm/groups/KeySets.js.map +1 -1
  139. package/dist/esm/groups/index.d.ts +1 -1
  140. package/dist/esm/groups/index.d.ts.map +1 -1
  141. package/dist/esm/groups/index.js +1 -1
  142. package/dist/esm/interaction/{AccessControlManager.d.ts → FabricAccessControl.d.ts} +12 -7
  143. package/dist/esm/interaction/FabricAccessControl.d.ts.map +1 -0
  144. package/dist/esm/interaction/{AccessControlManager.js → FabricAccessControl.js} +44 -19
  145. package/dist/esm/interaction/FabricAccessControl.js.map +6 -0
  146. package/dist/esm/interaction/index.d.ts +1 -1
  147. package/dist/esm/interaction/index.d.ts.map +1 -1
  148. package/dist/esm/interaction/index.js +1 -1
  149. package/dist/esm/mdns/MdnsScanner.d.ts +1 -1
  150. package/dist/esm/mdns/MdnsScanner.d.ts.map +1 -1
  151. package/dist/esm/mdns/MdnsScanner.js +2 -6
  152. package/dist/esm/mdns/MdnsScanner.js.map +1 -1
  153. package/dist/esm/peer/ControllerCommissioningFlow.d.ts.map +1 -1
  154. package/dist/esm/peer/ControllerCommissioningFlow.js +2 -4
  155. package/dist/esm/peer/ControllerCommissioningFlow.js.map +1 -1
  156. package/dist/esm/protocol/DeviceCommissioner.js +1 -1
  157. package/dist/esm/protocol/DeviceCommissioner.js.map +1 -1
  158. package/dist/esm/protocol/MessageExchange.d.ts +6 -0
  159. package/dist/esm/protocol/MessageExchange.d.ts.map +1 -1
  160. package/dist/esm/protocol/MessageExchange.js +13 -0
  161. package/dist/esm/protocol/MessageExchange.js.map +1 -1
  162. package/dist/esm/securechannel/SecureChannelStatusMessageSchema.d.ts +1 -1
  163. package/dist/esm/session/NodeSession.js +1 -1
  164. package/dist/esm/session/Session.d.ts +1 -1
  165. package/dist/esm/session/SessionManager.d.ts.map +1 -1
  166. package/dist/esm/session/SessionManager.js +1 -0
  167. package/dist/esm/session/SessionManager.js.map +1 -1
  168. package/dist/esm/session/case/CaseClient.d.ts.map +1 -1
  169. package/dist/esm/session/case/CaseClient.js +34 -30
  170. package/dist/esm/session/case/CaseClient.js.map +1 -1
  171. package/dist/esm/session/case/CaseMessages.d.ts +8 -8
  172. package/dist/esm/session/case/CaseMessages.js +8 -8
  173. package/dist/esm/session/case/CaseMessages.js.map +1 -1
  174. package/dist/esm/session/case/CaseServer.d.ts.map +1 -1
  175. package/dist/esm/session/case/CaseServer.js +26 -23
  176. package/dist/esm/session/case/CaseServer.js.map +1 -1
  177. package/dist/esm/session/pase/PaseClient.d.ts.map +1 -1
  178. package/dist/esm/session/pase/PaseClient.js +4 -1
  179. package/dist/esm/session/pase/PaseClient.js.map +1 -1
  180. package/dist/esm/session/pase/PaseServer.d.ts.map +1 -1
  181. package/dist/esm/session/pase/PaseServer.js +4 -1
  182. package/dist/esm/session/pase/PaseServer.js.map +1 -1
  183. package/package.json +6 -6
  184. package/src/certificate/AttestationCertificateManager.ts +2 -2
  185. package/src/certificate/CertificateAuthority.ts +7 -7
  186. package/src/certificate/CertificateManager.ts +22 -11
  187. package/src/certificate/DeviceCertification.ts +5 -3
  188. package/src/events/OccurrenceManager.ts +16 -9
  189. package/src/fabric/Fabric.ts +24 -15
  190. package/src/fabric/FabricAuthority.ts +7 -0
  191. package/src/fabric/FabricManager.ts +9 -2
  192. package/src/fabric/TestFabric.ts +73 -0
  193. package/src/fabric/index.ts +1 -0
  194. package/src/groups/{FabricGroupsManager.ts → FabricGroups.ts} +4 -4
  195. package/src/groups/KeySets.ts +1 -1
  196. package/src/groups/index.ts +1 -1
  197. package/src/interaction/{AccessControlManager.ts → FabricAccessControl.ts} +61 -25
  198. package/src/interaction/index.ts +1 -1
  199. package/src/mdns/MdnsScanner.ts +2 -6
  200. package/src/peer/ControllerCommissioningFlow.ts +2 -4
  201. package/src/protocol/DeviceCommissioner.ts +1 -1
  202. package/src/protocol/MessageExchange.ts +14 -0
  203. package/src/session/NodeSession.ts +1 -1
  204. package/src/session/SessionManager.ts +1 -0
  205. package/src/session/case/CaseClient.ts +34 -30
  206. package/src/session/case/CaseMessages.ts +8 -8
  207. package/src/session/case/CaseServer.ts +27 -23
  208. package/src/session/pase/PaseClient.ts +4 -1
  209. package/src/session/pase/PaseServer.ts +4 -1
  210. package/dist/cjs/groups/FabricGroupsManager.d.ts.map +0 -1
  211. package/dist/cjs/groups/FabricGroupsManager.js.map +0 -6
  212. package/dist/cjs/interaction/AccessControlManager.d.ts.map +0 -1
  213. package/dist/cjs/interaction/AccessControlManager.js.map +0 -6
  214. package/dist/esm/groups/FabricGroupsManager.d.ts.map +0 -1
  215. package/dist/esm/groups/FabricGroupsManager.js.map +0 -6
  216. package/dist/esm/interaction/AccessControlManager.d.ts.map +0 -1
  217. package/dist/esm/interaction/AccessControlManager.js.map +0 -6
@@ -66,6 +66,13 @@ export class FabricAuthority {
66
66
  this.#config = context.config;
67
67
  }
68
68
 
69
+ /**
70
+ * Access the certificate authority.
71
+ */
72
+ get ca() {
73
+ return this.#ca;
74
+ }
75
+
69
76
  /**
70
77
  * Obtain the default fabric for this authority.
71
78
  */
@@ -160,6 +160,9 @@ export class FabricManager {
160
160
  this.#fabrics.set(fabricIndex, fabric);
161
161
  fabric.addRemoveCallback(async () => this.removeFabric(fabricIndex));
162
162
  fabric.persistCallback = (isUpdate = true) => {
163
+ if (!this.#storage) {
164
+ return;
165
+ }
163
166
  const persistResult = this.persistFabrics();
164
167
  return MaybePromise.then(persistResult, () => {
165
168
  if (isUpdate) {
@@ -184,7 +187,9 @@ export class FabricManager {
184
187
  `Fabric with index ${fabricIndex} cannot be removed because it does not exist.`,
185
188
  );
186
189
  this.#fabrics.delete(fabricIndex);
187
- await this.persistFabrics();
190
+ if (this.#storage) {
191
+ await this.persistFabrics();
192
+ }
188
193
  await fabric.storage?.clearAll();
189
194
  this.#events.deleted.emit(fabric);
190
195
  }
@@ -253,7 +258,9 @@ export class FabricManager {
253
258
  );
254
259
  }
255
260
  this.#fabrics.set(fabricIndex, fabric);
256
- await this.persistFabrics();
261
+ if (this.#storage) {
262
+ await this.persistFabrics();
263
+ }
257
264
  this.#events.updated.emit(fabric);
258
265
  }
259
266
 
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Project CHIP Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { CertificateAuthority } from "#certificate/CertificateAuthority.js";
8
+ import { ImplementationError, nonentropic } from "#general";
9
+ import { FabricIndex, VendorId } from "#types";
10
+ import { FabricAuthority } from "./FabricAuthority.js";
11
+ import { FabricManager } from "./FabricManager.js";
12
+
13
+ /**
14
+ * WARNING: ONLY FOR USE IN PROTECTED TESTING ENVIRONMENTS WHERE SECURITY IS NOT A CONCERN
15
+ *
16
+ * Generate a fabric useful for testing purposes.
17
+ *
18
+ * The properties of the fabric, including crypto matter, are stable with regards to {@link index}.
19
+ */
20
+ export async function TestFabric(options: TestFabric.Options = {}) {
21
+ const authority = await TestFabric.Authority(options);
22
+
23
+ return authority.createFabric();
24
+ }
25
+
26
+ export namespace TestFabric {
27
+ /**
28
+ * WARNING: ONLY FOR USE IN PROTECTED TESTING ENVIRONMENTS WHERE SECURITY IS NOT A CONCERN
29
+ *
30
+ * Obtain a test authority.
31
+ *
32
+ * Crypto matter is stable with respect to {@link index}.
33
+ */
34
+ export async function Authority({ index, fabrics }: Options = {}) {
35
+ if (index === undefined) {
36
+ if (fabrics) {
37
+ index = fabrics.allocateFabricIndex();
38
+ } else {
39
+ index = 1;
40
+ }
41
+ }
42
+
43
+ return forFabric(index, async () => {
44
+ const authority = new FabricAuthority({
45
+ ca: await CertificateAuthority.create(),
46
+ config: {
47
+ adminFabricLabel: `mock-fabric-${index}`,
48
+ adminVendorId: VendorId(0xfff1),
49
+ fabricIndex: FabricIndex(index),
50
+ },
51
+ fabrics: fabrics ?? new FabricManager(),
52
+ });
53
+
54
+ const createFabric = authority.createFabric.bind(authority);
55
+ authority.createFabric = () => forFabric(index ?? 1, createFabric);
56
+
57
+ return authority;
58
+ });
59
+ }
60
+
61
+ export interface Options {
62
+ index?: number;
63
+ fabrics?: FabricManager;
64
+ }
65
+ }
66
+
67
+ async function forFabric<T>(index: number, actor: () => Promise<T>): Promise<T> {
68
+ if (index < 1 || index > 254) {
69
+ throw new ImplementationError("Test fabric indexes must be in the range 1-254");
70
+ }
71
+
72
+ return nonentropic(index, actor);
73
+ }
@@ -7,3 +7,4 @@
7
7
  export * from "./Fabric.js";
8
8
  export * from "./FabricAuthority.js";
9
9
  export * from "./FabricManager.js";
10
+ export * from "./TestFabric.js";
@@ -15,7 +15,7 @@ export const GROUP_SECURITY_INFO = Bytes.fromString("GroupKey v1.0");
15
15
  /**
16
16
  * Class that contains an operational view on the Group Keys for a fabric
17
17
  */
18
- export class FabricGroupsManager {
18
+ export class FabricGroups {
19
19
  #fabric: Fabric;
20
20
  #groups: Groups;
21
21
  #messagingState: MessagingState;
@@ -122,11 +122,11 @@ export class FabricGroupsManager {
122
122
 
123
123
  // Lets pre-calculate the operational keys
124
124
  const operationalId = this.#fabric.operationalId;
125
- const operationalEpochKey0 = await Crypto.hkdf(epochKey0, operationalId, GROUP_SECURITY_INFO);
125
+ const operationalEpochKey0 = await Crypto.createHkdfKey(epochKey0, operationalId, GROUP_SECURITY_INFO);
126
126
  const operationalEpochKey1 =
127
- epochKey1 !== null ? await Crypto.hkdf(epochKey1, operationalId, GROUP_SECURITY_INFO) : null;
127
+ epochKey1 !== null ? await Crypto.createHkdfKey(epochKey1, operationalId, GROUP_SECURITY_INFO) : null;
128
128
  const operationalEpochKey2 =
129
- epochKey2 !== null ? await Crypto.hkdf(epochKey2, operationalId, GROUP_SECURITY_INFO) : null;
129
+ epochKey2 !== null ? await Crypto.createHkdfKey(epochKey2, operationalId, GROUP_SECURITY_INFO) : null;
130
130
  this.#keySets.add({
131
131
  ...groupKeySet,
132
132
  operationalEpochKey0,
@@ -143,7 +143,7 @@ export class KeySets<T extends OperationalKeySet> extends BasicSet<T> {
143
143
  /** Calculates a group session id based on the operational group key. */
144
144
  async sessionIdFromKey(operationalGroupKey: Uint8Array) {
145
145
  // GroupKeyHash is an array of 2 bytes (16 bits) per Crypto_KDF
146
- const groupKeyHash = await Crypto.hkdf(operationalGroupKey, new Uint8Array(), GROUP_KEY_INFO, 2);
146
+ const groupKeyHash = await Crypto.createHkdfKey(operationalGroupKey, new Uint8Array(), GROUP_KEY_INFO, 2);
147
147
 
148
148
  // GroupSessionId is computed by considering the GroupKeyHash as a Big-Endian value. GroupSessionId is a scalar.
149
149
  // Its use in fields within messages may cause a re-serialization into a different byte order than the one used
@@ -4,5 +4,5 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- export * from "./FabricGroupsManager.js";
7
+ export * from "./FabricGroups.js";
8
8
  export * from "./KeySets.js";
@@ -5,7 +5,8 @@
5
5
  */
6
6
  import { Subject } from "#action/server/Subject.js";
7
7
  import { AccessControl } from "#clusters/access-control";
8
- import { MatterFlowError } from "#general";
8
+ import { Fabric } from "#fabric/Fabric.js";
9
+ import { InternalError, Logger, MatterFlowError } from "#general";
9
10
  import { AccessLevel } from "#model";
10
11
  import {
11
12
  CaseAuthenticatedTag,
@@ -20,6 +21,8 @@ import {
20
21
  } from "#types";
21
22
  import { AccessControl as AccessControlContext } from "../action/server/AccessControl.js";
22
23
 
24
+ const logger = Logger.get("FabricAccessControl");
25
+
23
26
  export type AclEntry = Omit<AccessControl.AccessControlEntry, "privilege"> & {
24
27
  privilege: AccessLevel;
25
28
  };
@@ -59,10 +62,11 @@ export class AccessDeniedError extends StatusResponseError {
59
62
  }
60
63
 
61
64
  /**
62
- * Implements Access Control Logic as per Matter Specification @see {@link MatterSpecification.v12.Core} § 6.6.5.2.
65
+ * Implements Access Control Logic For one fabric as per Matter Specification @see {@link MatterSpecification.v12.Core} § 6.6.5.2.
63
66
  */
64
- export class AccessControlManager {
65
- #aclList: AclList;
67
+ export class FabricAccessControl {
68
+ #fabricIndex: FabricIndex;
69
+ #aclList: AclList = [];
66
70
  #extensionEntryAccessCheck: (
67
71
  aclList: AclList,
68
72
  aclEntry: AclEntry,
@@ -71,9 +75,36 @@ export class AccessControlManager {
71
75
  clusterId: ClusterId,
72
76
  ) => boolean = () => true;
73
77
 
74
- constructor(
75
- aclList: AccessControl.AccessControlEntry[] = [],
76
- extensionEntryAccessCheck?: (
78
+ constructor(fabric?: Fabric) {
79
+ if (fabric === undefined) {
80
+ this.#fabricIndex = FabricIndex.NO_FABRIC;
81
+ // Add the implicit default PASE ACL entry for the fabric
82
+ this.#aclList.push(ImplicitDefaultPaseAclEntry);
83
+ } else {
84
+ this.#fabricIndex = fabric.fabricIndex;
85
+ }
86
+ }
87
+
88
+ set fabricIndex(fabricIndex: FabricIndex) {
89
+ if (this.#fabricIndex === undefined || this.#fabricIndex === FabricIndex.NO_FABRIC) {
90
+ this.#fabricIndex = fabricIndex;
91
+ }
92
+ throw new InternalError("Can not overwrite FabricIndex");
93
+ }
94
+
95
+ /**
96
+ * Public method used to update the Access Control List on changes.
97
+ */
98
+ set aclList(aclList: AccessControl.AccessControlEntry[]) {
99
+ if (aclList.some(({ fabricIndex }) => fabricIndex !== this.#fabricIndex)) {
100
+ throw new InternalError("ACL entries must match the fabric index of the manager");
101
+ }
102
+ this.#aclList = [...aclList] as unknown as AclList; // It is the same structure we just use an internal type for privilege
103
+ logger.info("ACL List updated for FabricIndex ", this.#fabricIndex, this.#aclList);
104
+ }
105
+
106
+ set extensionEntryAccessCheck(
107
+ func: (
77
108
  aclList: AclList,
78
109
  aclEntry: AclEntry,
79
110
  subjectDesc: IncomingSubjectDescriptor,
@@ -81,24 +112,31 @@ export class AccessControlManager {
81
112
  clusterId: ClusterId,
82
113
  ) => boolean,
83
114
  ) {
84
- this.#aclList = aclList as unknown as AclList; // It is the same structure we just use an internal type for privilege
85
- if (extensionEntryAccessCheck !== undefined) {
86
- this.#extensionEntryAccessCheck = extensionEntryAccessCheck;
87
- }
115
+ this.#extensionEntryAccessCheck = func;
88
116
  }
89
117
 
90
118
  /**
91
- * Public method used to update the Access Control List on changes.
119
+ * Implements the access control check for the given context, location and endpoint and is called by the
120
+ * InteractionServer. The method returns the list of granted Access privileges for the given context, location and
121
+ * endpoint.
92
122
  */
93
- updateAccessControlList(aclList: AccessControl.AccessControlEntry[] = []): void {
94
- this.#aclList = [...aclList] as unknown as AclList; // It is the same structure we just use an internal type for privilege
95
- }
123
+ accessLevelsFor(
124
+ context: AccessControlContext.Session,
125
+ location: AccessControlContext.Location,
126
+ endpoint?: AclEndpointContext,
127
+ ): AccessLevel[] {
128
+ if (location.cluster === undefined) {
129
+ // Without a cluster, internal behaviors are only accessible internally, so this is an irrelevant placeholder
130
+ logger.warn("Access control check without cluster, returning View access level");
131
+ return [AccessLevel.View];
132
+ }
133
+ if (endpoint === undefined) {
134
+ // Without an endpoint, we can't determine access levels, ???
135
+ logger.warn("Access control check without endpoint, returning View access level");
136
+ return [AccessLevel.View];
137
+ }
96
138
 
97
- /**
98
- * Get the Access Control List for a given fabric.
99
- */
100
- #getAccessControlEntriesForFabric(fabricIndex: FabricIndex): AclList {
101
- return this.#aclList.filter(entry => entry.fabricIndex === fabricIndex);
139
+ return this.#getGrantedPrivileges(context, endpoint, location.cluster);
102
140
  }
103
141
 
104
142
  /**
@@ -152,15 +190,13 @@ export class AccessControlManager {
152
190
  /**
153
191
  * Determines the granted privileges for the given session, endpoint, and cluster ID and returns them.
154
192
  */
155
- getGrantedPrivileges(
193
+ #getGrantedPrivileges(
156
194
  context: AccessControlContext.Session,
157
195
  endpoint: AclEndpointContext,
158
196
  clusterId: ClusterId,
159
197
  ): AccessLevel[] {
160
198
  const endpointId = endpoint.id;
161
- const fabric = context.fabric;
162
199
  const subjectDesc = this.#getIsdFromMessage(context);
163
- const acl = fabric ? this.#getAccessControlEntriesForFabric(fabric) : [ImplicitDefaultPaseAclEntry];
164
200
 
165
201
  // Granted privileges set is initially empty
166
202
  const grantedPrivileges = new Set<AccessLevel>();
@@ -170,7 +206,7 @@ export class AccessControlManager {
170
206
  this.#addGrantedPrivilege(grantedPrivileges, AccessLevel.Administer);
171
207
  }
172
208
 
173
- for (const aclEntry of acl) {
209
+ for (const aclEntry of this.#aclList) {
174
210
  if (grantedPrivileges.has(AccessLevel.Administer)) {
175
211
  // End checking if highest privilege is granted
176
212
  break;
@@ -254,7 +290,7 @@ export class AccessControlManager {
254
290
  }
255
291
 
256
292
  // Extensions processing must not fail
257
- if (!this.#extensionEntryAccessCheck(acl, aclEntry, subjectDesc, endpoint, clusterId)) {
293
+ if (!this.#extensionEntryAccessCheck(this.#aclList, aclEntry, subjectDesc, endpoint, clusterId)) {
258
294
  continue;
259
295
  }
260
296
 
@@ -4,10 +4,10 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
- export * from "./AccessControlManager.js";
8
7
  export * from "./AttributeDataDecoder.js";
9
8
  export * from "./AttributeDataEncoder.js";
10
9
  export * from "./EventDataDecoder.js";
10
+ export * from "./FabricAccessControl.js";
11
11
  export * from "./InteractionClient.js";
12
12
  export * from "./InteractionMessenger.js";
13
13
  export * from "./Subscription.js";
@@ -120,9 +120,7 @@ export interface MdnsScannerTargetCriteria {
120
120
  * queries to discover various types of Matter device types and listens for announcements.
121
121
  */
122
122
  export class MdnsScanner implements Scanner {
123
- get type() {
124
- return ChannelType.UDP;
125
- }
123
+ readonly type = ChannelType.UDP;
126
124
 
127
125
  static async create(network: Network, options?: { enableIpv4?: boolean; netInterface?: string }) {
128
126
  const { enableIpv4, netInterface } = options ?? {};
@@ -502,9 +500,7 @@ export class MdnsScanner implements Scanner {
502
500
  const { timer, resolver, resolveOnUpdatedRecords, commissionable } = waiter;
503
501
  if (isUpdatedRecord && !resolveOnUpdatedRecords) return;
504
502
  logger.debug(`Finishing waiter for query ${queryId}, resolving: ${resolvePromise}`);
505
- if (timer !== undefined) {
506
- timer.stop();
507
- }
503
+ timer?.stop();
508
504
  if (resolvePromise) {
509
505
  resolver();
510
506
  }
@@ -1274,10 +1274,8 @@ export class ControllerCommissioningFlow {
1274
1274
  // Assume concurrent connections are supported if not know (which should not be the case when we came here)
1275
1275
  isConcurrentFlow,
1276
1276
  );
1277
- } catch (error) {
1278
- const commError = new OperativeConnectionFailedError("Operative reconnection with device failed");
1279
- commError.cause = error;
1280
- throw commError;
1277
+ } catch (cause) {
1278
+ throw new OperativeConnectionFailedError("Operative reconnection with device failed", { cause });
1281
1279
  }
1282
1280
 
1283
1281
  reArmFailsafeInterval.stop();
@@ -153,7 +153,7 @@ export class DeviceCommissioner {
153
153
  this.#context.secureChannelProtocol.setPaseCommissioner(
154
154
  await PaseServer.fromPin(this.#context.sessions, this.#context.commissioningConfig.values.passcode, {
155
155
  iterations: 1000,
156
- salt: Crypto.get().getRandomData(32),
156
+ salt: Crypto.getRandomData(32),
157
157
  }),
158
158
  );
159
159
 
@@ -202,6 +202,7 @@ export class MessageExchange {
202
202
  readonly #exchangeId: number;
203
203
  readonly #protocolId: number;
204
204
  readonly #closed = AsyncObservable<[]>();
205
+ readonly #closing = AsyncObservable<[]>();
205
206
  readonly #useMRP: boolean;
206
207
 
207
208
  constructor(
@@ -252,10 +253,19 @@ export class MessageExchange {
252
253
  );
253
254
  }
254
255
 
256
+ /** Emits when the exchange is actually closed. This happens after all Retries and Communication are done. */
255
257
  get closed() {
256
258
  return this.#closed;
257
259
  }
258
260
 
261
+ /**
262
+ * Emit when the exchange is closing, but not yet closed. We only wait for acks and retries to happen, but the
263
+ * actual interaction logic is already done.
264
+ */
265
+ get closing() {
266
+ return this.#closing;
267
+ }
268
+
259
269
  get isClosing() {
260
270
  return this.#isClosing;
261
271
  }
@@ -705,6 +715,7 @@ export class MessageExchange {
705
715
  return this.#close();
706
716
  }
707
717
  this.#isClosing = true;
718
+ this.#closing.emit();
708
719
 
709
720
  if (this.#receivedMessageToAck !== undefined) {
710
721
  this.#receivedMessageAckTimer.stop();
@@ -742,6 +753,9 @@ export class MessageExchange {
742
753
  }
743
754
 
744
755
  async #close() {
756
+ if (!this.#isClosing) {
757
+ this.#closing.emit();
758
+ }
745
759
  this.#isClosing = true;
746
760
  this.#retransmissionTimer?.stop();
747
761
  this.#closeTimer?.stop();
@@ -72,7 +72,7 @@ export class NodeSession extends SecureSession {
72
72
  peerSessionParameters,
73
73
  caseAuthenticatedTags,
74
74
  } = args;
75
- const keys = await Crypto.hkdf(
75
+ const keys = await Crypto.createHkdfKey(
76
76
  sharedSecret,
77
77
  salt,
78
78
  isResumption ? SESSION_RESUMPTION_KEYS_INFO : SESSION_KEYS_INFO,
@@ -699,6 +699,7 @@ export class SessionManager {
699
699
  compressIdRange(upperBound: number) {
700
700
  this.#idUpperBound = upperBound;
701
701
  this.#nextSessionId = Crypto.getRandomUInt32() % upperBound;
702
+ if (this.#nextSessionId === 0) this.#nextSessionId++;
702
703
  }
703
704
  }
704
705
 
@@ -52,8 +52,8 @@ export class CaseClient {
52
52
  // Generate pairing info
53
53
  const initiatorRandom = Crypto.getRandom();
54
54
  const initiatorSessionId = await this.#sessions.getNextAvailableSessionId(); // Initiator Session Id
55
- const { operationalIdentityProtectionKey, operationalCert: nodeOpCert, intermediateCACert } = fabric;
56
- const { publicKey: initiatorEcdhPublicKey, ecdh } = await Crypto.ecdhGeneratePublicKey();
55
+ const { operationalIdentityProtectionKey, operationalCert: localNoc, intermediateCACert: localIcac } = fabric;
56
+ const localKey = await Crypto.createKeyPair();
57
57
 
58
58
  // Send sigma1
59
59
  let sigma1Bytes;
@@ -61,7 +61,7 @@ export class CaseClient {
61
61
  let resumptionRecord = this.#sessions.findResumptionRecordByAddress(fabric.addressOf(peerNodeId));
62
62
  if (resumptionRecord !== undefined) {
63
63
  const { sharedSecret, resumptionId } = resumptionRecord;
64
- const resumeKey = await Crypto.hkdf(
64
+ const resumeKey = await Crypto.createHkdfKey(
65
65
  sharedSecret,
66
66
  Bytes.concat(initiatorRandom, resumptionId),
67
67
  KDFSR1_KEY_INFO,
@@ -70,7 +70,7 @@ export class CaseClient {
70
70
  sigma1Bytes = await messenger.sendSigma1({
71
71
  initiatorSessionId,
72
72
  destinationId: await fabric.currentDestinationIdFor(peerNodeId, initiatorRandom),
73
- initiatorEcdhPublicKey,
73
+ initiatorEcdhPublicKey: localKey.publicBits,
74
74
  initiatorRandom,
75
75
  resumptionId,
76
76
  initiatorResumeMic,
@@ -80,7 +80,7 @@ export class CaseClient {
80
80
  sigma1Bytes = await messenger.sendSigma1({
81
81
  initiatorSessionId,
82
82
  destinationId: await fabric.currentDestinationIdFor(peerNodeId, initiatorRandom),
83
- initiatorEcdhPublicKey,
83
+ initiatorEcdhPublicKey: localKey.publicBits,
84
84
  initiatorRandom,
85
85
  initiatorSessionParams: this.#sessions.sessionParameters,
86
86
  });
@@ -106,7 +106,7 @@ export class CaseClient {
106
106
  };
107
107
 
108
108
  const resumeSalt = Bytes.concat(initiatorRandom, resumptionId);
109
- const resumeKey = await Crypto.hkdf(sharedSecret, resumeSalt, KDFSR2_KEY_INFO);
109
+ const resumeKey = await Crypto.createHkdfKey(sharedSecret, resumeSalt, KDFSR2_KEY_INFO);
110
110
  Crypto.decrypt(resumeKey, resumeMic, RESUME2_MIC_NONCE);
111
111
 
112
112
  const secureSessionSalt = Bytes.concat(initiatorRandom, resumptionRecord.resumptionId);
@@ -134,7 +134,7 @@ export class CaseClient {
134
134
  } else {
135
135
  // Process sigma2
136
136
  const {
137
- responderEcdhPublicKey: peerEcdhPublicKey,
137
+ responderEcdhPublicKey: peerKey,
138
138
  encrypted: peerEncrypted,
139
139
  responderRandom,
140
140
  responderSessionId: peerSessionId,
@@ -145,33 +145,33 @@ export class CaseClient {
145
145
  ...exchange.session.parameters,
146
146
  ...(responderSessionParams ?? {}),
147
147
  };
148
- const sharedSecret = await Crypto.ecdhGenerateSecret(peerEcdhPublicKey, ecdh);
148
+ const sharedSecret = await Crypto.generateDhSecret(localKey, PublicKey(peerKey));
149
149
  const sigma2Salt = Bytes.concat(
150
150
  operationalIdentityProtectionKey,
151
151
  responderRandom,
152
- peerEcdhPublicKey,
153
- await Crypto.hash(sigma1Bytes),
152
+ peerKey,
153
+ await Crypto.computeSha256(sigma1Bytes),
154
154
  );
155
- const sigma2Key = await Crypto.hkdf(sharedSecret, sigma2Salt, KDFSR2_INFO);
155
+ const sigma2Key = await Crypto.createHkdfKey(sharedSecret, sigma2Salt, KDFSR2_INFO);
156
156
  const peerEncryptedData = Crypto.decrypt(sigma2Key, peerEncrypted, TBE_DATA2_NONCE);
157
157
  const {
158
- nodeOpCert: peerNewOpCert,
159
- intermediateCACert: peerIntermediateCACert,
158
+ responderNoc: peerNoc,
159
+ responderIcac: peerIcac,
160
160
  signature: peerSignature,
161
161
  resumptionId: peerResumptionId,
162
162
  } = TlvEncryptedDataSigma2.decode(peerEncryptedData);
163
163
  const peerSignatureData = TlvSignedData.encode({
164
- nodeOpCert: peerNewOpCert,
165
- intermediateCACert: peerIntermediateCACert,
166
- ecdhPublicKey: peerEcdhPublicKey,
167
- peerEcdhPublicKey: initiatorEcdhPublicKey,
164
+ responderNoc: peerNoc,
165
+ responderIcac: peerIcac,
166
+ responderPublicKey: peerKey,
167
+ initiatorPublicKey: localKey.publicBits,
168
168
  });
169
169
  const {
170
170
  ellipticCurvePublicKey: peerPublicKey,
171
171
  subject: { fabricId: peerFabricIdNOCert, nodeId: peerNodeIdNOCert },
172
- } = TlvOperationalCertificate.decode(peerNewOpCert);
172
+ } = TlvOperationalCertificate.decode(peerNoc);
173
173
 
174
- await Crypto.verify(PublicKey(peerPublicKey), peerSignatureData, peerSignature);
174
+ await Crypto.verifyEcdsa(PublicKey(peerPublicKey), peerSignatureData, peerSignature);
175
175
 
176
176
  if (peerNodeIdNOCert !== peerNodeId) {
177
177
  throw new UnexpectedDataError(
@@ -183,10 +183,10 @@ export class CaseClient {
183
183
  `The fabric ID in the peer certificate ${peerFabricIdNOCert} doesn't match the expected fabric ID ${fabric.fabricId}`,
184
184
  );
185
185
  }
186
- if (peerIntermediateCACert !== undefined) {
186
+ if (peerIcac !== undefined) {
187
187
  const {
188
188
  subject: { fabricId: peerFabricIdIcaCert },
189
- } = TlvIntermediateCertificate.decode(peerIntermediateCACert);
189
+ } = TlvIntermediateCertificate.decode(peerIcac);
190
190
 
191
191
  if (peerFabricIdIcaCert !== undefined && peerFabricIdIcaCert !== fabric.fabricId) {
192
192
  throw new UnexpectedDataError(
@@ -194,22 +194,26 @@ export class CaseClient {
194
194
  );
195
195
  }
196
196
  }
197
- await fabric.verifyCredentials(peerNewOpCert, peerIntermediateCACert);
197
+ await fabric.verifyCredentials(peerNoc, peerIcac);
198
198
 
199
199
  // Generate and send sigma3
200
200
  const sigma3Salt = Bytes.concat(
201
201
  operationalIdentityProtectionKey,
202
- await Crypto.hash([sigma1Bytes, sigma2Bytes]),
202
+ await Crypto.computeSha256([sigma1Bytes, sigma2Bytes]),
203
203
  );
204
- const sigma3Key = await Crypto.hkdf(sharedSecret, sigma3Salt, KDFSR3_INFO);
204
+ const sigma3Key = await Crypto.createHkdfKey(sharedSecret, sigma3Salt, KDFSR3_INFO);
205
205
  const signatureData = TlvSignedData.encode({
206
- nodeOpCert,
207
- intermediateCACert,
208
- ecdhPublicKey: initiatorEcdhPublicKey,
209
- peerEcdhPublicKey,
206
+ responderNoc: localNoc,
207
+ responderIcac: localIcac,
208
+ responderPublicKey: localKey.publicBits,
209
+ initiatorPublicKey: peerKey,
210
210
  });
211
211
  const signature = await fabric.sign(signatureData);
212
- const encryptedData = TlvEncryptedDataSigma3.encode({ nodeOpCert, intermediateCACert, signature });
212
+ const encryptedData = TlvEncryptedDataSigma3.encode({
213
+ responderNoc: localNoc,
214
+ responderIcac: localIcac,
215
+ signature,
216
+ });
213
217
  const encrypted = Crypto.encrypt(sigma3Key, encryptedData, TBE_DATA3_NONCE);
214
218
  const sigma3Bytes = await messenger.sendSigma3({ encrypted });
215
219
  await messenger.waitForSuccess("Sigma3-Success");
@@ -218,7 +222,7 @@ export class CaseClient {
218
222
  const { caseAuthenticatedTags } = resumptionRecord ?? {}; // Even if resumption does not work try to reuse the caseAuthenticatedTags
219
223
  const secureSessionSalt = Bytes.concat(
220
224
  operationalIdentityProtectionKey,
221
- await Crypto.hash([sigma1Bytes, sigma2Bytes, sigma3Bytes]),
225
+ await Crypto.computeSha256([sigma1Bytes, sigma2Bytes, sigma3Bytes]),
222
226
  );
223
227
  secureSession = await this.#sessions.createSecureSession({
224
228
  sessionId: initiatorSessionId,
@@ -59,23 +59,23 @@ export const TlvCaseSigma3 = TlvObject({
59
59
 
60
60
  /** @see {@link MatterSpecification.v10.Core} § 4.13.2.3 */
61
61
  export const TlvSignedData = TlvObject({
62
- nodeOpCert: TlvField(1, TlvByteString),
63
- intermediateCACert: TlvOptionalField(2, TlvByteString),
64
- ecdhPublicKey: TlvField(3, TlvByteString.bound({ length: CRYPTO_PUBLIC_KEY_SIZE_BYTES })),
65
- peerEcdhPublicKey: TlvField(4, TlvByteString.bound({ length: CRYPTO_PUBLIC_KEY_SIZE_BYTES })),
62
+ responderNoc: TlvField(1, TlvByteString),
63
+ responderIcac: TlvOptionalField(2, TlvByteString),
64
+ responderPublicKey: TlvField(3, TlvByteString.bound({ length: CRYPTO_PUBLIC_KEY_SIZE_BYTES })),
65
+ initiatorPublicKey: TlvField(4, TlvByteString.bound({ length: CRYPTO_PUBLIC_KEY_SIZE_BYTES })),
66
66
  });
67
67
 
68
68
  /** @see {@link MatterSpecification.v10.Core} § 4.13.2.3 */
69
69
  export const TlvEncryptedDataSigma2 = TlvObject({
70
- nodeOpCert: TlvField(1, TlvByteString),
71
- intermediateCACert: TlvOptionalField(2, TlvByteString),
70
+ responderNoc: TlvField(1, TlvByteString),
71
+ responderIcac: TlvOptionalField(2, TlvByteString),
72
72
  signature: TlvField(3, TlvByteString.bound({ length: CASE_SIGNATURE_LENGTH })),
73
73
  resumptionId: TlvField(4, TlvByteString.bound({ length: 16 })),
74
74
  });
75
75
 
76
76
  /** @see {@link MatterSpecification.v10.Core} § 4.13.2.3 */
77
77
  export const TlvEncryptedDataSigma3 = TlvObject({
78
- nodeOpCert: TlvField(1, TlvByteString),
79
- intermediateCACert: TlvOptionalField(2, TlvByteString),
78
+ responderNoc: TlvField(1, TlvByteString),
79
+ responderIcac: TlvOptionalField(2, TlvByteString),
80
80
  signature: TlvField(3, TlvByteString.bound({ length: CASE_SIGNATURE_LENGTH })),
81
81
  });