Expand description
§git-crypt
A Rust implementation of git-crypt for transparent encryption of files in a git repository.
§Features
- Transparent Encryption: Files are automatically encrypted when committed and decrypted when checked out
- AES-256-GCM Encryption: Strong, authenticated encryption with built-in tamper detection
- Git Filter Integration: Uses git’s clean/smudge filters for seamless operation
- Key Management: Export and import symmetric keys for secure sharing
- GPG Support: Optional GPG integration for team key distribution
- SSH/age Sharing: Optional age/rage integration for sharing keys with SSH recipients
- Simple CLI: Easy-to-use command-line interface
§Quick Start
§Installation
Install from GitHub:
cargo install --git https://github.com/AprilNEA/git-crypt-rsOr build from source:
cargo build --release
# Binary will be at target/release/git-crypt§Basic Usage
# Initialize in your git repository
git-crypt init
# Configure which files to encrypt in .gitattributes
echo "*.secret filter=git-crypt diff=git-crypt" >> .gitattributes
git add .gitattributes
git commit -m "Configure git-crypt"
# Add encrypted files (automatically encrypted)
echo "my secret data" > test.secret
git add test.secret
git commit -m "Add encrypted file"
# Export key for sharing
git-crypt export-key git-crypt-key.bin§How It Works
git-crypt uses git’s filter system to transparently encrypt and decrypt files:
- Clean filter (encryption): When you
git adda file, the clean filter encrypts it before storing in the repository - Smudge filter (decryption): When you
git checkout, the smudge filter decrypts it in your working directory - Diff filter: When you
git diff, it shows that the file is encrypted rather than binary gibberish
The encryption key is stored in .git/git-crypt/keys/default and is never committed to the repository.
§Data Flow
Encryption (git add):
File content → git add → clean filter → encrypt → store in .gitDecryption (git checkout):
Encrypted data in .git → smudge filter → decrypt → working directory§Module Overview
crypto- Core AES-256-GCM encryption/decryption operationskey- Key management, storage, export/importgit- Git filter integration and repository operationsgpg- Optional GPG support for key sharing (requiresgpgfeature)rage_support- Optional age/rage-based SSH key sharing (requiressshfeature)error- Error types and unified error handling
§Commands
init- Initialize git-crypt in the current repositorylock- Lock the repository (remove filters, show encrypted content)unlock [--key-file PATH]- Unlock the repositoryexport-key OUTPUT- Export the symmetric key to a fileimport-key INPUT- Import a symmetric key from a fileadd-gpg-user GPG_ID- Grant access to a GPG user (requiresgpgfeature)add-ssh-user --ssh-key PATH- Encrypt the key for an SSH recipient via age/rage (requiressshfeature)import-age-key --input FILE --identity SSH_KEY- Decrypt an age/rage key blob with your SSH key (requiressshfeature)status- Show status of encrypted files (not yet implemented)
§Examples
§Complete Workflow
# 1. Initialize a git repository
git init my-secure-repo
cd my-secure-repo
# 2. Initialize git-crypt
git-crypt init
# 3. Configure encryption patterns in .gitattributes
cat > .gitattributes << 'EOF'
# Encrypt all files in the secrets/ directory
secrets/** filter=git-crypt diff=git-crypt
# Encrypt specific file types
*.key filter=git-crypt diff=git-crypt
*.secret filter=git-crypt diff=git-crypt
# Encrypt specific config files
config/database.yml filter=git-crypt diff=git-crypt
.env.production filter=git-crypt diff=git-crypt
EOF
git add .gitattributes
git commit -m "Configure git-crypt"
# 4. Add encrypted files
mkdir -p secrets
echo "AWS_SECRET_KEY=secret123" > secrets/api_keys.txt
git add secrets/
git commit -m "Add encrypted secrets"
# 5. Share access with team members
git-crypt export-key team-key.bin
# Share team-key.bin securely (password manager, secure channel, etc.)§Unlocking on Another Machine
# Clone the repository
git clone <repository-url>
cd <repository>
# At this point, encrypted files show as encrypted data
# Unlock with the shared key
git-crypt unlock --key-file team-key.bin
# Refresh working directory
git checkout HEAD -- .
# Now files are decrypted
cat secrets/api_keys.txt§Lock/Unlock
# Lock repository (useful before sharing working directory)
git-crypt lock
# Now all encrypted files show their encrypted content
# Unlock again
git-crypt unlock
git checkout HEAD -- .§Security Considerations
§Threat Model
Protected against:
- Unauthorized access to repository content
- Accidental exposure of secrets in public repositories
- Historical secret leakage in git history
Not protected against:
- Attacks on the working directory (files are plaintext there)
- Compromised git client or filters
- Key extraction from
.gitdirectory - Side-channel attacks
§Best Practices
- Keep
.git/git-crypt/directory secure - Use restrictive file permissions (automatic on Unix)
- Never commit key files to the repository
- Use GPG for team key distribution when possible
- Rotate keys if compromised
- Consider full-disk encryption for additional security
- Share exported keys through secure channels only
§Cryptography Details
- Algorithm: AES-256-GCM (Galois/Counter Mode)
- Key size: 256 bits (32 bytes)
- Nonce size: 96 bits (12 bytes), randomly generated per encryption
- Authentication: Built into GCM mode (16-byte tag)
§Encrypted File Format
[GITCRYPT][12-byte nonce][variable-length ciphertext + 16-byte GCM tag]The magic header ensures reliable detection of encrypted data and provides versioning capability for future format changes.
§GPG Support (Optional)
To enable GPG support, install system dependencies and build with the gpg feature:
macOS:
brew install nettle gmp
cargo install --git https://github.com/AprilNEA/git-crypt-rs --features gpgUbuntu/Debian:
sudo apt-get install libnettle-dev libgmp-dev
cargo install --git https://github.com/AprilNEA/git-crypt-rs --features gpgThen use GPG for key sharing:
git-crypt add-gpg-user user@example.com§SSH/age Support (Optional)
Build with the ssh feature (which pulls in the age dependency) to share repository keys using SSH recipients via age/rage:
cargo install --git https://github.com/AprilNEA/git-crypt-rs --features ssh
# Encrypt the repo key for a teammate's SSH public key
git-crypt add-ssh-user --ssh-key ~/.ssh/id_ed25519.pub --alias teammate
# Teammate imports it with their private key
git-crypt import-age-key --input .git/git-crypt/keys/age/teammate.age --identity ~/.ssh/id_ed25519§Compatibility
Not compatible with original git-crypt:
- Different file format (magic header + nonce prepended)
- Different key storage location
- Different filter commands
This is a complete reimplementation focusing on:
- Memory safety (Rust)
- Modern cryptography practices
- Simplicity and maintainability
- Optional features (GPG)
§Testing
The project has a comprehensive test suite with 63 tests covering:
§Unit Tests (29 tests)
Run all unit tests:
cargo test --lib- Crypto module (
crypto): Encryption correctness, authentication, edge cases - Key management (
key): File operations, permissions, key lifecycle
§Integration Tests (16 tests)
Run all integration tests:
cargo test --test integration_testTests complete workflows: initialization, lock/unlock, key export/import, multi-repo isolation.
§Filter Tests (6 tests)
Run filter tests:
cargo test --test filter_testTests git filter operations: clean (encrypt), smudge (decrypt), diff.
§Edge Case Tests (12 tests)
Run edge case tests:
cargo test --test edge_cases_testTests corner cases: large files (10MB), empty files, binary data, Unicode, corruption detection, concurrency, permissions.
§Running All Tests
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Run with backtrace
RUST_BACKTRACE=1 cargo test
# Run specific test
cargo test test_encrypt_decrypt§Test Coverage
Generate coverage report (requires cargo-tarpaulin):
cargo install cargo-tarpaulin
cargo tarpaulin --out Html§Security Testing
Tests verify security properties:
- ✅ Authentication (wrong key fails decryption)
- ✅ Tamper detection (corrupted data rejected)
- ✅ File permissions (0600 on Unix)
- ✅ Key isolation (different repos use different keys)
- ✅ Nonce uniqueness (no nonce reuse)
Re-exports§
pub use crypto::CryptoKey;pub use error::GitCryptError;pub use error::Result;pub use git::GitRepo;pub use key::KeyManager;