Encryption Utilities in the croc crypt.go Package: AES-GCM and ChaCha20-Poly1305
The crypt.go package provides two families of authenticated encryption utilities: PBKDF2-derived AES-GCM via the New, Encrypt, and Decrypt functions, and Argon2id-derived ChaCha20-Poly1305 via NewArgon2, EncryptChaCha, and DecryptChaCha.
The schollz/croc file transfer tool relies on symmetric encryption to secure payload data and metadata in transit. The encryption utilities provided by the crypt.go package wrap industry-standard algorithms in a minimal API located at src/crypt/crypt.go, exposing only six core functions that handle key derivation, nonce generation, and authenticated encryption automatically.
PBKDF2-Derived AES-GCM Encryption
The first encryption family combines password-based key derivation with AES-GCM authenticated encryption. According to the source code in src/crypt/crypt.go (lines 16-74), this implementation is optimized for general-purpose use where hardware-accelerated AES is available.
Key Derivation with New()
The New() function generates a 256-bit encryption key from a user-provided passphrase and optional salt. When the usersalt parameter is nil, the function generates a random 8-byte salt. It then derives the key using pbkdf2.Key(passphrase, salt, 100, 32, sha256.New), which applies 100 iterations of PBKDF2 with SHA-256 to produce a 32-byte key suitable for AES-256.
Authenticated Encryption with Encrypt() and Decrypt()
The Encrypt() function implements AES-256-GCM with automatic nonce handling. As defined in src/crypt/crypt.go, the function generates a random 12-byte IV using rand.Read, constructs an AES cipher via aes.NewCipher(key), and wraps it in a GCM AEAD using cipher.NewGCM. The plaintext is sealed with Seal, and the 12-byte IV is prepended to the ciphertext output.
The Decrypt() function splits the IV from the ciphertext and calls Open to verify authenticity and recover the plaintext. It returns an error for tampered ciphertexts or incorrect keys.
Argon2-Derived ChaCha20-Poly1305 Encryption
For scenarios requiring memory-hard key derivation resistant to GPU cracking, the package provides a second family using Argon2id and XChaCha20-Poly1305. The implementation in src/crypt/crypt.go (lines 76-125) exposes this through the NewArgon2, EncryptChaCha, and DecryptChaCha functions.
Memory-Hard Key Derivation with NewArgon2()
The NewArgon2() function creates a cipher.AEAD interface directly rather than returning a raw key byte slice. The function generates a random 8-byte salt if none is provided, then derives a 32-byte key using argon2.IDKey(passphrase, salt, 1, 64*1024, 4, 32). This configures Argon2id with 1 iteration, 64 MB of memory, and 4 parallel threads. The resulting key initializes an XChaCha20-Poly1305 instance via chacha20poly1305.NewX().
Stream Cipher Operations with EncryptChaCha() and DecryptChaCha()
The EncryptChaCha() function allocates a random nonce of length aead.NonceSize() and calls aead.Seal to produce authenticated ciphertext, prepending the nonce to the output. The DecryptChaCha() function extracts this nonce and invokes Open to validate and decrypt. Both functions return errors on malformed input or authentication failures.
Practical Example: Using croc's Encryption Utilities
The following example demonstrates both encryption families using the actual API from src/crypt/crypt.go:
package main
import (
"fmt"
"log"
"github.com/schollz/croc/src/crypt"
)
func main() {
// PBKDF2 + AES-GCM
pass := []byte("my-strong-passphrase")
key, salt, err := crypt.New(pass, nil) // generates random salt
if err != nil {
log.Fatalf("key generation failed: %v", err)
}
plain := []byte("secret payload")
ciphertext, err := crypt.Encrypt(plain, key)
if err != nil {
log.Fatalf("encryption failed: %v", err)
}
decrypted, err := crypt.Decrypt(ciphertext, key)
if err != nil {
log.Fatalf("decryption failed: %v", err)
}
fmt.Printf("AES-GCM: %s (salt %x)\n", decrypted, salt)
// Argon2id + ChaCha20-Poly1305
aead, salt2, err := crypt.NewArgon2(pass, nil)
if err != nil {
log.Fatalf("argon2 generation failed: %v", err)
}
cipher2, err := crypt.EncryptChaCha(plain, aead)
if err != nil {
log.Fatalf("ChaCha encrypt failed: %v", err)
}
plain2, err := crypt.DecryptChaCha(cipher2, aead)
if err != nil {
log.Fatalf("ChaCha decrypt failed: %v", err)
}
fmt.Printf("ChaCha20-Poly1305: %s (salt %x)\n", plain2, salt2)
}
Both code paths are verified in src/crypt/crypt_test.go, which validates round-trip integrity and error handling for corrupted data.
Summary
- The
crypt.gopackage inschollz/crocprovides two distinct authenticated encryption utilities: PBKDF2-derived AES-GCM and Argon2id-derived ChaCha20-Poly1305. - PBKDF2 uses 100 iterations of SHA-256 with an 8-byte salt, while Argon2id uses memory-hard parameters (64 MB, 4 threads) to resist GPU attacks.
- Six core functions—
New,Encrypt,Decrypt,NewArgon2,EncryptChaCha, andDecryptChaCha—handle all key derivation, nonce management, and authenticated encryption operations. - All functions automatically prepend nonces/IVs to ciphertext and are thoroughly tested in
src/crypt/crypt_test.go.
Frequently Asked Questions
What encryption algorithms does the croc crypt.go package implement?
The package implements AES-256-GCM for block encryption and XChaCha20-Poly1305 for stream encryption. Both are authenticated encryption with associated data (AEAD) constructions that verify data integrity during decryption, preventing tampering and corruption.
How does croc derive encryption keys from user passphrases?
The package derives keys using either PBKDF2 with 100 iterations of SHA-256 or Argon2id with memory-hard settings (64 MB of memory, 4 threads). Both methods generate random 8-byte salts to ensure unique keys even when users reuse passwords across different file transfers.
What is the difference between New() and NewArgon2()?
New() returns a raw 32-byte key slice derived via PBKDF2 for use with AES-GCM, while NewArgon2() returns a cipher.AEAD interface initialized with XChaCha20-Poly1305. The Argon2 variant provides better resistance against hardware-accelerated brute force attacks due to its memory-hard computation requirements.
Where are the encryption utilities tested in the croc repository?
All encryption functions are exercised in src/crypt/crypt_test.go, which contains unit tests verifying correct encryption/decryption round-trips, proper error handling for corrupted ciphertexts, and benchmarks measuring the relative performance of PBKDF2 versus Argon2 key derivation.
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 →