@matter/general 0.14.1-alpha.0-20250607-a93593303 → 0.15.0-alpha.0-20250612-ddd428561

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 (185) hide show
  1. package/dist/cjs/codec/DerCodec.d.ts +12 -17
  2. package/dist/cjs/codec/DerCodec.d.ts.map +1 -1
  3. package/dist/cjs/codec/DerCodec.js +90 -51
  4. package/dist/cjs/codec/DerCodec.js.map +1 -1
  5. package/dist/cjs/codec/DerTypes.js +1 -1
  6. package/dist/cjs/codec/DnsCodec.d.ts +5 -5
  7. package/dist/cjs/crypto/Crypto.d.ts +111 -62
  8. package/dist/cjs/crypto/Crypto.d.ts.map +1 -1
  9. package/dist/cjs/crypto/Crypto.js +92 -31
  10. package/dist/cjs/crypto/Crypto.js.map +1 -1
  11. package/dist/cjs/crypto/CryptoError.d.ts +32 -0
  12. package/dist/cjs/crypto/CryptoError.d.ts.map +1 -0
  13. package/dist/cjs/crypto/CryptoError.js +44 -0
  14. package/dist/cjs/crypto/CryptoError.js.map +6 -0
  15. package/dist/cjs/crypto/Key.d.ts +2 -2
  16. package/dist/cjs/crypto/Key.d.ts.map +1 -1
  17. package/dist/cjs/crypto/Key.js +15 -16
  18. package/dist/cjs/crypto/Key.js.map +1 -1
  19. package/dist/cjs/crypto/Spake2p.js +5 -5
  20. package/dist/cjs/crypto/Spake2p.js.map +1 -1
  21. package/dist/cjs/crypto/StandardCrypto.d.ts +33 -0
  22. package/dist/cjs/crypto/StandardCrypto.d.ts.map +1 -0
  23. package/dist/cjs/crypto/StandardCrypto.js +208 -0
  24. package/dist/cjs/crypto/StandardCrypto.js.map +6 -0
  25. package/dist/cjs/crypto/aes/Aes.d.ts +21 -0
  26. package/dist/cjs/crypto/aes/Aes.d.ts.map +1 -0
  27. package/dist/cjs/crypto/aes/Aes.js +132 -0
  28. package/dist/cjs/crypto/aes/Aes.js.map +6 -0
  29. package/dist/cjs/crypto/aes/Ccm.d.ts +71 -0
  30. package/dist/cjs/crypto/aes/Ccm.d.ts.map +1 -0
  31. package/dist/cjs/crypto/aes/Ccm.js +194 -0
  32. package/dist/cjs/crypto/aes/Ccm.js.map +6 -0
  33. package/dist/cjs/crypto/aes/WordArray.d.ts +30 -0
  34. package/dist/cjs/crypto/aes/WordArray.d.ts.map +1 -0
  35. package/dist/cjs/crypto/aes/WordArray.js +91 -0
  36. package/dist/cjs/crypto/aes/WordArray.js.map +6 -0
  37. package/dist/cjs/crypto/index.d.ts +3 -0
  38. package/dist/cjs/crypto/index.d.ts.map +1 -1
  39. package/dist/cjs/crypto/index.js +3 -0
  40. package/dist/cjs/crypto/index.js.map +1 -1
  41. package/dist/cjs/crypto/nonentropic.d.ts +16 -0
  42. package/dist/cjs/crypto/nonentropic.d.ts.map +1 -0
  43. package/dist/cjs/crypto/nonentropic.js +70 -0
  44. package/dist/cjs/crypto/nonentropic.js.map +6 -0
  45. package/dist/cjs/environment/Environment.d.ts.map +1 -1
  46. package/dist/cjs/environment/Environment.js +1 -5
  47. package/dist/cjs/environment/Environment.js.map +1 -1
  48. package/dist/cjs/environment/RuntimeService.d.ts +2 -4
  49. package/dist/cjs/environment/RuntimeService.d.ts.map +1 -1
  50. package/dist/cjs/environment/RuntimeService.js +4 -4
  51. package/dist/cjs/environment/RuntimeService.js.map +1 -1
  52. package/dist/cjs/environment/VariableService.d.ts.map +1 -1
  53. package/dist/cjs/environment/VariableService.js +1 -0
  54. package/dist/cjs/environment/VariableService.js.map +1 -1
  55. package/dist/cjs/log/LogFormat.js +17 -11
  56. package/dist/cjs/log/LogFormat.js.map +1 -1
  57. package/dist/cjs/net/Network.d.ts +0 -1
  58. package/dist/cjs/net/Network.d.ts.map +1 -1
  59. package/dist/cjs/net/Network.js +0 -4
  60. package/dist/cjs/net/Network.js.map +1 -1
  61. package/dist/cjs/time/Time.d.ts.map +1 -1
  62. package/dist/cjs/time/Time.js +2 -2
  63. package/dist/cjs/time/Time.js.map +1 -1
  64. package/dist/cjs/util/Bytes.d.ts +6 -0
  65. package/dist/cjs/util/Bytes.d.ts.map +1 -1
  66. package/dist/cjs/util/Bytes.js +15 -1
  67. package/dist/cjs/util/Bytes.js.map +1 -1
  68. package/dist/cjs/util/DataWriter.d.ts +1 -1
  69. package/dist/cjs/util/DataWriter.js +2 -2
  70. package/dist/cjs/util/DataWriter.js.map +1 -1
  71. package/dist/cjs/util/DeepCopy.js +1 -1
  72. package/dist/cjs/util/DeepCopy.js.map +1 -1
  73. package/dist/cjs/util/GeneratedClass.d.ts +3 -3
  74. package/dist/cjs/util/GeneratedClass.d.ts.map +1 -1
  75. package/dist/cjs/util/GeneratedClass.js +99 -73
  76. package/dist/cjs/util/GeneratedClass.js.map +2 -2
  77. package/dist/cjs/util/Number.d.ts +0 -1
  78. package/dist/cjs/util/Number.d.ts.map +1 -1
  79. package/dist/cjs/util/Number.js +0 -4
  80. package/dist/cjs/util/Number.js.map +1 -1
  81. package/dist/esm/codec/DerCodec.d.ts +12 -17
  82. package/dist/esm/codec/DerCodec.d.ts.map +1 -1
  83. package/dist/esm/codec/DerCodec.js +90 -51
  84. package/dist/esm/codec/DerCodec.js.map +1 -1
  85. package/dist/esm/codec/DerTypes.js +2 -2
  86. package/dist/esm/codec/DnsCodec.d.ts +5 -5
  87. package/dist/esm/crypto/Crypto.d.ts +111 -62
  88. package/dist/esm/crypto/Crypto.d.ts.map +1 -1
  89. package/dist/esm/crypto/Crypto.js +93 -32
  90. package/dist/esm/crypto/Crypto.js.map +1 -1
  91. package/dist/esm/crypto/CryptoError.d.ts +32 -0
  92. package/dist/esm/crypto/CryptoError.d.ts.map +1 -0
  93. package/dist/esm/crypto/CryptoError.js +24 -0
  94. package/dist/esm/crypto/CryptoError.js.map +6 -0
  95. package/dist/esm/crypto/Key.d.ts +2 -2
  96. package/dist/esm/crypto/Key.d.ts.map +1 -1
  97. package/dist/esm/crypto/Key.js +15 -16
  98. package/dist/esm/crypto/Key.js.map +1 -1
  99. package/dist/esm/crypto/Spake2p.js +5 -5
  100. package/dist/esm/crypto/Spake2p.js.map +1 -1
  101. package/dist/esm/crypto/StandardCrypto.d.ts +33 -0
  102. package/dist/esm/crypto/StandardCrypto.d.ts.map +1 -0
  103. package/dist/esm/crypto/StandardCrypto.js +188 -0
  104. package/dist/esm/crypto/StandardCrypto.js.map +6 -0
  105. package/dist/esm/crypto/aes/Aes.d.ts +21 -0
  106. package/dist/esm/crypto/aes/Aes.d.ts.map +1 -0
  107. package/dist/esm/crypto/aes/Aes.js +112 -0
  108. package/dist/esm/crypto/aes/Aes.js.map +6 -0
  109. package/dist/esm/crypto/aes/Ccm.d.ts +71 -0
  110. package/dist/esm/crypto/aes/Ccm.d.ts.map +1 -0
  111. package/dist/esm/crypto/aes/Ccm.js +174 -0
  112. package/dist/esm/crypto/aes/Ccm.js.map +6 -0
  113. package/dist/esm/crypto/aes/WordArray.d.ts +30 -0
  114. package/dist/esm/crypto/aes/WordArray.d.ts.map +1 -0
  115. package/dist/esm/crypto/aes/WordArray.js +71 -0
  116. package/dist/esm/crypto/aes/WordArray.js.map +6 -0
  117. package/dist/esm/crypto/index.d.ts +3 -0
  118. package/dist/esm/crypto/index.d.ts.map +1 -1
  119. package/dist/esm/crypto/index.js +3 -0
  120. package/dist/esm/crypto/index.js.map +1 -1
  121. package/dist/esm/crypto/nonentropic.d.ts +16 -0
  122. package/dist/esm/crypto/nonentropic.d.ts.map +1 -0
  123. package/dist/esm/crypto/nonentropic.js +50 -0
  124. package/dist/esm/crypto/nonentropic.js.map +6 -0
  125. package/dist/esm/environment/Environment.d.ts.map +1 -1
  126. package/dist/esm/environment/Environment.js +1 -5
  127. package/dist/esm/environment/Environment.js.map +1 -1
  128. package/dist/esm/environment/RuntimeService.d.ts +2 -4
  129. package/dist/esm/environment/RuntimeService.d.ts.map +1 -1
  130. package/dist/esm/environment/RuntimeService.js +4 -4
  131. package/dist/esm/environment/RuntimeService.js.map +1 -1
  132. package/dist/esm/environment/VariableService.d.ts.map +1 -1
  133. package/dist/esm/environment/VariableService.js +1 -0
  134. package/dist/esm/environment/VariableService.js.map +1 -1
  135. package/dist/esm/log/LogFormat.js +17 -11
  136. package/dist/esm/log/LogFormat.js.map +1 -1
  137. package/dist/esm/net/Network.d.ts +0 -1
  138. package/dist/esm/net/Network.d.ts.map +1 -1
  139. package/dist/esm/net/Network.js +1 -5
  140. package/dist/esm/net/Network.js.map +1 -1
  141. package/dist/esm/time/Time.d.ts.map +1 -1
  142. package/dist/esm/time/Time.js +2 -2
  143. package/dist/esm/time/Time.js.map +1 -1
  144. package/dist/esm/util/Bytes.d.ts +6 -0
  145. package/dist/esm/util/Bytes.d.ts.map +1 -1
  146. package/dist/esm/util/Bytes.js +15 -1
  147. package/dist/esm/util/Bytes.js.map +1 -1
  148. package/dist/esm/util/DataWriter.d.ts +1 -1
  149. package/dist/esm/util/DataWriter.js +3 -3
  150. package/dist/esm/util/DataWriter.js.map +1 -1
  151. package/dist/esm/util/DeepCopy.js +1 -1
  152. package/dist/esm/util/DeepCopy.js.map +1 -1
  153. package/dist/esm/util/GeneratedClass.d.ts +3 -3
  154. package/dist/esm/util/GeneratedClass.d.ts.map +1 -1
  155. package/dist/esm/util/GeneratedClass.js +97 -71
  156. package/dist/esm/util/GeneratedClass.js.map +2 -2
  157. package/dist/esm/util/Number.d.ts +0 -1
  158. package/dist/esm/util/Number.d.ts.map +1 -1
  159. package/dist/esm/util/Number.js +0 -4
  160. package/dist/esm/util/Number.js.map +1 -1
  161. package/package.json +3 -3
  162. package/src/codec/DerCodec.ts +106 -52
  163. package/src/codec/DerTypes.ts +2 -2
  164. package/src/crypto/Crypto.ts +196 -76
  165. package/src/crypto/CryptoError.ts +32 -0
  166. package/src/crypto/Key.ts +17 -18
  167. package/src/crypto/Spake2p.ts +5 -5
  168. package/src/crypto/StandardCrypto.ts +252 -0
  169. package/src/crypto/aes/Aes.ts +210 -0
  170. package/src/crypto/aes/Ccm.ts +350 -0
  171. package/src/crypto/aes/README.md +4 -0
  172. package/src/crypto/aes/WordArray.ts +105 -0
  173. package/src/crypto/index.ts +3 -0
  174. package/src/crypto/nonentropic.ts +65 -0
  175. package/src/environment/Environment.ts +1 -6
  176. package/src/environment/RuntimeService.ts +5 -5
  177. package/src/environment/VariableService.ts +1 -0
  178. package/src/log/LogFormat.ts +19 -11
  179. package/src/net/Network.ts +1 -7
  180. package/src/time/Time.ts +4 -4
  181. package/src/util/Bytes.ts +19 -0
  182. package/src/util/DataWriter.ts +3 -3
  183. package/src/util/DeepCopy.ts +2 -2
  184. package/src/util/GeneratedClass.ts +161 -102
  185. package/src/util/Number.ts +0 -4
package/src/crypto/Key.ts CHANGED
@@ -9,14 +9,13 @@ import { DerCodec, DerNode, DerType } from "../codec/DerCodec.js";
9
9
  import { MatterError, NotImplementedError } from "../MatterError.js";
10
10
  import { Bytes } from "../util/Bytes.js";
11
11
  import { ec } from "./Crypto.js";
12
+ import { KeyInputError } from "./CryptoError.js";
12
13
 
13
14
  const {
14
15
  numberToBytesBE,
15
16
  p256: { ProjectivePoint },
16
17
  } = ec;
17
18
 
18
- class KeyError extends MatterError {}
19
-
20
19
  const JWK_KEYS = [
21
20
  "crv",
22
21
  "d",
@@ -228,7 +227,7 @@ function checkDerVersion(type: string, node: DerNode | undefined, version: numbe
228
227
  node && node._tag === DerType.Integer && node._bytes && node._bytes.length === 1 && node._bytes[0];
229
228
 
230
229
  if (derVersion !== version) {
231
- throw new KeyError(`${type} key version mismatch`);
230
+ throw new KeyInputError(`${type} key version mismatch`);
232
231
  }
233
232
  }
234
233
 
@@ -237,14 +236,14 @@ function getDerObjectID(type: string, node?: DerNode) {
237
236
 
238
237
  if (id) return id;
239
238
 
240
- throw new KeyError(`Missing object in ${type} key`);
239
+ throw new KeyInputError(`Missing object in ${type} key`);
241
240
  }
242
241
 
243
242
  function getDerCurve(type: string, node?: DerNode) {
244
243
  const oid = getDerObjectID(type, node);
245
244
  const curve = (<any>CurveLookup)[Bytes.toHex(oid)];
246
245
  if (curve) return curve;
247
- throw new KeyError(`Unsupported ${type} EC curve`);
246
+ throw new KeyInputError(`Unsupported ${type} EC curve`);
248
247
  }
249
248
 
250
249
  function getDerKey(type: string, node?: DerNode, derType: DerType = DerType.OctetString) {
@@ -297,7 +296,7 @@ namespace Translators {
297
296
  const algorithmElements = outer?._elements?.[1]?._elements;
298
297
  const algorithm = getDerObjectID("PKCS #8", algorithmElements?.[0]);
299
298
  if (Bytes.toHex(algorithm) !== Asn1ObjectID.ecPublicKey) {
300
- throw new KeyError("Unsupported PKCS #8 decryption algorithm");
299
+ throw new KeyInputError("Unsupported PKCS #8 decryption algorithm");
301
300
  }
302
301
 
303
302
  // Curve
@@ -306,7 +305,7 @@ namespace Translators {
306
305
  // Private key
307
306
  const innerBytes = outer?._elements?.[2]._bytes;
308
307
  if (innerBytes === undefined || innerBytes === null) {
309
- throw new KeyError("Invalid PKCS #8 key");
308
+ throw new KeyInputError("Invalid PKCS #8 key");
310
309
  }
311
310
  const inner = DerCodec.decode(innerBytes);
312
311
  const key = getDerKey("PKCS #8", inner?._elements?.[1]);
@@ -331,7 +330,7 @@ namespace Translators {
331
330
  // Algorithm
332
331
  const algorithm = getDerObjectID("SPKI", algorithmElements?.[0]);
333
332
  if (Bytes.toHex(algorithm) !== Asn1ObjectID.ecPublicKey) {
334
- throw new KeyError("Unsupported SPKI decryption algorithm");
333
+ throw new KeyInputError("Unsupported SPKI decryption algorithm");
335
334
  }
336
335
 
337
336
  // Curve
@@ -354,19 +353,19 @@ namespace Translators {
354
353
  export const publicBits = {
355
354
  set: function (this: Key, input: Uint8Array) {
356
355
  if (!(input.length % 2)) {
357
- throw new KeyError("Invalid public key encoding");
356
+ throw new KeyInputError("Invalid public key encoding");
358
357
  }
359
358
 
360
359
  switch (input[0]) {
361
360
  case 2:
362
361
  case 3:
363
- throw new KeyError("Unsupported public key compression");
362
+ throw new KeyInputError("Unsupported public key compression");
364
363
 
365
364
  case 4:
366
365
  break;
367
366
 
368
367
  case 5:
369
- throw new KeyError("Illegal public key format specifier");
368
+ throw new KeyInputError("Illegal public key format specifier");
370
369
  }
371
370
 
372
371
  const coordinateLength = (input.length - 1) / 2;
@@ -446,7 +445,7 @@ function inferCurve(key: Key, bytes: number) {
446
445
  break;
447
446
 
448
447
  default:
449
- throw new KeyError(`Cannot infer named curve from key length ${bytes}`);
448
+ throw new KeyInputError(`Cannot infer named curve from key length ${bytes}`);
450
449
  }
451
450
  }
452
451
  }
@@ -502,7 +501,7 @@ export function Key(properties: Partial<Key>) {
502
501
  get: () => {
503
502
  const result = that[target];
504
503
  if (result === undefined) {
505
- throw new KeyError(`Key field ${target} is not defined`);
504
+ throw new KeyInputError(`Key field ${target} is not defined`);
506
505
  }
507
506
  return result;
508
507
  },
@@ -517,8 +516,8 @@ export function Key(properties: Partial<Key>) {
517
516
 
518
517
  /** Compute public point from private EC key */
519
518
  function derivePublicFromPrivate() {
520
- if (that.type !== KeyType.EC) throw new KeyError("EC key type required to compute public point");
521
- if (!that.private) throw new KeyError("EC private key required to compute public point");
519
+ if (that.type !== KeyType.EC) throw new KeyInputError("EC key type required to compute public point");
520
+ if (!that.private) throw new KeyInputError("EC private key required to compute public point");
522
521
 
523
522
  const crv = that.crv;
524
523
  let keyLength: number;
@@ -532,7 +531,7 @@ export function Key(properties: Partial<Key>) {
532
531
  break;
533
532
 
534
533
  default:
535
- throw new KeyError(`Unsupported elliptic curve ${crv}`);
534
+ throw new KeyInputError(`Unsupported elliptic curve ${crv}`);
536
535
  }
537
536
 
538
537
  // Compute
@@ -559,7 +558,7 @@ export function Key(properties: Partial<Key>) {
559
558
  }
560
559
 
561
560
  /**
562
- * Private key factory.
561
+ * EC private key factory.
563
562
  */
564
563
  export function PrivateKey(privateKey: Uint8Array | BinaryKeyPair, options?: Partial<Key>) {
565
564
  let priv, pub;
@@ -578,7 +577,7 @@ export function PrivateKey(privateKey: Uint8Array | BinaryKeyPair, options?: Par
578
577
  }
579
578
 
580
579
  /**
581
- * Public key factory.
580
+ * EC public key factory.
582
581
  */
583
582
  export function PublicKey(publicKey: Uint8Array, options?: Partial<Key>) {
584
583
  return Key({
@@ -32,7 +32,7 @@ export class Spake2p {
32
32
  static async computeW0W1({ iterations, salt }: PbkdfParameters, pin: number) {
33
33
  const pinWriter = new DataWriter(Endian.Little);
34
34
  pinWriter.writeUInt32(pin);
35
- const ws = await Crypto.pbkdf2(pinWriter.toByteArray(), salt, iterations, CRYPTO_W_SIZE_BYTES * 2);
35
+ const ws = await Crypto.createPbkdf2Key(pinWriter.toByteArray(), salt, iterations, CRYPTO_W_SIZE_BYTES * 2);
36
36
  const w0 = mod(bytesToNumberBE(ws.slice(0, 40)), P256_CURVE.n);
37
37
  const w1 = mod(bytesToNumberBE(ws.slice(40, 80)), P256_CURVE.n);
38
38
  return { w0, w1 };
@@ -96,12 +96,12 @@ export class Spake2p {
96
96
  const Ka = TT_HASH.slice(0, 16);
97
97
  const Ke = TT_HASH.slice(16, 32);
98
98
 
99
- const KcAB = await Crypto.hkdf(Ka, new Uint8Array(0), Bytes.fromString("ConfirmationKeys"), 32);
99
+ const KcAB = await Crypto.createHkdfKey(Ka, new Uint8Array(0), Bytes.fromString("ConfirmationKeys"), 32);
100
100
  const KcA = KcAB.slice(0, 16);
101
101
  const KcB = KcAB.slice(16, 32);
102
102
 
103
- const hAY = await Crypto.hmac(KcA, Y);
104
- const hBX = await Crypto.hmac(KcB, X);
103
+ const hAY = await Crypto.signHmac(KcA, Y);
104
+ const hBX = await Crypto.signHmac(KcB, X);
105
105
 
106
106
  return { Ke, hAY, hBX };
107
107
  }
@@ -118,7 +118,7 @@ export class Spake2p {
118
118
  this.addToContext(TTwriter, Z);
119
119
  this.addToContext(TTwriter, V);
120
120
  this.addToContext(TTwriter, numberToBytesBE(this.w0, 32));
121
- return Crypto.hash(TTwriter.toByteArray());
121
+ return Crypto.computeSha256(TTwriter.toByteArray());
122
122
  }
123
123
 
124
124
  private addToContext(TTwriter: DataWriter<Endian.Little>, data: Uint8Array) {
@@ -0,0 +1,252 @@
1
+ /**
2
+ * @license
3
+ *
4
+ * Portions copyright 2022-2023 Project CHIP Authors
5
+ * SPDX-License-Identifier: Apache-2.0
6
+ */
7
+
8
+ import { DerBigUint, DerCodec, DerError } from "#codec/DerCodec.js";
9
+ import { Boot } from "#util/Boot.js";
10
+ import { Bytes } from "#util/Bytes.js";
11
+ import { Ccm } from "./aes/Ccm.js";
12
+ import { Crypto, CRYPTO_SYMMETRIC_KEY_LENGTH, CryptoDsaEncoding } from "./Crypto.js";
13
+ import { CryptoVerifyError, KeyInputError } from "./CryptoError.js";
14
+ import { CurveType, Key, KeyType, PrivateKey, PublicKey } from "./Key.js";
15
+
16
+ const subtle = globalThis.crypto.subtle;
17
+
18
+ const SIGNATURE_ALGORITHM = <EcdsaParams>{
19
+ name: "ECDSA",
20
+ namedCurve: "P-256",
21
+ hash: { name: "SHA-256" },
22
+ };
23
+
24
+ /**
25
+ * A {@link Crypto} implementation based on standard "crypto.subtle" with missing portions implemented using JS.
26
+ *
27
+ * WARNING: This code is unaudited. Use a trusted native alternative where available.
28
+ *
29
+ * This module is mostly based on {@link crypto.subtle}. This should be a reliable native implementation. However,
30
+ * Web Crypto doesn't support AES-CCM required by Matter so fall back to a JS implementation for that. See relevant
31
+ * warnings in the "aes" subdirectory.
32
+ */
33
+ export class StandardCrypto implements Crypto {
34
+ implementationName = "JS";
35
+
36
+ static provider() {
37
+ return new StandardCrypto();
38
+ }
39
+
40
+ getRandomData(length: number): Uint8Array {
41
+ const result = new Uint8Array(length);
42
+ crypto.getRandomValues(result);
43
+ return result;
44
+ }
45
+
46
+ encrypt(key: Uint8Array, data: Uint8Array, nonce: Uint8Array, associatedData?: Uint8Array) {
47
+ const ccm = Ccm(key);
48
+ return ccm.encrypt({ pt: data, nonce, adata: associatedData });
49
+ }
50
+
51
+ decrypt(key: Uint8Array, data: Uint8Array, nonce: Uint8Array, associatedData?: Uint8Array) {
52
+ const ccm = Ccm(key);
53
+ return ccm.decrypt({ ct: data, nonce, adata: associatedData });
54
+ }
55
+
56
+ async computeSha256(buffer: Uint8Array | Uint8Array[]) {
57
+ if (Array.isArray(buffer)) {
58
+ buffer = Bytes.concat(...buffer);
59
+ }
60
+ return new Uint8Array(await subtle.digest("SHA-256", buffer));
61
+ }
62
+
63
+ async createPbkdf2Key(secret: Uint8Array, salt: Uint8Array, iteration: number, keyLength: number) {
64
+ const key = await importKey("raw", secret, "PBKDF2", false, ["deriveBits"]);
65
+ const bits = await subtle.deriveBits(
66
+ {
67
+ name: "PBKDF2",
68
+ hash: "SHA-256",
69
+ salt: salt,
70
+ iterations: iteration,
71
+ },
72
+ key,
73
+ keyLength * 8,
74
+ );
75
+ return new Uint8Array(bits);
76
+ }
77
+
78
+ async createHkdfKey(
79
+ secret: Uint8Array,
80
+ salt: Uint8Array,
81
+ info: Uint8Array,
82
+ length: number = CRYPTO_SYMMETRIC_KEY_LENGTH,
83
+ ) {
84
+ const key = await importKey("raw", secret, "HKDF", false, ["deriveBits"]);
85
+ const bits = await subtle.deriveBits(
86
+ {
87
+ name: "HKDF",
88
+ hash: "SHA-256",
89
+ salt: salt,
90
+ info: info,
91
+ },
92
+ key,
93
+ 8 * length,
94
+ );
95
+ return new Uint8Array(bits);
96
+ }
97
+
98
+ async signHmac(secret: Uint8Array, data: Uint8Array) {
99
+ const key = await importKey("raw", secret, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
100
+ return new Uint8Array(await subtle.sign("HMAC", key, data));
101
+ }
102
+
103
+ async signEcdsa(key: JsonWebKey, data: Uint8Array | Uint8Array[], dsaEncoding?: CryptoDsaEncoding) {
104
+ if (Array.isArray(data)) {
105
+ data = Bytes.concat(...data);
106
+ }
107
+
108
+ const { crv, kty, d, x, y } = key;
109
+
110
+ key = {
111
+ kty,
112
+ crv,
113
+ d,
114
+ x,
115
+ y,
116
+ ext: true, // Required by some subtle implementations to sign
117
+ key_ops: ["sign"],
118
+ };
119
+
120
+ const subtleKey = await importKey("jwk", key, SIGNATURE_ALGORITHM, false, ["sign"]);
121
+
122
+ const ieeeP1363 = await subtle.sign(SIGNATURE_ALGORITHM, subtleKey, data);
123
+
124
+ if (dsaEncoding !== "der") return new Uint8Array(ieeeP1363);
125
+
126
+ const bytesPerComponent = ieeeP1363.byteLength / 2;
127
+
128
+ return DerCodec.encode({
129
+ r: DerBigUint(ieeeP1363.slice(0, bytesPerComponent)),
130
+ s: DerBigUint(ieeeP1363.slice(bytesPerComponent)),
131
+ });
132
+ }
133
+
134
+ async verifyEcdsa(key: JsonWebKey, data: Uint8Array, signature: Uint8Array, dsaEncoding?: CryptoDsaEncoding) {
135
+ const { crv, kty, x, y } = key;
136
+ key = { crv, kty, x, y };
137
+ const subtleKey = await importKey("jwk", key, SIGNATURE_ALGORITHM, false, ["verify"]);
138
+
139
+ if (dsaEncoding === "der") {
140
+ try {
141
+ const decoded = DerCodec.decode(signature);
142
+
143
+ const r = DerCodec.decodeBigUint(decoded?._elements?.[0], 32);
144
+ const s = DerCodec.decodeBigUint(decoded?._elements?.[1], 32);
145
+
146
+ signature = Bytes.concat(r, s);
147
+ } catch (cause) {
148
+ DerError.accept(cause);
149
+
150
+ throw new CryptoVerifyError("Invalid DER signature", { cause });
151
+ }
152
+ }
153
+
154
+ const verified = await subtle.verify(SIGNATURE_ALGORITHM, subtleKey, signature, data);
155
+
156
+ if (!verified) {
157
+ throw new CryptoVerifyError("Signature verification failed");
158
+ }
159
+ }
160
+
161
+ async createKeyPair() {
162
+ const subtleKey = await subtle.generateKey(
163
+ {
164
+ // We must specify either ECDH or ECDSA to get an EC key but we may use the key for either (but not for
165
+ // both)
166
+ name: "ECDH",
167
+ namedCurve: "P-256",
168
+ },
169
+ true,
170
+
171
+ // We must also specify usage but will drop this on export
172
+ ["deriveKey"],
173
+ );
174
+
175
+ // Do not export as JWK because we do not want to inherit the algorithm and key_ops
176
+ const key = await subtle.exportKey("jwk", subtleKey.privateKey);
177
+
178
+ // Extract only private and public fields; we do not want key_ops
179
+ return Key({
180
+ kty: KeyType.EC,
181
+ crv: CurveType.p256,
182
+ d: key.d,
183
+ x: key.x,
184
+ y: key.y,
185
+ }) as PrivateKey;
186
+ }
187
+
188
+ async generateDhSecret(key: PrivateKey, peerKey: PublicKey) {
189
+ const subtleKey = await importKey(
190
+ "jwk",
191
+ key,
192
+ {
193
+ name: "ECDH",
194
+ namedCurve: "P-256",
195
+ },
196
+ false,
197
+ ["deriveBits"],
198
+ );
199
+
200
+ const subtlePeerKey = await importKey(
201
+ "jwk",
202
+ peerKey,
203
+ {
204
+ name: "ECDH",
205
+ namedCurve: "P-256",
206
+ },
207
+ false,
208
+ [],
209
+ );
210
+
211
+ const secret = await subtle.deriveBits(
212
+ {
213
+ name: "ECDH",
214
+ public: subtlePeerKey,
215
+ },
216
+ subtleKey,
217
+ 256,
218
+ );
219
+
220
+ return new Uint8Array(secret);
221
+ }
222
+ }
223
+
224
+ // Only install if VM supports Web Crypto
225
+ if ((globalThis.crypto as any)?.subtle?.[Symbol.toStringTag] === "SubtleCrypto") {
226
+ Boot.init(() => {
227
+ Crypto.provider = StandardCrypto.provider;
228
+ });
229
+ }
230
+
231
+ function importKey(
232
+ format: "jwk",
233
+ keyData: JsonWebKey,
234
+ algorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm,
235
+ extractable: boolean,
236
+ keyUsages: ReadonlyArray<KeyUsage>,
237
+ ): Promise<CryptoKey>;
238
+ function importKey(
239
+ format: Exclude<KeyFormat, "jwk">,
240
+ keyData: BufferSource,
241
+ algorithm: AlgorithmIdentifier | RsaHashedImportParams | EcKeyImportParams | HmacImportParams | AesKeyAlgorithm,
242
+ extractable: boolean,
243
+ keyUsages: KeyUsage[],
244
+ ): Promise<CryptoKey>;
245
+
246
+ async function importKey(...params: unknown[]) {
247
+ try {
248
+ return await crypto.subtle.importKey(...(params as Parameters<SubtleCrypto["importKey"]>));
249
+ } catch (cause) {
250
+ throw new KeyInputError("Invalid key", { cause });
251
+ }
252
+ }
@@ -0,0 +1,210 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Project CHIP Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * SJCL: https://github.com/bitwiseshiftleft/sjcl/blob/master/core/aes.js
9
+ *
10
+ * OpenSSL: https://github.com/openssl/openssl/blob/master/crypto/aes/aes_core.c
11
+ */
12
+
13
+ import { WordArray } from "./WordArray.js";
14
+
15
+ /**
16
+ * AES core block cipher implementation.
17
+ *
18
+ * WARNING: Unaudited. Consider platform replacement if available.
19
+ */
20
+ export function Aes(key: Uint8Array) {
21
+ const { encryptKey, decryptKey } = expandKey(key);
22
+
23
+ return {
24
+ encrypt(pt: WordArray, ct = pt) {
25
+ return crypt(pt, ct, encryptKey, Tables.enc);
26
+ },
27
+
28
+ decrypt(ct: WordArray, pt = ct) {
29
+ return crypt(ct, pt, decryptKey, Tables.dec);
30
+ },
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Precomputed tables for AES algorithm.
36
+ */
37
+ interface Tables {
38
+ mix1: WordArray;
39
+ mix2: WordArray;
40
+ mix3: WordArray;
41
+ mix4: WordArray;
42
+ sbox: WordArray;
43
+ }
44
+
45
+ let etabs: Tables | undefined, dtabs: Tables | undefined;
46
+
47
+ const Tables = {
48
+ get enc() {
49
+ if (!etabs) {
50
+ generateTables();
51
+ }
52
+ return etabs!;
53
+ },
54
+
55
+ get dec() {
56
+ if (!dtabs) {
57
+ generateTables();
58
+ }
59
+ return dtabs!;
60
+ },
61
+ };
62
+
63
+ const mixNames = ["mix1", "mix2", "mix3", "mix4"] as const;
64
+
65
+ /**
66
+ * Generate tables used by {@link crypt}.
67
+ *
68
+ * These tables hold static data used during encryption and decryption.
69
+ */
70
+ function generateTables() {
71
+ etabs = Tables();
72
+ dtabs = Tables();
73
+
74
+ const d = Table(),
75
+ th = Table();
76
+ let i: number, x: number, xInv: number, x2: number, x4: number, x8: number, s: number, tEnc: number, tDec: number;
77
+
78
+ for (i = 0; i < 256; i++) {
79
+ th[(d[i] = (i << 1) ^ ((i >> 7) * 283)) ^ i] = i;
80
+ }
81
+
82
+ for (x = xInv = 0; !etabs.sbox[x]; x ^= x2 || 1, xInv = th[xInv] || 1) {
83
+ s = xInv ^ (xInv << 1) ^ (xInv << 2) ^ (xInv << 3) ^ (xInv << 4);
84
+ s = (s >> 8) ^ (s & 255) ^ 99;
85
+ etabs.sbox[x] = s;
86
+ dtabs.sbox[s] = x;
87
+
88
+ x8 = d[(x4 = d[(x2 = d[x])])];
89
+ tDec = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100);
90
+ tEnc = (d[s] * 0x101) ^ (s * 0x1010100);
91
+
92
+ for (const name of mixNames) {
93
+ etabs[name][x] = tEnc = (tEnc << 24) ^ (tEnc >>> 8);
94
+ dtabs[name][s] = tDec = (tDec << 24) ^ (tDec >>> 8);
95
+ }
96
+ }
97
+
98
+ function Tables() {
99
+ return Object.fromEntries([...mixNames, "sbox"].map(k => [k, Table()])) as unknown as Tables;
100
+ }
101
+
102
+ function Table() {
103
+ return WordArray(256);
104
+ }
105
+ }
106
+
107
+ /**
108
+ * AES computation.
109
+ *
110
+ * Performs encryption/decryption of one or more blocks in {@link data}.
111
+ *
112
+ * {@link input} and {@link output} may be the same buffer to operate in place.
113
+ */
114
+ function crypt(input: WordArray, output: WordArray, roundKeys: WordArray, tabs: Tables) {
115
+ const decrypt = tabs === dtabs,
116
+ numRounds = roundKeys.length / 4 - 2;
117
+
118
+ // These are the precomputed tables generated in generateTables
119
+ const { mix1, mix2, mix3, mix4, sbox } = tabs;
120
+
121
+ // Allocate working variables for each word in the block
122
+ let a = input[0] ^ roundKeys[0],
123
+ b = input[decrypt ? 3 : 1] ^ roundKeys[1],
124
+ c = input[2] ^ roundKeys[2],
125
+ d = input[decrypt ? 1 : 3] ^ roundKeys[3],
126
+ roundKeyAt = 4;
127
+
128
+ // Perform computation (sub bytes, shift rows, mix columns & add round key) for each round
129
+ for (let i = 0; i < numRounds; i++) {
130
+ // Note that we need to use unsigned bit shift (>>>) for the most significant byte because JS integers are
131
+ // 32-bit unsigned and the sign will otherwise mess us up
132
+ const atemp =
133
+ mix1[a >>> 24] ^ mix2[(b >> 16) & 255] ^ mix3[(c >> 8) & 255] ^ mix4[d & 255] ^ roundKeys[roundKeyAt++];
134
+ const btemp =
135
+ mix1[b >>> 24] ^ mix2[(c >> 16) & 255] ^ mix3[(d >> 8) & 255] ^ mix4[a & 255] ^ roundKeys[roundKeyAt++];
136
+ const ctemp =
137
+ mix1[c >>> 24] ^ mix2[(d >> 16) & 255] ^ mix3[(a >> 8) & 255] ^ mix4[b & 255] ^ roundKeys[roundKeyAt++];
138
+ d = mix1[d >>> 24] ^ mix2[(a >> 16) & 255] ^ mix3[(b >> 8) & 255] ^ mix4[c & 255] ^ roundKeys[roundKeyAt++];
139
+ a = atemp;
140
+ b = btemp;
141
+ c = ctemp;
142
+ }
143
+
144
+ // Write working blocks into output buffer
145
+ for (let i = 0; i < 4; i++) {
146
+ output[decrypt ? 3 & -i : i] =
147
+ (sbox[a >>> 24] << 24) ^
148
+ (sbox[(b >> 16) & 255] << 16) ^
149
+ (sbox[(c >> 8) & 255] << 8) ^
150
+ sbox[d & 255] ^
151
+ roundKeys[roundKeyAt++];
152
+ const atemp = a;
153
+ a = b;
154
+ b = c;
155
+ c = d;
156
+ d = atemp;
157
+ }
158
+
159
+ return output;
160
+ }
161
+
162
+ /**
163
+ * Schedule the key used for encryption.
164
+ *
165
+ * This generates keys for each round based off of the input key.
166
+ */
167
+ function expandKey(key: Uint8Array) {
168
+ const inputLength = key.length / 4,
169
+ roundsNeeded = inputLength + 7,
170
+ wordsNeeded = roundsNeeded * 4,
171
+ encryptKey = WordArray.fromByteArray(key, wordsNeeded),
172
+ sbox = Tables.enc.sbox;
173
+
174
+ for (let i = inputLength, rcon = 1; i < wordsNeeded; i++) {
175
+ let temp = encryptKey[i - 1];
176
+
177
+ if (i % inputLength === 0 || (inputLength === 8 && i % inputLength === 4)) {
178
+ temp =
179
+ (sbox[temp >>> 24] << 24) ^
180
+ (sbox[(temp >> 16) & 255] << 16) ^
181
+ (sbox[(temp >> 8) & 255] << 8) ^
182
+ sbox[temp & 255];
183
+
184
+ if (i % inputLength === 0) {
185
+ temp = (temp << 8) ^ (temp >>> 24) ^ (rcon << 24);
186
+ rcon = (rcon << 1) ^ ((rcon >> 7) * 283);
187
+ }
188
+ }
189
+
190
+ encryptKey[i] = encryptKey[i - inputLength] ^ temp;
191
+ }
192
+
193
+ const { mix1, mix2, mix3, mix4 } = Tables.dec,
194
+ decryptKey = WordArray(encryptKey.length);
195
+
196
+ for (let i = encryptKey.length, j = 0; i; j++, i--) {
197
+ const tmp = encryptKey[j & 3 ? i : i - 4];
198
+ if (i <= 4 || j < 4) {
199
+ decryptKey[j] = tmp;
200
+ } else {
201
+ decryptKey[j] =
202
+ mix1[sbox[tmp >>> 24]] ^
203
+ mix2[sbox[(tmp >> 16) & 255]] ^
204
+ mix3[sbox[(tmp >> 8) & 255]] ^
205
+ mix4[sbox[tmp & 255]];
206
+ }
207
+ }
208
+
209
+ return { encryptKey, decryptKey };
210
+ }