How croc Implements Password-Authenticated Key Agreement (PAKE)

croc uses the external library github.com/schollz/pake/v3 to perform Password-Authenticated Key Agreement (PAKE), deriving a strong symmetric session key from a shared code phrase without ever transmitting the password over the network.

The croc file transfer tool establishes end-to-end encrypted connections using PAKE to bootstrap secure communication from a short, human-readable code phrase. This eliminates the need for public-key infrastructure while cryptographically binding the session key to the shared secret. The implementation spans the core transfer logic, relay server handshakes, and WebAssembly bindings for browser-based transfers.

Core PAKE Components in croc

The PAKE implementation is distributed across three architectural layers, each initializing the protocol with role-specific parameters and elliptic curve configurations.

croc Core Logic (src/croc/croc.go)

The main transfer engine initializes PAKE instances differently for senders and receivers using the pake.InitCurve function.

For the sender (role 0), initialization occurs at line 292:

c.Pake, err = pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 0, c.Options.Curve)

For the receiver (role 1), the call at line 2099 swaps the role parameter:

c.Pake, err = pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 1, c.Options.Curve)

Both parties strip the first five characters from the code phrase (c.Options.SharedSecret[5:]) to derive the actual password used in the cryptographic exchange.

TCP Relay Handshake (src/tcp/tcp.go)

When communicating with the relay server, croc performs a lightweight PAKE exchange to protect relay-side traffic. The implementation uses the "siec" curve for these connections.

The sender initializes PAKE at line 551:

A, err := pake.InitCurve(weakKey, 0, "siec")

The receiver initializes at line 280 with role 1:

B, err := pake.InitCurve(weakKey, 1, "siec")

WebAssembly Bridge (web/wasm/main.go)

Browser-based transfers expose PAKE functions through WebAssembly bindings. Line 107 handles initialization:

instance, err := pake.InitCurve(password, args[1].Int(), args[2].String())

Line 125 processes incoming peer bytes:

pakeError := instance.Update(peerBytes)

The PAKE Handshake Flow

The protocol execution follows a strict sequence to establish the shared session key:

  1. Derive the password
    croc extracts the cryptographic secret by slicing the user-provided code phrase (sharedSecret[5:]), keeping the displayed phrase separate from the actual key material.

  2. Initialize PAKE objects
    Each party creates an instance using pake.InitCurve(password, role, curve). The sender uses role 0, the receiver uses role 1. This generates public bytes accessible via the Bytes field.

  3. Exchange public payloads
    The sender transmits a Message of type TypePAKE containing its public bytes unencrypted. The receiver calls Update(peerBytes) to incorporate these bytes and generates its own public response.

  4. Derive the session key
    After the two-way exchange, both parties invoke SessionKey() on their PAKE instance. This computes identical symmetric keys (kA on the sender, kB on the receiver) bound to the original password.

  5. Encrypt subsequent traffic
    The derived key is stored in c.Key and used by the crypt package to encrypt all following messages. Only TypePAKE messages remain unencrypted, as defined in src/message/message.go.

Security Benefits of PAKE in croc

Password-only authentication allows users to secure transfers with just a memorable code phrase, eliminating certificate management or key distribution.

Forward secrecy is maintained because the session key derives from an elliptic-curve Diffie-Hellman exchange bound to the password. Compromising the code phrase later does not expose past session data.

Relay-agnostic security ensures end-to-end confidentiality even when traffic routes through untrusted relay servers, as both direct and relay channels execute identical PAKE handshakes.

Summary

  • croc delegates PAKE operations to the github.com/schollz/pake/v3 library, calling InitCurve with role-specific parameters (0 for sender, 1 for receiver).
  • The cryptographic password is derived from the user code phrase by removing the first five characters ([5:]).
  • Initialization occurs in src/croc/croc.go (lines 292 and 2099), src/tcp/tcp.go (lines 551 and 280), and web/wasm/main.go.
  • Public bytes are exchanged via unencrypted TypePAKE messages, followed by Update calls to compute the shared secret.
  • The resulting session key from SessionKey() encrypts all subsequent file metadata and chunk transfers via the crypt package.

Frequently Asked Questions

What Go library does croc use for PAKE implementation?

croc imports github.com/schollz/pake/v3 as defined in the go.mod file. This library provides the InitCurve, Update, and SessionKey functions used throughout the codebase to perform elliptic-curve-based password-authenticated key exchange.

How do the sender and receiver roles differ in croc's PAKE implementation?

The sender initializes PAKE with role 0 (pake.InitCurve(password, 0, curve)), while the receiver uses role 1 (pake.InitCurve(password, 1, curve)). This role distinction determines the mathematical operations each party performs during the key agreement protocol, ensuring they compute the same shared secret through complementary operations.

Is the code phrase ever transmitted over the network during the PAKE handshake?

No. The code phrase itself never leaves the device. croc derives the cryptographic key from the phrase (skipping the first five characters) and only transmits the PAKE public bytes, which are mathematically related to the password but reveal no information about it to eavesdroppers.

What elliptic curves does croc support for PAKE?

croc supports configurable curves via c.Options.Curve, commonly using p256 for peer-to-peer connections. When communicating with relay servers, croc specifically uses the siec curve (Simplified Isochronous Elliptic Curve) as hardcoded in src/tcp/tcp.go.

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 →