@matter/protocol 0.12.4-alpha.0-20250215-5af08a8d6 → 0.12.4-alpha.0-20250223-1e0341a1a
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/interaction/InteractionMessenger.d.ts.map +1 -1
- package/dist/cjs/interaction/InteractionMessenger.js +5 -1
- package/dist/cjs/interaction/InteractionMessenger.js.map +1 -1
- package/dist/cjs/protocol/ExchangeManager.d.ts.map +1 -1
- package/dist/cjs/protocol/ExchangeManager.js +12 -8
- package/dist/cjs/protocol/ExchangeManager.js.map +1 -1
- package/dist/cjs/session/InsecureSession.d.ts.map +1 -1
- package/dist/cjs/session/InsecureSession.js +1 -0
- package/dist/cjs/session/InsecureSession.js.map +1 -1
- package/dist/cjs/session/SecureSession.d.ts.map +1 -1
- package/dist/cjs/session/SecureSession.js +4 -0
- package/dist/cjs/session/SecureSession.js.map +1 -1
- package/dist/cjs/session/Session.d.ts +3 -1
- package/dist/cjs/session/Session.d.ts.map +1 -1
- package/dist/cjs/session/Session.js +5 -1
- package/dist/cjs/session/Session.js.map +1 -1
- package/dist/cjs/session/case/CaseClient.d.ts.map +1 -1
- package/dist/cjs/session/case/CaseClient.js +22 -15
- package/dist/cjs/session/case/CaseClient.js.map +1 -1
- package/dist/cjs/session/case/CaseServer.js +1 -1
- package/dist/esm/interaction/InteractionMessenger.d.ts.map +1 -1
- package/dist/esm/interaction/InteractionMessenger.js +5 -1
- package/dist/esm/interaction/InteractionMessenger.js.map +1 -1
- package/dist/esm/protocol/ExchangeManager.d.ts.map +1 -1
- package/dist/esm/protocol/ExchangeManager.js +12 -8
- package/dist/esm/protocol/ExchangeManager.js.map +1 -1
- package/dist/esm/session/InsecureSession.d.ts.map +1 -1
- package/dist/esm/session/InsecureSession.js +1 -0
- package/dist/esm/session/InsecureSession.js.map +1 -1
- package/dist/esm/session/SecureSession.d.ts.map +1 -1
- package/dist/esm/session/SecureSession.js +4 -0
- package/dist/esm/session/SecureSession.js.map +1 -1
- package/dist/esm/session/Session.d.ts +3 -1
- package/dist/esm/session/Session.d.ts.map +1 -1
- package/dist/esm/session/Session.js +6 -2
- package/dist/esm/session/Session.js.map +1 -1
- package/dist/esm/session/case/CaseClient.d.ts.map +1 -1
- package/dist/esm/session/case/CaseClient.js +22 -15
- package/dist/esm/session/case/CaseClient.js.map +1 -1
- package/dist/esm/session/case/CaseServer.js +1 -1
- package/package.json +6 -6
- package/src/interaction/InteractionMessenger.ts +7 -2
- package/src/protocol/ExchangeManager.ts +12 -8
- package/src/session/InsecureSession.ts +1 -0
- package/src/session/SecureSession.ts +4 -0
- package/src/session/Session.ts +7 -2
- package/src/session/case/CaseClient.ts +18 -12
- package/src/session/case/CaseServer.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matter/protocol",
|
|
3
|
-
"version": "0.12.4-alpha.0-
|
|
3
|
+
"version": "0.12.4-alpha.0-20250223-1e0341a1a",
|
|
4
4
|
"description": "Low-level APIs for Matter interaction",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"iot",
|
|
@@ -40,14 +40,14 @@
|
|
|
40
40
|
"#*": "./src/*"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@matter/general": "0.12.4-alpha.0-
|
|
44
|
-
"@matter/model": "0.12.4-alpha.0-
|
|
45
|
-
"@matter/types": "0.12.4-alpha.0-
|
|
43
|
+
"@matter/general": "0.12.4-alpha.0-20250223-1e0341a1a",
|
|
44
|
+
"@matter/model": "0.12.4-alpha.0-20250223-1e0341a1a",
|
|
45
|
+
"@matter/types": "0.12.4-alpha.0-20250223-1e0341a1a",
|
|
46
46
|
"@noble/curves": "^1.8.1"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@matter/tools": "0.12.4-alpha.0-
|
|
50
|
-
"@matter/testing": "0.12.4-alpha.0-
|
|
49
|
+
"@matter/tools": "0.12.4-alpha.0-20250223-1e0341a1a",
|
|
50
|
+
"@matter/testing": "0.12.4-alpha.0-20250223-1e0341a1a"
|
|
51
51
|
},
|
|
52
52
|
"files": [
|
|
53
53
|
"dist/**/*",
|
|
@@ -542,7 +542,6 @@ export class InteractionServerMessenger extends InteractionMessenger {
|
|
|
542
542
|
allowMissingFieldsForNonFabricFilteredRead,
|
|
543
543
|
}),
|
|
544
544
|
);
|
|
545
|
-
|
|
546
545
|
break;
|
|
547
546
|
}
|
|
548
547
|
availableBytes -= encodedChunkDataSize;
|
|
@@ -583,6 +582,12 @@ export class InteractionServerMessenger extends InteractionMessenger {
|
|
|
583
582
|
attributeReportsToSend.push(attributeToSend);
|
|
584
583
|
continue;
|
|
585
584
|
}
|
|
585
|
+
if (encodedSize > this.exchange.maxPayloadSize - emptyDataReportBytes.length - 3) {
|
|
586
|
+
// We sent the message but the current attribute is too big for a message alone so needs to
|
|
587
|
+
// be chunked, so add it to the queue at the beginning
|
|
588
|
+
attributeReportsToSend.unshift(attributeToSend);
|
|
589
|
+
continue;
|
|
590
|
+
}
|
|
586
591
|
}
|
|
587
592
|
messageSize += encodedSize;
|
|
588
593
|
if (dataReport.attributeReports === undefined) {
|
|
@@ -626,7 +631,7 @@ export class InteractionServerMessenger extends InteractionMessenger {
|
|
|
626
631
|
const encodedMessage = TlvDataReportForSend.encode(dataReportToSend);
|
|
627
632
|
if (encodedMessage.length > this.exchange.maxPayloadSize) {
|
|
628
633
|
throw new MatterFlowError(
|
|
629
|
-
`DataReport is too long to fit in a single chunk, This should not happen! Data: ${Logger.toJSON(
|
|
634
|
+
`DataReport with ${encodedMessage.length}bytes is too long to fit in a single chunk (${this.exchange.maxPayloadSize}bytes), This should not happen! Data: ${Logger.toJSON(
|
|
630
635
|
dataReportToSend,
|
|
631
636
|
)}`,
|
|
632
637
|
);
|
|
@@ -48,6 +48,7 @@ export class MessageChannel implements Channel<Message> {
|
|
|
48
48
|
closeCallback?: () => Promise<void>,
|
|
49
49
|
) {
|
|
50
50
|
this.#closeCallback = closeCallback;
|
|
51
|
+
this.session.destroyed.on(() => this.close());
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
set closeCallback(callback: () => Promise<void>) {
|
|
@@ -272,7 +273,7 @@ export class ExchangeManager {
|
|
|
272
273
|
!message.payloadHeader.requiresAck
|
|
273
274
|
) {
|
|
274
275
|
logger.debug(
|
|
275
|
-
`Ignoring unsolicited standalone ack message ${messageId} for protocol ${message.payloadHeader.protocolId} and exchange id ${message.payloadHeader.exchangeId}
|
|
276
|
+
`Ignoring unsolicited standalone ack message ${messageId} for protocol ${message.payloadHeader.protocolId} and exchange id ${message.payloadHeader.exchangeId} on channel ${channel.name}`,
|
|
276
277
|
);
|
|
277
278
|
return;
|
|
278
279
|
}
|
|
@@ -295,7 +296,7 @@ export class ExchangeManager {
|
|
|
295
296
|
});
|
|
296
297
|
await exchange.close();
|
|
297
298
|
logger.debug(
|
|
298
|
-
`Ignoring unsolicited message ${messageId} for protocol ${message.payloadHeader.protocolId}
|
|
299
|
+
`Ignoring unsolicited message ${messageId} for protocol ${message.payloadHeader.protocolId} on channel ${channel.name}`,
|
|
299
300
|
);
|
|
300
301
|
} else {
|
|
301
302
|
if (protocolHandler === undefined) {
|
|
@@ -303,14 +304,14 @@ export class ExchangeManager {
|
|
|
303
304
|
}
|
|
304
305
|
if (isDuplicate) {
|
|
305
306
|
logger.info(
|
|
306
|
-
`Ignoring duplicate message ${messageId} (requires no ack) for protocol ${message.payloadHeader.protocolId}
|
|
307
|
+
`Ignoring duplicate message ${messageId} (requires no ack) for protocol ${message.payloadHeader.protocolId} on channel ${channel.name}`,
|
|
307
308
|
);
|
|
308
309
|
return;
|
|
309
310
|
} else {
|
|
310
311
|
logger.info(
|
|
311
312
|
`Discarding unexpected message ${messageId} for protocol ${
|
|
312
313
|
message.payloadHeader.protocolId
|
|
313
|
-
}, exchangeIndex ${exchangeIndex} and sessionId ${session.id} : ${Logger.toJSON(message)}`,
|
|
314
|
+
}, exchangeIndex ${exchangeIndex} and sessionId ${session.id} on channel ${channel.name}: ${Logger.toJSON(message)}`,
|
|
314
315
|
);
|
|
315
316
|
}
|
|
316
317
|
}
|
|
@@ -355,7 +356,7 @@ export class ExchangeManager {
|
|
|
355
356
|
}
|
|
356
357
|
if (session.sendCloseMessageWhenClosing) {
|
|
357
358
|
const channel = this.#channelManager.getChannelForSession(session);
|
|
358
|
-
logger.debug(`Channel for session ${
|
|
359
|
+
logger.debug(`Channel for session ${sessionName} is ${channel?.name}`);
|
|
359
360
|
if (channel !== undefined) {
|
|
360
361
|
const exchange = this.initiateExchangeWithChannel(channel, SECURE_CHANNEL_PROTOCOL_ID);
|
|
361
362
|
if (exchange !== undefined) {
|
|
@@ -422,18 +423,21 @@ export class ExchangeManager {
|
|
|
422
423
|
transportInterface.onData((socket, data) => {
|
|
423
424
|
if (udpInterface && data.length > socket.maxPayloadSize) {
|
|
424
425
|
logger.warn(
|
|
425
|
-
`Ignoring UDP message with size ${data.length} from ${socket.name}, which is larger than the maximum allowed size of ${socket.maxPayloadSize}.`,
|
|
426
|
+
`Ignoring UDP message on channel ${socket.name} with size ${data.length} from ${socket.name}, which is larger than the maximum allowed size of ${socket.maxPayloadSize}.`,
|
|
426
427
|
);
|
|
427
428
|
return;
|
|
428
429
|
}
|
|
429
430
|
|
|
430
431
|
try {
|
|
431
432
|
this.onMessage(socket, data).catch(error =>
|
|
432
|
-
logger.info(
|
|
433
|
+
logger.info(
|
|
434
|
+
`Error on channel ${socket.name}:`,
|
|
435
|
+
error instanceof MatterError ? error.message : error,
|
|
436
|
+
),
|
|
433
437
|
);
|
|
434
438
|
} catch (error) {
|
|
435
439
|
logger.info(
|
|
436
|
-
|
|
440
|
+
`Ignoring UDP message on channel ${socket.name} with error`,
|
|
437
441
|
error instanceof MatterError ? error.message : error,
|
|
438
442
|
);
|
|
439
443
|
}
|
|
@@ -327,9 +327,13 @@ export class SecureSession extends Session {
|
|
|
327
327
|
await this.closer;
|
|
328
328
|
} catch (error) {
|
|
329
329
|
NoChannelError.accept(error);
|
|
330
|
+
} finally {
|
|
331
|
+
await this.destroyed.emit();
|
|
330
332
|
}
|
|
333
|
+
return;
|
|
331
334
|
}
|
|
332
335
|
}
|
|
336
|
+
await this.destroyed.emit();
|
|
333
337
|
}
|
|
334
338
|
|
|
335
339
|
/**
|
package/src/session/Session.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { Time } from "#general";
|
|
7
|
+
import { AsyncObservable, Time } from "#general";
|
|
8
8
|
import { NodeId } from "#types";
|
|
9
9
|
import { DecodedMessage, DecodedPacket, Message, Packet } from "../codec/MessageCodec.js";
|
|
10
10
|
import { Fabric } from "../fabric/Fabric.js";
|
|
@@ -76,9 +76,10 @@ export abstract class Session {
|
|
|
76
76
|
/**
|
|
77
77
|
* If the ExchangeManager performs async work to clean up a session it sets this promise. This is because
|
|
78
78
|
* historically we didn't return from destroy() until ExchangeManager was complete. Not sure if this is entirely
|
|
79
|
-
* necessary but it makes sense so this allows us to maintain the old behavior.
|
|
79
|
+
* necessary, but it makes sense so this allows us to maintain the old behavior.
|
|
80
80
|
*/
|
|
81
81
|
closer?: Promise<void>;
|
|
82
|
+
#destroyed = AsyncObservable<[]>();
|
|
82
83
|
|
|
83
84
|
constructor(args: {
|
|
84
85
|
manager?: SessionManager;
|
|
@@ -117,6 +118,10 @@ export abstract class Session {
|
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
120
|
|
|
121
|
+
get destroyed() {
|
|
122
|
+
return this.#destroyed;
|
|
123
|
+
}
|
|
124
|
+
|
|
120
125
|
notifyActivity(messageReceived: boolean) {
|
|
121
126
|
this.timestamp = Time.nowMs();
|
|
122
127
|
if (messageReceived) {
|
|
@@ -79,7 +79,12 @@ export class CaseClient {
|
|
|
79
79
|
if (sigma2Resume !== undefined) {
|
|
80
80
|
// Process sigma2 resume
|
|
81
81
|
if (resumptionRecord === undefined) throw new UnexpectedDataError("Received an unexpected sigma2Resume.");
|
|
82
|
-
const {
|
|
82
|
+
const {
|
|
83
|
+
sharedSecret,
|
|
84
|
+
fabric,
|
|
85
|
+
sessionParameters: resumptionSessionParams,
|
|
86
|
+
caseAuthenticatedTags,
|
|
87
|
+
} = resumptionRecord;
|
|
83
88
|
const { responderSessionId: peerSessionId, resumptionId, resumeMic } = sigma2Resume;
|
|
84
89
|
|
|
85
90
|
// We use the Fallbacks for the session parameters overridden by our stored ones from the resumption record
|
|
@@ -103,10 +108,11 @@ export class CaseClient {
|
|
|
103
108
|
isInitiator: true,
|
|
104
109
|
isResumption: true,
|
|
105
110
|
peerSessionParameters: sessionParameters,
|
|
111
|
+
caseAuthenticatedTags,
|
|
106
112
|
});
|
|
107
113
|
await messenger.sendSuccess();
|
|
108
114
|
logger.info(
|
|
109
|
-
`Case client: Session resumed with ${messenger.getChannelName()} and parameters`,
|
|
115
|
+
`Case client: Session ${secureSession.id} successfully resumed with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(fabric.nodeId)}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)} and parameters`,
|
|
110
116
|
secureSession.parameterDiagnostics(),
|
|
111
117
|
);
|
|
112
118
|
|
|
@@ -157,17 +163,12 @@ export class CaseClient {
|
|
|
157
163
|
|
|
158
164
|
if (peerNodeIdNOCert !== peerNodeId) {
|
|
159
165
|
throw new UnexpectedDataError(
|
|
160
|
-
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
if (peerNodeIdNOCert !== peerNodeId) {
|
|
164
|
-
throw new UnexpectedDataError(
|
|
165
|
-
"The node ID in the peer certificate doesn't match the expected peer node ID",
|
|
166
|
+
`The node ID in the peer certificate ${peerNodeIdNOCert} doesn't match the expected peer node ID ${peerNodeId}`,
|
|
166
167
|
);
|
|
167
168
|
}
|
|
168
169
|
if (peerFabricIdNOCert !== fabric.fabricId) {
|
|
169
170
|
throw new UnexpectedDataError(
|
|
170
|
-
|
|
171
|
+
`The fabric ID in the peer certificate ${peerFabricIdNOCert} doesn't match the expected fabric ID ${fabric.fabricId}`,
|
|
171
172
|
);
|
|
172
173
|
}
|
|
173
174
|
if (peerIntermediateCACert !== undefined) {
|
|
@@ -175,9 +176,9 @@ export class CaseClient {
|
|
|
175
176
|
subject: { fabricId: peerFabricIdIcaCert },
|
|
176
177
|
} = TlvIntermediateCertificate.decode(peerIntermediateCACert);
|
|
177
178
|
|
|
178
|
-
if (peerFabricIdIcaCert !== fabric.fabricId) {
|
|
179
|
+
if (peerFabricIdIcaCert !== undefined && peerFabricIdIcaCert !== fabric.fabricId) {
|
|
179
180
|
throw new UnexpectedDataError(
|
|
180
|
-
|
|
181
|
+
`The fabric ID in the peer intermediate CA certificate ${peerFabricIdIcaCert} doesn't match the expected fabric ID ${fabric.fabricId}`,
|
|
181
182
|
);
|
|
182
183
|
}
|
|
183
184
|
}
|
|
@@ -199,6 +200,7 @@ export class CaseClient {
|
|
|
199
200
|
await messenger.waitForSuccess("Sigma3-Success");
|
|
200
201
|
|
|
201
202
|
// All good! Create secure session
|
|
203
|
+
const { caseAuthenticatedTags } = resumptionRecord ?? {}; // Even if resumption does not work try to reuse the caseAuthenticatedTags
|
|
202
204
|
const secureSessionSalt = Bytes.concat(
|
|
203
205
|
operationalIdentityProtectionKey,
|
|
204
206
|
Crypto.hash([sigma1Bytes, sigma2Bytes, sigma3Bytes]),
|
|
@@ -213,9 +215,12 @@ export class CaseClient {
|
|
|
213
215
|
isInitiator: true,
|
|
214
216
|
isResumption: false,
|
|
215
217
|
peerSessionParameters: sessionParameters,
|
|
218
|
+
caseAuthenticatedTags,
|
|
216
219
|
});
|
|
217
220
|
logger.info(
|
|
218
|
-
`Case client
|
|
221
|
+
`Case client Session ${secureSession.id} established successfully with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(
|
|
222
|
+
fabric.nodeId,
|
|
223
|
+
)}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}and parameters`,
|
|
219
224
|
secureSession.parameterDiagnostics(),
|
|
220
225
|
);
|
|
221
226
|
resumptionRecord = {
|
|
@@ -224,6 +229,7 @@ export class CaseClient {
|
|
|
224
229
|
sharedSecret,
|
|
225
230
|
resumptionId: peerResumptionId,
|
|
226
231
|
sessionParameters: secureSession.parameters,
|
|
232
|
+
caseAuthenticatedTags,
|
|
227
233
|
};
|
|
228
234
|
}
|
|
229
235
|
|
|
@@ -234,7 +234,7 @@ export class CaseServer implements ProtocolHandler {
|
|
|
234
234
|
caseAuthenticatedTags,
|
|
235
235
|
});
|
|
236
236
|
logger.info(
|
|
237
|
-
`
|
|
237
|
+
`Session ${secureSession.id} created with ${messenger.getChannelName()} for Fabric ${NodeId.toHexString(
|
|
238
238
|
fabric.nodeId,
|
|
239
239
|
)}(index ${fabric.fabricIndex}) and PeerNode ${NodeId.toHexString(peerNodeId)}`,
|
|
240
240
|
"with CATs",
|