@gjsify/tls 0.55.0 → 0.57.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.
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{checkHostMatch as e,splitHost as t}from"./internal/hostname.js";import{createSecureContext as n}from"./secure-context.js";import{TLSSocket as r,claimGjsifyConnection as i,foreignSocketError as a}from"./tls-socket.js";import{parseClientHelloSni as o}from"./internal/sni-parser.js";import s from"@girs/gio-2.0";import c from"@girs/glib-2.0";import{Server as l}from"node:net";import{createNodeError as u,deferEmit as d}from"@gjsify/utils/core";var TLSServer=class extends l{_tlsCertificate=null;_tlsOptions;_sniContexts=new Map;_secureContext;constructor(e,t){super(),this._tlsOptions=e??{},this._secureContext=n(this._tlsOptions),this._tlsCertificate=this._secureContext.certificate,t&&this.on(`secureConnection`,t),this._tlsOptions.cert&&!this._tlsCertificate&&d(this,`error`,u(Error(`Failed to parse TLS certificate`),`createServer`,{}))}addContext(e,t){try{let r=n(t);this._sniContexts.set(e.toLowerCase(),r)}catch(e){this.emit(`error`,u(e,`addContext`,{}))}}_resolveSniContext(n,r){let i=this._secureContext;if(!n){r(i);return}let a=n.toLowerCase(),o=this._sniContexts.get(a);if(o){r(o);return}let s=t(a);for(let[t,n]of this._sniContexts)if(e(s,t)){r(n);return}if(this._tlsOptions.SNICallback)try{this._tlsOptions.SNICallback(n,(e,t)=>{if(e||!t){r(i);return}r(t)});return}catch{r(i);return}r(i)}listen(...e){return this.on(`connection`,e=>{this._upgradeTls(e)}),super.listen(...e)}_upgradeTls(e){let t=i(e);if(!t){this.emit(`tlsClientError`,a(),e),e.destroy();return}let n=t.connection;if(!n){let t=Error(`Cannot upgrade socket: no underlying connection`);this.emit(`tlsClientError`,t,e),e.destroy();return}let closeClaimedConnection=()=>{try{(t.connection??t.ioStream)?.close(null)}catch{}};if(!this._tlsCertificate&&this._sniContexts.size===0&&!this._tlsOptions.SNICallback){let t=Error(`TLS server has no certificate configured`);this.emit(`tlsClientError`,t,e),closeClaimedConnection(),e.destroy();return}let r=n;if(this._sniContexts.size===0&&!this._tlsOptions.SNICallback){let t=this._tlsCertificate;if(!t){let t=Error(`TLS server has no certificate configured`);this.emit(`tlsClientError`,t,e),closeClaimedConnection(),e.destroy();return}this._completeUpgrade(r,t,this._secureContext,e,closeClaimedConnection);return}let l=r.get_input_stream(),u=r.get_output_stream(),d=s.BufferedInputStream.new_sized(l,4096);d.fill_async(4096,c.PRIORITY_DEFAULT,null,(t,n)=>{let r=null;try{d.fill_finish(n),r=o(d.peek_buffer())}catch{}this._resolveSniContext(r,t=>{let n=t.certificate??this._tlsCertificate;if(!n){let t=Error(`SNI resolution returned no certificate`);this.emit(`tlsClientError`,t,e),closeClaimedConnection(),e.destroy();return}let r=new s.SimpleIOStream({inputStream:d,outputStream:u});this._completeUpgrade(r,n,t,e,closeClaimedConnection)})})}_completeUpgrade(e,t,n,i,a){try{let o=s.TlsServerConnection.new(e,t);this._tlsOptions.requestCert?o.authenticationMode=this._tlsOptions.rejectUnauthorized===!1?s.TlsAuthenticationMode.REQUESTED:s.TlsAuthenticationMode.REQUIRED:o.authenticationMode=s.TlsAuthenticationMode.NONE;let l=!!this._tlsOptions.requestCert&&this._tlsOptions.rejectUnauthorized!==!1,d=this._secureContext.caCertificates;o.connect(`accept-certificate`,(e,t,n)=>{if(!l)return!0;if(d.length===0)return!1;for(let e of d)try{if(t.verify(null,e)===s.TlsCertificateFlags.NO_FLAGS)return!0}catch{}return!1}),this._tlsOptions.ALPNProtocols&&this._tlsOptions.ALPNProtocols.length>0&&o.set_advertised_protocols(this._tlsOptions.ALPNProtocols);let f=new s.Cancellable;o.handshake_async(c.PRIORITY_DEFAULT,f,(e,t)=>{try{o.handshake_finish(t);let e=new r;e.encrypted=!0,e.authorized=!0,e._secureContext=n,e._setupTlsStreams(o),e.alpnProtocol=e.getAlpnProtocol(),e.once(`close`,()=>i.destroy()),e._startReading(),this.emit(`secureConnection`,e)}catch(e){let t=u(e,`handshake`,{});this.emit(`tlsClientError`,t,i),a(),i.destroy()}})}catch(e){let t=u(e,`tls_wrap`,{});this.emit(`tlsClientError`,t,i),a(),i.destroy()}}};function createServer(e,t){return typeof e==`function`?new TLSServer(void 0,e):new TLSServer(e,t)}export{TLSServer,createServer};
1
+ import"./_virtual/_rolldown/runtime.js";import{checkHostMatch as e,splitHost as t}from"./internal/hostname.js";import{createSecureContext as n}from"./secure-context.js";import{TLSSocket as r,claimGjsifyConnection as i,foreignSocketError as a}from"./tls-socket.js";import{parseClientHelloSni as o}from"./internal/sni-parser.js";import s from"@girs/gio-2.0";import c from"@girs/glib-2.0";import{Server as l}from"node:net";import{createNodeError as u,deferEmit as d}from"@gjsify/utils/core";var TLSServer=class extends l{_tlsCertificate=null;_tlsOptions;_sniContexts=new Map;_secureContext;constructor(e,t){super(),this._tlsOptions=e??{},this._secureContext=n(this._tlsOptions),this._tlsCertificate=this._secureContext.certificate,t&&this.on(`secureConnection`,t),this._tlsOptions.cert&&!this._tlsCertificate&&d(this,`error`,u(Error(`Failed to parse TLS certificate`),`createServer`,{}))}addContext(e,t){try{let r=n(t);this._sniContexts.set(e.toLowerCase(),r)}catch(e){this.emit(`error`,u(e,`addContext`,{}))}}_resolveSniContext(n,r){let i=this._secureContext;if(!n){r(i);return}let a=n.toLowerCase(),o=this._sniContexts.get(a);if(o){r(o);return}let s=t(a);for(let[t,n]of this._sniContexts)if(e(s,t)){r(n);return}if(this._tlsOptions.SNICallback)try{this._tlsOptions.SNICallback(n,(e,t)=>{if(e||!t){r(i);return}r(t)});return}catch{r(i);return}r(i)}listen(...e){return this.on(`connection`,e=>{this._upgradeTls(e)}),super.listen(...e)}_upgradeTls(e){let t=i(e);if(!t){this.emit(`tlsClientError`,a(),e),e.destroy();return}let n=t.connection;if(!n){let t=Error(`Cannot upgrade socket: no underlying connection`);this.emit(`tlsClientError`,t,e),e.destroy();return}let closeClaimedConnection=()=>{try{(t.connection??t.ioStream)?.close(null)}catch{}};if(!this._tlsCertificate&&this._sniContexts.size===0&&!this._tlsOptions.SNICallback){let t=Error(`TLS server has no certificate configured`);this.emit(`tlsClientError`,t,e),closeClaimedConnection(),e.destroy();return}let r=n;if(this._sniContexts.size===0&&!this._tlsOptions.SNICallback){let t=this._tlsCertificate;if(!t){let t=Error(`TLS server has no certificate configured`);this.emit(`tlsClientError`,t,e),closeClaimedConnection(),e.destroy();return}this._completeUpgrade(r,t,this._secureContext,e,closeClaimedConnection);return}let l=r.get_input_stream(),u=r.get_output_stream(),d=s.BufferedInputStream.new_sized(l,4096);d.fill_async(4096,c.PRIORITY_DEFAULT,null,(t,n)=>{let r=null;try{d.fill_finish(n),r=o(d.peek_buffer())}catch{}this._resolveSniContext(r,t=>{let n=t.certificate??this._tlsCertificate;if(!n){let t=Error(`SNI resolution returned no certificate`);this.emit(`tlsClientError`,t,e),closeClaimedConnection(),e.destroy();return}let r=new s.SimpleIOStream({inputStream:d,outputStream:u});this._completeUpgrade(r,n,t,e,closeClaimedConnection)})})}_completeUpgrade(e,t,n,i,a){try{let o=s.TlsServerConnection.new(e,t);this._tlsOptions.requestCert?o.authenticationMode=this._tlsOptions.rejectUnauthorized===!1?s.TlsAuthenticationMode.REQUESTED:s.TlsAuthenticationMode.REQUIRED:o.authenticationMode=s.TlsAuthenticationMode.NONE;let l=!!this._tlsOptions.requestCert&&this._tlsOptions.rejectUnauthorized!==!1,d=this._secureContext.caCertificates;o.connect(`accept-certificate`,(e,t,n)=>{if(!l)return!0;if(d.length===0)return!1;for(let e of d)try{if(t.verify(null,e)===s.TlsCertificateFlags.NO_FLAGS)return!0}catch{}return!1}),this._tlsOptions.ALPNProtocols&&this._tlsOptions.ALPNProtocols.length>0&&o.set_advertised_protocols(this._tlsOptions.ALPNProtocols);let f=new s.Cancellable;o.handshake_async(c.PRIORITY_DEFAULT,f,(e,t)=>{try{o.handshake_finish(t);let e=new r;e.encrypted=!0,e.authorized=!0,e._secureContext=n,e._setupTlsStreams(o),e.alpnProtocol=e.getAlpnProtocol(),e.once(`close`,()=>i.destroy()),e._startReading(),e.on(`error`,()=>{}),this.emit(`secureConnection`,e)}catch(e){let t=u(e,`handshake`,{});this.emit(`tlsClientError`,t,i),a(),i.destroy()}})}catch(e){let t=u(e,`tls_wrap`,{});this.emit(`tlsClientError`,t,i),a(),i.destroy()}}};function createServer(e,t){return typeof e==`function`?new TLSServer(void 0,e):new TLSServer(e,t)}export{TLSServer,createServer};
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{tlsCertToPeerCert as e}from"./internal/cert-utils.js";import{createSecureContext as t}from"./secure-context.js";import{createSessionAccess as n,hasTlsSessionAccess as r}from"./session-access.js";import i from"@girs/gio-2.0";import a from"@girs/glib-2.0";import{Buffer as o}from"node:buffer";import{Socket as s}from"node:net";import c from"node:process";function claimGjsifyConnection(e){let t=e;return typeof t?._claimConnection==`function`?t._claimConnection():null}var TLSSocket=class extends s{encrypted=!0;authorized=!1;authorizationError;alpnProtocol=!1;servername;_tlsConnection=null;_secureContext=null;_sessionAccess=null;_adoptedSocket=null;_awaitingSecure=!1;_afterSecure=[];_handshakeClaim=null;_certRejection=null;constructor(e,t){super(),e&&this._startClient(e,t??{})}_claimSocketStreams(e){let t=claimGjsifyConnection(e);return t||this.destroy(foreignSocketError()),t}_startClient(e,t){this.servername=t.servername||t.host||`localhost`,this._adoptedSocket=e,this._awaitingSecure=!0,this.once(`close`,()=>{e.destroyed||e.destroy()});let begin=()=>{this._adoptConnection(e).then(e=>{e&&this._performHandshake(t)})};e.connecting?e.once(`connect`,begin):c.nextTick(begin)}async _adoptConnection(e){if(this.destroyed||e.destroyed)return!1;let t=e;if(typeof t._detachReader!=`function`)return this.destroy(foreignSocketError()),!1;let n=this._claimSocketStreams(t);if(!n)return!1;let r=await t._detachReader();if(this.destroyed){try{(n.connection??n.ioStream)?.close(null)}catch{}return!1}if(r&&r.length>0){this.destroy(_upgradeRaceError());try{(n.connection??n.ioStream)?.close(null)}catch{}return!1}let i=this;i._connection=n.connection,i._ioStream=n.ioStream,i._inputStream=n.inputStream,i._outputStream=n.outputStream;let a=this,o=e;return a.remoteAddress=o.remoteAddress,a.remotePort=o.remotePort,a.remoteFamily=o.remoteFamily,a.localAddress=o.localAddress,a.localPort=o.localPort,a.connecting=!1,a.pending=!1,!0}_performHandshake(e){let n=this.servername||e.servername||e.host||`localhost`;this.servername=n;let o=e.port||this.remotePort||443,s=e.rejectUnauthorized!==!1,c=e.secureContext??t(e);this._secureContext=c;let l=e.checkServerIdentity,u=this,d=this._claimSocketStreams(u);if(!d)return;let f=d.connection;if(!f){this.destroy(Error(`No underlying connection for TLS upgrade`));return}this._handshakeClaim=d;let fail=e=>{this.destroy(e instanceof Error?e:Error(String(e)))};try{let t=i.NetworkAddress.new(n,o),p=i.TlsClientConnection.new(f,t);if(p.set_server_identity(t),e.session&&r())try{this._tlsConnection=p,this.setSession(e.session)}catch{}if(c.certificate)try{p.set_certificate(c.certificate)}catch(e){console.warn(`[tls] failed to set client certificate:`,e)}e.ALPNProtocols&&e.ALPNProtocols.length>0&&p.set_advertised_protocols(e.ALPNProtocols),p.connect(`accept-certificate`,(e,r,a)=>{if(!s)return!0;let o=a;for(let e of c.caCertificates)try{if(o=r.verify(t,e),o===i.TlsCertificateFlags.NO_FLAGS)return!0}catch{}return this._certRejection=_certRejection(r,o,n),!1}),u._pendingIo++,p.handshake_async(a.PRIORITY_DEFAULT,u._cancellable,(e,t)=>{let r=null;try{p.handshake_finish(t)}catch(e){r=e}if(this.destroyed){u._ioSettled();return}if(this._handshakeClaim=null,u._ioSettled(),r){this.authorized=!1;let e=this._certRejection,t=e?Object.assign(Error(e.message),{code:e.code}):r;this.authorizationError=t instanceof Error?t.message:String(t),u._connection=d.connection,u._ioStream=d.ioStream,fail(t);return}this._secureEstablished(p,n,s,l)})}catch(e){fail(e)}}_secureEstablished(e,t,n,i){if(this.authorized=!0,this._setupTlsStreams(e),this.alpnProtocol=this.getAlpnProtocol(),i){let e=i(t,this.getPeerCertificate());if(e&&(this.authorized=!1,this.authorizationError=e.message,n)){this.destroy(e);return}}let a=this;if(a._reading=!1,a._startReading(),r()){let e=this.getSession();e&&this.emit(`session`,e)}this.emit(`secureConnect`),this._flushAfterSecure()}_write(e,t,n){if(this._awaitingSecure){this._afterSecure.push({run:()=>super._write(e,t,n),abort:()=>n(_destroyedError())});return}super._write(e,t,n)}_flushAfterSecure(){let e=this._afterSecure;this._afterSecure=[];for(let t of e)t.run()}_destroy(e,t){let n=this._handshakeClaim;if(n){this._handshakeClaim=null;let e=this;e._connection=n.connection,e._ioStream=n.ioStream}let r=this._afterSecure;this._afterSecure=[];for(let e of r)e.abort();super._destroy(e,t)}_final(e){if(this._awaitingSecure){this._afterSecure.push({run:()=>this._final(e),abort:()=>{}});return}let t=this._tlsConnection;if(!t){super._final(e);return}let n=this,r=t.get_output_stream();n._pendingIo++,r.close_async(a.PRIORITY_DEFAULT,null,(t,i)=>{try{r.close_finish(i)}catch{}n._ioSettled(),e()})}_setupTlsStreams(e){this._awaitingSecure=!1,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=n(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;switch(this._tlsConnection.get_protocol_version()){case i.TlsProtocolVersion.TLS_1_0:return`TLSv1`;case i.TlsProtocolVersion.TLS_1_1:return`TLSv1.1`;case i.TlsProtocolVersion.TLS_1_2:return`TLSv1.2`;case i.TlsProtocolVersion.TLS_1_3:return`TLSv1.3`;default:return null}}getCipher(){return this._tlsConnection?{name:this._tlsConnection.get_ciphersuite_name()||`unknown`,version:this.getProtocol()||`unknown`}:null}getAlpnProtocol(){return this._tlsConnection&&this._tlsConnection.get_negotiated_protocol()||!1}getFinished(){if(!r())return;let e=this._getSessionAccess();if(e)try{return _bytesToBuffer(e.get_finished())}catch{return}}getPeerFinished(){if(!r())return;let e=this._getSessionAccess();if(e)try{return _bytesToBuffer(e.get_peer_finished())}catch{return}}getSession(){if(!r())return;let e=this._getSessionAccess();if(e)try{return _bytesToBuffer(e.get_session_data())}catch{return}}setSession(e){if(!r())return;let t=this._getSessionAccess();if(t)try{t.set_session_data(_bufferToBytes(e))}catch{}}isSessionReused(){if(!r())return!1;let e=this._getSessionAccess();if(!e)return!1;try{return e.is_session_reused()}catch{return!1}}};function _destroyedError(){let e=Error(`Cannot call write after a stream was destroyed`);return e.code=`ERR_STREAM_DESTROYED`,e}function _certRejection(e,t,n){return t&i.TlsCertificateFlags.BAD_IDENTITY?{code:`ERR_TLS_CERT_ALTNAME_INVALID`,message:`Hostname/IP does not match certificate's altnames: Host: ${n}. is not in the cert's altnames`}:t&i.TlsCertificateFlags.EXPIRED?{code:`CERT_HAS_EXPIRED`,message:`certificate has expired`}:t&i.TlsCertificateFlags.NOT_ACTIVATED?{code:`CERT_NOT_YET_VALID`,message:`certificate is not yet valid`}:e.subjectName&&e.subjectName===e.issuerName?{code:`DEPTH_ZERO_SELF_SIGNED_CERT`,message:`self-signed certificate`}:{code:`UNABLE_TO_VERIFY_LEAF_SIGNATURE`,message:`unable to verify the first certificate`}}function _upgradeRaceError(){let e=Error(`tls: data arrived on the socket before the TLS handshake could take over reading its input stream — Gio.TlsClientConnection has no API to replay already-read bytes into the handshake. This should not happen for a well-behaved STARTTLS peer.`);return e.code=`ERR_GJSIFY_TLS_UPGRADE_RACE`,e}function foreignSocketError(){let e=Error(`tls.connect({socket}) / new tls.TLSSocket(socket, …) can only upgrade a @gjsify/net Socket today — the given socket doesn't carry the Gio connection needed to build a Gio.TlsClientConnection. Generic Duplex support (Node's real contract) is tracked in status/open-todos/README.md.`);return e.code=`ERR_GJSIFY_TLS_FOREIGN_SOCKET`,e}function _bytesToBuffer(e){if(e==null)return;if(e instanceof Uint8Array)return o.from(e);let t=e;if(typeof t.toArray==`function`)try{return o.from(t.toArray())}catch{return}}function _bufferToBytes(e){return e}export{TLSSocket,claimGjsifyConnection,foreignSocketError};
1
+ import"./_virtual/_rolldown/runtime.js";import{tlsCertToPeerCert as e}from"./internal/cert-utils.js";import{createSecureContext as t}from"./secure-context.js";import{createSessionAccess as n,hasTlsSessionAccess as r}from"./session-access.js";import{createPeerVerifier as i,resolveRejectUnauthorized as a}from"./verify.js";import o from"@girs/gio-2.0";import s from"@girs/glib-2.0";import{Buffer as c}from"node:buffer";import{Socket as l}from"node:net";import u from"node:process";function claimGjsifyConnection(e){let t=e;return typeof t?._claimConnection==`function`?t._claimConnection():null}var TLSSocket=class extends l{encrypted=!0;authorized=!1;authorizationError;alpnProtocol=!1;servername;_tlsConnection=null;_secureContext=null;_sessionAccess=null;_adoptedSocket=null;_awaitingSecure=!1;_afterSecure=[];_handshakeClaim=null;constructor(e,t){super(),e&&this._startClient(e,t??{})}_claimSocketStreams(e){let t=claimGjsifyConnection(e);return t||this.destroy(foreignSocketError()),t}_startClient(e,t){this.servername=t.servername||t.host||`localhost`,this._adoptedSocket=e,this._awaitingSecure=!0,this.once(`close`,()=>{e.destroyed||e.destroy()});let begin=()=>{this._adoptConnection(e).then(e=>{e&&this._performHandshake(t)})};e.connecting?e.once(`connect`,begin):u.nextTick(begin)}async _adoptConnection(e){if(this.destroyed||e.destroyed)return!1;let t=e;if(typeof t._detachReader!=`function`)return this.destroy(foreignSocketError()),!1;let n=this._claimSocketStreams(t);if(!n)return!1;let r=await t._detachReader();if(this.destroyed){try{(n.connection??n.ioStream)?.close(null)}catch{}return!1}if(r&&r.length>0){this.destroy(_upgradeRaceError());try{(n.connection??n.ioStream)?.close(null)}catch{}return!1}let i=this;i._connection=n.connection,i._ioStream=n.ioStream,i._inputStream=n.inputStream,i._outputStream=n.outputStream;let a=this,o=e;return a.remoteAddress=o.remoteAddress,a.remotePort=o.remotePort,a.remoteFamily=o.remoteFamily,a.localAddress=o.localAddress,a.localPort=o.localPort,a.connecting=!1,a.pending=!1,!0}_performHandshake(e){let n=this.servername||e.servername||e.host||`localhost`;this.servername=n;let c=e.port||this.remotePort||443,l=a(e.rejectUnauthorized),u=e.secureContext??t(e);this._secureContext=u;let d=i({caCertificates:u.caCertificates,rejectUnauthorized:e.rejectUnauthorized,host:n,checkServerIdentity:e.checkServerIdentity}),f=this,p=this._claimSocketStreams(f);if(!p)return;let m=p.connection;if(!m){this.destroy(Error(`No underlying connection for TLS upgrade`));return}this._handshakeClaim=p;let fail=e=>{this.destroy(e instanceof Error?e:Error(String(e)))};try{let t=o.NetworkAddress.new(n,c),i=o.TlsClientConnection.new(m,t);if(i.set_server_identity(t),e.session&&r())try{this._tlsConnection=i,this.setSession(e.session)}catch{}if(u.certificate)try{i.set_certificate(u.certificate)}catch(e){console.warn(`[tls] failed to set client certificate:`,e)}e.ALPNProtocols&&e.ALPNProtocols.length>0&&i.set_advertised_protocols(e.ALPNProtocols),i.set_validation_flags(o.TlsCertificateFlags.NO_FLAGS),i.session_resumption_enabled=!1,f._pendingIo++,i.handshake_async(s.PRIORITY_DEFAULT,f._cancellable,(t,n)=>{let r=null;try{i.handshake_finish(n)}catch(e){r=e}if(this.destroyed){f._ioSettled();return}this._handshakeClaim=null,f._ioSettled();let a=r?null:this._verifyPeer(i,d,!!e.session),o=r??(l?a:null);if(o){this.authorized=!1,this.authorizationError=_authorizationError(o),f._connection=i,f._ioStream=i,fail(o);return}this._secureEstablished(i,a?_authorizationError(a):null)})}catch(e){fail(e)}}_verifyPeer(e,t,n){let r=e.get_peer_certificate();return r?(t.accept(r),t.error):n?null:_noPeerCertificateError()}_secureEstablished(e,t){this.authorized=t===null,this.authorizationError=t??void 0,this._setupTlsStreams(e),this.alpnProtocol=this.getAlpnProtocol();let n=this;if(n._reading=!1,n._startReading(),r()){let e=this.getSession();e&&this.emit(`session`,e)}this.emit(`secureConnect`),this._flushAfterSecure()}_write(e,t,n){if(this._awaitingSecure){this._afterSecure.push({run:()=>super._write(e,t,n),abort:()=>n(_destroyedError())});return}super._write(e,t,n)}_flushAfterSecure(){let e=this._afterSecure;this._afterSecure=[];for(let t of e)t.run()}_destroy(e,t){let n=this._handshakeClaim;if(n){this._handshakeClaim=null;let e=this;e._connection=n.connection,e._ioStream=n.ioStream}let r=this._afterSecure;this._afterSecure=[];for(let e of r)e.abort();super._destroy(e,t)}_final(e){if(this._awaitingSecure){this._afterSecure.push({run:()=>this._final(e),abort:()=>{}});return}let t=this._tlsConnection;if(!t){super._final(e);return}let n=this,r=t.get_output_stream();n._pendingIo++,r.close_async(s.PRIORITY_DEFAULT,null,(t,i)=>{try{r.close_finish(i)}catch{}n._ioSettled(),e()})}_setupTlsStreams(e){this._awaitingSecure=!1,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=n(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;switch(this._tlsConnection.get_protocol_version()){case o.TlsProtocolVersion.TLS_1_0:return`TLSv1`;case o.TlsProtocolVersion.TLS_1_1:return`TLSv1.1`;case o.TlsProtocolVersion.TLS_1_2:return`TLSv1.2`;case o.TlsProtocolVersion.TLS_1_3:return`TLSv1.3`;default:return null}}getCipher(){return this._tlsConnection?{name:this._tlsConnection.get_ciphersuite_name()||`unknown`,version:this.getProtocol()||`unknown`}:null}getAlpnProtocol(){return this._tlsConnection&&this._tlsConnection.get_negotiated_protocol()||!1}getFinished(){if(!r())return;let e=this._getSessionAccess();if(e)try{return _bytesToBuffer(e.get_finished())}catch{return}}getPeerFinished(){if(!r())return;let e=this._getSessionAccess();if(e)try{return _bytesToBuffer(e.get_peer_finished())}catch{return}}getSession(){if(!r())return;let e=this._getSessionAccess();if(e)try{return _bytesToBuffer(e.get_session_data())}catch{return}}setSession(e){if(!r())return;let t=this._getSessionAccess();if(t)try{t.set_session_data(_bufferToBytes(e))}catch{}}isSessionReused(){if(!r())return!1;let e=this._getSessionAccess();if(!e)return!1;try{return e.is_session_reused()}catch{return!1}}};function _destroyedError(){let e=Error(`Cannot call write after a stream was destroyed`);return e.code=`ERR_STREAM_DESTROYED`,e}function _authorizationError(e){return e instanceof Error?e.code??e.message:String(e)}function _noPeerCertificateError(){let e=Error(`the peer presented no certificate (resumed TLS session); nothing to verify`);return e.code=`ERR_GJSIFY_TLS_NO_PEER_CERTIFICATE`,e}function _upgradeRaceError(){let e=Error(`tls: data arrived on the socket before the TLS handshake could take over reading its input stream — Gio.TlsClientConnection has no API to replay already-read bytes into the handshake. This should not happen for a well-behaved STARTTLS peer.`);return e.code=`ERR_GJSIFY_TLS_UPGRADE_RACE`,e}function foreignSocketError(){let e=Error(`tls.connect({socket}) / new tls.TLSSocket(socket, …) can only upgrade a @gjsify/net Socket today — the given socket doesn't carry the Gio connection needed to build a Gio.TlsClientConnection. Generic Duplex support (Node's real contract) is tracked in status/open-todos/README.md.`);return e.code=`ERR_GJSIFY_TLS_FOREIGN_SOCKET`,e}function _bytesToBuffer(e){if(e==null)return;if(e instanceof Uint8Array)return c.from(e);let t=e;if(typeof t.toArray==`function`)try{return c.from(t.toArray())}catch{return}}function _bufferToBytes(e){return e}export{TLSSocket,claimGjsifyConnection,foreignSocketError};
package/lib/esm/verify.js CHANGED
@@ -1 +1 @@
1
- import"./_virtual/_rolldown/runtime.js";import{checkServerIdentity as e}from"./internal/hostname.js";import{tlsCertToPeerCert as t}from"./internal/cert-utils.js";import{createSecureContext as n}from"./secure-context.js";import r from"@girs/gio-2.0";import i from"@girs/glib-2.0";function resolveRejectUnauthorized(e){return e===void 0?i.getenv(`NODE_TLS_REJECT_UNAUTHORIZED`)!==`0`:e!==!1}function createPeerVerifier(n){let r=resolveRejectUnauthorized(n.rejectUnauthorized),i=n.checkServerIdentity??e,a=n.caCertificates??[],o=null;return{accept(e){return o=chainError(e,a)??i(n.host,t(e,!0))??null,o===null||!r},get error(){return o},get authorized(){return o===null},get authorizationError(){if(o)return o.code??o.message}}}const a=r.TlsCertificateFlags.VALIDATE_ALL&~r.TlsCertificateFlags.BAD_IDENTITY;function chainFlags(e,t){if(t.length===0)return r.TlsBackend.get_default().get_default_database().verify_chain(e,r.TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,null,null,r.TlsDatabaseVerifyFlags.NONE,null);let n=r.TlsCertificateFlags.UNKNOWN_CA;for(let i of t){let t=e.verify(null,i)&a;if(t===r.TlsCertificateFlags.NO_FLAGS)return t;t&r.TlsCertificateFlags.UNKNOWN_CA||(n=t)}return n}function isSelfSigned(e){return e.get_subject_name()===e.get_issuer_name()}function verifyError(e,t){let n=Error(t);return n.code=e,n}function chainError(e,t=[]){let n=chainFlags(e,t)&a,i=r.TlsCertificateFlags;if(n===i.NO_FLAGS)return null;if(n&i.UNKNOWN_CA){if(isSelfSigned(e))return verifyError(`DEPTH_ZERO_SELF_SIGNED_CERT`,`self-signed certificate`);let t=e;for(let e=t.get_issuer();e&&!e.is_same(t);e=t.get_issuer())t=e;return t===e?verifyError(`UNABLE_TO_VERIFY_LEAF_SIGNATURE`,`unable to verify the first certificate`):isSelfSigned(t)?verifyError(`SELF_SIGNED_CERT_IN_CHAIN`,`self-signed certificate in certificate chain`):verifyError(`UNABLE_TO_GET_ISSUER_CERT_LOCALLY`,`unable to get local issuer certificate`)}return n&i.REVOKED?verifyError(`CERT_REVOKED`,`certificate revoked`):n&i.EXPIRED?verifyError(`CERT_HAS_EXPIRED`,`certificate has expired`):n&i.NOT_ACTIVATED?verifyError(`CERT_NOT_YET_VALID`,`certificate is not yet valid`):n&i.INSECURE?verifyError(`CA_MD_TOO_WEAK`,`CA signature digest algorithm too weak`):verifyError(`CERT_REJECTED`,`certificate rejected`)}export{chainError,createPeerVerifier,n as createSecureContext,resolveRejectUnauthorized};
1
+ import"./_virtual/_rolldown/runtime.js";import{checkServerIdentity as e}from"./internal/hostname.js";import{tlsCertToPeerCert as t}from"./internal/cert-utils.js";import{createSecureContext as n}from"./secure-context.js";import r from"@girs/gio-2.0";import i from"@girs/glib-2.0";function resolveRejectUnauthorized(e){return e===void 0?i.getenv(`NODE_TLS_REJECT_UNAUTHORIZED`)!==`0`:e!==!1}function createPeerVerifier(n){let r=resolveRejectUnauthorized(n.rejectUnauthorized),i=n.checkServerIdentity??e,a=n.caCertificates??[],o=null;return{accept(e){return o=chainError(e,a)??i(n.host,t(e,!0))??null,o===null||!r},get error(){return o},get authorized(){return o===null},get authorizationError(){if(o)return o.code??o.message}}}const chainFlagsMask=()=>r.TlsCertificateFlags.VALIDATE_ALL&~r.TlsCertificateFlags.BAD_IDENTITY;function chainFlags(e,t){if(t.length===0)return r.TlsBackend.get_default().get_default_database().verify_chain(e,r.TLS_DATABASE_PURPOSE_AUTHENTICATE_SERVER,null,null,r.TlsDatabaseVerifyFlags.NONE,null);let n=r.TlsCertificateFlags.UNKNOWN_CA;for(let i of t){let t=e.verify(null,i)&chainFlagsMask();if(t===r.TlsCertificateFlags.NO_FLAGS)return t;t&r.TlsCertificateFlags.UNKNOWN_CA||(n=t)}return n}function isSelfSigned(e){return e.get_subject_name()===e.get_issuer_name()}function verifyError(e,t){let n=Error(t);return n.code=e,n}function chainError(e,t=[]){let n=chainFlags(e,t)&chainFlagsMask(),i=r.TlsCertificateFlags;if(n===i.NO_FLAGS)return null;if(n&i.UNKNOWN_CA){if(isSelfSigned(e))return verifyError(`DEPTH_ZERO_SELF_SIGNED_CERT`,`self-signed certificate`);let t=e;for(let e=t.get_issuer();e&&!e.is_same(t);e=t.get_issuer())t=e;return t===e?verifyError(`UNABLE_TO_VERIFY_LEAF_SIGNATURE`,`unable to verify the first certificate`):isSelfSigned(t)?verifyError(`SELF_SIGNED_CERT_IN_CHAIN`,`self-signed certificate in certificate chain`):verifyError(`UNABLE_TO_GET_ISSUER_CERT_LOCALLY`,`unable to get local issuer certificate`)}return n&i.REVOKED?verifyError(`CERT_REVOKED`,`certificate revoked`):n&i.EXPIRED?verifyError(`CERT_HAS_EXPIRED`,`certificate has expired`):n&i.NOT_ACTIVATED?verifyError(`CERT_NOT_YET_VALID`,`certificate is not yet valid`):n&i.INSECURE?verifyError(`CA_MD_TOO_WEAK`,`CA signature digest algorithm too weak`):verifyError(`CERT_REJECTED`,`certificate rejected`)}export{chainError,createPeerVerifier,n as createSecureContext,resolveRejectUnauthorized};
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: () => Promise<void>;
2
+ export default _default;
@@ -12,7 +12,7 @@ export interface TlsConnectOptions extends SecureContextOptions {
12
12
  ALPNProtocols?: string[];
13
13
  /** Pre-built secure context from createSecureContext(). */
14
14
  secureContext?: SecureContext;
15
- /** Custom server-identity check (runs after the GnuTLS-level check). */
15
+ /** Replaces the default hostname check, as in Node; runs on a chain that verified. */
16
16
  checkServerIdentity?: (host: string, cert: PeerCertificate) => Error | undefined;
17
17
  /**
18
18
  * Previously serialized session blob to attempt resumption with.
@@ -114,8 +114,6 @@ export declare class TLSSocket extends Socket {
114
114
  * leaked the descriptor.
115
115
  */
116
116
  private _handshakeClaim;
117
- /** Why 'accept-certificate' refused the peer, as Node's error code + message. */
118
- private _certRejection;
119
117
  constructor(socket?: Socket, options?: TlsConnectOptions);
120
118
  /**
121
119
  * @internal Adopt `providedSocket` — already connected, or still
@@ -185,6 +183,13 @@ export declare class TLSSocket extends Socket {
185
183
  * Reference: Node.js `lib/internal/tls/wrap.js` `TLSSocket.prototype._init`.
186
184
  */
187
185
  _performHandshake(options: TlsConnectOptions): void;
186
+ /**
187
+ * The error the peer's certificate earns, or null when it is authorized.
188
+ * A resumed session carries no certificate, so there is nothing to verify:
189
+ * only an explicit `session` option — the caller asked for it — may
190
+ * proceed that way.
191
+ */
192
+ private _verifyPeer;
188
193
  private _secureEstablished;
189
194
  _write(chunk: unknown, encoding: BufferEncoding, callback: (error?: Error | null) => void): void;
190
195
  private _flushAfterSecure;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/tls",
3
- "version": "0.55.0",
3
+ "version": "0.57.0",
4
4
  "description": "Node.js tls module for Gjs",
5
5
  "type": "module",
6
6
  "module": "lib/esm/index.js",
@@ -41,17 +41,17 @@
41
41
  "tls"
42
42
  ],
43
43
  "devDependencies": {
44
- "@gjsify/cli": "^0.55.0",
45
- "@gjsify/unit": "^0.55.0",
44
+ "@gjsify/cli": "^0.57.0",
45
+ "@gjsify/unit": "^0.57.0",
46
46
  "@types/node": "^25.9.2",
47
47
  "typescript": "^6.0.3"
48
48
  },
49
49
  "dependencies": {
50
50
  "@girs/gio-2.0": "5.4.0",
51
51
  "@girs/glib-2.0": "5.4.0",
52
- "@gjsify/net": "^0.55.0",
53
- "@gjsify/tls-native": "^0.55.0",
54
- "@gjsify/utils": "^0.55.0"
52
+ "@gjsify/net": "^0.57.0",
53
+ "@gjsify/tls-native": "^0.57.0",
54
+ "@gjsify/utils": "^0.57.0"
55
55
  },
56
56
  "gjsify": {
57
57
  "runtimes": {