PAKE Initialization Details in src/croc/croc.go: How croc Bootstraps Encryption
The croc client initializes Password-Authenticated Key Exchange (PAKE) through three distinct code paths in src/croc/croc.go, using role-specific curve initialization with sliced shared secrets to derive symmetric session keys before any file data traverses the network.
The croc secure file transfer tool (github.com/schollz/croc) leverages the github.com/schollz/pake/v3 library to establish encrypted channels between sender and receiver without pre-shared certificates. Examining the PAKE initialization details in src/croc/croc.go reveals how the tool transforms a human-readable code into a cryptographically secure session key that protects data even against compromised relay servers.
PAKE Initialization Flow in src/croc/croc.go
The croc client performs PAKE initialization in three specific scenarios within src/croc/croc.go. Each invocation uses pake.InitCurve with distinct role parameters to establish the cryptographic handshake.
Receiver Initialization (Role 0)
When a client operates as a receiver (c.Options.IsSender == false), the constructor initializes PAKE with role 0. This occurs in the New function where the client trims the first five characters from the shared secret and passes the remainder to the PAKE library.
// New establishes the client configuration
func New(ops Options) (*Client, error) {
// ...
if !c.Options.IsSender {
c.Pake, err = pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 0, c.Options.Curve)
}
// ...
}
This initialization happens at lines 128-132 of src/croc/croc.go. The role flag 0 designates this instance as the receiver in the key exchange protocol.
Sender Handshake Initialization (Role 1)
During the sender's handshake phase, the senderWaitForHandshake function creates a temporary PAKE instance with role 1 to initiate the cryptographic exchange. The sender uses the same secret slicing logic as the receiver.
func (c *Client) senderWaitForHandshake(conn *comm.Comm) error {
// ...
B, err := pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 1, c.Options.Curve)
// ...
}
Located at lines 144-148, this code path executes when the sender connects to the relay and prepares to transmit PAKE bytes to the receiver.
Reconnection Recovery
If a transfer interruption triggers a reconnect attempt, the resetForReconnectAttempt function re-initializes the PAKE instance with role 0 to restart the key exchange fresh.
c.Pake, err = pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 0, c.Options.Curve)
This re-initialization appears at lines 546-550, ensuring that resumed transfers generate new session keys rather than reusing potentially compromised previous states.
Technical Implementation Details
The PAKE initialization relies on several critical implementation choices that ensure cryptographic security across different network conditions.
Secret Slicing and Code Extraction
The first five characters of the user-provided secret represent the connection code used for relay identification. The actual cryptographic entropy comes from c.Options.SharedSecret[5:], which passes to pake.InitCurve as the password material. This separation ensures that the human-visible code does not directly serve as the PAKE password, adding a layer of abstraction between the relay routing identifier and the encryption key material.
Curve Selection and Role Flags
The third parameter to pake.InitCurve specifies the elliptic curve (e.g., "ed25519", "p256", "p521"), determined by c.Options.Curve. The second parameter defines the role flag:
- 0: Receiver (waiter) role
- 1: Sender (initiator) role
This distinction ensures that both parties generate complementary key shares that combine into a single shared secret, preventing role confusion attacks.
Session Key Derivation
After the initial PAKE message exchange, both client instances call SessionKey() to derive the symmetric encryption key. The croc implementation stores this as kA (sender) or kB (receiver), subsequently using it via the crypt package to encrypt control messages such as IP address requests and file metadata before the actual payload transfer begins.
Summary
- Three initialization points:
src/croc/croc.goinitializes PAKE in theNewconstructor for receivers (lines 128-132), insenderWaitForHandshakefor senders (lines 144-148), and inresetForReconnectAttemptfor reconnections (lines 546-550). - Role-based differentiation: Receivers use role
0while senders use role1in thepake.InitCurvecall. - Secret trimming: The implementation slices
SharedSecret[5:]to exclude the five-character connection code from the cryptographic material. - Curve flexibility: The curve parameter supports multiple elliptic curves based on user configuration via
c.Options.Curve. - Secure bootstrapping: PAKE establishes encryption keys before any file data transmission, ensuring confidentiality even if the relay server is compromised.
Frequently Asked Questions
How does croc derive the encryption key from the shared secret?
The croc client extracts the password material by removing the first five characters from the shared secret using c.Options.SharedSecret[5:]. This sliced byte array feeds into pake.InitCurve along with the role flag and curve type. After the PAKE handshake completes, both parties call SessionKey() to generate the symmetric key used for encrypting subsequent communications.
What is the difference between role 0 and role 1 in croc's PAKE implementation?
Role 0 designates the receiver (the client waiting to accept files), initialized in the New function and during reconnections. Role 1 designates the sender (the client transmitting files), initialized in senderWaitForHandshake. These role flags ensure the PAKE library generates complementary key shares that properly combine into a shared session key.
Which elliptic curves does croc support for PAKE?
According to the source code in src/croc/croc.go, croc supports multiple curves including ed25519, p256, p384, and p521, passed through c.Options.Curve to pake.InitCurve. The default curve depends on the croc version and build configuration, but users can specify alternatives via the --curve command-line flag.
Why does croc re-initialize PAKE during reconnection attempts?
The resetForReconnectAttempt function re-initializes the PAKE instance to ensure that interrupted transfers generate fresh session keys rather than reusing previous cryptographic states. This prevents potential key reuse vulnerabilities and ensures that each connection attempt establishes independent encryption contexts, maintaining forward secrecy across multiple connection attempts.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →