# Encryption Utilities in the croc crypt.go Package: AES-GCM and ChaCha20-Poly1305

> Explore AES-GCM and ChaCha20-Poly1305 encryption utilities in the croc crypt.go package. Secure your data with these powerful Go functions.

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

---

**The [`crypt.go`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/crypt.go) package** wrap industry-standard algorithms in a minimal API located at [`src/crypt/crypt.go`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/src/crypt/crypt.go):

```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`](https://github.com/schollz/croc/blob/main/src/crypt/crypt_test.go), which validates round-trip integrity and error handling for corrupted data.

## Summary

- The [`crypt.go`](https://github.com/schollz/croc/blob/main/crypt.go) package in `schollz/croc` provides 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`, and `DecryptChaCha`—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`](https://github.com/schollz/croc/blob/main/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`](https://github.com/schollz/croc/blob/main/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.