src/vs/platform/tunnel/node/selfSignedCert.ts

251 LOC · 235 covered · 16 uncovered · 7 ranges · 28 concepts · 2 introducers · 33 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filetunnelProxy.test|title=TunnelProxy managed (non-NodeSocket) transport CONNECT tunnels bidirectional data through a managed socket|occurrence=1 · 0 introduced LOCtunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy managed (non-NodeSocket) transport forwards an authenticated HTTP GET through a managed socket|occurrence=1 · 0 introduced LOCtunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy managed (non-NodeSocket) transport dispose disposes the managed remote socket via the adapter|occurrence=1 · 0 introduced LOCtunnelProxy.test|title=T…tunnelProxy.ts ×1 · 6 introduced LOCtunnelProxy.ts ×1tunnelProxy.ts ×2 · 6 introduced LOCtunnelProxy.ts ×2tunnelProxy.test|title=TunnelProxy dispose terminates active CONNECT tunnels|occurrence=1, tunnelProxy.test|title=TunnelProxy CONNECT establishes a tunnel to the target|occurrence=1, +1 · 0 introduced LOCtunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy forwards authenticated HTTP GET to target|occurrence=1, tunnelProxy.test|title=TunnelProxy forwards authenticated HTTP POST to target|occurrence=1, +4 · 0 introduced LOCtunnelProxy.test|title=T…tunnelProxy.ts ×2 · 11 introduced LOCtunnelProxy.ts ×2tunnelProxy.ts ×5 · 21 introduced LOCtunnelProxy.ts ×5tunnelProxy.ts ×4 · 20 introduced LOCtunnelProxy.ts ×4tunnelProxy.ts ×3 · 9 introduced LOCtunnelProxy.ts ×3tunnelProxy.ts ×10 · 66 introduced LOCtunnelProxy.ts ×10tunnelProxy.ts ×1 · 14 introduced LOCtunnelProxy.ts ×1tunnelProxy.ts ×1 · 1 introduced LOCtunnelProxy.ts ×1tunnelProxy.ts ×2 · 9 introduced LOCtunnelProxy.ts ×2tunnelProxy.ts ×2 · 6 introduced LOCtunnelProxy.ts ×2tunnelProxy.ts ×7 · 27 introduced LOCtunnelProxy.ts ×7tunnelProxy.ts ×1 · 2 introduced LOCtunnelProxy.ts ×1tunnelProxy.ts ×3 · 17 introduced LOCtunnelProxy.ts ×3tunnelProxy.ts ×1 · 4 introduced LOCtunnelProxy.ts ×1tunnelProxy.ts ×1 · 8 introduced LOCtunnelProxy.ts ×1tunnelProxy.ts ×5 · 11 introduced LOCtunnelProxy.ts ×5tunnelProxy.ts ×4 · 13 introduced LOCtunnelProxy.ts ×4tunnelProxy.ts ×1 · 2 introduced LOCtunnelProxy.ts ×1tunnelProxy.ts ×20 · 329 introduced LOCtunnelProxy.ts ×20selfSignedCert.ts ×1 · 2 introduced LOCselfSignedCert.ts ×1<base64> format|occurrence=1, selfSignedCert.test|title=selfSignedCert returns PEM-encoded key|occurrence=1, +7 · 0 introduced LOC<base64> format|occurren…selfSignedCert.ts ×6 · 233 introduced LOCselfSignedCert.ts ×6selfSignedCert.test|title=selfSignedCert certificate can be parsed by Node X509Certificate|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/selfSignedCert.test|title=selfSignedCert certificate can be parsed by Node X509Certificate|occurrence=1selfSignedCert.test|titl…selfSignedCert.test|title=selfSignedCert certificate has SAN with IP 127.0.0.1|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/selfSignedCert.test|title=selfSignedCert certificate has SAN with IP 127.0.0.1|occurrence=1selfSignedCert.test|titl…selfSignedCert.test|title=selfSignedCert certificate is not yet expired|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/selfSignedCert.test|title=selfSignedCert certificate is not yet expired|occurrence=1selfSignedCert.test|titl…selfSignedCert.test|title=selfSignedCert each invocation produces a unique certificate|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/selfSignedCert.test|title=selfSignedCert each invocation produces a unique certificate|occurrence=1selfSignedCert.test|titl…<base64> format|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/selfSignedCert.test|title=selfSignedCert fingerprint is in Electron sha256/<base64> format|occurrence=1<base64> format|occurren…selfSignedCert.test|title=selfSignedCert fingerprint matches the certificate DER|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/selfSignedCert.test|title=selfSignedCert fingerprint matches the certificate DER|occurrence=1selfSignedCert.test|titl…selfSignedCert.test|title=selfSignedCert key and certificate form a valid TLS pair|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/selfSignedCert.test|title=selfSignedCert key and certificate form a valid TLS pair|occurrence=1selfSignedCert.test|titl…selfSignedCert.test|title=selfSignedCert produces canonical DER INTEGER serials across many runs|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/selfSignedCert.test|title=selfSignedCert produces canonical DER INTEGER serials across many runs|occurrence=1selfSignedCert.test|titl…selfSignedCert.test|title=selfSignedCert returns PEM-encoded certificate|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/selfSignedCert.test|title=selfSignedCert returns PEM-encoded certificate|occurrence=1selfSignedCert.test|titl…selfSignedCert.test|title=selfSignedCert returns PEM-encoded key|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/selfSignedCert.test|title=selfSignedCert returns PEM-encoded key|occurrence=1selfSignedCert.test|titl…tunnelProxy.test|title=TunnelProxy CONNECT establishes a tunnel to the target|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy CONNECT establishes a tunnel to the target|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy CONNECT rejects invalid port 0|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy CONNECT rejects invalid port 0|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy CONNECT rejects port > 65535|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy CONNECT rejects port > 65535|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy a reset on a pooled tunnel socket does not escalate to an uncaught exception|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy a reset on a pooled tunnel socket does not escalate to an uncaught exception|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy dispose shuts down the server|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy dispose shuts down the server|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy dispose synchronously destroys the remote tunnel socket|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy dispose synchronously destroys the remote tunnel socket|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy dispose terminates CONNECT sockets stuck waiting for the upstream tunnel|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy dispose terminates CONNECT sockets stuck waiting for the upstream tunnel|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy dispose terminates active CONNECT tunnels|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy dispose terminates active CONNECT tunnels|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy dispose terminates idle HTTPS keep-alive connections|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy dispose terminates idle HTTPS keep-alive connections|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy drainConnectionPool destroys pooled tunnel sockets|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy drainConnectionPool destroys pooled tunnel sockets|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy fails the request when the tunnel connection fails|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy fails the request when the tunnel connection fails|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy forwards authenticated HTTP GET to target|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy forwards authenticated HTTP GET to target|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy forwards authenticated HTTP POST to target|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy forwards authenticated HTTP POST to target|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy managed (non-NodeSocket) transport CONNECT tunnels bidirectional data through a managed socket|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy managed (non-NodeSocket) transport CONNECT tunnels bidirectional data through a managed socket|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy managed (non-NodeSocket) transport dispose disposes the managed remote socket via the adapter|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy managed (non-NodeSocket) transport dispose disposes the managed remote socket via the adapter|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy managed (non-NodeSocket) transport forwards an authenticated HTTP GET through a managed socket|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy managed (non-NodeSocket) transport forwards an authenticated HTTP GET through a managed socket|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy rejects CONNECT without credentials (407)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy rejects CONNECT without credentials (407)|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy rejects plain HTTP request without credentials (407)|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy rejects plain HTTP request without credentials (407)|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy returns 400 for malformed URL|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy returns 400 for malformed URL|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy reuses tunnel socket for multiple requests to the same host|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy reuses tunnel socket for multiple requests to the same host|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy server uses TLS|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy server uses TLS|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy start returns a valid ITunnelProxyInfo|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy start returns a valid ITunnelProxyInfo|occurrence=1tunnelProxy.test|title=T…tunnelProxy.test|title=TunnelProxy strips hop-by-hop headers from forwarded request|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/tunnel/test/node/tunnelProxy.test|title=TunnelProxy strips hop-by-hop headers from forwarded request|occurrence=1tunnelProxy.test|title=T…Focused file · src/vs/platform/tunnel/node/selfSignedCert.ts · 251 LOCnode/selfSignedCert.ts

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- selfSignedCert.ts ×6
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > /**
7 > * Result of generating a self-signed certificate.
8 > */
9 > export interface ISelfSignedCert {
10 > /** PEM-encoded private key. */
11 > key: string;
12 > /** PEM-encoded X.509 certificate. */
13 > cert: string;
14 > /** SHA-256 fingerprint in Electron's format: `sha256/<base64>`. */
15 > fingerprint: string;
16 > }
17 >
18 > /**
19 > * Generate a self-signed ECDSA (P-256) certificate for `127.0.0.1` using
20 > * only Node's built-in `crypto` module. The certificate is valid for one
21 > * year from the current time.
22 > *
23 > * The raw ASN.1/DER construction avoids external dependencies. Only a
24 > * minimal X.509 v3 certificate is produced — just enough for TLS on the
25 > * loopback interface with certificate pinning.
26 > *
27 > * **Security note:** this certificate is a defence-in-depth measure for a
28 > * proxy that is already bound exclusively to `127.0.0.1`. TLS prevents
29 > * other local processes from passively sniffing tunnel traffic and the
30 > * pinned fingerprint stops active MITM on loopback. If certificate
31 > * generation fails the proxy simply will not start — the failure is
32 > * non-critical to the overall application.
33 > *
34 > * Do not rely on this certificate for security-critical scenarios.
35 > */
36 > export async function generateSelfSignedCert(): Promise<ISelfSignedCert> {
37 > const crypto = await import('crypto');
38 >
39 > const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {
40 > namedCurve: 'prime256v1',
41 > publicKeyEncoding: { type: 'spki', format: 'pem' },
42 > privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
43 > });
44 >
45 > const cert = createSelfSignedCertPem(crypto, privateKey, publicKey);
46 >
47 > // Compute SHA-256 fingerprint in Electron's format: "sha256/<base64>"
48 > const certDer = pemToDer(cert);
49 > const hash = crypto.createHash('sha256').update(certDer).digest('base64');
50 > const fingerprint = `sha256/${hash}`;
51 >
52 > return { key: privateKey, cert, fingerprint };
53 > }
54 >
55 > /**
56 > * Build a minimal self-signed X.509 v3 certificate in DER, then
57 > * PEM-encode it. Uses raw ASN.1 construction to avoid external
58 > * dependencies.
59 > */
60 > function createSelfSignedCertPem(
61 > crypto: typeof import('crypto'),
62 > privateKeyPem: string,
63 > publicKeyPem: string,
64 > ): string {
65 > // Parse the SPKI public key from PEM
66 > const spkiDer = pemToDer(publicKeyPem);
67 >
68 > // Build the TBS (To Be Signed) certificate
69 > const serial = crypto.randomBytes(8);
70 > // Ensure serial is positive (clear high bit)
71 > serial[0] &= 0x7f;
72 >
73 > const now = new Date();
74 > const notAfter = new Date(now);
75 > notAfter.setFullYear(now.getFullYear() + 1);
76 >
77 > const cnOid = derOid(Buffer.from([0x55, 0x04, 0x03])); // 2.5.4.3
78 >
79 > const issuerAndSubject = derSequence([
80 > derSet([
81 > derSequence([
82 > cnOid,
83 > derUtf8String('TunnelProxy'),
84 > ]),
85 > ]),
86 > ]);
87 >
88 > // Validity
89 > const validity = derSequence([
90 > derTime(now),
91 > derTime(notAfter),
92 > ]);
93 >
94 > // Version v3 [0] EXPLICIT INTEGER 2
95 > const version = Buffer.from([0xa0, 0x03, 0x02, 0x01, 0x02]);
96 >
97 > // Serial number
98 > const serialNumber = derInteger(serial);
99 >
100 > // Signature algorithm: ecdsa-with-SHA256 (1.2.840.10045.4.3.2)
101 > const sigAlgOidBytes = Buffer.from([0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02]);
102 > const sigAlg = derSequence([derOid(sigAlgOidBytes)]);
103 >
104 > // Extensions [3] EXPLICIT SEQUENCE — SAN with IP 127.0.0.1
105 > const sanExtension = buildSanExtension();
106 > const extensions = Buffer.concat([
107 > Buffer.from([0xa3]),
108 > derLengthPrefix(derSequence([sanExtension])),
109 > ]);
110 >
111 > // TBSCertificate
112 > const tbs = derSequence([
113 > version,
114 > serialNumber,
115 > sigAlg,
116 > issuerAndSubject,
117 > validity,
118 > issuerAndSubject,
119 > spkiDer,
120 > extensions,
121 > ]);
122 >
123 > // Sign the TBS
124 > const signer = crypto.createSign('SHA256');
125 > signer.update(tbs);
126 > const signature = signer.sign(privateKeyPem);
127 >
128 > // Wrap signature as BIT STRING
129 > const sigBitString = Buffer.concat([
130 > Buffer.from([0x03]),
131 > derLength(signature.length + 1),
132 > Buffer.from([0x00]), // no unused bits
133 > signature,
134 > ]);
135 >
136 > // Full certificate
137 > const certDer = derSequence([tbs, sigAlg, sigBitString]);
138 >
139 > // PEM encode
140 > const b64 = certDer.toString('base64');
141 > const lines: string[] = [];
142 > for (let i = 0; i < b64.length; i += 64) {
143 > lines.push(b64.substring(i, i + 64));
144 > }
145 > return `-----BEGIN CERTIFICATE-----\n${lines.join('\n')}\n-----END CERTIFICATE-----\n`;
146 > }
147 >
148 > /** Build a SAN extension with IP:127.0.0.1 */
149 > function buildSanExtension(): Buffer {
150 > // Extension OID: 2.5.29.17 (subjectAltName)
151 > const sanOid = derOid(Buffer.from([0x55, 0x1d, 0x11]));
152 >
153 > // GeneralName: iPAddress [7] 127.0.0.1
154 > const ipBytes = Buffer.from([0x87, 0x04, 0x7f, 0x00, 0x00, 0x01]);
155 >
156 > const sanValue = derOctetString(derSequence([ipBytes]));
157 >
158 > return derSequence([sanOid, sanValue]);
159 > }
160 >
161 > // #region ASN.1 DER helpers
162 >
163 > function pemToDer(pem: string): Buffer {
164 > const b64 = pem.replace(/-----[A-Z ]+-----/g, '').replace(/\s/g, '');
165 > return Buffer.from(b64, 'base64');
166 > }
167 >
168 > function derLength(length: number): Buffer {
169 > if (length < 0x80) {
170 > return Buffer.from([length]);
171 > } else if (length < 0x100) {
172 > return Buffer.from([0x81, length]);
173 > } else if (length < 0x10000) {
174 > return Buffer.from([0x82, (length >> 8) & 0xff, length & 0xff]);
175 > } else if (length < 0x1000000) {
176 return Buffer.from([0x83, (length >> 16) & 0xff, (length >> 8) & 0xff, length & 0xff]);
177 } else {
178 // X.690 section 8.1.3 allows up to 0x7f length octets, but anything
179 // beyond 3 bytes (16 MiB) is well outside our use case and most
180 // likely indicates a bug. Fail loudly rather than silently emit
181 // a truncated, malformed length.
182 throw new Error(`derLength: value too large (${length})`);
183 }
185 >
186 > function derLengthPrefix(content: Buffer): Buffer {
187 > return Buffer.concat([derLength(content.length), content]);
188 > }
189 >
190 > function derSequence(items: Buffer[]): Buffer {
191 > const content = Buffer.concat(items);
192 > return Buffer.concat([Buffer.from([0x30]), derLength(content.length), content]);
193 > }
194 >
195 > function derSet(items: Buffer[]): Buffer {
196 > const content = Buffer.concat(items);
197 > return Buffer.concat([Buffer.from([0x31]), derLength(content.length), content]);
198 > }
199 >
200 > function derInteger(value: Buffer): Buffer {
201 > // Canonical DER INTEGER encoding (X.690 section 8.3.2): the contents octets
202 > // must use the smallest number of octets. Strip leading 0x00 bytes
203 > // that are not needed to keep the high bit unset, then prepend a
204 > // single 0x00 if the high bit is set (to keep the value positive).
205 > // INTEGER value MUST contain at least one octet.
206 > if (value.length === 0) {
207 throw new Error('derInteger: value must be non-empty');
208 }
209 > let start = 0; selfSignedCert.ts ×6
210 > while (start < value.length - 1 && value[start] === 0 && (value[start + 1] & 0x80) === 0) {
211 > start++; selfSignedCert.ts ×1
212 > }
213 > let content = value.subarray(start); selfSignedCert.ts ×6
214 > if (content[0] & 0x80) {
215 content = Buffer.concat([Buffer.from([0x00]), content]);
216 }
217 > return Buffer.concat([Buffer.from([0x02]), derLength(content.length), content]); selfSignedCert.ts ×6
218 > }
219 >
220 > function derOid(value: Buffer): Buffer {
221 > return Buffer.concat([Buffer.from([0x06]), derLength(value.length), value]);
222 > }
223 >
224 > function derUtf8String(str: string): Buffer {
225 > const content = Buffer.from(str, 'utf8');
226 > return Buffer.concat([Buffer.from([0x0c]), derLength(content.length), content]);
227 > }
228 >
229 > function derOctetString(content: Buffer): Buffer {
230 > return Buffer.concat([Buffer.from([0x04]), derLength(content.length), content]);
231 > }
232 >
233 > function derTime(date: Date): Buffer {
234 > // RFC 5280 section 4.1.2.5: CAs MUST encode times through 2049 as UTCTime
235 > // (YY...) and times from 2050 onward as GeneralizedTime (YYYY...).
236 > // UTCTime two-digit years 50-99 are interpreted as 1950-1999 and
237 > // 00-49 as 2000-2049, so a UTCTime for 2050 would be misread as 1950.
238 > const iso = date.toISOString().replace(/[-:T]/g, '');
239 > const year = date.getUTCFullYear();
240 > if (year >= 1950 && year < 2050) {
241 > // UTCTime: YYMMDDHHMMSSZ
242 > const content = Buffer.from(iso.substring(2, 14) + 'Z', 'ascii');
243 > return Buffer.concat([Buffer.from([0x17]), derLength(content.length), content]);
244 > } else {
245 // GeneralizedTime: YYYYMMDDHHMMSSZ
246 const content = Buffer.from(iso.substring(0, 14) + 'Z', 'ascii');
247 return Buffer.concat([Buffer.from([0x18]), derLength(content.length), content]);
248 }
250 >
251 > // #endregion