@gjsify/tls 0.4.29 → 0.4.31

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.
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{TLSSocket as e}from"./tls-socket.js";import{createSecureContext as t}from"./secure-context.js";import n from"@girs/gio-2.0";import r from"@girs/glib-2.0";function connect(i,a){let o=new e(void 0,i);a&&o.once(`secureConnect`,a);let s=i.port||443,c=i.host||`localhost`,l=i.servername||c,u=i.rejectUnauthorized!==!1,d=i.secureContext??t(i);o._secureContext=d,o.servername=l;let f=i.checkServerIdentity;return o.once(`connect`,()=>{let e=o._connection;if(!e){o.destroy(Error(`No underlying connection for TLS upgrade`));return}try{let t=n.NetworkAddress.new(l,s),a=n.TlsClientConnection.new(e,t);if(a.set_server_identity(t),d.certificate)try{a.set_certificate(d.certificate)}catch(e){console.warn(`[tls] failed to set client certificate:`,e)}if(i.ALPNProtocols&&i.ALPNProtocols.length>0)try{a.set_advertised_protocols(i.ALPNProtocols)}catch{}a.connect(`accept-certificate`,(e,r,i)=>{if(!u)return!0;if(d.caCertificates.length===0)return!1;for(let e of d.caCertificates)try{if(r.verify(t,e)===n.TlsCertificateFlags.NO_FLAGS)return!0}catch{}return!1});let c=new n.Cancellable;a.handshake_async(r.PRIORITY_DEFAULT,c,(e,t)=>{try{if(a.handshake_finish(t),o.authorized=!0,o._setupTlsStreams(a),o.alpnProtocol=o.getAlpnProtocol(),f){let e=f(l,o.getPeerCertificate());if(e&&(o.authorized=!1,o.authorizationError=e.message,u)){o.destroy(e);return}}let e=o;e._reading=!1,e._startReading(),o.emit(`secureConnect`)}catch(e){o.authorized=!1,o.authorizationError=e instanceof Error?e.message:String(e),u?o.destroy(e instanceof Error?e:Error(String(e))):(o._setupTlsStreams(a),o.emit(`secureConnect`))}})}catch(e){o.destroy(e instanceof Error?e:Error(String(e)))}}),o.connect({port:s,host:c}),o}export{connect};
1
+ import"./_virtual/_rolldown/runtime.js";import{hasTlsSessionAccess as e}from"./session-access.js";import{TLSSocket as t}from"./tls-socket.js";import{createSecureContext as n}from"./secure-context.js";import r from"@girs/gio-2.0";import i from"@girs/glib-2.0";function connect(a,o){let s=new t(void 0,a);o&&s.once(`secureConnect`,o);let c=a.port||443,l=a.host||`localhost`,u=a.servername||l,d=a.rejectUnauthorized!==!1,f=a.secureContext??n(a);s._secureContext=f,s.servername=u;let p=a.checkServerIdentity;return s.once(`connect`,()=>{let t=s._connection;if(!t){s.destroy(Error(`No underlying connection for TLS upgrade`));return}try{let n=r.NetworkAddress.new(u,c),o=r.TlsClientConnection.new(t,n);if(o.set_server_identity(n),a.session&&e())try{s._tlsConnection=o,s.setSession(a.session)}catch{}if(f.certificate)try{o.set_certificate(f.certificate)}catch(e){console.warn(`[tls] failed to set client certificate:`,e)}if(a.ALPNProtocols&&a.ALPNProtocols.length>0)try{o.set_advertised_protocols(a.ALPNProtocols)}catch{}o.connect(`accept-certificate`,(e,t,i)=>{if(!d)return!0;if(f.caCertificates.length===0)return!1;for(let e of f.caCertificates)try{if(t.verify(n,e)===r.TlsCertificateFlags.NO_FLAGS)return!0}catch{}return!1});let l=new r.Cancellable;o.handshake_async(i.PRIORITY_DEFAULT,l,(t,n)=>{try{if(o.handshake_finish(n),s.authorized=!0,s._setupTlsStreams(o),s.alpnProtocol=s.getAlpnProtocol(),p){let e=p(u,s.getPeerCertificate());if(e&&(s.authorized=!1,s.authorizationError=e.message,d)){s.destroy(e);return}}let t=s;if(t._reading=!1,t._startReading(),e()){let e=s.getSession();e&&s.emit(`session`,e)}s.emit(`secureConnect`)}catch(e){s.authorized=!1,s.authorizationError=e instanceof Error?e.message:String(e),d?s.destroy(e instanceof Error?e:Error(String(e))):(s._setupTlsStreams(o),s.emit(`secureConnect`))}})}catch(e){s.destroy(e instanceof Error?e:Error(String(e)))}}),s.connect({port:c,host:l}),s}export{connect};
package/lib/esm/index.js CHANGED
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{TLSSocket as e}from"./tls-socket.js";import{createSecureContext as t}from"./secure-context.js";import{connect as n}from"./connect.js";import{checkServerIdentity as r}from"./internal/hostname.js";import{TLSServer as i,createServer as a}from"./tls-server.js";import{OcspCertStatus as o,OcspResponseStatus as s,hasOcspSupport as c,parseOcspResponse as l}from"./ocsp.js";const u=`TLSv1.2`,d=`TLSv1.3`,f=`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`]}const p=[],m={TLSSocket:e,TLSServer:i,Server:i,connect:n,createServer:a,createSecureContext:t,checkServerIdentity:r,getCiphers,rootCertificates:p,DEFAULT_MIN_VERSION:u,DEFAULT_MAX_VERSION:d,DEFAULT_CIPHERS:f,parseOcspResponse:l,hasOcspSupport:c,OcspCertStatus:o,OcspResponseStatus:s};export{f as DEFAULT_CIPHERS,d as DEFAULT_MAX_VERSION,u as DEFAULT_MIN_VERSION,o as OcspCertStatus,s as OcspResponseStatus,i as Server,i as TLSServer,e as TLSSocket,r as checkServerIdentity,n as connect,t as createSecureContext,a as createServer,m as default,getCiphers,c as hasOcspSupport,l as parseOcspResponse,p as rootCertificates};
1
+ import"./_virtual/_rolldown/runtime.js";import{TlsChannelBindingType as e,hasTlsSessionAccess as t}from"./session-access.js";import{TLSSocket as n}from"./tls-socket.js";import{createSecureContext as r}from"./secure-context.js";import{connect as i}from"./connect.js";import{checkServerIdentity as a}from"./internal/hostname.js";import{TLSServer as o,createServer as s}from"./tls-server.js";import{OcspCertStatus as c,OcspResponseStatus as l,hasOcspSupport as u,parseOcspResponse as d}from"./ocsp.js";const f=`TLSv1.2`,p=`TLSv1.3`,m=`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`]}const h=[],g={TLSSocket:n,TLSServer:o,Server:o,connect:i,createServer:s,createSecureContext:r,checkServerIdentity:a,getCiphers,rootCertificates:h,DEFAULT_MIN_VERSION:f,DEFAULT_MAX_VERSION:p,DEFAULT_CIPHERS:m,parseOcspResponse:d,hasOcspSupport:u,OcspCertStatus:c,OcspResponseStatus:l,hasTlsSessionAccess:t,TlsChannelBindingType:e};export{m as DEFAULT_CIPHERS,p as DEFAULT_MAX_VERSION,f as DEFAULT_MIN_VERSION,c as OcspCertStatus,l as OcspResponseStatus,o as Server,o as TLSServer,n as TLSSocket,e as TlsChannelBindingType,a as checkServerIdentity,i as connect,r as createSecureContext,s as createServer,g as default,getCiphers,u as hasOcspSupport,t as hasTlsSessionAccess,d as parseOcspResponse,h as rootCertificates};
@@ -0,0 +1 @@
1
+ import"./_virtual/_rolldown/runtime.js";import{TlsChannelBindingType as e,TlsChannelBindingType as t,createSessionAccess as n,hasTlsSessionAccess as r}from"@gjsify/tls-native";function hasTlsSessionAccess(){return r()}function createSessionAccess(e){return n(e)}export{e as TlsChannelBindingType,t as _TlsChannelBindingType,createSessionAccess,hasTlsSessionAccess};
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{tlsCertToPeerCert as e}from"./internal/cert-utils.js";import t from"@girs/gio-2.0";import{Socket as n}from"node:net";var TLSSocket=class extends n{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(t=!1){if(!this._tlsConnection)return{};try{let n=this._tlsConnection.get_peer_certificate();return n?e(n,t):{}}catch{return{}}}getProtocol(){if(!this._tlsConnection)return null;try{switch(this._tlsConnection.get_protocol_version()){case t.TlsProtocolVersion.TLS_1_0:return`TLSv1`;case t.TlsProtocolVersion.TLS_1_1:return`TLSv1.1`;case t.TlsProtocolVersion.TLS_1_2:return`TLSv1.2`;case t.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}}};export{TLSSocket};
1
+ import"./_virtual/_rolldown/runtime.js";import{tlsCertToPeerCert as e}from"./internal/cert-utils.js";import{createSessionAccess as t,hasTlsSessionAccess as n}from"./session-access.js";import r from"@girs/gio-2.0";import{Buffer as i}from"node:buffer";import{Socket as a}from"node:net";var TLSSocket=class extends a{encrypted=!0;authorized=!1;authorizationError;alpnProtocol=!1;servername;_tlsConnection=null;_secureContext=null;_sessionAccess=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}_getSessionAccess(){if(this._sessionAccess)return this._sessionAccess;if(!this._tlsConnection)return null;try{this._sessionAccess=t(this._tlsConnection)}catch{this._sessionAccess=null}return this._sessionAccess}getPeerCertificate(t=!1){if(!this._tlsConnection)return{};try{let n=this._tlsConnection.get_peer_certificate();return n?e(n,t):{}}catch{return{}}}getProtocol(){if(!this._tlsConnection)return null;try{switch(this._tlsConnection.get_protocol_version()){case r.TlsProtocolVersion.TLS_1_0:return`TLSv1`;case r.TlsProtocolVersion.TLS_1_1:return`TLSv1.1`;case r.TlsProtocolVersion.TLS_1_2:return`TLSv1.2`;case r.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}}getFinished(){if(!n())return;let e=this._getSessionAccess();if(e)try{return _bytesToBuffer(e.get_finished())}catch{return}}getPeerFinished(){if(!n())return;let e=this._getSessionAccess();if(e)try{return _bytesToBuffer(e.get_peer_finished())}catch{return}}getSession(){if(!n())return;let e=this._getSessionAccess();if(e)try{return _bytesToBuffer(e.get_session_data())}catch{return}}setSession(e){if(!n())return;let t=this._getSessionAccess();if(t)try{t.set_session_data(_bufferToBytes(e))}catch{}}isSessionReused(){if(!n())return!1;let e=this._getSessionAccess();if(!e)return!1;try{return e.is_session_reused()}catch{return!1}}};function _bytesToBuffer(e){if(e==null)return;if(e instanceof Uint8Array)return i.from(e);let t=e;if(typeof t.toArray==`function`)try{return i.from(t.toArray())}catch{return}}function _bufferToBytes(e){return e}export{TLSSocket};
@@ -16,12 +16,14 @@ export type { SNICallback, TlsServerOptions } from './tls-server.js';
16
16
  export { TLSServer, createServer } from './tls-server.js';
17
17
  export { TLSServer as Server } from './tls-server.js';
18
18
  export { parseOcspResponse, hasOcspSupport, OcspCertStatus, OcspResponseStatus, type OcspResponseInfo, } from './ocsp.js';
19
+ export { hasTlsSessionAccess, TlsChannelBindingType, type NativeSessionAccess as TlsSessionAccess, } from './session-access.js';
19
20
  import { checkServerIdentity as _checkServerIdentity } from './internal/hostname.js';
20
21
  import { createSecureContext as _createSecureContext } from './secure-context.js';
21
22
  import { TLSSocket as _TLSSocket } from './tls-socket.js';
22
23
  import { connect as _connect } from './connect.js';
23
24
  import { TLSServer as _TLSServer, createServer as _createServer } from './tls-server.js';
24
25
  import { parseOcspResponse as _parseOcspResponse, hasOcspSupport as _hasOcspSupport } from './ocsp.js';
26
+ import { hasTlsSessionAccess as _hasTlsSessionAccess } from './session-access.js';
25
27
  declare const tlsExports: {
26
28
  TLSSocket: typeof _TLSSocket;
27
29
  TLSServer: typeof _TLSServer;
@@ -50,5 +52,11 @@ declare const tlsExports: {
50
52
  readonly SIG_REQUIRED: 5;
51
53
  readonly UNAUTHORIZED: 6;
52
54
  };
55
+ hasTlsSessionAccess: typeof _hasTlsSessionAccess;
56
+ TlsChannelBindingType: {
57
+ readonly TLS_UNIQUE: 0;
58
+ readonly TLS_SERVER_END_POINT: 1;
59
+ readonly TLS_EXPORTER: 2;
60
+ };
53
61
  };
54
62
  export default tlsExports;
@@ -0,0 +1,38 @@
1
+ import { TlsChannelBindingType as _TlsChannelBindingType, type NativeSessionAccess as _NativeSessionAccess, type TlsConnectionHandle as _TlsConnectionHandle } from '@gjsify/tls-native';
2
+ export { TlsChannelBindingType } from '@gjsify/tls-native';
3
+ export type { NativeSessionAccess, TlsConnectionHandle } from '@gjsify/tls-native';
4
+ /**
5
+ * Returns `true` when the @gjsify/tls-native Phase 2 session-access
6
+ * bridge is loaded AND functional. Today this returns `false` even
7
+ * on GJS with the typelib installed — the underlying gnutls_session_t
8
+ * extraction from `Gio.TlsConnection` is not yet implemented
9
+ * (`docs/poc/tls-phase2-session-access.md`).
10
+ *
11
+ * Use this predicate to detect "session-resumption / channel-binding
12
+ * available on this runtime" without triggering a thrown error. When
13
+ * it returns `false`:
14
+ *
15
+ * - `TLSSocket.getFinished()` / `getPeerFinished()` return `undefined`.
16
+ * - `TLSSocket.getSession()` returns `undefined`.
17
+ * - `TLSSocket.setSession()` / `tls.connect({session})` silently
18
+ * skip the resumption attempt (full handshake instead).
19
+ * - The `'session'` event never fires.
20
+ *
21
+ * This matches Node's behavior on a build without OpenSSL session
22
+ * support — present API surface, no-op when unavailable.
23
+ */
24
+ export declare function hasTlsSessionAccess(): boolean;
25
+ /**
26
+ * Internal: build a {@link NativeSessionAccess} wrapper around a live
27
+ * `Gio.TlsConnection`. Returns `null` when the native typelib isn't
28
+ * loaded OR @connection is `null`. The returned wrapper's methods
29
+ * may throw — callers must wrap in `try/catch` AND check
30
+ * `hasTlsSessionAccess()` first.
31
+ *
32
+ * @internal — used by `TLSSocket` and `connect.ts`. Public consumers
33
+ * should rely on the Node-shaped getters/setters on `TLSSocket`.
34
+ */
35
+ export declare function createSessionAccess(connection: _TlsConnectionHandle | null): _NativeSessionAccess | null;
36
+ /** Re-export for code that wants the channel-binding type constants
37
+ * without importing through the native package directly. */
38
+ export { _TlsChannelBindingType };
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -1,7 +1,9 @@
1
1
  import Gio from '@girs/gio-2.0';
2
+ import { Buffer } from 'node:buffer';
2
3
  import { Socket } from 'node:net';
3
4
  import { type PeerCertificate } from './internal/cert-utils.js';
4
5
  import type { SecureContext, SecureContextOptions } from './secure-context.js';
6
+ import type { NativeSessionAccess } from './session-access.js';
5
7
  export interface TlsConnectOptions extends SecureContextOptions {
6
8
  host?: string;
7
9
  port?: number;
@@ -12,6 +14,15 @@ export interface TlsConnectOptions extends SecureContextOptions {
12
14
  secureContext?: SecureContext;
13
15
  /** Custom server-identity check (runs after the GnuTLS-level check). */
14
16
  checkServerIdentity?: (host: string, cert: PeerCertificate) => Error | undefined;
17
+ /**
18
+ * Previously serialized session blob to attempt resumption with.
19
+ * Pulled from a prior connection's `'session'` event / `getSession()`.
20
+ *
21
+ * Silently ignored when {@link hasTlsSessionAccess} returns `false`
22
+ * (no native bridge, or the GIO struct-layout work hasn't landed
23
+ * yet) — the connection falls back to a full handshake.
24
+ */
25
+ session?: Buffer | Uint8Array;
15
26
  }
16
27
  /**
17
28
  * Internal cast for Socket's private-field shape. We own `node:net`'s
@@ -40,12 +51,24 @@ export declare class TLSSocket extends Socket {
40
51
  _tlsConnection: Gio.TlsConnection | null;
41
52
  /** @internal — preserved for diagnostics + future cert-chain verification. */
42
53
  _secureContext: SecureContext | null;
54
+ /**
55
+ * @internal Lazily constructed Phase 2 session-access bridge for
56
+ * this connection. Built on first call to a session/binding getter
57
+ * (`_getSessionAccess()`), reused across subsequent calls.
58
+ */
59
+ _sessionAccess: NativeSessionAccess | null;
43
60
  constructor(_socket?: Socket, _options?: SecureContextOptions);
44
61
  /**
45
62
  * @internal Wire the TLS connection's I/O streams into this socket
46
63
  * so that read/write operations go through the encrypted channel.
47
64
  */
48
65
  _setupTlsStreams(tlsConn: Gio.TlsConnection): void;
66
+ /**
67
+ * @internal Build (or reuse) the `NativeSessionAccess` for this
68
+ * TLS connection. Returns `null` when no TLS connection is wired
69
+ * yet or the native typelib is unavailable.
70
+ */
71
+ _getSessionAccess(): NativeSessionAccess | null;
49
72
  /**
50
73
  * Get the peer certificate. When `detailed` is true, walks the issuer chain
51
74
  * via `Gio.TlsCertificate.get_issuer()` and populates `issuerCertificate`
@@ -61,4 +84,50 @@ export declare class TLSSocket extends Socket {
61
84
  } | null;
62
85
  /** Get the negotiated ALPN protocol (or false if none). */
63
86
  getAlpnProtocol(): string | false;
87
+ /**
88
+ * Get the local Finished message bytes (RFC 5246 §7.4.9) for use
89
+ * as a `tls-unique` channel binding (RFC 5929 §3). Used by
90
+ * SCRAM-SHA-* SASL mechanisms (RFC 5802 §6).
91
+ *
92
+ * Returns `undefined` when:
93
+ * - The handshake has not completed.
94
+ * - The native session-access bridge is not available
95
+ * ({@link hasTlsSessionAccess} returns `false`).
96
+ *
97
+ * On TLS 1.3 the underlying GnuTLS call returns the
98
+ * `tls-exporter` (RFC 9266) bytes — the Node-compat `getFinished`
99
+ * name is preserved but the semantics auto-degrade to the
100
+ * version-appropriate binding.
101
+ */
102
+ getFinished(): Buffer | undefined;
103
+ /**
104
+ * Get the peer's Finished message bytes. Same TLS 1.3 fallback as
105
+ * {@link getFinished}.
106
+ */
107
+ getPeerFinished(): Buffer | undefined;
108
+ /**
109
+ * Get the serialized session for resumption. Suitable for caching
110
+ * and feeding back into a future `tls.connect({session})` call.
111
+ *
112
+ * Returns `undefined` when the native session-access bridge is
113
+ * not available.
114
+ */
115
+ getSession(): Buffer | undefined;
116
+ /**
117
+ * Inject a previously serialized session blob. Must be called
118
+ * BEFORE the handshake completes — typically Node consumers use
119
+ * `tls.connect({session})` instead, which forwards here at the
120
+ * right time.
121
+ *
122
+ * Silently no-ops when the native session-access bridge is not
123
+ * available (matches Node's behavior on a build without session
124
+ * support).
125
+ */
126
+ setSession(session: Buffer | Uint8Array): void;
127
+ /**
128
+ * Returns `true` if this connection resumed an earlier session
129
+ * (via session ID or ticket). `false` for fresh handshakes and
130
+ * when the native bridge is unavailable.
131
+ */
132
+ isSessionReused(): boolean;
64
133
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/tls",
3
- "version": "0.4.29",
3
+ "version": "0.4.31",
4
4
  "description": "Node.js tls module for Gjs",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -33,16 +33,16 @@
33
33
  "tls"
34
34
  ],
35
35
  "devDependencies": {
36
- "@gjsify/cli": "^0.4.29",
37
- "@gjsify/unit": "^0.4.29",
36
+ "@gjsify/cli": "^0.4.31",
37
+ "@gjsify/unit": "^0.4.31",
38
38
  "@types/node": "^25.9.1",
39
39
  "typescript": "^6.0.3"
40
40
  },
41
41
  "dependencies": {
42
42
  "@girs/gio-2.0": "2.88.0-4.0.1",
43
43
  "@girs/glib-2.0": "2.88.0-4.0.1",
44
- "@gjsify/net": "^0.4.29",
45
- "@gjsify/tls-native": "^0.4.29",
46
- "@gjsify/utils": "^0.4.29"
44
+ "@gjsify/net": "^0.4.31",
45
+ "@gjsify/tls-native": "^0.4.31",
46
+ "@gjsify/utils": "^0.4.31"
47
47
  }
48
48
  }