@gjsify/crypto 0.3.12 → 0.3.14

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/lib/esm/asn1.js CHANGED
@@ -1,504 +1,630 @@
1
1
  import { Buffer } from "node:buffer";
2
+
3
+ //#region src/asn1.ts
4
+ /**
5
+ * Strip PEM armor (header/footer lines), base64-decode the body to DER bytes.
6
+ */
2
7
  function pemToDer(pem) {
3
- const lines = pem.trim().split(/\r?\n/);
4
- const headerIdx = lines.findIndex((l) => l.startsWith("-----BEGIN "));
5
- if (headerIdx === -1) {
6
- throw new Error("Invalid PEM: no BEGIN line found");
7
- }
8
- const headerLine = lines[headerIdx];
9
- const headerMatch = headerLine.match(/^-----BEGIN (.+)-----$/);
10
- if (!headerMatch) {
11
- throw new Error("Invalid PEM header format");
12
- }
13
- const type = headerMatch[1];
14
- const footerIdx = lines.findIndex((l, i) => i > headerIdx && l.startsWith("-----END "));
15
- if (footerIdx === -1) {
16
- throw new Error("Invalid PEM: no END line found");
17
- }
18
- const base64Body = lines.slice(headerIdx + 1, footerIdx).join("");
19
- const der = Buffer.from(base64Body, "base64");
20
- return { type, der: new Uint8Array(der.buffer, der.byteOffset, der.byteLength) };
21
- }
8
+ const lines = pem.trim().split(/\r?\n/);
9
+ const headerIdx = lines.findIndex((l) => l.startsWith("-----BEGIN "));
10
+ if (headerIdx === -1) {
11
+ throw new Error("Invalid PEM: no BEGIN line found");
12
+ }
13
+ const headerLine = lines[headerIdx];
14
+ const headerMatch = headerLine.match(/^-----BEGIN (.+)-----$/);
15
+ if (!headerMatch) {
16
+ throw new Error("Invalid PEM header format");
17
+ }
18
+ const type = headerMatch[1];
19
+ const footerIdx = lines.findIndex((l, i) => i > headerIdx && l.startsWith("-----END "));
20
+ if (footerIdx === -1) {
21
+ throw new Error("Invalid PEM: no END line found");
22
+ }
23
+ const base64Body = lines.slice(headerIdx + 1, footerIdx).join("");
24
+ const der = Buffer.from(base64Body, "base64");
25
+ return {
26
+ type,
27
+ der: new Uint8Array(der.buffer, der.byteOffset, der.byteLength)
28
+ };
29
+ }
30
+ /** ASN.1 tag constants */
22
31
  const ASN1_INTEGER = 2;
23
32
  const ASN1_BIT_STRING = 3;
24
33
  const ASN1_OCTET_STRING = 4;
25
34
  const ASN1_NULL = 5;
26
35
  const ASN1_OID = 6;
27
36
  const ASN1_SEQUENCE = 48;
37
+ /**
38
+ * Parse one TLV (tag-length-value) from the DER buffer starting at `offset`.
39
+ * Returns the parsed value and the new offset past it.
40
+ */
28
41
  function parseTlv(buf, offset) {
29
- if (offset >= buf.length) {
30
- throw new Error("ASN.1 parse error: unexpected end of data");
31
- }
32
- const tag = buf[offset++];
33
- let length;
34
- const firstLenByte = buf[offset++];
35
- if (firstLenByte < 128) {
36
- length = firstLenByte;
37
- } else {
38
- const numLenBytes = firstLenByte & 127;
39
- if (numLenBytes === 0) {
40
- throw new Error("ASN.1 parse error: indefinite length not supported");
41
- }
42
- length = 0;
43
- for (let i = 0; i < numLenBytes; i++) {
44
- length = length << 8 | buf[offset++];
45
- }
46
- }
47
- const data = buf.slice(offset, offset + length);
48
- const next = offset + length;
49
- const result = { tag, data };
50
- if (tag === ASN1_SEQUENCE) {
51
- result.children = parseSequenceChildren(data);
52
- }
53
- return { value: result, next };
54
- }
42
+ if (offset >= buf.length) {
43
+ throw new Error("ASN.1 parse error: unexpected end of data");
44
+ }
45
+ const tag = buf[offset++];
46
+ let length;
47
+ const firstLenByte = buf[offset++];
48
+ if (firstLenByte < 128) {
49
+ length = firstLenByte;
50
+ } else {
51
+ const numLenBytes = firstLenByte & 127;
52
+ if (numLenBytes === 0) {
53
+ throw new Error("ASN.1 parse error: indefinite length not supported");
54
+ }
55
+ length = 0;
56
+ for (let i = 0; i < numLenBytes; i++) {
57
+ length = length << 8 | buf[offset++];
58
+ }
59
+ }
60
+ const data = buf.slice(offset, offset + length);
61
+ const next = offset + length;
62
+ const result = {
63
+ tag,
64
+ data
65
+ };
66
+ if (tag === ASN1_SEQUENCE) {
67
+ result.children = parseSequenceChildren(data);
68
+ }
69
+ return {
70
+ value: result,
71
+ next
72
+ };
73
+ }
74
+ /**
75
+ * Parse all TLV children within a SEQUENCE's data bytes.
76
+ */
55
77
  function parseSequenceChildren(data) {
56
- const children = [];
57
- let pos = 0;
58
- while (pos < data.length) {
59
- const { value, next } = parseTlv(data, pos);
60
- children.push(value);
61
- pos = next;
62
- }
63
- return children;
64
- }
78
+ const children = [];
79
+ let pos = 0;
80
+ while (pos < data.length) {
81
+ const { value, next } = parseTlv(data, pos);
82
+ children.push(value);
83
+ pos = next;
84
+ }
85
+ return children;
86
+ }
87
+ /**
88
+ * Parse the entire DER buffer as a single top-level TLV.
89
+ */
65
90
  function parseDer(buf) {
66
- const { value } = parseTlv(buf, 0);
67
- return value;
68
- }
91
+ const { value } = parseTlv(buf, 0);
92
+ return value;
93
+ }
94
+ /**
95
+ * Convert an ASN.1 INTEGER's raw data bytes to a non-negative BigInt.
96
+ * ASN.1 INTEGERs are big-endian two's-complement. For RSA keys all values
97
+ * are positive, so we just strip any leading 0x00 padding byte.
98
+ */
69
99
  function integerToBigInt(data) {
70
- let start = 0;
71
- while (start < data.length - 1 && data[start] === 0) {
72
- start++;
73
- }
74
- let result = 0n;
75
- for (let i = start; i < data.length; i++) {
76
- result = result << 8n | BigInt(data[i]);
77
- }
78
- return result;
79
- }
80
- const RSA_OID = new Uint8Array([42, 134, 72, 134, 247, 13, 1, 1, 1]);
100
+ let start = 0;
101
+ while (start < data.length - 1 && data[start] === 0) {
102
+ start++;
103
+ }
104
+ let result = 0n;
105
+ for (let i = start; i < data.length; i++) {
106
+ result = result << 8n | BigInt(data[i]);
107
+ }
108
+ return result;
109
+ }
110
+ /** RSA encryption OID: 1.2.840.113549.1.1.1 */
111
+ const RSA_OID = new Uint8Array([
112
+ 42,
113
+ 134,
114
+ 72,
115
+ 134,
116
+ 247,
117
+ 13,
118
+ 1,
119
+ 1,
120
+ 1
121
+ ]);
81
122
  function oidsEqual(a, b) {
82
- if (a.length !== b.length) return false;
83
- for (let i = 0; i < a.length; i++) {
84
- if (a[i] !== b[i]) return false;
85
- }
86
- return true;
87
- }
123
+ if (a.length !== b.length) return false;
124
+ for (let i = 0; i < a.length; i++) {
125
+ if (a[i] !== b[i]) return false;
126
+ }
127
+ return true;
128
+ }
129
+ /**
130
+ * Parse PKCS#1 RSAPublicKey:
131
+ * SEQUENCE { INTEGER n, INTEGER e }
132
+ */
88
133
  function parseRsaPublicKeyPkcs1(seq) {
89
- const children = seq.children;
90
- if (!children || children.length < 2) {
91
- throw new Error("Invalid PKCS#1 RSAPublicKey structure");
92
- }
93
- return {
94
- n: integerToBigInt(children[0].data),
95
- e: integerToBigInt(children[1].data)
96
- };
97
- }
134
+ const children = seq.children;
135
+ if (!children || children.length < 2) {
136
+ throw new Error("Invalid PKCS#1 RSAPublicKey structure");
137
+ }
138
+ return {
139
+ n: integerToBigInt(children[0].data),
140
+ e: integerToBigInt(children[1].data)
141
+ };
142
+ }
143
+ /**
144
+ * Parse PKCS#1 RSAPrivateKey:
145
+ * SEQUENCE {
146
+ * INTEGER version,
147
+ * INTEGER n,
148
+ * INTEGER e,
149
+ * INTEGER d,
150
+ * INTEGER p,
151
+ * INTEGER q,
152
+ * INTEGER dp,
153
+ * INTEGER dq,
154
+ * INTEGER qi
155
+ * }
156
+ */
98
157
  function parseRsaPrivateKeyPkcs1(seq) {
99
- const children = seq.children;
100
- if (!children || children.length < 6) {
101
- throw new Error("Invalid PKCS#1 RSAPrivateKey structure");
102
- }
103
- return {
104
- n: integerToBigInt(children[1].data),
105
- e: integerToBigInt(children[2].data),
106
- d: integerToBigInt(children[3].data),
107
- p: integerToBigInt(children[4].data),
108
- q: integerToBigInt(children[5].data)
109
- };
110
- }
158
+ const children = seq.children;
159
+ if (!children || children.length < 6) {
160
+ throw new Error("Invalid PKCS#1 RSAPrivateKey structure");
161
+ }
162
+ return {
163
+ n: integerToBigInt(children[1].data),
164
+ e: integerToBigInt(children[2].data),
165
+ d: integerToBigInt(children[3].data),
166
+ p: integerToBigInt(children[4].data),
167
+ q: integerToBigInt(children[5].data)
168
+ };
169
+ }
170
+ /**
171
+ * Parse PKCS#8 SubjectPublicKeyInfo:
172
+ * SEQUENCE {
173
+ * SEQUENCE { OID algorithm, NULL } -- AlgorithmIdentifier
174
+ * BIT STRING -- wraps PKCS#1 RSAPublicKey
175
+ * }
176
+ */
111
177
  function parseSubjectPublicKeyInfo(seq) {
112
- const children = seq.children;
113
- if (!children || children.length < 2) {
114
- throw new Error("Invalid SubjectPublicKeyInfo structure");
115
- }
116
- const algIdSeq = children[0];
117
- if (!algIdSeq.children || algIdSeq.children.length < 1) {
118
- throw new Error("Invalid AlgorithmIdentifier");
119
- }
120
- const oid = algIdSeq.children[0];
121
- if (oid.tag !== ASN1_OID || !oidsEqual(oid.data, RSA_OID)) {
122
- throw new Error("Unsupported algorithm: only RSA is supported");
123
- }
124
- const bitString = children[1];
125
- if (bitString.tag !== ASN1_BIT_STRING) {
126
- throw new Error("Expected BIT STRING for public key data");
127
- }
128
- const innerDer = bitString.data.slice(1);
129
- const innerSeq = parseDer(innerDer);
130
- return parseRsaPublicKeyPkcs1(innerSeq);
131
- }
178
+ const children = seq.children;
179
+ if (!children || children.length < 2) {
180
+ throw new Error("Invalid SubjectPublicKeyInfo structure");
181
+ }
182
+ const algIdSeq = children[0];
183
+ if (!algIdSeq.children || algIdSeq.children.length < 1) {
184
+ throw new Error("Invalid AlgorithmIdentifier");
185
+ }
186
+ const oid = algIdSeq.children[0];
187
+ if (oid.tag !== ASN1_OID || !oidsEqual(oid.data, RSA_OID)) {
188
+ throw new Error("Unsupported algorithm: only RSA is supported");
189
+ }
190
+ const bitString = children[1];
191
+ if (bitString.tag !== ASN1_BIT_STRING) {
192
+ throw new Error("Expected BIT STRING for public key data");
193
+ }
194
+ const innerDer = bitString.data.slice(1);
195
+ const innerSeq = parseDer(innerDer);
196
+ return parseRsaPublicKeyPkcs1(innerSeq);
197
+ }
198
+ /**
199
+ * Parse PKCS#8 PrivateKeyInfo:
200
+ * SEQUENCE {
201
+ * INTEGER version,
202
+ * SEQUENCE { OID algorithm, NULL } -- AlgorithmIdentifier
203
+ * OCTET STRING -- wraps PKCS#1 RSAPrivateKey
204
+ * }
205
+ */
132
206
  function parsePrivateKeyInfo(seq) {
133
- const children = seq.children;
134
- if (!children || children.length < 3) {
135
- throw new Error("Invalid PrivateKeyInfo structure");
136
- }
137
- const algIdSeq = children[1];
138
- if (!algIdSeq.children || algIdSeq.children.length < 1) {
139
- throw new Error("Invalid AlgorithmIdentifier");
140
- }
141
- const oid = algIdSeq.children[0];
142
- if (oid.tag !== ASN1_OID || !oidsEqual(oid.data, RSA_OID)) {
143
- throw new Error("Unsupported algorithm: only RSA is supported");
144
- }
145
- const octetString = children[2];
146
- if (octetString.tag !== ASN1_OCTET_STRING) {
147
- throw new Error("Expected OCTET STRING for private key data");
148
- }
149
- const innerSeq = parseDer(octetString.data);
150
- return parseRsaPrivateKeyPkcs1(innerSeq);
151
- }
207
+ const children = seq.children;
208
+ if (!children || children.length < 3) {
209
+ throw new Error("Invalid PrivateKeyInfo structure");
210
+ }
211
+ const algIdSeq = children[1];
212
+ if (!algIdSeq.children || algIdSeq.children.length < 1) {
213
+ throw new Error("Invalid AlgorithmIdentifier");
214
+ }
215
+ const oid = algIdSeq.children[0];
216
+ if (oid.tag !== ASN1_OID || !oidsEqual(oid.data, RSA_OID)) {
217
+ throw new Error("Unsupported algorithm: only RSA is supported");
218
+ }
219
+ const octetString = children[2];
220
+ if (octetString.tag !== ASN1_OCTET_STRING) {
221
+ throw new Error("Expected OCTET STRING for private key data");
222
+ }
223
+ const innerSeq = parseDer(octetString.data);
224
+ return parseRsaPrivateKeyPkcs1(innerSeq);
225
+ }
226
+ /**
227
+ * Encode a length value in DER format.
228
+ */
152
229
  function encodeLength(length) {
153
- if (length < 128) {
154
- return new Uint8Array([length]);
155
- }
156
- const bytes = [];
157
- let val = length;
158
- while (val > 0) {
159
- bytes.unshift(val & 255);
160
- val >>= 8;
161
- }
162
- return new Uint8Array([128 | bytes.length, ...bytes]);
163
- }
230
+ if (length < 128) {
231
+ return new Uint8Array([length]);
232
+ }
233
+ const bytes = [];
234
+ let val = length;
235
+ while (val > 0) {
236
+ bytes.unshift(val & 255);
237
+ val >>= 8;
238
+ }
239
+ return new Uint8Array([128 | bytes.length, ...bytes]);
240
+ }
241
+ /**
242
+ * Encode a TLV (tag-length-value).
243
+ */
164
244
  function encodeTlv(tag, data) {
165
- const len = encodeLength(data.length);
166
- const result = new Uint8Array(1 + len.length + data.length);
167
- result[0] = tag;
168
- result.set(len, 1);
169
- result.set(data, 1 + len.length);
170
- return result;
171
- }
245
+ const len = encodeLength(data.length);
246
+ const result = new Uint8Array(1 + len.length + data.length);
247
+ result[0] = tag;
248
+ result.set(len, 1);
249
+ result.set(data, 1 + len.length);
250
+ return result;
251
+ }
252
+ /**
253
+ * Encode a BigInt as an ASN.1 INTEGER.
254
+ */
172
255
  function bigintToAsn1Integer(value) {
173
- if (value === 0n) {
174
- return encodeTlv(ASN1_INTEGER, new Uint8Array([0]));
175
- }
176
- const hex = value.toString(16);
177
- const paddedHex = hex.length % 2 ? "0" + hex : hex;
178
- const bytes = [];
179
- for (let i = 0; i < paddedHex.length; i += 2) {
180
- bytes.push(parseInt(paddedHex.substring(i, i + 2), 16));
181
- }
182
- if (bytes[0] & 128) {
183
- bytes.unshift(0);
184
- }
185
- return encodeTlv(ASN1_INTEGER, new Uint8Array(bytes));
186
- }
256
+ if (value === 0n) {
257
+ return encodeTlv(ASN1_INTEGER, new Uint8Array([0]));
258
+ }
259
+ const hex = value.toString(16);
260
+ const paddedHex = hex.length % 2 ? "0" + hex : hex;
261
+ const bytes = [];
262
+ for (let i = 0; i < paddedHex.length; i += 2) {
263
+ bytes.push(parseInt(paddedHex.substring(i, i + 2), 16));
264
+ }
265
+ if (bytes[0] & 128) {
266
+ bytes.unshift(0);
267
+ }
268
+ return encodeTlv(ASN1_INTEGER, new Uint8Array(bytes));
269
+ }
270
+ /**
271
+ * Encode an ASN.1 SEQUENCE from its children.
272
+ */
187
273
  function encodeSequence(children) {
188
- let totalLen = 0;
189
- for (const child of children) totalLen += child.length;
190
- const data = new Uint8Array(totalLen);
191
- let offset = 0;
192
- for (const child of children) {
193
- data.set(child, offset);
194
- offset += child.length;
195
- }
196
- return encodeTlv(ASN1_SEQUENCE, data);
197
- }
274
+ let totalLen = 0;
275
+ for (const child of children) totalLen += child.length;
276
+ const data = new Uint8Array(totalLen);
277
+ let offset = 0;
278
+ for (const child of children) {
279
+ data.set(child, offset);
280
+ offset += child.length;
281
+ }
282
+ return encodeTlv(ASN1_SEQUENCE, data);
283
+ }
284
+ /**
285
+ * Encode an ASN.1 BIT STRING (with 0 unused bits).
286
+ */
198
287
  function encodeBitString(data) {
199
- const inner = new Uint8Array(1 + data.length);
200
- inner[0] = 0;
201
- inner.set(data, 1);
202
- return encodeTlv(ASN1_BIT_STRING, inner);
203
- }
288
+ const inner = new Uint8Array(1 + data.length);
289
+ inner[0] = 0;
290
+ inner.set(data, 1);
291
+ return encodeTlv(ASN1_BIT_STRING, inner);
292
+ }
293
+ /**
294
+ * Encode an ASN.1 OCTET STRING.
295
+ */
204
296
  function encodeOctetString(data) {
205
- return encodeTlv(ASN1_OCTET_STRING, data);
297
+ return encodeTlv(ASN1_OCTET_STRING, data);
206
298
  }
299
+ /**
300
+ * Encode an ASN.1 OID.
301
+ */
207
302
  function encodeOid(oidBytes) {
208
- return encodeTlv(ASN1_OID, oidBytes);
303
+ return encodeTlv(ASN1_OID, oidBytes);
209
304
  }
305
+ /**
306
+ * Encode ASN.1 NULL.
307
+ */
210
308
  function encodeNull() {
211
- return new Uint8Array([ASN1_NULL, 0]);
309
+ return new Uint8Array([ASN1_NULL, 0]);
212
310
  }
311
+ /**
312
+ * Encode RSA public key components as PKCS#1 RSAPublicKey DER.
313
+ */
213
314
  function encodeRsaPublicKeyPkcs1(components) {
214
- return encodeSequence([
215
- bigintToAsn1Integer(components.n),
216
- bigintToAsn1Integer(components.e)
217
- ]);
315
+ return encodeSequence([bigintToAsn1Integer(components.n), bigintToAsn1Integer(components.e)]);
218
316
  }
317
+ /**
318
+ * Encode RSA public key as PKCS#8 SubjectPublicKeyInfo DER.
319
+ */
219
320
  function encodeSubjectPublicKeyInfo(components) {
220
- const algorithmId = encodeSequence([encodeOid(RSA_OID), encodeNull()]);
221
- const rsaPublicKey = encodeRsaPublicKeyPkcs1(components);
222
- const bitString = encodeBitString(rsaPublicKey);
223
- return encodeSequence([algorithmId, bitString]);
224
- }
321
+ const algorithmId = encodeSequence([encodeOid(RSA_OID), encodeNull()]);
322
+ const rsaPublicKey = encodeRsaPublicKeyPkcs1(components);
323
+ const bitString = encodeBitString(rsaPublicKey);
324
+ return encodeSequence([algorithmId, bitString]);
325
+ }
326
+ /**
327
+ * Encode RSA private key components as PKCS#1 RSAPrivateKey DER.
328
+ */
225
329
  function encodeRsaPrivateKeyPkcs1(components) {
226
- const dp = components.d % (components.p - 1n);
227
- const dq = components.d % (components.q - 1n);
228
- const qi = modInverse(components.q, components.p);
229
- return encodeSequence([
230
- bigintToAsn1Integer(0n),
231
- // version
232
- bigintToAsn1Integer(components.n),
233
- bigintToAsn1Integer(components.e),
234
- bigintToAsn1Integer(components.d),
235
- bigintToAsn1Integer(components.p),
236
- bigintToAsn1Integer(components.q),
237
- bigintToAsn1Integer(dp),
238
- bigintToAsn1Integer(dq),
239
- bigintToAsn1Integer(qi)
240
- ]);
241
- }
330
+ const dp = components.d % (components.p - 1n);
331
+ const dq = components.d % (components.q - 1n);
332
+ const qi = modInverse(components.q, components.p);
333
+ return encodeSequence([
334
+ bigintToAsn1Integer(0n),
335
+ bigintToAsn1Integer(components.n),
336
+ bigintToAsn1Integer(components.e),
337
+ bigintToAsn1Integer(components.d),
338
+ bigintToAsn1Integer(components.p),
339
+ bigintToAsn1Integer(components.q),
340
+ bigintToAsn1Integer(dp),
341
+ bigintToAsn1Integer(dq),
342
+ bigintToAsn1Integer(qi)
343
+ ]);
344
+ }
345
+ /**
346
+ * Encode RSA private key as PKCS#8 PrivateKeyInfo DER.
347
+ */
242
348
  function encodePrivateKeyInfo(components) {
243
- const algorithmId = encodeSequence([encodeOid(RSA_OID), encodeNull()]);
244
- const rsaPrivateKey = encodeRsaPrivateKeyPkcs1(components);
245
- const octetString = encodeOctetString(rsaPrivateKey);
246
- return encodeSequence([
247
- bigintToAsn1Integer(0n),
248
- // version
249
- algorithmId,
250
- octetString
251
- ]);
252
- }
349
+ const algorithmId = encodeSequence([encodeOid(RSA_OID), encodeNull()]);
350
+ const rsaPrivateKey = encodeRsaPrivateKeyPkcs1(components);
351
+ const octetString = encodeOctetString(rsaPrivateKey);
352
+ return encodeSequence([
353
+ bigintToAsn1Integer(0n),
354
+ algorithmId,
355
+ octetString
356
+ ]);
357
+ }
358
+ /**
359
+ * Convert DER bytes to PEM string.
360
+ */
253
361
  function derToPem(der, type) {
254
- const base64 = Buffer.from(der).toString("base64");
255
- const lines = [`-----BEGIN ${type}-----`];
256
- for (let i = 0; i < base64.length; i += 64) {
257
- lines.push(base64.substring(i, i + 64));
258
- }
259
- lines.push(`-----END ${type}-----`);
260
- return lines.join("\n");
261
- }
362
+ const base64 = Buffer.from(der).toString("base64");
363
+ const lines = [`-----BEGIN ${type}-----`];
364
+ for (let i = 0; i < base64.length; i += 64) {
365
+ lines.push(base64.substring(i, i + 64));
366
+ }
367
+ lines.push(`-----END ${type}-----`);
368
+ return lines.join("\n");
369
+ }
370
+ /**
371
+ * Modular inverse: a^(-1) mod m using extended Euclidean algorithm.
372
+ */
262
373
  function modInverse(a, m) {
263
- let [old_r, r] = [a % m, m];
264
- let [old_s, s] = [1n, 0n];
265
- while (r !== 0n) {
266
- const q = old_r / r;
267
- [old_r, r] = [r, old_r - q * r];
268
- [old_s, s] = [s, old_s - q * s];
269
- }
270
- return (old_s % m + m) % m;
271
- }
374
+ let [old_r, r] = [a % m, m];
375
+ let [old_s, s] = [1n, 0n];
376
+ while (r !== 0n) {
377
+ const q = old_r / r;
378
+ [old_r, r] = [r, old_r - q * r];
379
+ [old_s, s] = [s, old_s - q * s];
380
+ }
381
+ return (old_s % m + m) % m;
382
+ }
383
+ /**
384
+ * Parse an X.509 certificate from PEM or DER.
385
+ */
272
386
  function parseX509(pem) {
273
- const { der } = pemToDer(pem);
274
- return parseX509Der(der);
387
+ const { der } = pemToDer(pem);
388
+ return parseX509Der(der);
275
389
  }
390
+ /**
391
+ * Parse an X.509 certificate from DER bytes.
392
+ */
276
393
  function parseX509Der(der) {
277
- const root = parseDer(der);
278
- if (root.tag !== ASN1_SEQUENCE || !root.children || root.children.length < 3) {
279
- throw new Error("Invalid X.509 certificate structure");
280
- }
281
- const tbsCertificate = root.children[0];
282
- const signatureAlgorithm = root.children[1];
283
- const signatureBitString = root.children[2];
284
- if (!tbsCertificate.children || tbsCertificate.children.length < 6) {
285
- throw new Error("Invalid TBSCertificate structure");
286
- }
287
- let idx = 0;
288
- if (tbsCertificate.children[0].tag === 160) {
289
- idx++;
290
- }
291
- const serialNumber = integerToBigInt(tbsCertificate.children[idx].data);
292
- idx++;
293
- idx++;
294
- const issuer = parseDN(tbsCertificate.children[idx]);
295
- idx++;
296
- const validity = tbsCertificate.children[idx];
297
- const validFrom = parseAsn1Time(validity.children[0]);
298
- const validTo = parseAsn1Time(validity.children[1]);
299
- idx++;
300
- const subject = parseDN(tbsCertificate.children[idx]);
301
- idx++;
302
- let publicKey = null;
303
- let publicKeyAlgorithm = "unknown";
304
- if (idx < tbsCertificate.children.length) {
305
- const spki = tbsCertificate.children[idx];
306
- try {
307
- if (spki.children && spki.children.length >= 2) {
308
- const algId = spki.children[0];
309
- if (algId.children && algId.children.length >= 1) {
310
- const oid = algId.children[0];
311
- if (oid.tag === ASN1_OID && oidsEqual(oid.data, RSA_OID)) {
312
- publicKeyAlgorithm = "rsa";
313
- publicKey = parseSubjectPublicKeyInfo(spki);
314
- }
315
- }
316
- }
317
- } catch {
318
- }
319
- idx++;
320
- }
321
- let subjectAltName;
322
- let extensions;
323
- for (let i = idx; i < tbsCertificate.children.length; i++) {
324
- const child = tbsCertificate.children[i];
325
- if (child.tag === 163 && child.data.length > 0) {
326
- const extSeq = parseDer(child.data);
327
- if (extSeq.children) {
328
- extensions = extSeq.children;
329
- const SAN_OID = new Uint8Array([85, 29, 17]);
330
- for (const ext of extSeq.children) {
331
- if (ext.children && ext.children.length >= 2) {
332
- const extOid = ext.children[0];
333
- if (extOid.tag === ASN1_OID && oidsEqual(extOid.data, SAN_OID)) {
334
- const extValue = ext.children[ext.children.length - 1];
335
- subjectAltName = parseSAN(extValue.data);
336
- }
337
- }
338
- }
339
- }
340
- }
341
- }
342
- let sigAlg = "unknown";
343
- if (signatureAlgorithm.children && signatureAlgorithm.children.length >= 1) {
344
- sigAlg = oidToName(signatureAlgorithm.children[0].data);
345
- }
346
- const signature = signatureBitString.tag === ASN1_BIT_STRING ? signatureBitString.data.slice(1) : signatureBitString.data;
347
- return {
348
- raw: der,
349
- tbsCertificate: tbsCertificate.data,
350
- serialNumber,
351
- issuer,
352
- subject,
353
- validFrom,
354
- validTo,
355
- publicKey,
356
- publicKeyAlgorithm,
357
- signatureAlgorithm: sigAlg,
358
- signature,
359
- subjectAltName,
360
- extensions
361
- };
394
+ const root = parseDer(der);
395
+ if (root.tag !== ASN1_SEQUENCE || !root.children || root.children.length < 3) {
396
+ throw new Error("Invalid X.509 certificate structure");
397
+ }
398
+ const tbsCertificate = root.children[0];
399
+ const signatureAlgorithm = root.children[1];
400
+ const signatureBitString = root.children[2];
401
+ if (!tbsCertificate.children || tbsCertificate.children.length < 6) {
402
+ throw new Error("Invalid TBSCertificate structure");
403
+ }
404
+ let idx = 0;
405
+ if (tbsCertificate.children[0].tag === 160) {
406
+ idx++;
407
+ }
408
+ const serialNumber = integerToBigInt(tbsCertificate.children[idx].data);
409
+ idx++;
410
+ idx++;
411
+ const issuer = parseDN(tbsCertificate.children[idx]);
412
+ idx++;
413
+ const validity = tbsCertificate.children[idx];
414
+ const validFrom = parseAsn1Time(validity.children[0]);
415
+ const validTo = parseAsn1Time(validity.children[1]);
416
+ idx++;
417
+ const subject = parseDN(tbsCertificate.children[idx]);
418
+ idx++;
419
+ let publicKey = null;
420
+ let publicKeyAlgorithm = "unknown";
421
+ if (idx < tbsCertificate.children.length) {
422
+ const spki = tbsCertificate.children[idx];
423
+ try {
424
+ if (spki.children && spki.children.length >= 2) {
425
+ const algId = spki.children[0];
426
+ if (algId.children && algId.children.length >= 1) {
427
+ const oid = algId.children[0];
428
+ if (oid.tag === ASN1_OID && oidsEqual(oid.data, RSA_OID)) {
429
+ publicKeyAlgorithm = "rsa";
430
+ publicKey = parseSubjectPublicKeyInfo(spki);
431
+ }
432
+ }
433
+ }
434
+ } catch {}
435
+ idx++;
436
+ }
437
+ let subjectAltName;
438
+ let extensions;
439
+ for (let i = idx; i < tbsCertificate.children.length; i++) {
440
+ const child = tbsCertificate.children[i];
441
+ if (child.tag === 163 && child.data.length > 0) {
442
+ const extSeq = parseDer(child.data);
443
+ if (extSeq.children) {
444
+ extensions = extSeq.children;
445
+ const SAN_OID = new Uint8Array([
446
+ 85,
447
+ 29,
448
+ 17
449
+ ]);
450
+ for (const ext of extSeq.children) {
451
+ if (ext.children && ext.children.length >= 2) {
452
+ const extOid = ext.children[0];
453
+ if (extOid.tag === ASN1_OID && oidsEqual(extOid.data, SAN_OID)) {
454
+ const extValue = ext.children[ext.children.length - 1];
455
+ subjectAltName = parseSAN(extValue.data);
456
+ }
457
+ }
458
+ }
459
+ }
460
+ }
461
+ }
462
+ let sigAlg = "unknown";
463
+ if (signatureAlgorithm.children && signatureAlgorithm.children.length >= 1) {
464
+ sigAlg = oidToName(signatureAlgorithm.children[0].data);
465
+ }
466
+ const signature = signatureBitString.tag === ASN1_BIT_STRING ? signatureBitString.data.slice(1) : signatureBitString.data;
467
+ return {
468
+ raw: der,
469
+ tbsCertificate: tbsCertificate.data,
470
+ serialNumber,
471
+ issuer,
472
+ subject,
473
+ validFrom,
474
+ validTo,
475
+ publicKey,
476
+ publicKeyAlgorithm,
477
+ signatureAlgorithm: sigAlg,
478
+ signature,
479
+ subjectAltName,
480
+ extensions
481
+ };
362
482
  }
363
483
  const OID_NAMES = {
364
- "2.5.4.3": "CN",
365
- "2.5.4.6": "C",
366
- "2.5.4.7": "L",
367
- "2.5.4.8": "ST",
368
- "2.5.4.10": "O",
369
- "2.5.4.11": "OU",
370
- "1.2.840.113549.1.9.1": "emailAddress"
484
+ "2.5.4.3": "CN",
485
+ "2.5.4.6": "C",
486
+ "2.5.4.7": "L",
487
+ "2.5.4.8": "ST",
488
+ "2.5.4.10": "O",
489
+ "2.5.4.11": "OU",
490
+ "1.2.840.113549.1.9.1": "emailAddress"
371
491
  };
372
492
  const SIG_ALG_NAMES = {
373
- "1.2.840.113549.1.1.1": "rsaEncryption",
374
- "1.2.840.113549.1.1.5": "sha1WithRSAEncryption",
375
- "1.2.840.113549.1.1.11": "sha256WithRSAEncryption",
376
- "1.2.840.113549.1.1.12": "sha384WithRSAEncryption",
377
- "1.2.840.113549.1.1.13": "sha512WithRSAEncryption"
493
+ "1.2.840.113549.1.1.1": "rsaEncryption",
494
+ "1.2.840.113549.1.1.5": "sha1WithRSAEncryption",
495
+ "1.2.840.113549.1.1.11": "sha256WithRSAEncryption",
496
+ "1.2.840.113549.1.1.12": "sha384WithRSAEncryption",
497
+ "1.2.840.113549.1.1.13": "sha512WithRSAEncryption"
378
498
  };
379
499
  function decodeOidString(data) {
380
- if (data.length === 0) return "";
381
- const components = [];
382
- components.push(Math.floor(data[0] / 40));
383
- components.push(data[0] % 40);
384
- let value = 0;
385
- for (let i = 1; i < data.length; i++) {
386
- value = value << 7 | data[i] & 127;
387
- if (!(data[i] & 128)) {
388
- components.push(value);
389
- value = 0;
390
- }
391
- }
392
- return components.join(".");
500
+ if (data.length === 0) return "";
501
+ const components = [];
502
+ components.push(Math.floor(data[0] / 40));
503
+ components.push(data[0] % 40);
504
+ let value = 0;
505
+ for (let i = 1; i < data.length; i++) {
506
+ value = value << 7 | data[i] & 127;
507
+ if (!(data[i] & 128)) {
508
+ components.push(value);
509
+ value = 0;
510
+ }
511
+ }
512
+ return components.join(".");
393
513
  }
394
514
  function oidToName(data) {
395
- const oidStr = decodeOidString(data);
396
- return SIG_ALG_NAMES[oidStr] || oidStr;
515
+ const oidStr = decodeOidString(data);
516
+ return SIG_ALG_NAMES[oidStr] || oidStr;
397
517
  }
398
518
  function parseDN(seq) {
399
- if (!seq.children) return "";
400
- const parts = [];
401
- for (const rdn of seq.children) {
402
- const rdnChildren = rdn.tag === 49 ? parseSequenceChildren(rdn.data) : rdn.children || [];
403
- for (const atv of rdnChildren) {
404
- if (atv.children && atv.children.length >= 2) {
405
- const oidData = atv.children[0].data;
406
- const oidStr = decodeOidString(oidData);
407
- const name = OID_NAMES[oidStr] || oidStr;
408
- const valueBytes = atv.children[1].data;
409
- const value = new TextDecoder().decode(valueBytes);
410
- parts.push(`${name}=${value}`);
411
- }
412
- }
413
- }
414
- return parts.join(", ");
519
+ if (!seq.children) return "";
520
+ const parts = [];
521
+ for (const rdn of seq.children) {
522
+ const rdnChildren = rdn.tag === 49 ? parseSequenceChildren(rdn.data) : rdn.children || [];
523
+ for (const atv of rdnChildren) {
524
+ if (atv.children && atv.children.length >= 2) {
525
+ const oidData = atv.children[0].data;
526
+ const oidStr = decodeOidString(oidData);
527
+ const name = OID_NAMES[oidStr] || oidStr;
528
+ const valueBytes = atv.children[1].data;
529
+ const value = new TextDecoder().decode(valueBytes);
530
+ parts.push(`${name}=${value}`);
531
+ }
532
+ }
533
+ }
534
+ return parts.join(", ");
415
535
  }
416
536
  function parseAsn1Time(tlv) {
417
- const str = new TextDecoder().decode(tlv.data);
418
- if (tlv.tag === 23) {
419
- let year = parseInt(str.substring(0, 2), 10);
420
- year = year >= 50 ? 1900 + year : 2e3 + year;
421
- const month = parseInt(str.substring(2, 4), 10) - 1;
422
- const day = parseInt(str.substring(4, 6), 10);
423
- const hour = parseInt(str.substring(6, 8), 10);
424
- const minute = parseInt(str.substring(8, 10), 10);
425
- const second = parseInt(str.substring(10, 12), 10);
426
- return new Date(Date.UTC(year, month, day, hour, minute, second));
427
- }
428
- if (tlv.tag === 24) {
429
- const year = parseInt(str.substring(0, 4), 10);
430
- const month = parseInt(str.substring(4, 6), 10) - 1;
431
- const day = parseInt(str.substring(6, 8), 10);
432
- const hour = parseInt(str.substring(8, 10), 10);
433
- const minute = parseInt(str.substring(10, 12), 10);
434
- const second = parseInt(str.substring(12, 14), 10);
435
- return new Date(Date.UTC(year, month, day, hour, minute, second));
436
- }
437
- throw new Error(`Unsupported time tag: 0x${tlv.tag.toString(16)}`);
537
+ const str = new TextDecoder().decode(tlv.data);
538
+ if (tlv.tag === 23) {
539
+ let year = parseInt(str.substring(0, 2), 10);
540
+ year = year >= 50 ? 1900 + year : 2e3 + year;
541
+ const month = parseInt(str.substring(2, 4), 10) - 1;
542
+ const day = parseInt(str.substring(4, 6), 10);
543
+ const hour = parseInt(str.substring(6, 8), 10);
544
+ const minute = parseInt(str.substring(8, 10), 10);
545
+ const second = parseInt(str.substring(10, 12), 10);
546
+ return new Date(Date.UTC(year, month, day, hour, minute, second));
547
+ }
548
+ if (tlv.tag === 24) {
549
+ const year = parseInt(str.substring(0, 4), 10);
550
+ const month = parseInt(str.substring(4, 6), 10) - 1;
551
+ const day = parseInt(str.substring(6, 8), 10);
552
+ const hour = parseInt(str.substring(8, 10), 10);
553
+ const minute = parseInt(str.substring(10, 12), 10);
554
+ const second = parseInt(str.substring(12, 14), 10);
555
+ return new Date(Date.UTC(year, month, day, hour, minute, second));
556
+ }
557
+ throw new Error(`Unsupported time tag: 0x${tlv.tag.toString(16)}`);
438
558
  }
439
559
  function parseSAN(data) {
440
- const names = [];
441
- try {
442
- const seq = parseDer(data);
443
- if (seq.tag === ASN1_SEQUENCE && seq.children) {
444
- for (const child of seq.children) {
445
- if (child.tag === 130) {
446
- names.push("DNS:" + new TextDecoder().decode(child.data));
447
- } else if (child.tag === 135) {
448
- if (child.data.length === 4) {
449
- names.push("IP Address:" + child.data.join("."));
450
- } else if (child.data.length === 16) {
451
- const parts = [];
452
- for (let i = 0; i < 16; i += 2) {
453
- parts.push((child.data[i] << 8 | child.data[i + 1]).toString(16));
454
- }
455
- names.push("IP Address:" + parts.join(":"));
456
- }
457
- }
458
- }
459
- }
460
- } catch {
461
- }
462
- return names;
463
- }
560
+ const names = [];
561
+ try {
562
+ const seq = parseDer(data);
563
+ if (seq.tag === ASN1_SEQUENCE && seq.children) {
564
+ for (const child of seq.children) {
565
+ if (child.tag === 130) {
566
+ names.push("DNS:" + new TextDecoder().decode(child.data));
567
+ } else if (child.tag === 135) {
568
+ if (child.data.length === 4) {
569
+ names.push("IP Address:" + child.data.join("."));
570
+ } else if (child.data.length === 16) {
571
+ const parts = [];
572
+ for (let i = 0; i < 16; i += 2) {
573
+ parts.push((child.data[i] << 8 | child.data[i + 1]).toString(16));
574
+ }
575
+ names.push("IP Address:" + parts.join(":"));
576
+ }
577
+ }
578
+ }
579
+ }
580
+ } catch {}
581
+ return names;
582
+ }
583
+ /**
584
+ * Parse a PEM-encoded RSA key. Supports:
585
+ * - RSA PUBLIC KEY (PKCS#1)
586
+ * - PUBLIC KEY (PKCS#8 SubjectPublicKeyInfo)
587
+ * - RSA PRIVATE KEY (PKCS#1)
588
+ * - PRIVATE KEY (PKCS#8 PrivateKeyInfo)
589
+ */
464
590
  function parsePemKey(pem) {
465
- const { type, der } = pemToDer(pem);
466
- const root = parseDer(der);
467
- if (root.tag !== ASN1_SEQUENCE) {
468
- throw new Error("Invalid key format: expected top-level SEQUENCE");
469
- }
470
- switch (type) {
471
- case "RSA PUBLIC KEY":
472
- return { type: "rsa-public", components: parseRsaPublicKeyPkcs1(root) };
473
- case "PUBLIC KEY":
474
- return { type: "rsa-public", components: parseSubjectPublicKeyInfo(root) };
475
- case "RSA PRIVATE KEY":
476
- return { type: "rsa-private", components: parseRsaPrivateKeyPkcs1(root) };
477
- case "PRIVATE KEY":
478
- return { type: "rsa-private", components: parsePrivateKeyInfo(root) };
479
- default:
480
- throw new Error(`Unsupported PEM type: ${type}`);
481
- }
482
- }
591
+ const { type, der } = pemToDer(pem);
592
+ const root = parseDer(der);
593
+ if (root.tag !== ASN1_SEQUENCE) {
594
+ throw new Error("Invalid key format: expected top-level SEQUENCE");
595
+ }
596
+ switch (type) {
597
+ case "RSA PUBLIC KEY": return {
598
+ type: "rsa-public",
599
+ components: parseRsaPublicKeyPkcs1(root)
600
+ };
601
+ case "PUBLIC KEY": return {
602
+ type: "rsa-public",
603
+ components: parseSubjectPublicKeyInfo(root)
604
+ };
605
+ case "RSA PRIVATE KEY": return {
606
+ type: "rsa-private",
607
+ components: parseRsaPrivateKeyPkcs1(root)
608
+ };
609
+ case "PRIVATE KEY": return {
610
+ type: "rsa-private",
611
+ components: parsePrivateKeyInfo(root)
612
+ };
613
+ default: throw new Error(`Unsupported PEM type: ${type}`);
614
+ }
615
+ }
616
+ /**
617
+ * Get the key size in bytes (byte length of modulus n).
618
+ */
483
619
  function rsaKeySize(n) {
484
- let bits = 0;
485
- let val = n;
486
- while (val > 0n) {
487
- bits++;
488
- val >>= 1n;
489
- }
490
- return Math.ceil(bits / 8);
491
- }
492
- export {
493
- bigintToAsn1Integer,
494
- derToPem,
495
- encodePrivateKeyInfo,
496
- encodeRsaPrivateKeyPkcs1,
497
- encodeRsaPublicKeyPkcs1,
498
- encodeSequence,
499
- encodeSubjectPublicKeyInfo,
500
- parsePemKey,
501
- parseX509,
502
- parseX509Der,
503
- rsaKeySize
504
- };
620
+ let bits = 0;
621
+ let val = n;
622
+ while (val > 0n) {
623
+ bits++;
624
+ val >>= 1n;
625
+ }
626
+ return Math.ceil(bits / 8);
627
+ }
628
+
629
+ //#endregion
630
+ export { bigintToAsn1Integer, derToPem, encodePrivateKeyInfo, encodeRsaPrivateKeyPkcs1, encodeRsaPublicKeyPkcs1, encodeSequence, encodeSubjectPublicKeyInfo, parsePemKey, parseX509, parseX509Der, rsaKeySize };