bramble-handshake 0.1.0

Bramble Handshake Protocol
Documentation
//! BHP protocol implementation

use crate::{Error, Result, BETA_VERSION, CURRENT_VERSION};
use bramble_crypto::{
    dh, hash, mac, KeyPair, Mac, PublicKey, Role, SymmetricKey, KEY_LEN, MAC_LEN,
};
use futures::io::{AsyncRead, AsyncWrite};
use rand::thread_rng;
use std::{pin::Pin, slice};

/// Perform the handshake over the given transport
pub async fn perform_handshake<T>(
    transport: T,
    our_static_kp: KeyPair,
    their_static_pk: PublicKey,
) -> Result<(SymmetricKey, Role)>
where
    T: AsyncRead + AsyncWrite,
{
    let our_bytes = our_static_kp.public().as_ref();
    let their_bytes = their_static_pk.as_ref();
    let role = if our_bytes < their_bytes {
        Role::Alice
    } else {
        Role::Bob
    };
    let protocol = Protocol {
        transport: Box::pin(transport),
        role,
        our_static_kp,
        their_static_pk,
    };
    Ok((protocol.perform().await?, role))
}

struct Protocol<T>
where
    T: AsyncRead + AsyncWrite,
{
    transport: Pin<Box<T>>,
    role: Role,
    our_static_kp: KeyPair,
    their_static_pk: PublicKey,
}

impl<T> Protocol<T>
where
    T: AsyncRead + AsyncWrite,
{
    pub async fn perform(mut self) -> Result<SymmetricKey> {
        let our_ephemeral_kp = KeyPair::generate(&mut thread_rng());
        let their_ephemeral_pk = if self.role == Role::Alice {
            self.send_ephemeral_public_key(our_ephemeral_kp.public())
                .await?;
            self.receive_ephemeral_public_key().await?
        } else {
            let their_ephemeral_pk = self.receive_ephemeral_public_key().await?;
            self.send_ephemeral_public_key(our_ephemeral_kp.public())
                .await?;
            their_ephemeral_pk
        };

        let master_key = self.derive_master_key(our_ephemeral_kp, their_ephemeral_pk)?;
        if self.role == Role::Alice {
            self.send_proof_of_ownership(&master_key, Role::Alice)
                .await?;
            self.receive_proof_of_ownership(&master_key, Role::Bob)
                .await?;
        } else {
            self.receive_proof_of_ownership(&master_key, Role::Alice)
                .await?;
            self.send_proof_of_ownership(&master_key, Role::Bob).await?;
        }
        Ok(master_key)
    }

    async fn send_ephemeral_public_key(&mut self, pk: &PublicKey) -> Result<()> {
        use futures::io::AsyncWriteExt;

        let mut buf = [0u8; EPHEMERAL_PUBLIC_KEY_RECORD_LEN];
        let (version, slice) = buf.split_at_mut(1);
        let (typ, slice) = slice.split_at_mut(1);
        let (len, key) = slice.split_at_mut(2);
        version.copy_from_slice(&CURRENT_VERSION.to_be_bytes());
        typ.copy_from_slice(&EPHEMERAL_PUBLIC_KEY_RECORD_TYPE.to_be_bytes());
        len.copy_from_slice(&(KEY_LEN as u16).to_be_bytes());
        key.copy_from_slice(pk.as_ref());
        self.transport.write_all(&buf).await?;
        Ok(())
    }

    async fn receive_ephemeral_public_key(&mut self) -> Result<PublicKey> {
        use futures::io::AsyncReadExt;

        let _version = self.read_version().await?;

        let mut record_type = 0u8;
        self.transport
            .read_exact(slice::from_mut(&mut record_type))
            .await?;
        if record_type != EPHEMERAL_PUBLIC_KEY_RECORD_TYPE {
            return Err(Error::InvalidRecord);
        }

        let mut len_bytes = [0u8; 2];
        self.transport.read_exact(&mut len_bytes).await?;
        let len = u16::from_be_bytes(len_bytes);
        if len as usize != KEY_LEN {
            return Err(Error::InvalidKey);
        }

        let mut pk_bytes = [0u8; KEY_LEN];
        self.transport.read_exact(&mut pk_bytes).await?;
        Ok(PublicKey::from(pk_bytes))
    }

    async fn send_proof_of_ownership(&mut self, key: &SymmetricKey, role: Role) -> Result<()> {
        use futures::io::AsyncWriteExt;

        let proof = self.derive_proof_of_ownership(key, role);
        let mut buf = [0u8; PROOF_OF_OWNERSHIP_RECORD_LEN];
        let (version, slice) = buf.split_at_mut(1);
        let (typ, slice) = slice.split_at_mut(1);
        let (len, key) = slice.split_at_mut(2);
        version.copy_from_slice(&CURRENT_VERSION.to_be_bytes());
        typ.copy_from_slice(&PROOF_OF_OWNERSHIP_RECORD_TYPE.to_be_bytes());
        len.copy_from_slice(&(MAC_LEN as u16).to_be_bytes());
        key.copy_from_slice(proof.as_ref());
        self.transport.write_all(&buf).await?;
        Ok(())
    }

    async fn receive_proof_of_ownership(&mut self, key: &SymmetricKey, role: Role) -> Result<()> {
        use futures::io::AsyncReadExt;

        let _version = self.read_version().await?;

        let mut record_type = 0u8;
        self.transport
            .read_exact(slice::from_mut(&mut record_type))
            .await?;
        if record_type != PROOF_OF_OWNERSHIP_RECORD_TYPE {
            return Err(Error::InvalidRecord);
        }

        let mut len_bytes = [0u8; 2];
        self.transport.read_exact(&mut len_bytes).await?;
        let len = u16::from_be_bytes(len_bytes);
        if len as usize != MAC_LEN {
            return Err(Error::InvalidKey);
        }

        let mut proof_bytes = [0u8; MAC_LEN];
        self.transport.read_exact(&mut proof_bytes).await?;
        let proof = Mac::from(proof_bytes);
        let expected = self.derive_proof_of_ownership(key, role);
        proof.verify(&expected).map_err(|_| Error::InvalidProof)
    }

    async fn read_version(&mut self) -> Result<u8> {
        use futures::io::AsyncReadExt;

        let mut version = 0u8;
        self.transport
            .read_exact(slice::from_mut(&mut version))
            .await?;
        if version == BETA_VERSION {
            return Err(Error::BetaVersion);
        }
        // allow absurd comparison while current version is 0
        #[allow(clippy::absurd_extreme_comparisons)]
        if version < CURRENT_VERSION {
            return Err(Error::OlderVersion);
        }
        if version > CURRENT_VERSION {
            return Err(Error::NewerVersion);
        }
        Ok(version)
    }

    fn derive_master_key(
        &self,
        our_ephemeral_kp: KeyPair,
        their_ephemeral_pk: PublicKey,
    ) -> Result<SymmetricKey> {
        let raw_static = dh(self.our_static_kp.secret(), &self.their_static_pk);
        let raw1 = dh(self.our_static_kp.secret(), &their_ephemeral_pk);
        let raw2 = dh(our_ephemeral_kp.secret(), &self.their_static_pk);
        let (
            raw_static_ephemeral,
            raw_ephemeral_static,
            alice_static_pk,
            bob_static_pk,
            alice_ephemeral_pk,
            bob_ephemeral_pk,
        ) = if self.role == Role::Alice {
            (
                raw1,
                raw2,
                self.our_static_kp.public(),
                &self.their_static_pk,
                our_ephemeral_kp.public(),
                &their_ephemeral_pk,
            )
        } else {
            (
                raw2,
                raw1,
                &self.their_static_pk,
                self.our_static_kp.public(),
                &their_ephemeral_pk,
                our_ephemeral_kp.public(),
            )
        };
        let key = SymmetricKey::from(hash(
            MASTER_KEY_LABEL,
            &[
                raw_static.as_ref(),
                raw_static_ephemeral.as_ref(),
                raw_ephemeral_static.as_ref(),
                alice_static_pk.as_ref(),
                bob_static_pk.as_ref(),
                alice_ephemeral_pk.as_ref(),
                bob_ephemeral_pk.as_ref(),
            ],
        ));

        Ok(key)
    }

    fn derive_proof_of_ownership(&mut self, key: &SymmetricKey, role: Role) -> Mac {
        let label = if role == Role::Alice {
            ALICE_PROOF_LABEL
        } else {
            BOB_PROOF_LABEL
        };
        mac(label, key, &[])
    }
}

const MASTER_KEY_LABEL: &[u8] = b"org.briarproject.bramble.handshake/MASTER_KEY";
const ALICE_PROOF_LABEL: &[u8] = b"org.briarproject.bramble.handshake/ALICE_PROOF";
const BOB_PROOF_LABEL: &[u8] = b"org.briarproject.bramble.handshake/BOB_PROOF";
const EPHEMERAL_PUBLIC_KEY_RECORD_LEN: usize = 1 + 1 + 2 + KEY_LEN;
const PROOF_OF_OWNERSHIP_RECORD_LEN: usize = 1 + 1 + 2 + MAC_LEN;
const EPHEMERAL_PUBLIC_KEY_RECORD_TYPE: u8 = 0;
const PROOF_OF_OWNERSHIP_RECORD_TYPE: u8 = 1;

#[cfg(test)]
mod test {
    use super::*;
    use bramble_common::make_duplex;
    use futures::{executor::block_on, try_join};
    use rand::thread_rng;

    #[test]
    fn both_sides_compute_the_same_master_secret() {
        let (transport1, transport2) = make_duplex();

        let mut rng = thread_rng();
        let kp1 = KeyPair::generate(&mut rng);
        let pk1 = kp1.public();

        let mut rng = thread_rng();
        let kp2 = KeyPair::generate(&mut rng);
        let pk2 = kp2.public();

        let fut1 = perform_handshake(transport1, kp1, *pk2);
        let fut2 = perform_handshake(transport2, kp2, *pk1);

        let ((master_key1, role1), (master_key2, role2)) =
            block_on(async { try_join!(fut1, fut2) }).unwrap();

        assert_eq!(master_key1, master_key2);
        assert_ne!(role1, role2);
    }
}