Skip to content

Commit 168eee1

Browse files
committed
docs(security): add security audit and improve crypto usage documentation
- add SECURITY_AUDIT.md with full pre-release security review - expand SECURITY.md with cryptographic guarantees and safe usage guidelines - clarify CryptoProvider context() vs direct() usage - add explicit warning for direct() pipeline No runtime or cryptographic changes. Pre-release security hardening for v1.0.0.
1 parent ecec75e commit 168eee1

3 files changed

Lines changed: 209 additions & 24 deletions

File tree

SECURITY.md

Lines changed: 119 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,134 @@
22

33
## Supported Versions
44

5-
Currently, only the latest major version of the Crypto module receives security updates.
5+
Only the latest major version of **maatify/crypto** receives security updates.
66

7-
| Version | Supported |
8-
| ------- | ------------------ |
9-
| 1.x | :white_check_mark: |
7+
| Version | Supported |
8+
| ------- | ---------- |
9+
| 1.x ||
10+
11+
---
1012

1113
## Cryptographic Guarantees
1214

13-
This module provides the following strict cryptographic guarantees:
15+
The library provides the following strict cryptographic guarantees:
16+
17+
1. **Authenticated Encryption (AEAD)**
18+
All reversible encryption uses modern AEAD algorithms.
19+
The default implementation uses **AES-256-GCM**, ensuring confidentiality and integrity.
20+
Any modification of ciphertext or metadata is detected during decryption.
21+
22+
2. **Domain Separation (HKDF)**
23+
Context-based encryption derives unique keys using **HKDF (RFC 5869)**.
24+
Each context (e.g. `auth:session:v1`) produces an independent encryption key, preventing cross-domain compromise.
25+
26+
3. **Secure Password Hashing**
27+
Passwords are hashed using **Argon2id**.
28+
The system supports an optional but highly recommended **global pepper** applied via HMAC-SHA256 before hashing.
29+
30+
4. **Fail-Closed Design**
31+
Any cryptographic failure results in an **immediate exception**.
32+
The library never:
33+
- falls back to weaker algorithms
34+
- silently ignores failures
35+
- returns partially decrypted data
36+
37+
5. **Key Rotation Safety**
38+
The system supports multiple keys with lifecycle states:
39+
- **ACTIVE** – used for encryption
40+
- **INACTIVE** – preserved for decryption
41+
- **RETIRED** – legacy decryption only
42+
43+
Exactly one key must always be **ACTIVE**.
44+
45+
---
46+
47+
## Safe Usage Guidelines
48+
49+
### Protect Your Root Keys and Peppers
50+
51+
The security of this library depends entirely on the secrecy of:
52+
53+
- encryption root keys
54+
- password peppers
55+
56+
Never hardcode them in source code.
57+
58+
Use secure storage mechanisms such as:
59+
60+
- environment variables
61+
- secret managers
62+
- vault systems
63+
64+
---
65+
66+
### Prefer Context-Based Encryption
67+
68+
Always prefer:
69+
70+
```php
71+
$crypto->context("domain:entity:v1");
72+
````
1473

15-
1. **Authenticated Encryption (AEAD):** All reversible encryption uses AEAD algorithms (e.g., XChaCha20-Poly1305 or AES-256-GCM). Data cannot be tampered with without detection.
16-
2. **Domain Separation (HKDF):** Context-based encryption derives unique, independent keys for different domains (contexts). A compromised key in one domain does not compromise data in another.
17-
3. **Secure Password Hashing:** Passwords are hashed using state-of-the-art algorithms (Argon2id) and support an optional, highly recommended global pepper (HMAC-SHA256) to mitigate offline attacks.
18-
4. **Fail-Closed Design:** Any cryptographic failure (e.g., missing keys, invalid tags, malformed data, unsupported algorithms) results in an immediate exception. The module will never fall back to an insecure state or return partial/corrupted data.
19-
5. **Seamless Key Rotation:** The system supports multiple keys with distinct states (Active, Inactive, Retired) to allow seamless decryption of legacy data while ensuring new data is always encrypted with the current Active key.
74+
This ensures proper **HKDF domain separation**.
2075

21-
## Safe Usage Warnings
76+
Direct encryption:
2277

23-
- **Protect Your Root Keys and Peppers:** The security of this module depends entirely on the secrecy of your injected root keys and password peppers. Do not hardcode them. Store them securely (e.g., in a secrets manager or environment variables) and inject them at runtime.
24-
- **Use Contexts (`cryptoProvider->context(...)`) over Direct Encryption:** Always prefer context-based encryption to ensure proper domain separation. Only use direct encryption (`cryptoProvider->direct(...)`) if you have a specific, justifiable reason to bypass HKDF.
25-
- **Do Not Ignore Exceptions:** Ensure your application handles exceptions thrown by the Crypto module gracefully. A thrown exception indicates a serious security condition (e.g., attempted tampering, missing key).
26-
- **Do Not Modify Core Algorithms:** The core algorithms and primitives are intentionally locked down. Do not attempt to bypass them or introduce custom, unverified cryptographic logic.
78+
```php
79+
$crypto->direct();
80+
```
81+
82+
bypasses HKDF and should only be used for:
83+
84+
* infrastructure secrets
85+
* internal system encryption
86+
* controlled environments where domain separation is unnecessary
87+
88+
---
89+
90+
### Always Handle Exceptions
91+
92+
Cryptographic operations may throw exceptions when:
93+
94+
* ciphertext is corrupted
95+
* authentication tags fail
96+
* keys are missing
97+
* metadata is invalid
98+
99+
These exceptions indicate **security-relevant conditions** and must never be ignored.
100+
101+
---
102+
103+
### Do Not Modify Cryptographic Primitives
104+
105+
The algorithms and primitives in this library are intentionally restricted.
106+
107+
Do **not**:
108+
109+
* replace algorithms
110+
* add fallback ciphers
111+
* inject custom crypto logic
112+
113+
Doing so may introduce severe vulnerabilities.
114+
115+
---
27116

28117
## Reporting a Vulnerability
29118

30-
If you discover a security vulnerability within this module, please report it immediately.
119+
If you discover a security vulnerability in **maatify/crypto**, please report it responsibly.
120+
121+
**Do not open a public GitHub issue.**
122+
123+
Instead, send a report to:
124+
125+
```
126+
security@maatify.com
127+
```
128+
129+
Please include:
31130

32-
**Do not open a public issue.**
131+
* description of the vulnerability
132+
* steps to reproduce
133+
* affected versions
33134

34-
Please send an email to the security contact for this repository. We will acknowledge receipt of your vulnerability report and strive to send you regular updates about our progress. If you do not receive a response within 48 hours, please follow up.
135+
We will acknowledge receipt within **48 hours** and work with you to resolve the issue responsibly.

SECURITY_AUDIT.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# SECURITY_AUDIT.md
2+
3+
## 1. Executive Summary
4+
5+
This document presents the findings of a comprehensive, read-only security audit of the `maatify/crypto` library prior to its v1.0.0 release. The audit focused on evaluating the cryptographic primitives, architectural isolation, fail-closed enforcement, and API design. The library demonstrates a highly secure, well-structured, and defense-in-depth approach to cryptography. Only minor, low-severity API risks were identified, and no critical or high-severity vulnerabilities exist. The library is secure and ready for public release.
6+
7+
## 2. Audit Scope
8+
9+
The scope of this audit covers the entire `src/` directory, specifically focusing on:
10+
11+
- Cryptographic Primitive Safety (AES-GCM implementations)
12+
- HKDF Domain Separation
13+
- Password Hashing Security
14+
- Key Rotation Safety
15+
- Encryption / Decryption Safety
16+
- API Misuse Risk (DX module)
17+
- Randomness & Entropy
18+
- Fail-Closed Behavior
19+
- Dependency Review
20+
- Test Coverage Adequacy
21+
22+
All actions performed during this audit were strictly read-only.
23+
24+
## 3. Security Findings
25+
26+
No critical or high-severity issues were found. The codebase implements rigorous controls and strong isolation.
27+
28+
**Finding 3.1 - Misuse potential of `direct()` encryption pipeline**
29+
- **Severity:** Low
30+
- **Description:** The `CryptoProvider` exposes a `direct()` method which bypasses HKDF domain separation, using root keys directly for encryption via `CryptoDirectFactory`. While this is explicitly documented as an advanced pipeline meant for internal secrets, inexperienced developers might use `direct()` instead of `context()` out of convenience, leading to a lack of domain separation.
31+
- **Impact:** If `direct()` is misused across different domains, compromising a key in one domain could allow decrypting data in another.
32+
33+
## 4. Verified Safe Design Decisions
34+
35+
The library employs several excellent, verifiable security decisions:
36+
37+
- **Cryptographic Primitives:** The default and recommended algorithm is AES-256-GCM (AEAD cipher). The implementation uses the correct constraints: 256-bit (32 bytes) key length, 96-bit (12 bytes) IV length generated safely via `random_bytes()`, and requires a 128-bit (16 bytes) authentication tag. Weak modes like ECB are explicitly absent. `OPENSSL_RAW_DATA` is used correctly.
38+
- **Domain Separation (HKDF):** The library uses standard RFC 5869 HMAC-SHA256 for key derivation. Contexts must be explicitly versioned (e.g., `:v1`), non-empty, and limited in length, ensuring tight domain boundaries.
39+
- **Password Hashing:** Implements a robust pipeline of HMAC-SHA256 (Pepper) followed by Argon2id. The pepper is required, missing peppers throw exceptions immediately, and constant-time `password_verify` is used.
40+
- **Key Rotation Invariants:** Enforced perfectly via `StrictSingleActiveKeyPolicy`. The system strictly validates that only *exactly one* ACTIVE key exists for encryption, while INACTIVE and RETIRED keys are preserved correctly for decryption.
41+
- **Fail-Closed Principle:** Exception handling is flawless. No operation falls back to weaker algorithms, returns false silently, or gracefully degrades. OpenSSL false returns, missing metadata, invalid lengths, and unregistered algorithms immediately throw explicit exceptions.
42+
- **RNG:** Predictable RNG functions (`rand`, `mt_rand`) are entirely absent. Entropy relies strictly on `random_bytes()`.
43+
- **Dependencies:** `composer.json` is clean, enforcing PHP `^8.2` and the necessary extensions (`ext-openssl`, `ext-sodium`), without risky runtime dependencies.
44+
45+
## 5. Potential Risks (if any)
46+
47+
- **Developer Convenience vs Security:** As noted in finding 3.1, providing the `direct()` method poses a minor risk if developers bypass domain separation when they shouldn't.
48+
49+
## 6. Recommendations
50+
51+
- **Documentation & DX Warnings:** Continue emphasizing in the documentation the dangers of `direct()` versus `context()`. Consider adding a PHPDoc `@warning` annotation to the `direct()` method in `CryptoProvider` to ensure IDEs highlight the risk of bypassing domain separation.
52+
53+
## 7. Final Security Assessment
54+
55+
The `maatify/crypto` library exhibits exceptional code quality and security posture. It successfully adheres to its stated goals of providing strict, decoupled, fail-closed, and isolated cryptographic primitives. The architecture perfectly separates key lifecycle management from the encryption routines.
56+
57+
**Conclusion:** The library is highly secure and is approved for the v1.0.0 release.

src/DX/CryptoProvider.php

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
* 1. Context-based encryption (HKDF Pipeline)
1616
* 2. Direct encryption (No-HKDF Pipeline)
1717
*
18+
* The recommended default for application-level encryption is `context()`.
19+
* It enforces HKDF-based domain separation.
20+
*
1821
* @internal This is a DX helper.
1922
*/
2023
final readonly class CryptoProvider
@@ -27,11 +30,19 @@ public function __construct(
2730

2831
/**
2932
* Get a crypto service bound to a specific context.
30-
* Uses HKDF to derive keys from the root rotation keys.
3133
*
32-
* Pipeline: KeyRotation -> HKDF -> ReversibleCrypto
34+
* Uses HKDF to derive domain-separated encryption keys
35+
* from the root rotation keys.
36+
*
37+
* Pipeline:
38+
* KeyRotation -> HKDF -> ReversibleCrypto
3339
*
34-
* @param string $context Explicit context string (e.g. "notification:email:v1")
40+
* Example contexts:
41+
* - "user:email:v1"
42+
* - "auth:session:v1"
43+
* - "payment:card:v1"
44+
*
45+
* @param string $context Explicit context string (must be versioned)
3546
* @return ReversibleCryptoService
3647
*/
3748
public function context(string $context): ReversibleCryptoService
@@ -40,10 +51,26 @@ public function context(string $context): ReversibleCryptoService
4051
}
4152

4253
/**
43-
* Get a direct crypto service using raw root keys.
44-
* WARNING: Does not provide domain separation.
54+
* Get a crypto service using raw root keys directly.
55+
*
56+
* ⚠ WARNING:
57+
* This pipeline bypasses HKDF domain separation.
58+
*
59+
* Without domain separation, the same root key may be reused
60+
* across different encryption domains, which increases the
61+
* blast radius if a key is ever compromised.
62+
*
63+
* Prefer `context()` for application data encryption.
64+
*
65+
* Only use `direct()` when encrypting:
66+
* - internal system secrets
67+
* - infrastructure-level data
68+
* - environments where domain separation is not required
69+
*
70+
* Pipeline:
71+
* KeyRotation -> ReversibleCrypto
4572
*
46-
* Pipeline: KeyRotation -> ReversibleCrypto
73+
* @warning This method intentionally bypasses HKDF domain separation.
4774
*
4875
* @return ReversibleCryptoService
4976
*/

0 commit comments

Comments
 (0)