/__w/smoldot/smoldot/repo/lib/src/libp2p/connection/single_stream_handshake.rs
Line | Count | Source (jump to first uncovered line) |
1 | | // Smoldot |
2 | | // Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. |
3 | | // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 |
4 | | |
5 | | // This program is free software: you can redistribute it and/or modify |
6 | | // it under the terms of the GNU General Public License as published by |
7 | | // the Free Software Foundation, either version 3 of the License, or |
8 | | // (at your option) any later version. |
9 | | |
10 | | // This program is distributed in the hope that it will be useful, |
11 | | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | // GNU General Public License for more details. |
14 | | |
15 | | // You should have received a copy of the GNU General Public License |
16 | | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
17 | | |
18 | | //! State machine handling the handshake with a TCP or WebSocket libp2p connection. |
19 | | //! |
20 | | //! A connection handshake consists of three steps: |
21 | | //! |
22 | | //! - A multistream-select negotiation to negotiate the encryption protocol. Only the noise |
23 | | //! protocol is supported at the moment. |
24 | | //! - A noise protocol handshake, where public keys are exchanged and symmetric encryption is |
25 | | //! initialized. |
26 | | //! - A multistream-select negotiation to negotiate the Yamux protocol. Only the Yamux protocol is |
27 | | //! supported at the moment. This negotiation is performed on top of the noise cipher. |
28 | | //! |
29 | | //! This entire handshake requires in total either three or five TCP packets (not including the |
30 | | //! TCP handshake), depending on the strategy used for the multistream-select protocol. |
31 | | |
32 | | // TODO: finish commenting on the number of round trips |
33 | | // TODO: some round-trips can be removed: the multistream-select ones, and maybe also a Noise one, but it's complicated |
34 | | |
35 | | use super::{ |
36 | | super::peer_id::PeerId, |
37 | | super::read_write::ReadWrite, |
38 | | established::ConnectionPrototype, |
39 | | multistream_select, |
40 | | noise::{self, NoiseKey}, |
41 | | yamux, |
42 | | }; |
43 | | |
44 | | use alloc::boxed::Box; |
45 | | use core::fmt; |
46 | | |
47 | | mod tests; |
48 | | |
49 | | /// Current state of a connection handshake. |
50 | | #[derive(Debug, derive_more::From)] |
51 | | pub enum Handshake { |
52 | | /// Connection handshake in progress. |
53 | | Healthy(HealthyHandshake), |
54 | | /// Handshake has succeeded. Connection is now open. |
55 | | Success { |
56 | | /// Network identity of the remote. |
57 | | remote_peer_id: PeerId, |
58 | | /// Prototype for the connection. |
59 | | connection: ConnectionPrototype, |
60 | | }, |
61 | | } |
62 | | |
63 | | impl Handshake { |
64 | | /// Shortcut for [`HealthyHandshake::noise_yamux`] wrapped in a [`Handshake`]. |
65 | 16 | pub fn noise_yamux( |
66 | 16 | noise_key: &NoiseKey, |
67 | 16 | noise_ephemeral_secret_key: &[u8; 32], |
68 | 16 | is_initiator: bool, |
69 | 16 | ) -> Self { |
70 | 16 | HealthyHandshake::noise_yamux(noise_key, noise_ephemeral_secret_key, is_initiator).into() |
71 | 16 | } _RNvMNtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection23single_stream_handshakeNtB2_9Handshake11noise_yamux Line | Count | Source | 65 | 16 | pub fn noise_yamux( | 66 | 16 | noise_key: &NoiseKey, | 67 | 16 | noise_ephemeral_secret_key: &[u8; 32], | 68 | 16 | is_initiator: bool, | 69 | 16 | ) -> Self { | 70 | 16 | HealthyHandshake::noise_yamux(noise_key, noise_ephemeral_secret_key, is_initiator).into() | 71 | 16 | } |
Unexecuted instantiation: _RNvMNtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection23single_stream_handshakeNtB2_9Handshake11noise_yamux |
72 | | } |
73 | | |
74 | | /// Connection handshake in progress. |
75 | | pub struct HealthyHandshake { |
76 | | state: NegotiationState, |
77 | | } |
78 | | |
79 | | enum NegotiationState { |
80 | | EncryptionProtocol { |
81 | | negotiation: multistream_select::InProgress<&'static str>, |
82 | | /// Handshake that will be driven after the protocol negotiation is successful. Created |
83 | | /// ahead of time but not actually used. |
84 | | handshake: noise::HandshakeInProgress, |
85 | | }, |
86 | | Encryption { |
87 | | handshake: noise::HandshakeInProgress, |
88 | | }, |
89 | | Multiplexing { |
90 | | peer_id: PeerId, |
91 | | encryption: Box<noise::Noise>, |
92 | | negotiation: multistream_select::InProgress<&'static str>, |
93 | | }, |
94 | | } |
95 | | |
96 | | impl HealthyHandshake { |
97 | | /// Initializes a new state machine for a Noise + Yamux handshake. |
98 | | /// |
99 | | /// Must pass `true` for `is_initiator` if the connection has been opened by the local machine, |
100 | | /// or `false` if it has been opened by the remote. |
101 | | /// |
102 | | /// The Noise ephemeral secret key must never be re-used. |
103 | 16 | pub fn noise_yamux( |
104 | 16 | noise_key: &NoiseKey, |
105 | 16 | noise_ephemeral_secret_key: &[u8; 32], |
106 | 16 | is_initiator: bool, |
107 | 16 | ) -> Self { |
108 | 16 | let negotiation = multistream_select::InProgress::new(if is_initiator { |
109 | 8 | multistream_select::Config::Dialer { |
110 | 8 | requested_protocol: noise::PROTOCOL_NAME, |
111 | 8 | } |
112 | | } else { |
113 | 8 | multistream_select::Config::Listener { |
114 | 8 | max_protocol_name_len: noise::PROTOCOL_NAME.len(), |
115 | 8 | } |
116 | | }); |
117 | | |
118 | 16 | HealthyHandshake { |
119 | 16 | state: NegotiationState::EncryptionProtocol { |
120 | 16 | negotiation, |
121 | 16 | handshake: noise::HandshakeInProgress::new(noise::Config { |
122 | 16 | key: noise_key, |
123 | 16 | is_initiator, |
124 | 16 | prologue: &[], |
125 | 16 | ephemeral_secret_key: noise_ephemeral_secret_key, |
126 | 16 | }), |
127 | 16 | }, |
128 | 16 | } |
129 | 16 | } _RNvMs_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection23single_stream_handshakeNtB4_16HealthyHandshake11noise_yamux Line | Count | Source | 103 | 16 | pub fn noise_yamux( | 104 | 16 | noise_key: &NoiseKey, | 105 | 16 | noise_ephemeral_secret_key: &[u8; 32], | 106 | 16 | is_initiator: bool, | 107 | 16 | ) -> Self { | 108 | 16 | let negotiation = multistream_select::InProgress::new(if is_initiator { | 109 | 8 | multistream_select::Config::Dialer { | 110 | 8 | requested_protocol: noise::PROTOCOL_NAME, | 111 | 8 | } | 112 | | } else { | 113 | 8 | multistream_select::Config::Listener { | 114 | 8 | max_protocol_name_len: noise::PROTOCOL_NAME.len(), | 115 | 8 | } | 116 | | }); | 117 | | | 118 | 16 | HealthyHandshake { | 119 | 16 | state: NegotiationState::EncryptionProtocol { | 120 | 16 | negotiation, | 121 | 16 | handshake: noise::HandshakeInProgress::new(noise::Config { | 122 | 16 | key: noise_key, | 123 | 16 | is_initiator, | 124 | 16 | prologue: &[], | 125 | 16 | ephemeral_secret_key: noise_ephemeral_secret_key, | 126 | 16 | }), | 127 | 16 | }, | 128 | 16 | } | 129 | 16 | } |
Unexecuted instantiation: _RNvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection23single_stream_handshakeNtB4_16HealthyHandshake11noise_yamux |
130 | | |
131 | | /// Feeds data coming from a socket and writes back data to send up. |
132 | | /// |
133 | | /// On success, returns the new state of the negotiation. |
134 | | /// |
135 | | /// An error is returned if the protocol is being violated by the remote. When that happens, |
136 | | /// the connection should be closed altogether. |
137 | 64 | pub fn read_write<TNow: Clone>( |
138 | 64 | mut self, |
139 | 64 | read_write: &mut ReadWrite<TNow>, |
140 | 64 | ) -> Result<Handshake, HandshakeError> { |
141 | 112 | loop { |
142 | 112 | match self.state { |
143 | | NegotiationState::EncryptionProtocol { |
144 | 32 | negotiation, |
145 | 32 | handshake, |
146 | | } => { |
147 | | // Earliest point of the handshake. The encryption is being negotiated. |
148 | | // Delegating read/write to the negotiation. |
149 | 32 | let updated = negotiation |
150 | 32 | .read_write(read_write) |
151 | 32 | .map_err(HandshakeError::EncryptionMultistreamSelect)?0 ; |
152 | | |
153 | 32 | return match updated { |
154 | 8 | multistream_select::Negotiation::InProgress(updated) => { |
155 | 8 | Ok(Handshake::Healthy(HealthyHandshake { |
156 | 8 | state: NegotiationState::EncryptionProtocol { |
157 | 8 | negotiation: updated, |
158 | 8 | handshake, |
159 | 8 | }, |
160 | 8 | })) |
161 | | } |
162 | | multistream_select::Negotiation::Success => { |
163 | 16 | self.state = NegotiationState::Encryption { handshake }; |
164 | 16 | continue; |
165 | | } |
166 | 8 | multistream_select::Negotiation::ListenerAcceptOrDeny(accept_reject) => { |
167 | 8 | let negotiation = |
168 | 8 | if accept_reject.requested_protocol() == noise::PROTOCOL_NAME { |
169 | 8 | accept_reject.accept() |
170 | | } else { |
171 | 0 | accept_reject.reject() |
172 | | }; |
173 | 8 | self.state = NegotiationState::EncryptionProtocol { |
174 | 8 | negotiation, |
175 | 8 | handshake, |
176 | 8 | }; |
177 | 8 | continue; |
178 | | } |
179 | | multistream_select::Negotiation::NotAvailable => { |
180 | 0 | Err(HandshakeError::NoEncryptionProtocol) |
181 | | } |
182 | | }; |
183 | | } |
184 | | |
185 | 40 | NegotiationState::Encryption { handshake } => { |
186 | | // Delegating read/write to the Noise handshake state machine. |
187 | 40 | let updated = handshake.read_write(read_write).map_err(|err| { |
188 | 0 | debug_assert!(!matches!(err, noise::HandshakeError::WriteClosed)); |
189 | 0 | HandshakeError::NoiseHandshake(err) |
190 | 40 | })?0 ; Unexecuted instantiation: _RNCINvMs_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection23single_stream_handshakeNtB7_16HealthyHandshake10read_writeNtNtCsaYZPK01V26L_4core4time8DurationE0Bd_ Unexecuted instantiation: _RNCINvMs_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection23single_stream_handshakeNtB7_16HealthyHandshake10read_writelE0Bd_ Unexecuted instantiation: _RNCINvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection23single_stream_handshakeNtB7_16HealthyHandshake10read_writeNtNtCsaYZPK01V26L_4core4time8DurationE0CsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RNCINvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection23single_stream_handshakeNtB7_16HealthyHandshake10read_writepE0Bd_ Unexecuted instantiation: _RNCINvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection23single_stream_handshakeNtB7_16HealthyHandshake10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantE0CsiUjFBJteJ7x_17smoldot_full_node |
191 | | |
192 | 40 | match updated { |
193 | | noise::NoiseHandshake::Success { |
194 | 16 | cipher, |
195 | 16 | remote_peer_id, |
196 | | } => { |
197 | | // Encryption layer has been successfully negotiated. Start the |
198 | | // handshake for the multiplexing protocol negotiation. |
199 | 16 | let negotiation = |
200 | 16 | multistream_select::InProgress::new(if cipher.is_initiator() { |
201 | 8 | multistream_select::Config::Dialer { |
202 | 8 | requested_protocol: yamux::PROTOCOL_NAME, |
203 | 8 | } |
204 | | } else { |
205 | 8 | multistream_select::Config::Listener { |
206 | 8 | max_protocol_name_len: yamux::PROTOCOL_NAME.len(), |
207 | 8 | } |
208 | | }); |
209 | | |
210 | 16 | self.state = NegotiationState::Multiplexing { |
211 | 16 | peer_id: remote_peer_id, |
212 | 16 | encryption: Box::new(cipher), |
213 | 16 | negotiation, |
214 | 16 | }; |
215 | 16 | |
216 | 16 | continue; |
217 | | } |
218 | 24 | noise::NoiseHandshake::InProgress(updated) => { |
219 | 24 | return Ok(Handshake::Healthy(HealthyHandshake { |
220 | 24 | state: NegotiationState::Encryption { handshake: updated }, |
221 | 24 | })); |
222 | | } |
223 | | }; |
224 | | } |
225 | | |
226 | | NegotiationState::Multiplexing { |
227 | 40 | negotiation, |
228 | 40 | mut encryption, |
229 | 40 | peer_id, |
230 | 40 | } => { |
231 | 40 | // During the multiplexing protocol negotiation, all exchanges have to go |
232 | 40 | // through the Noise cipher. |
233 | 40 | |
234 | 40 | if read_write.expected_incoming_bytes.is_none() { |
235 | 0 | return Err(HandshakeError::MultiplexingMultistreamSelect( |
236 | 0 | multistream_select::Error::ReadClosed, |
237 | 0 | )); |
238 | 40 | } |
239 | 40 | if read_write.write_bytes_queueable.is_none() { |
240 | 0 | return Err(HandshakeError::MultiplexingMultistreamSelect( |
241 | 0 | multistream_select::Error::WriteClosed, |
242 | 0 | )); |
243 | 40 | } |
244 | | |
245 | 40 | let negotiation_update = { |
246 | 40 | let mut decrypted_stream = encryption |
247 | 40 | .read_write(read_write) |
248 | 40 | .map_err(HandshakeError::Noise)?0 ; |
249 | 40 | negotiation |
250 | 40 | .read_write(&mut *decrypted_stream) |
251 | 40 | .map_err(HandshakeError::MultiplexingMultistreamSelect)?0 |
252 | | }; |
253 | | |
254 | 40 | return match negotiation_update { |
255 | 16 | multistream_select::Negotiation::InProgress(updated) => { |
256 | 16 | Ok(Handshake::Healthy(HealthyHandshake { |
257 | 16 | state: NegotiationState::Multiplexing { |
258 | 16 | negotiation: updated, |
259 | 16 | encryption, |
260 | 16 | peer_id, |
261 | 16 | }, |
262 | 16 | })) |
263 | | } |
264 | 8 | multistream_select::Negotiation::ListenerAcceptOrDeny(accept_reject) => { |
265 | 8 | let negotiation = |
266 | 8 | if accept_reject.requested_protocol() == yamux::PROTOCOL_NAME { |
267 | 8 | accept_reject.accept() |
268 | | } else { |
269 | 0 | accept_reject.reject() |
270 | | }; |
271 | 8 | self.state = NegotiationState::Multiplexing { |
272 | 8 | peer_id, |
273 | 8 | encryption, |
274 | 8 | negotiation, |
275 | 8 | }; |
276 | 8 | continue; |
277 | | } |
278 | 16 | multistream_select::Negotiation::Success => Ok(Handshake::Success { |
279 | 16 | connection: ConnectionPrototype::from_noise_yamux(*encryption), |
280 | 16 | remote_peer_id: peer_id, |
281 | 16 | }), |
282 | | multistream_select::Negotiation::NotAvailable => { |
283 | 0 | Err(HandshakeError::NoMultiplexingProtocol) |
284 | | } |
285 | | }; |
286 | | } |
287 | | } |
288 | | } |
289 | 64 | } _RINvMs_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection23single_stream_handshakeNtB5_16HealthyHandshake10read_writeNtNtCsaYZPK01V26L_4core4time8DurationEBb_ Line | Count | Source | 137 | 56 | pub fn read_write<TNow: Clone>( | 138 | 56 | mut self, | 139 | 56 | read_write: &mut ReadWrite<TNow>, | 140 | 56 | ) -> Result<Handshake, HandshakeError> { | 141 | 98 | loop { | 142 | 98 | match self.state { | 143 | | NegotiationState::EncryptionProtocol { | 144 | 28 | negotiation, | 145 | 28 | handshake, | 146 | | } => { | 147 | | // Earliest point of the handshake. The encryption is being negotiated. | 148 | | // Delegating read/write to the negotiation. | 149 | 28 | let updated = negotiation | 150 | 28 | .read_write(read_write) | 151 | 28 | .map_err(HandshakeError::EncryptionMultistreamSelect)?0 ; | 152 | | | 153 | 28 | return match updated { | 154 | 7 | multistream_select::Negotiation::InProgress(updated) => { | 155 | 7 | Ok(Handshake::Healthy(HealthyHandshake { | 156 | 7 | state: NegotiationState::EncryptionProtocol { | 157 | 7 | negotiation: updated, | 158 | 7 | handshake, | 159 | 7 | }, | 160 | 7 | })) | 161 | | } | 162 | | multistream_select::Negotiation::Success => { | 163 | 14 | self.state = NegotiationState::Encryption { handshake }; | 164 | 14 | continue; | 165 | | } | 166 | 7 | multistream_select::Negotiation::ListenerAcceptOrDeny(accept_reject) => { | 167 | 7 | let negotiation = | 168 | 7 | if accept_reject.requested_protocol() == noise::PROTOCOL_NAME { | 169 | 7 | accept_reject.accept() | 170 | | } else { | 171 | 0 | accept_reject.reject() | 172 | | }; | 173 | 7 | self.state = NegotiationState::EncryptionProtocol { | 174 | 7 | negotiation, | 175 | 7 | handshake, | 176 | 7 | }; | 177 | 7 | continue; | 178 | | } | 179 | | multistream_select::Negotiation::NotAvailable => { | 180 | 0 | Err(HandshakeError::NoEncryptionProtocol) | 181 | | } | 182 | | }; | 183 | | } | 184 | | | 185 | 35 | NegotiationState::Encryption { handshake } => { | 186 | | // Delegating read/write to the Noise handshake state machine. | 187 | 35 | let updated = handshake.read_write(read_write).map_err(|err| { | 188 | | debug_assert!(!matches!(err, noise::HandshakeError::WriteClosed)); | 189 | | HandshakeError::NoiseHandshake(err) | 190 | 35 | })?0 ; | 191 | | | 192 | 35 | match updated { | 193 | | noise::NoiseHandshake::Success { | 194 | 14 | cipher, | 195 | 14 | remote_peer_id, | 196 | | } => { | 197 | | // Encryption layer has been successfully negotiated. Start the | 198 | | // handshake for the multiplexing protocol negotiation. | 199 | 14 | let negotiation = | 200 | 14 | multistream_select::InProgress::new(if cipher.is_initiator() { | 201 | 7 | multistream_select::Config::Dialer { | 202 | 7 | requested_protocol: yamux::PROTOCOL_NAME, | 203 | 7 | } | 204 | | } else { | 205 | 7 | multistream_select::Config::Listener { | 206 | 7 | max_protocol_name_len: yamux::PROTOCOL_NAME.len(), | 207 | 7 | } | 208 | | }); | 209 | | | 210 | 14 | self.state = NegotiationState::Multiplexing { | 211 | 14 | peer_id: remote_peer_id, | 212 | 14 | encryption: Box::new(cipher), | 213 | 14 | negotiation, | 214 | 14 | }; | 215 | 14 | | 216 | 14 | continue; | 217 | | } | 218 | 21 | noise::NoiseHandshake::InProgress(updated) => { | 219 | 21 | return Ok(Handshake::Healthy(HealthyHandshake { | 220 | 21 | state: NegotiationState::Encryption { handshake: updated }, | 221 | 21 | })); | 222 | | } | 223 | | }; | 224 | | } | 225 | | | 226 | | NegotiationState::Multiplexing { | 227 | 35 | negotiation, | 228 | 35 | mut encryption, | 229 | 35 | peer_id, | 230 | 35 | } => { | 231 | 35 | // During the multiplexing protocol negotiation, all exchanges have to go | 232 | 35 | // through the Noise cipher. | 233 | 35 | | 234 | 35 | if read_write.expected_incoming_bytes.is_none() { | 235 | 0 | return Err(HandshakeError::MultiplexingMultistreamSelect( | 236 | 0 | multistream_select::Error::ReadClosed, | 237 | 0 | )); | 238 | 35 | } | 239 | 35 | if read_write.write_bytes_queueable.is_none() { | 240 | 0 | return Err(HandshakeError::MultiplexingMultistreamSelect( | 241 | 0 | multistream_select::Error::WriteClosed, | 242 | 0 | )); | 243 | 35 | } | 244 | | | 245 | 35 | let negotiation_update = { | 246 | 35 | let mut decrypted_stream = encryption | 247 | 35 | .read_write(read_write) | 248 | 35 | .map_err(HandshakeError::Noise)?0 ; | 249 | 35 | negotiation | 250 | 35 | .read_write(&mut *decrypted_stream) | 251 | 35 | .map_err(HandshakeError::MultiplexingMultistreamSelect)?0 | 252 | | }; | 253 | | | 254 | 35 | return match negotiation_update { | 255 | 14 | multistream_select::Negotiation::InProgress(updated) => { | 256 | 14 | Ok(Handshake::Healthy(HealthyHandshake { | 257 | 14 | state: NegotiationState::Multiplexing { | 258 | 14 | negotiation: updated, | 259 | 14 | encryption, | 260 | 14 | peer_id, | 261 | 14 | }, | 262 | 14 | })) | 263 | | } | 264 | 7 | multistream_select::Negotiation::ListenerAcceptOrDeny(accept_reject) => { | 265 | 7 | let negotiation = | 266 | 7 | if accept_reject.requested_protocol() == yamux::PROTOCOL_NAME { | 267 | 7 | accept_reject.accept() | 268 | | } else { | 269 | 0 | accept_reject.reject() | 270 | | }; | 271 | 7 | self.state = NegotiationState::Multiplexing { | 272 | 7 | peer_id, | 273 | 7 | encryption, | 274 | 7 | negotiation, | 275 | 7 | }; | 276 | 7 | continue; | 277 | | } | 278 | 14 | multistream_select::Negotiation::Success => Ok(Handshake::Success { | 279 | 14 | connection: ConnectionPrototype::from_noise_yamux(*encryption), | 280 | 14 | remote_peer_id: peer_id, | 281 | 14 | }), | 282 | | multistream_select::Negotiation::NotAvailable => { | 283 | 0 | Err(HandshakeError::NoMultiplexingProtocol) | 284 | | } | 285 | | }; | 286 | | } | 287 | | } | 288 | | } | 289 | 56 | } |
_RINvMs_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection23single_stream_handshakeNtB5_16HealthyHandshake10read_writelEBb_ Line | Count | Source | 137 | 8 | pub fn read_write<TNow: Clone>( | 138 | 8 | mut self, | 139 | 8 | read_write: &mut ReadWrite<TNow>, | 140 | 8 | ) -> Result<Handshake, HandshakeError> { | 141 | 14 | loop { | 142 | 14 | match self.state { | 143 | | NegotiationState::EncryptionProtocol { | 144 | 4 | negotiation, | 145 | 4 | handshake, | 146 | | } => { | 147 | | // Earliest point of the handshake. The encryption is being negotiated. | 148 | | // Delegating read/write to the negotiation. | 149 | 4 | let updated = negotiation | 150 | 4 | .read_write(read_write) | 151 | 4 | .map_err(HandshakeError::EncryptionMultistreamSelect)?0 ; | 152 | | | 153 | 4 | return match updated { | 154 | 1 | multistream_select::Negotiation::InProgress(updated) => { | 155 | 1 | Ok(Handshake::Healthy(HealthyHandshake { | 156 | 1 | state: NegotiationState::EncryptionProtocol { | 157 | 1 | negotiation: updated, | 158 | 1 | handshake, | 159 | 1 | }, | 160 | 1 | })) | 161 | | } | 162 | | multistream_select::Negotiation::Success => { | 163 | 2 | self.state = NegotiationState::Encryption { handshake }; | 164 | 2 | continue; | 165 | | } | 166 | 1 | multistream_select::Negotiation::ListenerAcceptOrDeny(accept_reject) => { | 167 | 1 | let negotiation = | 168 | 1 | if accept_reject.requested_protocol() == noise::PROTOCOL_NAME { | 169 | 1 | accept_reject.accept() | 170 | | } else { | 171 | 0 | accept_reject.reject() | 172 | | }; | 173 | 1 | self.state = NegotiationState::EncryptionProtocol { | 174 | 1 | negotiation, | 175 | 1 | handshake, | 176 | 1 | }; | 177 | 1 | continue; | 178 | | } | 179 | | multistream_select::Negotiation::NotAvailable => { | 180 | 0 | Err(HandshakeError::NoEncryptionProtocol) | 181 | | } | 182 | | }; | 183 | | } | 184 | | | 185 | 5 | NegotiationState::Encryption { handshake } => { | 186 | | // Delegating read/write to the Noise handshake state machine. | 187 | 5 | let updated = handshake.read_write(read_write).map_err(|err| { | 188 | | debug_assert!(!matches!(err, noise::HandshakeError::WriteClosed)); | 189 | | HandshakeError::NoiseHandshake(err) | 190 | 5 | })?0 ; | 191 | | | 192 | 5 | match updated { | 193 | | noise::NoiseHandshake::Success { | 194 | 2 | cipher, | 195 | 2 | remote_peer_id, | 196 | | } => { | 197 | | // Encryption layer has been successfully negotiated. Start the | 198 | | // handshake for the multiplexing protocol negotiation. | 199 | 2 | let negotiation = | 200 | 2 | multistream_select::InProgress::new(if cipher.is_initiator() { | 201 | 1 | multistream_select::Config::Dialer { | 202 | 1 | requested_protocol: yamux::PROTOCOL_NAME, | 203 | 1 | } | 204 | | } else { | 205 | 1 | multistream_select::Config::Listener { | 206 | 1 | max_protocol_name_len: yamux::PROTOCOL_NAME.len(), | 207 | 1 | } | 208 | | }); | 209 | | | 210 | 2 | self.state = NegotiationState::Multiplexing { | 211 | 2 | peer_id: remote_peer_id, | 212 | 2 | encryption: Box::new(cipher), | 213 | 2 | negotiation, | 214 | 2 | }; | 215 | 2 | | 216 | 2 | continue; | 217 | | } | 218 | 3 | noise::NoiseHandshake::InProgress(updated) => { | 219 | 3 | return Ok(Handshake::Healthy(HealthyHandshake { | 220 | 3 | state: NegotiationState::Encryption { handshake: updated }, | 221 | 3 | })); | 222 | | } | 223 | | }; | 224 | | } | 225 | | | 226 | | NegotiationState::Multiplexing { | 227 | 5 | negotiation, | 228 | 5 | mut encryption, | 229 | 5 | peer_id, | 230 | 5 | } => { | 231 | 5 | // During the multiplexing protocol negotiation, all exchanges have to go | 232 | 5 | // through the Noise cipher. | 233 | 5 | | 234 | 5 | if read_write.expected_incoming_bytes.is_none() { | 235 | 0 | return Err(HandshakeError::MultiplexingMultistreamSelect( | 236 | 0 | multistream_select::Error::ReadClosed, | 237 | 0 | )); | 238 | 5 | } | 239 | 5 | if read_write.write_bytes_queueable.is_none() { | 240 | 0 | return Err(HandshakeError::MultiplexingMultistreamSelect( | 241 | 0 | multistream_select::Error::WriteClosed, | 242 | 0 | )); | 243 | 5 | } | 244 | | | 245 | 5 | let negotiation_update = { | 246 | 5 | let mut decrypted_stream = encryption | 247 | 5 | .read_write(read_write) | 248 | 5 | .map_err(HandshakeError::Noise)?0 ; | 249 | 5 | negotiation | 250 | 5 | .read_write(&mut *decrypted_stream) | 251 | 5 | .map_err(HandshakeError::MultiplexingMultistreamSelect)?0 | 252 | | }; | 253 | | | 254 | 5 | return match negotiation_update { | 255 | 2 | multistream_select::Negotiation::InProgress(updated) => { | 256 | 2 | Ok(Handshake::Healthy(HealthyHandshake { | 257 | 2 | state: NegotiationState::Multiplexing { | 258 | 2 | negotiation: updated, | 259 | 2 | encryption, | 260 | 2 | peer_id, | 261 | 2 | }, | 262 | 2 | })) | 263 | | } | 264 | 1 | multistream_select::Negotiation::ListenerAcceptOrDeny(accept_reject) => { | 265 | 1 | let negotiation = | 266 | 1 | if accept_reject.requested_protocol() == yamux::PROTOCOL_NAME { | 267 | 1 | accept_reject.accept() | 268 | | } else { | 269 | 0 | accept_reject.reject() | 270 | | }; | 271 | 1 | self.state = NegotiationState::Multiplexing { | 272 | 1 | peer_id, | 273 | 1 | encryption, | 274 | 1 | negotiation, | 275 | 1 | }; | 276 | 1 | continue; | 277 | | } | 278 | 2 | multistream_select::Negotiation::Success => Ok(Handshake::Success { | 279 | 2 | connection: ConnectionPrototype::from_noise_yamux(*encryption), | 280 | 2 | remote_peer_id: peer_id, | 281 | 2 | }), | 282 | | multistream_select::Negotiation::NotAvailable => { | 283 | 0 | Err(HandshakeError::NoMultiplexingProtocol) | 284 | | } | 285 | | }; | 286 | | } | 287 | | } | 288 | | } | 289 | 8 | } |
Unexecuted instantiation: _RINvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection23single_stream_handshakeNtB5_16HealthyHandshake10read_writeNtNtCsaYZPK01V26L_4core4time8DurationECsDDUKWWCHAU_18smoldot_light_wasm Unexecuted instantiation: _RINvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection23single_stream_handshakeNtB5_16HealthyHandshake10read_writepEBb_ Unexecuted instantiation: _RINvMs_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection23single_stream_handshakeNtB5_16HealthyHandshake10read_writeNtNtCsbpXXxgr6u8g_3std4time7InstantECsiUjFBJteJ7x_17smoldot_full_node |
290 | | } |
291 | | |
292 | | impl fmt::Debug for HealthyHandshake { |
293 | 0 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
294 | 0 | f.debug_struct("HealthyHandshake").finish() |
295 | 0 | } Unexecuted instantiation: _RNvXs0_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection23single_stream_handshakeNtB5_16HealthyHandshakeNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt Unexecuted instantiation: _RNvXs0_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection23single_stream_handshakeNtB5_16HealthyHandshakeNtNtCsaYZPK01V26L_4core3fmt5Debug3fmt |
296 | | } |
297 | | |
298 | | /// Error during a connection handshake. The connection should be shut down. |
299 | 0 | #[derive(Debug, derive_more::Display)] Unexecuted instantiation: _RNvXs5_NtNtNtCsN16ciHI6Qf_7smoldot6libp2p10connection23single_stream_handshakeNtB5_14HandshakeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt Unexecuted instantiation: _RNvXs5_NtNtNtCseuYC0Zibziv_7smoldot6libp2p10connection23single_stream_handshakeNtB5_14HandshakeErrorNtNtCsaYZPK01V26L_4core3fmt7Display3fmt |
300 | | pub enum HandshakeError { |
301 | | /// Protocol error during the multistream-select negotiation of the encryption protocol. |
302 | | #[display(fmt = "Encryption protocol selection error: {_0}")] |
303 | | EncryptionMultistreamSelect(multistream_select::Error), |
304 | | /// Protocol error during the multistream-select negotiation of the multiplexing protocol. |
305 | | #[display(fmt = "Multiplexing protocol selection error: {_0}")] |
306 | | MultiplexingMultistreamSelect(multistream_select::Error), |
307 | | /// Protocol error during the noise handshake. |
308 | | #[display(fmt = "Noise handshake error: {_0}")] |
309 | | NoiseHandshake(noise::HandshakeError), |
310 | | /// No encryption protocol in common with the remote. |
311 | | /// |
312 | | /// The remote is behaving correctly but isn't compatible with the local node. |
313 | | NoEncryptionProtocol, |
314 | | /// No multiplexing protocol in common with the remote. |
315 | | /// |
316 | | /// The remote is behaving correctly but isn't compatible with the local node. |
317 | | NoMultiplexingProtocol, |
318 | | /// Error in the noise cipher. Data has most likely been corrupted. |
319 | | #[display(fmt = "Noise cipher error: {_0}")] |
320 | | Noise(noise::CipherError), |
321 | | } |