@matter/general 0.14.1-alpha.0-20250606-a9bcd03f9 → 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
@@ -54,7 +54,7 @@ export const DerObject = (objectId: string, content: any = {}) => ({
54
54
  [DerKey.ObjectId]: ObjectId(objectId),
55
55
  ...content,
56
56
  });
57
- export const BitByteArray = (data: Uint8Array, padding = 0) => ({
57
+ export const DerBitString = (data: Uint8Array, padding = 0) => ({
58
58
  [DerKey.TagId]: DerType.BitString as number,
59
59
  [DerKey.Bytes]: data,
60
60
  [DerKey.BitsPadding]: padding,
@@ -74,6 +74,23 @@ export const DatatypeOverride = (type: DerType, value: any) => ({
74
74
  export const RawBytes = (bytes: Uint8Array) => ({
75
75
  [DerKey.Bytes]: bytes,
76
76
  });
77
+ export const DerBigUint = (number: Uint8Array | ArrayBuffer) => {
78
+ // We don't need bigint support currently, but we can translate here if we ever do
79
+
80
+ if (!ArrayBuffer.isView(number)) {
81
+ number = new Uint8Array(number);
82
+ }
83
+
84
+ // Ensure value does not encode as negative
85
+ if ((number as Uint8Array)[0] & 0x80) {
86
+ number = Bytes.concat(new Uint8Array([0]), number as Uint8Array);
87
+ }
88
+
89
+ return {
90
+ [DerKey.TagId]: DerType.Integer,
91
+ [DerKey.Bytes]: number,
92
+ };
93
+ };
77
94
 
78
95
  export type DerNode = {
79
96
  [DerKey.TagId]: number;
@@ -83,19 +100,19 @@ export type DerNode = {
83
100
  };
84
101
 
85
102
  export class DerCodec {
86
- static encode(value: any): Uint8Array {
103
+ static encode(value: unknown): Uint8Array {
87
104
  if (Array.isArray(value)) {
88
- return this.encodeArray(value);
105
+ return this.#encodeArray(value);
89
106
  } else if (value instanceof Uint8Array) {
90
- return this.encodeOctetString(value);
107
+ return this.#encodeOctetString(value);
91
108
  } else if (value instanceof Date) {
92
- return this.encodeDate(value);
109
+ return this.#encodeDate(value);
93
110
  } else if (typeof value === "string") {
94
- return this.encodeString(value);
111
+ return this.#encodeString(value);
95
112
  } else if (typeof value === "number" || typeof value === "bigint") {
96
- return this.encodeInteger(value);
113
+ return this.#encodeInteger(value);
97
114
  } else if (typeof value === "boolean") {
98
- return this.encodeBoolean(value);
115
+ return this.#encodeBoolean(value);
99
116
  } else if (value === undefined) {
100
117
  return new Uint8Array(0);
101
118
  } else if (isObject(value)) {
@@ -110,7 +127,7 @@ export class DerCodec {
110
127
  if (bytes === undefined || !ArrayBuffer.isView(bytes)) {
111
128
  throw new DerError("DER bytes is not a byte array");
112
129
  }
113
- return this.encodeAsn1(
130
+ return this.#encodeAsn1(
114
131
  tagId,
115
132
  bitsPadding === undefined
116
133
  ? (bytes as Uint8Array)
@@ -118,24 +135,24 @@ export class DerCodec {
118
135
  );
119
136
  } else if (value[DerKey.TypeOverride] !== undefined && value[DerKey.RawData] !== undefined) {
120
137
  if (value[DerKey.TypeOverride] === DerType.Integer && value[DerKey.RawData] instanceof Uint8Array) {
121
- return this.encodeInteger(value[DerKey.RawData]);
138
+ return this.#encodeInteger(value[DerKey.RawData]);
122
139
  } else if (
123
140
  value[DerKey.TypeOverride] === DerType.BitString &&
124
141
  typeof value[DerKey.RawData] === "number"
125
142
  ) {
126
- return this.encodeBitString(value[DerKey.RawData]);
143
+ return this.#encodeBitString(value[DerKey.RawData]);
127
144
  } else if (
128
145
  value[DerKey.TypeOverride] === DerType.PrintableString &&
129
146
  typeof value[DerKey.RawData] === "string"
130
147
  ) {
131
- return this.encodePrintableString(value[DerKey.RawData]);
148
+ return this.#encodePrintableString(value[DerKey.RawData]);
132
149
  } else if (
133
150
  value[DerKey.TypeOverride] === DerType.IA5String &&
134
151
  typeof value[DerKey.RawData] === "string"
135
152
  ) {
136
- return this.encodeIA5String(value[DerKey.RawData]);
153
+ return this.#encodeIA5String(value[DerKey.RawData]);
137
154
  } else {
138
- throw new UnexpectedDataError(`Unsupported override type ${value[DerKey.TypeOverride]}`);
155
+ throw new DerError(`Unsupported override type ${value[DerKey.TypeOverride]}`);
139
156
  }
140
157
  } else if (
141
158
  value[DerKey.Bytes] !== undefined &&
@@ -145,19 +162,59 @@ export class DerCodec {
145
162
  // Raw Data
146
163
  return value[DerKey.Bytes];
147
164
  } else if (value[DerKey.TypeOverride] === undefined && value[DerKey.Bytes] === undefined) {
148
- return this.encodeObject(value);
165
+ return this.#encodeObject(value);
149
166
  } else {
150
- throw new UnexpectedDataError(`Unsupported object type ${typeof value}`);
167
+ throw new DerError(`Unsupported object type ${typeof value}`);
151
168
  }
152
169
  } else {
153
- throw new UnexpectedDataError(`Unsupported type ${typeof value}`);
170
+ throw new DerError(`Unsupported type ${typeof value}`);
171
+ }
172
+ }
173
+
174
+ static decode(data: Uint8Array): DerNode {
175
+ return this.#decodeRec(new DataReader(data));
176
+ }
177
+
178
+ /**
179
+ * Extract a large integer value to a byte array with a specific number of bytes.
180
+ */
181
+ static decodeBigUint(value: DerNode | undefined, byteLength: number) {
182
+ if (value === undefined) {
183
+ throw new DerError("Missing number in DER object");
184
+ }
185
+
186
+ if (value[DerKey.TagId] !== DerType.Integer) {
187
+ throw new DerError(`Expected integer but DER tag is ${DerType[value[DerKey.TagId]]}`);
154
188
  }
189
+
190
+ const bytes = value[DerKey.Bytes];
191
+ if (!ArrayBuffer.isView(bytes)) {
192
+ throw new DerError("Incorrect DER object type");
193
+ }
194
+
195
+ // The common case
196
+ if (bytes.length === byteLength) {
197
+ return bytes;
198
+ }
199
+
200
+ // Handle case where single "0" prefix ensures correct sign
201
+ if (bytes.length === byteLength + 1 && !bytes[0]) {
202
+ return bytes.slice(1);
203
+ }
204
+
205
+ // Pad out as necessary
206
+ if (bytes.length < byteLength) {
207
+ return Bytes.concat(new Uint8Array(byteLength - bytes.length), bytes);
208
+ }
209
+
210
+ // Invalid
211
+ throw new DerError("Encoded integer contains too many bytes");
155
212
  }
156
213
 
157
- private static encodeDate(date: Date) {
214
+ static #encodeDate(date: Date) {
158
215
  if (date.getFullYear() > 2049) {
159
216
  // Dates 2050+ are encoded as GeneralizedTime. This includes the special Non Well Defined date 9999-12-31.
160
- return this.encodeAsn1(
217
+ return this.#encodeAsn1(
161
218
  DerType.GeneralizedTime,
162
219
  Bytes.fromString(
163
220
  date
@@ -166,8 +223,8 @@ export class DerCodec {
166
223
  .slice(0, 14) + "Z",
167
224
  ),
168
225
  );
169
- } else
170
- return this.encodeAsn1(
226
+ } else {
227
+ return this.#encodeAsn1(
171
228
  DerType.UtcDate,
172
229
  Bytes.fromString(
173
230
  date
@@ -176,48 +233,49 @@ export class DerCodec {
176
233
  .slice(2, 14) + "Z",
177
234
  ),
178
235
  );
236
+ }
179
237
  }
180
238
 
181
- private static encodeBoolean(bool: boolean) {
182
- return this.encodeAsn1(DerType.Boolean, Uint8Array.of(bool ? 0xff : 0));
239
+ static #encodeBoolean(bool: boolean) {
240
+ return this.#encodeAsn1(DerType.Boolean, Uint8Array.of(bool ? 0xff : 0));
183
241
  }
184
242
 
185
- private static encodeArray(array: Array<any>) {
186
- return this.encodeAsn1(DerType.Set | CONSTRUCTED, Bytes.concat(...array.map(element => this.encode(element))));
243
+ static #encodeArray(array: Array<any>) {
244
+ return this.#encodeAsn1(DerType.Set | CONSTRUCTED, Bytes.concat(...array.map(element => this.encode(element))));
187
245
  }
188
246
 
189
- private static encodeOctetString(value: Uint8Array) {
190
- return this.encodeAsn1(DerType.OctetString, value);
247
+ static #encodeOctetString(value: Uint8Array) {
248
+ return this.#encodeAsn1(DerType.OctetString, value);
191
249
  }
192
250
 
193
- private static encodeObject(object: any) {
251
+ static #encodeObject(object: any) {
194
252
  const attributes = new Array<Uint8Array>();
195
253
  for (const key in object) {
196
254
  attributes.push(this.encode(object[key]));
197
255
  }
198
- return this.encodeAsn1(DerType.Sequence | CONSTRUCTED, Bytes.concat(...attributes));
256
+ return this.#encodeAsn1(DerType.Sequence | CONSTRUCTED, Bytes.concat(...attributes));
199
257
  }
200
258
 
201
- private static encodeString(value: string) {
202
- return this.encodeAsn1(DerType.UTF8String, Bytes.fromString(value));
259
+ static #encodeString(value: string) {
260
+ return this.#encodeAsn1(DerType.UTF8String, Bytes.fromString(value));
203
261
  }
204
262
 
205
- private static encodePrintableString(value: string) {
263
+ static #encodePrintableString(value: string) {
206
264
  if (!/^[a-z0-9 '()+,\-./:=?]*$/i.test(value)) {
207
- throw new UnexpectedDataError(`String ${value} is not a printable string.`);
265
+ throw new DerError(`String ${value} is not a printable string.`);
208
266
  }
209
- return this.encodeAsn1(DerType.PrintableString, Bytes.fromString(value));
267
+ return this.#encodeAsn1(DerType.PrintableString, Bytes.fromString(value));
210
268
  }
211
269
 
212
- private static encodeIA5String(value: string) {
270
+ static #encodeIA5String(value: string) {
213
271
  /*eslint-disable-next-line no-control-regex */
214
272
  if (!/^[\x00-\x7F]*$/.test(value)) {
215
- throw new UnexpectedDataError(`String ${value} is not an IA5 string.`);
273
+ throw new DerError(`String ${value} is not an IA5 string.`);
216
274
  }
217
- return this.encodeAsn1(DerType.IA5String, Bytes.fromString(value));
275
+ return this.#encodeAsn1(DerType.IA5String, Bytes.fromString(value));
218
276
  }
219
277
 
220
- private static encodeInteger(value: number | bigint | Uint8Array) {
278
+ static #encodeInteger(value: number | bigint | Uint8Array) {
221
279
  const isByteArray = ArrayBuffer.isView(value);
222
280
  let valueBytes: Uint8Array;
223
281
  if (isByteArray) {
@@ -234,17 +292,17 @@ export class DerCodec {
234
292
  start++;
235
293
  if (start === byteArray.length - 1) break;
236
294
  }
237
- return this.encodeAsn1(DerType.Integer, byteArray.slice(start));
295
+ return this.#encodeAsn1(DerType.Integer, byteArray.slice(start));
238
296
  }
239
297
 
240
- private static encodeBitString(value: number) {
298
+ static #encodeBitString(value: number) {
241
299
  const reversedBits = value.toString(2).padStart(8, "0");
242
300
  const unusedBits = reversedBits.indexOf("1");
243
301
  const bitByteArray = Uint8Array.of(parseInt(reversedBits.split("").reverse().join(""), 2));
244
- return this.encode(BitByteArray(bitByteArray, unusedBits === -1 ? 8 : unusedBits));
302
+ return this.encode(DerBitString(bitByteArray, unusedBits === -1 ? 8 : unusedBits));
245
303
  }
246
304
 
247
- private static encodeLengthBytes(value: number) {
305
+ static #encodeLengthBytes(value: number) {
248
306
  const byteArray = new Uint8Array(5);
249
307
  const dataView = Bytes.dataViewOf(byteArray);
250
308
  dataView.setUint32(1, value);
@@ -262,28 +320,24 @@ export class DerCodec {
262
320
  return byteArray.slice(start);
263
321
  }
264
322
 
265
- private static encodeAsn1(tag: number, data: Uint8Array) {
266
- return Bytes.concat(Uint8Array.of(tag), this.encodeLengthBytes(data.length), data);
267
- }
268
-
269
- static decode(data: Uint8Array): DerNode {
270
- return this.decodeRec(new DataReader(data));
323
+ static #encodeAsn1(tag: number, data: Uint8Array) {
324
+ return Bytes.concat(Uint8Array.of(tag), this.#encodeLengthBytes(data.length), data);
271
325
  }
272
326
 
273
- private static decodeRec(reader: DataReader): DerNode {
274
- const { tag, bytes } = this.decodeAsn1(reader);
327
+ static #decodeRec(reader: DataReader): DerNode {
328
+ const { tag, bytes } = this.#decodeAsn1(reader);
275
329
  if (tag === DerType.BitString)
276
330
  return { [DerKey.TagId]: tag, [DerKey.Bytes]: bytes.slice(1), [DerKey.BitsPadding]: bytes[0] };
277
331
  if ((tag & CONSTRUCTED) === 0) return { [DerKey.TagId]: tag, [DerKey.Bytes]: bytes };
278
332
  const elementsReader = new DataReader(bytes);
279
333
  const elements: DerNode[] = [];
280
334
  while (elementsReader.remainingBytesCount > 0) {
281
- elements.push(this.decodeRec(elementsReader));
335
+ elements.push(this.#decodeRec(elementsReader));
282
336
  }
283
337
  return { [DerKey.TagId]: tag, [DerKey.Bytes]: bytes, [DerKey.Elements]: elements };
284
338
  }
285
339
 
286
- private static decodeAsn1(reader: DataReader): { tag: number; bytes: Uint8Array } {
340
+ static #decodeAsn1(reader: DataReader): { tag: number; bytes: Uint8Array } {
287
341
  const tag = reader.readUInt8();
288
342
  let length = reader.readUInt8();
289
343
  if ((length & 0x80) !== 0) {
@@ -5,10 +5,10 @@
5
5
  */
6
6
 
7
7
  import {
8
- BitByteArray,
9
8
  ContextTagged,
10
9
  ContextTaggedBytes,
11
10
  DatatypeOverride,
11
+ DerBitString,
12
12
  DerCodec,
13
13
  DerError,
14
14
  DerObject,
@@ -22,7 +22,7 @@ export namespace X962 {
22
22
  algorithm: ObjectId("2A8648CE3D0201") /* EC Public Key */,
23
23
  curve: ObjectId("2A8648CE3D030107") /* Curve P256_V1 */,
24
24
  },
25
- bytes: BitByteArray(key),
25
+ bytes: DerBitString(key),
26
26
  });
27
27
  export const EcdsaWithSHA256 = DerObject("2A8648CE3D040302");
28
28
  }
@@ -4,15 +4,17 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ import { Diagnostic } from "#log/Diagnostic.js";
8
+ import { Logger } from "#log/Logger.js";
7
9
  import { Boot } from "#util/Boot.js";
8
10
  import { MaybePromise } from "#util/Promises.js";
9
11
  import * as mod from "@noble/curves/abstract/modular";
10
12
  import * as utils from "@noble/curves/abstract/utils";
11
13
  import { p256 } from "@noble/curves/p256";
12
- import { MatterError, NoProviderError } from "../MatterError.js";
14
+ import { NoProviderError } from "../MatterError.js";
13
15
  import { Endian } from "../util/Bytes.js";
14
16
  import { DataReader } from "../util/DataReader.js";
15
- import { PrivateKey } from "./Key.js";
17
+ import { PrivateKey, PublicKey } from "./Key.js";
16
18
 
17
19
  export const ec = {
18
20
  p256,
@@ -29,110 +31,228 @@ export const CRYPTO_AUTH_TAG_LENGTH = 16;
29
31
  export const CRYPTO_SYMMETRIC_KEY_LENGTH = 16;
30
32
  export type CryptoDsaEncoding = "ieee-p1363" | "der";
31
33
 
32
- export class CryptoVerifyError extends MatterError {}
33
- export class CryptoDecryptError extends MatterError {}
34
+ const logger = Logger.get("Crypto");
34
35
 
35
- export abstract class Crypto {
36
- static get: () => Crypto;
37
-
38
- abstract encrypt(key: Uint8Array, data: Uint8Array, nonce: Uint8Array, aad?: Uint8Array): Uint8Array;
39
- static readonly encrypt = (key: Uint8Array, data: Uint8Array, nonce: Uint8Array, aad?: Uint8Array): Uint8Array =>
40
- Crypto.get().encrypt(key, data, nonce, aad);
41
-
42
- abstract decrypt(key: Uint8Array, data: Uint8Array, nonce: Uint8Array, aad?: Uint8Array): Uint8Array;
43
- static readonly decrypt = (key: Uint8Array, data: Uint8Array, nonce: Uint8Array, aad?: Uint8Array): Uint8Array =>
44
- Crypto.get().decrypt(key, data, nonce, aad);
45
-
46
- abstract getRandomData(length: number): Uint8Array;
47
- static readonly getRandomData = (length: number): Uint8Array => Crypto.get().getRandomData(length);
48
-
49
- static readonly getRandom = (): Uint8Array => Crypto.get().getRandomData(CRYPTO_RANDOM_LENGTH);
50
-
51
- static readonly getRandomUInt16 = (): number =>
52
- new DataReader(Crypto.get().getRandomData(2), Endian.Little).readUInt16();
53
-
54
- static readonly getRandomUInt32 = (): number =>
55
- new DataReader(Crypto.get().getRandomData(4), Endian.Little).readUInt32();
56
-
57
- static readonly getRandomBigUInt64 = (): bigint =>
58
- new DataReader(Crypto.get().getRandomData(8), Endian.Little).readUInt64();
59
-
60
- static readonly getRandomBigInt = (size: number, maxValue?: bigint): bigint => {
61
- const { bytesToNumberBE } = ec;
62
- if (maxValue === undefined) {
63
- return bytesToNumberBE(Crypto.getRandomData(size));
64
- }
65
- while (true) {
66
- const random = bytesToNumberBE(Crypto.getRandomData(size));
67
- if (random < maxValue) return random;
68
- }
69
- };
36
+ /**
37
+ * These are the cryptographic primitives required to implement the Matter protocol.
38
+ *
39
+ * We provide a platform-independent implementation that uses Web Crypto via {@link crypto.subtle} and a JS-based
40
+ * AES-CCM implementation.
41
+ *
42
+ * If your platform does not fully implement Web Crypto, or offers a native implementation of AES-CCM, you can replace
43
+ * {@link Crypto.get} to expose a different implementation.
44
+ *
45
+ * WARNING: The standard implementation is unaudited. See relevant warnings in StandardCrypto.ts.
46
+ */
47
+ export interface Crypto {
48
+ /**
49
+ * The name used in log messages.
50
+ */
51
+ implementationName: string;
70
52
 
71
- abstract ecdhGeneratePublicKey(): MaybePromise<{ publicKey: Uint8Array; ecdh: any }>;
72
- static readonly ecdhGeneratePublicKey = () => Crypto.get().ecdhGeneratePublicKey();
53
+ /**
54
+ * Encrypt using AES-CCM with constants limited to those required by Matter.
55
+ */
56
+ encrypt(key: Uint8Array, data: Uint8Array, nonce: Uint8Array, aad?: Uint8Array): Uint8Array;
73
57
 
74
- abstract ecdhGeneratePublicKeyAndSecret(peerPublicKey: Uint8Array): MaybePromise<{
75
- publicKey: Uint8Array;
76
- sharedSecret: Uint8Array;
77
- }>;
78
- static readonly ecdhGeneratePublicKeyAndSecret = (peerPublicKey: Uint8Array) =>
79
- Crypto.get().ecdhGeneratePublicKeyAndSecret(peerPublicKey);
58
+ /**
59
+ * Decrypt using AES-CCM with constants limited to those required by Matter.
60
+ */
61
+ decrypt(key: Uint8Array, data: Uint8Array, nonce: Uint8Array, aad?: Uint8Array): Uint8Array;
80
62
 
81
- abstract ecdhGenerateSecret(peerPublicKey: Uint8Array, ecdh: any): MaybePromise<Uint8Array>;
82
- static readonly ecdhGenerateSecret = (peerPublicKey: Uint8Array, ecdh: any) =>
83
- Crypto.get().ecdhGenerateSecret(peerPublicKey, ecdh);
63
+ /**
64
+ * Obtain random bytes from the most cryptographically-appropriate source available.
65
+ */
66
+ getRandomData(length: number): Uint8Array;
84
67
 
85
- abstract hash(data: Uint8Array | Uint8Array[]): MaybePromise<Uint8Array>;
86
- static readonly hash = (data: Uint8Array | Uint8Array[]) => Crypto.get().hash(data);
68
+ /**
69
+ * Compute the SHA-256 hash of a buffer.
70
+ */
71
+ computeSha256(data: Uint8Array | Uint8Array[]): MaybePromise<Uint8Array>;
87
72
 
88
- abstract pbkdf2(
73
+ /**
74
+ * Create a key from a secret using PBKDF2.
75
+ */
76
+ createPbkdf2Key(
89
77
  secret: Uint8Array,
90
78
  salt: Uint8Array,
91
79
  iteration: number,
92
80
  keyLength: number,
93
81
  ): MaybePromise<Uint8Array>;
94
- static readonly pbkdf2 = (secret: Uint8Array, salt: Uint8Array, iteration: number, keyLength: number) =>
95
- Crypto.get().pbkdf2(secret, salt, iteration, keyLength);
96
82
 
97
- abstract hkdf(secret: Uint8Array, salt: Uint8Array, info: Uint8Array, length?: number): MaybePromise<Uint8Array>;
98
- static readonly hkdf = (secret: Uint8Array, salt: Uint8Array, info: Uint8Array, length?: number) =>
99
- Crypto.get().hkdf(secret, salt, info, length);
83
+ /**
84
+ * Create a key from a secret using HKDF.
85
+ */
86
+ createHkdfKey(secret: Uint8Array, salt: Uint8Array, info: Uint8Array, length?: number): MaybePromise<Uint8Array>;
100
87
 
101
- abstract hmac(key: Uint8Array, data: Uint8Array): MaybePromise<Uint8Array>;
102
- static readonly hmac = (key: Uint8Array, data: Uint8Array) => Crypto.get().hmac(key, data);
88
+ /**
89
+ * Create an HMAC signature.
90
+ */
91
+ signHmac(key: Uint8Array, data: Uint8Array): MaybePromise<Uint8Array>;
103
92
 
104
- abstract sign(
93
+ /**
94
+ * Create an ECDSA signature.
95
+ */
96
+ signEcdsa(
105
97
  privateKey: JsonWebKey,
106
98
  data: Uint8Array | Uint8Array[],
107
99
  dsaEncoding?: CryptoDsaEncoding,
108
100
  ): MaybePromise<Uint8Array>;
109
- static readonly sign = (privateKey: JsonWebKey, data: Uint8Array | Uint8Array[], dsaEncoding?: CryptoDsaEncoding) =>
110
- Crypto.get().sign(privateKey, data, dsaEncoding);
111
101
 
112
- abstract verify(
102
+ /**
103
+ * Authenticate an ECDSA signature.
104
+ */
105
+ verifyEcdsa(
113
106
  publicKey: JsonWebKey,
114
107
  data: Uint8Array,
115
108
  signature: Uint8Array,
116
109
  dsaEncoding?: CryptoDsaEncoding,
117
110
  ): MaybePromise<void>;
118
- static readonly verify = (
119
- publicKey: JsonWebKey,
120
- data: Uint8Array,
121
- signature: Uint8Array,
122
- dsaEncoding?: CryptoDsaEncoding,
123
- ) => Crypto.get().verify(publicKey, data, signature, dsaEncoding);
124
111
 
125
- abstract createKeyPair(): MaybePromise<PrivateKey>;
126
- static readonly createKeyPair = () => Crypto.get().createKeyPair();
112
+ /**
113
+ * Create a general-purpose EC key.
114
+ */
115
+ createKeyPair(): MaybePromise<PrivateKey>;
116
+
117
+ /**
118
+ * Compute the shared secret for a Diffie-Hellman exchange.
119
+ */
120
+ generateDhSecret(key: PrivateKey, peerKey: PublicKey): MaybePromise<Uint8Array>;
127
121
  }
128
122
 
123
+ let logImplementationName = true;
124
+ let defaultInstance: undefined | Crypto;
125
+ let defaultProvider: undefined | (() => Crypto);
126
+
127
+ /**
128
+ * Crypto support functions.
129
+ */
130
+ export const Crypto = {
131
+ /**
132
+ * The default crypto implementation.
133
+ */
134
+ get default() {
135
+ if (defaultInstance) {
136
+ return defaultInstance;
137
+ }
138
+
139
+ if (defaultProvider === undefined) {
140
+ throw new NoProviderError("There is no cryptography implementation installed");
141
+ }
142
+
143
+ defaultInstance = defaultProvider();
144
+
145
+ if (logImplementationName) {
146
+ logger.debug("Using", Diagnostic.strong(defaultInstance.implementationName), "cryptography implementation");
147
+ }
148
+
149
+ return defaultInstance;
150
+ },
151
+
152
+ get provider(): undefined | (() => Crypto) {
153
+ return defaultProvider;
154
+ },
155
+
156
+ /**
157
+ * Set the default crypto provider.
158
+ */
159
+ set provider(provider: () => Crypto) {
160
+ if (defaultProvider === provider) {
161
+ return;
162
+ }
163
+ defaultProvider = undefined;
164
+ defaultProvider = provider;
165
+ },
166
+
167
+ get implementationName() {
168
+ return Crypto.default.implementationName;
169
+ },
170
+
171
+ encrypt(key: Uint8Array, data: Uint8Array, nonce: Uint8Array, aad?: Uint8Array) {
172
+ return Crypto.default.encrypt(key, data, nonce, aad);
173
+ },
174
+
175
+ decrypt(key: Uint8Array, data: Uint8Array, nonce: Uint8Array, aad?: Uint8Array) {
176
+ return Crypto.default.decrypt(key, data, nonce, aad);
177
+ },
178
+
179
+ getRandomData(length: number) {
180
+ return Crypto.default.getRandomData(length);
181
+ },
182
+
183
+ getRandom() {
184
+ return Crypto.default.getRandomData(CRYPTO_RANDOM_LENGTH);
185
+ },
186
+
187
+ getRandomUInt16() {
188
+ return new DataReader(Crypto.default.getRandomData(2), Endian.Little).readUInt16();
189
+ },
190
+
191
+ getRandomUInt32() {
192
+ return new DataReader(Crypto.default.getRandomData(4), Endian.Little).readUInt32();
193
+ },
194
+
195
+ getRandomBigUInt64() {
196
+ return new DataReader(Crypto.default.getRandomData(8), Endian.Little).readUInt64();
197
+ },
198
+
199
+ getRandomBigInt(size: number, maxValue?: bigint) {
200
+ const { bytesToNumberBE } = ec;
201
+ if (maxValue === undefined) {
202
+ return bytesToNumberBE(Crypto.getRandomData(size));
203
+ }
204
+ while (true) {
205
+ const random = bytesToNumberBE(Crypto.getRandomData(size));
206
+ if (random < maxValue) return random;
207
+ }
208
+ },
209
+
210
+ computeSha256(data: Uint8Array | Uint8Array[]) {
211
+ return Crypto.default.computeSha256(data);
212
+ },
213
+
214
+ createPbkdf2Key(secret: Uint8Array, salt: Uint8Array, iteration: number, keyLength: number) {
215
+ return Crypto.default.createPbkdf2Key(secret, salt, iteration, keyLength);
216
+ },
217
+
218
+ createHkdfKey(secret: Uint8Array, salt: Uint8Array, info: Uint8Array, length?: number) {
219
+ return Crypto.default.createHkdfKey(secret, salt, info, length);
220
+ },
221
+
222
+ signHmac(key: Uint8Array, data: Uint8Array) {
223
+ return Crypto.default.signHmac(key, data);
224
+ },
225
+
226
+ signEcdsa(privateKey: JsonWebKey, data: Uint8Array | Uint8Array[], dsaEncoding?: CryptoDsaEncoding) {
227
+ return Crypto.default.signEcdsa(privateKey, data, dsaEncoding);
228
+ },
229
+
230
+ verifyEcdsa(publicKey: JsonWebKey, data: Uint8Array, signature: Uint8Array, dsaEncoding?: CryptoDsaEncoding) {
231
+ return Crypto.default.verifyEcdsa(publicKey, data, signature, dsaEncoding);
232
+ },
233
+
234
+ createKeyPair() {
235
+ return Crypto.default.createKeyPair();
236
+ },
237
+
238
+ generateDhSecret(key: PrivateKey, peerKey: PublicKey) {
239
+ return Crypto.default.generateDhSecret(key, peerKey);
240
+ },
241
+ };
242
+
243
+ Crypto satisfies Crypto;
244
+
129
245
  Boot.init(() => {
130
- Crypto.get = () => {
131
- throw new NoProviderError("No provider configured");
132
- };
246
+ logImplementationName = true;
247
+ defaultInstance = undefined;
248
+ defaultProvider = undefined;
133
249
 
134
- // Hook for testing frameworks
250
+ // Testing framework configuration
135
251
  if (typeof MatterHooks !== "undefined") {
252
+ // Crypto access occurs before log messages are intercepted so do not log implementation in test environment
253
+ logImplementationName = true;
254
+
255
+ // Configure mocking
136
256
  MatterHooks.cryptoSetup?.(Crypto);
137
257
  }
138
258
  });
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2022-2025 Project CHIP Authors
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ import { MatterError } from "#MatterError.js";
8
+
9
+ /**
10
+ * Errors thrown by crypto implementations.
11
+ */
12
+ export class CryptoError extends MatterError {}
13
+
14
+ /**
15
+ * Thrown when a crypto algorithm encounters invalid input.
16
+ */
17
+ export class CryptoInputError extends MatterError {}
18
+
19
+ /**
20
+ * Thrown when verification fails.
21
+ */
22
+ export class CryptoVerifyError extends CryptoError {}
23
+
24
+ /**
25
+ * Thrown when decryption fails.
26
+ */
27
+ export class CryptoDecryptError extends CryptoError {}
28
+
29
+ /**
30
+ * Thrown when cryptographic key parameters are invalid.
31
+ */
32
+ export class KeyInputError extends CryptoInputError {}