@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.
- package/dist/cjs/codec/DerCodec.d.ts +12 -17
- package/dist/cjs/codec/DerCodec.d.ts.map +1 -1
- package/dist/cjs/codec/DerCodec.js +90 -51
- package/dist/cjs/codec/DerCodec.js.map +1 -1
- package/dist/cjs/codec/DerTypes.js +1 -1
- package/dist/cjs/codec/DnsCodec.d.ts +5 -5
- package/dist/cjs/crypto/Crypto.d.ts +111 -62
- package/dist/cjs/crypto/Crypto.d.ts.map +1 -1
- package/dist/cjs/crypto/Crypto.js +92 -31
- package/dist/cjs/crypto/Crypto.js.map +1 -1
- package/dist/cjs/crypto/CryptoError.d.ts +32 -0
- package/dist/cjs/crypto/CryptoError.d.ts.map +1 -0
- package/dist/cjs/crypto/CryptoError.js +44 -0
- package/dist/cjs/crypto/CryptoError.js.map +6 -0
- package/dist/cjs/crypto/Key.d.ts +2 -2
- package/dist/cjs/crypto/Key.d.ts.map +1 -1
- package/dist/cjs/crypto/Key.js +15 -16
- package/dist/cjs/crypto/Key.js.map +1 -1
- package/dist/cjs/crypto/Spake2p.js +5 -5
- package/dist/cjs/crypto/Spake2p.js.map +1 -1
- package/dist/cjs/crypto/StandardCrypto.d.ts +33 -0
- package/dist/cjs/crypto/StandardCrypto.d.ts.map +1 -0
- package/dist/cjs/crypto/StandardCrypto.js +208 -0
- package/dist/cjs/crypto/StandardCrypto.js.map +6 -0
- package/dist/cjs/crypto/aes/Aes.d.ts +21 -0
- package/dist/cjs/crypto/aes/Aes.d.ts.map +1 -0
- package/dist/cjs/crypto/aes/Aes.js +132 -0
- package/dist/cjs/crypto/aes/Aes.js.map +6 -0
- package/dist/cjs/crypto/aes/Ccm.d.ts +71 -0
- package/dist/cjs/crypto/aes/Ccm.d.ts.map +1 -0
- package/dist/cjs/crypto/aes/Ccm.js +194 -0
- package/dist/cjs/crypto/aes/Ccm.js.map +6 -0
- package/dist/cjs/crypto/aes/WordArray.d.ts +30 -0
- package/dist/cjs/crypto/aes/WordArray.d.ts.map +1 -0
- package/dist/cjs/crypto/aes/WordArray.js +91 -0
- package/dist/cjs/crypto/aes/WordArray.js.map +6 -0
- package/dist/cjs/crypto/index.d.ts +3 -0
- package/dist/cjs/crypto/index.d.ts.map +1 -1
- package/dist/cjs/crypto/index.js +3 -0
- package/dist/cjs/crypto/index.js.map +1 -1
- package/dist/cjs/crypto/nonentropic.d.ts +16 -0
- package/dist/cjs/crypto/nonentropic.d.ts.map +1 -0
- package/dist/cjs/crypto/nonentropic.js +70 -0
- package/dist/cjs/crypto/nonentropic.js.map +6 -0
- package/dist/cjs/environment/Environment.d.ts.map +1 -1
- package/dist/cjs/environment/Environment.js +1 -5
- package/dist/cjs/environment/Environment.js.map +1 -1
- package/dist/cjs/environment/RuntimeService.d.ts +2 -4
- package/dist/cjs/environment/RuntimeService.d.ts.map +1 -1
- package/dist/cjs/environment/RuntimeService.js +4 -4
- package/dist/cjs/environment/RuntimeService.js.map +1 -1
- package/dist/cjs/environment/VariableService.d.ts.map +1 -1
- package/dist/cjs/environment/VariableService.js +1 -0
- package/dist/cjs/environment/VariableService.js.map +1 -1
- package/dist/cjs/log/LogFormat.js +17 -11
- package/dist/cjs/log/LogFormat.js.map +1 -1
- package/dist/cjs/net/Network.d.ts +0 -1
- package/dist/cjs/net/Network.d.ts.map +1 -1
- package/dist/cjs/net/Network.js +0 -4
- package/dist/cjs/net/Network.js.map +1 -1
- package/dist/cjs/time/Time.d.ts.map +1 -1
- package/dist/cjs/time/Time.js +2 -2
- package/dist/cjs/time/Time.js.map +1 -1
- package/dist/cjs/util/Bytes.d.ts +6 -0
- package/dist/cjs/util/Bytes.d.ts.map +1 -1
- package/dist/cjs/util/Bytes.js +15 -1
- package/dist/cjs/util/Bytes.js.map +1 -1
- package/dist/cjs/util/DataWriter.d.ts +1 -1
- package/dist/cjs/util/DataWriter.js +2 -2
- package/dist/cjs/util/DataWriter.js.map +1 -1
- package/dist/cjs/util/DeepCopy.js +1 -1
- package/dist/cjs/util/DeepCopy.js.map +1 -1
- package/dist/cjs/util/GeneratedClass.d.ts +3 -3
- package/dist/cjs/util/GeneratedClass.d.ts.map +1 -1
- package/dist/cjs/util/GeneratedClass.js +99 -73
- package/dist/cjs/util/GeneratedClass.js.map +2 -2
- package/dist/cjs/util/Number.d.ts +0 -1
- package/dist/cjs/util/Number.d.ts.map +1 -1
- package/dist/cjs/util/Number.js +0 -4
- package/dist/cjs/util/Number.js.map +1 -1
- package/dist/esm/codec/DerCodec.d.ts +12 -17
- package/dist/esm/codec/DerCodec.d.ts.map +1 -1
- package/dist/esm/codec/DerCodec.js +90 -51
- package/dist/esm/codec/DerCodec.js.map +1 -1
- package/dist/esm/codec/DerTypes.js +2 -2
- package/dist/esm/codec/DnsCodec.d.ts +5 -5
- package/dist/esm/crypto/Crypto.d.ts +111 -62
- package/dist/esm/crypto/Crypto.d.ts.map +1 -1
- package/dist/esm/crypto/Crypto.js +93 -32
- package/dist/esm/crypto/Crypto.js.map +1 -1
- package/dist/esm/crypto/CryptoError.d.ts +32 -0
- package/dist/esm/crypto/CryptoError.d.ts.map +1 -0
- package/dist/esm/crypto/CryptoError.js +24 -0
- package/dist/esm/crypto/CryptoError.js.map +6 -0
- package/dist/esm/crypto/Key.d.ts +2 -2
- package/dist/esm/crypto/Key.d.ts.map +1 -1
- package/dist/esm/crypto/Key.js +15 -16
- package/dist/esm/crypto/Key.js.map +1 -1
- package/dist/esm/crypto/Spake2p.js +5 -5
- package/dist/esm/crypto/Spake2p.js.map +1 -1
- package/dist/esm/crypto/StandardCrypto.d.ts +33 -0
- package/dist/esm/crypto/StandardCrypto.d.ts.map +1 -0
- package/dist/esm/crypto/StandardCrypto.js +188 -0
- package/dist/esm/crypto/StandardCrypto.js.map +6 -0
- package/dist/esm/crypto/aes/Aes.d.ts +21 -0
- package/dist/esm/crypto/aes/Aes.d.ts.map +1 -0
- package/dist/esm/crypto/aes/Aes.js +112 -0
- package/dist/esm/crypto/aes/Aes.js.map +6 -0
- package/dist/esm/crypto/aes/Ccm.d.ts +71 -0
- package/dist/esm/crypto/aes/Ccm.d.ts.map +1 -0
- package/dist/esm/crypto/aes/Ccm.js +174 -0
- package/dist/esm/crypto/aes/Ccm.js.map +6 -0
- package/dist/esm/crypto/aes/WordArray.d.ts +30 -0
- package/dist/esm/crypto/aes/WordArray.d.ts.map +1 -0
- package/dist/esm/crypto/aes/WordArray.js +71 -0
- package/dist/esm/crypto/aes/WordArray.js.map +6 -0
- package/dist/esm/crypto/index.d.ts +3 -0
- package/dist/esm/crypto/index.d.ts.map +1 -1
- package/dist/esm/crypto/index.js +3 -0
- package/dist/esm/crypto/index.js.map +1 -1
- package/dist/esm/crypto/nonentropic.d.ts +16 -0
- package/dist/esm/crypto/nonentropic.d.ts.map +1 -0
- package/dist/esm/crypto/nonentropic.js +50 -0
- package/dist/esm/crypto/nonentropic.js.map +6 -0
- package/dist/esm/environment/Environment.d.ts.map +1 -1
- package/dist/esm/environment/Environment.js +1 -5
- package/dist/esm/environment/Environment.js.map +1 -1
- package/dist/esm/environment/RuntimeService.d.ts +2 -4
- package/dist/esm/environment/RuntimeService.d.ts.map +1 -1
- package/dist/esm/environment/RuntimeService.js +4 -4
- package/dist/esm/environment/RuntimeService.js.map +1 -1
- package/dist/esm/environment/VariableService.d.ts.map +1 -1
- package/dist/esm/environment/VariableService.js +1 -0
- package/dist/esm/environment/VariableService.js.map +1 -1
- package/dist/esm/log/LogFormat.js +17 -11
- package/dist/esm/log/LogFormat.js.map +1 -1
- package/dist/esm/net/Network.d.ts +0 -1
- package/dist/esm/net/Network.d.ts.map +1 -1
- package/dist/esm/net/Network.js +1 -5
- package/dist/esm/net/Network.js.map +1 -1
- package/dist/esm/time/Time.d.ts.map +1 -1
- package/dist/esm/time/Time.js +2 -2
- package/dist/esm/time/Time.js.map +1 -1
- package/dist/esm/util/Bytes.d.ts +6 -0
- package/dist/esm/util/Bytes.d.ts.map +1 -1
- package/dist/esm/util/Bytes.js +15 -1
- package/dist/esm/util/Bytes.js.map +1 -1
- package/dist/esm/util/DataWriter.d.ts +1 -1
- package/dist/esm/util/DataWriter.js +3 -3
- package/dist/esm/util/DataWriter.js.map +1 -1
- package/dist/esm/util/DeepCopy.js +1 -1
- package/dist/esm/util/DeepCopy.js.map +1 -1
- package/dist/esm/util/GeneratedClass.d.ts +3 -3
- package/dist/esm/util/GeneratedClass.d.ts.map +1 -1
- package/dist/esm/util/GeneratedClass.js +97 -71
- package/dist/esm/util/GeneratedClass.js.map +2 -2
- package/dist/esm/util/Number.d.ts +0 -1
- package/dist/esm/util/Number.d.ts.map +1 -1
- package/dist/esm/util/Number.js +0 -4
- package/dist/esm/util/Number.js.map +1 -1
- package/package.json +3 -3
- package/src/codec/DerCodec.ts +106 -52
- package/src/codec/DerTypes.ts +2 -2
- package/src/crypto/Crypto.ts +196 -76
- package/src/crypto/CryptoError.ts +32 -0
- package/src/crypto/Key.ts +17 -18
- package/src/crypto/Spake2p.ts +5 -5
- package/src/crypto/StandardCrypto.ts +252 -0
- package/src/crypto/aes/Aes.ts +210 -0
- package/src/crypto/aes/Ccm.ts +350 -0
- package/src/crypto/aes/README.md +4 -0
- package/src/crypto/aes/WordArray.ts +105 -0
- package/src/crypto/index.ts +3 -0
- package/src/crypto/nonentropic.ts +65 -0
- package/src/environment/Environment.ts +1 -6
- package/src/environment/RuntimeService.ts +5 -5
- package/src/environment/VariableService.ts +1 -0
- package/src/log/LogFormat.ts +19 -11
- package/src/net/Network.ts +1 -7
- package/src/time/Time.ts +4 -4
- package/src/util/Bytes.ts +19 -0
- package/src/util/DataWriter.ts +3 -3
- package/src/util/DeepCopy.ts +2 -2
- package/src/util/GeneratedClass.ts +161 -102
- package/src/util/Number.ts +0 -4
package/src/codec/DerCodec.ts
CHANGED
|
@@ -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
|
|
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:
|
|
103
|
+
static encode(value: unknown): Uint8Array {
|
|
87
104
|
if (Array.isArray(value)) {
|
|
88
|
-
return this
|
|
105
|
+
return this.#encodeArray(value);
|
|
89
106
|
} else if (value instanceof Uint8Array) {
|
|
90
|
-
return this
|
|
107
|
+
return this.#encodeOctetString(value);
|
|
91
108
|
} else if (value instanceof Date) {
|
|
92
|
-
return this
|
|
109
|
+
return this.#encodeDate(value);
|
|
93
110
|
} else if (typeof value === "string") {
|
|
94
|
-
return this
|
|
111
|
+
return this.#encodeString(value);
|
|
95
112
|
} else if (typeof value === "number" || typeof value === "bigint") {
|
|
96
|
-
return this
|
|
113
|
+
return this.#encodeInteger(value);
|
|
97
114
|
} else if (typeof value === "boolean") {
|
|
98
|
-
return this
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
153
|
+
return this.#encodeIA5String(value[DerKey.RawData]);
|
|
137
154
|
} else {
|
|
138
|
-
throw new
|
|
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
|
|
165
|
+
return this.#encodeObject(value);
|
|
149
166
|
} else {
|
|
150
|
-
throw new
|
|
167
|
+
throw new DerError(`Unsupported object type ${typeof value}`);
|
|
151
168
|
}
|
|
152
169
|
} else {
|
|
153
|
-
throw new
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
182
|
-
return this
|
|
239
|
+
static #encodeBoolean(bool: boolean) {
|
|
240
|
+
return this.#encodeAsn1(DerType.Boolean, Uint8Array.of(bool ? 0xff : 0));
|
|
183
241
|
}
|
|
184
242
|
|
|
185
|
-
|
|
186
|
-
return this
|
|
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
|
-
|
|
190
|
-
return this
|
|
247
|
+
static #encodeOctetString(value: Uint8Array) {
|
|
248
|
+
return this.#encodeAsn1(DerType.OctetString, value);
|
|
191
249
|
}
|
|
192
250
|
|
|
193
|
-
|
|
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
|
|
256
|
+
return this.#encodeAsn1(DerType.Sequence | CONSTRUCTED, Bytes.concat(...attributes));
|
|
199
257
|
}
|
|
200
258
|
|
|
201
|
-
|
|
202
|
-
return this
|
|
259
|
+
static #encodeString(value: string) {
|
|
260
|
+
return this.#encodeAsn1(DerType.UTF8String, Bytes.fromString(value));
|
|
203
261
|
}
|
|
204
262
|
|
|
205
|
-
|
|
263
|
+
static #encodePrintableString(value: string) {
|
|
206
264
|
if (!/^[a-z0-9 '()+,\-./:=?]*$/i.test(value)) {
|
|
207
|
-
throw new
|
|
265
|
+
throw new DerError(`String ${value} is not a printable string.`);
|
|
208
266
|
}
|
|
209
|
-
return this
|
|
267
|
+
return this.#encodeAsn1(DerType.PrintableString, Bytes.fromString(value));
|
|
210
268
|
}
|
|
211
269
|
|
|
212
|
-
|
|
270
|
+
static #encodeIA5String(value: string) {
|
|
213
271
|
/*eslint-disable-next-line no-control-regex */
|
|
214
272
|
if (!/^[\x00-\x7F]*$/.test(value)) {
|
|
215
|
-
throw new
|
|
273
|
+
throw new DerError(`String ${value} is not an IA5 string.`);
|
|
216
274
|
}
|
|
217
|
-
return this
|
|
275
|
+
return this.#encodeAsn1(DerType.IA5String, Bytes.fromString(value));
|
|
218
276
|
}
|
|
219
277
|
|
|
220
|
-
|
|
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
|
|
295
|
+
return this.#encodeAsn1(DerType.Integer, byteArray.slice(start));
|
|
238
296
|
}
|
|
239
297
|
|
|
240
|
-
|
|
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(
|
|
302
|
+
return this.encode(DerBitString(bitByteArray, unusedBits === -1 ? 8 : unusedBits));
|
|
245
303
|
}
|
|
246
304
|
|
|
247
|
-
|
|
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
|
-
|
|
266
|
-
return Bytes.concat(Uint8Array.of(tag), this
|
|
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
|
-
|
|
274
|
-
const { tag, bytes } = this
|
|
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
|
|
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
|
-
|
|
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) {
|
package/src/codec/DerTypes.ts
CHANGED
|
@@ -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:
|
|
25
|
+
bytes: DerBitString(key),
|
|
26
26
|
});
|
|
27
27
|
export const EcdsaWithSHA256 = DerObject("2A8648CE3D040302");
|
|
28
28
|
}
|
package/src/crypto/Crypto.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
33
|
-
export class CryptoDecryptError extends MatterError {}
|
|
34
|
+
const logger = Logger.get("Crypto");
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Obtain random bytes from the most cryptographically-appropriate source available.
|
|
65
|
+
*/
|
|
66
|
+
getRandomData(length: number): Uint8Array;
|
|
84
67
|
|
|
85
|
-
|
|
86
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Compute the SHA-256 hash of a buffer.
|
|
70
|
+
*/
|
|
71
|
+
computeSha256(data: Uint8Array | Uint8Array[]): MaybePromise<Uint8Array>;
|
|
87
72
|
|
|
88
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
102
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Create an HMAC signature.
|
|
90
|
+
*/
|
|
91
|
+
signHmac(key: Uint8Array, data: Uint8Array): MaybePromise<Uint8Array>;
|
|
103
92
|
|
|
104
|
-
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
246
|
+
logImplementationName = true;
|
|
247
|
+
defaultInstance = undefined;
|
|
248
|
+
defaultProvider = undefined;
|
|
133
249
|
|
|
134
|
-
//
|
|
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 {}
|