[][src]Crate loco_protocol

Loco protocol implemention

An opensource loco protocol implemention written in Rust. Provides command, secure layer and crypto used in networking.

Specification

Command

namesize
header(Header) 22 bytes
dataheader.data_size
namesize
id4 bytes
status2 bytes
name11 bytes
data_type1 bytes
data_size4 bytes

Secure data

namesize
header(Header) 20 bytes
encrypted dataheader.size - 16

Header

namesize
iv16 bytes
size4 bytes

Handshake

namesize
key size4 bytes
key_encrypt_type4 bytes
encrypt_type4 bytes
key256 bytes

Note: current implemention only supports RSA-AES

Networking

Command

Stream: Command #0 | Response command #0 | Broadcast command #1

Client -> Command #0
Server <- Command #0
Server -> Response command #0
Client <- Response command #0
Server -> Broadcast command #1
Client <- Broadcast command #1

Command is likely unordered. To match response and request, use header id.

Secure data

Stream: Handshake | Secure data #0 | Secure data #1

Client -> Handshake
Client -> Secure data #0
Server <- Secure data #0
Server -> Secure data #1
Client <- Secure data #1

Examples

Echo server

use std::{net::{TcpListener, TcpStream}, thread, io};
use openssl::rsa::Rsa;
use loco_protocol::{command::{Command, Error, Header, processor::CommandProcessor}, io::SecureClientStream, io::SecureServerStream, secure::CryptoStore};
 
let key = Rsa::generate(2048).expect("Cannot generate rsa key pairs.");
 
let socket = TcpListener::bind("127.0.0.1:5022").expect("Cannot bind tcp server");
socket.set_nonblocking(true).expect("Cannot set socket to unblocked mode");
 
let tcp = TcpStream::connect("127.0.0.1:5022").expect("Cannot connect tcp stream");
tcp.set_nonblocking(true).expect("Cannot set client stream to unblocked mode");
 
let mut client = CommandProcessor::new(
    SecureClientStream::new(
        CryptoStore::new().unwrap(),
        key.clone(),
        tcp
    )
);
 
let command = Command {
    header: Header {
        id: 0,
        status: 0,
        name: [72_u8, 101, 108, 108, 111, 0, 0, 0, 0, 0, 0],
        data_type: 0,
        data_size: 5
    },
     
    data: "Hello".as_bytes().into(),
};
 
client.write_command(command.clone()).expect("Failed to send command");
 
loop {
    match socket.accept() {
 
        Ok(connection) => {
            let key = key.clone();
            let command = command.clone();
 
            thread::spawn(move || {
                let mut server = CommandProcessor::new(
                    SecureServerStream::new(
                        CryptoStore::new().unwrap(),
                        key,
                        connection.0
                    )
                );
 
                println!("Connected from {}", connection.1);
                loop {
                    match server.read_command() {
 
                        Ok(readed) => {
                            if readed.is_some() {
                                let received = readed.unwrap();
                                assert_eq!(received, command);
 
                                server.write_command(received.clone()).expect("Command failed to write");
                                break;
                            }
                        }
 
                        Err(err) => panic!(format!("{}", err))
                    }
                }
            });
        }
 
        Err(err) if err.kind() == io::ErrorKind::WouldBlock => {  }
 
        Err(err) => panic!(format!("{}", err))
    }
 
    match client.read_command() {
 
        Ok(readed) => {
            match readed {
 
                Some(received) => {
                    assert_eq!(received, command.clone());
                    return;
                }
 
                None => {}
            }
        }
 
        Err(err) => {
            match err {
                Error::Io(io_err) if io_err.kind() == io::ErrorKind::WouldBlock => {}
                 
                _ => panic!(format!("{}", err))
            }
        }
    }
}

Modules

command
io
network
secure