# How Code Phrase Establishment and PAKE Handshake Work in croc

> Learn how croc uses code phrase establishment and a PAKE handshake to secure file transfers with Argon2 and TCP authentication before data transmission.

- Repository: [Zack/croc](https://github.com/schollz/croc)
- Tags: internals
- Published: 2026-07-23

---

**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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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.

```go
// 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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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.

```go
// 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`](https://github.com/schollz/croc/blob/main/src/tcp/tcp.go) at line 550. The main event loop in [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/src/tcp/tcp.go)).
   - The receiver processes the message through the switch-case handler in [`src/croc/croc.go`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/src/croc/croc.go), and all subsequent file data is encrypted using the established AEAD instance.

```bash

# 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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/src/message/message.go) (line 16) to identify handshake packets.
- **TCP Implementation**: The client sends PAKE requests in [`src/tcp/tcp.go`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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.