libp2p_plaintext/
handshake.rs

1// Copyright 2019 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21use crate::PlainText2Config;
22use crate::error::PlainTextError;
23use crate::structs_proto::Exchange;
24
25use bytes::{Bytes, BytesMut};
26use futures::prelude::*;
27use asynchronous_codec::{Framed, FramedParts};
28use libp2p_core::{PublicKey, PeerId};
29use log::{debug, trace};
30use prost::Message;
31use std::io::{Error as IoError, ErrorKind as IoErrorKind};
32use unsigned_varint::codec::UviBytes;
33
34struct HandshakeContext<T> {
35    config: PlainText2Config,
36    state: T
37}
38
39// HandshakeContext<()> --with_local-> HandshakeContext<Local>
40struct Local {
41    // Our local exchange's raw bytes:
42    exchange_bytes: Vec<u8>,
43}
44
45// HandshakeContext<Local> --with_remote-> HandshakeContext<Remote>
46pub struct Remote {
47    // The remote's peer ID:
48    pub peer_id: PeerId,
49    // The remote's public key:
50    pub public_key: PublicKey,
51}
52
53impl HandshakeContext<Local> {
54    fn new(config: PlainText2Config) -> Result<Self, PlainTextError> {
55        let exchange = Exchange {
56            id: Some(config.local_public_key.clone().into_peer_id().to_bytes()),
57            pubkey: Some(config.local_public_key.clone().into_protobuf_encoding())
58        };
59        let mut buf = Vec::with_capacity(exchange.encoded_len());
60        exchange.encode(&mut buf).expect("Vec<u8> provides capacity as needed");
61
62        Ok(Self {
63            config,
64            state: Local {
65                exchange_bytes: buf
66            }
67        })
68    }
69
70    fn with_remote(self, exchange_bytes: BytesMut)
71        -> Result<HandshakeContext<Remote>, PlainTextError>
72    {
73        let prop = match Exchange::decode(exchange_bytes) {
74            Ok(prop) => prop,
75            Err(e) => {
76                debug!("failed to parse remote's exchange protobuf message");
77                return Err(PlainTextError::InvalidPayload(Some(e)));
78            },
79        };
80
81        let pb_pubkey = prop.pubkey.unwrap_or_default();
82        let public_key = match PublicKey::from_protobuf_encoding(pb_pubkey.as_slice()) {
83            Ok(p) => p,
84            Err(_) => {
85                debug!("failed to parse remote's exchange's pubkey protobuf");
86                return Err(PlainTextError::InvalidPayload(None));
87            },
88        };
89        let peer_id = match PeerId::from_bytes(&prop.id.unwrap_or_default()) {
90            Ok(p) => p,
91            Err(_) => {
92                debug!("failed to parse remote's exchange's id protobuf");
93                return Err(PlainTextError::InvalidPayload(None));
94            },
95        };
96
97        // Check the validity of the remote's `Exchange`.
98        if peer_id != public_key.clone().into_peer_id() {
99            debug!("the remote's `PeerId` isn't consistent with the remote's public key");
100            return Err(PlainTextError::InvalidPeerId)
101        }
102
103        Ok(HandshakeContext {
104            config: self.config,
105            state: Remote {
106                peer_id,
107                public_key,
108            }
109        })
110    }
111}
112
113pub async fn handshake<S>(socket: S, config: PlainText2Config)
114    -> Result<(S, Remote, Bytes), PlainTextError>
115where
116    S: AsyncRead + AsyncWrite + Send + Unpin,
117{
118    // The handshake messages all start with a variable-length integer indicating the size.
119    let mut framed_socket = Framed::new(socket, UviBytes::default());
120
121    trace!("starting handshake");
122    let context = HandshakeContext::new(config)?;
123
124    trace!("sending exchange to remote");
125    framed_socket.send(BytesMut::from(&context.state.exchange_bytes[..])).await?;
126
127    trace!("receiving the remote's exchange");
128    let context = match framed_socket.next().await {
129        Some(p) => context.with_remote(p?)?,
130        None => {
131            debug!("unexpected eof while waiting for remote's exchange");
132            let err = IoError::new(IoErrorKind::BrokenPipe, "unexpected eof");
133            return Err(err.into());
134        }
135    };
136
137    trace!("received exchange from remote; pubkey = {:?}", context.state.public_key);
138
139    let FramedParts { io, read_buffer, write_buffer, .. } = framed_socket.into_parts();
140    assert!(write_buffer.is_empty());
141    Ok((io, context.state, read_buffer.freeze()))
142}