@libp2p/tls 2.0.18-dd71d8a86 → 2.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.
@@ -17,11 +17,13 @@
17
17
  * })
18
18
  * ```
19
19
  */
20
- import type { ComponentLogger, ConnectionEncrypter, PrivateKey } from '@libp2p/interface';
20
+ import type { ComponentLogger, ConnectionEncrypter, Metrics, PrivateKey, Upgrader } from '@libp2p/interface';
21
21
  export declare const PROTOCOL = "/tls/1.0.0";
22
22
  export interface TLSComponents {
23
23
  privateKey: PrivateKey;
24
24
  logger: ComponentLogger;
25
+ upgrader: Upgrader;
26
+ metrics?: Metrics;
25
27
  }
26
28
  export declare function tls(): (components: TLSComponents) => ConnectionEncrypter;
27
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEzF,eAAO,MAAM,QAAQ,eAAe,CAAA;AAEpC,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,CAAA;IACtB,MAAM,EAAE,eAAe,CAAA;CACxB;AAED,wBAAgB,GAAG,IAAK,CAAC,UAAU,EAAE,aAAa,KAAK,mBAAmB,CAEzE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAE5G,eAAO,MAAM,QAAQ,eAAe,CAAA;AAEpC,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,CAAA;IACtB,MAAM,EAAE,eAAe,CAAA;IACvB,QAAQ,EAAE,QAAQ,CAAA;IAClB,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,wBAAgB,GAAG,IAAK,CAAC,UAAU,EAAE,aAAa,KAAK,mBAAmB,CAEzE"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAG9B,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAA;AAOpC,MAAM,UAAU,GAAG;IACjB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,CAAA;AAC5C,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAG9B,MAAM,CAAC,MAAM,QAAQ,GAAG,YAAY,CAAA;AASpC,MAAM,UAAU,GAAG;IACjB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,CAAA;AAC5C,CAAC"}
package/dist/src/tls.d.ts CHANGED
@@ -25,7 +25,8 @@ import type { Uint8ArrayList } from 'uint8arraylist';
25
25
  export declare class TLS implements ConnectionEncrypter {
26
26
  protocol: string;
27
27
  private readonly log;
28
- private readonly privateKey;
28
+ private readonly components;
29
+ private readonly metrics;
29
30
  constructor(components: TLSComponents);
30
31
  readonly [Symbol.toStringTag] = "@libp2p/tls";
31
32
  readonly [serviceCapabilities]: string[];
@@ -1 +1 @@
1
- {"version":3,"file":"tls.d.ts","sourceRoot":"","sources":["../../src/tls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAIvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAU,uBAAuB,EAAc,MAAM,mBAAmB,CAAA;AACjJ,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD,qBAAa,GAAI,YAAW,mBAAmB;IACtC,QAAQ,EAAE,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAC5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAY;gBAE1B,UAAU,EAAE,aAAa;IAKtC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,iBAAgB;IAE7C,QAAQ,CAAC,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAEvC;IAEK,aAAa,CAAE,MAAM,SAAS,MAAM,CAAC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,GAAG,mBAAmB,EAAG,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAI9L,cAAc,CAAE,MAAM,SAAS,MAAM,CAAC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,GAAG,mBAAmB,EAAG,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAIrM;;OAEG;IACG,QAAQ,CAAE,MAAM,SAAS,MAAM,CAAC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,GAAG,mBAAmB,EAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;CAkEnN"}
1
+ {"version":3,"file":"tls.d.ts","sourceRoot":"","sources":["../../src/tls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,EAA8B,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAInF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC/C,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAU,uBAAuB,EAAoC,MAAM,mBAAmB,CAAA;AACvK,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD,qBAAa,GAAI,YAAW,mBAAmB;IACtC,QAAQ,EAAE,MAAM,CAAW;IAClC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAC5B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAe;IAC1C,OAAO,CAAC,QAAQ,CAAC,OAAO,CASvB;gBAEY,UAAU,EAAE,aAAa;IA2BtC,QAAQ,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,iBAAgB;IAE7C,QAAQ,CAAC,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAEvC;IAEK,aAAa,CAAE,MAAM,SAAS,MAAM,CAAC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,GAAG,mBAAmB,EAAG,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAI9L,cAAc,CAAE,MAAM,SAAS,MAAM,CAAC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,GAAG,mBAAmB,EAAG,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAIrM;;OAEG;IACG,QAAQ,CAAE,MAAM,SAAS,MAAM,CAAC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,GAAG,mBAAmB,EAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;CAqJnN"}
package/dist/src/tls.js CHANGED
@@ -18,17 +18,40 @@
18
18
  * ```
19
19
  */
20
20
  import { TLSSocket, connect } from 'node:tls';
21
- import { serviceCapabilities } from '@libp2p/interface';
21
+ import { InvalidCryptoExchangeError, serviceCapabilities } from '@libp2p/interface';
22
22
  import { HandshakeTimeoutError } from './errors.js';
23
23
  import { generateCertificate, verifyPeerCertificate, itToStream, streamToIt } from './utils.js';
24
24
  import { PROTOCOL } from './index.js';
25
25
  export class TLS {
26
26
  protocol = PROTOCOL;
27
27
  log;
28
- privateKey;
28
+ components;
29
+ metrics;
29
30
  constructor(components) {
30
31
  this.log = components.logger.forComponent('libp2p:tls');
31
- this.privateKey = components.privateKey;
32
+ this.components = components;
33
+ this.metrics = {
34
+ server: {
35
+ events: components.metrics?.registerCounterGroup('libp2p_tls_server_events_total', {
36
+ label: 'event',
37
+ help: 'Total count of TLS connection encryption events by type'
38
+ }),
39
+ errors: components.metrics?.registerCounterGroup('libp2p_tls_server_errors_total', {
40
+ label: 'event',
41
+ help: 'Total count of TLS connection encryption errors by type'
42
+ })
43
+ },
44
+ client: {
45
+ events: components.metrics?.registerCounterGroup('libp2p_tls_server_events_total', {
46
+ label: 'event',
47
+ help: 'Total count of TLS connection encryption events by type'
48
+ }),
49
+ errors: components.metrics?.registerCounterGroup('libp2p_tls_server_errors_total', {
50
+ label: 'event',
51
+ help: 'Total count of TLS connection encryption errors by type'
52
+ })
53
+ }
54
+ };
32
55
  }
33
56
  [Symbol.toStringTag] = '@libp2p/tls';
34
57
  [serviceCapabilities] = [
@@ -44,14 +67,35 @@ export class TLS {
44
67
  * Encrypt connection
45
68
  */
46
69
  async _encrypt(conn, isServer, options) {
70
+ let streamMuxer;
47
71
  const opts = {
48
- ...await generateCertificate(this.privateKey),
72
+ ...await generateCertificate(this.components.privateKey),
49
73
  isServer,
50
74
  // require TLS 1.3 or later
51
75
  minVersion: 'TLSv1.3',
52
76
  maxVersion: 'TLSv1.3',
53
77
  // accept self-signed certificates
54
- rejectUnauthorized: false
78
+ rejectUnauthorized: false,
79
+ // early negotiation of muxer via ALPN protocols
80
+ ALPNProtocols: [
81
+ ...this.components.upgrader.getStreamMuxers().keys(),
82
+ 'libp2p'
83
+ ],
84
+ ALPNCallback: ({ protocols }) => {
85
+ this.log.trace('received protocols %s', protocols);
86
+ let chosenProtocol;
87
+ for (const protocol of protocols) {
88
+ if (protocol === 'libp2p') {
89
+ chosenProtocol = 'libp2p';
90
+ }
91
+ streamMuxer = this.components.upgrader.getStreamMuxers().get(protocol);
92
+ if (streamMuxer != null) {
93
+ chosenProtocol = protocol;
94
+ break;
95
+ }
96
+ }
97
+ return chosenProtocol;
98
+ }
55
99
  };
56
100
  let socket;
57
101
  if (isServer) {
@@ -69,39 +113,89 @@ export class TLS {
69
113
  }
70
114
  return new Promise((resolve, reject) => {
71
115
  options?.signal?.addEventListener('abort', () => {
72
- const err = new HandshakeTimeoutError();
73
- socket.destroy(err);
74
- reject(err);
116
+ this.metrics[isServer ? 'server' : 'client'].events?.increment({
117
+ abort: true
118
+ });
119
+ this.metrics[isServer ? 'server' : 'client'].errors?.increment({
120
+ encrypt_abort: true
121
+ });
122
+ socket.emit('error', new HandshakeTimeoutError());
75
123
  });
76
124
  const verifyRemote = () => {
77
125
  const remote = socket.getPeerCertificate();
78
126
  verifyPeerCertificate(remote.raw, options?.remotePeer, this.log)
79
127
  .then(remotePeer => {
80
128
  this.log('remote certificate ok, remote peer %p', remotePeer);
129
+ if (!isServer && typeof socket.alpnProtocol === 'string') {
130
+ streamMuxer = this.components.upgrader.getStreamMuxers().get(socket.alpnProtocol);
131
+ if (streamMuxer == null) {
132
+ this.log.error('selected muxer that did not exist');
133
+ }
134
+ }
135
+ // 'libp2p' is a special protocol - if it's sent the remote does not
136
+ // support early muxer negotiation
137
+ if (!isServer && typeof socket.alpnProtocol === 'string' && socket.alpnProtocol !== 'libp2p') {
138
+ this.log.trace('got early muxer', socket.alpnProtocol);
139
+ streamMuxer = this.components.upgrader.getStreamMuxers().get(socket.alpnProtocol);
140
+ if (streamMuxer == null) {
141
+ const err = new InvalidCryptoExchangeError(`Selected muxer ${socket.alpnProtocol} did not exist`);
142
+ this.log.error(`Selected muxer ${socket.alpnProtocol} did not exist - %e`, err);
143
+ if (isAbortable(conn)) {
144
+ conn.abort(err);
145
+ reject(err);
146
+ }
147
+ }
148
+ }
81
149
  resolve({
82
150
  remotePeer,
83
151
  conn: {
84
152
  ...conn,
85
153
  ...streamToIt(socket)
86
- }
154
+ },
155
+ streamMuxer
87
156
  });
88
157
  })
89
158
  .catch((err) => {
90
- reject(err);
159
+ this.metrics[isServer ? 'server' : 'client'].errors?.increment({
160
+ verify_peer_certificate: true
161
+ });
162
+ socket.emit('error', err);
91
163
  });
92
164
  };
93
165
  socket.on('error', (err) => {
166
+ this.log.error('error encrypting %s connection - %e', isServer ? 'server' : 'client', err);
167
+ if (err.name !== 'HandshakeTimeoutError') {
168
+ this.metrics[isServer ? 'server' : 'client'].events?.increment({
169
+ error: true
170
+ });
171
+ }
172
+ socket.destroy(err);
173
+ if (isAbortable(conn)) {
174
+ conn.abort(err);
175
+ }
94
176
  reject(err);
95
177
  });
96
178
  socket.once('secure', () => {
97
179
  this.log('verifying remote certificate');
180
+ this.metrics[isServer ? 'server' : 'client'].events?.increment({
181
+ secure: true
182
+ });
98
183
  verifyRemote();
99
184
  });
100
- })
101
- .catch(err => {
102
- socket.destroy(err);
103
- throw err;
185
+ socket.on('connect', () => {
186
+ this.metrics[isServer ? 'server' : 'client'].events?.increment({
187
+ connect: true
188
+ });
189
+ });
190
+ socket.on('close', () => {
191
+ this.metrics[isServer ? 'server' : 'client'].events?.increment({
192
+ close: true
193
+ });
194
+ });
104
195
  });
105
196
  }
106
197
  }
198
+ function isAbortable(obj) {
199
+ return typeof obj?.abort === 'function';
200
+ }
107
201
  //# sourceMappingURL=tls.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tls.js","sourceRoot":"","sources":["../../src/tls.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tls.js","sourceRoot":"","sources":["../../src/tls.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ {
2
+ "TLSComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_tls.TLSComponents.html",
3
+ ".:TLSComponents": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_tls.TLSComponents.html",
4
+ "PROTOCOL": "https://libp2p.github.io/js-libp2p/variables/_libp2p_tls.PROTOCOL.html",
5
+ ".:PROTOCOL": "https://libp2p.github.io/js-libp2p/variables/_libp2p_tls.PROTOCOL.html",
6
+ "tls": "https://libp2p.github.io/js-libp2p/functions/_libp2p_tls.tls.html",
7
+ ".:tls": "https://libp2p.github.io/js-libp2p/functions/_libp2p_tls.tls.html"
8
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@libp2p/tls",
3
- "version": "2.0.18-dd71d8a86",
3
+ "version": "2.1.0",
4
4
  "description": "A connection encrypter that uses TLS 1.3",
5
5
  "license": "Apache-2.0 OR MIT",
6
6
  "homepage": "https://github.com/libp2p/js-libp2p/tree/main/packages/connection-encrypter-tls#readme",
@@ -48,9 +48,9 @@
48
48
  "doc-check": "aegir doc-check"
49
49
  },
50
50
  "dependencies": {
51
- "@libp2p/crypto": "5.0.14-dd71d8a86",
52
- "@libp2p/interface": "2.6.1-dd71d8a86",
53
- "@libp2p/peer-id": "5.0.15-dd71d8a86",
51
+ "@libp2p/crypto": "^5.0.15",
52
+ "@libp2p/interface": "^2.7.0",
53
+ "@libp2p/peer-id": "^5.0.16",
54
54
  "@peculiar/asn1-schema": "^2.3.13",
55
55
  "@peculiar/asn1-x509": "^2.3.13",
56
56
  "@peculiar/webcrypto": "^1.5.0",
@@ -63,11 +63,12 @@
63
63
  "uint8arrays": "^5.1.0"
64
64
  },
65
65
  "devDependencies": {
66
- "@libp2p/logger": "5.1.11-dd71d8a86",
66
+ "@libp2p/logger": "^5.1.12",
67
67
  "aegir": "^45.1.1",
68
68
  "it-pair": "^2.0.6",
69
69
  "protons": "^7.6.0",
70
- "sinon": "^19.0.2"
70
+ "sinon": "^19.0.2",
71
+ "sinon-ts": "^2.0.0"
71
72
  },
72
73
  "browser": {
73
74
  "./dist/src/tls.js": "./dist/src/tls.browser.js"
package/src/index.ts CHANGED
@@ -19,13 +19,15 @@
19
19
  */
20
20
 
21
21
  import { TLS } from './tls.js'
22
- import type { ComponentLogger, ConnectionEncrypter, PrivateKey } from '@libp2p/interface'
22
+ import type { ComponentLogger, ConnectionEncrypter, Metrics, PrivateKey, Upgrader } from '@libp2p/interface'
23
23
 
24
24
  export const PROTOCOL = '/tls/1.0.0'
25
25
 
26
26
  export interface TLSComponents {
27
27
  privateKey: PrivateKey
28
28
  logger: ComponentLogger
29
+ upgrader: Upgrader
30
+ metrics?: Metrics
29
31
  }
30
32
 
31
33
  export function tls (): (components: TLSComponents) => ConnectionEncrypter {
package/src/tls.ts CHANGED
@@ -19,23 +19,55 @@
19
19
  */
20
20
 
21
21
  import { TLSSocket, type TLSSocketOptions, connect } from 'node:tls'
22
- import { serviceCapabilities } from '@libp2p/interface'
22
+ import { InvalidCryptoExchangeError, serviceCapabilities } from '@libp2p/interface'
23
23
  import { HandshakeTimeoutError } from './errors.js'
24
24
  import { generateCertificate, verifyPeerCertificate, itToStream, streamToIt } from './utils.js'
25
25
  import { PROTOCOL } from './index.js'
26
26
  import type { TLSComponents } from './index.js'
27
- import type { MultiaddrConnection, ConnectionEncrypter, SecuredConnection, Logger, SecureConnectionOptions, PrivateKey } from '@libp2p/interface'
27
+ import type { MultiaddrConnection, ConnectionEncrypter, SecuredConnection, Logger, SecureConnectionOptions, CounterGroup, StreamMuxerFactory } from '@libp2p/interface'
28
28
  import type { Duplex } from 'it-stream-types'
29
29
  import type { Uint8ArrayList } from 'uint8arraylist'
30
30
 
31
31
  export class TLS implements ConnectionEncrypter {
32
32
  public protocol: string = PROTOCOL
33
33
  private readonly log: Logger
34
- private readonly privateKey: PrivateKey
34
+ private readonly components: TLSComponents
35
+ private readonly metrics: {
36
+ server: {
37
+ events?: CounterGroup
38
+ errors?: CounterGroup
39
+ }
40
+ client: {
41
+ events?: CounterGroup
42
+ errors?: CounterGroup
43
+ }
44
+ }
35
45
 
36
46
  constructor (components: TLSComponents) {
37
47
  this.log = components.logger.forComponent('libp2p:tls')
38
- this.privateKey = components.privateKey
48
+ this.components = components
49
+ this.metrics = {
50
+ server: {
51
+ events: components.metrics?.registerCounterGroup('libp2p_tls_server_events_total', {
52
+ label: 'event',
53
+ help: 'Total count of TLS connection encryption events by type'
54
+ }),
55
+ errors: components.metrics?.registerCounterGroup('libp2p_tls_server_errors_total', {
56
+ label: 'event',
57
+ help: 'Total count of TLS connection encryption errors by type'
58
+ })
59
+ },
60
+ client: {
61
+ events: components.metrics?.registerCounterGroup('libp2p_tls_server_events_total', {
62
+ label: 'event',
63
+ help: 'Total count of TLS connection encryption events by type'
64
+ }),
65
+ errors: components.metrics?.registerCounterGroup('libp2p_tls_server_errors_total', {
66
+ label: 'event',
67
+ help: 'Total count of TLS connection encryption errors by type'
68
+ })
69
+ }
70
+ }
39
71
  }
40
72
 
41
73
  readonly [Symbol.toStringTag] = '@libp2p/tls'
@@ -56,14 +88,41 @@ export class TLS implements ConnectionEncrypter {
56
88
  * Encrypt connection
57
89
  */
58
90
  async _encrypt <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (conn: Stream, isServer: boolean, options?: SecureConnectionOptions): Promise<SecuredConnection<Stream>> {
91
+ let streamMuxer: StreamMuxerFactory | undefined
92
+
59
93
  const opts: TLSSocketOptions = {
60
- ...await generateCertificate(this.privateKey),
94
+ ...await generateCertificate(this.components.privateKey),
61
95
  isServer,
62
96
  // require TLS 1.3 or later
63
97
  minVersion: 'TLSv1.3',
64
98
  maxVersion: 'TLSv1.3',
65
99
  // accept self-signed certificates
66
- rejectUnauthorized: false
100
+ rejectUnauthorized: false,
101
+
102
+ // early negotiation of muxer via ALPN protocols
103
+ ALPNProtocols: [
104
+ ...this.components.upgrader.getStreamMuxers().keys(),
105
+ 'libp2p'
106
+ ],
107
+ ALPNCallback: ({ protocols }) => {
108
+ this.log.trace('received protocols %s', protocols)
109
+ let chosenProtocol: string | undefined
110
+
111
+ for (const protocol of protocols) {
112
+ if (protocol === 'libp2p') {
113
+ chosenProtocol = 'libp2p'
114
+ }
115
+
116
+ streamMuxer = this.components.upgrader.getStreamMuxers().get(protocol)
117
+
118
+ if (streamMuxer != null) {
119
+ chosenProtocol = protocol
120
+ break
121
+ }
122
+ }
123
+
124
+ return chosenProtocol
125
+ }
67
126
  }
68
127
 
69
128
  let socket: TLSSocket
@@ -83,9 +142,13 @@ export class TLS implements ConnectionEncrypter {
83
142
 
84
143
  return new Promise<SecuredConnection<Stream>>((resolve, reject) => {
85
144
  options?.signal?.addEventListener('abort', () => {
86
- const err = new HandshakeTimeoutError()
87
- socket.destroy(err)
88
- reject(err)
145
+ this.metrics[isServer ? 'server' : 'client'].events?.increment({
146
+ abort: true
147
+ })
148
+ this.metrics[isServer ? 'server' : 'client'].errors?.increment({
149
+ encrypt_abort: true
150
+ })
151
+ socket.emit('error', new HandshakeTimeoutError())
89
152
  })
90
153
 
91
154
  const verifyRemote = (): void => {
@@ -95,30 +158,90 @@ export class TLS implements ConnectionEncrypter {
95
158
  .then(remotePeer => {
96
159
  this.log('remote certificate ok, remote peer %p', remotePeer)
97
160
 
161
+ if (!isServer && typeof socket.alpnProtocol === 'string') {
162
+ streamMuxer = this.components.upgrader.getStreamMuxers().get(socket.alpnProtocol)
163
+
164
+ if (streamMuxer == null) {
165
+ this.log.error('selected muxer that did not exist')
166
+ }
167
+ }
168
+
169
+ // 'libp2p' is a special protocol - if it's sent the remote does not
170
+ // support early muxer negotiation
171
+ if (!isServer && typeof socket.alpnProtocol === 'string' && socket.alpnProtocol !== 'libp2p') {
172
+ this.log.trace('got early muxer', socket.alpnProtocol)
173
+ streamMuxer = this.components.upgrader.getStreamMuxers().get(socket.alpnProtocol)
174
+
175
+ if (streamMuxer == null) {
176
+ const err = new InvalidCryptoExchangeError(`Selected muxer ${socket.alpnProtocol} did not exist`)
177
+ this.log.error(`Selected muxer ${socket.alpnProtocol} did not exist - %e`, err)
178
+
179
+ if (isAbortable(conn)) {
180
+ conn.abort(err)
181
+ reject(err)
182
+ }
183
+ }
184
+ }
185
+
98
186
  resolve({
99
187
  remotePeer,
100
188
  conn: {
101
189
  ...conn,
102
190
  ...streamToIt(socket)
103
- }
191
+ },
192
+ streamMuxer
104
193
  })
105
194
  })
106
195
  .catch((err: Error) => {
107
- reject(err)
196
+ this.metrics[isServer ? 'server' : 'client'].errors?.increment({
197
+ verify_peer_certificate: true
198
+ })
199
+ socket.emit('error', err)
108
200
  })
109
201
  }
110
202
 
111
203
  socket.on('error', (err: Error) => {
204
+ this.log.error('error encrypting %s connection - %e', isServer ? 'server' : 'client', err)
205
+
206
+ if (err.name !== 'HandshakeTimeoutError') {
207
+ this.metrics[isServer ? 'server' : 'client'].events?.increment({
208
+ error: true
209
+ })
210
+ }
211
+
212
+ socket.destroy(err)
213
+
214
+ if (isAbortable(conn)) {
215
+ conn.abort(err)
216
+ }
217
+
112
218
  reject(err)
113
219
  })
114
220
  socket.once('secure', () => {
115
221
  this.log('verifying remote certificate')
222
+ this.metrics[isServer ? 'server' : 'client'].events?.increment({
223
+ secure: true
224
+ })
116
225
  verifyRemote()
117
226
  })
118
- })
119
- .catch(err => {
120
- socket.destroy(err)
121
- throw err
227
+ socket.on('connect', () => {
228
+ this.metrics[isServer ? 'server' : 'client'].events?.increment({
229
+ connect: true
230
+ })
122
231
  })
232
+ socket.on('close', () => {
233
+ this.metrics[isServer ? 'server' : 'client'].events?.increment({
234
+ close: true
235
+ })
236
+ })
237
+ })
123
238
  }
124
239
  }
240
+
241
+ interface Abortable {
242
+ abort (err: Error): void
243
+ }
244
+
245
+ function isAbortable <T> (obj: T & Partial<Abortable>): obj is T & Abortable {
246
+ return typeof obj?.abort === 'function'
247
+ }