@libp2p/noise 12.0.1 → 16.1.4-6059227cb

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.
Files changed (127) hide show
  1. package/README.md +95 -0
  2. package/dist/index.min.js +16 -20
  3. package/dist/index.min.js.map +7 -0
  4. package/dist/src/constants.d.ts +1 -0
  5. package/dist/src/constants.d.ts.map +1 -1
  6. package/dist/src/constants.js +1 -0
  7. package/dist/src/constants.js.map +1 -1
  8. package/dist/src/crypto/index.browser.d.ts +2 -0
  9. package/dist/src/crypto/index.browser.d.ts.map +1 -0
  10. package/dist/src/crypto/index.browser.js +3 -0
  11. package/dist/src/crypto/index.browser.js.map +1 -0
  12. package/dist/src/crypto/index.d.ts +3 -0
  13. package/dist/src/crypto/index.d.ts.map +1 -0
  14. package/dist/src/crypto/index.js +188 -0
  15. package/dist/src/crypto/index.js.map +1 -0
  16. package/dist/src/crypto/js.d.ts.map +1 -1
  17. package/dist/src/crypto/js.js +18 -17
  18. package/dist/src/crypto/js.js.map +1 -1
  19. package/dist/src/crypto.d.ts +11 -10
  20. package/dist/src/crypto.d.ts.map +1 -1
  21. package/dist/src/crypto.js +10 -1
  22. package/dist/src/crypto.js.map +1 -1
  23. package/dist/src/encoder.d.ts +5 -10
  24. package/dist/src/encoder.d.ts.map +1 -1
  25. package/dist/src/encoder.js +10 -51
  26. package/dist/src/encoder.js.map +1 -1
  27. package/dist/src/errors.d.ts +6 -0
  28. package/dist/src/errors.d.ts.map +1 -0
  29. package/dist/src/errors.js +9 -0
  30. package/dist/src/errors.js.map +1 -0
  31. package/dist/src/index.d.ts +50 -5
  32. package/dist/src/index.d.ts.map +1 -1
  33. package/dist/src/index.js +38 -1
  34. package/dist/src/index.js.map +1 -1
  35. package/dist/src/logger.d.ts +9 -10
  36. package/dist/src/logger.d.ts.map +1 -1
  37. package/dist/src/logger.js +39 -28
  38. package/dist/src/logger.js.map +1 -1
  39. package/dist/src/metrics.d.ts +1 -1
  40. package/dist/src/metrics.d.ts.map +1 -1
  41. package/dist/src/noise.d.ts +33 -30
  42. package/dist/src/noise.d.ts.map +1 -1
  43. package/dist/src/noise.js +122 -78
  44. package/dist/src/noise.js.map +1 -1
  45. package/dist/src/nonce.d.ts +2 -3
  46. package/dist/src/nonce.d.ts.map +1 -1
  47. package/dist/src/nonce.js +3 -2
  48. package/dist/src/nonce.js.map +1 -1
  49. package/dist/src/performHandshake.d.ts +5 -0
  50. package/dist/src/performHandshake.d.ts.map +1 -0
  51. package/dist/src/performHandshake.js +69 -0
  52. package/dist/src/performHandshake.js.map +1 -0
  53. package/dist/src/proto/payload.d.ts +4 -3
  54. package/dist/src/proto/payload.d.ts.map +1 -1
  55. package/dist/src/proto/payload.js +48 -29
  56. package/dist/src/proto/payload.js.map +1 -1
  57. package/dist/src/protocol.d.ts +71 -0
  58. package/dist/src/protocol.d.ts.map +1 -0
  59. package/dist/src/protocol.js +257 -0
  60. package/dist/src/protocol.js.map +1 -0
  61. package/dist/src/types.d.ts +98 -0
  62. package/dist/src/types.d.ts.map +1 -0
  63. package/dist/src/types.js +2 -0
  64. package/dist/src/types.js.map +1 -0
  65. package/dist/src/utils.d.ts +10 -19
  66. package/dist/src/utils.d.ts.map +1 -1
  67. package/dist/src/utils.js +159 -57
  68. package/dist/src/utils.js.map +1 -1
  69. package/package.json +43 -48
  70. package/src/constants.ts +1 -0
  71. package/src/crypto/index.browser.ts +3 -0
  72. package/src/crypto/index.ts +219 -0
  73. package/src/crypto/js.ts +25 -27
  74. package/src/crypto.ts +21 -10
  75. package/src/encoder.ts +10 -67
  76. package/src/errors.ts +10 -0
  77. package/src/index.ts +54 -6
  78. package/src/logger.ts +46 -32
  79. package/src/metrics.ts +1 -1
  80. package/src/noise.ts +176 -131
  81. package/src/nonce.ts +7 -7
  82. package/src/performHandshake.ts +91 -0
  83. package/src/proto/payload.proto +2 -1
  84. package/src/proto/payload.ts +53 -31
  85. package/src/protocol.ts +313 -0
  86. package/src/types.ts +105 -0
  87. package/src/utils.ts +189 -79
  88. package/dist/src/@types/basic.d.ts +0 -5
  89. package/dist/src/@types/basic.d.ts.map +0 -1
  90. package/dist/src/@types/basic.js +0 -2
  91. package/dist/src/@types/basic.js.map +0 -1
  92. package/dist/src/@types/handshake-interface.d.ts +0 -15
  93. package/dist/src/@types/handshake-interface.d.ts.map +0 -1
  94. package/dist/src/@types/handshake-interface.js +0 -2
  95. package/dist/src/@types/handshake-interface.js.map +0 -1
  96. package/dist/src/@types/handshake.d.ts +0 -40
  97. package/dist/src/@types/handshake.d.ts.map +0 -1
  98. package/dist/src/@types/handshake.js +0 -2
  99. package/dist/src/@types/handshake.js.map +0 -1
  100. package/dist/src/@types/libp2p.d.ts +0 -10
  101. package/dist/src/@types/libp2p.d.ts.map +0 -1
  102. package/dist/src/@types/libp2p.js +0 -2
  103. package/dist/src/@types/libp2p.js.map +0 -1
  104. package/dist/src/crypto/streaming.d.ts +0 -7
  105. package/dist/src/crypto/streaming.d.ts.map +0 -1
  106. package/dist/src/crypto/streaming.js +0 -50
  107. package/dist/src/crypto/streaming.js.map +0 -1
  108. package/dist/src/handshake-xx.d.ts +0 -33
  109. package/dist/src/handshake-xx.d.ts.map +0 -1
  110. package/dist/src/handshake-xx.js +0 -139
  111. package/dist/src/handshake-xx.js.map +0 -1
  112. package/dist/src/handshakes/abstract-handshake.d.ts +0 -36
  113. package/dist/src/handshakes/abstract-handshake.d.ts.map +0 -1
  114. package/dist/src/handshakes/abstract-handshake.js +0 -140
  115. package/dist/src/handshakes/abstract-handshake.js.map +0 -1
  116. package/dist/src/handshakes/xx.d.ts +0 -18
  117. package/dist/src/handshakes/xx.d.ts.map +0 -1
  118. package/dist/src/handshakes/xx.js +0 -161
  119. package/dist/src/handshakes/xx.js.map +0 -1
  120. package/src/@types/basic.ts +0 -5
  121. package/src/@types/handshake-interface.ts +0 -12
  122. package/src/@types/handshake.ts +0 -48
  123. package/src/@types/libp2p.ts +0 -10
  124. package/src/crypto/streaming.ts +0 -58
  125. package/src/handshake-xx.ts +0 -177
  126. package/src/handshakes/abstract-handshake.ts +0 -180
  127. package/src/handshakes/xx.ts +0 -184
package/src/noise.ts CHANGED
@@ -1,192 +1,237 @@
1
- import { decode } from 'it-length-prefixed'
2
- import { lpStream, type LengthPrefixedStream } from 'it-length-prefixed-stream'
3
- import { duplexPair } from 'it-pair/duplex'
4
- import { pipe } from 'it-pipe'
1
+ import { publicKeyFromProtobuf } from '@libp2p/crypto/keys'
2
+ import { InvalidCryptoExchangeError, serviceCapabilities } from '@libp2p/interface'
3
+ import { peerIdFromPublicKey } from '@libp2p/peer-id'
4
+ import { lpStream } from '@libp2p/utils'
5
+ import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
5
6
  import { NOISE_MSG_MAX_LENGTH_BYTES } from './constants.js'
6
- import { pureJsCrypto } from './crypto/js.js'
7
- import { decryptStream, encryptStream } from './crypto/streaming.js'
7
+ import { defaultCrypto } from './crypto/index.js'
8
+ import { wrapCrypto } from './crypto.js'
8
9
  import { uint16BEDecode, uint16BEEncode } from './encoder.js'
9
- import { XXHandshake } from './handshake-xx.js'
10
- import { type MetricsRegistry, registerMetrics } from './metrics.js'
11
- import { getPayload } from './utils.js'
12
- import type { bytes } from './@types/basic.js'
13
- import type { IHandshake } from './@types/handshake-interface.js'
14
- import type { INoiseConnection, KeyPair } from './@types/libp2p.js'
10
+ import { registerMetrics } from './metrics.js'
11
+ import { performHandshakeInitiator, performHandshakeResponder } from './performHandshake.js'
12
+ import { toMessageStream } from './utils.ts'
15
13
  import type { ICryptoInterface } from './crypto.js'
16
- import type { NoiseExtensions } from './proto/payload.js'
17
- import type { SecuredConnection } from '@libp2p/interface/connection-encrypter'
18
- import type { Metrics } from '@libp2p/interface/metrics'
19
- import type { PeerId } from '@libp2p/interface/peer-id'
20
- import type { Duplex, Source } from 'it-stream-types'
21
-
22
- interface HandshakeParams {
23
- connection: LengthPrefixedStream
24
- isInitiator: boolean
25
- localPeer: PeerId
26
- remotePeer?: PeerId
14
+ import type { NoiseComponents } from './index.js'
15
+ import type { MetricsRegistry } from './metrics.js'
16
+ import type { HandshakeResult, ICrypto, INoiseConnection, INoiseExtensions, KeyPair } from './types.js'
17
+ import type { SecuredConnection, PrivateKey, PublicKey, StreamMuxerFactory, SecureConnectionOptions, Logger, MessageStream } from '@libp2p/interface'
18
+ import type { LengthPrefixedStream } from '@libp2p/utils'
19
+
20
+ export interface NoiseExtensions {
21
+ webtransportCerthashes: Uint8Array[]
27
22
  }
28
23
 
29
24
  export interface NoiseInit {
30
25
  /**
31
26
  * x25519 private key, reuse for faster handshakes
32
27
  */
33
- staticNoiseKey?: bytes
34
- extensions?: NoiseExtensions
28
+ staticNoiseKey?: Uint8Array
29
+ extensions?: Partial<NoiseExtensions>
35
30
  crypto?: ICryptoInterface
36
31
  prologueBytes?: Uint8Array
37
- metrics?: Metrics
38
32
  }
39
33
 
40
34
  export class Noise implements INoiseConnection {
41
35
  public protocol = '/noise'
42
- public crypto: ICryptoInterface
36
+ public crypto: ICrypto
43
37
 
44
38
  private readonly prologue: Uint8Array
45
- private readonly staticKeys: KeyPair
39
+ private readonly staticKey: KeyPair
46
40
  private readonly extensions?: NoiseExtensions
47
41
  private readonly metrics?: MetricsRegistry
48
-
49
- constructor (init: NoiseInit = {}) {
50
- const { staticNoiseKey, extensions, crypto, prologueBytes, metrics } = init
51
-
52
- this.crypto = crypto ?? pureJsCrypto
53
- this.extensions = extensions
42
+ private readonly components: NoiseComponents
43
+ private readonly log: Logger
44
+
45
+ constructor (components: NoiseComponents, init: NoiseInit = {}) {
46
+ const { staticNoiseKey, extensions, crypto, prologueBytes } = init
47
+ const { metrics } = components
48
+
49
+ this.components = components
50
+ this.log = components.logger.forComponent('libp2p:noise')
51
+ const _crypto = crypto ?? defaultCrypto
52
+ this.crypto = wrapCrypto(_crypto)
53
+ this.extensions = {
54
+ webtransportCerthashes: [],
55
+ ...extensions
56
+ }
54
57
  this.metrics = metrics ? registerMetrics(metrics) : undefined
55
58
 
56
59
  if (staticNoiseKey) {
57
60
  // accepts x25519 private key of length 32
58
- this.staticKeys = this.crypto.generateX25519KeyPairFromSeed(staticNoiseKey)
61
+ this.staticKey = _crypto.generateX25519KeyPairFromSeed(staticNoiseKey)
59
62
  } else {
60
- this.staticKeys = this.crypto.generateX25519KeyPair()
63
+ this.staticKey = _crypto.generateX25519KeyPair()
61
64
  }
62
- this.prologue = prologueBytes ?? new Uint8Array(0)
65
+ this.prologue = prologueBytes ?? uint8ArrayAlloc(0)
63
66
  }
64
67
 
68
+ readonly [Symbol.toStringTag] = '@libp2p/noise'
69
+
70
+ readonly [serviceCapabilities]: string[] = [
71
+ '@libp2p/connection-encryption',
72
+ '@libp2p/noise'
73
+ ]
74
+
65
75
  /**
66
76
  * Encrypt outgoing data to the remote party (handshake as initiator)
67
77
  *
68
- * @param {PeerId} localPeer - PeerId of the receiving peer
69
- * @param {Duplex<AsyncGenerator<Uint8Array>, AsyncIterable<Uint8Array>, Promise<void>>} connection - streaming iterable duplex that will be encrypted
70
- * @param {PeerId} remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer.
71
- * @returns {Promise<SecuredConnection>}
78
+ * @param connection - streaming iterable duplex that will be encrypted
79
+ * @param options
80
+ * @param options.remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer
81
+ * @param options.signal - Used to abort the operation
72
82
  */
73
- public async secureOutbound (localPeer: PeerId, connection: Duplex<AsyncGenerator<Uint8Array>, AsyncIterable<Uint8Array>, Promise<void>>, remotePeer?: PeerId): Promise<SecuredConnection<NoiseExtensions>> {
74
- const wrappedConnection = lpStream(
75
- connection,
76
- {
77
- lengthEncoder: uint16BEEncode,
78
- lengthDecoder: uint16BEDecode,
79
- maxDataLength: NOISE_MSG_MAX_LENGTH_BYTES
80
- }
81
- )
82
- const handshake = await this.performHandshake({
83
- connection: wrappedConnection,
84
- isInitiator: true,
85
- localPeer,
86
- remotePeer
83
+ async secureOutbound (connection: MessageStream, options?: SecureConnectionOptions): Promise<SecuredConnection<INoiseExtensions>> {
84
+ const log = connection.log?.newScope('noise') ?? this.log
85
+ const wrappedConnection = lpStream(connection, {
86
+ lengthEncoder: uint16BEEncode,
87
+ lengthDecoder: uint16BEDecode,
88
+ maxDataLength: NOISE_MSG_MAX_LENGTH_BYTES
87
89
  })
88
- const conn = await this.createSecureConnection(wrappedConnection, handshake)
90
+
91
+ const handshake = await this.performHandshakeInitiator(
92
+ wrappedConnection,
93
+ this.components.privateKey,
94
+ log,
95
+ options?.remotePeer?.publicKey,
96
+ options
97
+ )
98
+ const publicKey = publicKeyFromProtobuf(handshake.payload.identityKey)
89
99
 
90
100
  return {
91
- conn,
92
- remoteExtensions: handshake.remoteExtensions,
93
- remotePeer: handshake.remotePeer
101
+ connection: toMessageStream(wrappedConnection.unwrap(), handshake, this.metrics),
102
+ remoteExtensions: handshake.payload.extensions,
103
+ remotePeer: peerIdFromPublicKey(publicKey),
104
+ streamMuxer: options?.skipStreamMuxerNegotiation === true ? undefined : this.getStreamMuxer(handshake.payload.extensions?.streamMuxers)
105
+ }
106
+ }
107
+
108
+ private getStreamMuxer (protocols?: string[]): StreamMuxerFactory | undefined {
109
+ if (protocols == null || protocols.length === 0) {
110
+ return
111
+ }
112
+
113
+ const streamMuxers = this.components.upgrader.getStreamMuxers()
114
+
115
+ if (streamMuxers != null) {
116
+ for (const protocol of protocols) {
117
+ const streamMuxer = streamMuxers.get(protocol)
118
+
119
+ if (streamMuxer != null) {
120
+ return streamMuxer
121
+ }
122
+ }
123
+ }
124
+
125
+ if (protocols.length) {
126
+ throw new InvalidCryptoExchangeError('Early muxer negotiation was requested but the initiator and responder had no common muxers')
94
127
  }
95
128
  }
96
129
 
97
130
  /**
98
131
  * Decrypt incoming data (handshake as responder).
99
132
  *
100
- * @param {PeerId} localPeer - PeerId of the receiving peer.
101
- * @param {Duplex<AsyncGenerator<Uint8Array>, AsyncIterable<Uint8Array>, Promise<void>>} connection - streaming iterable duplex that will be encryption.
102
- * @param {PeerId} remotePeer - optional PeerId of the initiating peer, if known. This may only exist during transport upgrades.
103
- * @returns {Promise<SecuredConnection>}
133
+ * @param connection - streaming iterable duplex that will be encrypted
134
+ * @param options
135
+ * @param options.remotePeer - PeerId of the remote peer. Used to validate the integrity of the remote peer
136
+ * @param options.signal - Used to abort the operation
104
137
  */
105
- public async secureInbound (localPeer: PeerId, connection: Duplex<AsyncGenerator<Uint8Array>, AsyncIterable<Uint8Array>, Promise<void>>, remotePeer?: PeerId): Promise<SecuredConnection<NoiseExtensions>> {
106
- const wrappedConnection = lpStream(
107
- connection,
108
- {
109
- lengthEncoder: uint16BEEncode,
110
- lengthDecoder: uint16BEDecode,
111
- maxDataLength: NOISE_MSG_MAX_LENGTH_BYTES
112
- }
113
- )
114
- const handshake = await this.performHandshake({
115
- connection: wrappedConnection,
116
- isInitiator: false,
117
- localPeer,
118
- remotePeer
138
+ async secureInbound (connection: MessageStream, options?: SecureConnectionOptions): Promise<SecuredConnection<INoiseExtensions>> {
139
+ const log = connection.log?.newScope('noise') ?? this.log
140
+ const wrappedConnection = lpStream(connection, {
141
+ lengthEncoder: uint16BEEncode,
142
+ lengthDecoder: uint16BEDecode,
143
+ maxDataLength: NOISE_MSG_MAX_LENGTH_BYTES
119
144
  })
120
- const conn = await this.createSecureConnection(wrappedConnection, handshake)
145
+
146
+ const handshake = await this.performHandshakeResponder(
147
+ wrappedConnection,
148
+ this.components.privateKey,
149
+ log,
150
+ options?.remotePeer?.publicKey,
151
+ options
152
+ )
153
+ const publicKey = publicKeyFromProtobuf(handshake.payload.identityKey)
121
154
 
122
155
  return {
123
- conn,
124
- remotePeer: handshake.remotePeer,
125
- remoteExtensions: handshake.remoteExtensions
156
+ connection: toMessageStream(wrappedConnection.unwrap(), handshake, this.metrics),
157
+ remoteExtensions: handshake.payload.extensions,
158
+ remotePeer: peerIdFromPublicKey(publicKey),
159
+ streamMuxer: options?.skipStreamMuxerNegotiation === true ? undefined : this.getStreamMuxer(handshake.payload.extensions?.streamMuxers)
126
160
  }
127
161
  }
128
162
 
129
163
  /**
130
- * If Noise pipes supported, tries IK handshake first with XX as fallback if it fails.
131
- * If noise pipes disabled or remote peer static key is unknown, use XX.
132
- *
133
- * @param {HandshakeParams} params
164
+ * Perform XX handshake as initiator.
134
165
  */
135
- private async performHandshake (params: HandshakeParams): Promise<IHandshake> {
136
- const payload = await getPayload(params.localPeer, this.staticKeys.publicKey, this.extensions)
137
-
138
- // run XX handshake
139
- return this.performXXHandshake(params, payload)
140
- }
141
-
142
- private async performXXHandshake (
143
- params: HandshakeParams,
144
- payload: bytes
145
- ): Promise<XXHandshake> {
146
- const { isInitiator, remotePeer, connection } = params
147
- const handshake = new XXHandshake(
148
- isInitiator,
149
- payload,
150
- this.prologue,
151
- this.crypto,
152
- this.staticKeys,
153
- connection,
154
- remotePeer
155
- )
166
+ private async performHandshakeInitiator (
167
+ connection: LengthPrefixedStream,
168
+ // TODO: pass private key in noise constructor via Components
169
+ privateKey: PrivateKey,
170
+ log: Logger,
171
+ remoteIdentityKey?: PublicKey,
172
+ options?: SecureConnectionOptions
173
+ ): Promise<HandshakeResult> {
174
+ let result: HandshakeResult
175
+ const streamMuxers = options?.skipStreamMuxerNegotiation === true ? [] : [...this.components.upgrader.getStreamMuxers().keys()]
156
176
 
157
177
  try {
158
- await handshake.propose()
159
- await handshake.exchange()
160
- await handshake.finish()
178
+ result = await performHandshakeInitiator({
179
+ connection,
180
+ privateKey,
181
+ remoteIdentityKey,
182
+ log: log.newScope('xxhandshake'),
183
+ crypto: this.crypto,
184
+ prologue: this.prologue,
185
+ s: this.staticKey,
186
+ extensions: {
187
+ streamMuxers,
188
+ webtransportCerthashes: [],
189
+ ...this.extensions
190
+ }
191
+ }, options)
161
192
  this.metrics?.xxHandshakeSuccesses.increment()
162
193
  } catch (e: unknown) {
163
194
  this.metrics?.xxHandshakeErrors.increment()
164
- if (e instanceof Error) {
165
- e.message = `Error occurred during XX handshake: ${e.message}`
166
- throw e
167
- }
195
+ throw e
168
196
  }
169
197
 
170
- return handshake
198
+ return result
171
199
  }
172
200
 
173
- private async createSecureConnection (
174
- connection: LengthPrefixedStream<Duplex<AsyncGenerator<Uint8Array>, AsyncIterable<Uint8Array>, Promise<void>>>,
175
- handshake: IHandshake
176
- ): Promise<Duplex<AsyncGenerator<Uint8Array>, Source<Uint8Array>, Promise<void>>> {
177
- // Create encryption box/unbox wrapper
178
- const [secure, user] = duplexPair<Uint8Array>()
179
- const network = connection.unwrap()
180
-
181
- await pipe(
182
- secure, // write to wrapper
183
- encryptStream(handshake, this.metrics), // encrypt data + prefix with message length
184
- network, // send to the remote peer
185
- (source) => decode(source, { lengthDecoder: uint16BEDecode }), // read message length prefix
186
- decryptStream(handshake, this.metrics), // decrypt the incoming data
187
- secure // pipe to the wrapper
188
- )
201
+ /**
202
+ * Perform XX handshake as responder.
203
+ */
204
+ private async performHandshakeResponder (
205
+ connection: LengthPrefixedStream,
206
+ privateKey: PrivateKey,
207
+ log: Logger,
208
+ remoteIdentityKey?: PublicKey,
209
+ options?: SecureConnectionOptions
210
+ ): Promise<HandshakeResult> {
211
+ let result: HandshakeResult
212
+ const streamMuxers = options?.skipStreamMuxerNegotiation === true ? [] : [...this.components.upgrader.getStreamMuxers().keys()]
213
+
214
+ try {
215
+ result = await performHandshakeResponder({
216
+ connection,
217
+ privateKey,
218
+ remoteIdentityKey,
219
+ log: log.newScope('xxhandshake'),
220
+ crypto: this.crypto,
221
+ prologue: this.prologue,
222
+ s: this.staticKey,
223
+ extensions: {
224
+ streamMuxers,
225
+ webtransportCerthashes: [],
226
+ ...this.extensions
227
+ }
228
+ }, options)
229
+ this.metrics?.xxHandshakeSuccesses.increment()
230
+ } catch (e: unknown) {
231
+ this.metrics?.xxHandshakeErrors.increment()
232
+ throw e
233
+ }
189
234
 
190
- return user
235
+ return result
191
236
  }
192
237
  }
package/src/nonce.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { bytes, uint64 } from './@types/basic.js'
1
+ import { alloc as uint8ArrayAlloc } from 'uint8arrays/alloc'
2
2
 
3
3
  export const MIN_NONCE = 0
4
4
  // For performance reasons, the nonce is represented as a JS `number`
@@ -9,20 +9,20 @@ export const MIN_NONCE = 0
9
9
  // this MAX_NONCE is still a large number of messages, so the practical effect of this is negligible.
10
10
  export const MAX_NONCE = 0xffffffff
11
11
 
12
- const ERR_MAX_NONCE = 'Cipherstate has reached maximum n, a new handshake must be performed'
12
+ const ERR_MAX_NONCE = 'Cipher state has reached maximum n, a new handshake must be performed'
13
13
 
14
14
  /**
15
15
  * The nonce is an uint that's increased over time.
16
16
  * Maintaining different representations help improve performance.
17
17
  */
18
18
  export class Nonce {
19
- private n: uint64
20
- private readonly bytes: bytes
19
+ private n: number
20
+ private readonly bytes: Uint8Array
21
21
  private readonly view: DataView
22
22
 
23
23
  constructor (n = MIN_NONCE) {
24
24
  this.n = n
25
- this.bytes = new Uint8Array(12)
25
+ this.bytes = uint8ArrayAlloc(12)
26
26
  this.view = new DataView(this.bytes.buffer, this.bytes.byteOffset, this.bytes.byteLength)
27
27
  this.view.setUint32(4, n, true)
28
28
  }
@@ -33,11 +33,11 @@ export class Nonce {
33
33
  this.view.setUint32(4, this.n, true)
34
34
  }
35
35
 
36
- getBytes (): bytes {
36
+ getBytes (): Uint8Array {
37
37
  return this.bytes
38
38
  }
39
39
 
40
- getUint64 (): uint64 {
40
+ getUint64 (): number {
41
41
  return this.n
42
42
  }
43
43
 
@@ -0,0 +1,91 @@
1
+ import {
2
+ logLocalStaticKeys,
3
+ logLocalEphemeralKeys,
4
+ logRemoteEphemeralKey,
5
+ logRemoteStaticKey,
6
+ logCipherState
7
+ } from './logger.js'
8
+ import { ZEROLEN, XXHandshakeState } from './protocol.js'
9
+ import { createHandshakePayload, decodeHandshakePayload } from './utils.js'
10
+ import type { HandshakeResult, HandshakeParams } from './types.js'
11
+ import type { AbortOptions } from '@libp2p/interface'
12
+
13
+ export async function performHandshakeInitiator (init: HandshakeParams, options?: AbortOptions): Promise<HandshakeResult> {
14
+ const { log, connection, crypto, privateKey, prologue, s, remoteIdentityKey, extensions } = init
15
+
16
+ const payload = await createHandshakePayload(privateKey, s.publicKey, extensions)
17
+ const xx = new XXHandshakeState({
18
+ crypto,
19
+ protocolName: 'Noise_XX_25519_ChaChaPoly_SHA256',
20
+ initiator: true,
21
+ prologue,
22
+ s
23
+ })
24
+
25
+ logLocalStaticKeys(xx.s, log)
26
+ log.trace('Stage 0 - Initiator starting to send first message.')
27
+ await connection.write(xx.writeMessageA(ZEROLEN), options)
28
+ log.trace('Stage 0 - Initiator finished sending first message.')
29
+ logLocalEphemeralKeys(xx.e, log)
30
+
31
+ log.trace('Stage 1 - Initiator waiting to receive first message from responder...')
32
+ const plaintext = xx.readMessageB(await connection.read(options))
33
+ log.trace('Stage 1 - Initiator received the message.')
34
+ logRemoteEphemeralKey(xx.re, log)
35
+ logRemoteStaticKey(xx.rs, log)
36
+
37
+ log.trace("Initiator going to check remote's signature...")
38
+ const receivedPayload = await decodeHandshakePayload(plaintext, xx.rs, remoteIdentityKey)
39
+ log.trace('All good with the signature!')
40
+
41
+ log.trace('Stage 2 - Initiator sending third handshake message.')
42
+ await connection.write(xx.writeMessageC(payload), options)
43
+ log.trace('Stage 2 - Initiator sent message with signed payload.')
44
+
45
+ const [cs1, cs2] = xx.ss.split()
46
+ logCipherState(cs1, cs2, log)
47
+
48
+ return {
49
+ payload: receivedPayload,
50
+ encrypt: (plaintext) => cs1.encryptWithAd(ZEROLEN, plaintext),
51
+ decrypt: (ciphertext, dst) => cs2.decryptWithAd(ZEROLEN, ciphertext, dst)
52
+ }
53
+ }
54
+
55
+ export async function performHandshakeResponder (init: HandshakeParams, options?: AbortOptions): Promise<HandshakeResult> {
56
+ const { log, connection, crypto, privateKey, prologue, s, remoteIdentityKey, extensions } = init
57
+
58
+ const payload = await createHandshakePayload(privateKey, s.publicKey, extensions)
59
+ const xx = new XXHandshakeState({
60
+ crypto,
61
+ protocolName: 'Noise_XX_25519_ChaChaPoly_SHA256',
62
+ initiator: false,
63
+ prologue,
64
+ s
65
+ })
66
+
67
+ logLocalStaticKeys(xx.s, log)
68
+ log.trace('Stage 0 - Responder waiting to receive first message.')
69
+ xx.readMessageA(await connection.read(options))
70
+ log.trace('Stage 0 - Responder received first message.')
71
+ logRemoteEphemeralKey(xx.re, log)
72
+
73
+ log.trace('Stage 1 - Responder sending out first message with signed payload and static key.')
74
+ await connection.write(xx.writeMessageB(payload), options)
75
+ log.trace('Stage 1 - Responder sent the second handshake message with signed payload.')
76
+ logLocalEphemeralKeys(xx.e, log)
77
+
78
+ log.trace('Stage 2 - Responder waiting for third handshake message...')
79
+ const plaintext = xx.readMessageC(await connection.read(options))
80
+ log.trace('Stage 2 - Responder received the message, finished handshake.')
81
+ const receivedPayload = await decodeHandshakePayload(plaintext, xx.rs, remoteIdentityKey)
82
+
83
+ const [cs1, cs2] = xx.ss.split()
84
+ logCipherState(cs1, cs2, log)
85
+
86
+ return {
87
+ payload: receivedPayload,
88
+ encrypt: (plaintext) => cs2.encryptWithAd(ZEROLEN, plaintext),
89
+ decrypt: (ciphertext, dst) => cs1.decryptWithAd(ZEROLEN, ciphertext, dst)
90
+ }
91
+ }
@@ -2,10 +2,11 @@ syntax = "proto3";
2
2
 
3
3
  message NoiseExtensions {
4
4
  repeated bytes webtransport_certhashes = 1;
5
+ repeated string stream_muxers = 2;
5
6
  }
6
7
 
7
8
  message NoiseHandshakePayload {
8
9
  bytes identity_key = 1;
9
10
  bytes identity_sig = 2;
10
11
  optional NoiseExtensions extensions = 4;
11
- }
12
+ }