Portal is an encrypted file transfer utility, written in Rust with an accompanying protocol library and relay.
Features
- Transfer files to any peer as long as both can connect to the same portal relay
- Optional Ring backend for Chacha20-Poly1305 (default is RustCrypto)
- Library to programatically interact via the portal protocol
- Linux, MacOS, and Windows client support
CLI Application
Easily transfer multiple files or directories via CLI:
portal send /etc/passwd ./files/
Your peer will enter an equivalent command on the other side:
portal recv
Note: The default relay is portal-relay.landhb.dev
. Your peer must connect to the same portal-relay as you. You can also host your own relay and change the value to any domain/IP address in your config.
Performance & Benchmarks
Currently only the performance of Portal::send_file
and Portal::recv_file
are tracked automatically for various file sizes. The benchmarks for the master
branch can be found here.
In the future we’d like to compare the RustCrypto Chacha20-Poly1305 implementation versus Ring’s implementation. Initial tests seemed to show Ring’s may be faster. Currently a significant amount of time is spent encrypting/decrypting, as shown below in the flamegraph collected from sending a 366MB file (clicking on the svg will allow you to interactively explore it):
Library
The library crate enables a user to programatically:
- Create/serialize/deserialize Portal request/response messages.
- Negoticate a symmetric key with a peer using SPAKE2
- Encrypt files with Chacha20-Poly1305 using either the RustCrypto implementation or Ring’s
- Send/receive files through a Portal relay
The library is broken up into two abstractions:
Portal
a higher level API, to facilitate automating transfers easilyProtocol
a lower-level API, if you need access to lower-level facilities
Example of sending a file with the higher-level abstraction:
use std::path::Path;
use std::error::Error;
use std::net::TcpStream;
use portal_lib::{Portal, Direction, TransferInfoBuilder};
fn my_send() -> Result<(), Box<dyn Error>> {
// Securely generate/exchange ID & Password with peer out-of-band
let id = String::from("id");
let password = String::from("password");
// Connect to the relay - the ID will be used to connect the peers
let mut portal = Portal::init(Direction::Sender, id, password)?;
let mut stream = TcpStream::connect("127.0.0.1:34254")?;
// The handshake must be performed first, otherwise
// there is no shared key to encrypt the file with
portal.handshake(&mut stream)?;
// Add any files/directories
let info = TransferInfoBuilder::new()
.add_file(Path::new("/etc/passwd"))?
.finalize();
// Optional: implement a custom callback to display how much
// has been transferred
fn progress(transferred: usize) {
println!("sent {:?} bytes", transferred);
}
// Send every file in TransferInfo
for (fullpath, metadata) in portal.outgoing(&mut stream, &info)? {
portal.send_file(&mut stream, fullpath, Some(progress))?;
}
Ok(())
}