@libp2p/tls 0.0.0-0321812e7

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.
@@ -0,0 +1,274 @@
1
+ import { Duplex as DuplexStream } from 'node:stream';
2
+ import { Ed25519PublicKey, Secp256k1PublicKey, marshalPublicKey, supportedKeys, unmarshalPrivateKey, unmarshalPublicKey } from '@libp2p/crypto/keys';
3
+ import { CodeError, InvalidCryptoExchangeError, UnexpectedPeerError } from '@libp2p/interface';
4
+ import { peerIdFromKeys } from '@libp2p/peer-id';
5
+ import { AsnConvert } from '@peculiar/asn1-schema';
6
+ import * as asn1X509 from '@peculiar/asn1-x509';
7
+ import { Crypto } from '@peculiar/webcrypto';
8
+ import * as x509 from '@peculiar/x509';
9
+ import * as asn1js from 'asn1js';
10
+ import { pushable } from 'it-pushable';
11
+ import { concat as uint8ArrayConcat } from 'uint8arrays/concat';
12
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
13
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
14
+ import { KeyType, PublicKey } from '../src/pb/index.js';
15
+ const crypto = new Crypto();
16
+ x509.cryptoProvider.set(crypto);
17
+ const LIBP2P_PUBLIC_KEY_EXTENSION = '1.3.6.1.4.1.53594.1.1';
18
+ const CERT_PREFIX = 'libp2p-tls-handshake:';
19
+ // https://github.com/libp2p/go-libp2p/blob/28c0f6ab32cd69e4b18e9e4b550ef6ce059a9d1a/p2p/security/tls/crypto.go#L265
20
+ const CERT_VALIDITY_PERIOD_FROM = 60 * 60 * 1000; // ~1 hour
21
+ // https://github.com/libp2p/go-libp2p/blob/28c0f6ab32cd69e4b18e9e4b550ef6ce059a9d1a/p2p/security/tls/crypto.go#L24C28-L24C44
22
+ const CERT_VALIDITY_PERIOD_TO = 100 * 365 * 24 * 60 * 60 * 1000; // ~100 years
23
+ export async function verifyPeerCertificate(rawCertificate, expectedPeerId, log) {
24
+ const now = Date.now();
25
+ const x509Cert = new x509.X509Certificate(rawCertificate);
26
+ if (x509Cert.notBefore.getTime() > now) {
27
+ log?.error('the certificate was not valid yet');
28
+ throw new CodeError('The certificate is not valid yet', 'ERR_INVALID_CERTIFICATE');
29
+ }
30
+ if (x509Cert.notAfter.getTime() < now) {
31
+ log?.error('the certificate has expired');
32
+ throw new CodeError('The certificate has expired', 'ERR_INVALID_CERTIFICATE');
33
+ }
34
+ const certSignatureValid = await x509Cert.verify();
35
+ if (!certSignatureValid) {
36
+ log?.error('certificate self signature was invalid');
37
+ throw new InvalidCryptoExchangeError('Invalid certificate self signature');
38
+ }
39
+ const certIsSelfSigned = await x509Cert.isSelfSigned();
40
+ if (!certIsSelfSigned) {
41
+ log?.error('certificate must be self signed');
42
+ throw new InvalidCryptoExchangeError('Certificate must be self signed');
43
+ }
44
+ const libp2pPublicKeyExtension = x509Cert.extensions[0];
45
+ if (libp2pPublicKeyExtension == null || libp2pPublicKeyExtension.type !== LIBP2P_PUBLIC_KEY_EXTENSION) {
46
+ log?.error('the certificate did not include the libp2p public key extension');
47
+ throw new CodeError('The certificate did not include the libp2p public key extension', 'ERR_INVALID_CERTIFICATE');
48
+ }
49
+ const { result: libp2pKeySequence } = asn1js.fromBER(libp2pPublicKeyExtension.value);
50
+ // @ts-expect-error deep chain
51
+ const remotePeerIdPb = libp2pKeySequence.valueBlock.value[0].valueBlock.valueHex;
52
+ const marshalledPeerId = new Uint8Array(remotePeerIdPb, 0, remotePeerIdPb.byteLength);
53
+ const remotePublicKey = PublicKey.decode(marshalledPeerId);
54
+ const remotePublicKeyData = remotePublicKey.data ?? new Uint8Array(0);
55
+ let remoteLibp2pPublicKey;
56
+ if (remotePublicKey.type === KeyType.Ed25519) {
57
+ remoteLibp2pPublicKey = new Ed25519PublicKey(remotePublicKeyData);
58
+ }
59
+ else if (remotePublicKey.type === KeyType.Secp256k1) {
60
+ remoteLibp2pPublicKey = new Secp256k1PublicKey(remotePublicKeyData);
61
+ }
62
+ else if (remotePublicKey.type === KeyType.RSA) {
63
+ remoteLibp2pPublicKey = supportedKeys.rsa.unmarshalRsaPublicKey(remotePublicKeyData);
64
+ }
65
+ else {
66
+ log?.error('unknown or unsupported key type', remotePublicKey.type);
67
+ throw new InvalidCryptoExchangeError('Unknown or unsupported key type');
68
+ }
69
+ // @ts-expect-error deep chain
70
+ const remoteSignature = libp2pKeySequence.valueBlock.value[1].valueBlock.valueHex;
71
+ const dataToVerify = encodeSignatureData(x509Cert.publicKey.rawData);
72
+ const result = await remoteLibp2pPublicKey.verify(dataToVerify, new Uint8Array(remoteSignature, 0, remoteSignature.byteLength));
73
+ if (!result) {
74
+ log?.error('invalid libp2p signature');
75
+ throw new InvalidCryptoExchangeError('Could not verify signature');
76
+ }
77
+ const marshalled = marshalPublicKey(remoteLibp2pPublicKey);
78
+ const remotePeerId = await peerIdFromKeys(marshalled);
79
+ if (expectedPeerId?.equals(remotePeerId) === false) {
80
+ log?.error('invalid peer id');
81
+ throw new UnexpectedPeerError();
82
+ }
83
+ return remotePeerId;
84
+ }
85
+ export async function generateCertificate(peerId) {
86
+ const now = Date.now();
87
+ const alg = {
88
+ name: 'ECDSA',
89
+ namedCurve: 'P-256',
90
+ hash: 'SHA-256'
91
+ };
92
+ const keys = await crypto.subtle.generateKey(alg, true, ['sign']);
93
+ const certPublicKeySpki = await crypto.subtle.exportKey('spki', keys.publicKey);
94
+ const dataToSign = encodeSignatureData(certPublicKeySpki);
95
+ if (peerId.privateKey == null) {
96
+ throw new InvalidCryptoExchangeError('Private key was missing from PeerId');
97
+ }
98
+ const privateKey = await unmarshalPrivateKey(peerId.privateKey);
99
+ const sig = await privateKey.sign(dataToSign);
100
+ let keyType;
101
+ let keyData;
102
+ if (peerId.publicKey == null) {
103
+ throw new CodeError('Public key missing from PeerId', 'ERR_INVALID_PEER_ID');
104
+ }
105
+ const publicKey = unmarshalPublicKey(peerId.publicKey);
106
+ if (peerId.type === 'Ed25519') {
107
+ // Ed25519: Only the 32 bytes of the public key
108
+ keyType = KeyType.Ed25519;
109
+ keyData = publicKey.marshal();
110
+ }
111
+ else if (peerId.type === 'secp256k1') {
112
+ // Secp256k1: Only the compressed form of the public key. 33 bytes.
113
+ keyType = KeyType.Secp256k1;
114
+ keyData = publicKey.marshal();
115
+ }
116
+ else if (peerId.type === 'RSA') {
117
+ // The rest of the keys are encoded as a SubjectPublicKeyInfo structure in PKIX, ASN.1 DER form.
118
+ keyType = KeyType.RSA;
119
+ keyData = publicKey.marshal();
120
+ }
121
+ else {
122
+ throw new CodeError('Unknown PeerId type', 'ERR_UNKNOWN_PEER_ID_TYPE');
123
+ }
124
+ const selfCert = await x509.X509CertificateGenerator.createSelfSigned({
125
+ serialNumber: uint8ArrayToString(crypto.getRandomValues(new Uint8Array(9)), 'base16'),
126
+ name: '',
127
+ notBefore: new Date(now - CERT_VALIDITY_PERIOD_FROM),
128
+ notAfter: new Date(now + CERT_VALIDITY_PERIOD_TO),
129
+ signingAlgorithm: alg,
130
+ keys,
131
+ extensions: [
132
+ new x509.Extension(LIBP2P_PUBLIC_KEY_EXTENSION, true, new asn1js.Sequence({
133
+ value: [
134
+ // publicKey
135
+ new asn1js.OctetString({
136
+ valueHex: PublicKey.encode({
137
+ type: keyType,
138
+ data: keyData
139
+ })
140
+ }),
141
+ // signature
142
+ new asn1js.OctetString({
143
+ valueHex: sig
144
+ })
145
+ ]
146
+ }).toBER())
147
+ ]
148
+ });
149
+ const certPrivateKeySpki = await crypto.subtle.exportKey('spki', keys.privateKey);
150
+ return {
151
+ cert: selfCert.toString(),
152
+ key: spkiToPEM(certPrivateKeySpki)
153
+ };
154
+ }
155
+ /**
156
+ * @see https://github.com/libp2p/specs/blob/master/tls/tls.md#libp2p-public-key-extension
157
+ */
158
+ export function encodeSignatureData(certPublicKey) {
159
+ const keyInfo = AsnConvert.parse(certPublicKey, asn1X509.SubjectPublicKeyInfo);
160
+ const bytes = AsnConvert.serialize(keyInfo);
161
+ return uint8ArrayConcat([
162
+ uint8ArrayFromString(CERT_PREFIX),
163
+ new Uint8Array(bytes, 0, bytes.byteLength)
164
+ ]);
165
+ }
166
+ function spkiToPEM(keydata) {
167
+ return formatAsPem(uint8ArrayToString(new Uint8Array(keydata), 'base64'));
168
+ }
169
+ function formatAsPem(str) {
170
+ let finalString = '-----BEGIN PRIVATE KEY-----\n';
171
+ while (str.length > 0) {
172
+ finalString += str.substring(0, 64) + '\n';
173
+ str = str.substring(64);
174
+ }
175
+ finalString = finalString + '-----END PRIVATE KEY-----';
176
+ return finalString;
177
+ }
178
+ export function itToStream(conn) {
179
+ const output = pushable();
180
+ const iterator = conn.source[Symbol.asyncIterator]();
181
+ const stream = new DuplexStream({
182
+ autoDestroy: false,
183
+ allowHalfOpen: true,
184
+ write(chunk, encoding, callback) {
185
+ output.push(chunk);
186
+ callback();
187
+ },
188
+ read() {
189
+ iterator.next()
190
+ .then(result => {
191
+ if (result.done === true) {
192
+ this.push(null);
193
+ }
194
+ else {
195
+ this.push(result.value);
196
+ }
197
+ }, (err) => {
198
+ this.destroy(err);
199
+ });
200
+ }
201
+ });
202
+ // @ts-expect-error return type of sink is unknown
203
+ conn.sink(output)
204
+ .catch((err) => {
205
+ stream.destroy(err);
206
+ });
207
+ return stream;
208
+ }
209
+ export function streamToIt(stream) {
210
+ const output = {
211
+ source: (async function* () {
212
+ const output = pushable();
213
+ stream.addListener('data', (buf) => {
214
+ output.push(buf.subarray());
215
+ });
216
+ // both ends closed
217
+ stream.addListener('close', () => {
218
+ output.end();
219
+ });
220
+ stream.addListener('error', (err) => {
221
+ output.end(err);
222
+ });
223
+ // just writable end closed
224
+ stream.addListener('finish', () => {
225
+ output.end();
226
+ });
227
+ try {
228
+ yield* output;
229
+ }
230
+ catch (err) {
231
+ stream.destroy(err);
232
+ throw err;
233
+ }
234
+ })(),
235
+ sink: async (source) => {
236
+ try {
237
+ for await (const buf of source) {
238
+ const sendMore = stream.write(buf.subarray());
239
+ if (!sendMore) {
240
+ await waitForBackpressure(stream);
241
+ }
242
+ }
243
+ // close writable end
244
+ stream.end();
245
+ }
246
+ catch (err) {
247
+ stream.destroy(err);
248
+ throw err;
249
+ }
250
+ }
251
+ };
252
+ return output;
253
+ }
254
+ async function waitForBackpressure(stream) {
255
+ await new Promise((resolve, reject) => {
256
+ const continueListener = () => {
257
+ cleanUp();
258
+ resolve();
259
+ };
260
+ const stopListener = (err) => {
261
+ cleanUp();
262
+ reject(err ?? new Error('Stream ended'));
263
+ };
264
+ const cleanUp = () => {
265
+ stream.removeListener('drain', continueListener);
266
+ stream.removeListener('end', stopListener);
267
+ stream.removeListener('error', stopListener);
268
+ };
269
+ stream.addListener('drain', continueListener);
270
+ stream.addListener('end', stopListener);
271
+ stream.addListener('error', stopListener);
272
+ });
273
+ }
274
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@libp2p/tls",
3
+ "version": "0.0.0-0321812e7",
4
+ "description": "A connection encrypter that uses TLS 1.3",
5
+ "license": "Apache-2.0 OR MIT",
6
+ "homepage": "https://github.com/libp2p/js-libp2p/tree/main/packages/connection-encrypter-tls#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/libp2p/js-libp2p.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/libp2p/js-libp2p/issues"
13
+ },
14
+ "publishConfig": {
15
+ "access": "public",
16
+ "provenance": true
17
+ },
18
+ "type": "module",
19
+ "types": "./dist/src/index.d.ts",
20
+ "files": [
21
+ "src",
22
+ "dist",
23
+ "!dist/test",
24
+ "!**/*.tsbuildinfo"
25
+ ],
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/src/index.d.ts",
29
+ "import": "./dist/src/index.js"
30
+ }
31
+ },
32
+ "eslintConfig": {
33
+ "extends": "ipfs",
34
+ "parserOptions": {
35
+ "project": true,
36
+ "sourceType": "module"
37
+ }
38
+ },
39
+ "scripts": {
40
+ "start": "node dist/src/main.js",
41
+ "build": "aegir build --bundle false",
42
+ "test": "aegir test -t node",
43
+ "clean": "aegir clean",
44
+ "generate": "protons ./src/pb/index.proto",
45
+ "lint": "aegir lint",
46
+ "test:node": "aegir test -t node --cov",
47
+ "dep-check": "aegir dep-check"
48
+ },
49
+ "dependencies": {
50
+ "@libp2p/crypto": "4.0.1-0321812e7",
51
+ "@libp2p/interface": "1.1.2-0321812e7",
52
+ "@libp2p/peer-id": "4.0.5-0321812e7",
53
+ "@peculiar/asn1-schema": "^2.3.8",
54
+ "@peculiar/asn1-x509": "^2.3.8",
55
+ "@peculiar/webcrypto": "^1.4.5",
56
+ "@peculiar/x509": "^1.9.7",
57
+ "asn1js": "^3.0.5",
58
+ "it-pushable": "^3.2.3",
59
+ "it-stream-types": "^2.0.1",
60
+ "protons-runtime": "^5.4.0",
61
+ "uint8arraylist": "^2.4.8",
62
+ "uint8arrays": "^5.0.1"
63
+ },
64
+ "devDependencies": {
65
+ "@libp2p/interface-compliance-tests": "5.2.0-0321812e7",
66
+ "@libp2p/logger": "4.0.5-0321812e7",
67
+ "@libp2p/peer-id-factory": "4.0.5-0321812e7",
68
+ "@multiformats/multiaddr": "^12.1.14",
69
+ "aegir": "^42.2.3",
70
+ "protons": "^7.5.0",
71
+ "sinon": "^17.0.1"
72
+ },
73
+ "sideEffects": false
74
+ }
package/src/index.ts ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * Implements the spec at https://github.com/libp2p/specs/blob/master/tls/tls.md
5
+ *
6
+ * @example
7
+ *
8
+ * ```typescript
9
+ * import { createLibp2p } from 'libp2p'
10
+ * import { tls } from '@libp2p/tls'
11
+ *
12
+ * const node = await createLibp2p({
13
+ * // ...other options
14
+ * connectionEncryption: [
15
+ * tls()
16
+ * ]
17
+ * })
18
+ * ```
19
+ */
20
+
21
+ import { TLS } from './tls.js'
22
+ import type { ComponentLogger, ConnectionEncrypter } from '@libp2p/interface'
23
+
24
+ export const PROTOCOL = '/tls/1.0.0'
25
+
26
+ export interface TLSComponents {
27
+ logger: ComponentLogger
28
+ }
29
+
30
+ export interface TLSInit {
31
+ /**
32
+ * The peer id exchange must complete within this many milliseconds
33
+ * (default: 1000)
34
+ */
35
+ timeout?: number
36
+ }
37
+
38
+ export function tls (init?: TLSInit): (components: TLSComponents) => ConnectionEncrypter {
39
+ return (components) => new TLS(components, init)
40
+ }
@@ -0,0 +1,13 @@
1
+ syntax = "proto3";
2
+
3
+ enum KeyType {
4
+ RSA = 0;
5
+ Ed25519 = 1;
6
+ Secp256k1 = 2;
7
+ ECDSA = 3;
8
+ }
9
+
10
+ message PublicKey {
11
+ optional KeyType type = 1;
12
+ optional bytes data = 2;
13
+ }
@@ -0,0 +1,95 @@
1
+ /* eslint-disable import/export */
2
+ /* eslint-disable complexity */
3
+ /* eslint-disable @typescript-eslint/no-namespace */
4
+ /* eslint-disable @typescript-eslint/no-unnecessary-boolean-literal-compare */
5
+ /* eslint-disable @typescript-eslint/no-empty-interface */
6
+
7
+ import { type Codec, decodeMessage, encodeMessage, enumeration, message } from 'protons-runtime'
8
+ import type { Uint8ArrayList } from 'uint8arraylist'
9
+
10
+ export enum KeyType {
11
+ RSA = 'RSA',
12
+ Ed25519 = 'Ed25519',
13
+ Secp256k1 = 'Secp256k1',
14
+ ECDSA = 'ECDSA'
15
+ }
16
+
17
+ enum __KeyTypeValues {
18
+ RSA = 0,
19
+ Ed25519 = 1,
20
+ Secp256k1 = 2,
21
+ ECDSA = 3
22
+ }
23
+
24
+ export namespace KeyType {
25
+ export const codec = (): Codec<KeyType> => {
26
+ return enumeration<KeyType>(__KeyTypeValues)
27
+ }
28
+ }
29
+ export interface PublicKey {
30
+ type?: KeyType
31
+ data?: Uint8Array
32
+ }
33
+
34
+ export namespace PublicKey {
35
+ let _codec: Codec<PublicKey>
36
+
37
+ export const codec = (): Codec<PublicKey> => {
38
+ if (_codec == null) {
39
+ _codec = message<PublicKey>((obj, w, opts = {}) => {
40
+ if (opts.lengthDelimited !== false) {
41
+ w.fork()
42
+ }
43
+
44
+ if (obj.type != null) {
45
+ w.uint32(8)
46
+ KeyType.codec().encode(obj.type, w)
47
+ }
48
+
49
+ if (obj.data != null) {
50
+ w.uint32(18)
51
+ w.bytes(obj.data)
52
+ }
53
+
54
+ if (opts.lengthDelimited !== false) {
55
+ w.ldelim()
56
+ }
57
+ }, (reader, length) => {
58
+ const obj: any = {}
59
+
60
+ const end = length == null ? reader.len : reader.pos + length
61
+
62
+ while (reader.pos < end) {
63
+ const tag = reader.uint32()
64
+
65
+ switch (tag >>> 3) {
66
+ case 1: {
67
+ obj.type = KeyType.codec().decode(reader)
68
+ break
69
+ }
70
+ case 2: {
71
+ obj.data = reader.bytes()
72
+ break
73
+ }
74
+ default: {
75
+ reader.skipType(tag & 7)
76
+ break
77
+ }
78
+ }
79
+ }
80
+
81
+ return obj
82
+ })
83
+ }
84
+
85
+ return _codec
86
+ }
87
+
88
+ export const encode = (obj: Partial<PublicKey>): Uint8Array => {
89
+ return encodeMessage(obj, PublicKey.codec())
90
+ }
91
+
92
+ export const decode = (buf: Uint8Array | Uint8ArrayList): PublicKey => {
93
+ return decodeMessage(buf, PublicKey.codec())
94
+ }
95
+ }
package/src/tls.ts ADDED
@@ -0,0 +1,115 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * Implements the spec at https://github.com/libp2p/specs/blob/master/tls/tls.md
5
+ *
6
+ * @example
7
+ *
8
+ * ```typescript
9
+ * import { createLibp2p } from 'libp2p'
10
+ * import { tls } from '@libp2p/tls'
11
+ *
12
+ * const node = await createLibp2p({
13
+ * // ...other options
14
+ * connectionEncryption: [
15
+ * tls()
16
+ * ]
17
+ * })
18
+ * ```
19
+ */
20
+
21
+ import { TLSSocket, type TLSSocketOptions, connect } from 'node:tls'
22
+ import { CodeError } from '@libp2p/interface'
23
+ import { generateCertificate, verifyPeerCertificate, itToStream, streamToIt } from './utils.js'
24
+ import { PROTOCOL } from './index.js'
25
+ import type { TLSComponents, TLSInit } from './index.js'
26
+ import type { MultiaddrConnection, ConnectionEncrypter, SecuredConnection, PeerId, Logger } from '@libp2p/interface'
27
+ import type { Duplex } from 'it-stream-types'
28
+ import type { Uint8ArrayList } from 'uint8arraylist'
29
+
30
+ export class TLS implements ConnectionEncrypter {
31
+ public protocol: string = PROTOCOL
32
+ private readonly log: Logger
33
+ private readonly timeout: number
34
+
35
+ constructor (components: TLSComponents, init: TLSInit = {}) {
36
+ this.log = components.logger.forComponent('libp2p:tls')
37
+ this.timeout = init.timeout ?? 1000
38
+ }
39
+
40
+ async secureInbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localId: PeerId, conn: Stream, remoteId?: PeerId): Promise<SecuredConnection<Stream>> {
41
+ return this._encrypt(localId, conn, false, remoteId)
42
+ }
43
+
44
+ async secureOutbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localId: PeerId, conn: Stream, remoteId?: PeerId): Promise<SecuredConnection<Stream>> {
45
+ return this._encrypt(localId, conn, true, remoteId)
46
+ }
47
+
48
+ /**
49
+ * Encrypt connection
50
+ */
51
+ async _encrypt <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localId: PeerId, conn: Stream, isServer: boolean, remoteId?: PeerId): Promise<SecuredConnection<Stream>> {
52
+ const opts: TLSSocketOptions = {
53
+ ...await generateCertificate(localId),
54
+ isServer,
55
+ // require TLS 1.3 or later
56
+ minVersion: 'TLSv1.3',
57
+ maxVersion: 'TLSv1.3',
58
+ // accept self-signed certificates
59
+ rejectUnauthorized: false
60
+ }
61
+
62
+ let socket: TLSSocket
63
+
64
+ if (isServer) {
65
+ socket = new TLSSocket(itToStream(conn), {
66
+ ...opts,
67
+ // require clients to send certificates
68
+ requestCert: true
69
+ })
70
+ } else {
71
+ socket = connect({
72
+ socket: itToStream(conn),
73
+ ...opts
74
+ })
75
+ }
76
+
77
+ return new Promise((resolve, reject) => {
78
+ const abortTimeout = setTimeout(() => {
79
+ socket.destroy(new CodeError('Handshake timeout', 'ERR_HANDSHAKE_TIMEOUT'))
80
+ }, this.timeout)
81
+
82
+ const verifyRemote = (): void => {
83
+ const remote = socket.getPeerCertificate()
84
+
85
+ verifyPeerCertificate(remote.raw, remoteId, this.log)
86
+ .then(remotePeer => {
87
+ this.log('remote certificate ok, remote peer %p', remotePeer)
88
+
89
+ resolve({
90
+ remotePeer,
91
+ conn: {
92
+ ...conn,
93
+ ...streamToIt(socket)
94
+ }
95
+ })
96
+ })
97
+ .catch(err => {
98
+ reject(err)
99
+ })
100
+ .finally(() => {
101
+ clearTimeout(abortTimeout)
102
+ })
103
+ }
104
+
105
+ socket.on('error', err => {
106
+ reject(err)
107
+ clearTimeout(abortTimeout)
108
+ })
109
+ socket.on('secure', (evt) => {
110
+ this.log('verifying remote certificate')
111
+ verifyRemote()
112
+ })
113
+ })
114
+ }
115
+ }