Expand description
§keycrypt
keycrypt is a small, production-oriented helper crate for symmetric encryption
using AES-256-GCM, with the encryption key stored in the OS keychain/credential store
via the keyring crate.
It is designed for applications that need to encrypt secrets locally without requiring
.env files, manual key management, or storing raw keys on disk.
§Design goals
-
Secure by default
- Uses AES-256-GCM (confidentiality + integrity).
- Uses a fresh random nonce for every encryption.
- Uses a fixed AAD (Associated Authenticated Data) for domain separation.
-
Key management handled by the OS
- A 32-byte master key is stored in the OS credential store.
encrypt()auto-initializes the key on first use.decrypt()never auto-initializes (it fails if the key is missing).
-
Simple API
- Encrypt/decrypt strings.
- Encrypt/decrypt raw bytes.
- Output is a portable Base64 string format.
-
No logging of sensitive material
- This crate intentionally does not log plaintext, ciphertext, or key material.
§Main API
Most users only need:
encrypt/decryptfor UTF-8 strings (decrypt returns Zeroizing) encrypt_bytes/decrypt_bytesfor raw bytes (decrypt_bytes returns Zeroizing<Vec>)
Optional helpers:
init_keychain_keyto pre-create the key (useful for first-run setup)has_keychain_keyto check whether the key exists
§Quick example (strings)
use keycrypt::{encrypt, decrypt};
let secret = "hello world";
let encrypted = encrypt(secret)?;
let decrypted = decrypt(&encrypted)?;
assert_eq!(decrypted.as_str(), secret);§Example (bytes)
use keycrypt::{encrypt_bytes, decrypt_bytes};
let raw = b"\x00\x01\x02\xff";
let encrypted = encrypt_bytes(raw)?;
let decrypted = decrypt_bytes(&encrypted)?;
assert_eq!(decrypted.as_slice(), raw);§Keychain behavior
The master key is stored using the OS credential store backend selected by keyring.
- On first call to
encrypt/encrypt_bytes, the key is created if missing. decrypt/decrypt_byteswill not create a key.- If no key exists, decryption returns
CryptoError::KeyNotSet.
- If no key exists, decryption returns
This prevents accidental “decrypting with a new key”, which would silently break data recovery.
§Ciphertext format
The encrypted output is an ASCII string with Base64 URL-safe encoding (no padding).
Current format:
v1:nonce_b64:ciphertext_b64Legacy format (still accepted):
nonce_b64:ciphertext_b64Where:
nonce_b64is a 12-byte nonce (AES-GCM standard)ciphertext_b64is the encrypted payload including the GCM authentication tag
§AAD (Associated Authenticated Data)
All encryption/decryption uses a fixed AAD value:
keycrypt:crypto:v1This provides domain separation / protocol binding. If the AAD changes, old ciphertext will no longer decrypt successfully.
§Error handling
This crate returns CryptoError for all failures, including:
CryptoError::KeyNotSetif the key is missing during decryptionCryptoError::InvalidFormatif the ciphertext string format is invalidCryptoError::InvalidNonceif the nonce length is wrongCryptoError::DecryptionFailedif authentication fails (wrong key or tampered data)
Decryption errors are intentionally generic to avoid leaking details.
§Security notes
- AES-GCM provides confidentiality + integrity, but it does not hide metadata such as ciphertext length.
- The key is stored in the OS keychain, but OS security policies vary.
- Keys and decrypted plaintext are held in memory using
zeroize::Zeroizingto reduce linger time.
If you need key rotation, multi-key decrypt fallback, or remote KMS support, build that logic above this crate.
Enums§
- Crypto
Error - Errors returned by this crate.
Functions§
- decrypt
- Decrypt an encoded ciphertext string into a UTF-8
String. - decrypt_
bytes - Decrypt an encoded ciphertext string into raw bytes.
- encrypt
- Encrypt a UTF-8 string and return a versioned ASCII ciphertext string.
- encrypt_
bytes - Encrypt raw bytes and return a versioned ASCII ciphertext string.
- has_
keychain_ key - Check whether the master key exists in the OS keychain.
- init_
keychain_ key - Ensure a master key exists in the OS keychain.