This guide is intended for:
- Backend Developers integrating encryption or password hashing into applications.
- Infrastructure Engineers configuring the runtime environment and secrets.
- Security Reviewers auditing the usage and boundaries of cryptographic operations.
Consumers are expected to interact with this module primarily through the DX Layer (CryptoProvider).
The underlying modules (KeyRotation, HKDF, Reversible) are designed to be composed together, but direct usage is reserved for advanced custom integrations.
Cryptographic services are consumed, not extended. You should not subclass the core services or attempt to override their behavior.
The Password pipeline is strictly for one-way hashing of authentication credentials.
Conceptual Flow:
- Retrieve the
PasswordServicevia Dependency Injection. - Hash a plain text password for storage.
- Verify a plain text password against a stored hash.
- Check if a stored hash needs rehashing (e.g., after policy updates).
use Maatify\Crypto\Password\PasswordHasherInterface;
class AuthLoginService
{
public function __construct(private PasswordHasherInterface $passwordHasher) {}
public function verifyLogin(string $inputPassword, string $storedHash): bool
{
$isValid = $this->passwordHasher->verify($inputPassword, $storedHash);
if ($isValid && $this->passwordHasher->needsRehash($storedHash)) {
$newHash = $this->passwordHasher->hash($inputPassword);
// Update storage with $newHash
}
return $isValid;
}
}Constraints:
- Never encrypt passwords. Always hash them.
- Do not make assumptions about the hash format (it is an opaque string).
This is the standard method for encrypting data. It ensures that different parts of your application use different encryption keys, derived from your master root keys.
Context Strings:
You must define explicit, versioned context strings.
Examples: user:email:v1, payment:card:v1, audit:log:v2.
Why HKDF?
If the user:email:v1 key is compromised, the payment:card:v1 data remains secure.
Usage:
use Maatify\Crypto\DX\CryptoProvider;
use Maatify\Crypto\DTO\EncryptedDataDTO;
class UserEmailCryptoService
{
public function __construct(private CryptoProvider $cryptoProvider) {}
public function encryptEmail(string $emailAddress): EncryptedDataDTO
{
// 1. Obtain an encrypter for a specific context
$encrypter = $this->cryptoProvider->context('user:email:v1');
// 2. Encrypt data
return $encrypter->encrypt($emailAddress);
}
public function decryptEmail(EncryptedDataDTO $encryptedData): string
{
$encrypter = $this->cryptoProvider->context('user:email:v1');
return $encrypter->decrypt($encryptedData);
}
}Note: The result of encryption is an object containing the ciphertext, IV, tag, and key ID. You must store all these components to decrypt successfully.
Direct encryption uses the Root Keys directly without HKDF derivation. This bypasses domain separation.
Risks:
- A key compromise affects ALL data encrypted with that key.
- No granular isolation between features.
Acceptable Use Cases:
- System-internal blobs where context is impossible to define.
- Interop with legacy systems (if strictly necessary).
$encrypter = $cryptoProvider->direct();
$encryptedResult = $encrypter->encrypt($data);The CryptoProvider is an optional facade. It exists to prevent "wiring fatigue" and to ensure that HKDF is correctly applied in the standard pipeline.
If you are extracting this library and do not wish to use the DX layer, you must manually wire:
KeyRotationService → HKDFService → ReversibleCryptoAlgorithmRegistry → ReversibleCryptoService.
The Cryptography Module is stateless and environment-agnostic. It does not know how to load secrets. It relies entirely on the host application to provide them via Dependency Injection (DTOs or Providers).
- Host Application: Responsible for reading environment variables (
.env,$_ENV,getenv()) and passing them to the module. - Crypto Module: Receives secrets as PHP strings/objects. It never reads the environment directly.
You typically need to provide:
- Root Keys: A JSON or array structure containing versioned keys for
KeyRotation. - Password Pepper: A high-entropy random string for the
Passwordmodule.
The host application is free to use $_ENV, getenv(), or any configuration loader.
Example: Reading from $_ENV
// In your App's Bootstrap or Config Service
$rootKeysJson = $_ENV['APP_ROOT_KEYS'] ?? null;
$passwordPepper = $_ENV['APP_PASSWORD_PEPPER'] ?? null;
if (!$rootKeysJson || !$passwordPepper) {
throw new RuntimeException("Critical security configuration missing.");
}Example: Reading via getenv()
$rootKeysJson = getenv('APP_ROOT_KEYS');
$passwordPepper = getenv('APP_PASSWORD_PEPPER');- Fail-Closed: If secrets are missing or empty, the application MUST halt. The Crypto module will throw exceptions if initialized with empty keys.
- Treat as Secrets: These values are the keys to your kingdom. Never log them. Never expose them in debug dumps.
- No Defaults: There are NO default keys. The module does not fallback to "development" keys.
- Immutable: The Crypto module does not modify these values. It uses them in memory only.
- Build-Time: Configuration (e.g., choosing
AES-256-GCMvsChaCha20) can be hardcoded or config-driven. - Runtime: Secrets (Keys, Peppers) must be injected at runtime. They should never be committed to source control.
Summary: The Crypto Module is a passive consumer of secrets. You must feed it securely.