@matter/protocol 0.16.0-alpha.0-20251220-0bb8d9f89 → 0.16.0-alpha.0-20251221-3dce6fa1b
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.
- package/dist/cjs/certificate/CertificateAuthority.d.ts +18 -7
- package/dist/cjs/certificate/CertificateAuthority.d.ts.map +1 -1
- package/dist/cjs/certificate/CertificateAuthority.js +32 -35
- package/dist/cjs/certificate/CertificateAuthority.js.map +1 -1
- package/dist/cjs/fabric/Fabric.d.ts.map +1 -1
- package/dist/cjs/fabric/Fabric.js +11 -0
- package/dist/cjs/fabric/Fabric.js.map +1 -1
- package/dist/cjs/fabric/FabricAuthority.d.ts +4 -3
- package/dist/cjs/fabric/FabricAuthority.d.ts.map +1 -1
- package/dist/cjs/fabric/FabricAuthority.js +35 -4
- package/dist/cjs/fabric/FabricAuthority.js.map +1 -1
- package/dist/esm/certificate/CertificateAuthority.d.ts +18 -7
- package/dist/esm/certificate/CertificateAuthority.d.ts.map +1 -1
- package/dist/esm/certificate/CertificateAuthority.js +32 -35
- package/dist/esm/certificate/CertificateAuthority.js.map +1 -1
- package/dist/esm/fabric/Fabric.d.ts.map +1 -1
- package/dist/esm/fabric/Fabric.js +11 -0
- package/dist/esm/fabric/Fabric.js.map +1 -1
- package/dist/esm/fabric/FabricAuthority.d.ts +4 -3
- package/dist/esm/fabric/FabricAuthority.d.ts.map +1 -1
- package/dist/esm/fabric/FabricAuthority.js +35 -4
- package/dist/esm/fabric/FabricAuthority.js.map +1 -1
- package/package.json +6 -6
- package/src/certificate/CertificateAuthority.ts +65 -47
- package/src/fabric/Fabric.ts +18 -0
- package/src/fabric/FabricAuthority.ts +39 -4
- package/src/session/case/CaseServer.ts +2 -2
package/src/fabric/Fabric.ts
CHANGED
|
@@ -295,6 +295,17 @@ export class Fabric {
|
|
|
295
295
|
}
|
|
296
296
|
|
|
297
297
|
async verifyCredentials(operationalCert: Bytes, intermediateCACert?: Bytes) {
|
|
298
|
+
if (intermediateCACert !== undefined && intermediateCACert.byteLength === 0) {
|
|
299
|
+
intermediateCACert = undefined;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Workaround for an issue with the Ikea hub where the root certificate was also provided as ICAC
|
|
303
|
+
// see https://github.com/project-chip/connectedhomeip/issues/42479
|
|
304
|
+
if (intermediateCACert !== undefined && Bytes.areEqual(this.rootCert, intermediateCACert)) {
|
|
305
|
+
logger.info("Intermediate CA certificate is identical to root certificate; omitting ICAC");
|
|
306
|
+
intermediateCACert = undefined;
|
|
307
|
+
}
|
|
308
|
+
|
|
298
309
|
const rootCert = Rcac.fromTlv(this.rootCert);
|
|
299
310
|
const nocCert = Noc.fromTlv(operationalCert);
|
|
300
311
|
const icaCert = intermediateCACert !== undefined ? Icac.fromTlv(intermediateCACert) : undefined;
|
|
@@ -499,6 +510,13 @@ export class FabricBuilder {
|
|
|
499
510
|
throw new MatterFlowError("Root certificate needs to be set first");
|
|
500
511
|
}
|
|
501
512
|
|
|
513
|
+
// Workaround for an issue with the Ikea hub where the root certificate was also provided as ICAC
|
|
514
|
+
// see https://github.com/project-chip/connectedhomeip/issues/42479
|
|
515
|
+
if (intermediateCACert !== undefined && Bytes.areEqual(this.#rootCert, intermediateCACert)) {
|
|
516
|
+
logger.info("Intermediate CA certificate is identical to root certificate; omitting ICAC");
|
|
517
|
+
intermediateCACert = undefined;
|
|
518
|
+
}
|
|
519
|
+
|
|
502
520
|
const rootCert = Rcac.fromTlv(this.#rootCert);
|
|
503
521
|
const nocCert = Noc.fromTlv(operationalCert);
|
|
504
522
|
const icaCert = intermediateCACert !== undefined ? Icac.fromTlv(intermediateCACert) : undefined;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { CertificateAuthority } from "#certificate/CertificateAuthority.js";
|
|
8
|
+
import { Noc } from "#certificate/kinds/Noc.js";
|
|
8
9
|
import {
|
|
9
10
|
AsyncObservable,
|
|
10
11
|
Bytes,
|
|
@@ -53,6 +54,7 @@ export class FabricAuthority {
|
|
|
53
54
|
#ca: CertificateAuthority;
|
|
54
55
|
#fabrics: FabricManager;
|
|
55
56
|
#fabricAdded = new AsyncObservable<[Fabric]>();
|
|
57
|
+
#rotatedFabricIndices = new Set<FabricIndex>(); // Remember which we already rotated in this run
|
|
56
58
|
|
|
57
59
|
constructor({ ca, fabrics }: FabricAuthorityContext) {
|
|
58
60
|
this.#ca = ca;
|
|
@@ -76,13 +78,17 @@ export class FabricAuthority {
|
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
/**
|
|
79
|
-
*
|
|
81
|
+
* Get the default fabric for this authority.
|
|
82
|
+
* When rotateNoc is true (the default), the NOC key pair is rotated once per runtime when the fabric already exists.
|
|
80
83
|
*/
|
|
81
|
-
async defaultFabric(config: FabricAuthorityConfiguration) {
|
|
84
|
+
async defaultFabric(config: FabricAuthorityConfiguration, rotateNoc = true) {
|
|
82
85
|
// First search for a fabric associated with the CA's root certificate
|
|
83
86
|
const caRootCert = this.#ca.rootCert;
|
|
84
|
-
|
|
87
|
+
let fabric = this.fabrics.find(fabric => Bytes.areEqual(fabric.rootCert, caRootCert));
|
|
85
88
|
if (fabric !== undefined) {
|
|
89
|
+
if (rotateNoc) {
|
|
90
|
+
fabric = await this.#rotateFabricNocKey(fabric);
|
|
91
|
+
}
|
|
86
92
|
if (fabric.label !== config.adminFabricLabel) {
|
|
87
93
|
await fabric.setLabel(config.adminFabricLabel);
|
|
88
94
|
}
|
|
@@ -118,7 +124,7 @@ export class FabricAuthority {
|
|
|
118
124
|
}
|
|
119
125
|
|
|
120
126
|
/**
|
|
121
|
-
* Create
|
|
127
|
+
* Create new fabric under our control.
|
|
122
128
|
*/
|
|
123
129
|
async createFabric(config: FabricAuthorityConfiguration) {
|
|
124
130
|
const rootNodeId = config.adminNodeId ?? NodeId.randomOperationalNodeId(this.#fabrics.crypto);
|
|
@@ -162,6 +168,35 @@ export class FabricAuthority {
|
|
|
162
168
|
return fabric;
|
|
163
169
|
}
|
|
164
170
|
|
|
171
|
+
async #rotateFabricNocKey(fabric: Fabric) {
|
|
172
|
+
if (this.#rotatedFabricIndices.has(fabric.fabricIndex)) {
|
|
173
|
+
// We only rotate once per runtime
|
|
174
|
+
return fabric;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const builder = await FabricBuilder.create(this.#fabrics.crypto);
|
|
178
|
+
builder.initializeFromFabricForUpdate(fabric);
|
|
179
|
+
const {
|
|
180
|
+
subject: { nodeId, fabricId, caseAuthenticatedTags },
|
|
181
|
+
} = Noc.fromTlv(fabric.operationalCert).cert;
|
|
182
|
+
if (nodeId !== fabric.rootNodeId) {
|
|
183
|
+
throw new ImplementationError(
|
|
184
|
+
`Cannot rotate NOC for fabric ${fabric.fabricIndex} because root node ID changed`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
await builder.setOperationalCert(
|
|
188
|
+
await this.#ca.generateNoc(builder.publicKey, fabricId, nodeId, caseAuthenticatedTags),
|
|
189
|
+
fabric.intermediateCACert,
|
|
190
|
+
);
|
|
191
|
+
const newFabric = await builder.build(fabric.fabricIndex);
|
|
192
|
+
logger.info(`Rotated NOC for fabric ${fabric.fabricIndex}`);
|
|
193
|
+
|
|
194
|
+
await this.#fabrics.replaceFabric(newFabric);
|
|
195
|
+
|
|
196
|
+
this.#rotatedFabricIndices.add(fabric.fabricIndex);
|
|
197
|
+
return newFabric;
|
|
198
|
+
}
|
|
199
|
+
|
|
165
200
|
static [Environmental.create](env: Environment) {
|
|
166
201
|
const instance = new FabricAuthority({
|
|
167
202
|
ca: env.get(CertificateAuthority),
|
|
@@ -78,7 +78,7 @@ export class CaseServer implements ProtocolHandler {
|
|
|
78
78
|
async #handleSigma1(messenger: CaseServerMessenger) {
|
|
79
79
|
logger.info("Received pairing request", Mark.INBOUND, Diagnostic.via(messenger.channelName));
|
|
80
80
|
|
|
81
|
-
// Initialize context with information from peer
|
|
81
|
+
// Initialize context with information from a peer
|
|
82
82
|
const { sigma1Bytes, sigma1 } = await messenger.readSigma1();
|
|
83
83
|
const resumptionRecord =
|
|
84
84
|
sigma1.resumptionId !== undefined && sigma1.initiatorResumeMic !== undefined
|
|
@@ -268,7 +268,7 @@ export class CaseServer implements ProtocolHandler {
|
|
|
268
268
|
|
|
269
269
|
await crypto.verifyEcdsa(PublicKey(peerPublicKey), peerSignatureData, new EcdsaSignature(peerSignature));
|
|
270
270
|
|
|
271
|
-
// All good! Create secure session
|
|
271
|
+
// All good! Create a secure session
|
|
272
272
|
const secureSessionSalt = Bytes.concat(
|
|
273
273
|
operationalIdentityProtectionKey,
|
|
274
274
|
await crypto.computeHash([cx.bytes, sigma2Bytes, sigma3Bytes]),
|