# Cryptek

A command-line file encryption tool using AES-256-GCM authenticated encryption, written in Go.

## Features

- **AES-256-GCM** authenticated encryption with cryptographically secure random nonce
- **Argon2id key derivation** (default) - resistant to GPU/ASIC attacks
- **PBKDF2-SHA512** alternative for compatibility (600,000 iterations, OWASP 2024)
- **Filename hiding** - original filename stored in encrypted header (`document.pdf` → `document.ctk`)
- **Anti-fingerprinting** - variable-length encrypted headers (64-512 bytes) prevent file type identification
- **`--hide-name` flag** - generate random output filename (no extension) for maximum privacy
- **`--hide-file` flag** - hide encrypted output from directory listings (dot prefix)
- **`--stealth` flag** - maximum concealment: random filename + hidden (combines both)
- **Folder encryption** - encrypt entire directories as tar archives
- **Keyfile support** - use files as key material (alone or combined with password)
- **Secure deletion** (`--secure-delete`) - overwrites files before removal (HDD: 3-pass, SSD: re-encrypt)
- **RAM decryption** (`--tmpfs`) - decrypt to RAM-based filesystem, data never touches disk (Linux/macOS)
- **Wildcard/glob support** for batch processing (`*.txt`, `**/*.doc`)
- **Progress bar** for batch operations
- **Graceful cancellation** - Ctrl+C stops batch operations cleanly
- Single static binary - no runtime dependencies
- Cross-platform: Linux, macOS, Windows (amd64/arm64)
- Secure password input (`--stdin` flag - not visible in process list)
- Environment variable support for password management
- 21 hash algorithms for file integrity verification (SHA-2, SHA-3, BLAKE2, BLAKE3, TIGER, etc.)
- Simple, intuitive CLI interface

## Installation

### From Binary

Download the latest release for your platform from the [Releases](https://github.com/hecperez/cryptek/releases) page.

```bash
# Linux/macOS
chmod +x cryptek
sudo mv cryptek /usr/local/bin/

# Or install to user directory (no sudo required)
mkdir -p ~/.local/bin
mv cryptek ~/.local/bin/
export PATH="$HOME/.local/bin:$PATH"  # Add to .bashrc/.zshrc
```

### From Source

Requires **Go 1.25.7 or later** (fixes crypto/tls vulnerabilities GO-2026-4340, GO-2026-4337).

```bash
# Check your Go version
go version  # Must be 1.25.7+

git clone https://github.com/hecperez/cryptek.git
cd cryptek
go build -o cryptek ./cmd/cryptek
```

### Man Page (macOS/Linux)

A man page is included in releases and can be installed for `man cryptek` access:

```bash
# From release archive (after extraction)
sudo cp docs/man/man1/cryptek.1 /usr/local/share/man/man1/

# From source (using make)
make install-man

# View without installing
man ./docs/man/man1/cryptek.1
```

**Linux packages (.deb/.rpm):** The man page is installed automatically.

**Windows:** Use `cryptek --help` or view `docs/cli/cryptek.md`.

## Quick Start

```bash
# Generate a keyfile (recommended for high security)
cryptek keygen mykey.bin

# Encrypt with keyfile (secret.txt → secret.ctk)
cryptek encrypt secret.txt --keyfile mykey.bin

# Decrypt with keyfile (secret.ctk → secret.txt)
cryptek decrypt secret.ctk --keyfile mykey.bin

# Or use password with secure prompt
cryptek encrypt secret.txt --stdin
cryptek decrypt secret.ctk --stdin

# Encrypt multiple files at once
cryptek encrypt file1.txt file2.txt file3.txt --stdin

# Hash files (SHA-256)
cryptek hash file1.txt file2.txt

# Encrypt/decrypt text strings
cryptek string encrypt --text "hello world" --stdin
cryptek string decrypt --text "base64data..." --stdin
```

Note: The original file extension is hidden inside the encrypted file and automatically restored during decryption.

## Usage

### Commands

Cryptek uses subcommands for its primary operations:

| Command | Description |
|---------|-------------|
| `cryptek encrypt` | Encrypt file(s) |
| `cryptek decrypt` | Decrypt file(s) |
| `cryptek hash` | Calculate SHA-256 hash of file(s) |
| `cryptek string encrypt` | Encrypt a text string |
| `cryptek string decrypt` | Decrypt a text string |
| `cryptek reveal` | Decrypt a file to stdout |
| `cryptek keygen` | Generate a random keyfile |
| `cryptek hashes` | List hash algorithms |
| `cryptek kdfs` | List key derivation functions |
| `cryptek version` | Show version |

### Encrypt Files

```bash
cryptek encrypt <files...> [flags]

Flags:
  -s, --stdin           Read password from stdin (secure, not visible)
  -P, --password        Password for encryption (visible in process list)
  -K, --keyfile         Use keyfile as key material
  -k, --keep            Keep original file after encryption
  -S, --secure-delete   Securely delete original files (overwrite before removal)
  -f, --folder          Encrypt a folder as a tar archive
  -z, --compress        Compress folder with gzip before encryption (requires --folder)
  -N, --hide-name       Generate random output filename (original stored in header)
  -H, --hide-file       Hide encrypted output from directory listings (dot prefix)
  --stealth             Maximum concealment (combines --hide-name and --hide-file)
  --kdf                 Select KDF: argon2id (default), pbkdf2
```

**Examples:**

```bash
# Secure password input (recommended - not visible in process list)
cryptek encrypt secret.txt --stdin
cryptek encrypt secret.txt -s              # Short form

# Multiple files at once
cryptek encrypt file1.txt file2.txt file3.txt --stdin

# Using a keyfile (256-bit entropy)
cryptek encrypt secret.txt --keyfile mykey.bin
cryptek encrypt secret.txt -K mykey.bin    # Short form

# Keyfile + password (defense in depth - requires both to decrypt)
cryptek encrypt secret.txt --keyfile mykey.bin --stdin

# Single file with password flag (visible in process list)
cryptek encrypt secret.txt --password myPassword123
cryptek encrypt secret.txt -P myPassword123        # Short form

# Using environment variable
export CRYPTEK_KEY="myPassword123"
cryptek encrypt secret.txt

# Using PBKDF2 instead of Argon2id
cryptek encrypt secret.txt --stdin --kdf pbkdf2

# Wildcard patterns - batch processing (quote to pass pattern to cryptek)
cryptek encrypt "*.txt" --stdin                 # All .txt in current dir
cryptek encrypt "docs/*.doc" --stdin            # All .doc in docs/
cryptek encrypt "**/*.txt" --stdin              # All .txt recursively

# Shell-expanded wildcards (unquoted - shell expands before cryptek sees them)
# Linux/macOS only - Windows must use quoted patterns
cryptek encrypt *.txt --stdin                   # Shell expands to: cryptek encrypt file1.txt file2.txt ...
cryptek encrypt docs/*.doc --stdin              # Shell expands files in docs/

# Keep original files
cryptek encrypt "*.txt" --stdin --keep

# Pipe password (for scripts)
echo "myPassword" | cryptek encrypt secret.txt --stdin

# Encrypt an entire folder
cryptek encrypt --folder /path/to/folder --stdin

# Encrypt folder with compression
cryptek encrypt --folder /path/to/folder --stdin --compress

# Keep original folder after encryption
cryptek encrypt --folder /path/to/folder --stdin --keep

# Hide filename (generate random output name, no .ctk extension)
cryptek encrypt secret.pdf --stdin --hide-name
# Output: a7b3c9d2e4f5a1b2 (original "secret.pdf" stored in header)

# Hide from directory listings (dot prefix)
cryptek encrypt secret.pdf --stdin --hide-file
# Output: .secret.ctk (hidden from ls, visible with ls -a)

# Maximum concealment: random name + hidden from listings
cryptek encrypt secret.pdf --stdin --stealth
# Output: .a7b3c9d2e4f5a1b2 (invisible without ls -a)

# Decrypt restores original filename from any concealment mode
cryptek decrypt a7b3c9d2e4f5a1b2 --stdin
cryptek decrypt .a7b3c9d2e4f5a1b2 --stdin
# Output: secret.pdf (restored from header)
```

The encrypted files are saved with a `.ctk` extension. The original file extension is hidden inside the encrypted header:
- `document.pdf` → `document.ctk` (extension "pdf" stored securely inside)
- `archive.tar.gz` → `archive.ctk` (compound extension stored)
- Folders: `mydir/` → `mydir.tar.ctk` (or `mydir.tar.gz.ctk` with `--compress`)

Original files/folders are deleted by default.

### Decrypt Files

```bash
cryptek decrypt <files...> [flags]

Flags:
  -s, --stdin           Read password from stdin (secure, not visible)
  -P, --password        Password for decryption (visible in process list)
  -K, --keyfile         Use keyfile as key material (must match encryption)
  -k, --keep            Keep encrypted file after decryption
  -S, --secure-delete   Securely delete encrypted files (overwrite before removal)
  -T, --tmpfs           Decrypt to RAM (tmpfs) - data never touches disk (Linux/macOS only)
  -o, --output          Output file path (use - for stdout)
  -x, --extract         Extract tar archive after decryption (for encrypted folders)
```

**Examples:**

```bash
# Secure password input (recommended)
cryptek decrypt secret.ctk --stdin

# Multiple files at once
cryptek decrypt file1.ctk file2.ctk file3.ctk --stdin

# Using keyfile (must be the same as used for encryption)
cryptek decrypt secret.ctk --keyfile mykey.bin

# Keyfile + password (if encrypted with both)
cryptek decrypt secret.ctk --keyfile mykey.bin --stdin

# Wildcard patterns - batch processing
cryptek decrypt "*.ctk" --stdin                 # All .ctk in current dir
cryptek decrypt "docs/*.ctk" --stdin            # All .ctk in docs/
cryptek decrypt "**/*.ctk" --stdin              # All .ctk recursively

# Shell-expanded wildcards (unquoted - shell expands before cryptek sees them)
# Linux/macOS only - Windows must use quoted patterns
cryptek decrypt *.ctk --stdin                   # Shell expands to: cryptek decrypt file1.ctk file2.ctk ...
cryptek decrypt docs/*.ctk --stdin              # Shell expands files in docs/

# Keep encrypted files
cryptek decrypt "*.ctk" --stdin --keep

# Output to stdout (pipe-friendly)
cryptek decrypt secret.ctk -o - --stdin | less
cryptek decrypt config.ctk -o - --stdin | jq '.database'

# Output to custom file
cryptek decrypt secret.ctk -o /tmp/decrypted.txt --stdin

# Decrypt and extract an encrypted folder
cryptek decrypt --extract folder.tar.ctk --stdin

# Keep encrypted archive after extraction
cryptek decrypt --extract folder.tar.ctk --stdin --keep
```

The KDF algorithm is auto-detected from the encrypted file header.

**Note:** The `--output` flag only works with a single file, not patterns matching multiple files.

**Difference between `reveal` and `decrypt -o -`:**
- `cryptek reveal`: Formatted output designed for human viewing in terminal
- `cryptek decrypt -o -`: Raw bytes output designed for piping to other programs

### Reveal (Decrypt to Terminal)

View encrypted file contents without creating a decrypted file on disk:

```bash
cryptek reveal <file.ctk> [flags]

Flags:
  -s, --stdin       Read password from stdin (secure, not visible)
  -P, --password    Password for decryption (visible in process list)
  -K, --keyfile     Use keyfile as key material (must match encryption)
```

**Examples:**

```bash
# Secure password input (recommended)
cryptek reveal notes.ctk --stdin

# Using keyfile
cryptek reveal notes.ctk --keyfile mykey.bin

# With password flag
cryptek reveal notes.ctk --password myPassword123
```

Useful for viewing sensitive text files without leaving decrypted copies on disk.

### Generate Keyfile

Generate a cryptographically secure random keyfile:

```bash
cryptek keygen <output-file> [flags]

Flags:
  -n, --size      Size in bytes (default: 32, min: 16, max: 1024)
```

**Examples:**

```bash
# Generate default 32-byte (256-bit) keyfile
cryptek keygen mykey.bin

# Generate 64-byte keyfile
cryptek keygen mykey.bin --size 64
cryptek keygen mykey.bin -n 64         # Short form
```

**Security notes:**
- Keyfiles are created with restrictive permissions (0600)
- Store keyfiles securely - anyone with access can decrypt your files
- Consider storing on a separate device (USB drive) for two-factor security
- Can be combined with password for defense in depth

### Calculate File Hash (SHA-256)

```bash
cryptek hash <files...>

# Single file
cryptek hash document.pdf

# Multiple files
cryptek hash file1.txt file2.txt file3.txt

# Wildcard patterns (batch hashing)
cryptek hash "*.txt"           # All .txt in current dir
cryptek hash "docs/*.pdf"      # All .pdf in docs/
cryptek hash "**/*.txt"        # All .txt recursively

# Shell-expanded wildcards (unquoted - Linux/macOS only)
cryptek hash *.txt             # Shell expands to: cryptek hash file1.txt file2.txt ...
```

**Note:** The `hash` command uses SHA-256, the recommended secure hash algorithm.

### String Encryption

Encrypt or decrypt text strings directly from the command line. Output is encoded as base64 (default) or hex for safe terminal/clipboard use.

```bash
cryptek string encrypt [flags]
cryptek string decrypt [flags]

Flags:
  -t, --text        Text string to encrypt/decrypt
  -F, --format      Output format: base64 (default), hex (encrypt) / auto, base64, hex (decrypt)
  -s, --stdin       Read password from stdin (secure, not visible)
  -P, --password    Password (visible in process list)
  -K, --keyfile     Use keyfile as key material
  --kdf             Select KDF: argon2id (default), pbkdf2 (encrypt only)
```

**Examples:**

```bash
# Encrypt a string with secure password input
cryptek string encrypt --text "hello world" --stdin

# Encrypt with password flag
cryptek string encrypt --text "secret message" -P myPassword123

# Output as hex instead of base64
cryptek string encrypt --text "hello" -P myPassword --format hex

# Decrypt a base64-encoded ciphertext
cryptek string decrypt --text "base64data..." -P myPassword123

# Decrypt hex-encoded ciphertext
cryptek string decrypt --text "hexdata..." -P myPassword --format hex

# Pipe text through stdin
echo "hello world" | cryptek string encrypt -P myPassword
echo "base64data..." | cryptek string decrypt -P myPassword
```

### List Available Information

```bash
# Hash algorithms (available in internal package)
cryptek hashes

# Key derivation functions
cryptek kdfs
```

### Version Information

```bash
cryptek version    # Subcommand (detailed)
cryptek --version  # Flag (standard CLI)
cryptek -v         # Short flag
```

## Keyfile Support

Keyfiles provide an alternative to passwords with higher entropy:

| Method | Typical Entropy | Use Case |
|--------|-----------------|----------|
| Password | ~40 bits (typical) | Convenience, memorability |
| Keyfile | 256 bits | Maximum security |
| Keyfile + Password | 256+ bits | Defense in depth |

### How Keyfiles Work

```
Keyfile bytes + Password bytes (optional)
              │
              ▼
    ┌─────────────────────────┐
    │  KDF (Argon2id/PBKDF2)  │◄── Random salt (per file)
    └─────────────────────────┘
              │
              ▼
       256-bit AES key
```

- Keyfile bytes are fed directly to the KDF (any file size up to 15MB)
- If both keyfile and password are provided, they are concatenated
- The salt is random per file, stored in the encrypted file header
- Any file can be used as a keyfile (images, random bytes, etc.)

### Keyfile Best Practices

1. **Generate random keyfiles** using `cryptek keygen` for maximum entropy
2. **Store separately** from encrypted files (USB drive, secure backup)
3. **Combine with password** for two-factor security
4. **Back up keyfiles** - if lost, encrypted files cannot be recovered
5. **Use restrictive permissions** (automatic with `keygen`)

## Folder Encryption

Cryptek can encrypt entire directories as tar archives, preserving the folder structure.

### Available Options

| Option | Description |
|--------|-------------|
| `--folder`, `-f` | Treat input as a folder to encrypt (creates tar archive) |
| `--compress`, `-z` | Compress the tar archive with gzip before encryption (requires `--folder`) |
| `--stdin`, `-s` | Read password securely (not visible in terminal or process list) |
| `--keep`, `-k` | Keep the original folder after encryption |
| `--keyfile`, `-K` | Use a keyfile instead of (or in addition to) a password |
| `--kdf` | Select key derivation function: `argon2id` (default) or `pbkdf2` |

### Encrypt Folder - All Combinations

| Command | What It Does |
|---------|--------------|
| `cryptek encrypt --folder mydir -P password` | Encrypt folder with password (visible in process list) |
| `cryptek encrypt --folder mydir --stdin` | Encrypt folder, password prompted securely (recommended) |
| `cryptek encrypt --folder mydir --keyfile key.bin` | Encrypt folder using a keyfile |
| `cryptek encrypt --folder mydir --keyfile key.bin --stdin` | Encrypt using both keyfile AND password (maximum security) |
| `cryptek encrypt --folder mydir --stdin --compress` | Encrypt with gzip compression (smaller output, good for text files) |
| `cryptek encrypt --folder mydir --stdin --keep` | Encrypt but keep the original folder |
| `cryptek encrypt --folder mydir --stdin --compress --keep` | Compress, encrypt, and keep original |
| `cryptek encrypt --folder mydir --stdin --kdf pbkdf2` | Encrypt using PBKDF2 instead of Argon2id |

**Output files:**
- Without `--compress`: `mydir.tar.ctk`
- With `--compress`: `mydir.tar.gz.ctk`

### Decrypt Folder - All Combinations

| Command | What It Does |
|---------|--------------|
| `cryptek decrypt --extract mydir.tar.ctk -P password` | Decrypt and extract folder (password visible in process list) |
| `cryptek decrypt --extract mydir.tar.ctk --stdin` | Decrypt and extract, password prompted securely (recommended) |
| `cryptek decrypt --extract mydir.tar.ctk --keyfile key.bin` | Decrypt using keyfile |
| `cryptek decrypt --extract mydir.tar.ctk --keyfile key.bin --stdin` | Decrypt using both keyfile AND password |
| `cryptek decrypt --extract mydir.tar.ctk --stdin --keep` | Decrypt, extract, and keep the encrypted archive |

**Notes:**
- Gzip compression is auto-detected during extraction (no flag needed)
- The folder is extracted to the current directory
- Original directory structure, file names, and permissions are preserved

### Quick Reference

**"How do I encrypt a folder?"**
```bash
cryptek encrypt --folder /path/to/folder --stdin
```

**"How do I encrypt a folder and make it smaller?"**
```bash
cryptek encrypt --folder /path/to/folder --stdin --compress
```

**"How do I decrypt an encrypted folder?"**
```bash
cryptek decrypt --extract folder.tar.ctk --stdin
```

**"How do I encrypt a folder but keep the original?"**
```bash
cryptek encrypt --folder /path/to/folder --stdin --keep
```

**"How do I encrypt a folder with a keyfile for maximum security?"**
```bash
cryptek keygen mykey.bin
cryptek encrypt --folder /path/to/folder --keyfile mykey.bin --stdin
```

**"What's the difference between compressed and non-compressed?"**
- `--compress` applies gzip before encryption, reducing file size
- Best for text files, code, documents (50-80% smaller)
- Minimal benefit for already-compressed files (images, videos, archives)
- Adds slight processing overhead

### How It Works

```
Encryption Flow:
┌─────────────┐     ┌─────────────┐     ┌─────────────────┐
│   Folder    │ ──► │ Tar Archive │ ──► │ Encrypted .ctk  │
│  (files +   │     │ (optional   │     │    (AES-256)    │
│   subdirs)  │     │   gzip)     │     │                 │
└─────────────┘     └─────────────┘     └─────────────────┘

Decryption Flow:
┌─────────────────┐     ┌─────────────┐     ┌─────────────┐
│ Encrypted .ctk  │ ──► │ Tar Archive │ ──► │   Folder    │
│                 │     │ (auto-      │     │ (restored   │
│                 │     │  detected)  │     │  structure) │
└─────────────────┘     └─────────────┘     └─────────────┘
```

### Why Encrypt Folders This Way?

| Benefit | Explanation |
|---------|-------------|
| **Cross-platform** | Uses Go's `archive/tar` - works on Linux, macOS, and Windows |
| **Single file output** | One `.ctk` file instead of many - easier to store, transfer, backup |
| **Preserves structure** | Maintains folder hierarchy, file names, and permissions |
| **Atomic operation** | Either the whole folder encrypts or nothing does |
| **Optional compression** | Reduce size before encryption (encryption itself doesn't compress) |

## Wildcard Patterns

Cryptek supports glob patterns for batch processing multiple files.

**Platform differences:**
- **Windows**: Must use quoted patterns (`"*.txt"`) - Windows shell doesn't expand wildcards
- **Linux/macOS**: Can use either quoted (`"*.txt"`) or unquoted (`*.txt`) - both work

### Pattern Syntax

| Pattern | Description | Example |
|---------|-------------|---------|
| `*` | Matches any characters (except `/`) | `*.txt` matches `file.txt`, `doc.txt` |
| `?` | Matches any single character | `file?.txt` matches `file1.txt`, `fileA.txt` |
| `[abc]` | Matches any character in brackets | `file[123].txt` matches `file1.txt`, `file2.txt` |
| `[a-z]` | Matches any character in range | `file[a-c].txt` matches `filea.txt`, `fileb.txt` |
| `[!abc]` | Matches any character NOT in brackets | `file[!0-9].txt` matches `fileA.txt`, not `file1.txt` |
| `**` | Matches directories recursively | `**/*.txt` matches all `.txt` files in all subdirs |

### Examples

```bash
# All .txt files in current directory
cryptek encrypt "*.txt" --stdin

# All files starting with "report"
cryptek encrypt "report*" --stdin

# Files with single character before extension
cryptek encrypt "data?.csv" --stdin

# Files numbered 1-5
cryptek encrypt "file[1-5].txt" --stdin

# All .txt files in docs/ folder
cryptek encrypt "docs/*.txt" --stdin

# All .txt files recursively (all subdirectories)
cryptek encrypt "**/*.txt" --stdin

# All .ctk files in backup/ and its subdirectories
cryptek decrypt "backup/**/*.ctk" --stdin

# Shell-expanded wildcards (unquoted - Linux/macOS only, Windows must use quotes)
cryptek encrypt *.txt --stdin                   # Shell expands the pattern
cryptek decrypt *.ctk --stdin                   # Works the same way
```

### Batch Processing Behavior

- **Progress bar**: Visual feedback during batch operations
- **Graceful cancellation**: Press Ctrl+C to stop cleanly after current file finishes
- **Continue on error**: If one file fails, processing continues with remaining files
- **Summary output**: Shows "Processed X files: Y succeeded, Z failed, W cancelled"
- **Exit code**: Returns 1 if any file failed or operation was cancelled, 0 if all succeeded
- **Directories excluded**: Only regular files are processed
- **Symlinks skipped**: With warning (security: prevents scope escape)
- **Path traversal blocked**: Patterns with `../` are rejected
- **Sorted output**: Files are processed in alphabetical order

## Technical Details

### Security Features

| Feature | Implementation |
|---------|----------------|
| **Authenticated Encryption** | AES-256-GCM detects tampering with ciphertext |
| **Header Authentication (AAD)** | GCM Additional Authenticated Data prevents metadata tampering |
| **Secure Password Input** | `--stdin` flag reads password without echoing (not in process list) |
| **Keyfile Support** | Use file as key material (256-bit entropy), can combine with password |
| **Memory Zeroing** | Passwords, keys, and plaintext zeroed after use |
| **Atomic File Writes** | No race condition where files have wrong permissions |
| **Restrictive Permissions** | Files created with 0600 (owner read/write only) |
| **Strong KDF** | Argon2id (64MB memory) resists GPU/ASIC attacks |
| **Downgrade Protection** | KDF params validated against minimum thresholds |
| **Version Checking** | Rejects unsupported file format versions |
| **Path Validation** | Rejects path traversal (`../`), warns on symlinks |
| **Weak Hash Warnings** | MD4/MD5/SHA1 display security warnings |
| **Password Length Warning** | Warns when password < 8 characters, rejects passwords > 4096 bytes |
| **Single Format** | v6 only — no legacy code paths (v2-v5 removed in v0.15.0) |
| **File Operation Timeout** | 3-minute timeout per file prevents hanging |
| **Secure Deletion** | `--secure-delete` overwrites files before removal (HDD: 3-pass, SSD: re-encrypt) |

### Encryption Specification

| Parameter | Value |
|-----------|-------|
| Algorithm | AES-256-GCM (authenticated encryption) |
| Key Derivation | Argon2id (default) or PBKDF2 |
| Nonce | Cryptographically secure random (12 bytes) |
| Salt | Cryptographically secure random (16 bytes) |
| Auth Tag | 16 bytes (built-in GCM authentication) |
| File Extension | `.ctk` |

### Key Derivation Functions

| KDF | Parameters | Minimum Allowed | Security Level |
|-----|------------|-----------------|----------------|
| **Argon2id** (default) | 64MB memory, 2 iterations, 4 threads | 32MB, 1 iter, 1 thread | Highest - GPU/ASIC resistant |
| PBKDF2 | SHA-512, 600,000 iterations (OWASP 2024) | 100,000 iterations | High - widely compatible |

### Encrypted File Format (v6)

v6 uses variable-length encrypted headers for anti-fingerprinting - header size varies randomly between 80-528 bytes, making files harder to identify.

```
+------------+---------------+------------------+--------+-------------------+
| Outer Salt | Length Block  | Encrypted Header | Nonce  | Ciphertext+AuthTag|
|   16 B     |     32 B      |   80-528 B       | 12 B   | variable          |
+------------+---------------+------------------+--------+-------------------+
      |             |               |                            |
      |      (Header size)    (KDF params, filename)             |
      |      encrypted         all hidden                        |
      |                                                          |
      +-- Domain-separated keys for length/header/content -------+
```

- **Outer Salt**: Random 16 bytes (used to derive all keys)
- **Length Block**: 32 bytes (16B plaintext + 16B tag) containing magic, version, header size
- **Encrypted Header**: 80-528 bytes (64-512B plaintext + 16B tag) containing KDF params, full filename
- **Nonce**: Random 12-byte nonce for content encryption
- **Ciphertext+AuthTag**: AES-256-GCM encrypted data with 16-byte authentication tag

**Three-Layer Encryption with Domain Separation**:
1. **Length Block**: PBKDF2-SHA512 (500k iterations) with outer salt; outer salt is AAD
2. **Header**: PBKDF2-SHA512 (500k iterations) with `outer_salt||0x01`; length block is AAD
3. **Content**: Argon2id or PBKDF2-SHA512 (from header); encrypted header is AAD

**Anti-Fingerprinting**: Header size varies randomly by 0-448 bytes between files, preventing file type identification.

**Full Filename Storage**: v6 stores the complete original filename (up to 255 bytes), not just the extension.

**Stealth Property**: No visible "CTK" magic bytes - files are indistinguishable from random data.

**Single Format**: Cryptek supports only the v6 format. Legacy formats (v2-v5) were removed in v0.15.0. If you have files encrypted with an older version, use Cryptek v0.14.x or earlier to decrypt them first, then re-encrypt with the current version.

*See [docs/technical/02-FILE_FORMAT.md](docs/technical/02-FILE_FORMAT.md) for detailed byte layouts and format specifications.*

### Why Argon2id?

Argon2id is the winner of the Password Hashing Competition (2015) and provides:

- **Memory-hardness**: Requires 64MB RAM per hash, making GPU attacks impractical
- **Time-hardness**: Configurable iterations
- **Parallelism resistance**: Designed to resist both sequential and parallel attacks
- **Side-channel resistance**: Combines Argon2i and Argon2d for best security

**Attack resistance comparison:**

| KDF | Time to crack "Summer2024" (GPU) |
|-----|----------------------------------|
| SHA-256 (old method) | < 1 second |
| PBKDF2 (600k iterations) | ~8 hours |
| **Argon2id (64MB, t=2)** | **Months to years** |

*See [docs/technical/03-KEY_DERIVATION.md](docs/technical/03-KEY_DERIVATION.md) for in-depth KDF analysis and memory-hard structure details.*

### Security Considerations

1. **Password Strength**: Even with Argon2id, use a strong password. The KDF protects against brute-force, but weak passwords are still vulnerable. Consider using a keyfile for maximum entropy.

2. **Keyfile Security**: Store keyfiles separately from encrypted files. If using both keyfile and password, an attacker needs both to decrypt.

3. **File Deletion**: By default, Cryptek deletes original files after encryption using standard OS file removal. For enhanced security, use the `--secure-delete` (`-S`) flag which overwrites files before removal:
   - **HDD (rotational drives)**: 3-pass overwrite (zeros, ones, random data)
   - **SSD (solid-state drives)**: Re-encrypt with random key (noise over noise), then TRIM

   **Limitations of secure deletion** (even with `--secure-delete`):
   - Journaling filesystems may keep copies in journal
   - SSD wear-leveling means old blocks may persist until garbage collection
   - Copy-on-write filesystems (ZFS, Btrfs) retain snapshots
   - Cloud storage providers may retain versions

   For maximum security, use full-disk encryption (FileVault, BitLocker, LUKS) as the primary protection layer.

4. **RAM Decryption (tmpfs)**: For highly sensitive files, use `--tmpfs` to decrypt directly to RAM:
   ```bash
   cryptek decrypt secret.ctk --tmpfs
   # Files are decrypted to RAM-based filesystem
   # Press Enter when done to securely erase
   ```
   - Data never touches disk (no forensic recovery possible)
   - Automatically erased when unmounted
   - RAM size auto-calculated from file sizes (10MB min, 512MB max)
   - Supported on Linux (native tmpfs) and macOS (hdiutil RAM disk)
   - Not supported on Windows (no built-in RAM disk)

5. **Memory Security**: Sensitive data (passwords, keyfile contents, plaintext, and derived keys) is zeroed after use to reduce memory exposure risks.

6. **Authenticated Encryption**: AES-256-GCM provides both confidentiality and integrity - any tampering with the ciphertext is detected during decryption. The v6 format uses three-layer encryption with variable-size encrypted headers (all metadata hidden) and AAD authentication at all layers.

7. **File Permissions**: Encrypted files, decrypted files, and keyfiles are created with restrictive permissions (0600 - owner read/write only).

8. **Password Input Security**: Use `--stdin` flag to enter passwords securely (not visible in process list or shell history). Alternatively, use the `CRYPTEK_KEY` environment variable or a keyfile.

*See [docs/technical/08-THREAT_MODEL.md](docs/technical/08-THREAT_MODEL.md) for complete threat model and attack scenario analysis.*

## Environment Variables

| Variable | Description |
|----------|-------------|
| `CRYPTEK_KEY` | Default password when not provided as argument |

Using environment variables keeps passwords out of shell history:

```bash
export CRYPTEK_KEY="mySecretPassword"
cryptek encrypt sensitive.doc    # Creates sensitive.ctk
cryptek decrypt sensitive.ctk    # Restores sensitive.doc
```

## Command Reference

### All Commands

| Command | Description |
|---------|-------------|
| `cryptek encrypt <files...>` | Encrypt file(s) |
| `cryptek decrypt <files...>` | Decrypt file(s) |
| `cryptek hash <files...>` | Calculate SHA-256 hash of file(s) |
| `cryptek string encrypt` | Encrypt a text string |
| `cryptek string decrypt` | Decrypt a text string |
| `cryptek reveal <file>` | Decrypt to stdout |
| `cryptek keygen <output-file>` | Generate random keyfile |
| `cryptek hashes` | List hash algorithms |
| `cryptek kdfs` | List key derivation functions |
| `cryptek version` | Show version |

### Flags

| Flag | Command | Description |
|------|---------|-------------|
| `-s, --stdin` | encrypt, decrypt, reveal, string | Read password securely from stdin |
| `-P, --password` | encrypt, decrypt, reveal, string | Password (visible in process list) |
| `-K, --keyfile` | encrypt, decrypt, reveal, string | Use keyfile as key material |
| `-k, --keep` | encrypt, decrypt | Keep original/encrypted file |
| `-S, --secure-delete` | encrypt, decrypt | Securely delete files (overwrite before removal) |
| `--kdf` | encrypt, string encrypt | Select KDF: argon2id (default), pbkdf2 |
| `-f, --folder` | encrypt | Encrypt folder as tar archive |
| `-z, --compress` | encrypt | Compress folder with gzip (requires --folder) |
| `-N, --hide-name` | encrypt | Generate random output filename (original stored in header) |
| `-H, --hide-file` | encrypt | Hide encrypted output from directory listings (dot prefix) |
| `--stealth` | encrypt | Maximum concealment (combines --hide-name and --hide-file) |
| `-o, --output` | decrypt | Output file path (use `-` for stdout) |
| `-x, --extract` | decrypt | Extract tar archive after decryption |
| `-T, --tmpfs` | decrypt | Decrypt to RAM (Linux/macOS only) |
| `-t, --text` | string encrypt, string decrypt | Text string to encrypt/decrypt |
| `-F, --format` | string encrypt, string decrypt | Encoding format: base64, hex (auto for decrypt) |
| `-n, --size` | keygen | Keyfile size in bytes (default: 32) |

## Building & Development

### Required Tools

| Tool | Version | Required For | Installation |
|------|---------|--------------|--------------|
| **Go** | 1.25.7+ | Building, testing | [golang.org/dl](https://golang.org/dl/) or `brew install go` |
| **golangci-lint** | 2.x | Linting (optional locally) | `brew install golangci-lint` or [docs](https://golangci-lint.run/welcome/install/) |
| **govulncheck** | latest | Security scanning | `go install golang.org/x/vuln/cmd/govulncheck@latest` |

**Check installed tools:**

```bash
make check-tools
```

**Why Go 1.25.7+?** Fixes crypto/tls vulnerabilities GO-2026-4340, GO-2026-4337. The `go.mod` file enforces this minimum version.

### From Source

```bash
git clone https://github.com/hecperez/cryptek.git
cd cryptek
go build -o cryptek ./cmd/cryptek
```

### Running Tests

```bash
# Run all tests
make test

# Or directly with go
go test ./... -p=1

# With verbose output
make test-v

# With coverage report
make test-coverage
```

The test suite includes 202 test functions (495 including subtests) covering:
- **Unit tests**: Encryption/decryption, KDF algorithms, file hashing, glob patterns, keyfile operations
- **Integration tests**: CLI workflows, wildcard batch processing, error handling
- **Security tests**: Tamper detection, header authentication, version validation, weak parameter rejection

### Local Development Checks

Before pushing code, run all checks locally:

```bash
# Run ALL checks (test + lint + security) - recommended before pushing
make check

# Or run individually
make test       # Run tests
make lint       # Run golangci-lint
make security   # Run govulncheck

# Install pre-push hook (runs 'make check' automatically before each push)
make install-hooks
```

### Cross-Platform Builds

Using GoReleaser (recommended):

```bash
goreleaser release --snapshot --clean
```

Manual cross-compilation:

```bash
# Linux (amd64)
GOOS=linux GOARCH=amd64 go build -o cryptek-linux-amd64 ./cmd/cryptek

# Linux (arm64)
GOOS=linux GOARCH=arm64 go build -o cryptek-linux-arm64 ./cmd/cryptek

# macOS (Intel)
GOOS=darwin GOARCH=amd64 go build -o cryptek-darwin-amd64 ./cmd/cryptek

# macOS (Apple Silicon)
GOOS=darwin GOARCH=arm64 go build -o cryptek-darwin-arm64 ./cmd/cryptek

# Windows
GOOS=windows GOARCH=amd64 go build -o cryptek-windows-amd64.exe ./cmd/cryptek
```

## CI/CD

GitHub Actions workflows handle continuous integration and releases, optimized to save CI minutes.

### Local Checks (Recommended)

Run checks locally before pushing to avoid CI failures:

```bash
make check           # Run test + lint + security
make install-hooks   # Auto-run checks before every push
```

### Continuous Integration (`ci.yml`)

| Event | Tests | Lint & Security |
|-------|-------|-----------------|
| Push to main/develop (code) | ✓ | - |
| Push (docs-only changes) | - | - |
| Pull Request | ✓ | ✓ |

**Optimizations:**
- Docs-only changes (`**.md`, `docs/**`, `.claude/**`) skip CI entirely
- Lint and security only run on PRs (local checks catch these earlier)
- Concurrency control cancels redundant runs

### Releases (`release.yml`)

Triggers on version tags (`v*`):
- Runs tests
- Builds with GoReleaser for all platforms
- Creates GitHub release with binaries, .deb, .rpm packages

## Version History

### v0.15.0 - CLI Subcommands, String Encryption, Stealth Filenames & Legacy Removal
- **Subcommand-based CLI**: `encrypt`, `decrypt`, `hash` are now subcommands (replaces `-E`/`-D`/`-H` flags)
  - `cryptek encrypt file.txt --stdin`
  - `cryptek decrypt file.ctk --stdin`
  - `cryptek hash file.txt`
  - Existing subcommands (`reveal`, `keygen`, `hashes`, `kdfs`, `version`) unchanged
- **String encryption**: New `string encrypt` and `string decrypt` commands for text strings
  - Output as base64 (default) or hex
  - Pipe-friendly: accepts text from stdin
- **DecryptData API**: Exported `crypto.DecryptData()` for byte-level decryption
- **Stealth filenames**:
  - `--hide-name` improved: random output without `.ctk` extension (e.g., `secret.pdf` → `a7f3b9c1`)
  - `--hide-file`: hide output from directory listings (dot prefix on Unix, hidden attribute on Windows)
  - `--stealth`: maximum concealment combining `--hide-name` + `--hide-file`
- **Breaking**: Removed v2 (AES-CBC), v3 (GCM no-AAD), v4 (visible headers), v5 (fixed headers) decryption support
- Codebase now supports only v6 format — ~700 lines of legacy code removed
- **16 rounds of code review**: ~148 bugs fixed across security, data loss prevention, and correctness
- **202 test functions** (495 including subtests) with comprehensive security coverage

### v0.14.0 - v6 Format & Anti-Fingerprinting (Released 2026-02-14)
- **v6 file format** with variable-length encrypted headers (anti-fingerprinting)
  - Header size varies randomly between 64-512 bytes (25 possible sizes)
  - Three-layer encryption with domain-separated keys
  - Full filename storage (up to 255 bytes, not just extension)
- **Multi-file support**: `cryptek encrypt file1.txt file2.txt file3.txt`
- **Security hardening**: Constant-time comparisons, unified error messages, filename validation
- **171 tests** with comprehensive v6 security tests (tampering, replay, bit-flip attacks)

### v0.13.0 - Password Handling & CI Optimization
- **Breaking**: Removed positional password argument (use `--password` or `--stdin` instead)
- Clearer error message: "No password or keyfile provided"
- Priority order: `--stdin` > `--password` > env var
- **Local CI checks**: `make check` runs test + lint + security
- **Pre-push hook**: `make install-hooks` for automatic checks
- **CI optimization**: Skip docs-only changes, PR-only lint/security

### v0.12.0 - Password Flag
- **`--password`/`-P` flag** - Explicit password input for encrypt, decrypt, reveal commands

### v0.11.2 - tmpfs & CI Optimization
- **`--tmpfs` flag** - Decrypt to RAM-based filesystem (data never touches disk)
- Linux: Native tmpfs mount, macOS: RAM disk via hdiutil
- Comprehensive path traversal attack prevention
- CI/CD optimization: Reduced duplicate workflow runs

### v0.11.1 - Memory Security
- **Secure memory zeroing** - `clear()` builtin (guaranteed dead-store-safe) for memory zeroing
- New `internal/memzero` package for centralized secure zeroing

### v0.11.0 - Examples & Hash Enhancements
- **Examples folder** - `examples/demo.sh` demonstrates all commands
- **Hash wildcards** - `cryptek hash '**/*.txt'`
- **BLAKE3 & TIGER/TIGER2** hash algorithms added (21 total)
- **CLI formatting** - Color-coded messages (green success, yellow warning, red error)

### v0.10.5 - Secure Deletion
- **`--secure-delete` flag** - Overwrites files before removal
- HDD: 3-pass overwrite (zeros, ones, random)
- SSD: Re-encrypt with random key then delete

### v0.10.4 - License & Extension Hiding
- **License change** - MIT → Proprietary Freeware
- **Extension hiding** - Original extension stored in encrypted header

### v0.10.3 - Linting Hotfix
- Fixed `lostcancel` lint errors in cancellation tests

### v0.10.2 - Security Hardening
- **3-minute file operation timeout** - Prevents hanging on slow/stuck I/O
- **Trusted input model documented** - See Technical Deep Dive Section 8.5

### v0.10.1 - Graceful Cancellation
- **Graceful batch cancellation** - Ctrl+C (SIGINT) and SIGTERM stop operations cleanly
- Finishes current file before stopping (no corruption)
- Displays "cancelled" count in operation summary
- Returns exit code 1 when cancelled
- 12 new tests for cancellation functionality (93 total tests)

### v0.10.0 - Encrypted Headers (v5 Format)
- **v5 file format** with encrypted headers - all metadata hidden
- Files appear as random data (no visible "CTK" magic bytes)
- Two-layer encryption: Header (PBKDF2-SHA512) + Content (Argon2id/PBKDF2-SHA512)
- KDF type and parameters invisible without correct password
- PBKDF2 upgraded from SHA-256 to SHA-512 (OWASP recommendation)
- *Note: v5 format support removed in v0.15.0*

### v0.9.0 - Folder Encryption
- `--folder` (`-f`) flag to encrypt entire directories as tar archives
- `--compress` (`-z`) flag for optional gzip compression
- `--extract` (`-x`) flag to decrypt and extract tar archives
- Cross-platform: uses Go's `archive/tar` (Linux, macOS, Windows)
- Preserves folder structure, file names, and permissions

### v0.8.1 - Output Flag
- `--output` (`-o`) flag for decrypt command
- `-o -`: Output decrypted content to stdout (pipe-friendly, raw bytes)
- `-o <path>`: Output to specified file path
- Uses atomic writes for custom output files

### v0.8.0 - Keyfile Support
- `--keyfile` (`-K`) flag for encrypt, decrypt, reveal commands
- Keyfile can be used alone or combined with password (defense in depth)
- `cryptek keygen` command generates random keyfiles (32 bytes default)
- Maximum keyfile size: 15 MB
- Keyfiles created with restrictive permissions (0600)

### v0.7.0 - Progress Bar
- Progress bar for encrypt/decrypt operations
- Visual feedback during batch file processing
- Shows file count progress (e.g., 3/10)
- Results displayed after progress bar completes

### v0.6.x - Security Hardening
- PBKDF2 iterations increased to 600,000 (OWASP 2024)
- Atomic file writes eliminate permission race condition
- Path traversal patterns rejected (`../` not allowed)
- Symlinks skipped with warning
- Password handled as `[]byte` throughout for proper memory zeroing
- *Note: v2/v3 format support removed in v0.15.0*

### v0.5.0 - AAD Header Authentication
- v4 file format with Additional Authenticated Data
- Header authentication prevents metadata tampering
- *Note: v4 format support removed in v0.15.0*

### v0.4.0 - Security Audit
- AES-256-GCM authenticated encryption
- Memory zeroing for keys and plaintext
- Secure password input (`--stdin` flag)
- File permissions hardened to 0600
- KDF parameter validation (downgrade protection)

### Earlier Versions
- v0.3.0: Wildcard/glob pattern support
- v0.2.0: Argon2id and PBKDF2 KDF support

## License

Proprietary Freeware License - See [LICENSE](LICENSE) file for details.

## Author

Héctor Pérez
