// kit 01

PQC Toolkit

Production-ready post-quantum crypto. Pick stack, pick primitive, copy the code.

$ pqc_kem.php
L3
PHP
1<?php
2// Requires liboqs FFI binding (see downloads/pqc-kit-php).
3// PHP has no pure-PHP ML-KEM yet; this wraps liboqs via FFI.
4declare(strict_types=1);
5 
6final class PqcKem
7{
8 private \FFI $oqs;
9 
10 public function __construct(string $libPath = '/usr/local/lib/liboqs.so')
11 {
12 $hdr = <<<H
13typedef struct OQS_KEM OQS_KEM;
14OQS_KEM* OQS_KEM_new(const char* method_name);
15int OQS_KEM_keypair(OQS_KEM* kem, uint8_t* public_key, uint8_t* secret_key);
16int OQS_KEM_encaps(OQS_KEM* kem, uint8_t* ciphertext, uint8_t* shared_secret, const uint8_t* public_key);
17int OQS_KEM_decaps(OQS_KEM* kem, uint8_t* shared_secret, const uint8_t* ciphertext, const uint8_t* secret_key);
18void OQS_KEM_free(OQS_KEM* kem);
19H;
20 $this->oqs = \FFI::cdef($hdr, $libPath);
21 }
22 
23 public function keygen(): array
24 {
25 $kem = $this->oqs->OQS_KEM_new('ML-KEM-768');
26 $pub = \FFI::new('uint8_t[1184]');
27 $sec = \FFI::new('uint8_t[2400]');
28 $this->oqs->OQS_KEM_keypair($kem, $pub, $sec);
29 return ['pub' => \FFI::string($pub, 1184), 'sec' => \FFI::string($sec, 2400)];
30 }
31 
32 public function encaps(string $pub): array
33 {
34 $kem = $this->oqs->OQS_KEM_new('ML-KEM-768');
35 $ct = \FFI::new('uint8_t[1088]');
36 $ss = \FFI::new('uint8_t[32]');
37 $this->oqs->OQS_KEM_encaps($kem, $ct, $ss, $pub);
38 return ['ct' => \FFI::string($ct, 1088), 'ss' => \FFI::string($ss, 32)];
39 }
40}
41 
42function pqc_combine(string $ss_pq, string $ss_x, string $transcript): string
43{
44 return sodium_crypto_generichash($ss_x . $ss_pq . $transcript, '', 32);
45}
KEY SIZE
1184 B pub
SIG / CT
1088 B ct
NIST LEVEL
L3
NOTES

PHP has no native PQ; the kit ships an FFI binding to liboqs.

  • liboqs 0.10+ ships ML-KEM under both old (Kyber) and new (ML-KEM) names.
  • FFI has overhead; pool the OQS_KEM_new allocations in hot paths.
  • For shared hosting with no liboqs, fall back to a sidecar service via Unix socket.