# croc Protocol Specification: Technical Deep Dive into Secure File Transfer

> Explore the croc protocol specification for secure file transfer. Learn how this PAKE-authenticated, end-to-end encrypted TCP protocol safely exchanges files without exposing keys to relays.

- Repository: [Zack/croc](https://github.com/schollz/croc)
- Tags: deep-dive
- Published: 2026-07-27

---

**The croc protocol specification defines a lightweight, end-to-end encrypted file transfer protocol built on TCP that uses PAKE authentication and JSON-encoded messages to securely exchange files between peers without exposing encryption keys to intermediate relays.**

The croc protocol powers the popular command-line tool developed by **schollz/croc**, enabling secure cross-platform file transfers without port forwarding or centralized storage. This protocol specification outlines how the system establishes encrypted connections, negotiates keys, and streams file data through relays while maintaining zero-knowledge privacy guarantees.

## Protocol Architecture Overview

The croc protocol operates over a simple TCP relay architecture and consists of six distinct phases. Each phase builds upon the previous to create a secure, reliable transfer channel:

- **Connection Handshake** – Protocol version validation
- **PAKE Key Exchange** – Password-authenticated secret derivation
- **Secure Channel Establishment** – Encryption context initialization
- **Metadata Exchange** – File information negotiation
- **Data Transfer** – Chunked file transmission with flow control
- **Completion** – Graceful connection termination

## Connection and Handshake

When two peers connect to a relay, they immediately exchange a fixed validation payload to confirm protocol compatibility. According to the source code in [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go), the handshake transmits the byte slice `[]byte("handshake")` via the `handshakeRequest` mechanism.

This simple greeting serves two purposes: it confirms the remote peer is running croc-compatible software, and it validates that the TCP connection is stable before cryptographic operations begin. If the handshake fails or receives unexpected bytes, the connection aborts immediately.

## PAKE Key Exchange

After the handshake completes, peers initiate a **Password Authenticated Key Exchange (PAKE)** using the `schollz/pake` library. This phase utilizes the `TypePAKE` message constant defined in [`src/message/message.go`](https://github.com/schollz/croc/blob/main/src/message/message.go).

The PAKE algorithm allows both parties to derive an identical shared secret (`c.Key`) using only a pre-shared passphrase (the "code" in croc terminology). Critically, the protocol never transmits the passphrase or the derived key across the wire, preventing relay operators from intercepting encryption material even if they control the network path.

## Message Structure and Encoding

All protocol traffic travels inside the `Message` struct defined in [`src/message/message.go`](https://github.com/schollz/croc/blob/main/src/message/message.go). The structure uses JSON tags for serialization and supports both control messages and binary payloads:

```go
type Message struct {
    Type    Type   `json:"t,omitempty"` // Message type constant
    Message string `json:"m,omitempty"` // Optional text payload
    Bytes   []byte `json:"b,omitempty"` // Primary binary payload
    Bytes2  []byte `json:"b2,omitempty"`// Secondary payload (PAKE data)
    Num     int    `json:"n,omitempty"` // Numeric field (chunk indices)
}

```

The encoding pipeline follows a strict sequence in the `Encode` function. First, the struct undergoes JSON marshaling. Next, `compress.Compress` applies fastlz compression to the JSON bytes. Finally, if a PAKE-derived key exists (`c.Key != nil`), `crypt.Encrypt` encrypts the compressed payload using AES-GCM; otherwise, the data transmits in cleartext. The decoding process reverses these steps.

## Metadata Exchange

Before transferring file contents, the sender transmits file descriptors using `TypeFileInfo` messages. These payloads contain JSON-encoded metadata including filename, size, and hash values. The recipient acknowledges readiness using `TypeRecipientReady` messages.

This negotiation allows the receiver to validate available disk space and verify file integrity before accepting potentially gigabytes of data. The metadata exchange occurs over the encrypted channel established during PAKE, ensuring that file names and sizes remain confidential from the relay.

## Data Transfer and Flow Control

File contents flow as a sequence of chunks, with each chunk wrapped in a `Message` carrying the payload bytes and offset index. The protocol references chunk handling through the `Chunk` structure in [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go).

Rather than naive streaming, croc implements sliding-window flow control using `golang.org/x/time/rate` to prevent overwhelming slow receivers. Each chunk receives independent sequence numbering via the `Num` field, enabling out-of-order delivery handling and progress tracking during intermittent network conditions.

## Session Termination

Upon completing all chunk transmissions, the sender broadcasts a `TypeFinished` message defined in [`src/message/message.go`](https://github.com/schollz/croc/blob/main/src/message/message.go). Both parties then exchange `close-sender` and `close-recipient` messages before closing underlying TCP streams.

This graceful shutdown ensures that buffers flush completely and that both sides agree on transfer completion status before releasing resources. Unfinished transfers can resume from the last acknowledged chunk index due to the stateful nature of the protocol.

## Security Guarantees

The croc protocol specification provides three fundamental security properties:

- **End-to-end encryption** – The relay never learns the encryption key because PAKE exchange occurs directly between peers, and all subsequent traffic uses AES-GCM encryption implemented in [`src/crypt/crypt.go`](https://github.com/schollz/croc/blob/main/src/crypt/crypt.go).
- **Forward secrecy** – Each transfer generates a fresh PAKE exchange, producing a unique session key that cryptographically isolates individual transfer sessions.
- **Integrity verification** – The encryption layer includes HMAC authentication, preventing tampering with transferred data even if an attacker controls the relay infrastructure.

## Implementation Example

The following Go code demonstrates the protocol flow for custom implementations:

```go
// 1. Create a client (sender or receiver)
c := croc.NewClient(croc.Options{
    IsSender:     true,
    SharedSecret: "my-secret-phrase",
    RoomName:     "myroom",
})

// 2. Perform handshake and PAKE exchange automatically
if err := c.Start(); err != nil {
    log.Fatal(err)
}

// 3. Send file metadata
msg := message.Message{
    Type:  message.TypeFileInfo,
    Bytes: []byte(`{"n":"example.txt","s":12345}`),
}
message.Send(c.conn[0], c.Key, msg)

// 4. Stream data chunks
chunk := make([]byte, 64*1024)
n, _ := file.Read(chunk)
msg = message.Message{
    Type:  message.Type("data"),
    Bytes: chunk[:n],
    Num:   chunkIndex,
}
message.Send(c.conn[0], c.Key, msg)

// 5. Signal completion
message.Send(c.conn[0], c.Key, message.Message{Type: message.TypeFinished})

```

For standard usage, the CLI abstracts these details:

```bash

# Sender

croc send --code "my-secret-phrase" bigfile.zip

# Receiver

croc receive --code "my-secret-phrase"

```

## Summary

- The croc protocol specification defines a six-phase handshake that combines TCP reliability with PAKE-based key agreement.
- All messages use a standardized JSON struct that undergoes compression (`compress.Compress`) and conditional encryption (`crypt.Encrypt`) based on the derived session key.
- File transfers occur via sequenced chunks with sliding-window flow control to manage network congestion.
- End-to-end encryption ensures relays operate as blind data conduits, unable to decrypt or modify transferred content.
- Key implementation files include [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/src/croc/croc.go) for state management, [`src/message/message.go`](https://github.com/schollz/croc/blob/main/src/message/message.go) for encoding, and [`src/crypt/crypt.go`](https://github.com/schollz/croc/blob/main/src/crypt/crypt.go) for cryptographic primitives.

## Frequently Asked Questions

### What encryption algorithm does the croc protocol use?

The croc protocol uses AES-GCM for symmetric encryption of message payloads, implemented in [`src/crypt/crypt.go`](https://github.com/schollz/croc/blob/main/src/crypt/crypt.go). The encryption keys derive from a PAKE (Password Authenticated Key Exchange) operation that establishes a shared secret without transmitting it over the network, ensuring that only the two endpoints can decrypt the traffic.

### How does croc ensure the relay cannot access transferred files?

The protocol achieves zero-knowledge relay operation through end-to-end encryption. Because the PAKE exchange happens directly between peers and generates a session key never shared with the relay, the relay only sees encrypted byte streams. The relay cannot decrypt metadata (filenames, sizes) or file contents because it lacks the cryptographic key material generated during the handshake phase.

### What is the maximum file size supported by the croc protocol?

The protocol itself imposes no theoretical file size limits; it transfers files as a sequence of chunks referenced by integer indices in the `Num` field of the `Message` struct. Practical limits depend on available disk space and the maximum value of Go's `int` type on the target platform (typically 9 exabytes on 64-bit systems). The chunked approach allows resumption of interrupted transfers regardless of file size.

### Is the croc protocol specification open for third-party implementations?

Yes, the protocol is fully open and documented in the reference implementation at schollz/croc. Third-party implementations must adhere to the message format defined in [`src/message/message.go`](https://github.com/schollz/croc/blob/main/src/message/message.go), implement the PAKE handshake using compatible parameters, and follow the encoding sequence of JSON marshaling, fastlz compression, and AES-GCM encryption. The fixed handshake byte string `[]byte("handshake")` serves as the protocol version identifier for compatibility checking.