Several years ago, we designed sodium_compat: a pure-PHP implementation of (most of) libsodium. It has since been installed over 60 million times, not counting WordPress.
Our goals with sodium_compat were as follows:
- Ensure ease-of-adoption for ext-sodium. By providing a permissively licensed open source PHP library that implements the same algorithms, other open source projects could move faster to libsodium proper, even if their users were on shared hosting environments where they couldn't install PHP extensions from PECL.
- Get adopted by software that supported old versions of PHP. Specifically, 5.2.4, which WordPress still supported at the time. Due to WordPress's enormous footprint on the Internet, this was not a lightly taken decision.
- Work even on 32-bit platforms. It turns out, PHP gives you signed 32-bit integers on 32-bit platforms (or on Windows before PHP 7), and if you reach a value out of the range of those integers, your numbers are "conveniently" converted to a float instead. This is a nightmare when you're implementing cryptography.
- Get out of your way. If you install ext-sodium, our APIs will defer all cryptography to the proper implementation. This was a core tenet of sodium_compat's design.
To repeat ourselves a bit, this was several years ago. The PHP community has evolved. WordPress now requires PHP 7.0 to function (although they do recommend 7.4). As of this writing, the oldest still-supported version of PHP is 8.2 (8.1 if you count "security fixes only"). We asked ourselves, and the /r/PHP subreddit, if anyone still even needs PHP 5 support in 2024.
The answer was a resounding, "No."
That's the background story, anyway. If you're a busy developer, the remainder of this post will be broken down into a Q&A format to explain what's changing, what's not changing, what you need to do, and whether you need to do anything.
What's Changing?
Since we have two widely used polyfill libraries, I'll answer each separately.
sodium_compat
We have forked v1.x onto a separate branch, and v2.x will be developed in the main branch.
This new branch only supports 64-bit PHP, version 8.1 and newer.
Future releases that support PHP 5.2.4+ and 32-bit PHP will continue with a major version of 1, as usual.
random_compat
No change needed.
If you're on PHP 5, you can use it to get the same APIs we enjoy on PHP 7 and newer. Otherwise, you don't need it anymore. If your minimum PHP version is 7+, you can remove it from your composer.json file and sodium_compat v2 will not add it back as a recursive dependency.
If a security issue is discovered in random_compat, we will fix it. But otherwise, you can consider it code complete.
What's NOT Changing?
Any new features that land in ext-sodium, and therefore sodium_compat v2, will be backported to sodium_compat v1.
We will not be breaking your code or deployments unless you somehow install v2 on a 32-bit platform. Composer should prevent anyone from installing v2 on, e.g., PHP 5.6.
What Do I Need to Do?
Library and framework developers (or anyone else that develops code that might be pulled in as a dependency by someone else), make sure your version constraint for sodium_compat looks like this: ^1|^2
. This will enable your users to use v1 or v2 at their leisure.
Application developers, if you immediately care about older PHP or 32-bit platforms, make sure your version constraint for sodium_compat looks like this: ^1
. This will keep you on the maximally compatible version of our polyfill.
If you do not care about those things, make sure your version constraint for sodium_compat looks like this: ^2
. This will keep you on the latest and greatest version of our polyfill.
Anticipated Questions
Why Are You Dropping 32-bit Support in v2?
We understand that a lot of open source project leads will reflexively take a stand to defend their users on 32-bit hardware, no matter how vanishingly rare they are. Why are we leaving them behind?
The code needed to implement the same cryptographic algorithms with signed 32-bit integers is excruciating to develop for by every metric. It's also incredibly slow. But that's not our reason.
We haven't been adding new features for a long time, and nobody noticed or complained.
To wit, three years ago, when we updated sodium_compat to include the Ristretto group functions ahead of a ext-sodium release. We anticipated that, in the past three years, some project (most likely cryptocurrency-adjacent, if we're making realistic bets) would have sent us a GitHub issue or an email asking when they would be coming to sodium_compat.
In over three years since? Zero questions or complaints about 32-bit support.
Your 32-bit users, if they care about libsodium at all, will just install the PHP extension and not rely on our polyfill library.
The code that supports 32-bit PHP will continue to live in the v1.x branch, and we may eventually implement new algorithms for 32-bit PHP if there is sufficient interest, but we will not carry that burden forward to v2.x.
That is why our guidance for open source projects that are likely to become dependencies is, "support both", and for applications that can afford to be opinionated, "pick which ever matters to you".
Does sodium_compat v2 have backwards compatibility breaks for 64-bit PHP?
We now make heavy use of scalar type declarations instead of runtime checks, which will improve performance for most workloads.
Of course, installing ext-sodium will do far more to improve performance. You should, if you can.
We also make heavy use of the #[SensitiveParameter]
attribute, so if you're depending on the visibility of plaintext messages or cryptographic secrets in stack traces, sodium_compat v2 will make you sad.
Otherwise, the answer to that question should be, "No." If anything else does break, that is a bug, and we ought to fix it.
How much code did you delete in sodium_compat v2?
According to GitHub, the difference between v1.21.0
and v2.0.0
clocks in at 3,832 additions and 23,713 deletions, so just under 20kLoC.
How do I tell if I'm on 32-bit PHP or not?
From your shell, run the following:
php -r "echo PHP_INT_SIZE << 3;"
If this outputs 32
, you're on 32-bit PHP. It should output 64
for everyone else.
Can we get Argon2id support through FFI now that PHP 7.4+ is required?
No. If you can use FFI, you should be able to use libsodium. Do that instead.
You implemented AES recently. Can we get AES-GCM polyfilled too?
Probably not.
It's true that we implemented AES, based on a bitsliced software implementation provided by BearSSL, in order to support a new AEAD cipher called AEGIS (which is landing in PHP 8.4).
The reason we actually implemented AES in PHP was that we just needed the AES round function to implement AEGIS (and NOT the complete block cipher). The complete block cipher implementation exists just for testing.
If there is sufficient need for this, we're open to discussing options, but as it stands right now, we have no plans to polyfill AES-GCM. The reward isn't worth the risk.