@gjsify/tls 0.3.21 → 0.4.3
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/_virtual/_rolldown/runtime.js +1 -0
- package/lib/esm/index.js +2 -3
- package/lib/types/cert.spec.d.ts +2 -0
- package/lib/types/index.d.ts +82 -27
- package/lib/types/tls.gjs.spec.d.ts +2 -0
- package/package.json +45 -42
- package/src/index.spec.ts +0 -674
- package/src/index.ts +0 -510
- package/src/test.mts +0 -6
- package/tsconfig.json +0 -29
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty,__name=(t,n)=>e(t,`name`,{value:n,configurable:!0});export{__name};
|
package/lib/esm/index.js
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import e from"@girs/gio-2.0";import t from"@girs/glib-2.0";import{Server as n,Socket as r}from"node:net";import{createNodeError as i,deferEmit as a}from"@gjsify/utils";const o=`TLSv1.2`,s=`TLSv1.3`,c=`TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384`;function getCiphers(){return[`aes-128-gcm`,`aes-256-gcm`,`chacha20-poly1305`,`aes-128-cbc`,`aes-256-cbc`]}function
|
|
2
|
-
`):typeof t==`string`?t:t.toString(`utf-8`),i=n?Array.isArray(n)?n.map(e=>typeof e==`string`?e:e.toString(`utf-8`)).join(`
|
|
3
|
-
`):typeof n==`string`?n:n.toString(`utf-8`):``,a=i?`${r}\n${i}`:r;return e.TlsCertificate.new_from_pem(a,a.length)}var TLSServer=class extends n{_tlsCertificate=null;_tlsOptions;_sniContexts=new Map;constructor(e,t){if(super(),this._tlsOptions=e||{},t&&this.on(`secureConnection`,t),this._tlsOptions.cert)try{this._tlsCertificate=buildGioCertificate(this._tlsOptions.cert,this._tlsOptions.key)}catch(e){a(this,`error`,i(e,`createServer`,{}))}}addContext(e,t){if(t.cert)try{let n=buildGioCertificate(t.cert,t.key);this._sniContexts.set(e,n)}catch(e){this.emit(`error`,i(e,`addContext`,{}))}}listen(...e){return this.on(`connection`,e=>{this._upgradeTls(e)}),super.listen(...e)}_upgradeTls(n){let r=n._connection;if(!r){let e=Error(`Cannot upgrade socket: no underlying connection`);this.emit(`tlsClientError`,e,n),n.destroy();return}if(!this._tlsCertificate){let e=Error(`TLS server has no certificate configured`);this.emit(`tlsClientError`,e,n),n.destroy();return}try{let a=e.TlsServerConnection.new(r,this._tlsCertificate);if(this._tlsOptions.requestCert?a.authenticationMode=this._tlsOptions.rejectUnauthorized===!1?e.TlsAuthenticationMode.REQUESTED:e.TlsAuthenticationMode.REQUIRED:a.authenticationMode=e.TlsAuthenticationMode.NONE,this._tlsOptions.rejectUnauthorized===!1&&a.connect(`accept-certificate`,()=>!0),this._tlsOptions.ALPNProtocols&&this._tlsOptions.ALPNProtocols.length>0)try{a.set_advertised_protocols(this._tlsOptions.ALPNProtocols)}catch{}let o=new e.Cancellable;a.handshake_async(t.PRIORITY_DEFAULT,o,(e,t)=>{try{a.handshake_finish(t);let e=new TLSSocket;e.encrypted=!0,e.authorized=!0,e._setupTlsStreams(a),e.alpnProtocol=e.getAlpnProtocol(),e._startReading(),this.emit(`secureConnection`,e)}catch(e){let t=i(e,`handshake`,{});this.emit(`tlsClientError`,t,n),n.destroy()}})}catch(e){let t=i(e,`tls_wrap`,{});this.emit(`tlsClientError`,t,n),n.destroy()}}};function createServer(e,t){return typeof e==`function`?new TLSServer(void 0,e):new TLSServer(e,t)}var u={TLSSocket,TLSServer,Server:TLSServer,connect,createServer,createSecureContext,checkServerIdentity,getCiphers,rootCertificates:l,DEFAULT_MIN_VERSION:o,DEFAULT_MAX_VERSION:s,DEFAULT_CIPHERS:c};export{c as DEFAULT_CIPHERS,s as DEFAULT_MAX_VERSION,o as DEFAULT_MIN_VERSION,TLSServer as Server,TLSServer,TLSSocket,checkServerIdentity,connect,createSecureContext,createServer,u as default,getCiphers,l as rootCertificates};
|
|
1
|
+
import"./_virtual/_rolldown/runtime.js";import e from"@girs/gio-2.0";import t from"@girs/glib-2.0";import{Server as n,Socket as r}from"node:net";import{createNodeError as i,deferEmit as a}from"@gjsify/utils";const o=`TLSv1.2`,s=`TLSv1.3`,c=`TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384`;function getCiphers(){return[`aes-128-gcm`,`aes-256-gcm`,`chacha20-poly1305`,`aes-128-cbc`,`aes-256-cbc`]}function pemToString(e){if(Array.isArray(e))return e.map(pemToString).join(`
|
|
2
|
+
`);if(typeof e==`string`)return e;if(e&&typeof e.toString==`function`)try{return e.toString(`utf-8`)}catch{return new TextDecoder(`utf-8`).decode(e)}return String(e)}function splitPemBlocks(e){let t=[],n=/-----BEGIN [^-]+-----[\s\S]*?-----END [^-]+-----/g,r;for(;(r=n.exec(e))!==null;)t.push(r[0]);return t}function buildGioCertificate(t,n){let r=pemToString(t),i=n?pemToString(n):``,a=i?`${r}\n${i}`:r;return e.TlsCertificate.new_from_pem(a,a.length)}function buildCaCertificates(t){let n=[];if(Array.isArray(t))for(let e of t)n.push(...splitPemBlocks(pemToString(e)));else n.push(...splitPemBlocks(pemToString(t)));let r=[];for(let t of n)try{r.push(e.TlsCertificate.new_from_pem(t,t.length))}catch{}return r}function parseDistinguishedName(e){if(!e)return{};let t={};for(let n of e.split(/,(?![^=]*=)/)){let e=n.indexOf(`=`);if(e<0)continue;let r=n.slice(0,e).trim(),i=n.slice(e+1).trim(),a=t[r];a===void 0?t[r]=i:Array.isArray(a)?a.push(i):t[r]=[a,i]}return t}function formatCertDate(e){if(!e)return``;try{return e.format(`%b %d %H:%M:%S %Y GMT`)??``}catch{return``}}function formatAltNames(e){let t=[];try{let n=e.get_dns_names();if(n)for(let e of n){let n=e.get_data();n&&t.push(`DNS:${new TextDecoder(`utf-8`).decode(n)}`)}}catch{}try{let n=e.get_ip_addresses();if(n)for(let e of n)t.push(`IP Address:${e.to_string()}`)}catch{}return t.join(`, `)}function fingerprintFromBytes(e,n){try{let r=new t.Checksum(n);r.update(e);let i=r.get_string();if(!i)return``;let a=[];for(let e=0;e<i.length;e+=2)a.push(i.slice(e,e+2).toUpperCase());return a.join(`:`)}catch{return``}}function pemToDer(e){let t=/-----BEGIN CERTIFICATE-----([\s\S]*?)-----END CERTIFICATE-----/.exec(e);if(!t)return new Uint8Array;let n=t[1].replace(/[\s\r\n]+/g,``);try{let e=globalThis.atob;if(!e)return new Uint8Array;let t=e(n),r=new Uint8Array(t.length);for(let e=0;e<t.length;e++)r[e]=t.charCodeAt(e);return r}catch{return new Uint8Array}}function tlsCertToPeerCert(e,n){let r={};try{r.subject=parseDistinguishedName(e.get_subject_name())}catch{}try{r.issuer=parseDistinguishedName(e.get_issuer_name())}catch{}r.subjectaltname=formatAltNames(e);try{r.valid_from=formatCertDate(e.get_not_valid_before()),r.valid_to=formatCertDate(e.get_not_valid_after())}catch{}try{let n=e,i=n.certificate_pem??n.certificatePem;if(i){let e=pemToDer(i);r.raw=e,r.fingerprint=fingerprintFromBytes(e,t.ChecksumType.SHA1),r.fingerprint256=fingerprintFromBytes(e,t.ChecksumType.SHA256)}}catch{}if(n)try{let t=e.get_issuer();t&&!t.is_same(e)?r.issuerCertificate=tlsCertToPeerCert(t,!0):t&&(r.issuerCertificate=r)}catch{}return r}function unfqdn(e){return e.endsWith(`.`)?e.slice(0,-1):e}function splitHost(e){return unfqdn(e).toLowerCase().split(`.`)}function isPrintableAscii(e){for(let t=0;t<e.length;t++){let n=e.charCodeAt(t);if(n<33||n>126)return!1}return!0}function checkHostMatch(e,t){if(!t)return!1;let n=splitHost(t);if(e.length!==n.length||n.includes(``)||!n.every(isPrintableAscii))return!1;for(let t=e.length-1;t>0;t--)if(e[t]!==n[t])return!1;let r=e[0],i=n[0],a=i.split(`*`,3);if(a.length===1||i.includes(`xn--`))return r===i;if(a.length>2||n.length<=2)return!1;let o=a[0],s=a[1];return!(o.length+s.length>r.length||!r.startsWith(o)||!r.endsWith(s))}function checkServerIdentity(e,t){let n=t.subject,r=t.subjectaltname,i=[],a=[];if(e=String(e),r){let e=r.split(`, `);for(let t of e)t.startsWith(`DNS:`)?i.push(t.slice(4)):t.startsWith(`IP Address:`)&&a.push(t.slice(11).trim())}let o=!1,s=`Unknown reason`;e=unfqdn(e);let c=/^(\d{1,3}\.){3}\d{1,3}$/.test(e),l=e.includes(`:`);if(c||l)o=a.some(t=>t.toLowerCase()===e.toLowerCase()),o||(s=`IP: ${e} is not in the cert's list: ${a.join(`, `)}`);else if(i.length>0||n?.CN){let t=splitHost(e);if(i.length>0)o=i.some(e=>checkHostMatch(t,e.trim())),o||(s=`Host: ${e}. is not in the cert's altnames: ${r}`);else{let r=n?.CN;Array.isArray(r)?o=r.some(e=>checkHostMatch(t,e)):r&&(o=checkHostMatch(t,r)),o||(s=`Host: ${e}. is not cert's CN: ${r}`)}}else s=`Cert does not contain a DNS name`;if(!o){let n=Error(s);return n.reason=s,n.host=e,n.cert=t,n.code=`ERR_TLS_CERT_ALTNAME_INVALID`,n}}function createSecureContext(e){let t=e??{},n=null;if(t.cert)try{n=buildGioCertificate(t.cert,t.key)}catch{n=null}let r=t.ca?buildCaCertificates(t.ca):[],i={certificate:n,caCertificates:r,options:t};return i.context=i,i}var TLSSocket=class extends r{encrypted=!0;authorized=!1;authorizationError;alpnProtocol=!1;servername;_tlsConnection=null;_secureContext=null;constructor(e,t){super()}_setupTlsStreams(e){this._tlsConnection=e;let t=this;t._inputStream=e.get_input_stream(),t._outputStream=e.get_output_stream(),t._connection=e}getPeerCertificate(e=!1){if(!this._tlsConnection)return{};try{let t=this._tlsConnection.get_peer_certificate();return t?tlsCertToPeerCert(t,e):{}}catch{return{}}}getProtocol(){if(!this._tlsConnection)return null;try{switch(this._tlsConnection.get_protocol_version()){case e.TlsProtocolVersion.TLS_1_0:return`TLSv1`;case e.TlsProtocolVersion.TLS_1_1:return`TLSv1.1`;case e.TlsProtocolVersion.TLS_1_2:return`TLSv1.2`;case e.TlsProtocolVersion.TLS_1_3:return`TLSv1.3`;default:return null}}catch{return null}}getCipher(){if(!this._tlsConnection)return null;try{return{name:this._tlsConnection.get_ciphersuite_name()||`unknown`,version:this.getProtocol()||`unknown`}}catch{return null}}getAlpnProtocol(){if(!this._tlsConnection)return!1;try{return this._tlsConnection.get_negotiated_protocol()||!1}catch{return!1}}};function connect(n,r){let i=new TLSSocket(void 0,n);r&&i.once(`secureConnect`,r);let a=n.port||443,o=n.host||`localhost`,s=n.servername||o,c=n.rejectUnauthorized!==!1,l=n.secureContext??createSecureContext(n);i._secureContext=l,i.servername=s;let u=n.checkServerIdentity;return i.once(`connect`,()=>{let r=i._connection;if(!r){i.destroy(Error(`No underlying connection for TLS upgrade`));return}try{let o=e.NetworkAddress.new(s,a),d=e.TlsClientConnection.new(r,o);if(d.set_server_identity(o),l.certificate)try{d.set_certificate(l.certificate)}catch(e){console.warn(`[tls] failed to set client certificate:`,e)}if(n.ALPNProtocols&&n.ALPNProtocols.length>0)try{d.set_advertised_protocols(n.ALPNProtocols)}catch{}d.connect(`accept-certificate`,(t,n,r)=>{if(!c)return!0;if(l.caCertificates.length===0)return!1;for(let t of l.caCertificates)try{if(n.verify(o,t)===e.TlsCertificateFlags.NO_FLAGS)return!0}catch{}return!1});let f=new e.Cancellable;d.handshake_async(t.PRIORITY_DEFAULT,f,(e,t)=>{try{if(d.handshake_finish(t),i.authorized=!0,i._setupTlsStreams(d),i.alpnProtocol=i.getAlpnProtocol(),u){let e=u(s,i.getPeerCertificate());if(e&&(i.authorized=!1,i.authorizationError=e.message,c)){i.destroy(e);return}}let e=i;e._reading=!1,e._startReading(),i.emit(`secureConnect`)}catch(e){i.authorized=!1,i.authorizationError=e instanceof Error?e.message:String(e),c?i.destroy(e instanceof Error?e:Error(String(e))):(i._setupTlsStreams(d),i.emit(`secureConnect`))}})}catch(e){i.destroy(e instanceof Error?e:Error(String(e)))}}),i.connect({port:a,host:o}),i}const l=[];var TLSServer=class extends n{_tlsCertificate=null;_tlsOptions;_sniContexts=new Map;_secureContext;constructor(e,t){super(),this._tlsOptions=e??{},this._secureContext=createSecureContext(this._tlsOptions),this._tlsCertificate=this._secureContext.certificate,t&&this.on(`secureConnection`,t),this._tlsOptions.cert&&!this._tlsCertificate&&a(this,`error`,i(Error(`Failed to parse TLS certificate`),`createServer`,{}))}addContext(e,t){try{let n=createSecureContext(t);this._sniContexts.set(e.toLowerCase(),n)}catch(e){this.emit(`error`,i(e,`addContext`,{}))}}_resolveSniContext(e,t){let n=this._secureContext;if(!e){t(n);return}let r=e.toLowerCase(),i=this._sniContexts.get(r);if(i){t(i);return}let a=splitHost(r);for(let[e,n]of this._sniContexts)if(checkHostMatch(a,e)){t(n);return}if(this._tlsOptions.SNICallback)try{this._tlsOptions.SNICallback(e,(e,r)=>{if(e||!r){t(n);return}t(r)});return}catch{t(n);return}t(n)}listen(...e){return this.on(`connection`,e=>{this._upgradeTls(e)}),super.listen(...e)}_upgradeTls(n){let r=n._connection;if(!r){let e=Error(`Cannot upgrade socket: no underlying connection`);this.emit(`tlsClientError`,e,n),n.destroy();return}if(!this._tlsCertificate&&this._sniContexts.size===0&&!this._tlsOptions.SNICallback){let e=Error(`TLS server has no certificate configured`);this.emit(`tlsClientError`,e,n),n.destroy();return}this._resolveSniContext(null,a=>{let o=a.certificate??this._tlsCertificate;if(!o){let e=Error(`SNI resolution returned no certificate`);this.emit(`tlsClientError`,e,n),n.destroy();return}try{let s=e.TlsServerConnection.new(r,o);this._tlsOptions.requestCert?s.authenticationMode=this._tlsOptions.rejectUnauthorized===!1?e.TlsAuthenticationMode.REQUESTED:e.TlsAuthenticationMode.REQUIRED:s.authenticationMode=e.TlsAuthenticationMode.NONE;let c=!!this._tlsOptions.requestCert&&this._tlsOptions.rejectUnauthorized!==!1,l=this._secureContext.caCertificates;if(s.connect(`accept-certificate`,(t,n,r)=>{if(!c)return!0;if(l.length===0)return!1;for(let t of l)try{if(n.verify(null,t)===e.TlsCertificateFlags.NO_FLAGS)return!0}catch{}return!1}),this._tlsOptions.ALPNProtocols&&this._tlsOptions.ALPNProtocols.length>0)try{s.set_advertised_protocols(this._tlsOptions.ALPNProtocols)}catch{}let u=new e.Cancellable;s.handshake_async(t.PRIORITY_DEFAULT,u,(e,t)=>{try{s.handshake_finish(t);let e=new TLSSocket;e.encrypted=!0,e.authorized=!0,e._secureContext=a,e._setupTlsStreams(s),e.alpnProtocol=e.getAlpnProtocol(),e._startReading(),this.emit(`secureConnection`,e)}catch(e){let t=i(e,`handshake`,{});this.emit(`tlsClientError`,t,n),n.destroy()}})}catch(e){let t=i(e,`tls_wrap`,{});this.emit(`tlsClientError`,t,n),n.destroy()}})}};function createServer(e,t){return typeof e==`function`?new TLSServer(void 0,e):new TLSServer(e,t)}const u={TLSSocket,TLSServer,Server:TLSServer,connect,createServer,createSecureContext,checkServerIdentity,getCiphers,rootCertificates:l,DEFAULT_MIN_VERSION:o,DEFAULT_MAX_VERSION:s,DEFAULT_CIPHERS:c};export{c as DEFAULT_CIPHERS,s as DEFAULT_MAX_VERSION,o as DEFAULT_MIN_VERSION,TLSServer as Server,TLSServer,TLSSocket,checkServerIdentity,connect,createSecureContext,createServer,u as default,getCiphers,l as rootCertificates};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -5,33 +5,74 @@ export declare const DEFAULT_MAX_VERSION = "TLSv1.3";
|
|
|
5
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
6
|
/** Returns a list of supported TLS cipher names (subset; implementation-defined). */
|
|
7
7
|
export declare function getCiphers(): string[];
|
|
8
|
+
type PemInput = string | Buffer | Uint8Array | Array<string | Buffer | Uint8Array>;
|
|
9
|
+
export interface CertSubject {
|
|
10
|
+
CN?: string | string[];
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
8
13
|
export interface PeerCertificate {
|
|
9
|
-
subject?:
|
|
10
|
-
|
|
11
|
-
[key: string]: unknown;
|
|
12
|
-
};
|
|
14
|
+
subject?: CertSubject;
|
|
15
|
+
issuer?: CertSubject;
|
|
13
16
|
subjectaltname?: string;
|
|
17
|
+
valid_from?: string;
|
|
18
|
+
valid_to?: string;
|
|
19
|
+
fingerprint?: string;
|
|
20
|
+
fingerprint256?: string;
|
|
21
|
+
serialNumber?: string;
|
|
22
|
+
raw?: Uint8Array;
|
|
23
|
+
issuerCertificate?: PeerCertificate;
|
|
14
24
|
[key: string]: unknown;
|
|
15
25
|
}
|
|
26
|
+
/** Error returned by checkServerIdentity, with Node-compatible shape. */
|
|
27
|
+
export interface CertAltNameError extends Error {
|
|
28
|
+
reason: string;
|
|
29
|
+
host: string;
|
|
30
|
+
cert: PeerCertificate;
|
|
31
|
+
code: 'ERR_TLS_CERT_ALTNAME_INVALID';
|
|
32
|
+
}
|
|
16
33
|
/**
|
|
17
34
|
* Verifies that the certificate `cert` is valid for `hostname`.
|
|
18
|
-
* Returns an Error if the check
|
|
35
|
+
* Returns an Error (with code 'ERR_TLS_CERT_ALTNAME_INVALID') if the check
|
|
36
|
+
* fails, or `undefined` on success.
|
|
19
37
|
*
|
|
20
|
-
* Reference: Node.js lib/tls.js exports.checkServerIdentity
|
|
38
|
+
* Reference: Node.js lib/tls.js exports.checkServerIdentity (RFC 6125 §6.4.3).
|
|
21
39
|
*/
|
|
22
|
-
export declare function checkServerIdentity(hostname: string, cert: PeerCertificate):
|
|
40
|
+
export declare function checkServerIdentity(hostname: string, cert: PeerCertificate): CertAltNameError | undefined;
|
|
23
41
|
export interface SecureContextOptions {
|
|
24
|
-
ca?:
|
|
25
|
-
cert?:
|
|
26
|
-
key?:
|
|
42
|
+
ca?: PemInput;
|
|
43
|
+
cert?: PemInput;
|
|
44
|
+
key?: PemInput;
|
|
45
|
+
passphrase?: string;
|
|
27
46
|
rejectUnauthorized?: boolean;
|
|
47
|
+
ciphers?: string;
|
|
48
|
+
minVersion?: string;
|
|
49
|
+
maxVersion?: string;
|
|
50
|
+
ALPNProtocols?: string[];
|
|
51
|
+
}
|
|
52
|
+
/** Internal "secure context" — parsed TLS material shared by tls.connect/createServer. */
|
|
53
|
+
export interface SecureContext {
|
|
54
|
+
certificate: Gio.TlsCertificate | null;
|
|
55
|
+
caCertificates: Gio.TlsCertificate[];
|
|
56
|
+
options: SecureContextOptions;
|
|
57
|
+
/**
|
|
58
|
+
* Node-compat handle (Node returns a `SecureContext` with an internal native
|
|
59
|
+
* `context` field). We have no native handle, so this points back at the
|
|
60
|
+
* SecureContext object itself — `ctx.context !== undefined` matches Node.
|
|
61
|
+
*/
|
|
62
|
+
context: SecureContext;
|
|
28
63
|
}
|
|
64
|
+
/** Build a SecureContext from PEM material. Buffer/Uint8Array/string all accepted. */
|
|
65
|
+
export declare function createSecureContext(options?: SecureContextOptions): SecureContext;
|
|
29
66
|
export interface TlsConnectOptions extends SecureContextOptions {
|
|
30
67
|
host?: string;
|
|
31
68
|
port?: number;
|
|
32
69
|
socket?: Socket;
|
|
33
70
|
servername?: string;
|
|
34
71
|
ALPNProtocols?: string[];
|
|
72
|
+
/** Pre-built secure context from createSecureContext(). */
|
|
73
|
+
secureContext?: SecureContext;
|
|
74
|
+
/** Custom server-identity check (runs after the GnuTLS-level check). */
|
|
75
|
+
checkServerIdentity?: (host: string, cert: PeerCertificate) => Error | undefined;
|
|
35
76
|
}
|
|
36
77
|
/**
|
|
37
78
|
* TLSSocket wraps a net.Socket with TLS via Gio.TlsConnection.
|
|
@@ -41,24 +82,31 @@ export declare class TLSSocket extends Socket {
|
|
|
41
82
|
authorized: boolean;
|
|
42
83
|
authorizationError?: string;
|
|
43
84
|
alpnProtocol: string | false;
|
|
85
|
+
servername: string | undefined;
|
|
44
86
|
/** @internal */
|
|
45
87
|
_tlsConnection: Gio.TlsConnection | null;
|
|
46
|
-
|
|
88
|
+
/** @internal — preserved for diagnostics + future cert-chain verification. */
|
|
89
|
+
_secureContext: SecureContext | null;
|
|
90
|
+
constructor(_socket?: Socket, _options?: SecureContextOptions);
|
|
47
91
|
/**
|
|
48
92
|
* @internal Wire the TLS connection's I/O streams into this socket
|
|
49
93
|
* so that read/write operations go through the encrypted channel.
|
|
50
94
|
*/
|
|
51
95
|
_setupTlsStreams(tlsConn: Gio.TlsConnection): void;
|
|
52
|
-
/**
|
|
53
|
-
|
|
96
|
+
/**
|
|
97
|
+
* Get the peer certificate. When `detailed` is true, walks the issuer chain
|
|
98
|
+
* via `Gio.TlsCertificate.get_issuer()` and populates `issuerCertificate`
|
|
99
|
+
* recursively (with a self-reference on the root for compatibility).
|
|
100
|
+
*/
|
|
101
|
+
getPeerCertificate(detailed?: boolean): PeerCertificate;
|
|
54
102
|
/** Get the negotiated TLS protocol version. */
|
|
55
103
|
getProtocol(): string | null;
|
|
56
|
-
/** Get the cipher
|
|
104
|
+
/** Get the negotiated cipher suite name + version. */
|
|
57
105
|
getCipher(): {
|
|
58
106
|
name: string;
|
|
59
107
|
version: string;
|
|
60
108
|
} | null;
|
|
61
|
-
/** Get the negotiated ALPN protocol. */
|
|
109
|
+
/** Get the negotiated ALPN protocol (or false if none). */
|
|
62
110
|
getAlpnProtocol(): string | false;
|
|
63
111
|
}
|
|
64
112
|
/**
|
|
@@ -68,34 +116,41 @@ export declare class TLSSocket extends Socket {
|
|
|
68
116
|
* the connection to TLS using Gio.TlsClientConnection.
|
|
69
117
|
*/
|
|
70
118
|
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
119
|
export declare const rootCertificates: string[];
|
|
120
|
+
export type SNICallback = (servername: string, cb: (err: Error | null, ctx?: SecureContext) => void) => void;
|
|
78
121
|
export interface TlsServerOptions extends SecureContextOptions {
|
|
79
122
|
requestCert?: boolean;
|
|
80
123
|
rejectUnauthorized?: boolean;
|
|
81
124
|
ALPNProtocols?: string[];
|
|
125
|
+
SNICallback?: SNICallback;
|
|
82
126
|
}
|
|
83
127
|
/**
|
|
84
|
-
* TLSServer
|
|
128
|
+
* TLSServer accepts incoming TCP connections and upgrades each to TLS via
|
|
129
|
+
* `Gio.TlsServerConnection`. Supports mTLS via `requestCert`+`rejectUnauthorized`,
|
|
130
|
+
* SNI selection via `addContext`/`SNICallback`, and ALPN negotiation.
|
|
85
131
|
*/
|
|
86
132
|
export declare class TLSServer extends Server {
|
|
87
133
|
private _tlsCertificate;
|
|
88
134
|
private _tlsOptions;
|
|
89
135
|
private _sniContexts;
|
|
136
|
+
/** @internal — exposed for tests. */
|
|
137
|
+
_secureContext: SecureContext;
|
|
90
138
|
constructor(options?: TlsServerOptions, secureConnectionListener?: (socket: TLSSocket) => void);
|
|
91
139
|
/**
|
|
92
|
-
* Add
|
|
140
|
+
* Add an additional context for SNI (Server Name Indication). Uses RFC 6125
|
|
141
|
+
* matching against the requested server name.
|
|
93
142
|
*/
|
|
94
143
|
addContext(hostname: string, context: SecureContextOptions): void;
|
|
95
|
-
listen(...args: unknown[]): this;
|
|
96
144
|
/**
|
|
97
|
-
*
|
|
145
|
+
* Resolve a SecureContext for the given server name. Order:
|
|
146
|
+
* 1. exact match in `_sniContexts`
|
|
147
|
+
* 2. RFC 6125 wildcard match in `_sniContexts`
|
|
148
|
+
* 3. SNICallback (if provided)
|
|
149
|
+
* 4. fall through to the server's default context
|
|
98
150
|
*/
|
|
151
|
+
private _resolveSniContext;
|
|
152
|
+
listen(...args: unknown[]): this;
|
|
153
|
+
/** Upgrade a raw TCP socket to TLS using Gio.TlsServerConnection. */
|
|
99
154
|
private _upgradeTls;
|
|
100
155
|
}
|
|
101
156
|
/**
|
|
@@ -104,7 +159,7 @@ export declare class TLSServer extends Server {
|
|
|
104
159
|
export declare function createServer(options?: TlsServerOptions, secureConnectionListener?: (socket: TLSSocket) => void): TLSServer;
|
|
105
160
|
export declare function createServer(secureConnectionListener?: (socket: TLSSocket) => void): TLSServer;
|
|
106
161
|
export { TLSServer as Server };
|
|
107
|
-
declare const
|
|
162
|
+
declare const tlsExports: {
|
|
108
163
|
TLSSocket: typeof TLSSocket;
|
|
109
164
|
TLSServer: typeof TLSServer;
|
|
110
165
|
Server: typeof TLSServer;
|
|
@@ -118,4 +173,4 @@ declare const _default: {
|
|
|
118
173
|
DEFAULT_MAX_VERSION: string;
|
|
119
174
|
DEFAULT_CIPHERS: string;
|
|
120
175
|
};
|
|
121
|
-
export default
|
|
176
|
+
export default tlsExports;
|
package/package.json
CHANGED
|
@@ -1,44 +1,47 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
2
|
+
"name": "@gjsify/tls",
|
|
3
|
+
"version": "0.4.3",
|
|
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
|
+
"files": [
|
|
15
|
+
"lib"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"clear": "rm -rf lib tsconfig.tsbuildinfo tsconfig.types.tsbuildinfo test.gjs.mjs test.node.mjs || exit 0",
|
|
19
|
+
"check": "tsc --noEmit",
|
|
20
|
+
"build": "gjsify run build:gjsify && gjsify run build:types",
|
|
21
|
+
"build:gjsify": "gjsify build --library 'src/**/*.{ts,js}' --exclude 'src/**/*.spec.{mts,ts}' 'src/test.{mts,ts}'",
|
|
22
|
+
"build:types": "tsc",
|
|
23
|
+
"build:test": "gjsify run build:test:gjs && gjsify run build:test:node",
|
|
24
|
+
"build:test:gjs": "gjsify build src/test.mts --app gjs --outfile test.gjs.mjs",
|
|
25
|
+
"build:test:node": "gjsify build src/test.mts --app node --outfile test.node.mjs",
|
|
26
|
+
"test": "gjsify run build:gjsify && gjsify run build:test && gjsify run test:node && gjsify run test:gjs",
|
|
27
|
+
"test:gjs": "gjsify run test.gjs.mjs",
|
|
28
|
+
"test:node": "node test.node.mjs"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"gjs",
|
|
32
|
+
"node",
|
|
33
|
+
"tls"
|
|
34
|
+
],
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@gjsify/cli": "workspace:^",
|
|
37
|
+
"@gjsify/unit": "workspace:^",
|
|
38
|
+
"@types/node": "^25.6.2",
|
|
39
|
+
"typescript": "^6.0.3"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@girs/gio-2.0": "2.88.0-4.0.0-rc.15",
|
|
43
|
+
"@girs/glib-2.0": "2.88.0-4.0.0-rc.15",
|
|
44
|
+
"@gjsify/net": "workspace:^",
|
|
45
|
+
"@gjsify/utils": "workspace:^"
|
|
12
46
|
}
|
|
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": "gjsify run 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.3.21",
|
|
34
|
-
"@gjsify/unit": "^0.3.21",
|
|
35
|
-
"@types/node": "^25.6.2",
|
|
36
|
-
"typescript": "^6.0.3"
|
|
37
|
-
},
|
|
38
|
-
"dependencies": {
|
|
39
|
-
"@girs/gio-2.0": "2.88.0-4.0.0-rc.14",
|
|
40
|
-
"@girs/glib-2.0": "2.88.0-4.0.0-rc.14",
|
|
41
|
-
"@gjsify/net": "^0.3.21",
|
|
42
|
-
"@gjsify/utils": "^0.3.21"
|
|
43
|
-
}
|
|
44
|
-
}
|
|
47
|
+
}
|