2323mod proto {
2424 #![ allow( unreachable_pub) ]
2525 include ! ( "../generated/mod.rs" ) ;
26+ pub use self :: payload:: proto:: NoiseExtensions ;
2627 pub use self :: payload:: proto:: NoiseHandshakePayload ;
2728}
2829
@@ -31,8 +32,10 @@ use crate::protocol::{KeypairIdentity, STATIC_KEY_DOMAIN};
3132use crate :: { DecodeError , Error } ;
3233use bytes:: Bytes ;
3334use futures:: prelude:: * ;
35+ use libp2p_core:: multihash:: Multihash ;
3436use libp2p_identity as identity;
3537use quick_protobuf:: { BytesReader , MessageRead , MessageWrite , Writer } ;
38+ use std:: collections:: HashSet ;
3639use std:: io;
3740
3841//////////////////////////////////////////////////////////////////////////////
@@ -49,6 +52,15 @@ pub(crate) struct State<T> {
4952 dh_remote_pubkey_sig : Option < Vec < u8 > > ,
5053 /// The known or received public identity key of the remote, if any.
5154 id_remote_pubkey : Option < identity:: PublicKey > ,
55+ /// The WebTransport certhashes of the responder, if any.
56+ responder_webtransport_certhashes : Option < HashSet < Multihash > > ,
57+ /// The received extensions of the remote, if any.
58+ remote_extensions : Option < Extensions > ,
59+ }
60+
61+ /// Extensions
62+ struct Extensions {
63+ webtransport_certhashes : HashSet < Multihash > ,
5264}
5365
5466impl < T > State < T > {
@@ -63,12 +75,15 @@ impl<T> State<T> {
6375 session : snow:: HandshakeState ,
6476 identity : KeypairIdentity ,
6577 expected_remote_key : Option < identity:: PublicKey > ,
78+ responder_webtransport_certhashes : Option < HashSet < Multihash > > ,
6679 ) -> Self {
6780 Self {
6881 identity,
6982 io : NoiseFramed :: new ( io, session) ,
7083 dh_remote_pubkey_sig : None ,
7184 id_remote_pubkey : expected_remote_key,
85+ responder_webtransport_certhashes,
86+ remote_extensions : None ,
7287 }
7388 }
7489}
@@ -77,6 +92,7 @@ impl<T> State<T> {
7792 /// Finish a handshake, yielding the established remote identity and the
7893 /// [`Output`] for communicating on the encrypted channel.
7994 pub ( crate ) fn finish ( self ) -> Result < ( identity:: PublicKey , Output < T > ) , Error > {
95+ let is_initiator = self . io . is_initiator ( ) ;
8096 let ( pubkey, io) = self . io . into_transport ( ) ?;
8197
8298 let id_pk = self
@@ -91,10 +107,43 @@ impl<T> State<T> {
91107 return Err ( Error :: BadSignature ) ;
92108 }
93109
110+ // Check WebTransport certhashes that responder reported back to us
111+ if is_initiator {
112+ // We check only if we care (i.e. Config::with_webtransport_certhashes was used)
113+ if let Some ( expected_certhashes) = self . responder_webtransport_certhashes {
114+ let ext = self . remote_extensions . ok_or_else ( || {
115+ Error :: UnknownWebTransportCerthashes ( expected_certhashes. to_owned ( ) )
116+ } ) ?;
117+
118+ // Expected WebTransport certhashes must be a strict subset
119+ // of the reported ones
120+ let unknown_certhashes = expected_certhashes
121+ . difference ( & ext. webtransport_certhashes )
122+ . cloned ( )
123+ . collect :: < HashSet < _ > > ( ) ;
124+
125+ if !unknown_certhashes. is_empty ( ) {
126+ return Err ( Error :: UnknownWebTransportCerthashes ( unknown_certhashes) ) ;
127+ }
128+ }
129+ }
130+
94131 Ok ( ( id_pk, io) )
95132 }
96133}
97134
135+ impl From < proto:: NoiseExtensions > for Extensions {
136+ fn from ( value : proto:: NoiseExtensions ) -> Self {
137+ Extensions {
138+ webtransport_certhashes : value
139+ . webtransport_certhashes
140+ . into_iter ( )
141+ . filter_map ( |bytes| Multihash :: read ( & bytes[ ..] ) . ok ( ) )
142+ . collect ( ) ,
143+ }
144+ }
145+ }
146+
98147//////////////////////////////////////////////////////////////////////////////
99148// Handshake Message Futures
100149
@@ -149,6 +198,10 @@ where
149198 state. dh_remote_pubkey_sig = Some ( pb. identity_sig ) ;
150199 }
151200
201+ if let Some ( extensions) = pb. extensions {
202+ state. remote_extensions = Some ( extensions. into ( ) ) ;
203+ }
204+
152205 Ok ( ( ) )
153206}
154207
@@ -164,6 +217,17 @@ where
164217
165218 pb. identity_sig = state. identity . signature . clone ( ) ;
166219
220+ // If this is the responder then send WebTransport certhashes to initiator, if any
221+ if state. io . is_responder ( ) {
222+ if let Some ( ref certhashes) = state. responder_webtransport_certhashes {
223+ let ext = pb
224+ . extensions
225+ . get_or_insert_with ( proto:: NoiseExtensions :: default) ;
226+
227+ ext. webtransport_certhashes = certhashes. iter ( ) . map ( |hash| hash. to_bytes ( ) ) . collect ( ) ;
228+ }
229+ }
230+
167231 let mut msg = Vec :: with_capacity ( pb. get_size ( ) ) ;
168232
169233 let mut writer = Writer :: new ( & mut msg) ;
0 commit comments