commit
9142244bca
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
*~
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "rsh"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chacha20stream = { version = "1.0.3", features = ["async"] }
|
||||||
|
color-eyre = "0.5.11"
|
||||||
|
cryptohelpers = { version = "1.8", features = ["serialise", "full"] }
|
||||||
|
pin-project = "1.0.8"
|
||||||
|
serde = { version = "1.0.126", features = ["derive"] }
|
||||||
|
serde_cbor = "0.11.1"
|
||||||
|
tokio = { version = "0.2", features = ["full"] }
|
||||||
|
tokio-uring = "0.1.0"
|
||||||
|
uuid = { version = "0.8.2", features = ["v4", "serde"] }
|
@ -0,0 +1,24 @@
|
|||||||
|
//! Remote communication
|
||||||
|
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
#[macro_use] extern crate serde;
|
||||||
|
#[macro_use] extern crate pin_project;
|
||||||
|
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use color_eyre::{
|
||||||
|
eyre::{
|
||||||
|
self, eyre,
|
||||||
|
WrapErr,
|
||||||
|
},
|
||||||
|
SectionExt, Help,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod message;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> eyre::Result<()> {
|
||||||
|
println!("Hello, world!");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -0,0 +1,187 @@
|
|||||||
|
//! Messages
|
||||||
|
use super::*;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use cryptohelpers::{
|
||||||
|
sha256,
|
||||||
|
aes,
|
||||||
|
rsa,
|
||||||
|
};
|
||||||
|
use uuid::Uuid;
|
||||||
|
use std::borrow::Borrow;
|
||||||
|
use std::io;
|
||||||
|
use std::marker::Unpin;
|
||||||
|
use tokio::io::{
|
||||||
|
AsyncWrite,
|
||||||
|
AsyncRead,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod serial;
|
||||||
|
pub use serial::*;
|
||||||
|
|
||||||
|
/// Size of encrypted AES key
|
||||||
|
pub const RSA_BLOCK_SIZE: usize = 512;
|
||||||
|
|
||||||
|
/// A value that can be used for messages.
|
||||||
|
pub trait MessageValue: Serialize + for<'de> Deserialize<'de>{}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Message<V: ?Sized + MessageValue>
|
||||||
|
{
|
||||||
|
header: SerHeader,
|
||||||
|
/// Optional key to use to encrypt the message
|
||||||
|
key: Option<aes::AesKey>,
|
||||||
|
/// Should the message body be signed?
|
||||||
|
sign: bool,
|
||||||
|
|
||||||
|
/// Value to serialise
|
||||||
|
value: V,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `SerializedMessage` header.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
struct SerHeader
|
||||||
|
{
|
||||||
|
/// Message ID
|
||||||
|
id: Uuid,
|
||||||
|
/// Message idempodence ID
|
||||||
|
idemp: Uuid,
|
||||||
|
/// Timestamp of when this message was created (Unix TS, UTC).
|
||||||
|
timestamp: u64,
|
||||||
|
/// `id` of message this one is responding to, if needed.
|
||||||
|
responds_to: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct SerializedMessage<V: ?Sized + MessageValue>
|
||||||
|
{
|
||||||
|
header: SerHeader,
|
||||||
|
/// cbor serialised `V`.
|
||||||
|
data: Vec<u8>,
|
||||||
|
/// Hash of `data` (after encryption)
|
||||||
|
hash: sha256::Sha256Hash,
|
||||||
|
|
||||||
|
/// `key` encrypted with recipient's RSA public key.
|
||||||
|
enc_key: Option<[u8; RSA_BLOCK_SIZE]>, // we can't derive Serialize because of this array.. meh..
|
||||||
|
/// Signature of hash of un-encrypted `data`.
|
||||||
|
sig: Option<rsa::Signature>,
|
||||||
|
|
||||||
|
_phantom: PhantomData<V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: ?Sized + MessageValue> Message<V>
|
||||||
|
{
|
||||||
|
/// Serialise this message into one that can be converted to/from bytes.
|
||||||
|
pub fn serialise<S: ?Sized + MessageSender>(&self, send_with: impl Borrow<S>) -> eyre::Result<SerializedMessage<V>>
|
||||||
|
{
|
||||||
|
let send_with: &S = send_with.borrow();
|
||||||
|
let data = serde_cbor::to_vec(&self.value)?;
|
||||||
|
|
||||||
|
let sig = if self.sign {
|
||||||
|
Some(send_with.sign_data(&data[..]).expect("Message expected signing, sender did not support it"))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let (data, enc_key) = if let Some(key) = &self.key {
|
||||||
|
// Encrypt the body
|
||||||
|
let enc_key = send_with.encrypt_key(key).expect("Message expected encryption, sender did not support it");
|
||||||
|
(aes::encrypt_slice_sync(key, data)?, Some(enc_key))
|
||||||
|
} else {
|
||||||
|
// Don't encrypt the body
|
||||||
|
(data, None)
|
||||||
|
};
|
||||||
|
// Compute hash of data
|
||||||
|
let hash = sha256::compute_slice(&data);
|
||||||
|
|
||||||
|
Ok(SerializedMessage{
|
||||||
|
header: self.header.clone(),
|
||||||
|
data,
|
||||||
|
sig,
|
||||||
|
enc_key,
|
||||||
|
hash,
|
||||||
|
|
||||||
|
_phantom: PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: ?Sized + MessageValue> SerializedMessage<V>
|
||||||
|
{
|
||||||
|
/// Consume into an async writer
|
||||||
|
pub async fn into_writer_async<W:AsyncWrite+Unpin>(self, mut writer: W) -> eyre::Result<usize>
|
||||||
|
{
|
||||||
|
let mut w = 0;
|
||||||
|
macro_rules! write {
|
||||||
|
($b:expr) => {
|
||||||
|
w+=write_all_async(&mut writer, $b).await?
|
||||||
|
};
|
||||||
|
(? $o:expr) => {
|
||||||
|
match $o {
|
||||||
|
Some(key) => {
|
||||||
|
write!([1]);
|
||||||
|
write!(key);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
write!([0]);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(: $ser:expr) => {
|
||||||
|
{
|
||||||
|
let v = serde_cbor::to_vec($ser)?;
|
||||||
|
write!(&v[..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(: &self.header);
|
||||||
|
write!(self.data);
|
||||||
|
write!(self.hash);
|
||||||
|
write!(? self.enc_key);
|
||||||
|
write!(? self.sig);
|
||||||
|
|
||||||
|
Ok(w)
|
||||||
|
}
|
||||||
|
/// Consume into a syncronous writer
|
||||||
|
pub fn into_writer(self, mut writer: impl io::Write) -> eyre::Result<usize>
|
||||||
|
{
|
||||||
|
let mut w = 0;
|
||||||
|
macro_rules! write {
|
||||||
|
($b:expr) => {
|
||||||
|
w+=write_all(&mut writer, $b)?
|
||||||
|
};
|
||||||
|
(? $o:expr) => {
|
||||||
|
match $o {
|
||||||
|
Some(key) => {
|
||||||
|
write!([1]);
|
||||||
|
write!(key);
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
write!([0]);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(: $ser:expr) => {
|
||||||
|
{
|
||||||
|
let mut w2 = WriteCounter(0, &mut writer);
|
||||||
|
serde_cbor::to_writer(&mut w2, $ser)?;
|
||||||
|
w+=w2.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(: &self.header);
|
||||||
|
write!(self.data);
|
||||||
|
write!(self.hash);
|
||||||
|
write!(? self.enc_key);
|
||||||
|
write!(? self.sig);
|
||||||
|
|
||||||
|
Ok(w)
|
||||||
|
}
|
||||||
|
/// Consume into `Vec<u8>`.
|
||||||
|
pub fn into_bytes(self) -> Vec<u8>
|
||||||
|
{
|
||||||
|
let mut v = Vec::with_capacity(self.data.len()<<1);
|
||||||
|
self.into_writer(&mut v).expect("Failed to write to in-memory buffer");
|
||||||
|
v
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
//! Traits for serialising the message
|
||||||
|
use super::*;
|
||||||
|
use std::{
|
||||||
|
pin::Pin,
|
||||||
|
task::{
|
||||||
|
Context, Poll,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A type that can be used to serialise a message
|
||||||
|
pub trait MessageSender
|
||||||
|
{
|
||||||
|
#[inline] fn encrypt_key(&self, _key: &aes::AesKey) -> Option<[u8; RSA_BLOCK_SIZE]> { None }
|
||||||
|
#[inline] fn sign_data(&self, _data: &[u8]) -> Option<rsa::Signature> { None }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type that can be used to deserialise a message
|
||||||
|
pub trait MessageReceiver
|
||||||
|
{
|
||||||
|
#[inline] fn decrypt_key(&self, _enc_key: &[u8; RSA_BLOCK_SIZE]) -> Option<aes::AesKey>{ None }
|
||||||
|
#[inline] fn verify_data(&self, _data: &[u8], _sig: rsa::Signature) -> Option<bool> { None }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageSender for (){}
|
||||||
|
impl MessageReceiver for (){}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct WriteCounter<W:?Sized>(pub usize, pub W);
|
||||||
|
|
||||||
|
impl<W: ?Sized + io::Write> io::Write for WriteCounter<W>
|
||||||
|
{
|
||||||
|
#[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
let w = self.1.write(buf)?;
|
||||||
|
self.0 +=w;
|
||||||
|
Ok(w)
|
||||||
|
}
|
||||||
|
#[inline] fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.1.flush()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[pin_project]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(super) struct AsyncWriteCounter<W:?Sized>(pub usize, #[pin] pub W);
|
||||||
|
|
||||||
|
impl<W: ?Sized + AsyncWrite> AsyncWrite for AsyncWriteCounter<W>
|
||||||
|
{
|
||||||
|
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
self.project().1.poll_shutdown(cx)
|
||||||
|
}
|
||||||
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||||
|
self.project().1.poll_flush(cx)
|
||||||
|
}
|
||||||
|
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>> {
|
||||||
|
let this = self.project();
|
||||||
|
match this.1.poll_write(cx, buf) {
|
||||||
|
Poll::Ready(Ok(sz)) => {
|
||||||
|
*this.0 += sz;
|
||||||
|
Poll::Ready(Ok(sz))
|
||||||
|
},
|
||||||
|
x => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn write_all_async(mut to: impl AsyncWrite + Unpin, bytes: impl AsRef<[u8]>) -> io::Result<usize>
|
||||||
|
{
|
||||||
|
use tokio::prelude::*;
|
||||||
|
let bytes= bytes.as_ref();
|
||||||
|
to.write_all(bytes).await?;
|
||||||
|
Ok(bytes.len())
|
||||||
|
}
|
||||||
|
#[inline(always)] pub(super) fn write_all(mut to: impl io::Write, bytes: impl AsRef<[u8]>) -> io::Result<usize>
|
||||||
|
{
|
||||||
|
let bytes= bytes.as_ref();
|
||||||
|
to.write_all(bytes)?;
|
||||||
|
Ok(bytes.len())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in new issue