mirror of
https://github.com/CompeyDev/ruck.git
synced 2025-01-06 02:49:10 +00:00
Auto generate passwords; fix resume
This commit is contained in:
parent
09a67f9944
commit
5d2a216693
11 changed files with 101 additions and 40 deletions
37
Cargo.lock
generated
37
Cargo.lock
generated
|
@ -131,16 +131,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.0.14"
|
||||
version = "3.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b63edc3f163b3c71ec8aa23f9bd6070f77edbf3d1d198b164afa90ff00e4ec62"
|
||||
checksum = "8e538f9ee5aa3b3963f09a997035f883677966ed50fce0292611927ce6f6d8c6"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_lex",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"os_str_bytes",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap",
|
||||
|
@ -148,9 +148,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.0.14"
|
||||
version = "3.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a1132dc3944b31c20dd8b906b3a9f0a5d0243e092d59171414969657ac6aa85"
|
||||
checksum = "a7f98063cac4652f23ccda556b8d04347a7fc4b2cff1f7577cc8c6546e0d8078"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
|
@ -159,6 +159,15 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.1"
|
||||
|
@ -495,6 +504,16 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "names"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bddcd3bf5144b6392de80e04c347cd7fab2508f6df16a85fc496ecd5cec39bc"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"rand",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.6"
|
||||
|
@ -531,9 +550,6 @@ name = "os_str_bytes"
|
|||
version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
|
@ -702,6 +718,7 @@ dependencies = [
|
|||
"clap",
|
||||
"flate2",
|
||||
"futures",
|
||||
"names",
|
||||
"rand",
|
||||
"serde",
|
||||
"spake2",
|
||||
|
@ -813,9 +830,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.14.2"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0066c8d12af8b5acd21e00547c3797fde4e8677254a7ee429176ccebbe93dd80"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
name = "ruck"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
description = "A croc-inspired tool for hosting relay servers and sending e2e encrypted files."
|
||||
|
||||
[dependencies]
|
||||
|
||||
|
@ -13,6 +14,7 @@ bincode = "1.3.3"
|
|||
clap = { version = "3.0.14", features = ["derive"] }
|
||||
flate2 = "1.0"
|
||||
futures = { version = "0.3.0", features = ["thread-pool"]}
|
||||
names = "0.14.0"
|
||||
rand = "0.8.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
spake2 = "0.3.1"
|
||||
|
|
21
README.md
21
README.md
|
@ -4,15 +4,22 @@
|
|||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
## tab 1
|
||||
cargo run relay # this starts the server
|
||||
```
|
||||
ruck 0.1.0
|
||||
A croc-inspired tool for hosting relay servers and sending e2e encrypted files.
|
||||
|
||||
## tab 2
|
||||
cargo run send /path/to/file1.md /path/to/file2.md supersecretpassword
|
||||
USAGE:
|
||||
ruck <SUBCOMMAND>
|
||||
|
||||
## tab 3
|
||||
cargo run receive supersecretpassword
|
||||
OPTIONS:
|
||||
-h, --help Print help information
|
||||
-V, --version Print version information
|
||||
|
||||
SUBCOMMANDS:
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
receive Receive file(s). Must provide password shared out of band
|
||||
relay Start relay server
|
||||
send Send file(s). Can provide optional password
|
||||
```
|
||||
|
||||
## Protocol
|
||||
|
|
22
src/cli.rs
22
src/cli.rs
|
@ -1,27 +1,33 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use clap::{AppSettings, Parser, Subcommand};
|
||||
use clap::{Parser, Subcommand};
|
||||
|
||||
/// A fictional versioning CLI
|
||||
#[derive(Parser)]
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(name = "ruck")]
|
||||
#[clap(about = "Croc in rust", long_about = None)]
|
||||
#[clap(author, version, about, long_about = None)]
|
||||
pub struct Cli {
|
||||
#[clap(subcommand)]
|
||||
pub command: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Commands {
|
||||
#[clap(setting(AppSettings::ArgRequiredElseHelp))]
|
||||
/// Send file(s). Can provide optional password
|
||||
Send {
|
||||
#[clap(required = true, parse(from_os_str))]
|
||||
// Optional password to use, must be 16 characters at least. If none provided, one will be generated.
|
||||
#[clap(long, value_parser, required = false)]
|
||||
password: Option<String>,
|
||||
/// Paths to files to be sent.
|
||||
#[clap(value_parser, required = true)]
|
||||
paths: Vec<PathBuf>,
|
||||
password: String,
|
||||
},
|
||||
#[clap(setting(AppSettings::ArgRequiredElseHelp))]
|
||||
/// Receive file(s). Must provide password shared out of band
|
||||
Receive {
|
||||
/// Password shared out
|
||||
#[clap(value_parser, required = true)]
|
||||
password: String,
|
||||
},
|
||||
/// Start relay server
|
||||
Relay {},
|
||||
}
|
||||
|
|
|
@ -2,23 +2,29 @@ use crate::connection::Connection;
|
|||
use crate::file::{ChunkHeader, FileHandle, FileOffer, StdFileHandle};
|
||||
use crate::handshake::Handshake;
|
||||
use crate::message::{FileOfferPayload, FileRequestPayload, Message};
|
||||
use crate::password::validate_generate_pw;
|
||||
use crate::ui::prompt_user_for_file_confirmation;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
use std::path::PathBuf;
|
||||
use tokio::fs::File;
|
||||
use tokio::fs::{File, OpenOptions};
|
||||
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
pub async fn send(file_paths: &Vec<PathBuf>, password: &String) -> Result<()> {
|
||||
pub async fn send(file_paths: &Vec<PathBuf>, password: &Option<String>) -> Result<()> {
|
||||
// Fail early if there are problems generating file handles
|
||||
let handles = FileHandle::get_file_handles(file_paths).await?;
|
||||
|
||||
// Establish connection to server
|
||||
let socket = TcpStream::connect("127.0.0.1:8080").await?;
|
||||
|
||||
let (handshake, s1) = Handshake::from_password(password);
|
||||
let pw = validate_generate_pw(password.clone())?;
|
||||
println!(
|
||||
"Type `ruck receive {}` on other end to receive file(s).",
|
||||
pw
|
||||
);
|
||||
let (handshake, s1) = Handshake::from_password(&pw)?;
|
||||
// Complete handshake, returning key used for encryption
|
||||
let (socket, key) = handshake.negotiate(socket, s1).await?;
|
||||
|
||||
|
@ -38,7 +44,7 @@ pub async fn send(file_paths: &Vec<PathBuf>, password: &String) -> Result<()> {
|
|||
pub async fn receive(password: &String) -> Result<()> {
|
||||
// Establish connection to server
|
||||
let socket = TcpStream::connect("127.0.0.1:8080").await?;
|
||||
let (handshake, s1) = Handshake::from_password(password);
|
||||
let (handshake, s1) = Handshake::from_password(password)?;
|
||||
// Complete handshake, returning key used for encryption
|
||||
let (socket, key) = handshake.negotiate(socket, s1).await?;
|
||||
let mut connection = Connection::new(socket, key);
|
||||
|
@ -97,8 +103,7 @@ pub async fn create_or_find_files(desired_files: Vec<FileOffer>) -> Result<Vec<S
|
|||
let mut v = Vec::new();
|
||||
for desired_file in desired_files {
|
||||
let filename = desired_file.path;
|
||||
// filename.push_str(".part");
|
||||
let file = match File::open(filename.clone()).await {
|
||||
let file = match OpenOptions::new().append(true).open(&filename).await {
|
||||
Ok(file) => {
|
||||
println!(
|
||||
"File {:?} already exists. Attempting to resume download.",
|
||||
|
|
|
@ -3,3 +3,4 @@ pub const HANDSHAKE_MSG_SIZE: usize = 33; // generated by Spake2
|
|||
pub const PER_CLIENT_BUFFER: usize = 1024 * 64; // buffer size allocated by server for each client
|
||||
pub const BUFFER_SIZE: usize = 1024 * 64; // chunk size for files sent over wire
|
||||
pub const NONCE_SIZE: usize = 96 / 8; // used for every encrypted message
|
||||
pub const PASSWORD_LEN: usize = 12; // min size of password
|
||||
|
|
|
@ -81,7 +81,7 @@ impl Connection {
|
|||
let elapsed = before.elapsed();
|
||||
let mb_sent = bytes_sent / 1_048_576;
|
||||
println!(
|
||||
"{:?}: {:?} mb sent (compressed), {:?} iterations. {:?} total time, {:?} avg per iteration, {:?} avg mb/sec",
|
||||
"{:?}: {:?} mb sent (compressed), {:?} messages. {:?} total time, {:?} avg per msg, {:?} avg mb/sec",
|
||||
handle.name,
|
||||
mb_sent,
|
||||
count,
|
||||
|
|
|
@ -82,7 +82,7 @@ impl FileHandle {
|
|||
};
|
||||
}
|
||||
None => {
|
||||
println!("Skipping file b/c not in requested chunks");
|
||||
println!("Skipping {:?} b/c not in requested chunks.", handle.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ pub struct Handshake {
|
|||
}
|
||||
|
||||
impl Handshake {
|
||||
pub fn from_password(pw: &String) -> (Handshake, spake2::Spake2<spake2::Ed25519Group>) {
|
||||
let password = Bytes::from(pw.to_string());
|
||||
pub fn from_password(pw: &String) -> Result<(Handshake, spake2::Spake2<spake2::Ed25519Group>)> {
|
||||
let password = Bytes::from(pw.clone());
|
||||
let id = Handshake::pass_to_bytes(&pw);
|
||||
let (s1, outbound_msg) =
|
||||
Spake2::<Ed25519Group>::start_symmetric(&Password::new(&password), &Identity::new(&id));
|
||||
|
@ -22,7 +22,7 @@ impl Handshake {
|
|||
buffer.extend_from_slice(&outbound_msg[..HANDSHAKE_MSG_SIZE]);
|
||||
let outbound_msg = buffer.freeze();
|
||||
let handshake = Handshake { id, outbound_msg };
|
||||
(handshake, s1)
|
||||
Ok((handshake, s1))
|
||||
}
|
||||
|
||||
pub async fn from_socket(socket: TcpStream) -> Result<(Handshake, TcpStream)> {
|
||||
|
@ -64,9 +64,7 @@ impl Handshake {
|
|||
}
|
||||
|
||||
fn pass_to_bytes(password: &String) -> Bytes {
|
||||
let mut hasher = Blake2s256::new();
|
||||
hasher.update(password.as_bytes());
|
||||
let res = hasher.finalize();
|
||||
BytesMut::from(&res[..]).freeze()
|
||||
let bytes = Blake2s256::digest(password.as_bytes());
|
||||
BytesMut::from(&bytes[..]).freeze()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ mod crypto;
|
|||
mod file;
|
||||
mod handshake;
|
||||
mod message;
|
||||
mod password;
|
||||
mod server;
|
||||
mod ui;
|
||||
|
||||
|
|
24
src/password.rs
Normal file
24
src/password.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
use crate::conf::PASSWORD_LEN;
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use names::{Generator, Name};
|
||||
|
||||
pub fn validate_generate_pw(pw: Option<String>) -> Result<String> {
|
||||
let pass = pw.unwrap_or_else(generate_random_password);
|
||||
match validate_pw(&pass) {
|
||||
true => Ok(pass),
|
||||
false => Err(anyhow!("Password too short.")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate_pw(pw: &String) -> bool {
|
||||
PASSWORD_LEN <= pw.len()
|
||||
}
|
||||
|
||||
pub fn generate_random_password() -> String {
|
||||
let mut generator = Generator::with_naming(Name::Numbered);
|
||||
match generator.next() {
|
||||
Some(s) if PASSWORD_LEN <= s.len() => s,
|
||||
_ => generate_random_password(),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue