vimpil / whatsapp-media-crypto
PSR-7 stream decorators for WhatsApp media encryption/decryption
Requires
- php: >=7.4
- ext-openssl: *
- guzzlehttp/psr7: ^2.0
- psr/http-message: ^1.1 || ^2.0
Requires (Dev)
- phpunit/phpunit: ^9.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-10-20 12:48:24 UTC
README
An experimental PHP implementation of the WhatsApp media encryption scheme (HKDF, AES-CBC, HMAC) as PSR-7 stream decorators.
Status: experimental, not production-ready.
- Known correctness issues: behavior for data larger than 64 KiB is incorrect.
- Known security issue: decrypted plaintext may be released before the MAC of the whole stream has been verified. Do not use it to process untrusted input.
- Not an official implementation. It is an independent project, not affiliated with or endorsed by WhatsApp or Meta, and its compatibility with WhatsApp is not guaranteed.
- Not audited for security. No stable release is planned, and
dev-mastermay change without notice.Treat it as a learning and experimentation project.
Requirements
- PHP 7.4+
- OpenSSL extension
- Composer
Installation
Not recommended for production use (see the status notice above). For experimentation only:
composer require vimpil/whatsapp-media-crypto:dev-master
Main Features
- PSR-7 compatible stream decorators
- Support for various media types (images, video, audio, documents)
- Stream-based API (see the known issue above for data over 64 KiB)
- Built-in sidecar generation for video and audio streaming
- Refactored
StreamFactorywith modular validation methods
Project Structure
src/
├── Stream/
│ ├── AbstractCryptoStream.php # Base class for cryptographic streams
│ ├── EncryptingStream.php # Encryption implementation
│ └── DecryptingStream.php # Decryption implementation
├── HKDF.php # HKDF implementation
├── MediaKey.php # Media key handling
└── StreamFactory.php # Stream factory with modular validation
Usage Examples
Image Encryption
use WhatsAppMedia\StreamFactory; use GuzzleHttp\Psr7\Utils; $source = Utils::streamFor(fopen('image.jpg', 'rb')); $mediaKey = file_get_contents('image.key'); $encryptedStream = StreamFactory::createEncryptingStream( $source, $mediaKey, 'IMAGE' );
Video/Audio Encryption with Sidecar Generation
$source = Utils::streamFor(fopen('video.mp4', 'rb')); $mediaKey = file_get_contents('video.key'); $encryptedStream = StreamFactory::createEncryptingStream( $source, $mediaKey, 'VIDEO', true // enable sidecar generation ); // Drain the encrypted stream first so that the sidecar is fully generated while (!$encryptedStream->eof()) { $encryptedStream->read(8192); } // Get the sidecar after encryption $sidecar = $encryptedStream->getSidecar();
File Decryption
$encrypted = Utils::streamFor(fopen('encrypted_file', 'rb')); $mediaKey = file_get_contents('file.key'); $decryptedStream = StreamFactory::createDecryptingStream( $encrypted, $mediaKey, 'VIDEO' // or 'AUDIO', 'IMAGE', 'DOCUMENT' );
Supported Media Types
| Type | Description | Application Info | Sidecar Support |
|---|---|---|---|
| IMAGE | Images | WhatsApp Image Keys | No |
| VIDEO | Video | WhatsApp Video Keys | Yes |
| AUDIO | Audio | WhatsApp Audio Keys | Yes |
| DOCUMENT | Documents | WhatsApp Document Keys | No |
Ready-to-Use Examples
The examples/ directory contains ready-to-use scripts:
Encryption
encrypt_image.php- image encryptionencrypt_video.php- video encryptionencrypt_audio.php- audio encryptionencrypt_document.php- document encryptionencrypt_video_streaming.php- video encryption with sidecarencrypt_audio_streaming.php- audio encryption with sidecar
Decryption
decrypt_image.php- image decryptiondecrypt_video.php- video decryptiondecrypt_audio.php- audio decryptiondecrypt_document.php- document decryption
All examples include:
- File existence checks
- Output directory creation
- Error handling
- File size information
Implementation Details
Encryption
- Generate or use existing mediaKey (32 bytes)
- Expand key using HKDF with SHA-256 and type-specific application info
- Split into components:
- iv: first 16 bytes
- cipherKey: next 32 bytes
- macKey: next 32 bytes
- refKey: remaining 32 bytes (not used)
- AES-CBC encryption using cipherKey and iv
- HMAC generation using macKey
Sidecar Generation (for video and audio)
- Chunk size: 64KB
- Overlap: 16 bytes
- HMAC SHA-256 for each chunk, truncated to 10 bytes
- On-the-fly generation without additional reads from source stream
Note: The 10-byte MAC truncation is WhatsApp-specific and is not an AEAD construction. It is implemented here for protocol compatibility and should not be reused as a generic cryptographic design.
Testing
composer install vendor/bin/phpunit
Test Coverage
tests/CryptoTest.php contains PHPUnit tests for encryption/decryption round trips, sidecar generation, invalid MAC, empty stream, key length validation and unsupported types. These tests do not cover the known issues listed at the top of this file, and no coverage figure is claimed.
Security
This library makes no security guarantees. See the status notice at the top of this file for the known issues.
License
MIT License