Client handshake prototype

This commit is contained in:
Donald Knuth 2022-02-10 14:52:28 -05:00
parent eca6ddf3b8
commit 88a312981b
9 changed files with 398 additions and 45 deletions

261
Cargo.lock generated
View file

@ -2,6 +2,47 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "aead"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
dependencies = [
"generic-array",
]
[[package]]
name = "aes"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"opaque-debug",
]
[[package]]
name = "aes-gcm"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
dependencies = [
"aead",
"aes",
"cipher",
"ctr",
"ghash",
"subtle",
]
[[package]]
name = "anyhow"
version = "1.0.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
[[package]]
name = "atty"
version = "0.2.14"
@ -34,6 +75,21 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array",
]
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.1.0"
@ -49,6 +105,15 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cipher"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
dependencies = [
"generic-array",
]
[[package]]
name = "clap"
version = "3.0.14"
@ -79,6 +144,66 @@ dependencies = [
"syn",
]
[[package]]
name = "cpufeatures"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06"
dependencies = [
"generic-array",
]
[[package]]
name = "ctr"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
dependencies = [
"cipher",
]
[[package]]
name = "curve25519-dalek"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
dependencies = [
"byteorder",
"digest 0.9.0",
"rand_core",
"subtle",
"zeroize",
]
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array",
]
[[package]]
name = "digest"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "educe"
version = "0.4.18"
@ -194,6 +319,37 @@ dependencies = [
"slab",
]
[[package]]
name = "generic-array"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "ghash"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
dependencies = [
"opaque-debug",
"polyval",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
@ -215,6 +371,24 @@ dependencies = [
"libc",
]
[[package]]
name = "hkdf"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "158bc31e00a68e380286904cc598715f861f2b0ccf7aa6fe20c6d0c49ca5d0f6"
dependencies = [
"hmac",
]
[[package]]
name = "hmac"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddca131f3e7f2ce2df364b57949a9d47915cfbd35e46cfee355ccebbf794d6a2"
dependencies = [
"digest 0.10.2",
]
[[package]]
name = "indexmap"
version = "1.8.0"
@ -347,6 +521,12 @@ version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "os_str_bytes"
version = "6.0.0"
@ -413,6 +593,18 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "polyval"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
dependencies = [
"cfg-if",
"cpufeatures",
"opaque-debug",
"universal-hash",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
@ -455,6 +647,15 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.2.10"
@ -468,11 +669,14 @@ dependencies = [
name = "ruck"
version = "0.1.0"
dependencies = [
"aes-gcm",
"anyhow",
"bincode",
"bytes",
"clap",
"futures",
"serde",
"spake2",
"tokio",
"tokio-serde",
"tokio-stream",
@ -505,6 +709,17 @@ dependencies = [
"syn",
]
[[package]]
name = "sha2"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec"
dependencies = [
"cfg-if",
"cpufeatures",
"digest 0.10.2",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.0"
@ -526,12 +741,30 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "spake2"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7139ade210556eca57dfc299ec2454846ab6be09232eb1139a36e285ae7fd48e"
dependencies = [
"curve25519-dalek",
"hkdf",
"rand_core",
"sha2",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.86"
@ -630,18 +863,40 @@ dependencies = [
"tokio",
]
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "universal-hash"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
dependencies = [
"generic-array",
"subtle",
]
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "winapi"
version = "0.3.9"
@ -672,3 +927,9 @@ name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "zeroize"
version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c88870063c39ee00ec285a2f8d6a966e5b6fb2becc4e8dac77ed0d370ed6006"

View file

@ -4,11 +4,15 @@ version = "0.1.0"
edition = "2021"
[dependencies]
aes-gcm = "0.9.4"
anyhow = "1.0"
bytes = { version = "1", features = ["serde"] }
bincode = {version = "1.3.3" }
bincode = "1.3.3"
clap = { version = "3.0.14", features = ["derive"] }
futures = { version = "0.3.0", features = ["thread-pool"]}
serde = { version = "1.0", features = ["derive"] }
spake2 = "0.3.1"
tokio = { version = "1.16.1", features = ["full"] }
tokio-serde = { version = "~0.8", features = ["bincode"] }
tokio-stream = { version = "~0.1.6", features = ["net"]}

13
README.md Normal file
View file

@ -0,0 +1,13 @@
https://github.com/mcginty/snow/tree/master/examples
https://github.com/black-binary/snowstorm/tree/master/examples
https://cryptoservices.github.io/cryptography/protocols/2016/04/27/noise-protocol.html
https://github.com/libp2p
https://docs.rs/spake2/latest/spake2/
https://crates.io/crates/chacha20poly1305
https://briansmith.org/rustdoc/ring/aead/index.html
https://libsodium.gitbook.io/doc/secret-key_cryptography/secretstream
https://kerkour.com/rust-symmetric-encryption-aead-benchmark/
https://docs.rs/aes-gcm/latest/aes_gcm/

View file

@ -17,6 +17,7 @@ pub enum Commands {
Send {
#[clap(required = true, parse(from_os_str))]
paths: Vec<PathBuf>,
password: String,
},
#[clap(setting(AppSettings::ArgRequiredElseHelp))]
Receive {

View file

@ -1,31 +1,55 @@
use crate::message::Message;
use crate::crypto::handshake;
use crate::message::{Message, MessageStream};
use bytes::{BufMut, BytesMut};
use anyhow::Result;
use bytes::{BufMut, Bytes, BytesMut};
use futures::prelude::*;
use std::path::PathBuf;
use tokio::net::TcpStream;
use tokio_util::codec::{FramedWrite, LengthDelimitedCodec};
pub async fn send(paths: &Vec<PathBuf>) -> Result<(), Box<dyn std::error::Error>> {
// Delimit frames using a length header
let socket = TcpStream::connect("127.0.0.1:8080").await.unwrap();
let length_delimited = FramedWrite::new(socket, LengthDelimitedCodec::new());
let mut stream = tokio_serde::SymmetricallyFramed::new(
length_delimited,
tokio_serde::formats::SymmetricalBincode::<Message>::default(),
);
pub async fn send(file_paths: &Vec<PathBuf>, password: &String) -> Result<()> {
let socket = TcpStream::connect("127.0.0.1:8080").await?;
let mut stream = Message::to_stream(socket);
let (stream, key) = handshake(
&mut stream,
Bytes::from(password.to_string()),
Bytes::from("id123"),
)
.await?;
return upload_encrypted_files(stream, file_paths, key).await;
// Send the value
for path in paths.iter() {
let b = path.to_str().unwrap().as_bytes();
let mut buf = BytesMut::with_capacity(1024);
buf.put(&b[..]);
let body = buf.freeze();
let m = Message {
from_sender: true,
body: body,
};
stream.send(m).await.unwrap();
}
Ok(())
// for path in paths.iter() {
// let b = path.to_str().unwrap().as_bytes();
// let mut buf = BytesMut::with_capacity(1024);
// buf.put(&b[..]);
// let body = buf.freeze();
// let m = Message {
// key: "abc".to_string(),
// from_sender: true,
// body: body,
// };
// stream.send(m).await.unwrap();
// }
}
pub async fn receive(password: &String) -> Result<()> {
let socket = TcpStream::connect("127.0.0.1:8080").await?;
let mut stream = Message::to_stream(socket);
let (stream, key) = handshake(
&mut stream,
Bytes::from(password.to_string()),
Bytes::from("id123"),
)
.await?;
return Ok(());
}
pub async fn upload_encrypted_files(
stream: &mut MessageStream,
file_paths: &Vec<PathBuf>,
key: Bytes,
) -> Result<()> {
return Ok(());
}

36
src/crypto.rs Normal file
View file

@ -0,0 +1,36 @@
use crate::message::{HandshakeMessage, Message, MessageStream};
use anyhow::{anyhow, Result};
use bytes::Bytes;
use futures::prelude::*;
use spake2::{Ed25519Group, Identity, Password, Spake2};
pub async fn handshake(
stream: &mut MessageStream,
password: Bytes,
id: Bytes,
) -> Result<(&mut MessageStream, Bytes)> {
let (s1, outbound_msg) =
Spake2::<Ed25519Group>::start_symmetric(&Password::new(password), &Identity::new(&id));
stream
.send(Message::HandshakeMessage(HandshakeMessage {
id,
msg: Bytes::from(outbound_msg),
}))
.await?;
let first_message = match stream.next().await {
Some(Ok(msg)) => match msg {
Message::HandshakeMessage(response) => response.msg,
_ => return Err(anyhow!("Expecting handshake message response")),
},
_ => {
return Err(anyhow!("No response to handshake message"));
}
};
let key = match s1.finish(&first_message[..]) {
Ok(key_bytes) => key_bytes,
Err(e) => return Err(anyhow!(e.to_string())),
};
println!("Handshake successful. Key is {:?}", key);
return Ok((stream, Bytes::from(key)));
}

View file

@ -1,11 +1,12 @@
mod cli;
mod client;
mod crypto;
mod message;
mod server;
use clap::Parser;
use cli::{Cli, Commands};
use client::send;
use client::{receive, send};
use server::serve;
use std::error::Error;
@ -13,12 +14,13 @@ use std::error::Error;
async fn main() -> Result<(), Box<dyn Error>> {
let args = Cli::parse();
match &args.command {
Commands::Send { paths } => {
Commands::Send { paths, password } => {
println!("Sending {:?}", paths);
send(&paths).await?;
send(&paths, password).await?;
}
Commands::Receive { password } => {
println!("Receiving password {}", password);
receive(password).await?
}
Commands::Relay {} => {
serve().await?;

View file

@ -1,8 +1,31 @@
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use tokio::net::TcpStream;
use tokio_serde::{formats::SymmetricalBincode, SymmetricallyFramed};
use tokio_util::codec::{Framed, LengthDelimitedCodec};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Message {
pub from_sender: bool,
pub body: Bytes,
pub enum Message {
HandshakeMessage(HandshakeMessage),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct HandshakeMessage {
pub id: Bytes,
pub msg: Bytes,
}
impl Message {
pub fn to_stream(stream: TcpStream) -> MessageStream {
tokio_serde::SymmetricallyFramed::new(
Framed::new(stream, LengthDelimitedCodec::new()),
tokio_serde::formats::SymmetricalBincode::<Message>::default(),
)
}
}
pub type MessageStream = SymmetricallyFramed<
Framed<TcpStream, LengthDelimitedCodec>,
Message,
SymmetricalBincode<Message>,
>;

View file

@ -1,4 +1,4 @@
use crate::message::Message;
use crate::message::{Message, MessageStream};
use futures::prelude::*;
use std::collections::HashMap;
@ -7,18 +7,10 @@ use std::net::SocketAddr;
use std::sync::Arc;
use tokio::net::{TcpListener, TcpStream};
use tokio::sync::{mpsc, Mutex};
use tokio_serde::SymmetricallyFramed;
use tokio_util::codec::{Framed, LengthDelimitedCodec};
type Tx = mpsc::UnboundedSender<Message>;
type Rx = mpsc::UnboundedReceiver<Message>;
type MessageStream = SymmetricallyFramed<
Framed<TcpStream, LengthDelimitedCodec>,
Message,
tokio_serde::formats::SymmetricalBincode<Message>,
>;
pub struct Shared {
rooms: HashMap<String, RoomInfo>,
}
@ -90,11 +82,7 @@ pub async fn handle_connection(
socket: TcpStream,
addr: SocketAddr,
) -> Result<(), Box<dyn std::error::Error>> {
let length_delimited = Framed::new(socket, LengthDelimitedCodec::new());
let mut stream = tokio_serde::SymmetricallyFramed::new(
length_delimited,
tokio_serde::formats::SymmetricalBincode::<Message>::default(),
);
let mut stream = Message::to_stream(socket);
let first_message = match stream.next().await {
Some(Ok(msg)) => {
println!("first msg: {:?}", msg);
@ -105,12 +93,13 @@ pub async fn handle_connection(
return Ok(());
}
};
let mut client = Client::new(first_message.from_sender, state.clone(), stream).await?;
let mut client = Client::new(true, state.clone(), stream).await?;
// add client to state here
loop {
tokio::select! {
Some(msg) = client.rx.recv() => {
println!("message received to client.rx {:?}", msg);
client.messages.send(msg).await?
}
result = client.messages.next() => match result {
Some(Ok(msg)) => {