Skip to content

Latest commit

 

History

History
171 lines (151 loc) · 7.66 KB

PROTOCOL.md

File metadata and controls

171 lines (151 loc) · 7.66 KB

Teleport Protocol Documentation

The Teleport protocol is a semi-simple protocol that can transfer a file from one location to another quickly, while also transmitting metadata. The protocol involves two endpoints, the client and the server. The server binds and listens for incoming connections on a TCP socket (default TCP port 9001). The client will connect to the server at the specified port and begin transferring the metadata and file data for each file in the command line arguments provided.

Protocol

Every packet in the protocol is wrapped in a TeleportHeader, which is defined as:

pub struct TeleportHeader {
    protocol: u64, // [ 'T', 'E', 'L', 'E', 'P', 'O', 'R', 'T' ]
    data_len: u32,
    pub action: TeleportAction, // as u8
    pub iv: Option<[u8; 12]>,
    pub data: Vec<u8>,
}

The protocol field is always TELEPORT. The data_len is the length of the data field in the packet, which is calculated by adding the data_len value with the length of the protocol, data_len, action fields, and optionally iv depending on the value in action (8 + 4 + 1 + 12 if iv.is_some()). The vector of data is deserialized based on what the value of action is. TeleportAction is defined here:

pub enum TeleportAction {
    Init = 0x01,
    InitAck = 0x02,
    Ecdh = 0x04,
    EcdhAck = 0x08,
    Data = 0x40,
    Encrypted = 0x80,
}

When encryption is enabled, the action field is OR'd with the Encrypted value, which is how the TeleportHeader deserialization knows if the iv field is present or not.

For standard unencrypted transfers, the protocol flows like this:

Client:                         Server:
TeleportAction::Init ==========>
        <====================== TeleportAction::InitAck
TeleportAction::Data ==========>
TeleportAction::Data ==========>
TeleportAction::Data ==========>
...

For encrypted transfers, the protocol flows like this:

Client:                                             Server:
TeleportAction::Ecdh ==============================>
        <========================================== TeleportAction::EcdhAck
TeleportAction::Init|TeleportAction::Encrypted ====>
        <========================================== TeleportAction::InitAck|TeleportAction::Encrypted
TeleportAction::Data|TeleportAction::Encrypted ====>
TeleportAction::Data|TeleportAction::Encrypted ====>
TeleportAction::Data|TeleportAction::Encrypted ====>
...

The Ecdh and EcdhAck action packets only contain the Client and Server ECDH public keys, respectively, in the TeleportHeader's data field. This allows Teleporter to do an ECDH key exchange and generate a secure secret key. This secret key is used to encrypt the rest of the connection, which will only last for 1 file transfer. Every file transfer renegotiates a new secret key. All the data in the TeleportHeader data field is encrypted, and the iv used is stored in the iv field.

The packet that initiates the transfer is the Init action packet, defined as follows:

// Client to server
pub struct TeleportInit {
    pub version: [u16; 3], // [ major, minor, patch ]
    pub features: TeleportFeatures, // as u32
    pub chmod: u32,
    pub filesize: u64,
    pub filename_len: u16,
    pub filename: Vec<char>,
}

The version value is whatever the current version of the client is, for example: [0, 4, 6]. The features value is a bitfield of any requested features that the client supports and would like the server to support. chmod is the current file permissions to be applied to the file when it is received on the server side. filesize is the size of the file to be transferred in bytes. The length of the filename is stored in filename_len, and the vector of characters of the filename is sent in filename.

The current feature set is:

pub enum TeleportFeatures {
    NewFile = 0x01,
    Delta = 0x02,
    Overwrite = 0x04,
    Backup = 0x08,
    Rename = 0x10,
}

NewFile is the minimum default feature that should be enabled on any transfer. The Delta enables delta file transfers by hashing the file into chunks and comparing the hash values to calculate the minimum data that needs to be sent to transfer the file. The Overwrite flag allows the Client to send a file and overwrite a file that already exists on the Server. The Backup flag tells the Server to make a backup of the file if it is being overwritten (saving it to $filename.bak). The Rename flag tells the server to save the new file transfer to $filename.1 instead of overwriting an existing file.

The TeleportInit file is responded to with a TeleportAck, which has the following properties:

pub struct TeleportInitAck {
    pub ack: TeleportInitStatus, // as u8
    pub version: [u16; 3],
    pub features: Option<u32>,
    pub delta: Option<TeleportDelta>,
}

The values of ack are of the enumerated type TeleportInitStatus as u8, which are described below. The version array is the current version of the server. Only the major and minor versions of the version array must match; the point release must not introduce protocol breaking changes. features is an optional field that is only present if ack == TeleportInitStatus::Proceed. The optional delta field is included last if the Delta flag is present in the features field and is described in detail after TeleportInitStatus.

pub enum TeleportInitStatus {
    Proceed,
    NoOverwrite,
    NoSpace,
    NoPermission,
    WrongVersion,
    EncryptionError,
    UnknownAction,
}

The value Proceed tells the client that it is ready to proceed with the file transfer. All the other values are specific error scenarios that cause the client to not proceed with the file transfer.

pub struct TeleportDelta {
    filesize: u64,
    hash: u64,
    chunk_size: u32,
    chunk_hash_len: u16,
    chunk_hash: Vec<u64>,
}

The TeleportDelta option is included when the client receives an Overwrite feature value. This struct is constructed to inform to the client what chunks need to be sent to the server to perform a delta file transfer. Delta file transfers can save valuable time by only transferring parts of the file that are different. The filesize value is sent to indicate what the size of the file that exists on the server is, to be compared with the file size on the client. The chunk_size relates how large blocks of data are to be used for the delta chunks, with a max chunk size of 4GB. hash is a xxHash3 hash value of the entire file on the server, and chunk_hash is a vector of xxHash3 hash values for each chunk of length chunk_size in the file. The xxHash3 hash values are 8 bytes in length and are stored as u64.

Once the server replies back to the client with a Proceed TeleportInitAck packet, the client will begin sending data. If the server sent an Overwrite feature back, then the client will read in chunk_size chunks and hash them with xxHash3. If the hash value matches for the vector value in chunk_hash then it will not send the chunk and will iterate to the next one. When a hash value does not match, indicating a chunk was changed, or when the server is not overwriting a file, the client will chunk up the file to be transferred and send it to the server using TeleportData structs, defined as:

pub struct TeleportData {
    offset: u64,
    length: u32,
    data: Vec<u8>,
}

The length value is the size of the data vector in bytes. The offset value is the location in the file to begin writing the chunk to. The data vector is a vector of unsigned bytes of data that are the file data.

Once the file is completely transferred the TCP connection is closed. If there is another file to transfer from the client, a new TCP connection is made.