How Code Phrase Establishment and PAKE Handshake Work in croc

The croc file transfer tool secures connections by deriving a symmetric key from a user-supplied code phrase using Argon2, then authenticating both parties via a PAKE handshake over TCP before any data is transmitted.

The schollz/croc repository implements a secure file transfer system that relies on code phrase establishment and a PAKE handshake to create encrypted channels through an untrusted relay. This article examines the actual source code to explain how user-supplied phrases become cryptographic keys and how the Password-Authenticated Key Exchange validates peer identity without exposing secrets to the intermediary server.

From Code Phrase to Cryptographic Key

When a user provides a code phrase (e.g., croc send -c "oak-dazzle-tulip"), the CLI parses this input in src/cli/cli.go (lines 656-659) by joining the phrase parts with hyphens. This string is then passed to the key derivation function in src/crypt/crypt.go (lines 79-94).

The NewArgon2 function implements a memory-hard key derivation using Argon2. It takes the code phrase and a random salt to generate a symmetric key, returning an cipher.AEAD interface stored in the connection state. This AEAD instance (aead) encrypts and decrypts all subsequent traffic, ensuring that the raw code phrase never traverses the network.

// Conceptual flow based on src/crypt/crypt.go
kdf := crypt.NewArgon2(phrase, salt)
aead, err := kdf.Key()
// aead is used for all message encryption

PAKE Handshake Implementation

After key derivation, both peers connect to the relay and perform a PAKE (Password-Authenticated Key Exchange) handshake to prove mutual knowledge of the derived key without transmitting it.

PAKE Message Structure

The message type is defined in src/message/message.go at line 16 as TypePAKE. This constant identifies packets that contain the handshake payload. Each PAKE message carries a fresh random nonce and encrypted data generated by the AEAD's Seal method.

Client PAKE Request

The initiator sends the first PAKE message in src/tcp/tcp.go at line 279. The code constructs a payload containing a random nonce and an encrypted handshake string (e.g., []byte("handshake")) sealed with the derived key. This establishes the initial secure communication request to the relay.

// Simplified representation of src/tcp/tcp.go:279
pakeMsg := message.New(message.TypePAKE, 
    aead.Seal(nil, nonce, []byte("handshake"), nil))
conn.Send(pakeMsg)

Server Response and Validation

The receiving peer obtains the PAKE connection in src/tcp/tcp.go at line 550. The main event loop in src/croc/croc.go processes incoming PAKE messages at lines 1801, 2118, and 2234-2248. The handler verifies the nonce, decrypts the payload using the same AEAD instance, and validates the handshake content. Upon successful verification, the connection state is promoted to authenticated (c.Authenticated = true).

Step-by-Step Authentication Flow

The complete process from user input to secure channel involves four distinct stages:

  1. Key Derivation: The CLI joins the code phrase and invokes NewArgon2 in src/crypt/crypt.go to generate the AEAD key. The salt is transmitted within the first PAKE payload so the receiver can derive an identical key.

  2. TCP Connection: Both sender and receiver open TCP connections to the relay server, preparing for message exchange.

  3. PAKE Exchange:

    • The sender emits a TypePAKE message with a random nonce and encrypted payload (line 279 in src/tcp/tcp.go).
    • The receiver processes the message through the switch-case handler in src/croc/croc.go (lines 2234-2248), decrypts the payload, and replies with its own encrypted PAKE payload.
  4. Channel Promotion: After successful mutual verification, the connection is marked authenticated in src/croc/croc.go, and all subsequent file data is encrypted using the established AEAD instance.


# Sender establishes the code phrase

croc send -c "correct-horse-battery-staple" file.tar.gz

# Receiver uses the identical phrase

croc receive -c "correct-horse-battery-staple"

Summary

  • Argon2 KDF: Located in src/crypt/crypt.go (lines 79-94), this function transforms the user-supplied code phrase into a symmetric AEAD key using a memory-hard hash function.
  • PAKE Message Type: Defined as TypePAKE in src/message/message.go (line 16) to identify handshake packets.
  • TCP Implementation: The client sends PAKE requests in src/tcp/tcp.go (line 279) and the server accepts them at line 550.
  • Handshake Logic: The main processing loop in src/croc/croc.go (lines 1801, 2118, 2234-2248) validates nonces and decrypts payloads to authenticate peers.
  • Security Property: The relay forwards only encrypted ciphertext and cannot derive the encryption key without the original code phrase.

Frequently Asked Questions

What is PAKE and why does croc use it?

PAKE (Password-Authenticated Key Exchange) is a cryptographic protocol that allows two parties to authenticate each other using a shared password without transmitting the password over the network. croc uses PAKE to ensure that only parties who know the exact code phrase can establish the encrypted channel, preventing man-in-the-middle attacks even when connecting through an untrusted relay.

How is the code phrase converted into an encryption key?

The conversion happens in src/crypt/crypt.go via the NewArgon2 function (lines 79-94). The function feeds the code phrase and a random salt into the Argon2id key derivation function, producing a high-entropy symmetric key that initializes an AEAD cipher used for all message encryption.

Can the relay server decrypt the transferred files?

No. The relay server only handles TCP connections and forwards opaque encrypted payloads. Because the relay never receives the code phrase or the derived AEAD key, it cannot decrypt PAKE handshake messages or subsequent file data. The encryption is end-to-end between the sender and receiver.

What happens if the code phrases don't match during the handshake?

If the code phrases differ, the derived keys will mismatch, causing the AEAD decryption in src/croc/croc.go (lines 2234-2248) to fail nonce verification. The PAKE handshake will not complete, the connection will not reach the Authenticated state, and the transfer will abort before any file data is transmitted.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →