8.9 KiB
Contractless PHP Crypto Modules
These native PHP modules provide the cryptography required by
php-contractless-rpc:
- Skein-256 hashing through PHP-Skein-Hash
- Contractless-compatible Falcon-512 signing and verification through liboqs
Contractless uses fixed 666-byte Falcon signatures and a specific FN-DSA raw message format. The included liboqs-php patch adds the SHAKE256 operation needed to reproduce that format exactly.
Supported Environment
The automated installer currently supports 64-bit Debian and Ubuntu systems. This includes Ubuntu running through WSL. The modules can also be compiled manually on other Linux distributions.
Native modules are tied to the operating system, CPU architecture, and PHP extension API. They should normally be compiled on the machine where they will run.
Debian And Ubuntu Requirements
sudo apt update
sudo apt install -y \
php-cli php-dev \
swig cmake ninja-build \
build-essential git pkg-config libssl-dev
Automated Debian And Ubuntu Build
chmod +x build-linux.sh install-linux.sh
./build-linux.sh
This downloads and compiles:
- A minimal liboqs build containing
Falcon-padded-512 - The patched liboqs-php extension
- The PHP-Skein-Hash extension
Building does not install or enable the modules.
Automated Debian And Ubuntu Installation
After a successful build:
sudo ./install-linux.sh
The installer copies both modules into the active PHP extension directory,
creates contractless-crypto.ini, enables it through phpenmod, and verifies
that PHP can see both modules.
Do not add either module directly to php.ini. The installer manages both
module declarations through contractless-crypto.ini. Loading them through
both locations causes duplicate-module warnings.
Confirm the installation manually:
php -m | grep -E '^(skein|oqsphp)$'
Expected output:
oqsphp
skein
Apache or PHP-FPM may need to be restarted:
sudo systemctl restart apache2
or:
sudo systemctl restart php8.3-fpm
Use the PHP version installed on the server in the service name.
Manual Build On Other Linux Distributions
Other distributions can use the same modules but may use different package names and PHP configuration locations.
The following tools are required:
- PHP CLI and the matching PHP development package
phpizeandphp-config- SWIG
- CMake
- Ninja
- Git
- A C and C++ compiler
- Make
- OpenSSL development headers
pkg-config
The PHP development package must match the PHP installation that will load the modules. For example, an extension built for PHP 8.3 should not be loaded into PHP 8.4.
Fedora, RHEL, Rocky Linux, And AlmaLinux
Package names vary between system repositories and third-party PHP repositories, but a typical installation is:
sudo dnf install \
php-cli php-devel \
swig cmake ninja-build \
gcc gcc-c++ make git pkgconf-pkg-config openssl-devel
Arch Linux
sudo pacman -S --needed \
php swig cmake ninja \
base-devel git pkgconf openssl
Alpine Linux
The exact PHP development package includes the installed PHP version on some Alpine releases:
sudo apk add \
php php-dev \
swig cmake ninja \
build-base git pkgconf openssl-dev
Confirm the required PHP build commands exist:
php --version
phpize --version
php-config --version
swig -version
Confirm that the runtime and development tools use the same PHP module API:
php -i | grep '^PHP API'
php-config --phpapi
phpize --version
The API numbers reported by all three commands must match. A module compiled
with PHP 8.4 development tools cannot be loaded by PHP 8.3, and the reverse is
also true. build-linux.sh stops with an error when it detects this mismatch.
Build liboqs
Choose a working directory:
mkdir -p contractless-php-build
cd contractless-php-build
Download and compile a minimal liboqs containing the fixed-size Falcon algorithm used by Contractless:
git clone --depth 1 https://github.com/open-quantum-safe/liboqs.git
cmake -S liboqs -B liboqs/build -GNinja \
-DBUILD_SHARED_LIBS=OFF \
-DOQS_BUILD_ONLY_LIB=ON \
-DOQS_MINIMAL_BUILD="SIG_falcon_padded_512"
ninja -C liboqs/build
Do not substitute Falcon-512. Contractless requires
Falcon-padded-512, which produces fixed 666-byte signatures.
Build The Contractless liboqs-php Module
Clone the PHP wrapper:
git clone --depth 1 https://github.com/Muzosh/liboqs-php.git
Apply oqsphp-contractless.patch from this repository:
cd liboqs-php
git apply /full/path/to/Contractless-PHP-Modules/oqsphp-contractless.patch
Build the PHP extension against the liboqs copy compiled above:
LIBOQS_ROOT="$(cd ../liboqs && pwd)" bash build.sh
cd ..
The resulting module is:
liboqs-php/build/oqsphp.so
Build The Skein Module
git clone --depth 1 https://github.com/jedisct1/PHP-Skein-Hash.git php-skein
cd php-skein
phpize
./configure
make
cd ..
The resulting module is:
php-skein/modules/skein.so
Install The Modules Manually
Find the extension directory used by the active PHP installation:
php-config --extension-dir
Copy both modules into that directory:
sudo install -m 0755 \
liboqs-php/build/oqsphp.so \
"$(php-config --extension-dir)/oqsphp.so"
sudo install -m 0755 \
php-skein/modules/skein.so \
"$(php-config --extension-dir)/skein.so"
Find the PHP configuration directories:
php --ini
Create one dedicated extension configuration file containing:
extension=skein.so
extension=oqsphp.so
CLI PHP, Apache, and PHP-FPM can use different configuration directories. The
modules must be enabled for every PHP runtime that uses
php-contractless-rpc. Enable or link this dedicated file in each required
runtime configuration directory. Do not also add these declarations directly
to php.ini.
Restart Apache or PHP-FPM after changing its configuration. Service names vary by distribution and installed PHP version.
Verify the manual installation:
php -m | grep -E '^(skein|oqsphp)$'
Compatibility Test
The compatibility test proves that PHP and Contractless Rust produce matching Skein hashes and mutually valid Falcon signatures:
cd interop
cargo run --release
cd ..
php tests/native.php
The generated private key is a disposable test fixture. Never use it as an application wallet.
Using The Modules
Applications normally use these modules through php-contractless-rpc rather
than calling them directly.
Direct Skein hashing:
$digest = skein_hash($data, 256);
Direct Falcon Signing And Verification
Contractless uses Falcon-padded-512. Do not substitute the variable-length
Falcon-512 algorithm.
Falcon keys and signatures are binary data. Load and store them without text, hexadecimal, Base64, or character-encoding conversions unless the application explicitly performs and reverses that conversion.
Contractless Falcon signatures use a SHAKE256 prefix derived from the public key, followed by the raw-message marker and the original message:
SHAKE256(public_key, 64 bytes) || 0x00 || 0x00 || original_message
Sign A Message
<?php
declare(strict_types=1);
$message = 'Message to sign';
$publicKey = file_get_contents('public-key.bin');
$privateKey = file_get_contents('private-key.bin');
if ($publicKey === false || $privateKey === false) {
throw new RuntimeException('Unable to load the Falcon keys.');
}
$falcon = new OQS_SIGNATURE('Falcon-padded-512');
$adaptedMessage =
contractless_shake256($publicKey, 64)
. "\0\0"
. $message;
$signature = '';
if ($falcon->sign($signature, $adaptedMessage, $privateKey) !== 0) {
throw new RuntimeException('Falcon signing failed.');
}
if (strlen($signature) !== 666) {
throw new RuntimeException('Invalid Contractless signature length.');
}
file_put_contents('signature.bin', $signature);
Verify A Signature
<?php
declare(strict_types=1);
$message = 'Message to sign';
$publicKey = file_get_contents('public-key.bin');
$signature = file_get_contents('signature.bin');
if ($publicKey === false || $signature === false) {
throw new RuntimeException('Unable to load the public key or signature.');
}
if (strlen($signature) !== 666) {
throw new RuntimeException('Invalid Contractless signature length.');
}
$falcon = new OQS_SIGNATURE('Falcon-padded-512');
$adaptedMessage =
contractless_shake256($publicKey, 64)
. "\0\0"
. $message;
if ($falcon->verify($adaptedMessage, $signature, $publicKey) !== 0) {
throw new RuntimeException('Falcon signature verification failed.');
}
echo "Signature verified.\n";
These examples expose the low-level module behavior for testing and debugging.
Applications should normally use the NativeCrypto class provided by
php-contractless-rpc, which applies the Contractless message framing
automatically.