use crate::errors::PortalError::*;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::error::Error;
use std::io::{Read, Write};
use hkdf::Hkdf;
use sha2::Sha256;
use spake2::{Ed25519Group, Spake2};
mod exchange;
pub use exchange::*;
mod encrypted;
pub use encrypted::*;
mod transferinfo;
pub use transferinfo::*;
#[cfg(test)]
mod tests;
pub struct Protocol;
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Copy, Clone)]
pub enum Direction {
Sender,
Receiver,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub struct ConnectMessage {
pub id: String,
pub direction: Direction,
}
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
pub enum PortalMessage {
Connect(ConnectMessage),
KeyExchange(PortalKeyExchange),
Confirm(PortalConfirmation),
EncryptedDataHeader(EncryptedMessage),
}
impl PortalMessage {
pub fn send<W: Write>(&mut self, writer: &mut W) -> Result<usize, Box<dyn Error>> {
let data = bincode::serialize(&self).or(Err(SerializeError))?;
writer.write_all(&data).or(Err(IOError))?;
Ok(data.len())
}
pub fn recv<R: Read>(reader: &mut R) -> Result<Self, Box<dyn Error>> {
Ok(bincode::deserialize_from::<&mut R, PortalMessage>(reader)?)
}
pub fn parse(data: &[u8]) -> Result<Self, Box<dyn Error>> {
Ok(bincode::deserialize(data)?)
}
}
impl Protocol {
pub fn connect<P: Read + Write>(
peer: &mut P,
id: &str,
direction: Direction,
msg: PortalKeyExchange,
) -> Result<PortalKeyExchange, Box<dyn Error>> {
let c = ConnectMessage {
id: id.to_owned(),
direction,
};
PortalMessage::Connect(c).send(peer)?;
let _info = PortalMessage::recv(peer)?;
PortalMessage::KeyExchange(msg).send(peer)?;
match PortalMessage::recv(peer).or(Err(IOError))? {
PortalMessage::KeyExchange(data) => Ok(data),
_ => Err(Box::new(BadMsg)),
}
}
pub fn derive_key(
state: Spake2<Ed25519Group>,
peer_data: &PortalKeyExchange,
) -> Result<Vec<u8>, Box<dyn Error>> {
Ok(state.finish(peer_data.into()).or(Err(BadMsg))?)
}
pub fn confirm_peer<P: Read + Write>(
peer: &mut P,
id: &str,
direction: Direction,
key: &[u8],
) -> Result<(), Box<dyn Error>> {
let sender_info = format!("{}-{}", id, "senderinfo");
let receiver_info = format!("{}-{}", id, "receiverinfo");
let h = Hkdf::<Sha256>::new(None, key);
let mut sender_confirm = [0u8; 42];
let mut receiver_confirm = [0u8; 42];
h.expand(sender_info.as_bytes(), &mut sender_confirm)
.or(Err(BadMsg))?;
h.expand(receiver_info.as_bytes(), &mut receiver_confirm)
.or(Err(BadMsg))?;
let (to_send, expected) = match direction {
Direction::Sender => (sender_confirm, receiver_confirm),
Direction::Receiver => (receiver_confirm, sender_confirm),
};
let expected = PortalConfirmation(expected);
PortalMessage::Confirm(PortalConfirmation(to_send)).send(peer)?;
let peer_msg = match PortalMessage::recv(peer)? {
PortalMessage::Confirm(inner) => inner,
_ => return Err(BadMsg.into()),
};
if peer_msg != expected {
return Err(PeerKeyMismatch.into());
}
Ok(())
}
pub fn read_encrypted_from<R, D>(reader: &mut R, key: &[u8]) -> Result<D, Box<dyn Error>>
where
R: Read,
D: DeserializeOwned,
{
let mut storage = [0u8; 2048];
Protocol::read_encrypted_zero_copy(reader, key, &mut storage)?;
Ok(bincode::deserialize(&storage).or(Err(BadMsg))?)
}
pub fn read_encrypted_zero_copy<R>(
reader: &mut R,
key: &[u8],
storage: &mut [u8],
) -> Result<usize, Box<dyn Error>>
where
R: Read,
{
let mut msg = match PortalMessage::recv(reader).or(Err(IOError))? {
PortalMessage::EncryptedDataHeader(inner) => inner,
_ => return Err(BadMsg.into()),
};
if storage.len() < msg.len {
return Err(BufferTooSmall.into());
}
let mut pos = 0;
while pos < msg.len {
match reader.read(&mut storage[pos..msg.len]) {
Ok(0) => break,
Ok(len) => {
pos += len;
}
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e.into()),
};
}
msg.decrypt(key, &mut storage[..pos])
}
pub fn encrypt_and_write_object<W, S>(
writer: &mut W,
key: &[u8],
nseq: &mut NonceSequence,
msg: &S,
) -> Result<usize, Box<dyn Error>>
where
W: Write,
S: Serialize,
{
let mut data = bincode::serialize(msg)?;
let encmsg = EncryptedMessage::encrypt(key, nseq, &mut data)?;
PortalMessage::EncryptedDataHeader(encmsg).send(writer)?;
writer.write_all(&data).or(Err(IOError))?;
Ok(data.len())
}
pub fn encrypt_and_write_header_only<W>(
writer: &mut W,
key: &[u8],
nseq: &mut NonceSequence,
data: &mut [u8],
) -> Result<usize, Box<dyn Error>>
where
W: Write,
{
let header = EncryptedMessage::encrypt(key, nseq, data)?;
PortalMessage::EncryptedDataHeader(header).send(writer)
}
}