1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use crate::{
    codec::handshake::{HandshakeInitialize, HandshakeResponse},
    Handle,
};
use anyhow::{bail, Context as _, Result};
use keynesis_core::{
    hash::Blake2b,
    key::{
        ed25519::{self, PublicKey},
        Dh,
    },
    noise::{ik::A, IK},
};
use rand_core::{CryptoRng, RngCore};
use tokio::io::{AsyncRead, AsyncReadExt as _, AsyncWrite, AsyncWriteExt as _};

/// accept incoming handshake
///
/// upon opening an ASMTP session with our node, the initiator will send
/// the initial handshake to authenticate themselves and the responser is required
/// to respond accordingly.
///
/// This object offers the necessary tooling to identify the initiator so it is
/// possible to deny the connection early enough (see [Accepting::accept])
pub struct Accepting<I, O, RNG, K = ed25519::SecretKey> {
    reader: I,
    writer: O,
    state: IK<K, Blake2b, RNG, A>,
}

impl<I, O, K, RNG> Accepting<I, O, RNG, K>
where
    K: Dh,
    RNG: CryptoRng + RngCore,
{
    pub(crate) fn new(rng: RNG, reader: I, writer: O) -> Self {
        Self {
            reader,
            writer,
            state: IK::new(rng, &[]),
        }
    }
}

impl<I, O, K, RNG> Accepting<I, O, RNG, K>
where
    I: AsyncRead + Unpin,
    O: AsyncWrite + Unpin,
    K: Dh,
    RNG: CryptoRng + RngCore,
{
    /// perform the initial handshake with the peer
    ///
    /// Upon receiving the initial handshake message, the function `check_id` will
    /// verify the public key of the user. For example the user can maintain a list
    /// of unwelcome public keys.
    ///
    /// If the peer is accepted and is using a supported version of the protocol
    /// then the functions replies the response handshake.
    ///
    /// # Errors
    ///
    /// This function may fail for IO operations as well as for processing the
    /// noise handshake.
    ///
    pub async fn accept<F>(self, k: &K, check_id: F) -> Result<Handle<I, O>>
    where
        F: Fn(&PublicKey) -> bool,
    {
        let Self {
            mut reader,
            mut writer,
            state,
        } = self;

        let mut bytes = [0; HandshakeInitialize::SIZE];

        reader
            .read_exact(&mut bytes)
            .await
            .context("Cannot receive the Noise IK initiate Handshake")?;

        let message = HandshakeInitialize::from_bytes(bytes);

        if !message.version().is_supported() {
            bail!("Unsupported version {:?}", message.version());
        }

        let state = state
            .receive(k, message.message())
            .context("Noise IK Handshake Initiate failed")?;

        if !check_id(state.remote_public_identity()) {
            bail!(
                "Rejecting connection with {}",
                state.remote_public_identity()
            )
        }

        let mut message = HandshakeResponse::DEFAULT;

        let state = state
            .reply(&mut message.message_mut())
            .context("Cannot prep the Noise's Handshake Response message")?;

        writer
            .write_all(message.as_ref())
            .await
            .context("Cannot send the Noise IK response Handshake")?;

        Ok(Handle::new(reader, writer, state))
    }
}