@gjsify/tls 0.1.0
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/README.md +27 -0
- package/lib/esm/index.js +377 -0
- package/lib/types/index.d.ts +121 -0
- package/package.json +44 -0
- package/src/index.spec.ts +674 -0
- package/src/index.ts +510 -0
- package/src/test.mts +6 -0
- package/tsconfig.json +31 -0
- package/tsconfig.tsbuildinfo +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @gjsify/tls
|
|
2
|
+
|
|
3
|
+
GJS partial implementation of the Node.js `tls` module using Gio.TlsClientConnection.
|
|
4
|
+
|
|
5
|
+
Part of the [gjsify](https://github.com/gjsify/gjsify) project — Node.js and Web APIs for GJS (GNOME JavaScript).
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @gjsify/tls
|
|
11
|
+
# or
|
|
12
|
+
yarn add @gjsify/tls
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { TLSSocket, connect } from '@gjsify/tls';
|
|
19
|
+
|
|
20
|
+
const socket = connect({ host: 'example.com', port: 443 }, () => {
|
|
21
|
+
console.log('Connected');
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## License
|
|
26
|
+
|
|
27
|
+
MIT
|
package/lib/esm/index.js
ADDED
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import Gio from "@girs/gio-2.0";
|
|
2
|
+
import GLib from "@girs/glib-2.0";
|
|
3
|
+
import { Socket, Server } from "node:net";
|
|
4
|
+
import { createNodeError, deferEmit } from "@gjsify/utils";
|
|
5
|
+
const DEFAULT_MIN_VERSION = "TLSv1.2";
|
|
6
|
+
const DEFAULT_MAX_VERSION = "TLSv1.3";
|
|
7
|
+
const DEFAULT_CIPHERS = "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384";
|
|
8
|
+
function getCiphers() {
|
|
9
|
+
return [
|
|
10
|
+
"aes-128-gcm",
|
|
11
|
+
"aes-256-gcm",
|
|
12
|
+
"chacha20-poly1305",
|
|
13
|
+
"aes-128-cbc",
|
|
14
|
+
"aes-256-cbc"
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
function unfqdn(host) {
|
|
18
|
+
return host.endsWith(".") ? host.slice(0, -1) : host;
|
|
19
|
+
}
|
|
20
|
+
function splitHost(host) {
|
|
21
|
+
return unfqdn(host).toLowerCase().split(".");
|
|
22
|
+
}
|
|
23
|
+
function checkWildcard(hostParts, pattern) {
|
|
24
|
+
const patParts = splitHost(pattern);
|
|
25
|
+
if (patParts.length !== hostParts.length) return false;
|
|
26
|
+
if (patParts[0] === "*") {
|
|
27
|
+
return patParts.slice(1).join(".") === hostParts.slice(1).join(".");
|
|
28
|
+
}
|
|
29
|
+
return patParts.every((p, i) => p === hostParts[i]);
|
|
30
|
+
}
|
|
31
|
+
function checkServerIdentity(hostname, cert) {
|
|
32
|
+
const subject = cert.subject;
|
|
33
|
+
const altNames = cert.subjectaltname;
|
|
34
|
+
const dnsNames = [];
|
|
35
|
+
const ips = [];
|
|
36
|
+
hostname = String(hostname);
|
|
37
|
+
if (altNames) {
|
|
38
|
+
const parts = altNames.split(", ");
|
|
39
|
+
for (const name of parts) {
|
|
40
|
+
if (name.startsWith("DNS:")) {
|
|
41
|
+
dnsNames.push(name.slice(4));
|
|
42
|
+
} else if (name.startsWith("IP Address:")) {
|
|
43
|
+
ips.push(name.slice(11).trim());
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
let valid = false;
|
|
48
|
+
let reason = "Unknown reason";
|
|
49
|
+
hostname = unfqdn(hostname);
|
|
50
|
+
const isIPv4 = /^(\d{1,3}\.){3}\d{1,3}$/.test(hostname);
|
|
51
|
+
const isIPv6 = hostname.includes(":");
|
|
52
|
+
if (isIPv4 || isIPv6) {
|
|
53
|
+
valid = ips.some((ip) => ip.toLowerCase() === hostname.toLowerCase());
|
|
54
|
+
if (!valid)
|
|
55
|
+
reason = `IP: ${hostname} is not in the cert's list: ${ips.join(", ")}`;
|
|
56
|
+
} else if (dnsNames.length > 0 || subject?.CN) {
|
|
57
|
+
const hostParts = splitHost(hostname);
|
|
58
|
+
if (dnsNames.length > 0) {
|
|
59
|
+
valid = dnsNames.some((pattern) => checkWildcard(hostParts, pattern));
|
|
60
|
+
if (!valid)
|
|
61
|
+
reason = `Host: ${hostname}. is not in the cert's altnames: ${altNames}`;
|
|
62
|
+
} else {
|
|
63
|
+
const cn = subject.CN;
|
|
64
|
+
if (Array.isArray(cn)) {
|
|
65
|
+
valid = cn.some((c) => checkWildcard(hostParts, c));
|
|
66
|
+
} else if (cn) {
|
|
67
|
+
valid = checkWildcard(hostParts, cn);
|
|
68
|
+
}
|
|
69
|
+
if (!valid)
|
|
70
|
+
reason = `Host: ${hostname}. is not cert's CN: ${cn}`;
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
reason = "Cert does not contain a DNS name";
|
|
74
|
+
}
|
|
75
|
+
if (!valid) {
|
|
76
|
+
const err = new Error(reason);
|
|
77
|
+
err.reason = reason;
|
|
78
|
+
err.host = hostname;
|
|
79
|
+
err.cert = cert;
|
|
80
|
+
return err;
|
|
81
|
+
}
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
class TLSSocket extends Socket {
|
|
85
|
+
encrypted = true;
|
|
86
|
+
authorized = false;
|
|
87
|
+
authorizationError;
|
|
88
|
+
alpnProtocol = false;
|
|
89
|
+
/** @internal */
|
|
90
|
+
_tlsConnection = null;
|
|
91
|
+
constructor(socket, options) {
|
|
92
|
+
super();
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* @internal Wire the TLS connection's I/O streams into this socket
|
|
96
|
+
* so that read/write operations go through the encrypted channel.
|
|
97
|
+
*/
|
|
98
|
+
_setupTlsStreams(tlsConn) {
|
|
99
|
+
this._tlsConnection = tlsConn;
|
|
100
|
+
this._inputStream = tlsConn.get_input_stream();
|
|
101
|
+
this._outputStream = tlsConn.get_output_stream();
|
|
102
|
+
this._connection = tlsConn;
|
|
103
|
+
}
|
|
104
|
+
/** Get the peer certificate info. */
|
|
105
|
+
getPeerCertificate(_detailed) {
|
|
106
|
+
if (!this._tlsConnection) return {};
|
|
107
|
+
try {
|
|
108
|
+
const cert = this._tlsConnection.get_peer_certificate();
|
|
109
|
+
if (!cert) return {};
|
|
110
|
+
return {
|
|
111
|
+
subject: {},
|
|
112
|
+
issuer: {},
|
|
113
|
+
valid_from: "",
|
|
114
|
+
valid_to: ""
|
|
115
|
+
};
|
|
116
|
+
} catch {
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** Get the negotiated TLS protocol version. */
|
|
121
|
+
getProtocol() {
|
|
122
|
+
if (!this._tlsConnection) return null;
|
|
123
|
+
try {
|
|
124
|
+
const proto = this._tlsConnection.get_protocol_version();
|
|
125
|
+
switch (proto) {
|
|
126
|
+
case Gio.TlsProtocolVersion.TLS_1_0:
|
|
127
|
+
return "TLSv1";
|
|
128
|
+
case Gio.TlsProtocolVersion.TLS_1_1:
|
|
129
|
+
return "TLSv1.1";
|
|
130
|
+
case Gio.TlsProtocolVersion.TLS_1_2:
|
|
131
|
+
return "TLSv1.2";
|
|
132
|
+
case Gio.TlsProtocolVersion.TLS_1_3:
|
|
133
|
+
return "TLSv1.3";
|
|
134
|
+
default:
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
} catch {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/** Get the cipher info. */
|
|
142
|
+
getCipher() {
|
|
143
|
+
if (!this._tlsConnection) return null;
|
|
144
|
+
try {
|
|
145
|
+
const name = this._tlsConnection.get_ciphersuite_name();
|
|
146
|
+
return { name: name || "unknown", version: this.getProtocol() || "unknown" };
|
|
147
|
+
} catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/** Get the negotiated ALPN protocol. */
|
|
152
|
+
getAlpnProtocol() {
|
|
153
|
+
if (!this._tlsConnection) return false;
|
|
154
|
+
try {
|
|
155
|
+
const proto = this._tlsConnection.get_negotiated_protocol();
|
|
156
|
+
return proto || false;
|
|
157
|
+
} catch {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function connect(options, callback) {
|
|
163
|
+
const socket = new TLSSocket(void 0, options);
|
|
164
|
+
if (callback) {
|
|
165
|
+
socket.once("secureConnect", callback);
|
|
166
|
+
}
|
|
167
|
+
const port = options.port || 443;
|
|
168
|
+
const host = options.host || "localhost";
|
|
169
|
+
const servername = options.servername || host;
|
|
170
|
+
const rejectUnauthorized = options.rejectUnauthorized !== false;
|
|
171
|
+
socket.once("connect", () => {
|
|
172
|
+
const rawConnection = socket._connection;
|
|
173
|
+
if (!rawConnection) {
|
|
174
|
+
socket.destroy(new Error("No underlying connection for TLS upgrade"));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const connectable = Gio.NetworkAddress.new(servername, port);
|
|
179
|
+
const tlsConn = Gio.TlsClientConnection.new(
|
|
180
|
+
rawConnection,
|
|
181
|
+
connectable
|
|
182
|
+
);
|
|
183
|
+
tlsConn.set_server_identity(connectable);
|
|
184
|
+
if (options.ALPNProtocols && options.ALPNProtocols.length > 0) {
|
|
185
|
+
try {
|
|
186
|
+
tlsConn.set_advertised_protocols(options.ALPNProtocols);
|
|
187
|
+
} catch {
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (!rejectUnauthorized) {
|
|
191
|
+
tlsConn.connect(
|
|
192
|
+
"accept-certificate",
|
|
193
|
+
() => true
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
const cancellable = new Gio.Cancellable();
|
|
197
|
+
tlsConn.handshake_async(
|
|
198
|
+
GLib.PRIORITY_DEFAULT,
|
|
199
|
+
cancellable,
|
|
200
|
+
(_source, asyncResult) => {
|
|
201
|
+
try {
|
|
202
|
+
tlsConn.handshake_finish(asyncResult);
|
|
203
|
+
socket.authorized = true;
|
|
204
|
+
socket._setupTlsStreams(tlsConn);
|
|
205
|
+
socket.alpnProtocol = socket.getAlpnProtocol();
|
|
206
|
+
socket._reading = false;
|
|
207
|
+
socket._startReading();
|
|
208
|
+
socket.emit("secureConnect");
|
|
209
|
+
} catch (err) {
|
|
210
|
+
socket.authorized = false;
|
|
211
|
+
socket.authorizationError = err instanceof Error ? err.message : String(err);
|
|
212
|
+
if (rejectUnauthorized) {
|
|
213
|
+
socket.destroy(err instanceof Error ? err : new Error(String(err)));
|
|
214
|
+
} else {
|
|
215
|
+
socket._setupTlsStreams(tlsConn);
|
|
216
|
+
socket.emit("secureConnect");
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
} catch (err) {
|
|
222
|
+
socket.destroy(err instanceof Error ? err : new Error(String(err)));
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
socket.connect({ port, host });
|
|
226
|
+
return socket;
|
|
227
|
+
}
|
|
228
|
+
function createSecureContext(options) {
|
|
229
|
+
return { context: options || {} };
|
|
230
|
+
}
|
|
231
|
+
const rootCertificates = [];
|
|
232
|
+
function buildGioCertificate(cert, key) {
|
|
233
|
+
const certStr = Array.isArray(cert) ? cert.map((c) => typeof c === "string" ? c : c.toString("utf-8")).join("\n") : typeof cert === "string" ? cert : cert.toString("utf-8");
|
|
234
|
+
const keyStr = key ? Array.isArray(key) ? key.map((k) => typeof k === "string" ? k : k.toString("utf-8")).join("\n") : typeof key === "string" ? key : key.toString("utf-8") : "";
|
|
235
|
+
const pem = keyStr ? `${certStr}
|
|
236
|
+
${keyStr}` : certStr;
|
|
237
|
+
return Gio.TlsCertificate.new_from_pem(pem, pem.length);
|
|
238
|
+
}
|
|
239
|
+
class TLSServer extends Server {
|
|
240
|
+
_tlsCertificate = null;
|
|
241
|
+
_tlsOptions;
|
|
242
|
+
_sniContexts = /* @__PURE__ */ new Map();
|
|
243
|
+
constructor(options, secureConnectionListener) {
|
|
244
|
+
super();
|
|
245
|
+
this._tlsOptions = options || {};
|
|
246
|
+
if (secureConnectionListener) {
|
|
247
|
+
this.on("secureConnection", secureConnectionListener);
|
|
248
|
+
}
|
|
249
|
+
if (this._tlsOptions.cert) {
|
|
250
|
+
try {
|
|
251
|
+
this._tlsCertificate = buildGioCertificate(this._tlsOptions.cert, this._tlsOptions.key);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
deferEmit(this, "error", createNodeError(err, "createServer", {}));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Add a context for SNI (Server Name Indication).
|
|
259
|
+
*/
|
|
260
|
+
addContext(hostname, context) {
|
|
261
|
+
if (context.cert) {
|
|
262
|
+
try {
|
|
263
|
+
const cert = buildGioCertificate(context.cert, context.key);
|
|
264
|
+
this._sniContexts.set(hostname, cert);
|
|
265
|
+
} catch (err) {
|
|
266
|
+
this.emit("error", createNodeError(err, "addContext", {}));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
listen(...args) {
|
|
271
|
+
this.on("connection", (socket) => {
|
|
272
|
+
this._upgradeTls(socket);
|
|
273
|
+
});
|
|
274
|
+
return super.listen(...args);
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Upgrade a raw TCP socket to TLS using Gio.TlsServerConnection.
|
|
278
|
+
*/
|
|
279
|
+
_upgradeTls(socket) {
|
|
280
|
+
const rawConnection = socket._connection;
|
|
281
|
+
if (!rawConnection) {
|
|
282
|
+
const err = new Error("Cannot upgrade socket: no underlying connection");
|
|
283
|
+
this.emit("tlsClientError", err, socket);
|
|
284
|
+
socket.destroy();
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (!this._tlsCertificate) {
|
|
288
|
+
const err = new Error("TLS server has no certificate configured");
|
|
289
|
+
this.emit("tlsClientError", err, socket);
|
|
290
|
+
socket.destroy();
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
const tlsConn = Gio.TlsServerConnection.new(
|
|
295
|
+
rawConnection,
|
|
296
|
+
this._tlsCertificate
|
|
297
|
+
);
|
|
298
|
+
if (this._tlsOptions.requestCert) {
|
|
299
|
+
tlsConn.authenticationMode = this._tlsOptions.rejectUnauthorized !== false ? Gio.TlsAuthenticationMode.REQUIRED : Gio.TlsAuthenticationMode.REQUESTED;
|
|
300
|
+
} else {
|
|
301
|
+
tlsConn.authenticationMode = Gio.TlsAuthenticationMode.NONE;
|
|
302
|
+
}
|
|
303
|
+
if (this._tlsOptions.rejectUnauthorized === false) {
|
|
304
|
+
tlsConn.connect(
|
|
305
|
+
"accept-certificate",
|
|
306
|
+
() => true
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
if (this._tlsOptions.ALPNProtocols && this._tlsOptions.ALPNProtocols.length > 0) {
|
|
310
|
+
try {
|
|
311
|
+
tlsConn.set_advertised_protocols(this._tlsOptions.ALPNProtocols);
|
|
312
|
+
} catch {
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
const cancellable = new Gio.Cancellable();
|
|
316
|
+
tlsConn.handshake_async(
|
|
317
|
+
GLib.PRIORITY_DEFAULT,
|
|
318
|
+
cancellable,
|
|
319
|
+
(_source, asyncResult) => {
|
|
320
|
+
try {
|
|
321
|
+
tlsConn.handshake_finish(asyncResult);
|
|
322
|
+
const tlsSocket = new TLSSocket();
|
|
323
|
+
tlsSocket.encrypted = true;
|
|
324
|
+
tlsSocket.authorized = true;
|
|
325
|
+
tlsSocket._setupTlsStreams(tlsConn);
|
|
326
|
+
tlsSocket.alpnProtocol = tlsSocket.getAlpnProtocol();
|
|
327
|
+
tlsSocket._startReading();
|
|
328
|
+
this.emit("secureConnection", tlsSocket);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
const nodeErr = createNodeError(err, "handshake", {});
|
|
331
|
+
this.emit("tlsClientError", nodeErr, socket);
|
|
332
|
+
socket.destroy();
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
);
|
|
336
|
+
} catch (err) {
|
|
337
|
+
const nodeErr = createNodeError(err, "tls_wrap", {});
|
|
338
|
+
this.emit("tlsClientError", nodeErr, socket);
|
|
339
|
+
socket.destroy();
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function createServer(optionsOrListener, secureConnectionListener) {
|
|
344
|
+
if (typeof optionsOrListener === "function") {
|
|
345
|
+
return new TLSServer(void 0, optionsOrListener);
|
|
346
|
+
}
|
|
347
|
+
return new TLSServer(optionsOrListener, secureConnectionListener);
|
|
348
|
+
}
|
|
349
|
+
var index_default = {
|
|
350
|
+
TLSSocket,
|
|
351
|
+
TLSServer,
|
|
352
|
+
Server: TLSServer,
|
|
353
|
+
connect,
|
|
354
|
+
createServer,
|
|
355
|
+
createSecureContext,
|
|
356
|
+
checkServerIdentity,
|
|
357
|
+
getCiphers,
|
|
358
|
+
rootCertificates,
|
|
359
|
+
DEFAULT_MIN_VERSION,
|
|
360
|
+
DEFAULT_MAX_VERSION,
|
|
361
|
+
DEFAULT_CIPHERS
|
|
362
|
+
};
|
|
363
|
+
export {
|
|
364
|
+
DEFAULT_CIPHERS,
|
|
365
|
+
DEFAULT_MAX_VERSION,
|
|
366
|
+
DEFAULT_MIN_VERSION,
|
|
367
|
+
TLSServer as Server,
|
|
368
|
+
TLSServer,
|
|
369
|
+
TLSSocket,
|
|
370
|
+
checkServerIdentity,
|
|
371
|
+
connect,
|
|
372
|
+
createSecureContext,
|
|
373
|
+
createServer,
|
|
374
|
+
index_default as default,
|
|
375
|
+
getCiphers,
|
|
376
|
+
rootCertificates
|
|
377
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import Gio from '@girs/gio-2.0';
|
|
2
|
+
import { Socket, Server } from 'node:net';
|
|
3
|
+
export declare const DEFAULT_MIN_VERSION = "TLSv1.2";
|
|
4
|
+
export declare const DEFAULT_MAX_VERSION = "TLSv1.3";
|
|
5
|
+
export declare const DEFAULT_CIPHERS = "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384";
|
|
6
|
+
/** Returns a list of supported TLS cipher names (subset; implementation-defined). */
|
|
7
|
+
export declare function getCiphers(): string[];
|
|
8
|
+
export interface PeerCertificate {
|
|
9
|
+
subject?: {
|
|
10
|
+
CN?: string | string[];
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
};
|
|
13
|
+
subjectaltname?: string;
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Verifies that the certificate `cert` is valid for `hostname`.
|
|
18
|
+
* Returns an Error if the check fails, or undefined on success.
|
|
19
|
+
*
|
|
20
|
+
* Reference: Node.js lib/tls.js exports.checkServerIdentity
|
|
21
|
+
*/
|
|
22
|
+
export declare function checkServerIdentity(hostname: string, cert: PeerCertificate): Error | undefined;
|
|
23
|
+
export interface SecureContextOptions {
|
|
24
|
+
ca?: string | Buffer | Array<string | Buffer>;
|
|
25
|
+
cert?: string | Buffer | Array<string | Buffer>;
|
|
26
|
+
key?: string | Buffer | Array<string | Buffer>;
|
|
27
|
+
rejectUnauthorized?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface TlsConnectOptions extends SecureContextOptions {
|
|
30
|
+
host?: string;
|
|
31
|
+
port?: number;
|
|
32
|
+
socket?: Socket;
|
|
33
|
+
servername?: string;
|
|
34
|
+
ALPNProtocols?: string[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* TLSSocket wraps a net.Socket with TLS via Gio.TlsConnection.
|
|
38
|
+
*/
|
|
39
|
+
export declare class TLSSocket extends Socket {
|
|
40
|
+
encrypted: boolean;
|
|
41
|
+
authorized: boolean;
|
|
42
|
+
authorizationError?: string;
|
|
43
|
+
alpnProtocol: string | false;
|
|
44
|
+
/** @internal */
|
|
45
|
+
_tlsConnection: Gio.TlsConnection | null;
|
|
46
|
+
constructor(socket?: Socket, options?: SecureContextOptions);
|
|
47
|
+
/**
|
|
48
|
+
* @internal Wire the TLS connection's I/O streams into this socket
|
|
49
|
+
* so that read/write operations go through the encrypted channel.
|
|
50
|
+
*/
|
|
51
|
+
_setupTlsStreams(tlsConn: Gio.TlsConnection): void;
|
|
52
|
+
/** Get the peer certificate info. */
|
|
53
|
+
getPeerCertificate(_detailed?: boolean): any;
|
|
54
|
+
/** Get the negotiated TLS protocol version. */
|
|
55
|
+
getProtocol(): string | null;
|
|
56
|
+
/** Get the cipher info. */
|
|
57
|
+
getCipher(): {
|
|
58
|
+
name: string;
|
|
59
|
+
version: string;
|
|
60
|
+
} | null;
|
|
61
|
+
/** Get the negotiated ALPN protocol. */
|
|
62
|
+
getAlpnProtocol(): string | false;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create a TLS client connection.
|
|
66
|
+
*
|
|
67
|
+
* Connects via TCP first (using net.Socket.connect), then upgrades
|
|
68
|
+
* the connection to TLS using Gio.TlsClientConnection.
|
|
69
|
+
*/
|
|
70
|
+
export declare function connect(options: TlsConnectOptions, callback?: () => void): TLSSocket;
|
|
71
|
+
/**
|
|
72
|
+
* Create a TLS secure context.
|
|
73
|
+
*/
|
|
74
|
+
export declare function createSecureContext(options?: SecureContextOptions): {
|
|
75
|
+
context: any;
|
|
76
|
+
};
|
|
77
|
+
export declare const rootCertificates: string[];
|
|
78
|
+
export interface TlsServerOptions extends SecureContextOptions {
|
|
79
|
+
requestCert?: boolean;
|
|
80
|
+
rejectUnauthorized?: boolean;
|
|
81
|
+
ALPNProtocols?: string[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* TLSServer wraps a net.Server to accept TLS connections.
|
|
85
|
+
*/
|
|
86
|
+
export declare class TLSServer extends Server {
|
|
87
|
+
private _tlsCertificate;
|
|
88
|
+
private _tlsOptions;
|
|
89
|
+
private _sniContexts;
|
|
90
|
+
constructor(options?: TlsServerOptions, secureConnectionListener?: (socket: TLSSocket) => void);
|
|
91
|
+
/**
|
|
92
|
+
* Add a context for SNI (Server Name Indication).
|
|
93
|
+
*/
|
|
94
|
+
addContext(hostname: string, context: SecureContextOptions): void;
|
|
95
|
+
listen(...args: unknown[]): this;
|
|
96
|
+
/**
|
|
97
|
+
* Upgrade a raw TCP socket to TLS using Gio.TlsServerConnection.
|
|
98
|
+
*/
|
|
99
|
+
private _upgradeTls;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create a TLS server.
|
|
103
|
+
*/
|
|
104
|
+
export declare function createServer(options?: TlsServerOptions, secureConnectionListener?: (socket: TLSSocket) => void): TLSServer;
|
|
105
|
+
export declare function createServer(secureConnectionListener?: (socket: TLSSocket) => void): TLSServer;
|
|
106
|
+
export { TLSServer as Server };
|
|
107
|
+
declare const _default: {
|
|
108
|
+
TLSSocket: typeof TLSSocket;
|
|
109
|
+
TLSServer: typeof TLSServer;
|
|
110
|
+
Server: typeof TLSServer;
|
|
111
|
+
connect: typeof connect;
|
|
112
|
+
createServer: typeof createServer;
|
|
113
|
+
createSecureContext: typeof createSecureContext;
|
|
114
|
+
checkServerIdentity: typeof checkServerIdentity;
|
|
115
|
+
getCiphers: typeof getCiphers;
|
|
116
|
+
rootCertificates: string[];
|
|
117
|
+
DEFAULT_MIN_VERSION: string;
|
|
118
|
+
DEFAULT_MAX_VERSION: string;
|
|
119
|
+
DEFAULT_CIPHERS: string;
|
|
120
|
+
};
|
|
121
|
+
export default _default;
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gjsify/tls",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Node.js tls module for Gjs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"module": "lib/esm/index.js",
|
|
7
|
+
"types": "lib/types/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./lib/types/index.d.ts",
|
|
11
|
+
"default": "./lib/esm/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"clear": "rm -rf lib tsconfig.tsbuildinfo tsconfig.types.tsbuildinfo test.gjs.mjs test.node.mjs || exit 0",
|
|
16
|
+
"check": "tsc --noEmit",
|
|
17
|
+
"build": "yarn build:gjsify && yarn build:types",
|
|
18
|
+
"build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
|
|
19
|
+
"build:types": "tsc",
|
|
20
|
+
"build:test": "yarn build:test:gjs && yarn build:test:node",
|
|
21
|
+
"build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
|
|
22
|
+
"build:test:node": "gjsify build src/test.mts --app node --outfile test.node.mjs",
|
|
23
|
+
"test": "yarn build:gjsify && yarn build:test && yarn test:node && yarn test:gjs",
|
|
24
|
+
"test:gjs": "gjs -m test.gjs.mjs",
|
|
25
|
+
"test:node": "node test.node.mjs"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"gjs",
|
|
29
|
+
"node",
|
|
30
|
+
"tls"
|
|
31
|
+
],
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@gjsify/cli": "^0.1.0",
|
|
34
|
+
"@gjsify/unit": "^0.1.0",
|
|
35
|
+
"@types/node": "^25.5.0",
|
|
36
|
+
"typescript": "^6.0.2"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@girs/gio-2.0": "^2.88.0-4.0.0-beta.42",
|
|
40
|
+
"@girs/glib-2.0": "^2.88.0-4.0.0-beta.42",
|
|
41
|
+
"@gjsify/net": "^0.1.0",
|
|
42
|
+
"@gjsify/utils": "^0.1.0"
|
|
43
|
+
}
|
|
44
|
+
}
|