libp2p_noise/io/
handshake.rs1pub(super) mod proto {
24 #![allow(unreachable_pub)]
25 include!("../generated/mod.rs");
26 pub use self::payload::proto::{NoiseExtensions, NoiseHandshakePayload};
27}
28
29use std::{collections::HashSet, io, mem};
30
31use asynchronous_codec::Framed;
32use futures::prelude::*;
33use libp2p_identity as identity;
34use multihash::Multihash;
35use quick_protobuf::MessageWrite;
36
37use super::framed::Codec;
38use crate::{
39 io::Output,
40 protocol::{KeypairIdentity, PublicKey, STATIC_KEY_DOMAIN},
41 Error,
42};
43
44pub(crate) struct State<T> {
49 io: Framed<T, Codec<snow::HandshakeState>>,
51 identity: KeypairIdentity,
54 dh_remote_pubkey_sig: Option<Vec<u8>>,
56 id_remote_pubkey: Option<identity::PublicKey>,
58 responder_webtransport_certhashes: Option<HashSet<Multihash<64>>>,
60 remote_extensions: Option<Extensions>,
62}
63
64struct Extensions {
66 webtransport_certhashes: HashSet<Multihash<64>>,
67}
68
69impl<T> State<T>
70where
71 T: AsyncRead + AsyncWrite,
72{
73 pub(crate) fn new(
79 io: T,
80 session: snow::HandshakeState,
81 identity: KeypairIdentity,
82 expected_remote_key: Option<identity::PublicKey>,
83 responder_webtransport_certhashes: Option<HashSet<Multihash<64>>>,
84 ) -> Self {
85 Self {
86 identity,
87 io: Framed::new(io, Codec::new(session)),
88 dh_remote_pubkey_sig: None,
89 id_remote_pubkey: expected_remote_key,
90 responder_webtransport_certhashes,
91 remote_extensions: None,
92 }
93 }
94}
95
96impl<T> State<T>
97where
98 T: AsyncRead + AsyncWrite,
99{
100 pub(crate) fn finish(self) -> Result<(identity::PublicKey, Output<T>), Error> {
103 let is_initiator = self.io.codec().is_initiator();
104
105 let (pubkey, framed) = map_into_transport(self.io)?;
106
107 let id_pk = self
108 .id_remote_pubkey
109 .ok_or_else(|| Error::AuthenticationFailed)?;
110
111 let is_valid_signature = self.dh_remote_pubkey_sig.as_ref().is_some_and(|s| {
112 id_pk.verify(&[STATIC_KEY_DOMAIN.as_bytes(), pubkey.as_ref()].concat(), s)
113 });
114
115 if !is_valid_signature {
116 return Err(Error::BadSignature);
117 }
118
119 if is_initiator {
121 if let Some(expected_certhashes) = self.responder_webtransport_certhashes {
123 let ext = self.remote_extensions.ok_or_else(|| {
124 Error::UnknownWebTransportCerthashes(
125 expected_certhashes.to_owned(),
126 HashSet::new(),
127 )
128 })?;
129
130 let received_certhashes = ext.webtransport_certhashes;
131
132 if !expected_certhashes.is_subset(&received_certhashes) {
135 return Err(Error::UnknownWebTransportCerthashes(
136 expected_certhashes,
137 received_certhashes,
138 ));
139 }
140 }
141 }
142
143 Ok((id_pk, Output::new(framed)))
144 }
145}
146
147fn map_into_transport<T>(
158 framed: Framed<T, Codec<snow::HandshakeState>>,
159) -> Result<(PublicKey, Framed<T, Codec<snow::TransportState>>), Error>
160where
161 T: AsyncRead + AsyncWrite,
162{
163 let mut parts = framed.into_parts().map_codec(Some);
164
165 let (pubkey, codec) = mem::take(&mut parts.codec)
166 .expect("We just set it to `Some`")
167 .into_transport()?;
168
169 let parts = parts.map_codec(|_| codec);
170 let framed = Framed::from_parts(parts);
171
172 Ok((pubkey, framed))
173}
174
175impl From<proto::NoiseExtensions> for Extensions {
176 fn from(value: proto::NoiseExtensions) -> Self {
177 Extensions {
178 webtransport_certhashes: value
179 .webtransport_certhashes
180 .into_iter()
181 .filter_map(|bytes| Multihash::read(&bytes[..]).ok())
182 .collect(),
183 }
184 }
185}
186
187async fn recv<T>(state: &mut State<T>) -> Result<proto::NoiseHandshakePayload, Error>
192where
193 T: AsyncRead + Unpin,
194{
195 match state.io.next().await {
196 None => Err(io::Error::new(io::ErrorKind::UnexpectedEof, "eof").into()),
197 Some(Err(e)) => Err(e.into()),
198 Some(Ok(p)) => Ok(p),
199 }
200}
201
202pub(crate) async fn recv_empty<T>(state: &mut State<T>) -> Result<(), Error>
204where
205 T: AsyncRead + Unpin,
206{
207 let payload = recv(state).await?;
208 if payload.get_size() != 0 {
209 return Err(io::Error::new(io::ErrorKind::InvalidData, "Expected empty payload.").into());
210 }
211
212 Ok(())
213}
214
215pub(crate) async fn send_empty<T>(state: &mut State<T>) -> Result<(), Error>
217where
218 T: AsyncWrite + Unpin,
219{
220 state
221 .io
222 .send(&proto::NoiseHandshakePayload::default())
223 .await?;
224 Ok(())
225}
226
227pub(crate) async fn recv_identity<T>(state: &mut State<T>) -> Result<(), Error>
229where
230 T: AsyncRead + Unpin,
231{
232 let pb = recv(state).await?;
233 state.id_remote_pubkey = Some(identity::PublicKey::try_decode_protobuf(&pb.identity_key)?);
234
235 if !pb.identity_sig.is_empty() {
236 state.dh_remote_pubkey_sig = Some(pb.identity_sig);
237 }
238
239 if let Some(extensions) = pb.extensions {
240 state.remote_extensions = Some(extensions.into());
241 }
242
243 Ok(())
244}
245
246pub(crate) async fn send_identity<T>(state: &mut State<T>) -> Result<(), Error>
248where
249 T: AsyncRead + AsyncWrite + Unpin,
250{
251 let mut pb = proto::NoiseHandshakePayload {
252 identity_key: state.identity.public.encode_protobuf(),
253 ..Default::default()
254 };
255
256 pb.identity_sig.clone_from(&state.identity.signature);
257
258 if state.io.codec().is_responder() {
260 if let Some(ref certhashes) = state.responder_webtransport_certhashes {
261 let ext = pb
262 .extensions
263 .get_or_insert_with(proto::NoiseExtensions::default);
264
265 ext.webtransport_certhashes = certhashes.iter().map(|hash| hash.to_bytes()).collect();
266 }
267 }
268
269 state.io.send(&pb).await?;
270
271 Ok(())
272}