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