2222
2323import { UnexpectedPeerError , InvalidCryptoExchangeError } from '@libp2p/interface/errors'
2424import { peerIdFromBytes , peerIdFromKeys } from '@libp2p/peer-id'
25- import { handshake } from 'it-handshake'
26- import * as lp from 'it-length-prefixed'
27- import map from 'it-map'
25+ import { pbStream } from 'it-protobuf-stream'
2826import { Exchange , KeyType } from './pb/proto.js'
2927import type { ComponentLogger , Logger } from '@libp2p/interface'
28+ import type { MultiaddrConnection } from '@libp2p/interface/connection'
3029import type { ConnectionEncrypter , SecuredConnection } from '@libp2p/interface/connection-encrypter'
3130import type { PeerId } from '@libp2p/interface/peer-id'
32- import type { Duplex , Source } from 'it-stream-types'
31+ import type { Duplex } from 'it-stream-types'
3332import type { Uint8ArrayList } from 'uint8arraylist'
3433
3534const PROTOCOL = '/plaintext/2.0.0'
3635
37- function lpEncodeExchange ( exchange : Exchange ) : Uint8ArrayList {
38- const pb = Exchange . encode ( exchange )
39-
40- return lp . encode . single ( pb )
41- }
42-
4336export interface PlaintextComponents {
4437 logger : ComponentLogger
4538}
4639
40+ export interface PlaintextInit {
41+ /**
42+ * The peer id exchange must complete within this many milliseconds
43+ * (default: 1000)
44+ */
45+ timeout ?: number
46+ }
47+
4748class Plaintext implements ConnectionEncrypter {
4849 public protocol : string = PROTOCOL
4950 private readonly log : Logger
51+ private readonly timeout : number
5052
51- constructor ( components : PlaintextComponents ) {
53+ constructor ( components : PlaintextComponents , init : PlaintextInit = { } ) {
5254 this . log = components . logger . forComponent ( 'libp2p:plaintext' )
55+ this . timeout = init . timeout ?? 1000
5356 }
5457
55- async secureInbound ( localId : PeerId , conn : Duplex < AsyncGenerator < Uint8Array > , Source < Uint8Array > , Promise < void > > , remoteId ?: PeerId ) : Promise < SecuredConnection > {
58+ async secureInbound < Stream extends Duplex < AsyncGenerator < Uint8Array | Uint8ArrayList > > = MultiaddrConnection > ( localId : PeerId , conn : Stream , remoteId ?: PeerId ) : Promise < SecuredConnection < Stream > > {
5659 return this . _encrypt ( localId , conn , remoteId )
5760 }
5861
59- async secureOutbound ( localId : PeerId , conn : Duplex < AsyncGenerator < Uint8Array > , Source < Uint8Array > , Promise < void > > , remoteId ?: PeerId ) : Promise < SecuredConnection > {
62+ async secureOutbound < Stream extends Duplex < AsyncGenerator < Uint8Array | Uint8ArrayList > > = MultiaddrConnection > ( localId : PeerId , conn : Stream , remoteId ?: PeerId ) : Promise < SecuredConnection < Stream > > {
6063 return this . _encrypt ( localId , conn , remoteId )
6164 }
6265
6366 /**
6467 * Encrypt connection
6568 */
66- async _encrypt ( localId : PeerId , conn : Duplex < AsyncGenerator < Uint8Array > , Source < Uint8Array > , Promise < void > > , remoteId ?: PeerId ) : Promise < SecuredConnection > {
67- const shake = handshake ( conn )
69+ async _encrypt < Stream extends Duplex < AsyncGenerator < Uint8Array | Uint8ArrayList > > = MultiaddrConnection > ( localId : PeerId , conn : Stream , remoteId ?: PeerId ) : Promise < SecuredConnection < Stream > > {
70+ const signal = AbortSignal . timeout ( this . timeout )
71+ const pb = pbStream ( conn ) . pb ( Exchange )
6872
6973 let type = KeyType . RSA
7074
@@ -75,45 +79,40 @@ class Plaintext implements ConnectionEncrypter {
7579 }
7680
7781 // Encode the public key and write it to the remote peer
78- shake . write (
79- lpEncodeExchange ( {
80- id : localId . toBytes ( ) ,
81- pubkey : {
82- Type : type ,
83- Data : localId . publicKey ?? new Uint8Array ( 0 )
84- }
85- } ) . subarray ( )
86- )
82+ await pb . write ( {
83+ id : localId . toBytes ( ) ,
84+ pubkey : {
85+ Type : type ,
86+ Data : localId . publicKey ?? new Uint8Array ( 0 )
87+ }
88+ } , {
89+ signal
90+ } )
8791
8892 this . log ( 'write pubkey exchange to peer %p' , remoteId )
8993
9094 // Get the Exchange message
91- const response = ( await lp . decode . fromReader ( shake . reader ) . next ( ) ) . value
92-
93- if ( response == null ) {
94- throw new Error ( 'Did not read response' )
95- }
96-
97- const id = Exchange . decode ( response )
98- this . log ( 'read pubkey exchange from peer %p' , remoteId )
95+ const response = await pb . read ( {
96+ signal
97+ } )
9998
10099 let peerId
101100 try {
102- if ( id . pubkey == null ) {
101+ if ( response . pubkey == null ) {
103102 throw new Error ( 'Public key missing' )
104103 }
105104
106- if ( id . pubkey . Data . length === 0 ) {
105+ if ( response . pubkey . Data . length === 0 ) {
107106 throw new Error ( 'Public key data too short' )
108107 }
109108
110- if ( id . id == null ) {
109+ if ( response . id == null ) {
111110 throw new Error ( 'Remote id missing' )
112111 }
113112
114- peerId = await peerIdFromKeys ( id . pubkey . Data )
113+ peerId = await peerIdFromKeys ( response . pubkey . Data )
115114
116- if ( ! peerId . equals ( peerIdFromBytes ( id . id ) ) ) {
115+ if ( ! peerId . equals ( peerIdFromBytes ( response . id ) ) ) {
117116 throw new Error ( 'Public key did not match id' )
118117 }
119118 } catch ( err : any ) {
@@ -127,18 +126,13 @@ class Plaintext implements ConnectionEncrypter {
127126
128127 this . log ( 'plaintext key exchange completed successfully with peer %p' , peerId )
129128
130- shake . rest ( )
131-
132129 return {
133- conn : {
134- sink : shake . stream . sink ,
135- source : map ( shake . stream . source , ( buf ) => buf . subarray ( ) )
136- } ,
130+ conn : pb . unwrap ( ) . unwrap ( ) ,
137131 remotePeer : peerId
138132 }
139133 }
140134}
141135
142- export function plaintext ( ) : ( components : PlaintextComponents ) => ConnectionEncrypter {
143- return ( components ) => new Plaintext ( components )
136+ export function plaintext ( init ?: PlaintextInit ) : ( components : PlaintextComponents ) => ConnectionEncrypter {
137+ return ( components ) => new Plaintext ( components , init )
144138}
0 commit comments