dsbaars/chacha20
Pure PHP implementation of the ChaCha20 encryption algorithm, built for high performance relative to PHP. Supports streaming via persistent context objects and keystream seeking with setCounter. A fork of leigh/chacha20.
Architecture fit. This is a raw stream-cipher primitive, not an encryption library. It exposes init/encrypt/decrypt/setCounter on a ChaCha20\Cipher class and provides no authentication tag, no AEAD construction, no key derivation, no nonce generation, and no envelope format. Laravel already ships a complete encryption stack (Illuminate\Encryption\Encrypter over OpenSSL AES-256-CBC/GCM with HMAC, plus sodium_* bindings for ChaCha20-Poly1305 in PHP 7.2+). The package therefore does not compete with the framework's default; it only fits if a specific requirement cannot be met by OpenSSL or libsodium. The two defensible fits are (a) wire-format compatibility with a legacy or third-party protocol that specifies raw ChaCha20 with exact nonce/counter layout, and (b) seekable keystream access via setCounter, which enables random-access decryption of large files or object ranges without decrypting from byte zero — a capability Laravel's Encrypter does not offer.
Integration feasibility. Low ceremony, low leverage. There is no service provider, facade, config, or contract implementation; adoption means a composer require, a thin wrapper class, and a container binding. That is a day of work. The real cost is the cryptographic wrapper we would have to design ourselves: nonce allocation and uniqueness guarantees, an Encrypt-then-MAC construction (HMAC-SHA-256 or a separate Poly1305 path), key rotation, and a versioned ciphertext envelope. We would be building and owning the security-critical layer that every mature library gives us for free.
Technical risk.
encrypt directly produces malleable output. Bit-flipping and truncation are undetectable. This is the single largest footgun and will happen unless the raw API is never exposed to application code.Key questions. Which nonce length and counter width does init accept? Are RFC 8439 test vectors in the CI suite, and do they pass on our PHP version? What is the supported PHP range and does it include our runtime and any PHP 8.x JIT configurations? What is the measured throughput versus sodium_crypto_aead_chacha20poly1305_ietf_encrypt and openssl_encrypt for our realistic payload sizes? Is upstream lt/PHP-ChaCha20 still maintained, and what is our policy if this fork is abandoned? Is there a named internal owner for cryptographic correctness on this path?
Stack fit. Poor-to-conditional. For anything Laravel-native — session cookies, Crypt::encrypt, encrypted casts, signed URLs — the answer is no; the framework's OpenSSL/sodium path is faster, authenticated, and maintained. The viable pattern is a narrow, purpose-built service: a ChaCha20FileCipher-style class bound as a singleton, used only for one identified workload (e.g., seekable encryption of large at-rest blobs, or decoding a partner protocol), with the raw Cipher class kept internal and never injected into controllers or models.
Migration path. Treat this as an add, not a swap. Do not touch the default encrypter. Introduce a feature-flagged capability behind an interface we define (SeekableCipher or LegacyProtocolCipher), with the ChaCha20 package as one implementation. That interface gives us a clean exit to a native implementation later if sodium or OpenSSL covers the need — which for the seekable case it partly can via counter manipulation on sodium_crypto_stream_chacha20_ietf_xor_ic, a much better-supported starting point worth evaluating first.
Compatibility. Requires a spike to confirm PSR-4 autoloading, namespace stability, and behavior across our supported PHP matrix. No Laravel version coupling, no framework conflicts, no transitive dependency surface — that is the package's one genuine strength. Note that Composer will happily install it alongside an existing sodium or OpenSSL path with no conflict, so nothing forces a decision at install time.
Sequencing.
sodium_crypto_stream_chacha20_ietf_xor_ic cannot satisfy it. Escalate if it can — most of this work then disappears.Maintenance. The package is effectively unmaintained for our purposes: 1 star, 0 dependents, "readme"-level maturity, no versioning narrative beyond a Composer constraint. Assume we own it. That means vendoring or pinning an exact version, an internal fork if any patch is ever needed, and an assigned owner who re-runs test vectors on every PHP upgrade. Budget a recurring, if small, line item — this is not a fire-and-forget dependency.
Support. No community, no issue triage velocity, no vendor. Escalation path is the internal owner plus, at best, the upstream author. Security incident response (e.g., a variant or counter bug is found) is entirely on us, and the blast radius is all data encrypted with it — including data already at rest, which cannot be re-encrypted instantly.
Scaling. CPU-bound and single-threaded per request. Pure PHP ARX rounds mean throughput an order of magnitude or more below native, though the exact ratio must be measured rather than assumed. Under PHP-FPM, encryption of large payloads will hold workers, so queue large-object workloads rather than doing them inline. The 64-byte block counter places a hard ceiling on keystream per (key, nonce) pair; the wrapper must enforce it and fail loudly rather than wrap. Streaming via Context keeps memory flat, which is the package's operational bright spot — do not undermine it by buffering whole files.
Failure modes.
Ramp-up. The code is small and readable, so onboarding an engineer to call it is trivial. The gap is cryptographic literacy: nonce discipline, AEAD vs. raw stream cipher, counter limits, and constant-time reasoning are not skills the average Laravel developer exercises, because the framework normally hides them. Plan for a security reviewer in the loop and a written internal standard for this cipher path — otherwise the failure mode is a well-intentioned developer calling encrypt() directly with a uniqid() nonce.
Recommendation. Conditional reject. Adopt only if a documented, non-substitutable requirement (protocol interop or seekable keystream) survives the requirements gate, and then only behind an internal wrapper with authenticated framing, enforced nonce and counter management, RFC vectors in CI, and a named owner. Absent that, use sodium_crypto_aead_chacha20poly1305_ietf_* or the existing Laravel encrypter.
How can I help you explore Laravel packages today?