Skip to content

feat: Add PSR-4 implementation with modern authenticated encryption#2

Open
ralflang wants to merge 5 commits intoFRAMEWORK_6_0from
feat/secret-psr4-modernization
Open

feat: Add PSR-4 implementation with modern authenticated encryption#2
ralflang wants to merge 5 commits intoFRAMEWORK_6_0from
feat/secret-psr4-modernization

Conversation

@ralflang
Copy link
Member

@ralflang ralflang commented Mar 3, 2026

Modernizes Horde_Secret with dual-stack architecture (PSR-0 + PSR-4).

Key features:

  • Modern authenticated encryption (Libsodium XSalsa20-Poly1305, AES-256-GCM)
  • 100% backward compatible (zero breaking changes)
  • Automatic legacy data detection
  • 111 tests, 213 assertions (all pass)
  • Complete documentation (README, UPGRADING guide)

Stats: 1,523 lines implementation, 1,707 lines tests, 831 lines docs.

See README.md and UPGRADING.md for details.

ralflang added 5 commits March 3, 2026 10:41
- Update phpunit.xml.dist to PHPUnit 11.5 schema
- Reorganize tests to test/Unit/ structure
- Convert tests to use PHPUnit attributes (#[CoversClass], etc.)
- Replace deprecated annotations with modern attributes
- Add strict_types declarations to all test files
- Update test/bootstrap.php with modern autoloading
- Add comprehensive test coverage for encryption/decryption
- Update .gitignore for PHPUnit cache and Composer artifacts
- Update composer.json: PHP ^8.1 requirement, PHPUnit ^11.5

Test results:
- 11 tests, 15 assertions: ✅ ALL PASS
- PHP 8.2.30: ✅ PASS
- PHP 8.4.18: ✅ PASS
- Only 1 deprecation in vendor code (horde/crypt_blowfish)
- Zero deprecations in Secret library code
Implement complete PSR-4 architecture with three cipher backends:

Cipher Implementations:
- SodiumCipher (0x02): XSalsa20-Poly1305 via libsodium (PRIMARY)
  * 256-bit keys, 192-bit nonces, authenticated encryption
  * Constant-time operations, recommended for new deployments
- AesGcmCipher (0x03): AES-256-GCM via OpenSSL (FALLBACK)
  * 256-bit keys, 96-bit nonces, AEAD mode
  * Hardware acceleration (AES-NI), FIPS 140-2 compliant
- BlowfishCipher (0x01): Legacy Blowfish adapter (BACKWARD COMPAT)
  * Wraps Horde_Crypt_Blowfish for old encrypted data
  * No authenticated encryption, ECB mode, 56-byte key limit

Core Components:
- CipherInterface: Contract for all cipher implementations
- EncryptedData: Immutable value object with format header
  * Magic header 'HS' + version byte + payload
  * Base64/Base64URL encoding support
  * Collision probability: 0.0015% (1/65536)
- SecretManager: Main facade with factory methods
  * Automatic cipher selection (Sodium preferred)
  * Format detection: magic header vs legacy
  * Transparent decryption of old Blowfish data
  * needsReEncryption() helper for gradual migration

Exception Hierarchy:
- SecretException (base)
- InvalidKeyException (key validation errors)
- EncryptionException (encryption failures)
- DecryptionException (decryption/auth failures)
- UnsupportedCipherException (missing extensions)

Format Specification:
- New format: ['H']['S'][version:1byte][nonce+ciphertext+tag]
- Legacy format: [raw Blowfish ciphertext] (no header)
- Version 0x02: Sodium (24-byte nonce + ciphertext + 16-byte tag)
- Version 0x03: AES-GCM (12-byte nonce + ciphertext + 16-byte tag)
- Version 0x01: Blowfish with header (future use)

Features:
- Automatic cipher negotiation and fallback
- Gradual migration path from Blowfish to modern ciphers
- Type-safe with PHP 8.1+ readonly properties
- Strict types throughout (declare(strict_types=1))
- PSR-4 autoloading

Security:
- Authenticated encryption prevents tampering
- Random nonce generation (cryptographically secure)
- Constant-time operations (Sodium)
- Key length validation
- Empty plaintext protection
Implement extensive test coverage for all PSR-4 components:

Test Files Created:
- EncryptedDataTest.php (24 tests):
  * Magic header format validation
  * Version byte handling
  * Base64/Base64URL encoding/decoding
  * Round-trip serialization
  * Binary payload support
  * All version bytes (0x01-0xFF)

- SodiumCipherTest.php (20 tests):
  * XSalsa20-Poly1305 encryption/decryption
  * Key validation (32 bytes required)
  * Authentication tag verification
  * Tampering detection
  * Nonce uniqueness
  * Binary and Unicode support
  * Large data handling

- AesGcmCipherTest.php (17 tests):
  * AES-256-GCM authenticated encryption
  * Key length validation
  * Wrong key detection
  * Tampering protection
  * Nonce structure (12 bytes)
  * Large file support

- BlowfishCipherTest.php (11 tests):
  * Legacy Blowfish compatibility
  * Key truncation (56-byte limit)
  * Empty string handling (PSR-0 compat)
  * Horde_Secret integration
  * No nonce/tag (ECB mode)

- SecretManagerTest.php (21 tests):
  * Automatic cipher selection
  * Factory methods (Sodium, AES-GCM, Blowfish)
  * Round-trip encryption
  * Format detection (header vs legacy)
  * needsReEncryption() helper

**Migration & Upgrade Tests:**
  * testDecryptLegacyBlowfishData: Decrypt PSR-0 Horde_Secret data
  * testMigrationScenarioLegacyToModern: Full Blowfish→Sodium migration
  * testMigrationScenarioCrossAlgorithm: AES-GCM→Sodium migration
  * testBatchMigrationSimulation: Database migration simulation
  * testLazyMigrationPattern: On-demand re-encryption pattern

Key Derivation:
- Added HKDF-based key derivation for variable-length keys
- Ensures 32-byte keys for modern ciphers (Sodium, AES-GCM)
- Preserves original key for Blowfish (legacy compatibility)
- Uses hash_hkdf('sha256') with 'horde-secret-v1' context

Legacy Compatibility:
- Validates legacy ciphertext format (min 8 bytes)
- Graceful fallback to Blowfish for data without header
- Tests verify PSR-0 Horde_Secret interoperability

Test Results:
- 104 tests, 185 assertions
- ✅ ALL PASS
- Only 1 deprecation (vendor horde/crypt_blowfish)
- Zero warnings in Secret library code
- Tested on PHP 8.2 and 8.4

Coverage:
- All cipher implementations
- All exception paths
- Format detection edge cases
- Cross-algorithm compatibility
- Real-world migration scenarios
Update copyright years in PSR-0 implementation and add comprehensive
integration tests verifying PSR-0 and PSR-4 can coexist.

Changes:
- Update copyright to 2026 in lib/Horde/Secret.php and Exception.php
- Add documentation to PSR-0 Exception about BC maintenance
- Add 7 integration tests verifying interoperability

PSR-0 API remains completely unchanged - drop-in compatible upgrade.

Test results: 111 tests, 213 assertions, all pass
Add comprehensive documentation for PSR-4 modernization and dual-stack
architecture.

Changes:
- Add README.md with quick start, usage examples, migration guide
- Add UPGRADING.md with detailed migration instructions and FAQ
- Add doc/changelog.yml with version history
- Update .horde.yml to version 3.0.0alpha6 with enhanced description
- Update composer.json with keywords and suggest extensions
- Remove horde/support dependency (not used in PSR-4)

Documentation covers:
- Dual-stack architecture (PSR-0 + PSR-4)
- Backward compatibility guarantees (100% drop-in)
- Migration patterns (lazy, batch, cross-algorithm)
- Security considerations (AEAD vs Blowfish ECB)
- Cipher selection and availability checks
- Error handling and troubleshooting
- Performance benchmarking
- FAQ and getting help

Test status: 111 tests, 213 assertions, all pass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant