Encryption
Info: Modern Cryptography: Uses XChaCha20-Poly1305 AEAD cipher from libsodium, providing both encryption and authentication in a single operation.
Overview
Larafony's encryption system provides:
-
XChaCha20-Poly1305 - Modern AEAD (Authenticated Encryption with Associated Data) cipher
-
libsodium - Industry-standard cryptographic library
-
Automatic Authentication - Prevents tampering with encrypted data
-
Constraint-Driven Validation - Five specialized assertion classes for safe error handling
-
Sensitive Parameter Protection - Uses
#[SensitiveParameter]to redact values from stack traces
Warning: vs Laravel: Laravel uses AES-256-CBC with OpenSSL + separate HMAC. Larafony uses XChaCha20-Poly1305, which is faster, more secure against timing attacks, and combines encryption + authentication in one operation.
Key Generation
Generate Encryption Key (CLI)
The easiest way to generate a secure encryption key:
php bin/console key:generate
This command automatically generates a cryptographically secure 32-byte key using libsodium,
encodes it as base64, and stores it in your .env file as APP_KEY.
Success: Recommended: Use the
key:generatecommand for production. It automatically handles key generation and .env file updates safely.
Generate Key Programmatically
If you need to generate keys in code (for testing or custom scenarios):
use Larafony\Framework\Encryption\KeyGenerator;
$generator = new KeyGenerator();
$key = $generator->generateKey();
// Returns base64-encoded 32-byte key
// Example: base64:Hj3kL9mN2pQ5rS8tU0vW1xY4zA6bC7dE9fG0hI2jK3l=
.env File Format
# .env file (automatically set by key:generate)
APP_KEY=base64:Hj3kL9mN2pQ5rS8tU0vW1xY4zA6bC7dE9fG0hI2jK3l=
Danger: Security Warning: Never commit your
APP_KEYto version control. Keep it secret and rotate it periodically in production.
Basic Encryption
Encrypt Data
use Larafony\Framework\Encryption\EncryptionService;
$encryptor = new EncryptionService();
// Encrypt any value (strings, arrays, objects)
$encrypted = $encryptor->encrypt('secret message');
// Encrypt complex data
$encrypted = $encryptor->encrypt([
'user_id' => 123,
'api_token' => 'abc123xyz',
'expires_at' => '2025-12-31'
]);
Decrypt Data
use Larafony\Framework\Encryption\EncryptionService;
$encryptor = new EncryptionService();
try {
$decrypted = $encryptor->decrypt($encrypted);
// Returns original value
} catch (\InvalidArgumentException $e) {
// Decryption failed - data corrupted or wrong key
// Possible errors:
// - Invalid base64 encoding
// - Data too short
// - Decryption failed (tampered data)
}
How It Works
Encryption Process
-
Key Validation - Validates APP_KEY exists and is 32 bytes when decoded
-
Serialization - Converts value to string using PHP's serialize()
-
Nonce Generation - Creates random 24-byte nonce for this encryption
-
AEAD Encryption - Uses XChaCha20-Poly1305 to encrypt + authenticate in one step
-
Combine & Encode - Merges nonce + ciphertext, encodes as base64
// Internal process (simplified):
$nonce = random_bytes(24); // Unique per encryption
$ciphertext = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt(
serialize($value),
'', // No additional data
$nonce,
$this->key
);
return base64_encode($nonce . $ciphertext);
Decryption Process
-
Base64 Decode - Converts base64 string to binary
-
Extract Nonce - First 24 bytes are the nonce
-
Extract Ciphertext - Remaining bytes are encrypted data
-
AEAD Decryption - Decrypts and verifies authentication tag
-
Unserialize - Converts string back to original PHP value
Constraint Assertions
Larafony uses specialized assertion classes following Single Responsibility Principle. Each validates one specific constraint:
Available Assertions
use Larafony\Framework\Encryption\Assert\EncryptionKeyExists;
use Larafony\Framework\Encryption\Assert\KeyLengthIsValid;
use Larafony\Framework\Encryption\Assert\Base64IsValid;
use Larafony\Framework\Encryption\Assert\DataLengthIsValid;
use Larafony\Framework\Encryption\Assert\DecryptionSucceeded;
// 1. Validates encryption key exists
EncryptionKeyExists::assert($key);
// Throws: RuntimeException if null
// 2. Validates key length (32 bytes for XChaCha20)
KeyLengthIsValid::assert($decodedKey, 32);
// Throws: InvalidArgumentException if wrong length
// 3. Validates base64 decoding
$decoded = base64_decode($encrypted, true);
Base64IsValid::assert($decoded);
// Throws: InvalidArgumentException if invalid base64
// 4. Validates minimum data length
DataLengthIsValid::assert($decoded, 24);
// Throws: InvalidArgumentException if too short
// 5. Validates decryption success
$decrypted = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt(...);
DecryptionSucceeded::assert($decrypted);
// Throws: InvalidArgumentException if decryption failed
PHPStan Support
Assertion classes include @phpstan-assert annotations for static analysis type narrowing:
/**
* @param string|false $decoded
* @phpstan-assert string $decoded
*/
public static function assert(string|false $decoded): void
{
if ($decoded === false) {
throw new InvalidArgumentException('Invalid base64 encoding');
}
}
// After this assertion, PHPStan knows $decoded is string, not string|false
Security Features
Sensitive Parameter Protection
The encrypt() method uses PHP 8.2+ #[SensitiveParameter] attribute:
public function encrypt(#[\SensitiveParameter] mixed $value): string
{
// If exception occurs, $value won't appear in stack traces
}
AEAD: Encryption + Authentication
Unlike older ciphers that require separate HMAC, XChaCha20-Poly1305 provides:
-
Confidentiality - Data is encrypted and unreadable
-
Authentication - Detects any tampering with encrypted data
-
Performance - Single operation is faster than encrypt + HMAC
-
Timing Safety - Resistant to timing side-channel attacks
Comparison with Other Frameworks
| Feature | Larafony | Laravel | Symfony |
|---|---|---|---|
| Cipher | XChaCha20-Poly1305 | AES-256-CBC | Varies (bundles) |
| Library | libsodium | OpenSSL | libsodium (secrets) |
| Authentication | Built-in (AEAD) | Separate HMAC | Varies |
| Performance | Fast (single op) | Slower (two ops) | Varies |
| Timing Attacks | Resistant | More vulnerable | Varies |
| Key Management | Symmetric (APP_KEY) | Symmetric (APP_KEY) | Asymmetric (pub/priv)
Use CasesEncrypt Sensitive Configuration
Encrypt User Data
Best Practices
Next Steps
Learn MoreThis implementation is explained in detail with step-by-step tutorials, tests, and best practices at masterphp.eu Demo App: See encryption in action with automatic cookie and session encryption. The demo application showcases XChaCha20-Poly1305 AEAD cipher with constraint-driven validation and PHPStan type narrowing. |