@matter/general 0.16.0-alpha.0-20250809-ee8375bcb → 0.16.0-alpha.0-20250812-285b75d83
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/MatterError.d.ts +1 -1
- package/dist/cjs/MatterError.d.ts.map +1 -1
- package/dist/cjs/MatterError.js +1 -1
- package/dist/cjs/MatterError.js.map +1 -1
- package/dist/cjs/codec/DnsCodec.d.ts +4 -5
- package/dist/cjs/codec/DnsCodec.d.ts.map +1 -1
- package/dist/cjs/codec/DnsCodec.js +6 -9
- package/dist/cjs/codec/DnsCodec.js.map +1 -1
- package/dist/cjs/crypto/Key.d.ts +7 -0
- package/dist/cjs/crypto/Key.d.ts.map +1 -1
- package/dist/cjs/crypto/Key.js +9 -4
- package/dist/cjs/crypto/Key.js.map +1 -1
- package/dist/cjs/crypto/StandardCrypto.d.ts +5 -1
- package/dist/cjs/crypto/StandardCrypto.d.ts.map +1 -1
- package/dist/cjs/crypto/StandardCrypto.js +45 -31
- package/dist/cjs/crypto/StandardCrypto.js.map +2 -2
- package/dist/cjs/crypto/WebCrypto.d.ts +12 -0
- package/dist/cjs/crypto/WebCrypto.d.ts.map +1 -0
- package/dist/cjs/crypto/WebCrypto.js +22 -0
- package/dist/cjs/crypto/WebCrypto.js.map +6 -0
- package/dist/cjs/crypto/index.d.ts +1 -0
- package/dist/cjs/crypto/index.d.ts.map +1 -1
- package/dist/cjs/crypto/index.js +1 -0
- package/dist/cjs/crypto/index.js.map +1 -1
- package/dist/cjs/net/RetrySchedule.d.ts +77 -0
- package/dist/cjs/net/RetrySchedule.d.ts.map +1 -0
- package/dist/cjs/net/RetrySchedule.js +79 -0
- package/dist/cjs/net/RetrySchedule.js.map +6 -0
- package/dist/cjs/net/UdpMulticastServer.d.ts +3 -2
- package/dist/cjs/net/UdpMulticastServer.d.ts.map +1 -1
- package/dist/cjs/net/UdpMulticastServer.js +3 -0
- package/dist/cjs/net/UdpMulticastServer.js.map +1 -1
- package/dist/cjs/net/index.d.ts +1 -0
- package/dist/cjs/net/index.d.ts.map +1 -1
- package/dist/cjs/net/index.js +1 -0
- package/dist/cjs/net/index.js.map +1 -1
- package/dist/cjs/time/Time.js +1 -1
- package/dist/cjs/time/Time.js.map +1 -1
- package/dist/cjs/util/Bytes.d.ts +1 -0
- package/dist/cjs/util/Bytes.d.ts.map +1 -1
- package/dist/cjs/util/Bytes.js +10 -0
- package/dist/cjs/util/Bytes.js.map +1 -1
- package/dist/cjs/util/Cache.d.ts +1 -1
- package/dist/cjs/util/Cache.d.ts.map +1 -1
- package/dist/cjs/util/Cache.js +3 -3
- package/dist/cjs/util/Cache.js.map +1 -1
- package/dist/cjs/util/Cancelable.js +1 -1
- package/dist/cjs/util/Cancelable.js.map +1 -1
- package/dist/cjs/util/Multiplex.d.ts +12 -1
- package/dist/cjs/util/Multiplex.d.ts.map +1 -1
- package/dist/cjs/util/Multiplex.js +40 -0
- package/dist/cjs/util/Multiplex.js.map +1 -1
- package/dist/cjs/util/index.d.ts +1 -0
- package/dist/cjs/util/index.d.ts.map +1 -1
- package/dist/cjs/util/index.js +1 -0
- package/dist/cjs/util/index.js.map +1 -1
- package/dist/esm/MatterError.d.ts +1 -1
- package/dist/esm/MatterError.d.ts.map +1 -1
- package/dist/esm/MatterError.js +1 -1
- package/dist/esm/MatterError.js.map +1 -1
- package/dist/esm/codec/DnsCodec.d.ts +4 -5
- package/dist/esm/codec/DnsCodec.d.ts.map +1 -1
- package/dist/esm/codec/DnsCodec.js +6 -9
- package/dist/esm/codec/DnsCodec.js.map +1 -1
- package/dist/esm/crypto/Key.d.ts +7 -0
- package/dist/esm/crypto/Key.d.ts.map +1 -1
- package/dist/esm/crypto/Key.js +9 -4
- package/dist/esm/crypto/Key.js.map +1 -1
- package/dist/esm/crypto/StandardCrypto.d.ts +5 -1
- package/dist/esm/crypto/StandardCrypto.d.ts.map +1 -1
- package/dist/esm/crypto/StandardCrypto.js +45 -31
- package/dist/esm/crypto/StandardCrypto.js.map +2 -2
- package/dist/esm/crypto/WebCrypto.d.ts +12 -0
- package/dist/esm/crypto/WebCrypto.d.ts.map +1 -0
- package/dist/esm/crypto/WebCrypto.js +6 -0
- package/dist/esm/crypto/WebCrypto.js.map +6 -0
- package/dist/esm/crypto/index.d.ts +1 -0
- package/dist/esm/crypto/index.d.ts.map +1 -1
- package/dist/esm/crypto/index.js +1 -0
- package/dist/esm/crypto/index.js.map +1 -1
- package/dist/esm/net/RetrySchedule.d.ts +77 -0
- package/dist/esm/net/RetrySchedule.d.ts.map +1 -0
- package/dist/esm/net/RetrySchedule.js +59 -0
- package/dist/esm/net/RetrySchedule.js.map +6 -0
- package/dist/esm/net/UdpMulticastServer.d.ts +3 -2
- package/dist/esm/net/UdpMulticastServer.d.ts.map +1 -1
- package/dist/esm/net/UdpMulticastServer.js +3 -0
- package/dist/esm/net/UdpMulticastServer.js.map +1 -1
- package/dist/esm/net/index.d.ts +1 -0
- package/dist/esm/net/index.d.ts.map +1 -1
- package/dist/esm/net/index.js +1 -0
- package/dist/esm/net/index.js.map +1 -1
- package/dist/esm/time/Time.js +1 -1
- package/dist/esm/time/Time.js.map +1 -1
- package/dist/esm/util/Bytes.d.ts +1 -0
- package/dist/esm/util/Bytes.d.ts.map +1 -1
- package/dist/esm/util/Bytes.js +10 -0
- package/dist/esm/util/Bytes.js.map +1 -1
- package/dist/esm/util/Cache.d.ts +1 -1
- package/dist/esm/util/Cache.d.ts.map +1 -1
- package/dist/esm/util/Cache.js +3 -3
- package/dist/esm/util/Cache.js.map +1 -1
- package/dist/esm/util/Cancelable.js +1 -1
- package/dist/esm/util/Cancelable.js.map +1 -1
- package/dist/esm/util/Multiplex.d.ts +12 -1
- package/dist/esm/util/Multiplex.d.ts.map +1 -1
- package/dist/esm/util/Multiplex.js +36 -0
- package/dist/esm/util/Multiplex.js.map +1 -1
- package/dist/esm/util/index.d.ts +1 -0
- package/dist/esm/util/index.d.ts.map +1 -1
- package/dist/esm/util/index.js +1 -0
- package/dist/esm/util/index.js.map +1 -1
- package/package.json +2 -2
- package/src/MatterError.ts +1 -1
- package/src/codec/DnsCodec.ts +3 -20
- package/src/crypto/Key.ts +14 -3
- package/src/crypto/StandardCrypto.ts +59 -45
- package/src/crypto/WebCrypto.ts +11 -0
- package/src/crypto/index.ts +1 -0
- package/src/net/RetrySchedule.ts +120 -0
- package/src/net/UdpMulticastServer.ts +6 -2
- package/src/net/index.ts +1 -0
- package/src/time/Time.ts +1 -1
- package/src/util/Bytes.ts +12 -0
- package/src/util/Cache.ts +3 -3
- package/src/util/Cancelable.ts +1 -1
- package/src/util/Multiplex.ts +56 -1
- package/src/util/index.ts +1 -0
package/src/codec/DnsCodec.ts
CHANGED
|
@@ -16,20 +16,13 @@ import { ipv4BytesToString, ipv4ToBytes, ipv6BytesToString, ipv6ToBytes, isIPv4,
|
|
|
16
16
|
*/
|
|
17
17
|
export const MAX_MDNS_MESSAGE_SIZE = 1232; // 1280bytes (IPv6 packet size) - 8bytes (UDP header) - 40bytes (IPv6 IP header, IPv4 is only 20bytes)
|
|
18
18
|
|
|
19
|
-
export const PtrRecord = (
|
|
20
|
-
name: string,
|
|
21
|
-
ptr: string,
|
|
22
|
-
forInstance?: string,
|
|
23
|
-
ttl = 120,
|
|
24
|
-
flushCache = false,
|
|
25
|
-
): DnsRecord<string> => ({
|
|
19
|
+
export const PtrRecord = (name: string, ptr: string, ttl = 120, flushCache = false): DnsRecord<string> => ({
|
|
26
20
|
name,
|
|
27
21
|
value: ptr,
|
|
28
22
|
ttl,
|
|
29
23
|
recordType: DnsRecordType.PTR,
|
|
30
24
|
recordClass: DnsRecordClass.IN,
|
|
31
25
|
flushCache,
|
|
32
|
-
forInstance,
|
|
33
26
|
});
|
|
34
27
|
export const ARecord = (name: string, ip: string, ttl = 120, flushCache = false): DnsRecord<string> => ({
|
|
35
28
|
name,
|
|
@@ -47,25 +40,17 @@ export const AAAARecord = (name: string, ip: string, ttl = 120, flushCache = fal
|
|
|
47
40
|
recordClass: DnsRecordClass.IN,
|
|
48
41
|
flushCache,
|
|
49
42
|
});
|
|
50
|
-
export const TxtRecord = (
|
|
51
|
-
name: string,
|
|
52
|
-
entries: string[],
|
|
53
|
-
forInstance?: string,
|
|
54
|
-
ttl = 120,
|
|
55
|
-
flushCache = false,
|
|
56
|
-
): DnsRecord<string[]> => ({
|
|
43
|
+
export const TxtRecord = (name: string, entries: string[], ttl = 120, flushCache = false): DnsRecord<string[]> => ({
|
|
57
44
|
name,
|
|
58
45
|
value: entries,
|
|
59
46
|
ttl,
|
|
60
47
|
recordType: DnsRecordType.TXT,
|
|
61
48
|
recordClass: DnsRecordClass.IN,
|
|
62
49
|
flushCache,
|
|
63
|
-
forInstance,
|
|
64
50
|
});
|
|
65
51
|
export const SrvRecord = (
|
|
66
52
|
name: string,
|
|
67
53
|
srv: SrvRecordValue,
|
|
68
|
-
forInstance?: string,
|
|
69
54
|
ttl = 120,
|
|
70
55
|
flushCache = false,
|
|
71
56
|
): DnsRecord<SrvRecordValue> => ({
|
|
@@ -75,7 +60,6 @@ export const SrvRecord = (
|
|
|
75
60
|
recordType: DnsRecordType.SRV,
|
|
76
61
|
recordClass: DnsRecordClass.IN,
|
|
77
62
|
flushCache,
|
|
78
|
-
forInstance,
|
|
79
63
|
});
|
|
80
64
|
|
|
81
65
|
export type SrvRecordValue = {
|
|
@@ -92,14 +76,13 @@ export type DnsQuery = {
|
|
|
92
76
|
uniCastResponse?: boolean;
|
|
93
77
|
};
|
|
94
78
|
|
|
95
|
-
export type DnsRecord<T> = {
|
|
79
|
+
export type DnsRecord<T = unknown> = {
|
|
96
80
|
name: string;
|
|
97
81
|
recordType: DnsRecordType;
|
|
98
82
|
recordClass: DnsRecordClass;
|
|
99
83
|
flushCache?: boolean;
|
|
100
84
|
ttl: number;
|
|
101
85
|
value: T;
|
|
102
|
-
forInstance?: string;
|
|
103
86
|
};
|
|
104
87
|
|
|
105
88
|
export type DnsMessage = {
|
package/src/crypto/Key.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { KeyInputError } from "./CryptoError.js";
|
|
|
13
13
|
|
|
14
14
|
const {
|
|
15
15
|
numberToBytesBE,
|
|
16
|
-
p256: {
|
|
16
|
+
p256: { Point, getSharedSecret },
|
|
17
17
|
} = ec;
|
|
18
18
|
|
|
19
19
|
const JWK_KEYS = [
|
|
@@ -211,6 +211,7 @@ export interface PrivateKey extends PublicKey {
|
|
|
211
211
|
privateKey: Uint8Array;
|
|
212
212
|
keyPair: BinaryKeyPair;
|
|
213
213
|
keyPairBits: BinaryKeyPair;
|
|
214
|
+
sharedSecretFor(peerKey: PublicKey): Uint8Array;
|
|
214
215
|
}
|
|
215
216
|
|
|
216
217
|
/**
|
|
@@ -535,7 +536,7 @@ export function Key(properties: Partial<Key>) {
|
|
|
535
536
|
}
|
|
536
537
|
|
|
537
538
|
// Compute
|
|
538
|
-
const ecKey =
|
|
539
|
+
const ecKey = Point.fromPrivateKey(that.privateKey);
|
|
539
540
|
|
|
540
541
|
// Install
|
|
541
542
|
that.xBits = numberToBytesBE(ecKey.x, keyLength);
|
|
@@ -573,7 +574,8 @@ export function PrivateKey(privateKey: Uint8Array | BinaryKeyPair, options?: Par
|
|
|
573
574
|
privateKey: priv,
|
|
574
575
|
publicKey: pub,
|
|
575
576
|
...options,
|
|
576
|
-
|
|
577
|
+
sharedSecretFor,
|
|
578
|
+
} as Key) as PrivateKey;
|
|
577
579
|
}
|
|
578
580
|
|
|
579
581
|
/**
|
|
@@ -597,3 +599,12 @@ export function SymmetricKey(privateKey: Uint8Array, options?: Partial<Key>) {
|
|
|
597
599
|
...options,
|
|
598
600
|
});
|
|
599
601
|
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Diffie-Hellman shared secret computation.
|
|
605
|
+
*
|
|
606
|
+
* We provide this for platforms without a native implementation.
|
|
607
|
+
*/
|
|
608
|
+
export function sharedSecretFor(this: PrivateKey, peerKey: PublicKey): Uint8Array {
|
|
609
|
+
return getSharedSecret(this.privateBits, peerKey.publicBits);
|
|
610
|
+
}
|
|
@@ -14,6 +14,10 @@ import { Ccm } from "./aes/Ccm.js";
|
|
|
14
14
|
import { Crypto, CRYPTO_SYMMETRIC_KEY_LENGTH, CryptoDsaEncoding } from "./Crypto.js";
|
|
15
15
|
import { CryptoVerifyError, KeyInputError } from "./CryptoError.js";
|
|
16
16
|
import { CurveType, Key, KeyType, PrivateKey, PublicKey } from "./Key.js";
|
|
17
|
+
import { WebCrypto } from "./WebCrypto.js";
|
|
18
|
+
|
|
19
|
+
// Ensure we don't reference global crypto accidentally
|
|
20
|
+
declare const crypto: never;
|
|
17
21
|
|
|
18
22
|
const SIGNATURE_ALGORITHM = <EcdsaParams>{
|
|
19
23
|
name: "ECDSA",
|
|
@@ -21,6 +25,8 @@ const SIGNATURE_ALGORITHM = <EcdsaParams>{
|
|
|
21
25
|
hash: { name: "SHA-256" },
|
|
22
26
|
};
|
|
23
27
|
|
|
28
|
+
const requiredCryptoMethods: Array<keyof WebCrypto> = ["getRandomValues"];
|
|
29
|
+
|
|
24
30
|
const requiredSubtleMethods: Array<keyof SubtleCrypto> = [
|
|
25
31
|
"digest",
|
|
26
32
|
"deriveBits",
|
|
@@ -42,34 +48,32 @@ const requiredSubtleMethods: Array<keyof SubtleCrypto> = [
|
|
|
42
48
|
*/
|
|
43
49
|
export class StandardCrypto extends Crypto {
|
|
44
50
|
implementationName = "JS";
|
|
51
|
+
#crypto: WebCrypto;
|
|
45
52
|
#subtle: SubtleCrypto;
|
|
46
53
|
|
|
47
|
-
constructor(
|
|
48
|
-
|
|
49
|
-
throw new ImplementationError(
|
|
50
|
-
"You cannot instantiate StandardCrypto in this runtime because crypto.subtle is not present",
|
|
51
|
-
);
|
|
52
|
-
}
|
|
54
|
+
constructor(crypto: WebCrypto = globalThis.crypto) {
|
|
55
|
+
const { subtle } = crypto;
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
throw new ImplementationError(
|
|
57
|
-
`SubtleCrypto implementation is missing required method${missingMethods.length === 1 ? "" : "s"} ${describeList("and", ...missingMethods)}`,
|
|
58
|
-
);
|
|
59
|
-
}
|
|
57
|
+
assertInterface("crypto", crypto, requiredCryptoMethods);
|
|
58
|
+
assertInterface("crypto.subtle", subtle, requiredSubtleMethods);
|
|
60
59
|
|
|
61
60
|
super();
|
|
62
61
|
|
|
62
|
+
this.#crypto = crypto;
|
|
63
63
|
this.#subtle = subtle;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
protected get subtle() {
|
|
67
|
+
return this.#subtle;
|
|
68
|
+
}
|
|
69
|
+
|
|
66
70
|
static provider() {
|
|
67
71
|
return new StandardCrypto();
|
|
68
72
|
}
|
|
69
73
|
|
|
70
74
|
randomBytes(length: number): Uint8Array {
|
|
71
75
|
const result = new Uint8Array(length);
|
|
72
|
-
crypto.getRandomValues(result);
|
|
76
|
+
this.#crypto.getRandomValues(result);
|
|
73
77
|
return result;
|
|
74
78
|
}
|
|
75
79
|
|
|
@@ -91,7 +95,7 @@ export class StandardCrypto extends Crypto {
|
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
async createPbkdf2Key(secret: Uint8Array, salt: Uint8Array, iteration: number, keyLength: number) {
|
|
94
|
-
const key = await this
|
|
98
|
+
const key = await this.importKey("raw", secret, "PBKDF2", false, ["deriveBits"]);
|
|
95
99
|
const bits = await this.#subtle.deriveBits(
|
|
96
100
|
{
|
|
97
101
|
name: "PBKDF2",
|
|
@@ -111,7 +115,7 @@ export class StandardCrypto extends Crypto {
|
|
|
111
115
|
info: Uint8Array,
|
|
112
116
|
length: number = CRYPTO_SYMMETRIC_KEY_LENGTH,
|
|
113
117
|
) {
|
|
114
|
-
const key = await this
|
|
118
|
+
const key = await this.importKey("raw", secret, "HKDF", false, ["deriveBits"]);
|
|
115
119
|
const bits = await this.#subtle.deriveBits(
|
|
116
120
|
{
|
|
117
121
|
name: "HKDF",
|
|
@@ -126,7 +130,7 @@ export class StandardCrypto extends Crypto {
|
|
|
126
130
|
}
|
|
127
131
|
|
|
128
132
|
async signHmac(secret: Uint8Array, data: Uint8Array) {
|
|
129
|
-
const key = await this
|
|
133
|
+
const key = await this.importKey("raw", secret, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
130
134
|
return new Uint8Array(await this.#subtle.sign("HMAC", key, data));
|
|
131
135
|
}
|
|
132
136
|
|
|
@@ -147,7 +151,7 @@ export class StandardCrypto extends Crypto {
|
|
|
147
151
|
key_ops: ["sign"],
|
|
148
152
|
};
|
|
149
153
|
|
|
150
|
-
const subtleKey = await this
|
|
154
|
+
const subtleKey = await this.importKey("jwk", key, SIGNATURE_ALGORITHM, false, ["sign"]);
|
|
151
155
|
|
|
152
156
|
const ieeeP1363 = await this.#subtle.sign(SIGNATURE_ALGORITHM, subtleKey, data);
|
|
153
157
|
|
|
@@ -164,7 +168,7 @@ export class StandardCrypto extends Crypto {
|
|
|
164
168
|
async verifyEcdsa(key: JsonWebKey, data: Uint8Array, signature: Uint8Array, dsaEncoding?: CryptoDsaEncoding) {
|
|
165
169
|
const { crv, kty, x, y } = key;
|
|
166
170
|
key = { crv, kty, x, y };
|
|
167
|
-
const subtleKey = await this
|
|
171
|
+
const subtleKey = await this.importKey("jwk", key, SIGNATURE_ALGORITHM, false, ["verify"]);
|
|
168
172
|
|
|
169
173
|
if (dsaEncoding === "der") {
|
|
170
174
|
try {
|
|
@@ -189,6 +193,19 @@ export class StandardCrypto extends Crypto {
|
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
async createKeyPair() {
|
|
196
|
+
const key = await this.generateJwk();
|
|
197
|
+
|
|
198
|
+
// Extract only private and public fields; we do not want key_ops
|
|
199
|
+
return Key({
|
|
200
|
+
kty: KeyType.EC,
|
|
201
|
+
crv: CurveType.p256,
|
|
202
|
+
d: key.d,
|
|
203
|
+
x: key.x,
|
|
204
|
+
y: key.y,
|
|
205
|
+
}) as PrivateKey;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
protected async generateJwk() {
|
|
192
209
|
const subtleKey = await this.#subtle.generateKey(
|
|
193
210
|
{
|
|
194
211
|
// We must specify either ECDH or ECDSA to get an EC key but we may use the key for either (but not for
|
|
@@ -203,20 +220,11 @@ export class StandardCrypto extends Crypto {
|
|
|
203
220
|
);
|
|
204
221
|
|
|
205
222
|
// Do not export as JWK because we do not want to inherit the algorithm and key_ops
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
// Extract only private and public fields; we do not want key_ops
|
|
209
|
-
return Key({
|
|
210
|
-
kty: KeyType.EC,
|
|
211
|
-
crv: CurveType.p256,
|
|
212
|
-
d: key.d,
|
|
213
|
-
x: key.x,
|
|
214
|
-
y: key.y,
|
|
215
|
-
}) as PrivateKey;
|
|
223
|
+
return await this.#subtle.exportKey("jwk", subtleKey.privateKey);
|
|
216
224
|
}
|
|
217
225
|
|
|
218
226
|
async generateDhSecret(key: PrivateKey, peerKey: PublicKey) {
|
|
219
|
-
const subtleKey = await this
|
|
227
|
+
const subtleKey = await this.importKey(
|
|
220
228
|
"jwk",
|
|
221
229
|
key,
|
|
222
230
|
{
|
|
@@ -227,7 +235,7 @@ export class StandardCrypto extends Crypto {
|
|
|
227
235
|
["deriveBits"],
|
|
228
236
|
);
|
|
229
237
|
|
|
230
|
-
const subtlePeerKey = await this
|
|
238
|
+
const subtlePeerKey = await this.importKey(
|
|
231
239
|
"jwk",
|
|
232
240
|
peerKey,
|
|
233
241
|
{
|
|
@@ -250,32 +258,38 @@ export class StandardCrypto extends Crypto {
|
|
|
250
258
|
return new Uint8Array(secret);
|
|
251
259
|
}
|
|
252
260
|
|
|
253
|
-
|
|
254
|
-
format:
|
|
255
|
-
keyData: JsonWebKey,
|
|
261
|
+
protected async importKey(
|
|
262
|
+
format: KeyFormat,
|
|
263
|
+
keyData: JsonWebKey | BufferSource,
|
|
256
264
|
algorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm,
|
|
257
265
|
extractable: boolean,
|
|
258
266
|
keyUsages: ReadonlyArray<KeyUsage>,
|
|
259
|
-
)
|
|
260
|
-
#importKey(
|
|
261
|
-
format: Exclude<KeyFormat, "jwk">,
|
|
262
|
-
keyData: BufferSource,
|
|
263
|
-
algorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm,
|
|
264
|
-
extractable: boolean,
|
|
265
|
-
keyUsages: KeyUsage[],
|
|
266
|
-
): Promise<CryptoKey>;
|
|
267
|
-
|
|
268
|
-
async #importKey(...params: unknown[]) {
|
|
267
|
+
) {
|
|
269
268
|
try {
|
|
270
|
-
return await this.#subtle.importKey(
|
|
269
|
+
return await this.#subtle.importKey(format as any, keyData as any, algorithm, extractable, keyUsages);
|
|
271
270
|
} catch (cause) {
|
|
272
271
|
throw new KeyInputError("Invalid key", { cause });
|
|
273
272
|
}
|
|
274
273
|
}
|
|
275
274
|
}
|
|
276
275
|
|
|
276
|
+
function assertInterface<T extends {}>(name: string, object: T, requiredMethods: (keyof T & string)[]) {
|
|
277
|
+
if (typeof object !== "object" || object === null) {
|
|
278
|
+
throw new ImplementationError(
|
|
279
|
+
`The ${name} implementation passed to StandardCrypto is invalid (received ${typeof object})`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const missingMethods = requiredMethods.filter(name => typeof object[name] !== "function");
|
|
284
|
+
if (missingMethods.length) {
|
|
285
|
+
throw new ImplementationError(
|
|
286
|
+
`The ${name} implementation passed to StandardCrypto is missing required method${missingMethods.length === 1 ? "" : "s"} ${describeList("and", ...missingMethods)}`,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
277
291
|
// If available, unconditionally add to Environment as it has not been exported yet so there can be no other
|
|
278
292
|
// implementation present
|
|
279
293
|
if ("crypto" in globalThis && globalThis.crypto?.subtle) {
|
|
280
|
-
Environment.default.set(Crypto, new StandardCrypto(
|
|
294
|
+
Environment.default.set(Crypto, new StandardCrypto());
|
|
281
295
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2025 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Our "Crypto" interface masks the standard web crypto "Crypto" type provided by typescript. Make an alias to work
|
|
9
|
+
* around this.
|
|
10
|
+
*/
|
|
11
|
+
export interface WebCrypto extends Crypto {}
|
package/src/crypto/index.ts
CHANGED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2022-2025 Matter.js Authors
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Crypto } from "#crypto/Crypto.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* An iterable of retry values based on a scheduling configuration.
|
|
11
|
+
*/
|
|
12
|
+
export class RetrySchedule {
|
|
13
|
+
#crypto: Crypto;
|
|
14
|
+
readonly config: RetrySchedule.Configuration;
|
|
15
|
+
|
|
16
|
+
constructor(crypto: Crypto, config: RetrySchedule.Configuration) {
|
|
17
|
+
this.#crypto = crypto;
|
|
18
|
+
this.config = config;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Yields intervals.
|
|
23
|
+
*
|
|
24
|
+
* Will yield indefinitely until canceled unless {@link config} specifies a timeout.
|
|
25
|
+
*/
|
|
26
|
+
*[Symbol.iterator]() {
|
|
27
|
+
const {
|
|
28
|
+
timeout,
|
|
29
|
+
maximumInterval,
|
|
30
|
+
maximumCount,
|
|
31
|
+
jitterFactor = 0,
|
|
32
|
+
initialInterval = 1000,
|
|
33
|
+
backoffFactor = 2,
|
|
34
|
+
} = this.config;
|
|
35
|
+
|
|
36
|
+
let count = 0;
|
|
37
|
+
let baseInterval = initialInterval;
|
|
38
|
+
let timeSoFar = 0;
|
|
39
|
+
|
|
40
|
+
while ((timeout === undefined || timeSoFar < timeout) && (maximumCount === undefined || maximumCount > count)) {
|
|
41
|
+
count++;
|
|
42
|
+
const maxJitter = jitterFactor * baseInterval;
|
|
43
|
+
const jitter = Math.floor((maxJitter * this.#crypto.randomUint32) / Math.pow(2, 32));
|
|
44
|
+
let interval = baseInterval + jitter;
|
|
45
|
+
|
|
46
|
+
if (timeout !== undefined && timeSoFar + interval > timeout) {
|
|
47
|
+
interval = timeout - timeSoFar;
|
|
48
|
+
}
|
|
49
|
+
if (maximumInterval !== undefined && interval > maximumInterval) {
|
|
50
|
+
interval = maximumInterval;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
yield interval;
|
|
54
|
+
timeSoFar += interval;
|
|
55
|
+
|
|
56
|
+
baseInterval *= backoffFactor;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export namespace RetrySchedule {
|
|
62
|
+
/**
|
|
63
|
+
* Configuration parameters for retry schedule.
|
|
64
|
+
*
|
|
65
|
+
* All intervals are in milliseconds.
|
|
66
|
+
*/
|
|
67
|
+
export interface Configuration {
|
|
68
|
+
/**
|
|
69
|
+
* Overall timeout in seconds.
|
|
70
|
+
*
|
|
71
|
+
* Leave undefined to allow indefinite transmission.
|
|
72
|
+
*/
|
|
73
|
+
readonly timeout?: number;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Maximum number of occurrences (including first).
|
|
77
|
+
*
|
|
78
|
+
* Set to zero to disable transmission completely; leave undefined to allow any number of transmissions.
|
|
79
|
+
*/
|
|
80
|
+
readonly maximumCount?: number;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Interval between first request and final interval.
|
|
84
|
+
*
|
|
85
|
+
* Defaults to 1000 ms.
|
|
86
|
+
*/
|
|
87
|
+
readonly initialInterval?: number;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Multiplier for subsequent retries.
|
|
91
|
+
*
|
|
92
|
+
* Defaults to 2.
|
|
93
|
+
*/
|
|
94
|
+
readonly backoffFactor?: number;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Maximum interval between retries (excluding jitter).
|
|
98
|
+
*
|
|
99
|
+
* Leave undefined for interval to allow interval to grow continuously.
|
|
100
|
+
*/
|
|
101
|
+
readonly maximumInterval?: number;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Multiplier for retry jitter.
|
|
105
|
+
*
|
|
106
|
+
* Leave undefined to disable jitter.
|
|
107
|
+
*/
|
|
108
|
+
readonly jitterFactor?: number;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Create a full configuration with defaults.
|
|
113
|
+
*/
|
|
114
|
+
export function Configuration(defaults: Configuration, config?: Configuration) {
|
|
115
|
+
return {
|
|
116
|
+
...defaults,
|
|
117
|
+
...config,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -76,15 +76,19 @@ export class UdpMulticastServer {
|
|
|
76
76
|
);
|
|
77
77
|
|
|
78
78
|
private constructor(
|
|
79
|
-
|
|
79
|
+
readonly network: Network,
|
|
80
80
|
private readonly broadcastAddressIpv4: string | undefined,
|
|
81
81
|
private readonly broadcastAddressIpv6: string,
|
|
82
82
|
private readonly broadcastPort: number,
|
|
83
83
|
private readonly serverIpv4: UdpChannel | undefined,
|
|
84
84
|
private readonly serverIpv6: UdpChannel,
|
|
85
|
-
|
|
85
|
+
readonly netInterface: string | undefined,
|
|
86
86
|
) {}
|
|
87
87
|
|
|
88
|
+
get supportsIpv4() {
|
|
89
|
+
return this.serverIpv4 !== undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
88
92
|
onMessage(listener: (message: Uint8Array, peerAddress: string, netInterface: string) => void) {
|
|
89
93
|
this.serverIpv4?.onData((netInterface, peerAddress, _port, message) => {
|
|
90
94
|
if (netInterface === undefined) {
|
package/src/net/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./Channel.js";
|
|
|
8
8
|
export * from "./mock/index.js";
|
|
9
9
|
export * from "./NetInterface.js";
|
|
10
10
|
export * from "./Network.js";
|
|
11
|
+
export * from "./RetrySchedule.js";
|
|
11
12
|
export * from "./ServerAddress.js";
|
|
12
13
|
export * from "./TransportInterface.js";
|
|
13
14
|
export * from "./UdpChannel.js";
|
package/src/time/Time.ts
CHANGED
package/src/util/Bytes.ts
CHANGED
|
@@ -56,6 +56,18 @@ export namespace Bytes {
|
|
|
56
56
|
return array1.every((value, index) => array2[index] === value);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
export function of(source: BufferSource) {
|
|
60
|
+
if (source instanceof Uint8Array) {
|
|
61
|
+
return source;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (ArrayBuffer.isView(source)) {
|
|
65
|
+
return new Uint8Array(source.buffer, source.byteLength, source.byteOffset);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return new Uint8Array(source);
|
|
69
|
+
}
|
|
70
|
+
|
|
59
71
|
export function fromHex(hexString: string) {
|
|
60
72
|
if (hexString.length === 0) return new Uint8Array(0);
|
|
61
73
|
if (hexString.length % 2 !== 0) throw new UnexpectedDataError("Hex string should have an even length.");
|
package/src/util/Cache.ts
CHANGED
|
@@ -32,7 +32,7 @@ class GenericCache<T> {
|
|
|
32
32
|
return Array.from(this.knownKeys.values());
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
async delete(key: string) {
|
|
36
36
|
const value = this.values.get(key);
|
|
37
37
|
if (this.expireCallback !== undefined && value !== undefined) {
|
|
38
38
|
await this.expireCallback(key, value);
|
|
@@ -43,7 +43,7 @@ class GenericCache<T> {
|
|
|
43
43
|
|
|
44
44
|
async clear() {
|
|
45
45
|
for (const key of this.values.keys()) {
|
|
46
|
-
await this.
|
|
46
|
+
await this.delete(key);
|
|
47
47
|
}
|
|
48
48
|
this.values.clear();
|
|
49
49
|
this.timestamps.clear();
|
|
@@ -59,7 +59,7 @@ class GenericCache<T> {
|
|
|
59
59
|
const now = Time.nowMs();
|
|
60
60
|
for (const [key, timestamp] of this.timestamps.entries()) {
|
|
61
61
|
if (now - timestamp < this.expirationMs) continue;
|
|
62
|
-
await this.
|
|
62
|
+
await this.delete(key);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
}
|
package/src/util/Cancelable.ts
CHANGED
|
@@ -116,7 +116,7 @@ export class CancelablePromise<T = void> implements Promise<T>, Cancelable {
|
|
|
116
116
|
catch<TResult = never>(
|
|
117
117
|
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null,
|
|
118
118
|
): CancelablePromise<T | TResult> {
|
|
119
|
-
return this.then(onrejected);
|
|
119
|
+
return this.then(undefined, onrejected);
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
finally(onfinally?: (() => void) | null): CancelablePromise<T> {
|
package/src/util/Multiplex.ts
CHANGED
|
@@ -4,11 +4,66 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { Logger } from "#log/Logger.js";
|
|
8
|
+
import { MatterAggregateError } from "#MatterError.js";
|
|
9
|
+
import { MaybePromise } from "./Promises.js";
|
|
10
|
+
import { BasicSet } from "./Set.js";
|
|
11
|
+
|
|
12
|
+
const logger = Logger.get("Multiplex");
|
|
13
|
+
|
|
7
14
|
/**
|
|
8
15
|
* A "multiplex" tracks an extensible set of promises.
|
|
9
16
|
*/
|
|
10
17
|
export interface Multiplex {
|
|
11
|
-
add(worker: Promise<unknown
|
|
18
|
+
add(worker: Promise<unknown>, description?: string): void;
|
|
12
19
|
close(): Promise<void>;
|
|
13
20
|
[Symbol.asyncDispose](): Promise<void>;
|
|
14
21
|
}
|
|
22
|
+
|
|
23
|
+
interface WorkerEntry {
|
|
24
|
+
done: Promise<unknown>;
|
|
25
|
+
description?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A basic multiplex that tracks all promises given to it.
|
|
30
|
+
*/
|
|
31
|
+
export class BasicMultiplex implements PromiseLike<void> {
|
|
32
|
+
#workers = new BasicSet<WorkerEntry>();
|
|
33
|
+
|
|
34
|
+
add(worker: MaybePromise<unknown>, description?: string) {
|
|
35
|
+
if (!MaybePromise.is(worker)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const entry = {
|
|
40
|
+
done: Promise.resolve(worker)
|
|
41
|
+
.catch(e => {
|
|
42
|
+
let message = "Unhandled error";
|
|
43
|
+
if (description) {
|
|
44
|
+
message = `${message} in ${description}`;
|
|
45
|
+
}
|
|
46
|
+
logger.error(message, e);
|
|
47
|
+
})
|
|
48
|
+
.finally(() => this.#workers.delete(entry)),
|
|
49
|
+
description,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
this.#workers.add(entry);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async close() {
|
|
56
|
+
while (this.#workers.size) {
|
|
57
|
+
await MatterAggregateError.allSettled([...this.#workers].map(entry => entry.done));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
then<TResult1 = void, TResult2 = never>(
|
|
62
|
+
onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null,
|
|
63
|
+
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,
|
|
64
|
+
): PromiseLike<TResult1 | TResult2> {
|
|
65
|
+
return this.close().then(onfulfilled, onrejected);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
[Symbol.asyncDispose] = this.close.bind(this);
|
|
69
|
+
}
|
package/src/util/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export * from "./GeneratedClass.js";
|
|
|
21
21
|
export * from "./Ip.js";
|
|
22
22
|
export * from "./Lifecycle.js";
|
|
23
23
|
export * from "./Map.js";
|
|
24
|
+
export * from "./Multiplex.js";
|
|
24
25
|
export * from "./Mutex.js";
|
|
25
26
|
export * from "./NamedHandler.js";
|
|
26
27
|
export * from "./Number.js";
|