@matter/protocol 0.14.0 → 0.14.1-alpha.0-20250606-a9bcd03f9

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 (312) hide show
  1. package/dist/cjs/action/server/AccessControl.d.ts +5 -7
  2. package/dist/cjs/action/server/AccessControl.d.ts.map +1 -1
  3. package/dist/cjs/action/server/AccessControl.js.map +1 -1
  4. package/dist/cjs/action/server/AttributeWriteResponse.d.ts.map +1 -1
  5. package/dist/cjs/action/server/AttributeWriteResponse.js +23 -0
  6. package/dist/cjs/action/server/AttributeWriteResponse.js.map +1 -1
  7. package/dist/cjs/action/server/CommandInvokeResponse.d.ts.map +1 -1
  8. package/dist/cjs/action/server/CommandInvokeResponse.js +24 -1
  9. package/dist/cjs/action/server/CommandInvokeResponse.js.map +1 -1
  10. package/dist/cjs/action/server/DataResponse.d.ts +1 -1
  11. package/dist/cjs/action/server/DataResponse.d.ts.map +1 -1
  12. package/dist/cjs/action/server/Subject.d.ts +25 -0
  13. package/dist/cjs/action/server/Subject.d.ts.map +1 -0
  14. package/dist/cjs/action/server/Subject.js +54 -0
  15. package/dist/cjs/action/server/Subject.js.map +6 -0
  16. package/dist/cjs/action/server/index.d.ts +1 -0
  17. package/dist/cjs/action/server/index.d.ts.map +1 -1
  18. package/dist/cjs/action/server/index.js +1 -0
  19. package/dist/cjs/action/server/index.js.map +1 -1
  20. package/dist/cjs/certificate/DeviceCertification.d.ts +2 -2
  21. package/dist/cjs/certificate/DeviceCertification.d.ts.map +1 -1
  22. package/dist/cjs/certificate/DeviceCertification.js.map +1 -1
  23. package/dist/cjs/cluster/client/AttributeClient.d.ts +3 -3
  24. package/dist/cjs/cluster/client/AttributeClient.d.ts.map +1 -1
  25. package/dist/cjs/cluster/client/AttributeClient.js +14 -2
  26. package/dist/cjs/cluster/client/AttributeClient.js.map +1 -1
  27. package/dist/cjs/cluster/client/ClusterClient.d.ts +3 -2
  28. package/dist/cjs/cluster/client/ClusterClient.d.ts.map +1 -1
  29. package/dist/cjs/cluster/client/ClusterClient.js +60 -1
  30. package/dist/cjs/cluster/client/ClusterClient.js.map +1 -1
  31. package/dist/cjs/cluster/client/ClusterClientTypes.d.ts +33 -8
  32. package/dist/cjs/cluster/client/ClusterClientTypes.d.ts.map +1 -1
  33. package/dist/cjs/cluster/client/EventClient.d.ts +3 -3
  34. package/dist/cjs/cluster/client/EventClient.d.ts.map +1 -1
  35. package/dist/cjs/cluster/client/EventClient.js +7 -0
  36. package/dist/cjs/cluster/client/EventClient.js.map +1 -1
  37. package/dist/cjs/codec/MessageCodec.d.ts.map +1 -1
  38. package/dist/cjs/codec/MessageCodec.js +31 -6
  39. package/dist/cjs/codec/MessageCodec.js.map +1 -1
  40. package/dist/cjs/fabric/Fabric.d.ts +20 -30
  41. package/dist/cjs/fabric/Fabric.d.ts.map +1 -1
  42. package/dist/cjs/fabric/Fabric.js +38 -62
  43. package/dist/cjs/fabric/Fabric.js.map +2 -2
  44. package/dist/cjs/fabric/FabricManager.d.ts.map +1 -1
  45. package/dist/cjs/fabric/FabricManager.js +10 -4
  46. package/dist/cjs/fabric/FabricManager.js.map +1 -1
  47. package/dist/cjs/groups/FabricGroupsManager.d.ts +46 -0
  48. package/dist/cjs/groups/FabricGroupsManager.d.ts.map +1 -0
  49. package/dist/cjs/groups/FabricGroupsManager.js +155 -0
  50. package/dist/cjs/groups/FabricGroupsManager.js.map +6 -0
  51. package/dist/cjs/groups/Groups.d.ts +34 -0
  52. package/dist/cjs/groups/Groups.d.ts.map +1 -0
  53. package/dist/cjs/groups/Groups.js +89 -0
  54. package/dist/cjs/groups/Groups.js.map +6 -0
  55. package/dist/cjs/groups/KeySets.d.ts +64 -0
  56. package/dist/cjs/groups/KeySets.d.ts.map +1 -0
  57. package/dist/cjs/groups/KeySets.js +179 -0
  58. package/dist/cjs/groups/KeySets.js.map +6 -0
  59. package/dist/cjs/groups/MessagingState.d.ts +24 -0
  60. package/dist/cjs/groups/MessagingState.d.ts.map +1 -0
  61. package/dist/cjs/groups/MessagingState.js +91 -0
  62. package/dist/cjs/groups/MessagingState.js.map +6 -0
  63. package/dist/cjs/groups/index.d.ts +8 -0
  64. package/dist/cjs/groups/index.d.ts.map +1 -0
  65. package/dist/cjs/groups/index.js +25 -0
  66. package/dist/cjs/groups/index.js.map +6 -0
  67. package/dist/cjs/index.d.ts +1 -0
  68. package/dist/cjs/index.d.ts.map +1 -1
  69. package/dist/cjs/index.js +1 -0
  70. package/dist/cjs/index.js.map +1 -1
  71. package/dist/cjs/interaction/AccessControlManager.d.ts +4 -13
  72. package/dist/cjs/interaction/AccessControlManager.d.ts.map +1 -1
  73. package/dist/cjs/interaction/AccessControlManager.js +38 -47
  74. package/dist/cjs/interaction/AccessControlManager.js.map +1 -1
  75. package/dist/cjs/interaction/InteractionClient.d.ts +5 -4
  76. package/dist/cjs/interaction/InteractionClient.d.ts.map +1 -1
  77. package/dist/cjs/interaction/InteractionClient.js +53 -3
  78. package/dist/cjs/interaction/InteractionClient.js.map +1 -1
  79. package/dist/cjs/interaction/InteractionMessenger.d.ts.map +1 -1
  80. package/dist/cjs/interaction/InteractionMessenger.js +15 -0
  81. package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
  82. package/dist/cjs/interaction/Subscription.d.ts +3 -3
  83. package/dist/cjs/interaction/Subscription.d.ts.map +1 -1
  84. package/dist/cjs/interaction/Subscription.js.map +1 -1
  85. package/dist/cjs/peer/PeerAddress.d.ts +1 -0
  86. package/dist/cjs/peer/PeerAddress.d.ts.map +1 -1
  87. package/dist/cjs/peer/PeerAddress.js +5 -0
  88. package/dist/cjs/peer/PeerAddress.js.map +1 -1
  89. package/dist/cjs/peer/PeerSet.d.ts.map +1 -1
  90. package/dist/cjs/peer/PeerSet.js +31 -2
  91. package/dist/cjs/peer/PeerSet.js.map +1 -1
  92. package/dist/cjs/protocol/ChannelManager.d.ts.map +1 -1
  93. package/dist/cjs/protocol/ChannelManager.js +7 -8
  94. package/dist/cjs/protocol/ChannelManager.js.map +1 -1
  95. package/dist/cjs/protocol/ExchangeManager.d.ts.map +1 -1
  96. package/dist/cjs/protocol/ExchangeManager.js +39 -25
  97. package/dist/cjs/protocol/ExchangeManager.js.map +1 -1
  98. package/dist/cjs/protocol/MessageExchange.d.ts +1 -1
  99. package/dist/cjs/protocol/MessageExchange.d.ts.map +1 -1
  100. package/dist/cjs/protocol/MessageExchange.js +32 -4
  101. package/dist/cjs/protocol/MessageExchange.js.map +1 -1
  102. package/dist/cjs/protocol/MessageReceptionState.d.ts +1 -1
  103. package/dist/cjs/securechannel/SecureChannelProtocol.js +1 -1
  104. package/dist/cjs/securechannel/SecureChannelProtocol.js.map +1 -1
  105. package/dist/cjs/session/GroupSession.d.ts +56 -0
  106. package/dist/cjs/session/GroupSession.d.ts.map +1 -0
  107. package/dist/cjs/session/GroupSession.js +188 -0
  108. package/dist/cjs/session/GroupSession.js.map +6 -0
  109. package/dist/cjs/session/InsecureSession.d.ts +2 -1
  110. package/dist/cjs/session/InsecureSession.d.ts.map +1 -1
  111. package/dist/cjs/session/InsecureSession.js +3 -2
  112. package/dist/cjs/session/InsecureSession.js.map +1 -1
  113. package/dist/cjs/session/NodeSession.d.ts +88 -0
  114. package/dist/cjs/session/NodeSession.d.ts.map +1 -0
  115. package/dist/cjs/session/NodeSession.js +318 -0
  116. package/dist/cjs/session/NodeSession.js.map +6 -0
  117. package/dist/cjs/session/SecureSession.d.ts +10 -75
  118. package/dist/cjs/session/SecureSession.d.ts.map +1 -1
  119. package/dist/cjs/session/SecureSession.js +9 -280
  120. package/dist/cjs/session/SecureSession.js.map +2 -2
  121. package/dist/cjs/session/Session.d.ts +6 -5
  122. package/dist/cjs/session/Session.d.ts.map +1 -1
  123. package/dist/cjs/session/Session.js +11 -1
  124. package/dist/cjs/session/Session.js.map +1 -1
  125. package/dist/cjs/session/SessionManager.d.ts +27 -9
  126. package/dist/cjs/session/SessionManager.d.ts.map +1 -1
  127. package/dist/cjs/session/SessionManager.js +83 -5
  128. package/dist/cjs/session/SessionManager.js.map +2 -2
  129. package/dist/cjs/session/case/CaseClient.d.ts +1 -1
  130. package/dist/cjs/session/case/CaseClient.js +2 -2
  131. package/dist/cjs/session/case/CaseClient.js.map +1 -1
  132. package/dist/cjs/session/index.d.ts +2 -0
  133. package/dist/cjs/session/index.d.ts.map +1 -1
  134. package/dist/cjs/session/index.js +2 -0
  135. package/dist/cjs/session/index.js.map +1 -1
  136. package/dist/cjs/session/pase/PaseClient.d.ts +1 -1
  137. package/dist/esm/action/server/AccessControl.d.ts +5 -7
  138. package/dist/esm/action/server/AccessControl.d.ts.map +1 -1
  139. package/dist/esm/action/server/AccessControl.js.map +1 -1
  140. package/dist/esm/action/server/AttributeWriteResponse.d.ts.map +1 -1
  141. package/dist/esm/action/server/AttributeWriteResponse.js +23 -0
  142. package/dist/esm/action/server/AttributeWriteResponse.js.map +1 -1
  143. package/dist/esm/action/server/CommandInvokeResponse.d.ts.map +1 -1
  144. package/dist/esm/action/server/CommandInvokeResponse.js +24 -1
  145. package/dist/esm/action/server/CommandInvokeResponse.js.map +1 -1
  146. package/dist/esm/action/server/DataResponse.d.ts +1 -1
  147. package/dist/esm/action/server/DataResponse.d.ts.map +1 -1
  148. package/dist/esm/action/server/Subject.d.ts +25 -0
  149. package/dist/esm/action/server/Subject.d.ts.map +1 -0
  150. package/dist/esm/action/server/Subject.js +34 -0
  151. package/dist/esm/action/server/Subject.js.map +6 -0
  152. package/dist/esm/action/server/index.d.ts +1 -0
  153. package/dist/esm/action/server/index.d.ts.map +1 -1
  154. package/dist/esm/action/server/index.js +1 -0
  155. package/dist/esm/action/server/index.js.map +1 -1
  156. package/dist/esm/certificate/DeviceCertification.d.ts +2 -2
  157. package/dist/esm/certificate/DeviceCertification.d.ts.map +1 -1
  158. package/dist/esm/certificate/DeviceCertification.js.map +1 -1
  159. package/dist/esm/cluster/client/AttributeClient.d.ts +3 -3
  160. package/dist/esm/cluster/client/AttributeClient.d.ts.map +1 -1
  161. package/dist/esm/cluster/client/AttributeClient.js +13 -1
  162. package/dist/esm/cluster/client/AttributeClient.js.map +1 -1
  163. package/dist/esm/cluster/client/ClusterClient.d.ts +3 -2
  164. package/dist/esm/cluster/client/ClusterClient.d.ts.map +1 -1
  165. package/dist/esm/cluster/client/ClusterClient.js +61 -2
  166. package/dist/esm/cluster/client/ClusterClient.js.map +1 -1
  167. package/dist/esm/cluster/client/ClusterClientTypes.d.ts +33 -8
  168. package/dist/esm/cluster/client/ClusterClientTypes.d.ts.map +1 -1
  169. package/dist/esm/cluster/client/EventClient.d.ts +3 -3
  170. package/dist/esm/cluster/client/EventClient.d.ts.map +1 -1
  171. package/dist/esm/cluster/client/EventClient.js +7 -0
  172. package/dist/esm/cluster/client/EventClient.js.map +1 -1
  173. package/dist/esm/codec/MessageCodec.d.ts.map +1 -1
  174. package/dist/esm/codec/MessageCodec.js +41 -7
  175. package/dist/esm/codec/MessageCodec.js.map +1 -1
  176. package/dist/esm/fabric/Fabric.d.ts +20 -30
  177. package/dist/esm/fabric/Fabric.d.ts.map +1 -1
  178. package/dist/esm/fabric/Fabric.js +38 -62
  179. package/dist/esm/fabric/Fabric.js.map +2 -2
  180. package/dist/esm/fabric/FabricManager.d.ts.map +1 -1
  181. package/dist/esm/fabric/FabricManager.js +10 -4
  182. package/dist/esm/fabric/FabricManager.js.map +1 -1
  183. package/dist/esm/groups/FabricGroupsManager.d.ts +46 -0
  184. package/dist/esm/groups/FabricGroupsManager.d.ts.map +1 -0
  185. package/dist/esm/groups/FabricGroupsManager.js +135 -0
  186. package/dist/esm/groups/FabricGroupsManager.js.map +6 -0
  187. package/dist/esm/groups/Groups.d.ts +34 -0
  188. package/dist/esm/groups/Groups.d.ts.map +1 -0
  189. package/dist/esm/groups/Groups.js +69 -0
  190. package/dist/esm/groups/Groups.js.map +6 -0
  191. package/dist/esm/groups/KeySets.d.ts +64 -0
  192. package/dist/esm/groups/KeySets.d.ts.map +1 -0
  193. package/dist/esm/groups/KeySets.js +159 -0
  194. package/dist/esm/groups/KeySets.js.map +6 -0
  195. package/dist/esm/groups/MessagingState.d.ts +24 -0
  196. package/dist/esm/groups/MessagingState.d.ts.map +1 -0
  197. package/dist/esm/groups/MessagingState.js +71 -0
  198. package/dist/esm/groups/MessagingState.js.map +6 -0
  199. package/dist/esm/groups/index.d.ts +8 -0
  200. package/dist/esm/groups/index.d.ts.map +1 -0
  201. package/dist/esm/groups/index.js +8 -0
  202. package/dist/esm/groups/index.js.map +6 -0
  203. package/dist/esm/index.d.ts +1 -0
  204. package/dist/esm/index.d.ts.map +1 -1
  205. package/dist/esm/index.js +1 -0
  206. package/dist/esm/index.js.map +1 -1
  207. package/dist/esm/interaction/AccessControlManager.d.ts +4 -13
  208. package/dist/esm/interaction/AccessControlManager.d.ts.map +1 -1
  209. package/dist/esm/interaction/AccessControlManager.js +39 -48
  210. package/dist/esm/interaction/AccessControlManager.js.map +1 -1
  211. package/dist/esm/interaction/InteractionClient.d.ts +5 -4
  212. package/dist/esm/interaction/InteractionClient.d.ts.map +1 -1
  213. package/dist/esm/interaction/InteractionClient.js +54 -4
  214. package/dist/esm/interaction/InteractionClient.js.map +1 -1
  215. package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
  216. package/dist/esm/interaction/InteractionMessenger.js +15 -0
  217. package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
  218. package/dist/esm/interaction/Subscription.d.ts +3 -3
  219. package/dist/esm/interaction/Subscription.d.ts.map +1 -1
  220. package/dist/esm/interaction/Subscription.js.map +1 -1
  221. package/dist/esm/peer/PeerAddress.d.ts +1 -0
  222. package/dist/esm/peer/PeerAddress.d.ts.map +1 -1
  223. package/dist/esm/peer/PeerAddress.js +5 -0
  224. package/dist/esm/peer/PeerAddress.js.map +1 -1
  225. package/dist/esm/peer/PeerSet.d.ts.map +1 -1
  226. package/dist/esm/peer/PeerSet.js +33 -3
  227. package/dist/esm/peer/PeerSet.js.map +1 -1
  228. package/dist/esm/protocol/ChannelManager.d.ts.map +1 -1
  229. package/dist/esm/protocol/ChannelManager.js +7 -8
  230. package/dist/esm/protocol/ChannelManager.js.map +1 -1
  231. package/dist/esm/protocol/ExchangeManager.d.ts.map +1 -1
  232. package/dist/esm/protocol/ExchangeManager.js +41 -27
  233. package/dist/esm/protocol/ExchangeManager.js.map +1 -1
  234. package/dist/esm/protocol/MessageExchange.d.ts +1 -1
  235. package/dist/esm/protocol/MessageExchange.d.ts.map +1 -1
  236. package/dist/esm/protocol/MessageExchange.js +39 -5
  237. package/dist/esm/protocol/MessageExchange.js.map +1 -1
  238. package/dist/esm/protocol/MessageReceptionState.d.ts +1 -1
  239. package/dist/esm/securechannel/SecureChannelProtocol.js +2 -2
  240. package/dist/esm/securechannel/SecureChannelProtocol.js.map +1 -1
  241. package/dist/esm/session/GroupSession.d.ts +56 -0
  242. package/dist/esm/session/GroupSession.d.ts.map +1 -0
  243. package/dist/esm/session/GroupSession.js +177 -0
  244. package/dist/esm/session/GroupSession.js.map +6 -0
  245. package/dist/esm/session/InsecureSession.d.ts +2 -1
  246. package/dist/esm/session/InsecureSession.d.ts.map +1 -1
  247. package/dist/esm/session/InsecureSession.js +3 -2
  248. package/dist/esm/session/InsecureSession.js.map +1 -1
  249. package/dist/esm/session/NodeSession.d.ts +88 -0
  250. package/dist/esm/session/NodeSession.d.ts.map +1 -0
  251. package/dist/esm/session/NodeSession.js +298 -0
  252. package/dist/esm/session/NodeSession.js.map +6 -0
  253. package/dist/esm/session/SecureSession.d.ts +10 -75
  254. package/dist/esm/session/SecureSession.d.ts.map +1 -1
  255. package/dist/esm/session/SecureSession.js +10 -291
  256. package/dist/esm/session/SecureSession.js.map +2 -2
  257. package/dist/esm/session/Session.d.ts +6 -5
  258. package/dist/esm/session/Session.d.ts.map +1 -1
  259. package/dist/esm/session/Session.js +12 -2
  260. package/dist/esm/session/Session.js.map +1 -1
  261. package/dist/esm/session/SessionManager.d.ts +27 -9
  262. package/dist/esm/session/SessionManager.d.ts.map +1 -1
  263. package/dist/esm/session/SessionManager.js +84 -6
  264. package/dist/esm/session/SessionManager.js.map +1 -1
  265. package/dist/esm/session/case/CaseClient.d.ts +1 -1
  266. package/dist/esm/session/case/CaseClient.js +2 -2
  267. package/dist/esm/session/case/CaseClient.js.map +1 -1
  268. package/dist/esm/session/index.d.ts +2 -0
  269. package/dist/esm/session/index.d.ts.map +1 -1
  270. package/dist/esm/session/index.js +2 -0
  271. package/dist/esm/session/index.js.map +1 -1
  272. package/dist/esm/session/pase/PaseClient.d.ts +1 -1
  273. package/package.json +6 -6
  274. package/src/action/server/AccessControl.ts +4 -7
  275. package/src/action/server/AttributeWriteResponse.ts +29 -7
  276. package/src/action/server/CommandInvokeResponse.ts +28 -7
  277. package/src/action/server/DataResponse.ts +1 -1
  278. package/src/action/server/Subject.ts +45 -0
  279. package/src/action/server/index.ts +1 -0
  280. package/src/certificate/DeviceCertification.ts +2 -2
  281. package/src/cluster/client/AttributeClient.ts +15 -3
  282. package/src/cluster/client/ClusterClient.ts +90 -4
  283. package/src/cluster/client/ClusterClientTypes.ts +38 -9
  284. package/src/cluster/client/EventClient.ts +9 -2
  285. package/src/codec/MessageCodec.ts +49 -8
  286. package/src/fabric/Fabric.ts +51 -85
  287. package/src/fabric/FabricManager.ts +11 -4
  288. package/src/groups/FabricGroupsManager.ts +164 -0
  289. package/src/groups/Groups.ts +81 -0
  290. package/src/groups/KeySets.ts +194 -0
  291. package/src/groups/MessagingState.ts +76 -0
  292. package/src/groups/index.ts +8 -0
  293. package/src/index.ts +1 -0
  294. package/src/interaction/AccessControlManager.ts +49 -81
  295. package/src/interaction/InteractionClient.ts +66 -6
  296. package/src/interaction/InteractionMessenger.ts +15 -0
  297. package/src/interaction/Subscription.ts +3 -3
  298. package/src/peer/PeerAddress.ts +4 -0
  299. package/src/peer/PeerSet.ts +39 -4
  300. package/src/protocol/ChannelManager.ts +7 -9
  301. package/src/protocol/ExchangeManager.ts +51 -35
  302. package/src/protocol/MessageExchange.ts +42 -7
  303. package/src/protocol/MessageReceptionState.ts +2 -2
  304. package/src/securechannel/SecureChannelProtocol.ts +2 -2
  305. package/src/session/GroupSession.ts +223 -0
  306. package/src/session/InsecureSession.ts +3 -2
  307. package/src/session/NodeSession.ts +367 -0
  308. package/src/session/SecureSession.ts +14 -363
  309. package/src/session/Session.ts +17 -6
  310. package/src/session/SessionManager.ts +94 -14
  311. package/src/session/case/CaseClient.ts +2 -2
  312. package/src/session/index.ts +2 -3
@@ -167,6 +167,9 @@ export class FabricManager {
167
167
  }
168
168
  });
169
169
  };
170
+ if (this.#storage !== undefined) {
171
+ fabric.storage = this.#storage.createContext(`fabric-${fabricIndex}`);
172
+ }
170
173
  if (this.#initializationDone) {
171
174
  this.#events.added.emit(fabric);
172
175
  }
@@ -182,6 +185,7 @@ export class FabricManager {
182
185
  );
183
186
  this.#fabrics.delete(fabricIndex);
184
187
  await this.persistFabrics();
188
+ await fabric.storage?.clearAll();
185
189
  this.#events.deleted.emit(fabric);
186
190
  }
187
191
 
@@ -213,9 +217,10 @@ export class FabricManager {
213
217
  this.#construction.assert();
214
218
 
215
219
  for (const fabric of this.#fabrics.values()) {
216
- const candidateDestinationId = await fabric.getDestinationId(fabric.nodeId, initiatorRandom);
217
- if (!Bytes.areEqual(candidateDestinationId, destinationId)) continue;
218
- return fabric;
220
+ const candidateDestinationIds = await fabric.destinationIdsFor(fabric.nodeId, initiatorRandom);
221
+ if (candidateDestinationIds.some(candidate => Bytes.areEqual(candidate, destinationId))) {
222
+ return fabric;
223
+ }
219
224
  }
220
225
 
221
226
  throw new FabricNotFoundError();
@@ -233,7 +238,9 @@ export class FabricManager {
233
238
  }
234
239
 
235
240
  findByIndex(index: FabricIndex) {
236
- return Array.from(this.#fabrics.values()).find(fabric => fabric.fabricIndex === index);
241
+ this.#construction.assert();
242
+
243
+ return this.#fabrics.get(index);
237
244
  }
238
245
 
239
246
  async updateFabric(fabric: Fabric) {
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { Fabric } from "#fabric/Fabric.js";
7
+ import { BasicMap, Bytes, Crypto, InternalError, MatterFlowError, StorageContext } from "#general";
8
+ import { GroupKeySet, KeySets, OperationalKeySet } from "#groups/KeySets.js";
9
+ import { MessagingState } from "#groups/MessagingState.js";
10
+ import { GroupId } from "#types";
11
+ import { Groups } from "./Groups.js";
12
+
13
+ export const GROUP_SECURITY_INFO = Bytes.fromString("GroupKey v1.0");
14
+
15
+ /**
16
+ * Class that contains an operational view on the Group Keys for a fabric
17
+ */
18
+ export class FabricGroupsManager {
19
+ #fabric: Fabric;
20
+ #groups: Groups;
21
+ #messagingState: MessagingState;
22
+
23
+ /** Operationally enhanced variants of the group key sets based on their ID. */
24
+ #keySets = new KeySets();
25
+
26
+ constructor(fabric: Fabric, storage?: StorageContext) {
27
+ this.#fabric = fabric;
28
+ this.#groups = new Groups(fabric, this.#keySets);
29
+ this.#messagingState = new MessagingState(storage);
30
+
31
+ // KeySet with ID 0 is always the Fabric IPK, so we initialize from there because this is not stored
32
+ // in Key Management Cluster
33
+ this.#keySets.add({
34
+ groupKeySetId: 0,
35
+ epochKey0: fabric.identityProtectionKey,
36
+ operationalEpochKey0: fabric.operationalIdentityProtectionKey,
37
+ epochStartTime0: 0, // 0 is always ok, but only for the IPK key
38
+ groupSessionId0: null,
39
+ epochKey1: null,
40
+ operationalEpochKey1: null,
41
+ epochStartTime1: null,
42
+ groupSessionId1: null,
43
+ epochKey2: null,
44
+ operationalEpochKey2: null,
45
+ epochStartTime2: null,
46
+ groupSessionId2: null,
47
+ groupKeySecurityPolicy: 0, // GroupKeyManagement.GroupKeySecurityPolicy.TrustFirst, the other option is provisional
48
+ groupKeyMulticastPolicy: 0, // GroupKeyManagement.GroupKeyMulticastPolicy.PerGroupId, provisional!
49
+ });
50
+ if (storage !== undefined) {
51
+ this.storage = storage;
52
+ }
53
+ }
54
+
55
+ set storage(storage: StorageContext) {
56
+ this.#messagingState.storage = storage;
57
+ }
58
+
59
+ /** Operative lookup of the group key sets by their id. */
60
+ get keySets(): KeySets<OperationalKeySet> {
61
+ return this.#keySets;
62
+ }
63
+
64
+ get messaging(): MessagingState {
65
+ return this.#messagingState;
66
+ }
67
+
68
+ /** Operative lookup of the group key set id and the key by a group session Id. */
69
+ get sessions() {
70
+ return this.#keySets.sessions;
71
+ }
72
+
73
+ get groupKeyIdMap(): BasicMap<GroupId, number> {
74
+ return this.#groups.idMap;
75
+ }
76
+
77
+ set groupKeyIdMap(map: Map<GroupId, number>) {
78
+ this.#groups.idMap = map;
79
+ }
80
+
81
+ get endpoints() {
82
+ return this.#groups.endpointMap;
83
+ }
84
+
85
+ currentKeyForGroup(groupId: GroupId) {
86
+ return this.#groups.currentKeyForId(groupId);
87
+ }
88
+
89
+ subjectForGroup(id: GroupId, keySetId: number) {
90
+ return this.#groups.subjectForGroup(id, keySetId);
91
+ }
92
+
93
+ multicastAddressFor(groupId: GroupId): string {
94
+ return this.#groups.multicastAddress(groupId);
95
+ }
96
+
97
+ /*
98
+ TODO for Controller to generate new epochs
99
+ addGroupEpoch(groupKeySetId: number, startTimeMs = Time.nowMs()) {
100
+ // TODO for Controller to generate new epochs
101
+ const epochKey = Crypto.getRandomData(CRYPTO_SYMMETRIC_KEY_LENGTH);
102
+ const operationalEpochKey = Crypto.hkdf(epochKey, this.#fabric.operationalId, GROUP_SECURITY_INFO);
103
+ const epochStartTime = startTimeMs * 1000;
104
+ logger.debug(`addGroupEpoch: epochStartTime=${epochStartTime}`, operationalEpochKey);
105
+ // TODO extend in structure and such
106
+ }
107
+ */
108
+
109
+ /**
110
+ * Sets new group key set data and pre-calculates all operative data like session ids and operational keys.
111
+ * Overwriting the existing one if it exists.
112
+ */
113
+ async setFromGroupKeySet(groupKeySet: GroupKeySet) {
114
+ const { groupKeySetId, epochKey0, epochKey1, epochKey2 } = groupKeySet;
115
+
116
+ if (epochKey0 === null) {
117
+ throw new MatterFlowError("EpochKey0 must be set"); // checked before, but to make typing happy
118
+ }
119
+
120
+ // Clean up the counters and data from old keys, will be initialized again on next use
121
+ await this.#cleanUpCounters(groupKeySetId);
122
+
123
+ // Lets pre-calculate the operational keys
124
+ const operationalId = this.#fabric.operationalId;
125
+ const operationalEpochKey0 = await Crypto.hkdf(epochKey0, operationalId, GROUP_SECURITY_INFO);
126
+ const operationalEpochKey1 =
127
+ epochKey1 !== null ? await Crypto.hkdf(epochKey1, operationalId, GROUP_SECURITY_INFO) : null;
128
+ const operationalEpochKey2 =
129
+ epochKey2 !== null ? await Crypto.hkdf(epochKey2, operationalId, GROUP_SECURITY_INFO) : null;
130
+ this.#keySets.add({
131
+ ...groupKeySet,
132
+ operationalEpochKey0,
133
+ groupSessionId0: await this.#keySets.sessionIdFromKey(operationalEpochKey0),
134
+ operationalEpochKey1,
135
+ groupSessionId1:
136
+ operationalEpochKey1 !== null ? await this.#keySets.sessionIdFromKey(operationalEpochKey1) : null,
137
+ operationalEpochKey2,
138
+ groupSessionId2:
139
+ operationalEpochKey2 !== null ? await this.#keySets.sessionIdFromKey(operationalEpochKey2) : null,
140
+ });
141
+ }
142
+
143
+ /** Removes a group key set by its id and cleans up the counters and data. */
144
+ async removeGroupKeySet(groupKeySetId: number) {
145
+ if (groupKeySetId === 0) {
146
+ throw new InternalError("Cannot remove the group key set 0.");
147
+ }
148
+ await this.#cleanUpCounters(groupKeySetId, true);
149
+ return this.#keySets.delete("groupKeySetId", groupKeySetId);
150
+ }
151
+
152
+ /** Cleans up the counters and data for a group key set by its id. */
153
+ async #cleanUpCounters(groupKeySetId: number, forDelete = false) {
154
+ if (this.#keySets.forId(groupKeySetId) === undefined) {
155
+ return;
156
+ }
157
+
158
+ // Clean up counters for the group key set
159
+ const operationalKeys = this.#keySets.allKeysForId(groupKeySetId);
160
+ for (const { key } of operationalKeys) {
161
+ await this.#messagingState.removeCounter(key, forDelete);
162
+ }
163
+ }
164
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { Subject } from "#action/index.js";
7
+ import { Fabric } from "#fabric/Fabric.js";
8
+ import { BasicMap, DataWriter, ImplementationError, ipv6BytesToString } from "#general";
9
+ import { EndpointNumber, GroupId } from "#types";
10
+ import { KeySets, OperationalKeySet } from "./KeySets.js";
11
+
12
+ export class Groups {
13
+ #fabric: Fabric;
14
+ #keySets: KeySets<OperationalKeySet>;
15
+
16
+ /** Operational variant of the group key map attribute from Group Key Management cluster, maps group Ids to key sets. */
17
+ readonly #groupKeyIdMap = new BasicMap<GroupId, number>();
18
+
19
+ /** Operational variant of the group table, maps group Ids to a list of enabled endpoints. */
20
+ readonly endpointMap = new Map<GroupId, EndpointNumber[]>();
21
+
22
+ constructor(fabric: Fabric, keySets: KeySets<OperationalKeySet>) {
23
+ this.#fabric = fabric;
24
+ this.#keySets = keySets;
25
+ }
26
+
27
+ /** Operative lookup of the group key sets by a group id and to react on added removed groups. */
28
+ get idMap(): BasicMap<GroupId, number> {
29
+ return this.#groupKeyIdMap;
30
+ }
31
+
32
+ /** Updates the group key id map when changed in Group Key Management Cluster. Only changes are taken over. */
33
+ set idMap(map: Map<GroupId, number>) {
34
+ for (const [groupId, keySetId] of map.entries()) {
35
+ this.#groupKeyIdMap.set(groupId, keySetId);
36
+ }
37
+ for (const groupId of this.#groupKeyIdMap.keys()) {
38
+ if (!map.has(groupId)) {
39
+ // If the groupId is not in the new map, we remove it from the groupKeyIdMap
40
+ this.#groupKeyIdMap.delete(groupId);
41
+ }
42
+ }
43
+ }
44
+
45
+ subjectForGroup(id: GroupId, keySetId: number) {
46
+ return Subject.Group({
47
+ id,
48
+ hasValidMapping: this.idMap.get(id) === keySetId,
49
+ endpoints: this.endpointMap.get(id) ?? [],
50
+ });
51
+ }
52
+
53
+ /** Returns the multicast address for a given group id for this fabric. */
54
+ multicastAddress(groupId: GroupId) {
55
+ GroupId.assertGroupId(groupId);
56
+
57
+ // If GroupKeyMulticastPolicy ever becomes non-provisional then we need to adjust logic here, but so far we
58
+ // just use the default which is PerGroupId, which means we use the GroupId to create the multicast address.
59
+
60
+ const writer = new DataWriter();
61
+ writer.writeUInt16(0xff35);
62
+ writer.writeUInt16(0x0040);
63
+ writer.writeUInt8(0xfd);
64
+ writer.writeUInt64(this.#fabric.fabricId);
65
+ writer.writeUInt8(0x00);
66
+ writer.writeUInt16(GroupId(groupId));
67
+ return ipv6BytesToString(writer.toByteArray());
68
+ }
69
+
70
+ /**
71
+ * Returns the current operational group key for a given group id and returns the keys, start time and
72
+ * their session IDs.
73
+ */
74
+ currentKeyForId(groupId: GroupId) {
75
+ const keySetId = this.#groupKeyIdMap.get(groupId);
76
+ if (keySetId === undefined) {
77
+ throw new ImplementationError(`No group key set found for groupId ${groupId}.`);
78
+ }
79
+ return { ...this.#keySets.currentKeyForId(keySetId), keySetId };
80
+ }
81
+ }
@@ -0,0 +1,194 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import type { GroupKeyManagement } from "#clusters/group-key-management";
8
+ import { BasicSet, Bytes, Crypto, DataReader, ImplementationError, MatterFlowError, Time } from "#general";
9
+
10
+ export const GROUP_KEY_INFO = Bytes.fromString("GroupKeyHash");
11
+
12
+ export class KeySets<T extends OperationalKeySet> extends BasicSet<T> {
13
+ /** Operational enhanced structure for fast access based on the group session id. */
14
+ readonly #sessions = new Map<number, { keySetId: number; key: Uint8Array }[]>();
15
+
16
+ get sessions() {
17
+ return this.#sessions;
18
+ }
19
+
20
+ override add(item: T): void {
21
+ this.delete("groupKeySetId", item.groupKeySetId); // Remove any existing item with the same groupKeySetId
22
+ super.add(item);
23
+ this.#updateSessions();
24
+ }
25
+
26
+ override delete<F extends keyof T>(itemOrField: T | F, value?: T[F]): boolean {
27
+ const deleted = super.delete(itemOrField as any, value as any);
28
+ if (deleted) {
29
+ this.#updateSessions();
30
+ }
31
+ return deleted;
32
+ }
33
+
34
+ forId(groupKeySetId: number): OperationalKeySet | undefined {
35
+ return this.get("groupKeySetId", groupKeySetId);
36
+ }
37
+
38
+ /**
39
+ * Return an operative list of operational keys, start time and their session IDs for a specified
40
+ * group key set id. This is mainly used for receiving messages.
41
+ */
42
+ allKeysForId(keySetId: number) {
43
+ const groupKeySet = this.forId(keySetId);
44
+ if (groupKeySet === undefined) {
45
+ throw new MatterFlowError(`GroupKeySet for groupKeySet ${keySetId} not found.`);
46
+ }
47
+ const operationalKeys = Array<{ key: Uint8Array; sessionId?: number; startTime: number | bigint }>();
48
+ const {
49
+ operationalEpochKey0,
50
+ groupSessionId0,
51
+ epochStartTime0,
52
+ operationalEpochKey1,
53
+ groupSessionId1,
54
+ epochStartTime1,
55
+ operationalEpochKey2,
56
+ groupSessionId2,
57
+ epochStartTime2,
58
+ } = groupKeySet;
59
+ if (operationalEpochKey0 === null || epochStartTime0 === null || (keySetId !== 0 && groupSessionId0 === null)) {
60
+ // should never happen, but just in case
61
+ throw new MatterFlowError(`EpochKey0 for groupKeySet ${keySetId} not found.`);
62
+ }
63
+ operationalKeys.push({
64
+ key: operationalEpochKey0,
65
+ sessionId: groupSessionId0 !== null ? groupSessionId0 : undefined,
66
+ startTime: epochStartTime0,
67
+ });
68
+ if (operationalEpochKey1 !== null && groupSessionId1 !== null && epochStartTime1 !== null) {
69
+ operationalKeys.push({ key: operationalEpochKey1, sessionId: groupSessionId1, startTime: epochStartTime1 });
70
+ }
71
+ if (operationalEpochKey2 !== null && groupSessionId2 !== null && epochStartTime2 !== null) {
72
+ operationalKeys.push({ key: operationalEpochKey2, sessionId: groupSessionId2, startTime: epochStartTime2 });
73
+ }
74
+ return operationalKeys;
75
+ }
76
+
77
+ /**
78
+ * Returns the current operational group key for a given group KeySet Id and returns the keys, start time and
79
+ * their session IDs. This is mainly used for sending messages.
80
+ */
81
+ currentKeyForId(keySetId: number) {
82
+ const operationalKeys = this.allKeysForId(keySetId);
83
+ if (operationalKeys.length === 0) {
84
+ throw new MatterFlowError(`No operational keys found for groupKeySet ${keySetId}.`);
85
+ }
86
+ if (keySetId === 0) {
87
+ // Groups: For the generation of the Destination Identifier, the originator SHALL use the operational group key with
88
+ // the second newest EpochStartTime, if one exists, otherwise it SHALL use the single operational group key available.
89
+ // @see {@link MatterSpecification.v14.Core} § 4.14.2.6.
90
+ if (operationalKeys.length > 2) {
91
+ return operationalKeys[1];
92
+ }
93
+ return operationalKeys[0];
94
+ } else {
95
+ // Nodes sending group messages SHALL use operational group keys that are derived from the current
96
+ // epoch key (specifically, the epoch key with the latest start time that is not in the future).
97
+ // TODO Nodes that cannot reliably keep track of time calculate the current epoch key as described in
98
+ // Section 4.17.3.4, “Epoch Key Rotation without Time Synchronization”.
99
+ const now = Time.nowUs();
100
+ const relevantKeys = operationalKeys.filter(({ startTime }) => startTime <= now);
101
+ if (relevantKeys.length === 0) {
102
+ throw new ImplementationError(
103
+ `No operational keys found for groupKeySet ${keySetId} that are not in the future.`,
104
+ );
105
+ }
106
+ return relevantKeys[operationalKeys.length - 1];
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Returns the group key set for a given group key set id in the official data format from the Group key Management
112
+ * cluster.
113
+ */
114
+ asGroupKeySet(groupKeySetId: number) {
115
+ const groupKeySet = this.forId(groupKeySetId);
116
+ if (groupKeySet === undefined) {
117
+ return undefined;
118
+ }
119
+
120
+ const {
121
+ epochKey0,
122
+ epochStartTime0,
123
+ epochKey1,
124
+ epochStartTime1,
125
+ epochKey2,
126
+ epochStartTime2,
127
+ groupKeySecurityPolicy,
128
+ groupKeyMulticastPolicy,
129
+ } = groupKeySet;
130
+ return {
131
+ groupKeySetId,
132
+ epochKey0,
133
+ epochStartTime0,
134
+ epochKey1,
135
+ epochStartTime1,
136
+ epochKey2,
137
+ epochStartTime2,
138
+ groupKeySecurityPolicy,
139
+ groupKeyMulticastPolicy,
140
+ };
141
+ }
142
+
143
+ /** Calculates a group session id based on the operational group key. */
144
+ async sessionIdFromKey(operationalGroupKey: Uint8Array) {
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);
147
+
148
+ // GroupSessionId is computed by considering the GroupKeyHash as a Big-Endian value. GroupSessionId is a scalar.
149
+ // Its use in fields within messages may cause a re-serialization into a different byte order than the one used
150
+ // for initial generation.
151
+ return new DataReader(groupKeyHash).readUInt16();
152
+ }
153
+
154
+ /**
155
+ * Updates the group session map based on the current group key sets.
156
+ * This is used to quickly find the operational keys by their group session id.
157
+ */
158
+ #updateSessions() {
159
+ this.#sessions.clear();
160
+ for (const [id, keySet] of this.mapOf("groupKeySetId").entries()) {
161
+ if (id === 0) {
162
+ // Skip the default group key set (0), as it is not used for operational keys
163
+ continue;
164
+ }
165
+ if (keySet.groupSessionId0 !== null) {
166
+ const list = this.#sessions.get(keySet.groupSessionId0) ?? [];
167
+ list.push({ key: keySet.operationalEpochKey0, keySetId: id });
168
+ this.#sessions.set(keySet.groupSessionId0, list);
169
+ }
170
+ if (keySet.groupSessionId1 !== null && keySet.operationalEpochKey1 !== null) {
171
+ const list = this.#sessions.get(keySet.groupSessionId1) ?? [];
172
+ list.push({ key: keySet.operationalEpochKey1, keySetId: id });
173
+ this.#sessions.set(keySet.groupSessionId1, list);
174
+ }
175
+ if (keySet.groupSessionId2 !== null && keySet.operationalEpochKey2 !== null) {
176
+ const list = this.#sessions.get(keySet.groupSessionId2) ?? [];
177
+ list.push({ key: keySet.operationalEpochKey2, keySetId: id });
178
+ this.#sessions.set(keySet.groupSessionId2, list);
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ export type GroupKeySet = GroupKeyManagement.GroupKeySet;
185
+
186
+ /** Enhanced structure of GroupKeySet to include operational data for easier operational processing. */
187
+ export type OperationalKeySet = GroupKeySet & {
188
+ operationalEpochKey0: Uint8Array;
189
+ groupSessionId0: number | null;
190
+ operationalEpochKey1: Uint8Array | null;
191
+ groupSessionId1: number | null;
192
+ operationalEpochKey2: Uint8Array | null;
193
+ groupSessionId2: number | null;
194
+ };
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { Bytes, ImplementationError, InternalError, StorageContext } from "#general";
7
+ import { PersistedMessageCounter } from "#protocol/MessageCounter.js";
8
+ import { MessageReceptionStateEncryptedWithRollover } from "#protocol/MessageReceptionState.js";
9
+ import { NodeId } from "#types";
10
+
11
+ export class MessagingState {
12
+ /**
13
+ * Message counter for sending data messages to a group per Operational key. No need to scope to a source node
14
+ * because we are the sending node
15
+ * TODO: For management: Make sure to start rotating the key early enough that a former counter-value is not used
16
+ * again for the same key.
17
+ */
18
+ readonly #groupDataCounters = new Map<string, PersistedMessageCounter>();
19
+
20
+ /** Message reception state for data messages per Operational key and source node. */
21
+ readonly #messageDataReceptionState = new Map<string, Map<NodeId, MessageReceptionStateEncryptedWithRollover>>();
22
+ #storage?: StorageContext;
23
+
24
+ constructor(storage?: StorageContext) {
25
+ if (storage !== undefined) {
26
+ this.#storage = storage;
27
+ }
28
+ }
29
+
30
+ set storage(storage: StorageContext) {
31
+ if (this.#storage !== undefined) {
32
+ throw new InternalError("Storage context can only be set once.");
33
+ }
34
+ this.#storage = storage;
35
+ }
36
+
37
+ /**
38
+ * Return the message counter for sending messages to a group with the given operational key.
39
+ */
40
+ counterFor(operationalKey: Uint8Array) {
41
+ if (!this.#storage) {
42
+ throw new ImplementationError("Group session cannot be created without storage context.");
43
+ }
44
+ const operationalKeyHex = Bytes.toHex(operationalKey);
45
+ let counter = this.#groupDataCounters.get(operationalKeyHex);
46
+ if (counter === undefined) {
47
+ counter = new PersistedMessageCounter(this.#storage, `${operationalKeyHex}-data`);
48
+ this.#groupDataCounters.set(operationalKeyHex, counter);
49
+ }
50
+ return counter;
51
+ }
52
+
53
+ async removeCounter(key: Uint8Array, forDelete = false) {
54
+ const operationalKeyHex = Bytes.toHex(key);
55
+ this.#groupDataCounters.delete(operationalKeyHex);
56
+ if (forDelete) {
57
+ // If we are deleting the group key set, also delete the persisted counter values
58
+ await this.#storage?.delete(`${operationalKeyHex}-data`);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Returns the message reception state for a given source node id and operational key.
64
+ */
65
+ receptionStateFor(sourceNodeId: NodeId, operationalKey: Uint8Array) {
66
+ const operationalKeyHex = Bytes.toHex(operationalKey);
67
+ let receptionState = this.#messageDataReceptionState.get(operationalKeyHex)?.get(sourceNodeId);
68
+ if (receptionState === undefined) {
69
+ receptionState = new MessageReceptionStateEncryptedWithRollover();
70
+ const keyMap = this.#messageDataReceptionState.get(operationalKeyHex) ?? new Map();
71
+ keyMap.set(sourceNodeId, receptionState);
72
+ this.#messageDataReceptionState.set(operationalKeyHex, keyMap);
73
+ }
74
+ return receptionState;
75
+ }
76
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Matter.js Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ export * from "./FabricGroupsManager.js";
8
+ export * from "./KeySets.js";
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ export * from "./codec/index.js";
12
12
  export * from "./common/index.js";
13
13
  export * from "./events/index.js";
14
14
  export * from "./fabric/index.js";
15
+ export * from "./groups/index.js";
15
16
  export * from "./interaction/index.js";
16
17
  export * from "./mdns/index.js";
17
18
  export * from "./peer/index.js";