#文件传输 #文件加密 #chacha20-poly1305 #poly1305 #chacha20 #wormhole #spake2

portal-lib

一个安全的文件传输库,使用Rust编写。该库利用SPAKE2在非安全通道上协商密钥,并使用ChaCha20Poly1305认证加密来使用派生的共享对称密钥加密文件。这使得两个对等点可以在任何通道上传输文件,而无需信任中间代理。

4个版本 (重大变更)

0.5.0 2023年10月14日
0.4.0 2022年4月2日
0.3.0 2022年3月28日
0.2.0 2020年10月8日
0.1.0 2020年10月2日

#1160 in 加密学


2 crate 使用

Apache-2.0 OR MIT

75KB
1.5K SLoC

portal-lib

一个用于Portal的轻量级协议库 - 一个加密文件传输工具

此crate使消费者能够

  • 创建/序列化/反序列化Portal请求/响应消息。
  • 使用SPAKE2与对等方协商对称密钥
  • 使用Chacha20-Poly1305加密文件,可以使用RustCrypto实现或Ring的
  • 通过Portal中继发送/接收文件

该库分为两个抽象

  • 一个高级API,通过Portal结构体公开,以简化传输自动化
  • 一个低级API,通过Protocol结构体公开,如果您需要访问更底层的功能

高级API - 发送文件的示例

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(())
}

高级API - 接收文件的示例

use std::path::Path;
use std::error::Error;
use std::net::TcpStream;
use portal_lib::{Portal, Direction, TransferInfo};

fn my_recv() -> 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)?;

    // Optional: User callback to confirm/deny a transfer. If
    // none is provided, this will default accept the incoming file.
    // Return true to accept, false to reject the transfer.
    fn confirm_download(_info: &TransferInfo) -> bool { true }

    // Optional: implement a custom callback to display how much
    // has been transferred
    fn progress(transferred: usize) {
        println!("received {:?} bytes", transferred);
    }

    // Decide where downloads should go
    let my_downloads = Path::new("/tmp");

    // Receive every file in TransferInfo
    for metadata in portal.incoming(&mut stream, Some(confirm_download))? {
        portal.recv_file(&mut stream, my_downloads, Some(&metadata), Some(progress))?;
    }
    Ok(())
}

低级API - SPAKE2密钥协商示例

use spake2::{Ed25519Group, Identity, Password, Spake2};

// Securely receive/derive your id & password for this session
let channel_id = String::from("myid");
let password = String::from("mysecurepassword");

// Init a Spake2 context
let (state, outbound_msg) = Spake2::<Ed25519Group>::start_symmetric(
    &Password::new(&password.as_bytes()),
    &Identity::new(&channel_id.as_bytes()),
);

// Connect to the relay
let mut stream = TcpStream::connect("127.0.0.1:34254").unwrap();

// Send the connection message to the relay. If the relay cannot
// match us with a peer this will fail.
let confirm =
    Protocol::connect(&mut stream, &channel_id, Direction::Sender, outbound_msg).unwrap();

// Derive the shared session key
let key = Protocol::derive_key(state, &confirm).unwrap();

// confirm that the peer has the same key
Protocol::confirm_peer(&mut stream, &channel_id, Direction::Sender, &key)?;

您可以使用confirm_peer()方法来验证远程对等方是否已派生与您相同的密钥,只要通信流实现了std::io::Read和std::io::Write特性。

依赖项

~3–13MB
~174K SLoC