@matter/general 0.16.0-alpha.0-20250810-5c91a95d2 → 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.
Files changed (51) hide show
  1. package/dist/cjs/crypto/Key.d.ts +7 -0
  2. package/dist/cjs/crypto/Key.d.ts.map +1 -1
  3. package/dist/cjs/crypto/Key.js +9 -4
  4. package/dist/cjs/crypto/Key.js.map +1 -1
  5. package/dist/cjs/crypto/StandardCrypto.d.ts +5 -1
  6. package/dist/cjs/crypto/StandardCrypto.d.ts.map +1 -1
  7. package/dist/cjs/crypto/StandardCrypto.js +45 -31
  8. package/dist/cjs/crypto/StandardCrypto.js.map +2 -2
  9. package/dist/cjs/crypto/WebCrypto.d.ts +12 -0
  10. package/dist/cjs/crypto/WebCrypto.d.ts.map +1 -0
  11. package/dist/cjs/crypto/WebCrypto.js +22 -0
  12. package/dist/cjs/crypto/WebCrypto.js.map +6 -0
  13. package/dist/cjs/crypto/index.d.ts +1 -0
  14. package/dist/cjs/crypto/index.d.ts.map +1 -1
  15. package/dist/cjs/crypto/index.js +1 -0
  16. package/dist/cjs/crypto/index.js.map +1 -1
  17. package/dist/cjs/net/RetrySchedule.js +1 -1
  18. package/dist/cjs/net/RetrySchedule.js.map +1 -1
  19. package/dist/cjs/util/Bytes.d.ts +1 -0
  20. package/dist/cjs/util/Bytes.d.ts.map +1 -1
  21. package/dist/cjs/util/Bytes.js +10 -0
  22. package/dist/cjs/util/Bytes.js.map +1 -1
  23. package/dist/esm/crypto/Key.d.ts +7 -0
  24. package/dist/esm/crypto/Key.d.ts.map +1 -1
  25. package/dist/esm/crypto/Key.js +9 -4
  26. package/dist/esm/crypto/Key.js.map +1 -1
  27. package/dist/esm/crypto/StandardCrypto.d.ts +5 -1
  28. package/dist/esm/crypto/StandardCrypto.d.ts.map +1 -1
  29. package/dist/esm/crypto/StandardCrypto.js +45 -31
  30. package/dist/esm/crypto/StandardCrypto.js.map +2 -2
  31. package/dist/esm/crypto/WebCrypto.d.ts +12 -0
  32. package/dist/esm/crypto/WebCrypto.d.ts.map +1 -0
  33. package/dist/esm/crypto/WebCrypto.js +6 -0
  34. package/dist/esm/crypto/WebCrypto.js.map +6 -0
  35. package/dist/esm/crypto/index.d.ts +1 -0
  36. package/dist/esm/crypto/index.d.ts.map +1 -1
  37. package/dist/esm/crypto/index.js +1 -0
  38. package/dist/esm/crypto/index.js.map +1 -1
  39. package/dist/esm/net/RetrySchedule.js +1 -1
  40. package/dist/esm/net/RetrySchedule.js.map +1 -1
  41. package/dist/esm/util/Bytes.d.ts +1 -0
  42. package/dist/esm/util/Bytes.d.ts.map +1 -1
  43. package/dist/esm/util/Bytes.js +10 -0
  44. package/dist/esm/util/Bytes.js.map +1 -1
  45. package/package.json +2 -2
  46. package/src/crypto/Key.ts +14 -3
  47. package/src/crypto/StandardCrypto.ts +59 -45
  48. package/src/crypto/WebCrypto.ts +11 -0
  49. package/src/crypto/index.ts +1 -0
  50. package/src/net/RetrySchedule.ts +1 -1
  51. package/src/util/Bytes.ts +12 -0
@@ -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(subtle: SubtleCrypto = globalThis.crypto?.subtle) {
48
- if (typeof subtle !== "object" || subtle === null) {
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
- const missingMethods = requiredSubtleMethods.filter(name => typeof subtle[name] !== "function");
55
- if (missingMethods.length) {
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.#importKey("raw", secret, "PBKDF2", false, ["deriveBits"]);
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.#importKey("raw", secret, "HKDF", false, ["deriveBits"]);
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.#importKey("raw", secret, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
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.#importKey("jwk", key, SIGNATURE_ALGORITHM, false, ["sign"]);
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.#importKey("jwk", key, SIGNATURE_ALGORITHM, false, ["verify"]);
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
- const key = await this.#subtle.exportKey("jwk", subtleKey.privateKey);
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.#importKey(
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.#importKey(
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
- #importKey(
254
- format: "jwk",
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
- ): Promise<CryptoKey>;
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(...(params as Parameters<SubtleCrypto["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(globalThis.crypto.subtle));
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 {}
@@ -11,3 +11,4 @@ export * from "./Key.js";
11
11
  export * from "./MockCrypto.js";
12
12
  export * from "./Spake2p.js";
13
13
  export * from "./StandardCrypto.js";
14
+ export * from "./WebCrypto.js";
@@ -40,7 +40,7 @@ export class RetrySchedule {
40
40
  while ((timeout === undefined || timeSoFar < timeout) && (maximumCount === undefined || maximumCount > count)) {
41
41
  count++;
42
42
  const maxJitter = jitterFactor * baseInterval;
43
- const jitter = Math.floor((2 * maxJitter * this.#crypto.randomUint32) / Math.pow(2, 32) - maxJitter);
43
+ const jitter = Math.floor((maxJitter * this.#crypto.randomUint32) / Math.pow(2, 32));
44
44
  let interval = baseInterval + jitter;
45
45
 
46
46
  if (timeout !== undefined && timeSoFar + interval > timeout) {
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.");