@libp2p/tls 0.0.0 → 1.0.0-0c7bbbb07
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/index.d.ts +2 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -78
- package/dist/src/index.js.map +1 -1
- package/dist/src/tls.d.ts +36 -0
- package/dist/src/tls.d.ts.map +1 -0
- package/dist/src/tls.js +100 -0
- package/dist/src/tls.js.map +1 -0
- package/dist/src/utils.d.ts +8 -2
- package/dist/src/utils.d.ts.map +1 -1
- package/dist/src/utils.js +134 -17
- package/dist/src/utils.js.map +1 -1
- package/package.json +18 -15
- package/src/index.ts +4 -89
- package/src/tls.ts +115 -0
- package/src/utils.ts +152 -19
- package/dist/typedoc-urls.json +0 -8
package/dist/src/index.d.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @packageDocumentation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* This should not be used in production should be used for research purposes only.
|
|
4
|
+
* Implements the spec at https://github.com/libp2p/specs/blob/master/tls/tls.md
|
|
7
5
|
*
|
|
8
6
|
* @example
|
|
9
7
|
*
|
|
@@ -20,6 +18,7 @@
|
|
|
20
18
|
* ```
|
|
21
19
|
*/
|
|
22
20
|
import type { ComponentLogger, ConnectionEncrypter } from '@libp2p/interface';
|
|
21
|
+
export declare const PROTOCOL = "/tls/1.0.0";
|
|
23
22
|
export interface TLSComponents {
|
|
24
23
|
logger: ComponentLogger;
|
|
25
24
|
}
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAE7E,eAAO,MAAM,QAAQ,eAAe,CAAA;AAEpC,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,eAAe,CAAA;CACxB;AAED,MAAM,WAAW,OAAO;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,wBAAgB,GAAG,CAAE,IAAI,CAAC,EAAE,OAAO,GAAG,CAAC,UAAU,EAAE,aAAa,KAAK,mBAAmB,CAEvF"}
|
package/dist/src/index.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @packageDocumentation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* This should not be used in production should be used for research purposes only.
|
|
4
|
+
* Implements the spec at https://github.com/libp2p/specs/blob/master/tls/tls.md
|
|
7
5
|
*
|
|
8
6
|
* @example
|
|
9
7
|
*
|
|
@@ -19,81 +17,9 @@
|
|
|
19
17
|
* })
|
|
20
18
|
* ```
|
|
21
19
|
*/
|
|
22
|
-
import {
|
|
23
|
-
|
|
24
|
-
// @ts-expect-error no types
|
|
25
|
-
import itToStream from 'it-to-stream';
|
|
26
|
-
// @ts-expect-error no types
|
|
27
|
-
import streamToIt from 'stream-to-it';
|
|
28
|
-
import { generateCertificate, verifyPeerCertificate } from './utils.js';
|
|
29
|
-
const PROTOCOL = '/tls/1.0.0';
|
|
30
|
-
class TLS {
|
|
31
|
-
protocol = PROTOCOL;
|
|
32
|
-
// private readonly log: Logger
|
|
33
|
-
// private readonly timeout: number
|
|
34
|
-
// constructor (components: TLSComponents, init: TLSInit = {}) {
|
|
35
|
-
// this.log = components.logger.forComponent('libp2p:tls')
|
|
36
|
-
// this.timeout = init.timeout ?? 1000
|
|
37
|
-
// }
|
|
38
|
-
async secureInbound(localId, conn, remoteId) {
|
|
39
|
-
return this._encrypt(localId, conn, false, remoteId);
|
|
40
|
-
}
|
|
41
|
-
async secureOutbound(localId, conn, remoteId) {
|
|
42
|
-
return this._encrypt(localId, conn, true, remoteId);
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Encrypt connection
|
|
46
|
-
*/
|
|
47
|
-
async _encrypt(localId, conn, isServer, remoteId) {
|
|
48
|
-
const opts = {
|
|
49
|
-
...await generateCertificate(localId),
|
|
50
|
-
isServer,
|
|
51
|
-
requestCert: true,
|
|
52
|
-
// accept self-signed certificates
|
|
53
|
-
rejectUnauthorized: false,
|
|
54
|
-
// require TLS 1.3 or later
|
|
55
|
-
minVersion: 'TLSv1.3'
|
|
56
|
-
};
|
|
57
|
-
let socket;
|
|
58
|
-
if (isServer) {
|
|
59
|
-
socket = new TLSSocket(itToStream.duplex(conn), opts);
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
socket = connect({
|
|
63
|
-
socket: itToStream.duplex(conn),
|
|
64
|
-
...opts
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
return new Promise((resolve, reject) => {
|
|
68
|
-
function verifyRemote() {
|
|
69
|
-
const remote = socket.getPeerCertificate();
|
|
70
|
-
verifyPeerCertificate(remote.raw)
|
|
71
|
-
.then(remotePeer => {
|
|
72
|
-
if (remoteId?.equals(remotePeer) === false) {
|
|
73
|
-
throw new UnexpectedPeerError();
|
|
74
|
-
}
|
|
75
|
-
const outputStream = streamToIt.duplex(socket);
|
|
76
|
-
conn.source = outputStream.source;
|
|
77
|
-
conn.sink = outputStream.sink;
|
|
78
|
-
resolve({
|
|
79
|
-
remotePeer,
|
|
80
|
-
conn
|
|
81
|
-
});
|
|
82
|
-
})
|
|
83
|
-
.catch(err => {
|
|
84
|
-
reject(err);
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
socket.on('error', err => {
|
|
88
|
-
reject(err);
|
|
89
|
-
});
|
|
90
|
-
socket.on('secure', (evt) => {
|
|
91
|
-
verifyRemote();
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
}
|
|
20
|
+
import { TLS } from './tls.js';
|
|
21
|
+
export const PROTOCOL = '/tls/1.0.0';
|
|
96
22
|
export function tls(init) {
|
|
97
|
-
return (components) => new TLS();
|
|
23
|
+
return (components) => new TLS(components, init);
|
|
98
24
|
}
|
|
99
25
|
//# sourceMappingURL=index.js.map
|
package/dist/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA
|
|
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;AAcpC,MAAM,UAAU,GAAG,CAAE,IAAc;IACjC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;AAClD,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
import type { TLSComponents, TLSInit } from './index.js';
|
|
21
|
+
import type { MultiaddrConnection, ConnectionEncrypter, SecuredConnection, PeerId } from '@libp2p/interface';
|
|
22
|
+
import type { Duplex } from 'it-stream-types';
|
|
23
|
+
import type { Uint8ArrayList } from 'uint8arraylist';
|
|
24
|
+
export declare class TLS implements ConnectionEncrypter {
|
|
25
|
+
protocol: string;
|
|
26
|
+
private readonly log;
|
|
27
|
+
private readonly timeout;
|
|
28
|
+
constructor(components: TLSComponents, init?: TLSInit);
|
|
29
|
+
secureInbound<Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection>(localId: PeerId, conn: Stream, remoteId?: PeerId): Promise<SecuredConnection<Stream>>;
|
|
30
|
+
secureOutbound<Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection>(localId: PeerId, conn: Stream, remoteId?: PeerId): Promise<SecuredConnection<Stream>>;
|
|
31
|
+
/**
|
|
32
|
+
* Encrypt connection
|
|
33
|
+
*/
|
|
34
|
+
_encrypt<Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection>(localId: PeerId, conn: Stream, isServer: boolean, remoteId?: PeerId): Promise<SecuredConnection<Stream>>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=tls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls.d.ts","sourceRoot":"","sources":["../../src/tls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,OAAO,KAAK,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AACxD,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,EAAU,MAAM,mBAAmB,CAAA;AACpH,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,OAAO,CAAQ;gBAEnB,UAAU,EAAE,aAAa,EAAE,IAAI,GAAE,OAAY;IAKpD,aAAa,CAAE,MAAM,SAAS,MAAM,CAAC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,GAAG,mBAAmB,EAAG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAI/L,cAAc,CAAE,MAAM,SAAS,MAAM,CAAC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,GAAG,mBAAmB,EAAG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAItM;;OAEG;IACG,QAAQ,CAAE,MAAM,SAAS,MAAM,CAAC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,GAAG,mBAAmB,EAAG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC;CAgEpN"}
|
package/dist/src/tls.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
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
|
+
import { TLSSocket, connect } from 'node:tls';
|
|
21
|
+
import { CodeError } from '@libp2p/interface';
|
|
22
|
+
import { generateCertificate, verifyPeerCertificate, itToStream, streamToIt } from './utils.js';
|
|
23
|
+
import { PROTOCOL } from './index.js';
|
|
24
|
+
export class TLS {
|
|
25
|
+
protocol = PROTOCOL;
|
|
26
|
+
log;
|
|
27
|
+
timeout;
|
|
28
|
+
constructor(components, init = {}) {
|
|
29
|
+
this.log = components.logger.forComponent('libp2p:tls');
|
|
30
|
+
this.timeout = init.timeout ?? 1000;
|
|
31
|
+
}
|
|
32
|
+
async secureInbound(localId, conn, remoteId) {
|
|
33
|
+
return this._encrypt(localId, conn, false, remoteId);
|
|
34
|
+
}
|
|
35
|
+
async secureOutbound(localId, conn, remoteId) {
|
|
36
|
+
return this._encrypt(localId, conn, true, remoteId);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Encrypt connection
|
|
40
|
+
*/
|
|
41
|
+
async _encrypt(localId, conn, isServer, remoteId) {
|
|
42
|
+
const opts = {
|
|
43
|
+
...await generateCertificate(localId),
|
|
44
|
+
isServer,
|
|
45
|
+
// require TLS 1.3 or later
|
|
46
|
+
minVersion: 'TLSv1.3',
|
|
47
|
+
maxVersion: 'TLSv1.3',
|
|
48
|
+
// accept self-signed certificates
|
|
49
|
+
rejectUnauthorized: false
|
|
50
|
+
};
|
|
51
|
+
let socket;
|
|
52
|
+
if (isServer) {
|
|
53
|
+
socket = new TLSSocket(itToStream(conn), {
|
|
54
|
+
...opts,
|
|
55
|
+
// require clients to send certificates
|
|
56
|
+
requestCert: true
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
socket = connect({
|
|
61
|
+
socket: itToStream(conn),
|
|
62
|
+
...opts
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
const abortTimeout = setTimeout(() => {
|
|
67
|
+
socket.destroy(new CodeError('Handshake timeout', 'ERR_HANDSHAKE_TIMEOUT'));
|
|
68
|
+
}, this.timeout);
|
|
69
|
+
const verifyRemote = () => {
|
|
70
|
+
const remote = socket.getPeerCertificate();
|
|
71
|
+
verifyPeerCertificate(remote.raw, remoteId, this.log)
|
|
72
|
+
.then(remotePeer => {
|
|
73
|
+
this.log('remote certificate ok, remote peer %p', remotePeer);
|
|
74
|
+
resolve({
|
|
75
|
+
remotePeer,
|
|
76
|
+
conn: {
|
|
77
|
+
...conn,
|
|
78
|
+
...streamToIt(socket)
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
})
|
|
82
|
+
.catch(err => {
|
|
83
|
+
reject(err);
|
|
84
|
+
})
|
|
85
|
+
.finally(() => {
|
|
86
|
+
clearTimeout(abortTimeout);
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
socket.on('error', err => {
|
|
90
|
+
reject(err);
|
|
91
|
+
clearTimeout(abortTimeout);
|
|
92
|
+
});
|
|
93
|
+
socket.on('secure', (evt) => {
|
|
94
|
+
this.log('verifying remote certificate');
|
|
95
|
+
verifyRemote();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=tls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tls.js","sourceRoot":"","sources":["../../src/tls.ts"],"names":[],"mappings":"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"}
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Duplex as DuplexStream } from 'node:stream';
|
|
3
|
+
import type { PeerId, Logger } from '@libp2p/interface';
|
|
4
|
+
import type { Duplex } from 'it-stream-types';
|
|
5
|
+
import type { Uint8ArrayList } from 'uint8arraylist';
|
|
6
|
+
export declare function verifyPeerCertificate(rawCertificate: Uint8Array, expectedPeerId?: PeerId, log?: Logger): Promise<PeerId>;
|
|
3
7
|
export declare function generateCertificate(peerId: PeerId): Promise<{
|
|
4
8
|
cert: string;
|
|
5
9
|
key: string;
|
|
@@ -8,4 +12,6 @@ export declare function generateCertificate(peerId: PeerId): Promise<{
|
|
|
8
12
|
* @see https://github.com/libp2p/specs/blob/master/tls/tls.md#libp2p-public-key-extension
|
|
9
13
|
*/
|
|
10
14
|
export declare function encodeSignatureData(certPublicKey: ArrayBuffer): Uint8Array;
|
|
15
|
+
export declare function itToStream(conn: Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>>): DuplexStream;
|
|
16
|
+
export declare function streamToIt(stream: DuplexStream): Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>>;
|
|
11
17
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/src/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,aAAa,CAAA;AAcpD,OAAO,KAAK,EAAE,MAAM,EAAgC,MAAM,EAAE,MAAM,mBAAmB,CAAA;AACrF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAYpD,wBAAsB,qBAAqB,CAAE,cAAc,EAAE,UAAU,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA0E/H;AAED,wBAAsB,mBAAmB,CAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,CA8EjG;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAE,aAAa,EAAE,WAAW,GAAG,UAAU,CAQ3E;AAmBD,wBAAgB,UAAU,CAAE,IAAI,EAAE,MAAM,CAAC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,GAAG,YAAY,CAgCnG;AAED,wBAAgB,UAAU,CAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC,cAAc,CAAC,UAAU,GAAG,cAAc,CAAC,CAAC,CA+CrG"}
|
package/dist/src/utils.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { Duplex as DuplexStream } from 'node:stream';
|
|
1
2
|
import { Ed25519PublicKey, Secp256k1PublicKey, marshalPublicKey, supportedKeys, unmarshalPrivateKey, unmarshalPublicKey } from '@libp2p/crypto/keys';
|
|
2
|
-
import { CodeError, InvalidCryptoExchangeError } from '@libp2p/interface';
|
|
3
|
+
import { CodeError, InvalidCryptoExchangeError, UnexpectedPeerError } from '@libp2p/interface';
|
|
3
4
|
import { peerIdFromKeys } from '@libp2p/peer-id';
|
|
4
5
|
import { AsnConvert } from '@peculiar/asn1-schema';
|
|
5
6
|
import * as asn1X509 from '@peculiar/asn1-x509';
|
|
6
7
|
import { Crypto } from '@peculiar/webcrypto';
|
|
7
8
|
import * as x509 from '@peculiar/x509';
|
|
8
9
|
import * as asn1js from 'asn1js';
|
|
10
|
+
import { pushable } from 'it-pushable';
|
|
9
11
|
import { concat as uint8ArrayConcat } from 'uint8arrays/concat';
|
|
10
12
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
|
|
11
13
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
|
|
@@ -18,48 +20,67 @@ const CERT_PREFIX = 'libp2p-tls-handshake:';
|
|
|
18
20
|
const CERT_VALIDITY_PERIOD_FROM = 60 * 60 * 1000; // ~1 hour
|
|
19
21
|
// https://github.com/libp2p/go-libp2p/blob/28c0f6ab32cd69e4b18e9e4b550ef6ce059a9d1a/p2p/security/tls/crypto.go#L24C28-L24C44
|
|
20
22
|
const CERT_VALIDITY_PERIOD_TO = 100 * 365 * 24 * 60 * 60 * 1000; // ~100 years
|
|
21
|
-
export async function verifyPeerCertificate(rawCertificate) {
|
|
23
|
+
export async function verifyPeerCertificate(rawCertificate, expectedPeerId, log) {
|
|
22
24
|
const now = Date.now();
|
|
23
25
|
const x509Cert = new x509.X509Certificate(rawCertificate);
|
|
24
26
|
if (x509Cert.notBefore.getTime() > now) {
|
|
27
|
+
log?.error('the certificate was not valid yet');
|
|
25
28
|
throw new CodeError('The certificate is not valid yet', 'ERR_INVALID_CERTIFICATE');
|
|
26
29
|
}
|
|
27
30
|
if (x509Cert.notAfter.getTime() < now) {
|
|
31
|
+
log?.error('the certificate has expired');
|
|
28
32
|
throw new CodeError('The certificate has expired', 'ERR_INVALID_CERTIFICATE');
|
|
29
33
|
}
|
|
30
|
-
|
|
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
|
+
}
|
|
31
44
|
const libp2pPublicKeyExtension = x509Cert.extensions[0];
|
|
32
45
|
if (libp2pPublicKeyExtension == null || libp2pPublicKeyExtension.type !== LIBP2P_PUBLIC_KEY_EXTENSION) {
|
|
46
|
+
log?.error('the certificate did not include the libp2p public key extension');
|
|
33
47
|
throw new CodeError('The certificate did not include the libp2p public key extension', 'ERR_INVALID_CERTIFICATE');
|
|
34
48
|
}
|
|
35
49
|
const { result: libp2pKeySequence } = asn1js.fromBER(libp2pPublicKeyExtension.value);
|
|
36
50
|
// @ts-expect-error deep chain
|
|
37
51
|
const remotePeerIdPb = libp2pKeySequence.valueBlock.value[0].valueBlock.valueHex;
|
|
38
52
|
const marshalledPeerId = new Uint8Array(remotePeerIdPb, 0, remotePeerIdPb.byteLength);
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
let
|
|
42
|
-
if (
|
|
43
|
-
|
|
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);
|
|
44
58
|
}
|
|
45
|
-
else if (
|
|
46
|
-
|
|
59
|
+
else if (remotePublicKey.type === KeyType.Secp256k1) {
|
|
60
|
+
remoteLibp2pPublicKey = new Secp256k1PublicKey(remotePublicKeyData);
|
|
47
61
|
}
|
|
48
|
-
else if (
|
|
49
|
-
|
|
62
|
+
else if (remotePublicKey.type === KeyType.RSA) {
|
|
63
|
+
remoteLibp2pPublicKey = supportedKeys.rsa.unmarshalRsaPublicKey(remotePublicKeyData);
|
|
50
64
|
}
|
|
51
65
|
else {
|
|
52
|
-
|
|
66
|
+
log?.error('unknown or unsupported key type', remotePublicKey.type);
|
|
67
|
+
throw new InvalidCryptoExchangeError('Unknown or unsupported key type');
|
|
53
68
|
}
|
|
54
69
|
// @ts-expect-error deep chain
|
|
55
70
|
const remoteSignature = libp2pKeySequence.valueBlock.value[1].valueBlock.valueHex;
|
|
56
71
|
const dataToVerify = encodeSignatureData(x509Cert.publicKey.rawData);
|
|
57
|
-
const result = await
|
|
72
|
+
const result = await remoteLibp2pPublicKey.verify(dataToVerify, new Uint8Array(remoteSignature, 0, remoteSignature.byteLength));
|
|
58
73
|
if (!result) {
|
|
59
|
-
|
|
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();
|
|
60
82
|
}
|
|
61
|
-
|
|
62
|
-
return peerIdFromKeys(marshalled);
|
|
83
|
+
return remotePeerId;
|
|
63
84
|
}
|
|
64
85
|
export async function generateCertificate(peerId) {
|
|
65
86
|
const now = Date.now();
|
|
@@ -154,4 +175,100 @@ function formatAsPem(str) {
|
|
|
154
175
|
finalString = finalString + '-----END PRIVATE KEY-----';
|
|
155
176
|
return finalString;
|
|
156
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
|
+
}
|
|
157
274
|
//# sourceMappingURL=utils.js.map
|
package/dist/src/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,aAAa,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AACpJ,OAAO,EAAE,SAAS,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@libp2p/tls",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0-0c7bbbb07",
|
|
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",
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
"bugs": {
|
|
12
12
|
"url": "https://github.com/libp2p/js-libp2p/issues"
|
|
13
13
|
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public",
|
|
16
|
+
"provenance": true
|
|
17
|
+
},
|
|
14
18
|
"type": "module",
|
|
15
19
|
"types": "./dist/src/index.d.ts",
|
|
16
20
|
"files": [
|
|
@@ -43,28 +47,27 @@
|
|
|
43
47
|
"dep-check": "aegir dep-check"
|
|
44
48
|
},
|
|
45
49
|
"dependencies": {
|
|
46
|
-
"@libp2p/crypto": "
|
|
47
|
-
"@libp2p/interface": "
|
|
48
|
-
"@libp2p/peer-id": "
|
|
50
|
+
"@libp2p/crypto": "4.0.2-0c7bbbb07",
|
|
51
|
+
"@libp2p/interface": "1.1.3-0c7bbbb07",
|
|
52
|
+
"@libp2p/peer-id": "4.0.6-0c7bbbb07",
|
|
49
53
|
"@peculiar/asn1-schema": "^2.3.8",
|
|
50
54
|
"@peculiar/asn1-x509": "^2.3.8",
|
|
51
55
|
"@peculiar/webcrypto": "^1.4.5",
|
|
52
|
-
"@peculiar/x509": "^1.9.
|
|
56
|
+
"@peculiar/x509": "^1.9.7",
|
|
53
57
|
"asn1js": "^3.0.5",
|
|
58
|
+
"it-pushable": "^3.2.3",
|
|
54
59
|
"it-stream-types": "^2.0.1",
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"stream-to-it": "^0.2.4",
|
|
58
|
-
"uint8arraylist": "^2.4.7",
|
|
60
|
+
"protons-runtime": "^5.4.0",
|
|
61
|
+
"uint8arraylist": "^2.4.8",
|
|
59
62
|
"uint8arrays": "^5.0.1"
|
|
60
63
|
},
|
|
61
64
|
"devDependencies": {
|
|
62
|
-
"@libp2p/interface-compliance-tests": "
|
|
63
|
-
"@libp2p/logger": "
|
|
64
|
-
"@libp2p/peer-id-factory": "
|
|
65
|
-
"@multiformats/multiaddr": "^12.1.
|
|
66
|
-
"aegir": "^42.
|
|
67
|
-
"protons": "^7.
|
|
65
|
+
"@libp2p/interface-compliance-tests": "5.3.0-0c7bbbb07",
|
|
66
|
+
"@libp2p/logger": "4.0.6-0c7bbbb07",
|
|
67
|
+
"@libp2p/peer-id-factory": "4.0.6-0c7bbbb07",
|
|
68
|
+
"@multiformats/multiaddr": "^12.1.14",
|
|
69
|
+
"aegir": "^42.2.3",
|
|
70
|
+
"protons": "^7.5.0",
|
|
68
71
|
"sinon": "^17.0.1"
|
|
69
72
|
},
|
|
70
73
|
"sideEffects": false
|
package/src/index.ts
CHANGED
|
@@ -18,18 +18,10 @@
|
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
// @ts-expect-error no types
|
|
24
|
-
import itToStream from 'it-to-stream'
|
|
25
|
-
// @ts-expect-error no types
|
|
26
|
-
import streamToIt from 'stream-to-it'
|
|
27
|
-
import { generateCertificate, verifyPeerCertificate } from './utils.js'
|
|
28
|
-
import type { ComponentLogger, MultiaddrConnection, ConnectionEncrypter, SecuredConnection, PeerId } from '@libp2p/interface'
|
|
29
|
-
import type { Duplex } from 'it-stream-types'
|
|
30
|
-
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
21
|
+
import { TLS } from './tls.js'
|
|
22
|
+
import type { ComponentLogger, ConnectionEncrypter } from '@libp2p/interface'
|
|
31
23
|
|
|
32
|
-
const PROTOCOL = '/tls/1.0.0'
|
|
24
|
+
export const PROTOCOL = '/tls/1.0.0'
|
|
33
25
|
|
|
34
26
|
export interface TLSComponents {
|
|
35
27
|
logger: ComponentLogger
|
|
@@ -43,83 +35,6 @@ export interface TLSInit {
|
|
|
43
35
|
timeout?: number
|
|
44
36
|
}
|
|
45
37
|
|
|
46
|
-
class TLS implements ConnectionEncrypter {
|
|
47
|
-
public protocol: string = PROTOCOL
|
|
48
|
-
// private readonly log: Logger
|
|
49
|
-
// private readonly timeout: number
|
|
50
|
-
|
|
51
|
-
// constructor (components: TLSComponents, init: TLSInit = {}) {
|
|
52
|
-
// this.log = components.logger.forComponent('libp2p:tls')
|
|
53
|
-
// this.timeout = init.timeout ?? 1000
|
|
54
|
-
// }
|
|
55
|
-
|
|
56
|
-
async secureInbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localId: PeerId, conn: Stream, remoteId?: PeerId): Promise<SecuredConnection<Stream>> {
|
|
57
|
-
return this._encrypt(localId, conn, false, remoteId)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async secureOutbound <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localId: PeerId, conn: Stream, remoteId?: PeerId): Promise<SecuredConnection<Stream>> {
|
|
61
|
-
return this._encrypt(localId, conn, true, remoteId)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Encrypt connection
|
|
66
|
-
*/
|
|
67
|
-
async _encrypt <Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection> (localId: PeerId, conn: Stream, isServer: boolean, remoteId?: PeerId): Promise<SecuredConnection<Stream>> {
|
|
68
|
-
const opts: TLSSocketOptions = {
|
|
69
|
-
...await generateCertificate(localId),
|
|
70
|
-
isServer,
|
|
71
|
-
requestCert: true,
|
|
72
|
-
// accept self-signed certificates from clients
|
|
73
|
-
rejectUnauthorized: false,
|
|
74
|
-
// require TLS 1.3 or later
|
|
75
|
-
minVersion: 'TLSv1.3'
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
let socket: TLSSocket
|
|
79
|
-
|
|
80
|
-
if (isServer) {
|
|
81
|
-
socket = new TLSSocket(itToStream.duplex(conn), opts)
|
|
82
|
-
} else {
|
|
83
|
-
socket = connect({
|
|
84
|
-
socket: itToStream.duplex(conn),
|
|
85
|
-
...opts
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return new Promise((resolve, reject) => {
|
|
90
|
-
function verifyRemote (): void {
|
|
91
|
-
const remote = socket.getPeerCertificate()
|
|
92
|
-
|
|
93
|
-
verifyPeerCertificate(remote.raw)
|
|
94
|
-
.then(remotePeer => {
|
|
95
|
-
if (remoteId?.equals(remotePeer) === false) {
|
|
96
|
-
throw new UnexpectedPeerError()
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const outputStream = streamToIt.duplex(socket)
|
|
100
|
-
conn.source = outputStream.source
|
|
101
|
-
conn.sink = outputStream.sink
|
|
102
|
-
|
|
103
|
-
resolve({
|
|
104
|
-
remotePeer,
|
|
105
|
-
conn
|
|
106
|
-
})
|
|
107
|
-
})
|
|
108
|
-
.catch(err => {
|
|
109
|
-
reject(err)
|
|
110
|
-
})
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
socket.on('error', err => {
|
|
114
|
-
reject(err)
|
|
115
|
-
})
|
|
116
|
-
socket.on('secure', (evt) => {
|
|
117
|
-
verifyRemote()
|
|
118
|
-
})
|
|
119
|
-
})
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
38
|
export function tls (init?: TLSInit): (components: TLSComponents) => ConnectionEncrypter {
|
|
124
|
-
return (components) => new TLS()
|
|
39
|
+
return (components) => new TLS(components, init)
|
|
125
40
|
}
|
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
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
+
import { Duplex as DuplexStream } from 'node:stream'
|
|
1
2
|
import { Ed25519PublicKey, Secp256k1PublicKey, marshalPublicKey, supportedKeys, unmarshalPrivateKey, unmarshalPublicKey } from '@libp2p/crypto/keys'
|
|
2
|
-
import { CodeError, InvalidCryptoExchangeError } from '@libp2p/interface'
|
|
3
|
+
import { CodeError, InvalidCryptoExchangeError, UnexpectedPeerError } from '@libp2p/interface'
|
|
3
4
|
import { peerIdFromKeys } from '@libp2p/peer-id'
|
|
4
5
|
import { AsnConvert } from '@peculiar/asn1-schema'
|
|
5
6
|
import * as asn1X509 from '@peculiar/asn1-x509'
|
|
6
7
|
import { Crypto } from '@peculiar/webcrypto'
|
|
7
8
|
import * as x509 from '@peculiar/x509'
|
|
8
9
|
import * as asn1js from 'asn1js'
|
|
10
|
+
import { pushable } from 'it-pushable'
|
|
9
11
|
import { concat as uint8ArrayConcat } from 'uint8arrays/concat'
|
|
10
12
|
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
|
|
11
13
|
import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
|
|
12
14
|
import { KeyType, PublicKey } from '../src/pb/index.js'
|
|
13
|
-
import type { PeerId, PublicKey as Libp2pPublicKey } from '@libp2p/interface'
|
|
15
|
+
import type { PeerId, PublicKey as Libp2pPublicKey, Logger } from '@libp2p/interface'
|
|
16
|
+
import type { Duplex } from 'it-stream-types'
|
|
17
|
+
import type { Uint8ArrayList } from 'uint8arraylist'
|
|
14
18
|
|
|
15
19
|
const crypto = new Crypto()
|
|
16
20
|
x509.cryptoProvider.set(crypto)
|
|
@@ -22,23 +26,38 @@ const CERT_VALIDITY_PERIOD_FROM = 60 * 60 * 1000 // ~1 hour
|
|
|
22
26
|
// https://github.com/libp2p/go-libp2p/blob/28c0f6ab32cd69e4b18e9e4b550ef6ce059a9d1a/p2p/security/tls/crypto.go#L24C28-L24C44
|
|
23
27
|
const CERT_VALIDITY_PERIOD_TO = 100 * 365 * 24 * 60 * 60 * 1000 // ~100 years
|
|
24
28
|
|
|
25
|
-
export async function verifyPeerCertificate (rawCertificate: Uint8Array): Promise<PeerId> {
|
|
29
|
+
export async function verifyPeerCertificate (rawCertificate: Uint8Array, expectedPeerId?: PeerId, log?: Logger): Promise<PeerId> {
|
|
26
30
|
const now = Date.now()
|
|
27
31
|
const x509Cert = new x509.X509Certificate(rawCertificate)
|
|
28
32
|
|
|
29
33
|
if (x509Cert.notBefore.getTime() > now) {
|
|
34
|
+
log?.error('the certificate was not valid yet')
|
|
30
35
|
throw new CodeError('The certificate is not valid yet', 'ERR_INVALID_CERTIFICATE')
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
if (x509Cert.notAfter.getTime() < now) {
|
|
39
|
+
log?.error('the certificate has expired')
|
|
34
40
|
throw new CodeError('The certificate has expired', 'ERR_INVALID_CERTIFICATE')
|
|
35
41
|
}
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
const certSignatureValid = await x509Cert.verify()
|
|
44
|
+
|
|
45
|
+
if (!certSignatureValid) {
|
|
46
|
+
log?.error('certificate self signature was invalid')
|
|
47
|
+
throw new InvalidCryptoExchangeError('Invalid certificate self signature')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const certIsSelfSigned = await x509Cert.isSelfSigned()
|
|
51
|
+
|
|
52
|
+
if (!certIsSelfSigned) {
|
|
53
|
+
log?.error('certificate must be self signed')
|
|
54
|
+
throw new InvalidCryptoExchangeError('Certificate must be self signed')
|
|
55
|
+
}
|
|
38
56
|
|
|
39
57
|
const libp2pPublicKeyExtension = x509Cert.extensions[0]
|
|
40
58
|
|
|
41
59
|
if (libp2pPublicKeyExtension == null || libp2pPublicKeyExtension.type !== LIBP2P_PUBLIC_KEY_EXTENSION) {
|
|
60
|
+
log?.error('the certificate did not include the libp2p public key extension')
|
|
42
61
|
throw new CodeError('The certificate did not include the libp2p public key extension', 'ERR_INVALID_CERTIFICATE')
|
|
43
62
|
}
|
|
44
63
|
|
|
@@ -47,32 +66,40 @@ export async function verifyPeerCertificate (rawCertificate: Uint8Array): Promis
|
|
|
47
66
|
// @ts-expect-error deep chain
|
|
48
67
|
const remotePeerIdPb = libp2pKeySequence.valueBlock.value[0].valueBlock.valueHex
|
|
49
68
|
const marshalledPeerId = new Uint8Array(remotePeerIdPb, 0, remotePeerIdPb.byteLength)
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
let
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
|
|
56
|
-
} else if (
|
|
57
|
-
|
|
58
|
-
} else if (
|
|
59
|
-
|
|
69
|
+
const remotePublicKey = PublicKey.decode(marshalledPeerId)
|
|
70
|
+
const remotePublicKeyData = remotePublicKey.data ?? new Uint8Array(0)
|
|
71
|
+
let remoteLibp2pPublicKey: Libp2pPublicKey
|
|
72
|
+
|
|
73
|
+
if (remotePublicKey.type === KeyType.Ed25519) {
|
|
74
|
+
remoteLibp2pPublicKey = new Ed25519PublicKey(remotePublicKeyData)
|
|
75
|
+
} else if (remotePublicKey.type === KeyType.Secp256k1) {
|
|
76
|
+
remoteLibp2pPublicKey = new Secp256k1PublicKey(remotePublicKeyData)
|
|
77
|
+
} else if (remotePublicKey.type === KeyType.RSA) {
|
|
78
|
+
remoteLibp2pPublicKey = supportedKeys.rsa.unmarshalRsaPublicKey(remotePublicKeyData)
|
|
60
79
|
} else {
|
|
61
|
-
|
|
80
|
+
log?.error('unknown or unsupported key type', remotePublicKey.type)
|
|
81
|
+
throw new InvalidCryptoExchangeError('Unknown or unsupported key type')
|
|
62
82
|
}
|
|
63
83
|
|
|
64
84
|
// @ts-expect-error deep chain
|
|
65
85
|
const remoteSignature = libp2pKeySequence.valueBlock.value[1].valueBlock.valueHex
|
|
66
86
|
const dataToVerify = encodeSignatureData(x509Cert.publicKey.rawData)
|
|
67
|
-
const result = await
|
|
87
|
+
const result = await remoteLibp2pPublicKey.verify(dataToVerify, new Uint8Array(remoteSignature, 0, remoteSignature.byteLength))
|
|
68
88
|
|
|
69
89
|
if (!result) {
|
|
70
|
-
|
|
90
|
+
log?.error('invalid libp2p signature')
|
|
91
|
+
throw new InvalidCryptoExchangeError('Could not verify signature')
|
|
71
92
|
}
|
|
72
93
|
|
|
73
|
-
const marshalled = marshalPublicKey(
|
|
94
|
+
const marshalled = marshalPublicKey(remoteLibp2pPublicKey)
|
|
95
|
+
const remotePeerId = await peerIdFromKeys(marshalled)
|
|
74
96
|
|
|
75
|
-
|
|
97
|
+
if (expectedPeerId?.equals(remotePeerId) === false) {
|
|
98
|
+
log?.error('invalid peer id')
|
|
99
|
+
throw new UnexpectedPeerError()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return remotePeerId
|
|
76
103
|
}
|
|
77
104
|
|
|
78
105
|
export async function generateCertificate (peerId: PeerId): Promise<{ cert: string, key: string }> {
|
|
@@ -184,3 +211,109 @@ function formatAsPem (str: string): string {
|
|
|
184
211
|
|
|
185
212
|
return finalString
|
|
186
213
|
}
|
|
214
|
+
|
|
215
|
+
export function itToStream (conn: Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>>): DuplexStream {
|
|
216
|
+
const output = pushable()
|
|
217
|
+
const iterator = conn.source[Symbol.asyncIterator]() as AsyncGenerator<Uint8Array>
|
|
218
|
+
|
|
219
|
+
const stream = new DuplexStream({
|
|
220
|
+
autoDestroy: false,
|
|
221
|
+
allowHalfOpen: true,
|
|
222
|
+
write (chunk, encoding, callback) {
|
|
223
|
+
output.push(chunk)
|
|
224
|
+
callback()
|
|
225
|
+
},
|
|
226
|
+
read () {
|
|
227
|
+
iterator.next()
|
|
228
|
+
.then(result => {
|
|
229
|
+
if (result.done === true) {
|
|
230
|
+
this.push(null)
|
|
231
|
+
} else {
|
|
232
|
+
this.push(result.value)
|
|
233
|
+
}
|
|
234
|
+
}, (err) => {
|
|
235
|
+
this.destroy(err)
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
// @ts-expect-error return type of sink is unknown
|
|
241
|
+
conn.sink(output)
|
|
242
|
+
.catch((err: any) => {
|
|
243
|
+
stream.destroy(err)
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
return stream
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function streamToIt (stream: DuplexStream): Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> {
|
|
250
|
+
const output: Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = {
|
|
251
|
+
source: (async function * () {
|
|
252
|
+
const output = pushable<Uint8Array>()
|
|
253
|
+
|
|
254
|
+
stream.addListener('data', (buf) => {
|
|
255
|
+
output.push(buf.subarray())
|
|
256
|
+
})
|
|
257
|
+
// both ends closed
|
|
258
|
+
stream.addListener('close', () => {
|
|
259
|
+
output.end()
|
|
260
|
+
})
|
|
261
|
+
stream.addListener('error', (err) => {
|
|
262
|
+
output.end(err)
|
|
263
|
+
})
|
|
264
|
+
// just writable end closed
|
|
265
|
+
stream.addListener('finish', () => {
|
|
266
|
+
output.end()
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
yield * output
|
|
271
|
+
} catch (err: any) {
|
|
272
|
+
stream.destroy(err)
|
|
273
|
+
throw err
|
|
274
|
+
}
|
|
275
|
+
})(),
|
|
276
|
+
sink: async (source) => {
|
|
277
|
+
try {
|
|
278
|
+
for await (const buf of source) {
|
|
279
|
+
const sendMore = stream.write(buf.subarray())
|
|
280
|
+
|
|
281
|
+
if (!sendMore) {
|
|
282
|
+
await waitForBackpressure(stream)
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// close writable end
|
|
287
|
+
stream.end()
|
|
288
|
+
} catch (err: any) {
|
|
289
|
+
stream.destroy(err)
|
|
290
|
+
throw err
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return output
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async function waitForBackpressure (stream: DuplexStream): Promise<void> {
|
|
299
|
+
await new Promise<void>((resolve, reject) => {
|
|
300
|
+
const continueListener = (): void => {
|
|
301
|
+
cleanUp()
|
|
302
|
+
resolve()
|
|
303
|
+
}
|
|
304
|
+
const stopListener = (err?: Error): void => {
|
|
305
|
+
cleanUp()
|
|
306
|
+
reject(err ?? new Error('Stream ended'))
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const cleanUp = (): void => {
|
|
310
|
+
stream.removeListener('drain', continueListener)
|
|
311
|
+
stream.removeListener('end', stopListener)
|
|
312
|
+
stream.removeListener('error', stopListener)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
stream.addListener('drain', continueListener)
|
|
316
|
+
stream.addListener('end', stopListener)
|
|
317
|
+
stream.addListener('error', stopListener)
|
|
318
|
+
})
|
|
319
|
+
}
|
package/dist/typedoc-urls.json
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
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
|
-
"TLSInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_tls.TLSInit.html",
|
|
5
|
-
".:TLSInit": "https://libp2p.github.io/js-libp2p/interfaces/_libp2p_tls.TLSInit.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
|
-
}
|