t2pcorp/libcrypto
最新稳定版本:v1.8.8
Composer 安装命令:
composer require t2pcorp/libcrypto
包简介
A PHP library for cryptographic operations with KMS integration, Caching(local,redis) and Secret manager.
README 文档
README
PHP implementation of the LibCrypto library, converted from the original Golang version. This library provides secure encryption/decryption functionality with support for multiple key versions, caching, and AWS integration.
Features
- AES-256-GCM Encryption: Secure encryption using industry-standard algorithms
- Key Versioning: Support for multiple key versions with automatic rotation
- Multi-level Caching: Local and Redis caching for improved performance
- AWS Integration: Support for AWS Secrets Manager and KMS
- Mock Support: Complete mock implementations for testing and development
- Thread-Safe: Singleton pattern ensures thread safety
Directory Structure
PHP/
├── Cache/
│ ├── LocalCache/ # In-memory caching implementation
│ └── RedisCache/ # Redis-based caching implementation
├── Encryption/
│ └── LibKMS/ # AWS KMS encryption services
├── LibAWS/ # AWS configuration and utilities
├── LibConfig/ # Configuration management
│ └── MockConfig/ # Mock configuration for testing
├── Stores/
│ ├── SecretManager/ # AWS Secrets Manager integration
│ └── MockSecretManager/ # Mock secret store for testing
├── Types/ # Interface definitions and data structures
├── Utils/ # Utility functions and helpers
├── tests/ # Comprehensive test suite
├── examples.php # Usage examples
├── LibCrypto.php # Main library entry point
└── run.sh # Test runner script
Installation
Prerequisites
- PHP 8.0 or higher
- Required PHP extensions:
openssljsonmbstring
- Composer (for dependency management)
Dependencies
Install required dependencies using Composer:
composer install
AWS Dependencies (Optional)
For AWS integration, you'll need:
- AWS SDK for PHP
- Predis (for Redis support)
These are automatically installed via Composer.
Quick Start
Basic Usage with Mock Store
<?php
require_once 'vendor/autoload.php';
use LibCrypto\CryptoManager;
use LibCrypto\Types\CryptoConfig;
use LibCrypto\Stores\MockSecretManager\KeyStore as MockKeyStore;
// Create configuration
$config = new CryptoConfig([
'LIBC_SM_NAME' => 'T2P-LIBC-DEK-poc',
'LIBC_SM_REGION' => 'ap-southeat-7'
]);
// Use mock store for testing
$mockStore = new MockKeyStore($config);
// Get CryptoManager instance
$result = CryptoManager::getCryptoManager($config, $mockStore);
if ($result[1] !== null) {
throw new Exception('Failed to initialize: ' . $result[1]);
}
$cryptoManager = $result[0];
// Encrypt data
$plaintext = "Hello, World!";
$encryptResult = $cryptoManager->encrypt($plaintext);
if ($encryptResult[1] !== null) {
throw new Exception('Encryption failed: ' . $encryptResult[1]);
}
$encrypted = $encryptResult[0];
echo "Encrypted: " . $encrypted . "\n";
// Decrypt data
$decryptResult = $cryptoManager->decrypt($encrypted);
if ($decryptResult[1] !== null) {
throw new Exception('Decryption failed: ' . $decryptResult[1]);
}
$decrypted = $decryptResult[0];
echo "Decrypted: " . $decrypted . "\n";
Usage with AWS Integration
<?php
use LibCrypto\Stores\SecretManager\KeyStore as RealKeyStore;
// Configure with AWS credentials
$config = new CryptoConfig([
'LIBC_SM_NAME' => 'your-secret-name',
'LIBC_SM_ID' => 'your-aws-access-key',
'LIBC_SM_SECRET' => 'your-aws-secret-key',
'LIBC_SM_REGION' => 'ap-southeat-7',
'LIBC_CACHE_REDISHOST' => 'your-redis-host',
'LIBC_CACHE_REDISPORT' => '6379'
]);
$realStore = new RealKeyStore($config);
$result = CryptoManager::getCryptoManager($config, $realStore);
// ... use as above
Configuration
Environment Variables
The library supports configuration via environment variables:
LIBC_SM_NAME: AWS Secrets Manager secret nameLIBC_SM_ID: AWS Access Key IDLIBC_SM_SECRET: AWS Secret Access KeyLIBC_SM_ROLE: AWS IAM Role ARN (optional)LIBC_SM_REGION: AWS RegionLIBC_CACHE_REDISHOST: Redis hostLIBC_CACHE_REDISPORT: Redis portLIBC_CACHE_REDISPASS: Redis passwordLIBC_CACHE_REDISDB: Redis database numberLIBC_CACHE_REDISTLS: Enable Redis TLS (true/false)
Configuration Object
$config = new CryptoConfig([
'LIBC_SM_NAME' => 'my-secret',
'LIBC_SM_REGION' => 'us-west-2',
// ... other options
]);
API Reference
CryptoManager
Main class for encryption/decryption operations.
Methods
getCryptoManager(CryptoConfig $config, StoreInterface $store = null, ConfigInterface $libConfig = null): array- Returns
[CryptoManager instance, error] - Singleton pattern - returns the same instance across calls
- Returns
encrypt(string $plaintext): array- Returns
[encrypted_string, error] - Encrypts plaintext using the active key
- Returns
decrypt(string $encrypted): array- Returns
[decrypted_string, error] - Decrypts encrypted data
- Returns
getCipherMeta(string $encrypted): array- Returns
[CipherMeta object, error] - Gets metadata about encrypted data
- Returns
clearCache(): ?string- Returns error message or null
- Clears all cached keys and forces refresh
CipherMeta
Contains metadata about encrypted data:
class CipherMeta {
public string $version; // Key version used
public string $algorithm; // Encryption algorithm
public bool $isSupport; // Whether format is supported
}
Testing
Run All Tests
./run.sh
Run Specific Tests
# Basic examples
./run.sh 1
# Comprehensive tests
./run.sh 2
# Performance benchmark
./run.sh 3
# Installation check
./run.sh 4
Manual Testing
# Run examples
php examples.php
# Run test suite
php tests/LibCryptoTest.php
Performance
The library includes performance benchmarking:
use LibCrypto\Tests\PerformanceTest;
$perfTest = new PerformanceTest($cryptoManager);
$perfTest->benchmarkEncryption(1000); // Run 1000 cycles
Typical performance on modern hardware:
- Encryption: ~2-5ms per operation
- Decryption: ~2-5ms per operation
- Throughput: ~200-500 operations/second
Error Handling
The library uses a Go-style error handling pattern where methods return arrays:
[result, null]on success[null, error_message]on failure
$result = $cryptoManager->encrypt($data);
if ($result[1] !== null) {
// Handle error
echo "Error: " . $result[1];
} else {
// Use result
$encrypted = $result[0];
}
Caching
Local Cache
In-memory caching for single-process applications:
- Fast access
- No external dependencies
- Lost on process restart
Redis Cache
Distributed caching for multi-process/multi-server applications:
- Persistent across restarts
- Shared between processes
- Requires Redis server
Cache Timeout
- Default timeout: 20-24 hours (randomized)
- Automatic refresh on timeout
- Manual refresh via
clearCache()
Security Considerations
- Key Storage: Keys are stored securely in memory when possible
- Network Security: Use TLS for Redis connections in production
- AWS Security: Use IAM roles instead of hardcoded credentials when possible
- Memory Safety: Sensitive data is cleared from memory when possible
AWS Credential Caching
When using AWS IAM role assumption (LIBC_SM_ROLE), the library automatically caches credentials:
- Automatic Caching: Temporary credentials are cached in memory by AWS SDK
- Credential Duration: 1 hour (3600 seconds)
- Auto-Refresh: Credentials are automatically refreshed before expiration
- Rate Limiting Prevention: Eliminates excessive AssumeRole API calls
- No Manual Management: Caching and refresh are handled transparently by the SDK
This significantly improves performance and prevents AWS API rate limiting when the same role is used repeatedly.
PHP-FPM Deployment Considerations
Singleton Verification
Want to verify the singleton is working correctly?
Run the verification script:
php examples/verify-singleton-worker.php
This demonstrates that the singleton persists across requests and provides 100-1500x performance improvement.
📖 Full Documentation: See SINGLETON-VERIFICATION.md for complete verification guide, performance benchmarks, and troubleshooting.
Understanding PHP-FPM Worker Behavior
When running with PHP-FPM, it's important to understand how the library behaves:
Singleton Pattern Persistence with Configuration Change Detection
- CryptoManager uses a singleton pattern -
getInstance()returns the same instance for the same configuration - PHP-FPM workers persist - Unlike CGI, workers handle multiple requests
- Static variables survive - The singleton instance lives for the worker's lifetime
- Configuration change detection - Automatically detects when AWS credentials or configuration changes between requests
- Automatic re-initialization - Creates a new instance if configuration differs from cached instance
- Impact:
- First request initializes the CryptoManager
- Subsequent requests with the same config reuse the instance (optimal performance)
- Requests with different config get a new instance (supports multi-tenant scenarios)
AWS Client Caching (Optimized)
The library implements two-level caching for AWS clients:
Client Instance Caching (KeyStore level):
- AWS Secrets Manager client is cached as a static variable
- Reused across multiple
getSecretConfig()calls in the same worker - Automatically invalidated if AWS configuration changes
Credential Caching (AWS SDK level):
- AssumeRoleCredentialProvider caches temporary credentials
- Credentials valid for 1 hour
- Auto-refresh before expiration
Benefits in PHP-FPM
- Reduced AWS API Calls: AssumeRole called once per hour per worker (not per request)
- Better Performance: Client creation overhead eliminated for subsequent requests
- Cost Savings: Fewer AWS API calls = lower costs
- Rate Limit Protection: Prevents hitting AWS AssumeRole rate limits
Best Practices
Configure Appropriate Worker Count:
; php-fpm pool configuration pm = dynamic pm.max_children = 50 pm.start_servers = 5 pm.min_spare_servers = 5 pm.max_spare_servers = 10 pm.max_requests = 500 ; Recycle workers periodicallyWorker Recycling:
- Set
pm.max_requeststo recycle workers periodically - Prevents memory leaks and refreshes cached clients
- Recommended: 500-1000 requests per worker
- Set
Multi-Tenant Considerations:
- Now Supported: Configuration change detection automatically handles different credentials
- When
getInstance()is called with different credentials, a new instance is created - Previous instance is replaced (one active config per worker at a time)
- Best Practices:
- For high-performance single-tenant: Use environment variables (automatic caching)
- For multi-tenant: Pass different configs to
getInstance()per request (automatic switching) - For strict tenant isolation: Use separate PHP-FPM pools per tenant (most secure)
Health Monitoring:
// Check if CryptoManager is initialized $result = CryptoManager::getInstance(); if ($result[1] !== null) { error_log("CryptoManager initialization failed: " . $result[1]); }Graceful Restarts:
# Reload PHP-FPM gracefully to pick up new credentials systemctl reload php-fpm
Clearing Caches
To clear various caches (for testing or credential rotation):
// Option 1: Clear AWS client cache only (keeps CryptoManager instance)
\LibCrypto\Stores\SecretManager\KeyStore::clearClientCache();
// Option 2: Clear CryptoManager's key cache (forces re-fetch from AWS)
[$cryptoManager, $error] = CryptoManager::getInstance();
if ($error === null) {
$cryptoManager->clearCache();
}
// Option 3: Clear CryptoManager singleton instance (forces full re-initialization)
\LibCrypto\CryptoManager::clearInstance();
// Option 4: Clear everything (complete reset)
\LibCrypto\CryptoManager::clearInstance();
\LibCrypto\Stores\SecretManager\KeyStore::clearClientCache();
When to use each option:
- Option 1: When AWS credentials change but CryptoManager config is the same
- Option 2: When secrets in AWS Secrets Manager are updated
- Option 3: When you want to force CryptoManager re-initialization (e.g., testing)
- Option 4: Complete reset, useful for credential rotation or major config changes
Differences from Go Version
Skipped Components
As requested, the following components were not converted:
limitqueuepackage (timeout functionality)- Timeout/refresh queue functionality in config
PHP-Specific Adaptations
- Error Handling: Go-style
[result, error]pattern - Interfaces: PHP interfaces instead of Go interfaces
- Memory Management: PHP garbage collection instead of manual memory management
- Concurrency: Single-threaded execution model
- Dependencies: Composer instead of Go modules
Troubleshooting
Common Issues
Missing PHP Extensions
# Install required extensions (Ubuntu/Debian) sudo apt-get install php-openssl php-json php-mbstringAWS Credentials Not Found
- Set environment variables
- Configure AWS credentials file
- Use IAM roles on EC2
Redis Connection Failed
- Check Redis server is running
- Verify connection parameters
- Check firewall settings
Memory Issues
- Increase PHP memory limit:
ini_set('memory_limit', '512M'); - Use Redis cache for better memory management
- Increase PHP memory limit:
Debug Mode
Enable debug output:
// Add to your code for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
Contributing
- Follow PSR-12 coding standards
- Write tests for new functionality
- Update documentation
- Run the test suite before submitting
License
This PHP conversion maintains the same license as the original Go implementation.
Support
For issues specific to the PHP conversion, please check:
- PHP version compatibility (8.0+)
- Required extensions are installed
- Dependencies are up to date
- Configuration is correct
For general LibCrypto issues, refer to the original Go documentation.
LibCrypto PHP Library
A PHP library for cryptographic operations with KMS integration, Caching (local, Redis), and Secret Manager support.
Installation
You can install this library via Composer:
composer require t2pcorp/libcrypto
Usage
Please see example.php for basic usage.
<?php
require_once __DIR__ . '/vendor/autoload.php';
use LibCrypto\CryptoManager;
try {
// Initialize CryptoManager (assumes environment variables are set for configuration)
[$cryptoManager, $error] = CryptoManager::getCryptoManager();
if ($error !== null) {
throw new \Exception("Failed to initialize CryptoManager: " . $error);
}
$plaintext = "Hello, secure world!";
[$encrypted, $encError] = $cryptoManager->encrypt($plaintext);
if ($encError !== null) {
throw new \Exception("Encryption failed: " . $encError);
}
echo "Encrypted: " . $encrypted . "\n";
[$decrypted, $decError] = $cryptoManager->decrypt($encrypted);
if ($decError !== null) {
throw new \Exception("Decryption failed: " . $decError);
}
echo "Decrypted: " . $decrypted . "\n";
} catch (\Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
Publishing to Packagist
To make this library publicly available via Composer, follow these steps to publish it to Packagist.org:
Ensure your code is in a public Git repository:
- Packagist tracks your Git repository (e.g., on GitHub, GitLab, Bitbucket).
- Make sure your library's code, including the
composer.jsonfile and thesrc/directory, is committed and pushed to a public repository.
Sign up/Log in to Packagist:
- Go to Packagist.org.
- Sign up or log in. Using your GitHub account is often the easiest if your code is hosted there.
Submit Your Package:
- Once logged in, click the "Submit" button.
- In the "Repository URL (Git/Svn/Hg)" field, paste the public URL of your Git repository (e.g.,
https://github.com/your-username/libcrypto.git). - Click "Check". Packagist will attempt to read your
composer.json. - If successful and the package name
t2pcorp/libcryptois available, confirm the submission.
Set up Automatic Updates (Webhook):
- After submission, Packagist will show your package page. It's highly recommended to set up automatic updates.
- Packagist will provide a webhook URL.
- Go to your Git repository settings (e.g., GitHub: Settings -> Webhooks -> Add webhook).
- Paste the Payload URL from Packagist, set Content type to
application/json, and select the "Pushes" or "Push event" trigger.
Versioning with Git Tags:
- Composer relies on Git tags for versioning. To release a new version (e.g.,
v1.0.0):- Commit all changes:
git commit -am "Release v1.0.0" - Create a Git tag:
git tag v1.0.0 - Push the tag:
git push origin v1.0.0(orgit push --tags)
- Commit all changes:
- Packagist will be notified (via webhook) and make the new version available.
- Composer relies on Git tags for versioning. To release a new version (e.g.,
🧩 4. Submit Your Package to Packagist A. On Packagist: Go to: https://packagist.org/packages/submit Paste your GitLab repository URL (e.g.): https://gitlab.com/t2plib/libcrypto.git If it’s public, it should just work.
🔄 5. Set Up Auto-Update (Recommended) To allow Packagist to auto-update when you push a new tag or commit:
Option 1: Use GitLab Webhook Go to your GitLab project → Settings → Webhooks
Add this webhook URL:
perl Copy Edit https://packagist.org/api/update-package?username=YOUR_USERNAME&apiToken=YOUR_API_TOKEN You can get apiToken from your Packagist API page
Set it to trigger on Push events.
This README.md was generated based on the composer.json for t2pcorp/libcrypto.
#if have redis if use same redis host settings in CICD-PIPELINE
export LIBC_CACHE_REDISHOST="localhost"
export LIBC_CACHE_REDISPORT="6379"
export LIBC_CACHE_REDISUSER=
export LIBC_CACHE_REDISPASS=""
export LIBC_CACHE_REDISDB="0"
export LIBC_CACHE_REDISTLS="DISABLED"
export LIBC_CACHE_REDISCERT=
#SET by CICD-Pipeline
export LIBC_SM_NAME=T2P-LIBC-DEK-poc
export LIBC_SM_ID=""
export LIBC_SM_SECRET=""
export LIBC_SM_ROLE=""
export LIBC_SM_REGION="ap-southeast-7"
export AWS_DEFAULT_REGION=ap-southeast-7
export AWS_REGION=ap-southeast-1
export AWS_SECRET_ACCESS_KEY=SSO
export AWS_ACCESS_KEY_ID=SSO
export LOG_LEVEL=debug
Local Wrapping Key (LWK) System
LibCrypto uses a deterministic HKDF-based Local Wrapping Key system for per-instance key management. This enables automatic key rotation on instance restart while maintaining cache validity.
Features
- Deterministic Key Derivation: Uses HKDF-SHA256 to derive per-instance keys from instance metadata
- Multi-Environment Support: Works on EC2, ECS Fargate, Lambda, and local development
- Automatic Rotation: New instances automatically get new keys
- Cache Persistence: Worker restarts don't invalidate cached wrapped DEKs
- Fallback Chain: L1 (local memory) → Redis → AWS Secrets Manager
How It Works
Instance Start
↓
Detect Metadata (EC2/ECS/Lambda/Local)
↓
LWK = HKDF-SHA256(instance_id, region, type)
↓
Try L1 Cache → Try Redis → Fetch from Secret Manager
↓
Wrap DEK with LWK using AES-256-GCM
↓
Cache in L1 (memory) and Redis (24hr TTL)
Instance Metadata Detection
| Environment | Metadata Source | Example ID |
|---|---|---|
| EC2 | IMDSv2 | i-1234567890abcdef0 |
| ECS | Task Metadata Endpoint | task-abc123xyz789 |
| Lambda | Environment Variables | myfunction-latest |
| Local | Hostname + PID | local-macbook-12345 |
Cache Key Format
Per-instance cache keys prevent collision and enable isolation:
LIBC_DK:{type}:{instance_id}:{key_version}
Examples:
- LIBC_DK:ec2:i-1234567890ab:v1
- LIBC_DK:ecs:task-abc123:v1
- LIBC_DK:lambda:myfunction-$:v1
Performance
- LWK Derivation: <1ms average
- L1 Cache Hit: ~0.1ms
- Redis Cache Hit: ~2ms
- Secret Manager: ~150ms (only on cache miss)
Impact: Worker restart with same instance ID results in Redis cache hit instead of Secret Manager fetch (100x faster)
Verification
Run the verification script to test LWK derivation:
php examples/verify-lwk-derivation.php
Expected output:
=== Local Wrapping Key (LWK) Derivation Verification ===
✅ Instance metadata detected successfully
✅ LWK derivation is deterministic
✅ Different instances produce different LWKs
✅ Same instance produces same LWK after restart
✅ All tests passed!
Benchmarking
Run performance benchmarks:
php examples/benchmark-lwk.php
Architecture Documentation
For comprehensive documentation, see LWK-ARCHITECTURE.md:
- Detailed system architecture
- Security considerations
- Performance characteristics
- Troubleshooting guide
- Operational best practices
Migration
The LWK system is enabled by default. No configuration changes needed.
Cache Migration:
# Clear old cache format (one-time)
redis-cli KEYS "LIBC_DATAKEYCACHE*" | xargs redis-cli DEL
# Workers will automatically rebuild cache with new per-instance format
Lambda Optimization
Lambda instances automatically skip Redis to prevent key bloat. Cached wrapped DEKs are stored in local memory only for Lambda execution environments.
统计信息
- 总下载量: 168
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: proprietary
- 更新时间: 2025-05-22