# Business rules — encryptionatrestphp

Evidence date: 2026-10-15. Revision: `main` `9b3df5c`.

No business-rules document exists in the source repository. Rules below describe behaviour enforced by `EncryptionAtRest` or stated as operator guidance in project docs. Every rule is labelled **Inferred**, **Documented**, or **Implemented** as appropriate. These are not a confirmed organisational policy unless a human confirms them.

This library has no users, roles, or permissions of its own. Access control is entirely the consuming application’s responsibility.

## Cryptographic algorithm and parameters

**BR-001 — Cipher is fixed to AES-256-GCM.**  
Constant `CIPHER = 'aes-256-gcm'`. Callers cannot select another algorithm through the public API. **Implemented.**

**BR-002 — Key length must be exactly 32 bytes.**  
`validateKey` rejects any other length with `InvalidArgumentException`. **Implemented.** Matches AES-256. **Documented** in README.

**BR-003 — IV length is 12 bytes; GCM tag length is 16 bytes.**  
Constants `IV_LENGTH` / `TAG_LENGTH`. IV is random per `encrypt` call. **Implemented.**

**BR-004 — Associated authenticated data (AAD) is always empty.**  
`openssl_encrypt` is called with `''` as AAD. Callers cannot pass AAD. **Inferred** from implementation.

**BR-005 — Wire format is `base64(IV || tag || ciphertext)`.**  
**Implemented.** **Documented** as “base64 encoded” output; byte layout is **Inferred** from code (not spelled out in README).

## Input validation

**BR-006 — Plaintext for encrypt must not be PHP-empty.**  
`if (empty($data))` throws `InvalidArgumentException('Data cannot be empty')`. Under PHP semantics this also rejects `"0"`, `0`, `0.0`, `"0"`, empty array (type-hinted string only), etc., for string `"0"`. **Implemented.** Whether rejecting `"0"` is intentional product policy is **Unknown / Requires Confirmation**.

**BR-007 — Ciphertext for decrypt must not be PHP-empty.**  
Same `empty()` check with message `Encrypted data cannot be empty`. **Implemented.**

**BR-008 — Ciphertext must be valid base64 (strict decode).**  
`base64_decode(..., true)` failure → `InvalidArgumentException('Invalid base64 data')`. **Implemented.**

**BR-009 — Decoded ciphertext must be at least IV+tag bytes long.**  
Shorter payloads → `InvalidArgumentException('Encrypted data too short')`. **Implemented.**

**BR-010 — Import key must be valid base64 and decode to 32 bytes.**  
Invalid base64 → `InvalidArgumentException('Invalid base64 key')`; wrong length → key-length exception. **Implemented.**

## Key handling guidance (caller responsibility)

**BR-011 — Do not commit encryption keys to version control.**  
**Documented** in README Security Considerations.

**BR-012 — Prefer environment variables or a secure KMS for key storage.**  
**Documented.** Not enforced by code.

**BR-013 — Use different keys per environment.**  
**Documented** best practice. Not enforced.

**BR-014 — Rotate keys in production.**  
**Documented** advice. No rotation API in the class. **Inferred:** rotation requires re-encrypting data outside this library.

**BR-015 — Never log plaintext or keys; logging operations without data is advised.**  
**Documented.** Library does not implement logging.

## Field / data selection guidance

**BR-016 — Encrypt sensitive fields; leave fields needed for search/sort unencrypted.**  
**Documented** in `SENSITIVE_DATA_GUIDE.md` (e.g. encrypt email/SSN; keep username searchable). Not enforced by the library — callers choose fields.

**BR-017 — Passwords should use password hashing for authentication, not only this library.**  
Registration example in the sensitive-data guide uses `password_hash` for `password_hash` field while encrypting other PII. **Documented** example pattern. Whether all consumers must follow it is **Requires Confirmation**.

**BR-018 — Library accepts any non-empty string payload (including binary-as-string).**  
Tests encrypt binary, JSON, Unicode. **Implemented.** Domain “what must be encrypted” is application policy, not library rule.

## Error and failure behaviour

**BR-019 — OpenSSL encrypt failure throws generic `Exception` with OpenSSL error string.**  
**Implemented.**

**BR-020 — OpenSSL decrypt failure (wrong key, tampered data, bad tag) throws `Exception`.**  
**Implemented.** Authentication failure and wrong key are not distinguished to the caller beyond decrypt failure.

**BR-021 — Key generation failure throws `Exception`.**  
`generateKey` checks `random_bytes` result (defensive; `random_bytes` normally throws on failure in modern PHP). **Implemented.**

## Demo and example behaviour (not production product rules)

**BR-022 — Browser demo creates a new key on every POST.**  
`index.php` instantiates `new EncryptionAtRest()` per request. Prior ciphertext cannot be decrypted later with that page alone. **Inferred** from implementation.

**BR-023 — Framework example `getUser` does not round-trip `saveUser` ciphertext.**  
Hard-coded placeholder strings are passed to `decrypt`. **Inferred** defect in the example, not a business rule to adopt.

## Permissions and multi-tenancy

**BR-024 — No authentication or authorisation inside the library.**  
Whoever holds the PHP object and the key can encrypt/decrypt. **Inferred.**

**BR-025 — No built-in tenant isolation.**  
A single instance holds one key. Multi-tenant keying is the host application’s concern. **Inferred.**

## Compliance claims

**BR-026 — Guide asserts GDPR / HIPAA / PCI DSS alignment.**  
**Documented** in `SENSITIVE_DATA_GUIDE.md` as checkmark lists. These are not machine-enforced rules and are **not** treated here as verified certifications. Formal status: **Unknown / Requires Confirmation**.

## Summary table

| ID | Rule (short) | Provenance |
|---|---|---|
| BR-001 | AES-256-GCM only | Implemented |
| BR-002 | Key = 32 bytes | Implemented / Documented |
| BR-003 | IV 12 / tag 16 | Implemented |
| BR-004 | Empty AAD | Inferred |
| BR-005 | base64(IV\|\|tag\|\|ct) | Implemented |
| BR-006–010 | Input validation | Implemented |
| BR-011–015 | Key ops guidance | Documented |
| BR-016–018 | Field / payload policy | Documented / Implemented |
| BR-019–021 | Exception behaviour | Implemented |
| BR-022–023 | Demo caveats | Inferred |
| BR-024–025 | No auth / tenancy | Inferred |
| BR-026 | Compliance claims | Documented; unconfirmed |
