-
Notifications
You must be signed in to change notification settings - Fork 443
/
index.ts
129 lines (107 loc) · 4.02 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/**
* @packageDocumentation
*
* A connection encrypter that does no connection encryption.
*
* This should not be used in production should be used for research purposes only.
*
* @example
*
* ```typescript
* import { createLibp2p } from 'libp2p'
* import { plaintext } from '@libp2p/plaintext'
*
* const node = await createLibp2p({
* // ...other options
* connectionEncrypters: [
* plaintext()
* ]
* })
* ```
*/
import { publicKeyFromRaw } from '@libp2p/crypto/keys'
import { UnexpectedPeerError, InvalidCryptoExchangeError, serviceCapabilities, ProtocolError } from '@libp2p/interface'
import { peerIdFromPublicKey } from '@libp2p/peer-id'
import { pbStream } from 'it-protobuf-stream'
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
import { Exchange, KeyType } from './pb/proto.js'
import type { ComponentLogger, Logger, MultiaddrConnection, ConnectionEncrypter, SecuredConnection, PrivateKey, SecureConnectionOptions } from '@libp2p/interface'
import type { Duplex } from 'it-stream-types'
import type { Uint8ArrayList } from 'uint8arraylist'
const PROTOCOL = '/plaintext/2.0.0'
export interface PlaintextComponents {
privateKey: PrivateKey
logger: ComponentLogger
}
class Plaintext implements ConnectionEncrypter {
public protocol: string = PROTOCOL
private readonly privateKey: PrivateKey
private readonly log: Logger
constructor (components: PlaintextComponents) {
this.privateKey = components.privateKey
this.log = components.logger.forComponent('libp2p:plaintext')
}
readonly [Symbol.toStringTag] = '@libp2p/plaintext'
readonly [serviceCapabilities]: string[] = [
'@libp2p/connection-encryption'
]
async secureInbound<Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection>(conn: Stream, options?: SecureConnectionOptions): Promise<SecuredConnection<Stream>> {
return this._encrypt(conn, options)
}
async secureOutbound<Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection>(conn: Stream, options?: SecureConnectionOptions): Promise<SecuredConnection<Stream>> {
return this._encrypt(conn, options)
}
/**
* Encrypt connection
*/
async _encrypt<Stream extends Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> = MultiaddrConnection>(conn: Stream, options?: SecureConnectionOptions): Promise<SecuredConnection<Stream>> {
const pb = pbStream(conn).pb(Exchange)
this.log('write pubkey exchange to peer %p', options?.remotePeer)
const publicKey = this.privateKey.publicKey
const [
, response
] = await Promise.all([
// Encode the public key and write it to the remote peer
pb.write({
id: publicKey.toMultihash().bytes,
pubkey: {
Type: KeyType[publicKey.type],
Data: publicKey.raw
}
}, options),
// Get the Exchange message
pb.read(options)
])
let peerId
try {
if (response.pubkey == null) {
throw new ProtocolError('Public key missing')
}
if (response.pubkey.Data.byteLength === 0) {
throw new ProtocolError('Public key data too short')
}
if (response.id == null) {
throw new ProtocolError('Remote id missing')
}
const pubKey = publicKeyFromRaw(response.pubkey.Data)
peerId = peerIdFromPublicKey(pubKey)
if (!uint8ArrayEquals(peerId.toMultihash().bytes, response.id)) {
throw new InvalidCryptoExchangeError('Public key did not match id')
}
} catch (err: any) {
this.log.error(err)
throw new InvalidCryptoExchangeError('Invalid public key - ' + err.message)
}
if (options?.remotePeer != null && !peerId.equals(options?.remotePeer)) {
throw new UnexpectedPeerError()
}
this.log('plaintext key exchange completed successfully with peer %p', peerId)
return {
conn: pb.unwrap().unwrap(),
remotePeer: peerId
}
}
}
export function plaintext (): (components: PlaintextComponents) => ConnectionEncrypter {
return (components) => new Plaintext(components)
}